diff --git a/AUTHORS b/AUTHORS index 47cc72b7f9..9942cf9bce 100644 --- a/AUTHORS +++ b/AUTHORS @@ -224,13 +224,13 @@ The following people have contributed code to GIMP: David Odin Nelson A. de Oliveira Victor Oliveira + Yoshio Ono Thom van Os Garry R. Osgood Nathan Osman Benjamin Otte Petr Ovtchenkov Alan Paeth - Jehan Pagès Jay Painter Juan Palacios Ville Pätsi @@ -259,6 +259,7 @@ The following people have contributed code to GIMP: Tim Rowley Karthikeyan S Daniel Sabo + Oleksii Samorukov Mike Schaeffer John Schlag Norbert Schmitz diff --git a/ChangeLog b/ChangeLog index af1232c0f1..8d07efe73b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15999 @@ +commit 3d8535b55fdafffd5a46b57ae3779333e7fe3ac6 +Author: Michael Natterer +Date: Wed Jun 12 23:17:10 2019 +0200 + + configure.ac: bump versions for the 2.10.12 release + + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 9b205f15830025dee9b31b04e14c76fd6116ca17 +Author: Michael Natterer +Date: Wed Jun 12 23:16:19 2019 +0200 + + NEWS: formatting, typos + + NEWS | 53 +++++++++++++++++++++++++++++------------------------ + 1 file changed, 29 insertions(+), 24 deletions(-) + +commit 3809645bc59219ab8d756d6e570bb3b05f61a406 +Author: Jehan +Date: Wed Jun 12 23:10:00 2019 +0200 + + NEWS: create news section for GIMP 2.10.12. + + NEWS | 116 + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 116 insertions(+) + +commit 4938615236cf7813b2c018055b5e1937ec41d2f8 +Author: Alexandre Prokoudine +Date: Wed Jun 12 22:49:38 2019 +0300 + + Update Russian translation + + po-plug-ins/ru.po | 566 +++++++++++----------- + po/ru.po | 1366 + ++++++++++++++++++++++++++++------------------------- + 2 files changed, 1011 insertions(+), 921 deletions(-) + +commit fa87116fe6d2ce37b945c3be2e5508a097477ba7 +Author: Alexandre Prokoudine +Date: Wed Jun 12 22:44:33 2019 +0300 + + Offset tool: mark forgotten messages for translation + + app/tools/gimpoffsettool.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit add350f335b8c4ef00eeaded50537317d3a0eb4f +Author: Michael Natterer +Date: Wed Jun 12 21:43:19 2019 +0200 + + AUTHORS: regenerated + + AUTHORS | 2 ++ + 1 file changed, 2 insertions(+) + +commit 4abf2306b359c31f7ad5a6404f2534bec4c67539 +Author: Øyvind Kolås +Date: Wed Jun 12 21:32:47 2019 +0200 + + build: depend on babl-0.1.66 + + (cherry picked from commit b3dcb5aaacbabf3ed4559f524bd60c3486d32334) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit e70c08cdd01dcae7117f880dfc286e178fe4a262 +Author: Michael Natterer +Date: Wed Jun 12 21:06:56 2019 +0200 + + app: add missing parameter in test-xcf.c + + app/tests/test-xcf.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit aedee5ef825c95a323d1e64ff0c6cfd9e9027bb6 +Author: Kalev Lember +Date: Wed Jun 12 15:47:04 2019 +0200 + + desktop: Update gimp-data-extras.metainfo with gimp's new appstream ID + + (cherry picked from commit 678634390907e20bb2f9c0c5ce0702fc97e9479d) + + desktop/gimp-data-extras.metainfo.xml.in.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 4c8c4c7e96e5d52c708099b208d72fe1e736da44 +Author: Jehan +Date: Wed Jun 12 14:33:49 2019 +0200 + + authors.xml: add Oleksii Samorukov and Yoshio Ono. + + (cherry picked from commit 3ebad2253924e7224a2ccc26cbb8c9c77c1921a5) + + authors.xml | 2 ++ + 1 file changed, 2 insertions(+) + +commit 76f1a02631eb091d6d285f64710efa47f1716e61 +Author: Jehan +Date: Wed Jun 12 12:54:37 2019 +0200 + + plug-ins: get rid of compilation warning. + + Note that I am using twice TIFFErrorHandler type. This is because + TIFFWarningHandler does not exist, despite what the docs says and + upstream code actually uses twice the same handler type. + + (cherry picked from commit de866d881537a2931197ab4752c0047a5d29f766) + + plug-ins/file-tiff/file-tiff-io.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit b7b275f9088f6ab5187c1610e51987d485c056f1 +Author: Anders Jonsson +Date: Tue Jun 11 20:09:16 2019 +0000 + + Update Swedish translation + + po/sv.po | 2224 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 1192 insertions(+), 1032 deletions(-) + +commit 4fe8a76f9b9c8f541080a27d9165e96bb14c4c1f +Author: Anders Jonsson +Date: Tue Jun 11 19:18:26 2019 +0000 + + Update Swedish translation + + po-plug-ins/sv.po | 325 + +++++++++++++++++++++++++++++------------------------- + 1 file changed, 174 insertions(+), 151 deletions(-) + +commit e8f7b3575d09d262faf5593db7226dc66dabba74 +Author: Anders Jonsson +Date: Tue Jun 11 19:06:43 2019 +0000 + + Update Swedish translation + + po-libgimp/sv.po | 509 + +++++++++++++++++++++++++++---------------------------- + 1 file changed, 254 insertions(+), 255 deletions(-) + +commit 727b83cbe651f2b9f3b13d7b366944675b25e50e +Author: Michael Natterer +Date: Tue Jun 11 19:30:48 2019 +0200 + + tools: fix windows file handling in gimp-test-clipboard.c + + Use g_open() for proper filename encoding and _O_BINARY to avoid line + ending conversions. + + (cherry picked from commit a9b36245c9e976557e9d6ff29d74aac325a89273) + + tools/gimp-test-clipboard.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +commit bf3ea7539e02cbd61d75fe5f608046bae33cf852 +Author: Jehan +Date: Tue Jun 11 17:29:03 2019 +0200 + + desktop: give 2.10.12 release date. + + Let's make the release happen! + + (cherry picked from commit fa6b57cfbd8649a2358ec7238d50057e1668b9d6) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 0667fd27c62db2f655c6c6892201b6afcc4bdd89 +Author: Jehan +Date: Tue Jun 11 15:33:15 2019 +0200 + + Issue #3493: GIMP changes R-channel, when it should not. + + Our TIFF loading code was not taking into account the case when extra + channels were stored in the TIFF file while ExtraSamples field is not + set. Yet as a side effect of a later channel count, we were setting + `alpha` to TRUE while `save_transp_pixels` was left uninitialized + (hence + undefined behavior). + + For now let's make sure we have no undefined behavior. When the + ExtraSamples field is missing and at least one extra channel is + stored, + we will consider the first extra channel as non-premultiplied alpha + (this is also what we were doing when ExtraSamples was set to + "Unspecified data" and apparently according to Massimo, it would be a + common behavior in other software). + + Note that it is an improvement from previous code (no undefined + behaviour anymore, instead we handle explicitly the TIFF error). Yet + this is not perfect yet. Ideally we should pop-up a dialog asking what + to do with this extra channel: either open as a channel (no alpha), or + as premultiplied or non-premultiplied alpha. + + (cherry picked from commit b090bc521323d6a834c9c1e98d3c5dbd601f7d37) + + plug-ins/file-tiff/file-tiff-io.c | 10 ++++++++ + plug-ins/file-tiff/file-tiff-load.c | 46 + +++++++++++++++++++++++++++---------- + 2 files changed, 44 insertions(+), 12 deletions(-) + +commit 6a854e45fa1087a3df461430a494576c44b600f3 +Author: Jehan +Date: Tue Jun 11 02:14:18 2019 +0200 + + Issue #2194: Action search dialog behaves as a full window using a... + + ... tiling window manager. + Completing commit e6364ffa81 by additionally making sure the search + dialog is non-modal. Otherwise it prevents positionning it wherever we + want and it stays in the center, potentially hiding the canvas and + darkening the main window. + + (cherry picked from commit aa1171ada2a85428972a76eae0ce908fd45a841b) + + app/widgets/gimpsearchpopup.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 33aada6b6fbe05242b5497b8379701671964bfaf +Author: Daniel Mustieles +Date: Mon Jun 10 08:18:45 2019 +0000 + + Update Spanish translation + + po/es.po | 1737 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 915 insertions(+), 822 deletions(-) + +commit 9c8e1704c4d06adcce3530062395051c79485049 +Author: Ell +Date: Mon Jun 10 03:51:14 2019 -0400 + + Issue #3484 - While moving a nested layer group, some area isn't + updated + + In gimp_group_layer_translate(), when translating a nested group + layer, call gimp_drawable_update_all() to update the child-layers' + original area *before* updating the group's offset, at which point + the group parent's size is updated, causing subsequent area-updates + during translation to be clipped to the parent's new bounds, + preventing the original areas from being properly cleared. + + (cherry picked from commit 4a20a75dc32cf47a9aaa85bbd52f145816467c2a) + + app/core/gimpgrouplayer.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +commit 854374b2ced5ab4b1db8e130a5513c5a30657172 +Author: Ell +Date: Mon Jun 10 03:42:07 2019 -0400 + + app: add gimp_drawable_update_all() + + Add a new GimpDrawable::update_all() virtual function, and a + corresponding gimp_drawable_update_all() function, which updates + the full contents of the drawable. Unlike calling + `gimp_drawable_update (drawable, 0, 0, -1, -1)`, which updates the + entire drawable area, gimp_drawable_update_all() only updates the + area that has actual content. While the default implentation does + simply update the entire drawable area, GimpGroupLayer overrides + this function to recursively update its child layers, rather than + the its entire area. + + (cherry picked from commit 3e5cbb03d904ac59dbeb64d5a6d453c1a829ae90) + + app/core/gimpdrawable.c | 17 +++++++++++++++++ + app/core/gimpdrawable.h | 2 ++ + app/core/gimpgrouplayer.c | 25 +++++++++++++++++++++++++ + 3 files changed, 44 insertions(+) + +commit 1f0abe57cc256f8a0b0a5158e7c44c6fb9af5003 +Author: Piotr Drąg +Date: Sun Jun 9 17:26:11 2019 +0200 + + Update Polish translation + + po/pl.po | 416 + ++++++++++++++++++++++++++++++++------------------------------- + 1 file changed, 211 insertions(+), 205 deletions(-) + +commit d66e7dccac59ecc4c8c97fbd4ab1cfd4dd274831 +Author: Øyvind Kolås +Date: Sun Jun 9 12:29:30 2019 +0200 + + app: add GeglOperation *operation argument to GimpLayerModeBlendFunc + + Some blend funcs depend on constants from the specifc color space + we are + operating in and needs the space or operation propagated to the + worker function + of the operation as discovered in issue #3451. + + This commit propagates the operation, leaving the specific blend + functions + needing it to call gegl_operation_get_source_space or similar + without needing + that overhead for the rest. + + (cherry picked from commit 8e90468308c7aa8c8760a23e4d754bfb4f87ca52) + + .../layer-modes/gimpoperationlayermode-blend.c | 297 + ++++++++++++--------- + .../layer-modes/gimpoperationlayermode-blend.h | 297 + ++++++++++++--------- + .../layer-modes/gimpoperationlayermode.c | 4 +- + app/operations/operations-types.h | 3 +- + 4 files changed, 334 insertions(+), 267 deletions(-) + +commit c897aabcc8e88b1c7ed7eea2b3343c487c2f9cbb +Author: Jehan +Date: Fri Jun 7 18:18:33 2019 +0200 + + plug-ins: PNG export TRC chosen similarly to TIFF. + + While we tend to default to sRGB for 8-bit max formats (such as + JPEG or + WebP) when no explicitly-assigned profile was set, there is no need to + do so for PNG too. Indeed since we have the ability to export 16-bit + PNG, let's consider this is ok to export 16-bit linear data when + writing + GIMP's default linear profile. + + Moreover let's follow the profile's (fallback to storage's) TRC also + when exporting to a specific precision (as chosen manually in the + dialog), not only when sticking to "Automatic", unless we are + downsizing + a high bit depth work image to 8-bit without a manually assigned + profile + (the only case we forcefully export as sRGB data, hence convert the + profile if linear storage). + + (cherry picked from commit 24ed5870ade6e76ec828a752cf6b2a6d6c043f28) + + plug-ins/common/file-png.c | 100 + ++++++++++++++++++++++++++++++++------------- + 1 file changed, 72 insertions(+), 28 deletions(-) + +commit e7cd743b0529a61b2638ca582309dd8d6df6bdee +Author: Jehan +Date: Fri Jun 7 18:12:58 2019 +0200 + + plug-ins: simplify code a bit by removing useless variable. + + (cherry picked from commit 8cd3f5a8536aa29a2280db24777c9a566d5accd6) + + plug-ins/file-webp/file-webp-save.c | 7 ------- + 1 file changed, 7 deletions(-) + +commit 39672c855958b09d88bf580bfdc82a3290f2c10a +Author: Jehan +Date: Thu Jun 6 16:16:38 2019 +0200 + + plug-ins: add the 8-bit linear with no assigned profile exception... + + ... in JPEG export. + Same as the WebP export, which is quite similar (8-bit max format), + when + no profile was explicitly set, we want to convert any data from + storage + format to non-linear (unlike when exporting high bit depth formats, + such + as TIFF). + We only make an exception for 8-bit linear. This commit adds this + exception. + + (cherry picked from commit 0461022198624b1bb1fafcd2d886cd015e7faa22) + + plug-ins/file-jpeg/jpeg-save.c | 61 + +++++++++++++++++++++++++----------------- + 1 file changed, 36 insertions(+), 25 deletions(-) + +commit bb9571db601930e318b6244cdb7f5bca49c1f44e +Author: Jehan +Date: Thu Jun 6 16:06:59 2019 +0200 + + plug-ins: remove the "Advanced Options" expander in WebP export + dialog. + + Similarly to other export dialogs, we don't consider anymore + metadata or + profile writing as being advanced options. Since these were the only + options shown in our current WebP export dialog, I removed the + expander + altogether and moved everything out. + + See also commit 540cfa961171a2f8d26c183ce2291f4c507e9813. + + (cherry picked from commit 26ba915ca554693076324e69f59c071c6f1f1708) + + plug-ins/file-webp/file-webp-dialog.c | 23 +++-------------------- + 1 file changed, 3 insertions(+), 20 deletions(-) + +commit 5c8860bd49f8919b2da63125c19ebf7995f3e68e +Author: Jehan +Date: Thu Jun 6 16:01:29 2019 +0200 + + plug-ins: fix profile writing in save_animation() in WebP export. + + Profile and output linearity decision was doubled as save_layer() and + save_animation() and I realize that I forgot to update the + save_animation() one in earlier commits. + + To avoid code copy-paste and divergence, I added an internal + webp_decide_output() function which I use in both aforementionned + calls. + + (cherry picked from commit 6712228e5e65ac26b2e245937ab0b21940ae4ca1) + + plug-ins/file-webp/file-webp-save.c | 127 + ++++++++++++++++++++---------------- + 1 file changed, 71 insertions(+), 56 deletions(-) + +commit 0aad97cc1838cf2cb3734820bd465e7717b9d09e +Author: Jehan +Date: Thu Jun 6 15:29:07 2019 +0200 + + plug-ins: add a "Save color profile" checkbox in WebP export. + + Unlike most other formats, it didn't have one yet! + + (cherry picked from commit 96e37dfdaf796fc804bf12e4eab1e32ed27f6ca9) + + plug-ins/file-webp/file-webp-dialog.c | 9 +++++ + plug-ins/file-webp/file-webp-save.c | 66 + ++++++++++++++++++++--------------- + plug-ins/file-webp/file-webp-save.h | 1 + + plug-ins/file-webp/file-webp.c | 7 ++-- + 4 files changed, 51 insertions(+), 32 deletions(-) + +commit 1bdac39a9251e68d9cbe0c96cadfcff5f54f0333 +Author: Jehan +Date: Thu Jun 6 14:33:40 2019 +0200 + + plug-ins: improve decision on exported data format in Webp. + + Same as other formats, we make sure that an explicitly set profile TRC + is always followed. When no profile is set, we always export as sRGB + when there is precision downsizing to avoid shadow posterization, + since + WebP stores 8-bit max per channel (as far as I know, or at least + as far + as we implemented it in our plug-in). + We only take the storage format into account when it is 8-bit linear + with profile export. In such case, we avoid conversion from 8-bit + linear + to 8-bit sRGB. + + This is different to TIFF export for instance, where we fallback on + following storage format TRC as we are able to export all sort of + precisions, hence with minimum quality loss. + + (cherry picked from commit 2a1eabc1777c7c25b36b13ae3a48adf8311e6074) + + plug-ins/file-webp/file-webp-save.c | 48 + ++++++++++++++++++++++++------------- + 1 file changed, 31 insertions(+), 17 deletions(-) + +commit a11cc8a63d03e0999cd26a185784a8e3782b63b3 +Author: Jehan +Date: Wed Jun 5 17:16:43 2019 +0200 + + plug-ins: improve decision of exported data format in TIFF export. + + Same as we did for PNG and JPEG, we want to always export data + with the + TRC of a profile manually assigned, and in particular NOT the curve of + the storage in GIMP (i.e. you may work on 32-bit linear, yet if you + assigned a sRGB TRC profile, this is what you should export). The + storage format will only dictate the exported data TRC when writing + out + the default profile (i.e. no explicit profile assignement). + As a last case, when no profile is saved, we always export as sRGB. + + As for the export precision, we always follow the storage one's, + except + sometimes for 8-bit images. We promote 8-bit images to 16-bit if and + only if the storage does not match the export data linearity, to avoid + too much quality loss during format conversion. + + (cherry picked from commit 8d9e70011d493e82632e9b1195872274ed4bfdec) + + plug-ins/file-tiff/file-tiff-save.c | 93 + +++++++++++++------------------------ + 1 file changed, 32 insertions(+), 61 deletions(-) + +commit 7acb6f36abd4034988ab026142885f0a05ed9b14 +Author: Ell +Date: Fri Jun 7 03:53:23 2019 -0400 + + app: indentation fix in gimp:offset + + (cherry picked from commit 8c1567f901af20b7104f34844066b16b54f2cdf4) + + app/operations/gimpoperationoffset.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +commit 618b6a62bd0ce7bc276ad20053d050e4037f0c69 +Author: Ell +Date: Fri Jun 7 03:45:17 2019 -0400 + + app: in gimp:offset, fix OFFSET_TRANSPARENT fast-path for chunked + input + + (cherry picked from commit 533091055ee75ea406f335a99f6d0b397bab1d31) + + app/operations/gimpoperationoffset.c | 41 + ++++++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 16 deletions(-) + +commit 257e35dda15ddfc24a8ae6c257acf39997dc29c9 +Author: Ell +Date: Thu Jun 6 04:47:46 2019 -0400 + + app: avoid duplicate offset actions + + Blacklist the "tools-offset" action in the GUI, and only keep + "filters-offset", to avoid duplication. Update gimp:offset's + description, so that "filters-offset" gets a proper tool-tip. + + (cherry picked from commit 42d4255262740add4d6228d4f32299f1594c09d5) + + app/operations/gimpoperationoffset.c | 6 ++++-- + app/widgets/gimpaction.c | 1 + + po/POTFILES.in | 1 + + 3 files changed, 6 insertions(+), 2 deletions(-) + +commit 0d7a57d7820667bc2c399310dd9d89dbbc4e5008 +Author: Ell +Date: Thu Jun 6 02:58:48 2019 -0400 + + app: add GimpFilterTool::region_changed() virtual function + + Add a new GimpFilterTool::region_changed() virtual function, which + gets called whenever the filter region changes, either due to a + change to the tool's "region" option, or a change to the image + mask. + + Override GimpFilterTool::region_changed() in GimpOperationTool and + GimpOffsetTool, instead of listening to a change to the "region" + option in GimpTool::options_notify(), so that the tools are + properly updated when the image mask changes. + + (cherry picked from commit 066827e23c4e72a7483061de1ce28da393f4692a) + + app/tools/gimpfiltertool.c | 23 ++++++++++++-- + app/tools/gimpfiltertool.h | 1 + + app/tools/gimpoffsettool.c | 47 ++++++++++++----------------- + app/tools/gimpoperationtool.c | 70 + ++++++++++++++++++------------------------- + 4 files changed, 70 insertions(+), 71 deletions(-) + +commit 3af7c508ad93c5d6190580e5a993ff49765ea5e1 +Author: Ell +Date: Thu Jun 6 02:56:23 2019 -0400 + + app: in gimp_filter_tool_get_drawable_area(), don't return empty area + + In gimp_filter_tool_get_drawable_area(), when the image mask + doesn't intersect the drawable, return a minimal area, instead of + an empty/invalid area. + + (cherry picked from commit 428ee0e2ad9327145860787628aebb159a19a9b9) + + app/tools/gimpfiltertool.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +commit 175e3ea7d909e2a768854e8c7fcba6e9f08cf9c1 +Author: Ell +Date: Thu Jun 6 02:20:27 2019 -0400 + + app: various fixes/cleanups to last commits + + (cherry picked from commit 30429e30e27f5a82d067550509a023bb221cdd47) + + app/core/gimpdrawable-offset.c | 17 +++++++++++++++++ + app/operations/gimpoperationoffset.c | 10 ++++++++-- + app/widgets/gimphelp-ids.h | 1 - + devel-docs/app/app-sections.txt | 5 ----- + 4 files changed, 25 insertions(+), 8 deletions(-) + +commit ca4612cc438d6bb6e8c92248bcc49874af43dec3 +Author: Ell +Date: Wed Jun 5 18:09:28 2019 -0400 + + Issue #40 - Layer offset tool + + Add a new Offset filter tool, as a front-end to gimp:offset. The + tool replaces, and provides the same interface as, the drawable- + offset dialog, while also providing live preview and on-canvas + interaction. + + Note that we don't simply use a custom propgui constructor for + gimp:offset, since we need a little more control. + + (cherry picked from commit 3a4a00c71e2d40d5e1ec2c9cf7ed2eb47e8db16e) + + app/actions/drawable-actions.c | 10 +- + app/actions/drawable-commands.c | 64 --- + app/actions/drawable-commands.h | 2 - + app/actions/filters-actions.c | 6 + + app/actions/gimpgeglprocedure.c | 4 + + app/dialogs/Makefile.am | 2 - + app/dialogs/dialogs.c | 1 + + app/dialogs/offset-dialog.c | 343 ------------ + app/dialogs/offset-dialog.h | 38 -- + app/tools/Makefile.am | 2 + + app/tools/gimp-tools.c | 4 +- + app/tools/gimpoffsettool.c | 796 + +++++++++++++++++++++++++++ + app/tools/gimpoffsettool.h | 63 +++ + app/widgets/gimphelp-ids.h | 1 + + icons/Color/16/gimp-tool-offset.png | Bin 0 -> 792 bytes + icons/Color/24/gimp-tool-offset.png | Bin 0 -> 1140 bytes + icons/Color/24/gimp-tool-offset.svg | 293 ++++++++++ + icons/Color/color-scalable.svg | 266 ++++++++- + icons/Color/scalable/gimp-tool-offset.svg | 246 +++++++++ + icons/Symbolic/16/gimp-tool-offset.png | Bin 0 -> 567 bytes + icons/Symbolic/24/gimp-tool-offset.png | Bin 0 -> 723 bytes + icons/Symbolic/24/gimp-tool-offset.svg | 161 ++++++ + icons/Symbolic/scalable/gimp-tool-offset.svg | 162 ++++++ + icons/Symbolic/symbolic-scalable.svg | 70 ++- + icons/icon-list.mk | 4 + + libgimpwidgets/gimpicons.h | 1 + + menus/image-menu.xml.in | 2 +- + po/POTFILES.in | 2 +- + 28 files changed, 2074 insertions(+), 469 deletions(-) + +commit 003b138ee6164c91599e869276a4e1533b2ac87a +Author: Ell +Date: Wed Jun 5 18:01:18 2019 -0400 + + app: implement gimp_drawable_offset() in terms of gimp:offset + + Implement gimp_drawable_offset() in terms of gimp:offset, added in + the previous commit. Other than avoiding duplication, this also + allows gimp_drawable_offset() to respect the current selection and + component mask (see issue #39.) + + (cherry picked from commit 5b2f3980bdf1417234f77d4cce46d6957481b6be) + + app/core/gimpdrawable-offset.c | 216 + +++-------------------------------------- + 1 file changed, 14 insertions(+), 202 deletions(-) + +commit f7a1aec1e9c507fd2a9d64ef2286c0669ed648c7 +Author: Ell +Date: Wed Jun 5 17:53:16 2019 -0400 + + app, libgimpbase: add gimp:offset operation + + Add a new gimp:offset operation, which implements equivalent + functionality to gimp_drawable_offset(), in preparation for adding + an interactive offset tool. + + To simplify things, add a GIMP_OFFSET_WRAP_AROUND value to the + GimpOffsetType enum, to avoid the need for a separate wrap-around + flag. This makes the gimp-drawable-offset procedure parameters a + little superfluous, but whatever. + + (cherry picked from commit 40fefb6076cfe1938dd72d32aaba209ec7360907) + + app/operations/Makefile.am | 2 + + app/operations/gimp-operations.c | 2 + + app/operations/gimpoperationoffset.c | 480 + +++++++++++++++++++++++++++++++++++ + app/operations/gimpoperationoffset.h | 55 ++++ + libgimpbase/gimpbaseenums.c | 2 + + libgimpbase/gimpbaseenums.h | 4 +- + pdb/enums.pl | 6 +- + 7 files changed, 548 insertions(+), 3 deletions(-) + +commit fce4e98097f96c1ccae7c93f5efa980043399de9 +Author: Ell +Date: Mon Jun 3 12:36:36 2019 -0400 + + plug-ins: in file-tiff, don't leak image when not saving layers + + In file-tiff, when not saving layers, avoid duplicating the image + to create the merged version if it's already been duplicated, and + make sure to delete the duplicated image otherwise. + + (cherry picked from commit 8831ef2ea099c2224102105a8148c4c0a9687a11) + + plug-ins/file-tiff/file-tiff.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +commit 186236ff350e2aafa44e326dc1f6becbdbbbc215 +Author: Ell +Date: Mon Jun 3 09:55:37 2019 -0400 + + app: update drawable when committing Warp tool + + Make sure to update the drawable after committing the Warp tool, if + high-quality-preview is disabled, and we use a non-nearest sampler. + + Necessary after commit d928a80b7faf48f637315b89eb6107647ff9d05b. + + (cherry picked from commit 2da5cb562d24f18dc4b4140d3111f5a6ea13740c) + + app/tools/gimpwarptool.c | 14 ++++---------- + 1 file changed, 4 insertions(+), 10 deletions(-) + +commit eae506a9dadda3b56b6c4321b9c54e1dcfc80c90 +Author: Marco Ciampa +Date: Mon Jun 3 11:17:48 2019 +0200 + + Updated Italian translation + + po/it.po | 193 + +++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 108 insertions(+), 85 deletions(-) + +commit e905f84a52df21285d2b50ad7de94efb78f5ec42 +Author: Michael Natterer +Date: Sun Jun 2 15:08:36 2019 +0200 + + Issue #2057 - All tool presets change FG, BG and Pallete color... + + ...even when not supposed to + + Don't use gimp_config_copy() to set the tool options from the ones + stored in the preset. Instead, add utility function + tool_manager_copy_tool_options() that only copies properties of + GimpToolOptions and its subclasses. + + Simply set the tool, copy the tool options' context properties, then + copy the tool options' own properties and done. Much more obvious and + works. + + (cherry picked from commit a14834e3366429670295cc98b6b6d130006a1b22) + + app/tools/tool_manager.c | 100 + +++++++++++++++++++++++++---------------------- + 1 file changed, 53 insertions(+), 47 deletions(-) + +commit 46f94d2c7577671e40928f1a0b9cf1c0177baedc +Author: Michael Natterer +Date: Sun Jun 2 15:04:38 2019 +0200 + + app: remove redundant assignment in gimptoolpreset.c + + (cherry picked from commit 8d1c36d9ce2e39aabab0e1dba4801f727bc19da8) + + app/core/gimptoolpreset.c | 1 - + 1 file changed, 1 deletion(-) + +commit bf18c3162f80dbdf362f306e3947ea1362d3523b +Author: Piotr Drąg +Date: Sun Jun 2 14:09:26 2019 +0200 + + Update Polish translation + + po-plug-ins/pl.po | 253 ++++++++++++++++---------------- + po/pl.po | 427 + ++++++++++++++++++++++++++++-------------------------- + 2 files changed, 354 insertions(+), 326 deletions(-) + +commit 645d987e07ec1df3594f43a87a5b1674f05cd02c +Author: Michael Natterer +Date: Sun Jun 2 03:25:49 2019 +0200 + + Issue #2957 - Gimp crashes when I attempt to change the icon size + + Simplify GimpDockbook's signal connect/disconnect to + GimpGuiConfig::size-changed a lot, most likely to a point where some + connection doesn't get leaked, so this bug is probably fixed. + + app/widgets/gimpdockbook.c | 38 +++++++++++++------------------------- + 1 file changed, 13 insertions(+), 25 deletions(-) + +commit 9db555149f35c45c17255bc9086d5f9bcb1ce312 +Author: Ell +Date: Sat Jun 1 13:18:14 2019 -0400 + + app: avoid unnecessarily updating drawable after merging filter + + In gimp_drawable_merge_filter(), add an "update" parameter, which + specifies whether to update the affected region of the drawable + after applying the filter. Avoid updating the drawable when + commiting a GimpDrawableFilter (and manually update the drawable if + filter application was cancelled), and when anchoring a floating + selection, since in both cases the relevant region of the drawable + has already been updated. + + (cherry picked from commit d928a80b7faf48f637315b89eb6107647ff9d05b) + + app/core/gimpdrawable-filters.c | 12 ++++++++---- + app/core/gimpdrawable-filters.h | 3 ++- + app/core/gimpdrawablefilter.c | 6 +++++- + app/core/gimplayer-floating-selection.c | 2 +- + 4 files changed, 16 insertions(+), 7 deletions(-) + +commit 99ae9a32da55c1a5cd3426bddd9d2ebcbe78f583 +Author: Balázs Úr +Date: Sat Jun 1 16:58:25 2019 +0000 + + Update Hungarian translation + + po-plug-ins/hu.po | 234 + +++++++++++++++++++++++++++--------------------------- + 1 file changed, 119 insertions(+), 115 deletions(-) + +commit a624010931b21fe4d77328df8742d3636530afdb +Author: Michael Natterer +Date: Sat Jun 1 17:37:49 2019 +0200 + + Issue #2194 - Action search dialog behaves as a full window... + + ...using a tiling window manager + + Set GimpSearchPopup's type hint to GDK_WINDOW_TYPE_HINT_DIALOG. + + (cherry picked from commit e6364ffa81061a844483e392aaccedf6f5979a31) + + app/widgets/gimpsearchpopup.c | 1 + + 1 file changed, 1 insertion(+) + +commit dd3529c0e97947a6f9e667a4b96e1c22099c7e7b +Author: Michael Natterer +Date: Sat Jun 1 16:41:59 2019 +0200 + + libgimpwidgets: keep gimpwidgetsenums.h in alphabetical order + + (cherry picked from commit 2f925e72771298938c35d37b17db506c589fa12c) + + libgimpwidgets/gimpwidgetsenums.c | 64 + +++++++++++++++++++-------------------- + libgimpwidgets/gimpwidgetsenums.h | 44 ++++++++++++++------------- + 2 files changed, 55 insertions(+), 53 deletions(-) + +commit ef6bb9d4ade2cf6bd9cd5b55e83d4412dc4c0c93 +Author: Michael Natterer +Date: Sat Jun 1 14:19:05 2019 +0200 + + Issue #2643 - Conversion to grayscale fails with artifacts + + Connect GimpDisplayShell to GimpImage::mode-changed and update its + cached color transforms. Also get rid of a duplicate conection to + GimpImage::precision-changed. + + (cherry picked from commit a1aa17943633e5cfd6b09184f0ee4f43b4093c2b) + + No idea why the artifacty never appeared on 2.10, the code was just as + broken as in master... + + app/display/gimpdisplayshell-handlers.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +commit fe9c1732b962ff1772829130a5849559ce994023 +Author: Ell +Date: Fri May 31 13:02:51 2019 -0400 + + app: add "alpha" propertry to gimp:mask-components + + In gimp:mask-components, add an "alpha" property, which controls + the masked-in alpha value in case there's no aux buffer. Set it to + 0 by default, so that gimp:mask-components behaves normally in the + absence of an aux buffer (as if the aux buffer was empty). Set it + to 1 in the image's visible-mask node, to maintain the current + alpha-component visibility behavior. + + This fixes incorrect results when the output bounding box of a + drawable filter is smaller than the drawable, which can lead to a + NULL aux buffer being fed to the filter's gimp:mask-components + node. + + (cherry picked from commit 6425bf820a6cb2e18dc19d71df28e4246f1f87cc) + + app/core/gimpimage.c | 1 + + app/operations/gimpoperationmaskcomponents.cc | 70 + ++++++++++++++++----------- + app/operations/gimpoperationmaskcomponents.h | 4 +- + 3 files changed, 46 insertions(+), 29 deletions(-) + +commit e457ddf99354673e9078dc69388f8a8a4265e6a2 +Author: Ell +Date: Fri May 31 12:59:17 2019 -0400 + + Revert "app: avoid NULL output in layer-mode ops" + + On a second thought... nope :) We'll fix it another way. + + This reverts commit 60947b7a34826657395ca485d334ccb302106e07. + + (cherry picked from commit 3766af9ac9ff3bb595243235c585e07425455c22) + + app/operations/layer-modes/gimpoperationlayermode.c | 7 +------ + app/operations/layer-modes/gimpoperationreplace.c | 11 ++--------- + 2 files changed, 3 insertions(+), 15 deletions(-) + +commit 818570d6cc55c052fa1cf4f55b2cefe9d707dd6c +Author: Ell +Date: Fri May 31 12:19:21 2019 -0400 + + app: avoid NULL output in layer-mode ops + + In GimpOperationLayerMode and GimpOperationReplace, make sure we + don't return a NULL output buffer, or forward a NULL input buffer, + but rather create an appropriate empty buffer in this case. This + avoids wrong results when the layer-mode op's output is connected + to the aux input of a subsequent op, as a result of the op behaving + differently with a NULL aux buffer (in particular, this can happen + when a drawable filter's output bounding box is smaller than the + drawable.) + + (cherry picked from commit 8fcac3298c4947fa1535ae11d6e268e6760d6156) + + app/operations/layer-modes/gimpoperationlayermode.c | 7 ++++++- + app/operations/layer-modes/gimpoperationreplace.c | 11 +++++++++-- + 2 files changed, 15 insertions(+), 3 deletions(-) + +commit c8fb30ab3c8df96e43ab123f60b192092fd57885 +Author: Michael Natterer +Date: Fri May 31 15:44:07 2019 +0200 + + Issue #3057 - Retain last user-chosen ICC profile information... + + ...when generating "TRC variants" + + When creating a new profile with different TRC from an existing + profile, keep all the original profile's description, model, + manufacturer and copyright strings around, but prefix them with "GIMP + from " or similar to indicate that they are different. Also make sure + we don't prefix strings with GIMP stuff multiple times when profiles + are generated repeatedly. + + (cherry picked from commit 3cad4aa4c2f33661d68e31fb5a4fdcd7f8063fb2) + + libgimpcolor/gimpcolorprofile.c | 100 + +++++++++++++++++++++++++++++----------- + 1 file changed, 73 insertions(+), 27 deletions(-) + +commit 8d05d018209a3ca2a6cd8c2b9cb6894edba6114e +Author: Michael Natterer +Date: Thu May 30 16:44:23 2019 +0200 + + app: fix undoing image parasite attach/detach to emit the right + signals + + Add "gboolean push_undo" parameters to gimp_image_parasite_attach() + and _detach() and use the API also from undo, instead of implementing + attaching/removing manually and forgetting about the signals. + + Fixes updating of the image properties color profile page. + + (cherry picked from commit 710cfc1f478a2d9b75b622ee1445972feeb19c9a) + + app/actions/image-commands.c | 2 +- + app/core/gimp.c | 2 +- + app/core/gimpimage-color-profile.c | 6 +++--- + app/core/gimpimage-new.c | 2 +- + app/core/gimpimage.c | 27 +++++++++++++++------------ + app/core/gimpimage.h | 6 ++++-- + app/core/gimpimageundo.c | 12 ++---------- + app/core/gimpitem.c | 2 +- + app/file-data/file-data-gbr.c | 2 +- + app/file-data/file-data-gih.c | 6 +++--- + app/file-data/file-data-pat.c | 2 +- + app/pdb/image-cmds.c | 4 ++-- + app/pdb/plug-in-compat-cmds.c | 4 ++-- + app/widgets/gimpimagecommenteditor.c | 4 ++-- + app/xcf/xcf-load.c | 2 +- + pdb/groups/image.pdb | 4 ++-- + pdb/groups/plug_in_compat.pdb | 4 ++-- + 17 files changed, 44 insertions(+), 47 deletions(-) + +commit 29ef3742280eb197765b6c481aeb571b305c21cc +Author: Michael Natterer +Date: Thu May 30 16:07:57 2019 +0200 + + app: make sure builtin profiles *really* don't get attached to images + + gimp_image_parasite_attach(): when we detected that a builtin profile + is about to be attached, actually bail out after removing the old + profile, instead of continuing to attaching the builtin profile + anyway. Gah... + + (cherry picked from commit 07ffef38c360597a45de36db0fba137e39cee05a) + + app/core/gimpimage.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +commit 20b47618d01cd9097cfa792cd7987fdb07e21bc1 +Author: Michael Natterer +Date: Thu May 30 11:47:16 2019 +0200 + + app: use the DEFAULT_USE_PATTERN define instead of TRUE in GimpPreset + + (cherry picked from commit e7307194cffb27a79418bc43dcad2fbb17f85bd9) + + app/core/gimptoolpreset.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 0c4c1f1a8cc3b0500b80f32fccbf17fdf2574b7e +Author: Ell +Date: Thu May 30 03:30:32 2019 -0400 + + app: s/gimp_list_compare()/gimp_g_list_compare()/ + + ... so we don't clash with the GimpList namespace. + + (cherry picked from commit 8ef461bb7007e3d72a6561740d9310941054ed36) + + app/core/gimp-utils.c | 4 ++-- + app/core/gimp-utils.h | 2 +- + app/tools/gimpmovetool.c | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +commit 80ea17d90b8ad7ace63d263d2f202092c3058596 +Author: Ell +Date: Thu May 30 02:41:43 2019 -0400 + + app: fix memory leak in previous commit + + (cherry picked from commit 08ebcce2fa43be8e14de459bdeb2ae18568446e9) + + app/tools/gimpmovetool.c | 4 ++++ + 1 file changed, 4 insertions(+) + +commit 38e954ba253f5816749603851441b2221d92a6d8 +Author: Ell +Date: Thu May 30 01:39:26 2019 -0400 + + app: allow moving an intersecting pair of guides with the Move tool + + This commit adds support for moving together an intersecting pair + of guides using the Move tool, by dragging the guides at their + point of intersection. This is useful when the guides are used to + mark a point, rather than a pair of lines (e.g., as is the case for + the mandala symmetry guides, which mark the symmetry's point of + origin). + + Add gimp_image_pick_guides(), which can return a set of guides, + rather than a single guide. The API allows an arbitrary set of + guides to be returned, but, in practice, at most two intersecting + guides are returned, as per the above. + + In GimpMoveTool and GimpGuideTool, add support for moving multiple + guides together, and, in GimpMoveTool, use gimp_image_pick_guides() + to potentially pick multiple guides. + + (cherry picked from commit 1e95481feb432afc0589b264a3d1635f8caf3883) + + app/core/gimpimage-pick-item.c | 83 +++++++-- + app/core/gimpimage-pick-item.h | 6 + + app/tools/gimpguidetool.c | 410 + ++++++++++++++++++++++++++++------------- + app/tools/gimpguidetool.h | 37 ++-- + app/tools/gimpmovetool.c | 63 ++++--- + app/tools/gimpmovetool.h | 2 +- + 6 files changed, 415 insertions(+), 186 deletions(-) + +commit 4dd3e73aaac65e18972358caa6b3ef924f14547f +Author: Ell +Date: Thu May 30 01:37:32 2019 -0400 + + app: add gimp_list_compare() + + ... which lexicographically (shallowly) compares a pair of GLists. + + (cherry picked from commit c4ce70a2d3054f811ea046577d8f79192f74f75a) + + app/core/gimp-utils.c | 23 +++++++++++++++++++++++ + app/core/gimp-utils.h | 3 +++ + 2 files changed, 26 insertions(+) + +commit 7b4ef46fe485ad9ccdfb5f907367416ae9840935 +Author: Sabri Ünal +Date: Wed May 29 22:39:37 2019 +0000 + + Issue #3119 - Two tooltips were marked as translatable + + (cherry picked from commit b1c113c444f94a7f95da5c9febb47fa782693cb8) + + app/config/gimprc-blurbs.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit fb03526f92f1c8a73ad1d65b48c43aad8afe2ce9 +Author: Sabri Ünal +Date: Wed May 29 22:24:06 2019 +0000 + + Issue #3105 - "Maximum Image Size" changed to "Maximum new image size" + + Because of the real sentence is "Maximum new image size", this patch + was necessary. + + app/dialogs/image-new-dialog.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 159a0a779f9f2330aac8b892506326b9ee668a86 +Author: Michael Natterer +Date: Wed May 29 23:35:13 2019 +0200 + + Issue #2986 - Input controller configuration window is too small + by default + + Set a minimum size on the event list's scrolled window. + + (cherry picked from commit 476cf197478d8597e02be0fbe837eec1022c427b) + + app/widgets/gimpcontrollereditor.c | 1 + + 1 file changed, 1 insertion(+) + +commit b5159905e0ac266119f7e28bbcf5c126f08f868e +Author: Michael Natterer +Date: Wed May 29 16:12:21 2019 +0200 + + Issue #263 - Add a "Save Keyboard Shortcuts Now" button... + + ...to the Configure Keyboard Shortcuts dialog + + Add the button, based on an old patch from Sven Neumann, and make the + buttons in the keyboard shortcuts and input devices dialogs look and + behave the same. + + (cherry picked from commit 04b69e2494ac0f54e71d9fef84ffdab45a0cfa7d) + + app/dialogs/input-devices-dialog.c | 12 ++++------ + app/dialogs/keyboard-shortcuts-dialog.c | 41 + ++++++++++++++++++++++++++++++--- + 2 files changed, 43 insertions(+), 10 deletions(-) + +commit 92bec259ac426ea70f8e6007a01742c020e8a49a +Author: Ell +Date: Wed May 29 05:35:30 2019 -0400 + + app: add support for offset buffers in + gimp_gegl_apply_[cached_]operation() + + In gimp_gegl_apply_[cached_]operation(), add support for output + buffers whose extent's top-left corner is not (0, 0). This is + needed by the previous commit. + + (cherry picked from commit a6393e6c5518f6b563b9179d6f5c76b07d2822a2) + + app/gegl/gimp-gegl-apply-operation.c | 26 +++++++++----------------- + 1 file changed, 9 insertions(+), 17 deletions(-) + +commit df06c062cd5d9790d871e8b0a841f3ed8c71ec76 +Author: Ell +Date: Wed May 29 04:58:17 2019 -0400 + + app: fix symmetry transform in Clone/Heal tools when brush is cropped + + In GimpSourceCore, when applying a symmetry transform to the source + content, combine the transform op with translation to the paint- + buffer coordinates, so that subclasses (namely, GimpClone and + GimpHeal) can use the op to apply the transformation directly from + the source buffer to the paint buffer in a single step. This is + both more efficient, and avoids incorrect symmetry transforms when + the paint buffer is cropped to the bounds of the drawable. + + (cherry picked from commit a701032b4ea9e3588aea38de5d447db5091e3ccf) + + app/paint/gimpclone.c | 35 ++++++++++++++++++------------ + app/paint/gimpheal.c | 30 ++++++++++++-------------- + app/paint/gimpsourcecore.c | 54 + +++++++++++++++++++++++++++++++++++++++++----- + 3 files changed, 84 insertions(+), 35 deletions(-) + +commit c75ba806beacdacc4b22d95be802c246ba7113b7 +Author: Ell +Date: Wed May 29 04:46:21 2019 -0400 + + app: implement gimp_symmetry_get_operation() in terms of + gimp_symmetry_get_matrix() + + Remove the GimpSymmetry::get_operation() virtual function, and + instead implement gimp_symmetry_get_operation() by returning an + appropriate gegl:transform node based on the matrix returned by + gimp_symmetry_get_matrix(). The returned node is owned by the + caller; since we no longer use the node's identity for caching + trnasformed brushes, we no longer cache the transformation nodes. + + Remove the function's paint_width and paint_height parameters, and + instead return a transformation that should be applied at the + center of the brush. This simplifies the application of the + transformation in the Clone and Heal tools, as per the next commit. + + Remove the implementation of GimpSymmetry::get_operation() from all + its subclasses, which should now only implement + GimpSymmetry::get_transform(). + + (cherry picked from commit 60a3965020de8cb1a768b05506fe8f4096e6085c) + + app/core/gimpsymmetry-mandala.c | 81 + -------------------------------------- + app/core/gimpsymmetry-mandala.h | 5 --- + app/core/gimpsymmetry-mirror.c | 87 + ----------------------------------------- + app/core/gimpsymmetry-mirror.h | 25 +++++------- + app/core/gimpsymmetry.c | 76 +++++++++++++++-------------------- + app/core/gimpsymmetry.h | 10 +---- + 6 files changed, 43 insertions(+), 241 deletions(-) + +commit 3868733ea38c48ac9d3420e0e03b38c768585580 +Author: Ell +Date: Wed May 29 04:37:29 2019 -0400 + + app: add gimp_gegl_create_transform_node() + + ... which takes a GimpMatrix3, and returns a corresponding + gegl:transform node. + + (cherry picked from commit 3895dc07a95195a7ed5fcc30aa2ddc4f2deafd01) + + app/gegl/gimp-gegl-nodes.c | 16 ++++++++++++++++ + app/gegl/gimp-gegl-nodes.h | 2 ++ + 2 files changed, 18 insertions(+) + +commit 2025bbed656c9be2f1f64b218e0c726ea4b1e3d1 +Author: Ell +Date: Wed May 29 04:42:26 2019 -0400 + + app: use gimp_symmetry_get_matrix() in GimpInk + + ... instead of calculating the matrix manually. + + (cherry picked from commit 117734f45f556bb833be7722b5fbb74eaa837e9b) + + app/paint/gimpink.c | 124 + ++++++++++++++++++++++++---------------------------- + 1 file changed, 56 insertions(+), 68 deletions(-) + +commit a474e695732b758464288eb8169caf72351aea6c +Author: Ell +Date: Wed May 29 04:41:10 2019 -0400 + + app: add gimp_symmetry_get_matrix() + + ... which returns the brush transform corresponding to a given + GimpSymmetry stroke as a GimpMatrix3, as per + gimp_symmetry_get_transform(). + + (cherry picked from commit cd7bcd081b06e0211299d0a1d6eff0b7b80e7e9a) + + app/core/gimpsymmetry.c | 29 +++++++++++++++++++++++++++++ + app/core/gimpsymmetry.h | 3 +++ + 2 files changed, 32 insertions(+) + +commit bf6489f727fa93a393154fc26c289cff27fdcb6f +Author: Ell +Date: Wed May 29 04:25:38 2019 -0400 + + app: don't add fg color to history in GimpEraser + + Add GimpPaintbrush::get_color_history_color() virtual function, + which should return the color to be added to the color history upon + painting, if any. The default implementation returns the + foreground color, when not using a pixmap brush or color-from- + gradient. + + Override this function in GimpEraser, to return the background + color when the affected drawable has no alpha channel, instead of + overriding GimpPaintCore::paint() for this purpose. This avoids + erroneously adding both the background and foreground colors to the + history. + + (cherry picked from commit 86f4d4d41d5c27677beb041d1fe8b2f32bf781e1) + + app/paint/gimperaser.c | 81 + +++++++++++++++++----------------------------- + app/paint/gimppaintbrush.c | 80 + +++++++++++++++++++++++++++++---------------- + app/paint/gimppaintbrush.h | 20 +++++++----- + 3 files changed, 93 insertions(+), 88 deletions(-) + +commit fa173b967543b2f89e6c11f6b6f2d943cdad9d3a +Author: Michael Natterer +Date: Tue May 28 17:37:08 2019 +0200 + + libgimpcolor: better error messages in GimpColorProfile and + GimpColorTransform + + (cherry picked from commit 890b554230fda231689c3f791f051877e7e948b5) + + libgimpcolor/gimpcolorprofile.c | 2 +- + libgimpcolor/gimpcolortransform.c | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +commit 788c09259c43ab008212a7eff0ca886d5040415c +Author: Michael Natterer +Date: Tue May 28 15:05:05 2019 +0200 + + Issue #3132 - Missing accelerator on "Export Image as JPEG" dialog + + Change toggle button label to "Save color _profile". + + (cherry picked from commit 0be3fce4fb7ed7c37fcd4fd2a121925fcfd838fb) + + plug-ins/file-jpeg/jpeg-save.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d968eff2f6d7b3672ecb15f9ff09818de390ada2 +Author: Michael Natterer +Date: Tue May 28 11:55:59 2019 +0200 + + Issue #2794 - Gimp crash just on File Open and Edit Preferences + + As suggested by LRN, change gimp_sigfatal_handler() on Windows to + dstinguish between fatal and non-fatal exceptions so we don't abort on + each minor hickup. + + (cherry picked from commit 679fd5f23105f4dc3f4de140fa82470731bc9acf) + + app/Makefile.am | 6 +++++- + app/signals.c | 26 ++++++++++++++++++++------ + 2 files changed, 25 insertions(+), 7 deletions(-) + +commit 54a383685ecb8d16caac9ca64e63bd2730f855ef +Author: Michael Natterer +Date: Tue May 28 00:25:47 2019 +0200 + + app, plug-ins: make sure a GIH brush's spacing is preserved + + across load and save, by introducing a gimp-brush-pipe-spacing + parasite. + + (cherry picked from commit 0710051e2b822370e77169417867ef73f76335c4) + + app/file-data/file-data-gih.c | 10 ++++++++++ + plug-ins/common/file-gih.c | 18 ++++++++++++++++++ + 2 files changed, 28 insertions(+) + +commit 1b0a17c0ec1768073c8c1128f55b04437d4fccee +Author: Marco Ciampa +Date: Mon May 27 18:48:38 2019 +0200 + + Updated Italian translation + + po-libgimp/it.po | 497 +++++++++--------- + po-plug-ins/it.po | 256 ++++----- + po-windows-installer/it.po | 27 +- + po/it.po | 1225 + +++++++++++++++++++++++--------------------- + 4 files changed, 1051 insertions(+), 954 deletions(-) + +commit aea7f8e9435f044034de9b239540d633495485fa +Author: Piotr Drąg +Date: Mon May 27 18:18:35 2019 +0200 + + Update Polish translation + + po-windows-installer/pl.po | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit de568a0f61c6f867e09c13336960ca5dcf5b48b1 +Author: Michael Natterer +Date: Mon May 27 17:47:55 2019 +0200 + + app: use g_clear_pointer() in more places + + (cherry picked from commit 901350ba20be52c87469b997be104866fea80215) + + app/config/gimplangrc.c | 7 +-- + app/core/gimpbrushpipe.c | 3 +- + app/core/gimpchannel.c | 21 ++------ + app/core/gimpcurve.c | 16 ++---- + app/core/gimphistogram.c | 3 +- + app/core/gimplayer.c | 3 +- + app/core/gimpmybrush.c | 6 +-- + app/core/gimpviewable.c | 3 +- + app/display/gimpcanvasboundary.c | 14 ++--- + app/display/gimpcanvaspolygon.c | 17 ++---- + app/display/gimpcanvasprogress.c | 6 +-- + app/display/gimptoolrectangle.c | 6 +-- + app/pdb/gimpprocedure.c | 6 +-- + app/text/gimptext.c | 30 +++-------- + app/tools/gimpeditselectiontool.c | 29 +++-------- + app/tools/gimptextoptions.c | 6 +-- + app/tools/gimptexttool-editor.c | 3 +- + app/widgets/gimpactiongroup.c | 13 +---- + app/widgets/gimpactionview.c | 3 +- + app/widgets/gimpcolorhistory.c | 95 + +++++++++++++++++----------------- + app/widgets/gimpdockable.c | 31 ++--------- + app/widgets/gimpfiledialog.c | 24 ++------- + app/widgets/gimpimageparasiteview.c | 7 +-- + app/widgets/gimplanguageentry.c | 6 +-- + app/widgets/gimpmessagebox.c | 10 ++-- + app/widgets/gimpoverlaydialog.c | 13 +---- + app/widgets/gimpsearchpopup.c | 3 +- + app/widgets/gimpsessioninfo-dock.c | 6 +-- + app/widgets/gimpsessioninfo-dockable.c | 6 +-- + app/widgets/gimpstringaction.c | 6 +-- + app/widgets/gimptagentry.c | 6 +-- + app/widgets/gimptagpopup.c | 3 +- + app/widgets/gimptooleditor.c | 3 +- + app/widgets/gimpuimanager.c | 10 +--- + app/widgets/gimpviewablebutton.c | 20 ++----- + 35 files changed, 121 insertions(+), 323 deletions(-) + +commit 4208ff8147d1c24565b39f4417219f60b8dc4a2b +Author: Ell +Date: Mon May 27 11:07:20 2019 -0400 + + Issue #886 - Artifacts symmetry painting with big brushes + + In GimpClone and GimpHeal, use gimp_gegl_apply_operation(), instead + of gegl_node_process(), to apply the symmetry transform, which both + performs chunking and avoids aliasing problems. + + (cherry picked from commit 024bc4cd3fc5362e84f9270cc93fd23d51366c4c) + + app/paint/gimpclone.c | 21 ++++----------------- + app/paint/gimpheal.c | 20 +++----------------- + 2 files changed, 7 insertions(+), 34 deletions(-) + +commit 7a6926a50b3dc76485521abda37b3c5bdcda9a26 +Author: Michael Natterer +Date: Mon May 27 15:20:15 2019 +0200 + + Issue #3067 - "Make New from Visible" produces wrong results + + Call gimp_projectable_structure_changed() when the image's profile + changes so the projection buffer gets reallocated. + + (cherry picked from commit 1e07f00a9526ff348799e54b877b0895cb4f73ac) + + (Picking this is not strictly needed, but did it anyway because it + doesn't hurt and this commit is hard to miss if we finally merge space + invasion to stable). + + app/core/gimpimage.c | 1 + + 1 file changed, 1 insertion(+) + +commit 23c1e0328afcc00bc8de981031d451a809429c19 +Author: Michael Natterer +Date: Mon May 27 14:53:55 2019 +0200 + + Issue #3398 - colour map picker isn't positioned on the correct... + + ...-last positioned- monitor + + Use gimp_dialog_factory_position_dialog() in GimpColormapEditor and + GimpPaletteEditor so the color dialogs appear where they were before. + + (cherry picked from commit 765abcd316279c7f27420a05a7a8e3be8fa17215) + + app/widgets/gimpcolormapeditor.c | 7 +++++++ + app/widgets/gimppaletteeditor.c | 7 +++++++ + 2 files changed, 14 insertions(+) + +commit eb99c1a82fa02949242c6d55ba8838b81f44f13d +Author: Michael Natterer +Date: Mon May 27 14:15:09 2019 +0200 + + app: add gimp_dialog_factory_position_dialog() + + which is the logic that used to be in color_area_color_clicked() + factored out to proper API. It makes sure that a dialog that was + visible before (and was only hidden not destroyed) appears at the same + point on the same monitor as before. See issue #1093. + + (cherry picked from commit ebb6b08e621c302f55ec7209485f85270ccb3498) + + app/widgets/gimpdialogfactory.c | 58 + ++++++++++++++++++++++++++++++++++++ + app/widgets/gimpdialogfactory.h | 21 +++++++++++++ + app/widgets/gimptoolbox-color-area.c | 35 ++++------------------ + 3 files changed, 84 insertions(+), 30 deletions(-) + +commit 6a98b28a006af8c9f29ecb627339674a7fc4d2d5 +Author: Ell +Date: Mon May 27 02:13:36 2019 -0400 + + app: fix Ink tool symmetry transform + + ... to take tilt into account. + + (cherry picked from commit 39e1a6dfeae72900f0a8bf47fbcc6de1d31e9356) + + app/paint/gimpink.c | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +commit 6fa861cec6efc61e3b1aa9ad9840faf990ea51cf +Author: Michael Natterer +Date: Mon May 27 00:29:41 2019 +0200 + + app: minor formatting fixes in gimpdrawable-bucket-fill.c + + (cherry picked from commit c1cba759f879b08d2c6363c77c8754e3e4cfe86e) + + app/core/gimpdrawable-bucket-fill.c | 33 + +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +commit b2dac69b7ea12d19358f390fbd70708b4b3bef36 +Author: Ell +Date: Sun May 26 14:38:24 2019 -0400 + + app: derive GimpEraser from GimpPaintbrush + + In GimpPaintbrush, factor out the code responsible for determining + the current dab's paint parameters and content to a new + GimpPaintbrush::get_paint_params() virtual function. + + Derive GimpEraser from GimpPaintbrush, instead of directly from + GimpBrushCore, implementing get_paint_params() appropriately. + This allows GimpEraser to reuse the paint-buffer content across + dabs, improving performance. + + (cherry picked from commit aba4eef916172460dde062ac76c86cf6e1f71a52) + + app/paint/gimperaser.c | 148 +++++++++++--------------------------- + app/paint/gimperaser.h | 6 +- + app/paint/gimppaintbrush.c | 176 + ++++++++++++++++++++++++++++----------------- + app/paint/gimppaintbrush.h | 10 +++ + 4 files changed, 164 insertions(+), 176 deletions(-) + +commit eaef7dcf443db3f9e7f76b701fb5be84ee12d1d0 +Author: Ell +Date: Sun May 26 14:27:35 2019 -0400 + + app: add symmetry-transform support to the Ink tool + + In GimpInk, apply the current symmetry transform, as per + gimp_symmetry_get_transform(), to rendered blobs. + + (cherry picked from commit 3b56bd7a9aa7c9210bd469f688f021b835c11983) + + app/paint/gimpink.c | 44 +++++++++++++++++++++++++++++++++++--------- + 1 file changed, 35 insertions(+), 9 deletions(-) + +commit e961b13ae629d02f9224fa17aae145c65fa636fb +Author: Ell +Date: Sun May 26 14:21:09 2019 -0400 + + app: use gimp_symmetry_get_transform() in paint code + + Use gimp_symmetry_get_transform() instead of + gimp_symmetry_get_operation() throughout the paint code, where + possible. This allows us to combine the symmetry transform with + the ordinary brush transform, simplifying the code, improving + performance, and avoiding multiple resamplings. This also fixes + the paint-buffer size when using mandala symmetry with non-round + brushes, avoiding artifacts. + + (cherry picked from commit b63af476bd898c5c702653af8a1751466f6292c0) + + app/core/gimpbrush-boundary.c | 2 +- + app/core/gimpbrush.c | 75 ++++----------------------------- + app/core/gimpbrush.h | 2 - + app/core/gimpbrushcache.c | 25 +++++------ + app/core/gimpbrushcache.h | 2 - + app/paint/gimpbrushcore.c | 98 + ++++++++++++++++++++++++++++++++----------- + app/paint/gimpbrushcore.h | 20 +++++---- + app/paint/gimpclone.c | 3 +- + app/paint/gimpconvolve.c | 9 ++-- + app/paint/gimpdodgeburn.c | 21 ++++------ + app/paint/gimperaser.c | 20 ++++----- + app/paint/gimpheal.c | 4 +- + app/paint/gimppaintbrush.c | 13 +++--- + app/paint/gimpsmudge.c | 22 +++++----- + app/paint/gimpsourcecore.c | 5 ++- + 15 files changed, 148 insertions(+), 173 deletions(-) + +commit 85340f4b305d92d94ec2898cde074f0492fe4514 +Author: Ell +Date: Sun May 26 14:14:26 2019 -0400 + + app: add gimp_symmetry_get_transform() + + Add a GimpSymmetry::get_transform() virtual function, and a + corresponding gimp_symmetry_get_transform() function, which return + the brush transform corresponding to a given symmetry stroke in + terms of the rotation angle and reflection flag (in contrast to + gimp_symmetry_get_operation() which returns the same transforation + in terms of a GeglNode). This would allow us to simplify, fix, and + improve the painting-code perofmrnace in the next commits. + + Implement GimpSymmetry::get_transform() in its various subclasses. + + (cherry picked from commit e0f2a6f1be4514609bde5304be99b91182e31183) + + app/core/gimpsymmetry-mandala.c | 19 ++++++++++++++++ + app/core/gimpsymmetry-mirror.c | 49 + +++++++++++++++++++++++++++++++++++++++++ + app/core/gimpsymmetry-tiling.c | 15 ------------- + app/core/gimpsymmetry.c | 46 + ++++++++++++++++++++++++++++++++++++++ + app/core/gimpsymmetry.h | 8 +++++++ + 5 files changed, 122 insertions(+), 15 deletions(-) + +commit 80e426b65b00e40ce0d65ab41be351b5a017ff82 +Author: Tim Sabsch +Date: Sun May 26 13:00:12 2019 +0000 + + Update German translation + + po/de.po | 979 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 524 insertions(+), 455 deletions(-) + +commit 72c5d24fe91956a5572151ec036f5ad2d5f63cdc +Author: Jehan +Date: Sat May 25 14:01:55 2019 +0200 + + plug-ins: follow the assigned profile TRC in PNG export. + + Similar to JPEG export (commit + c5f7bac2ba2121636bbf84d5e4af4d11e98fa511) + as discussed with Ell. GIMP should follow and save as-is any + *assigned* + profile. We only make a decision about whether to convert from storage + precision to another format when the profile is the default GIMP one. + + plug-ins/common/file-png.c | 88 + ++++++++++++++++++++++++---------------------- + 1 file changed, 45 insertions(+), 43 deletions(-) + +commit 5c80a2e60086bf9a905d5d6a4a73332d96431670 +Author: fanjinke +Date: Thu May 16 23:39:53 2019 +0800 + + libgimpbase: add hygon cpu detection and enable MMX/SSE support + + Signed-off-by: fanjinke + (cherry picked from commit 5b1f8cb4d060eead34a8136ab64e577eb7a866a3) + + libgimpbase/gimpcpuaccel.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit e51d7b122df33dcc9633ee16b013082aafac82b4 +Author: Øyvind Kolås +Date: Sat May 25 19:51:42 2019 +0200 + + depend on babl-0.1.64 + + (cherry picked from commit a3f2d734a773f52f1565dd0875e27ef2d513da64) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 52a182eaed61283ee49b2b93b9b0c1901be1af9d +Author: Michael Natterer +Date: Sat May 25 17:05:49 2019 +0200 + + Issue #3124 - layer mask pastes as a greyscale layer/grayscale + layer... + + ...in a color image/colour image + + gimp_edit_paste_get_layer(): only use the pasted-to drawable's format + with alpha if this is really a floating paste, use the image's layer + format with alpha for "as new layer" cases. + + (cherry picked from commit 1f57675a46f71fbb77daf68357362e7bc4ae5902) + + app/core/gimp-edit.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +commit 3cacebd06a14e025736eb9609b9e6e899ceeaedd +Author: Massimo Valentini +Date: Sat Nov 4 13:50:47 2017 +0100 + + Issue #1220 - Text tool has color from previous line... + + ...when overwriting existing text + + gimp_text_tool_enter_text(): when replacing a selection, use the text + properties from the text style editor for the entered text, otherwise + the style of the text before the selection will be used. + + (cherry picked from commit 1a691f77e67cbe755c19d2085df5b143e79c748f) + + app/tools/gimptexttool-editor.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +commit d16c69ddf385d1b94df61e3ebac4ee4425b8fa18 +Author: Michael Natterer +Date: Sat May 25 11:42:07 2019 +0200 + + Issue #3343 - Some translated tooltips are appearing untranslated + + Tool options properties must be translated with _() not N_(). + + (cherry picked from commit e249a422261697ed87196efe528dc12e97a6c6c3) + + app/tools/gimprectangleoptions.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +commit 7a8d0c4afb156baf720be7dc875af3661e8e087f +Author: Ell +Date: Fri May 24 02:39:15 2019 -0400 + + app: disregard composite space in source-only trivial layer modes + + Extend last commit to also disregard the composite space when the + layer mode is trivial and only the source region is included in + compositing, since, in this case, the source color is unmodified. + + (cherry picked from commit c2021d3c5b72e53849f4b38c6613a15e7651152b) + + app/operations/layer-modes/gimp-layer-modes.c | 24 + +++++++++++++++--------- + 1 file changed, 15 insertions(+), 9 deletions(-) + +commit 731368619203524666bc24768adb93d454706ab7 +Author: Ell +Date: Fri May 24 01:33:53 2019 -0400 + + app: disregard composite space in non-union alpha-only layer modes + + In gimp_layer_mode_get_format(), disregard the requested composite + space when selecting the format, if the input layer mode is alpha- + only, and the requested composite mode is not UNION, since, in this + case, the layer mode doesn't combine the layer/backdrop colors, and + rather only modifies the alpha of one of them. This allows us to + use the preferred format, avoiding gamma conversion. + + This particularly improves the performance of the Eraser tool in + perceptual images. + + (cherry picked from commit a5962e4049461d2f7b29ab840e58ad4fab2b430c) + + app/core/gimpfilloptions.c | 2 ++ + app/gegl/gimp-gegl-nodes.c | 3 ++- + app/operations/layer-modes/gimp-layer-modes.c | 31 + +++++++++++++++++++--- + app/operations/layer-modes/gimp-layer-modes.h | 3 ++- + .../layer-modes/gimpoperationlayermode.c | 3 ++- + app/paint/gimpbrushcore.c | 8 ++++-- + app/paint/gimpink.c | 8 ++++-- + app/paint/gimppaintcore-loops.cc | 3 ++- + 8 files changed, 49 insertions(+), 12 deletions(-) + +commit 9501d63b201f2364942b1615afc06109a13493c7 +Author: Michael Natterer +Date: Thu May 23 14:34:00 2019 +0200 + + plug-ins: big formatting and indentation cleanup in file-dds + + Also change the license to GPL 3 or later, like all other files. + + (cherry picked from commit 2a48a5f8681e12bb280607bcafaa1eb8a77e9a73) + + plug-ins/file-dds/color.c | 94 ++- + plug-ins/file-dds/color.h | 123 +-- + plug-ins/file-dds/dds.c | 146 ++-- + plug-ins/file-dds/dds.h | 436 +++++----- + plug-ins/file-dds/ddsplugin.h | 22 +- + plug-ins/file-dds/ddsread.c | 118 +-- + plug-ins/file-dds/ddswrite.c | 670 ++++++++------- + plug-ins/file-dds/dxt.c | 1810 + +++++++++++++++++++++------------------- + plug-ins/file-dds/dxt.h | 78 +- + plug-ins/file-dds/dxt_tables.h | 398 ++++----- + plug-ins/file-dds/endian_rw.h | 140 ++-- + plug-ins/file-dds/imath.h | 76 +- + plug-ins/file-dds/mipmap.c | 1495 ++++++++++++++++++--------------- + plug-ins/file-dds/mipmap.h | 112 ++- + plug-ins/file-dds/misc.c | 320 +++---- + plug-ins/file-dds/misc.h | 53 +- + plug-ins/file-dds/vec.h | 233 +++--- + 17 files changed, 3391 insertions(+), 2933 deletions(-) + +commit 275c2f9978ae0c4e9715ad4bda03b0f114718d81 +Author: Ell +Date: Tue May 21 08:12:04 2019 -0400 + + app: don't show result-size warning when transforming a selection + + In gimp_drawable_transform_get_effective_clip(), always return + RESIZE_CLIP when the input drawable is the image mask, since the + presence of a selection doesn't matter in this case. This avoids + erroneously displaying a result-size warning when transforming the + selection using any of the selection tools. + + (cherry picked from commit 1c91578bb29b6fadd7973a97e5e38218cfd111d4) + + app/core/gimpdrawable-transform.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +commit 12b101b31c93c836d2ebcd8c3c520b7bf6320d9f +Author: Jordi Mas +Date: Mon May 20 18:31:02 2019 +0200 + + Update Catalan translation + + po-tips/ca.po | 331 + +++++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 212 insertions(+), 119 deletions(-) + +commit 0b6c0b38f21219bc70b305b126cdabdcd71cfb8a +Author: Jordi Mas +Date: Mon May 20 18:29:58 2019 +0200 + + Update Catalan translation + + po/ca.po | 5645 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 2977 insertions(+), 2668 deletions(-) + +commit 28a0d91fd5c45582a72c81d3e7e2752c02399fe1 +Author: Ell +Date: Mon May 20 03:06:02 2019 -0400 + + configure.ac: improve compiler version string escaping + + In configure.ac, improve backslash escaping in the compiler + version string. Backslashes are currently not properly escaped in + our Windows builds, leading to spurious (and, in particular, non- + UTF8) characters in the compiler version string. + + (cherry picked from commit 04f9281bdd1ee05bed93eb98d9f697e38fcc94ea) + + configure.ac | 25 ++++++++++++------------- + 1 file changed, 12 insertions(+), 13 deletions(-) + +commit 0fdf6c22893f05fec6f0c8a4d35a6f039145c69a +Author: Ell +Date: Sun May 19 10:46:52 2019 -0400 + + app: initialize GimpBacktrace earlier on + + Initialize GimpBacktrace earlier on in the startup process, so that + the Windows backend installs the thread-name exception handler + early enough to catch threads created before app_run() (in + particular, the GEGL worker threads). + + (cherry picked from commit 853d91b8e4d0afa371a8418fdb340e8ca975ab47) + + app/app.c | 7 ------- + app/main.c | 7 +++++++ + 2 files changed, 7 insertions(+), 7 deletions(-) + +commit b0f6996e614911a27a6e9d1058e98b3acb736589 +Author: Ell +Date: Wed May 15 13:32:31 2019 -0400 + + app: fix indentation in gimppaintbrush.h + + (cherry picked from commit 86a7c053ccd3e1abec45ba40699cc58b3aefc8b1) + + app/paint/gimppaintbrush.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit eb8ab6346263c53c0fd1eff7f9fb41085cc84348 +Author: Ell +Date: Wed May 15 10:01:01 2019 -0400 + + app: in GimpPaintbrush, reuse existing paint buffer + + In GimpPaintbrush, avoid refilling the paint buffer at each dab if + the paint color/pixmap hasn't changed, as gimp_paint_core_paste() + no longer modifies the buffer since the commit before last. + + Additionally, fix color-from-gradient dynamics when the image has a + profile. + + (cherry picked from commit edc99531e6a03956be7f1b22d55a48afda3cd958) + + app/paint/gimppaintbrush.c | 113 + ++++++++++++++++++++++++++++++++++----------- + app/paint/gimppaintbrush.h | 4 ++ + 2 files changed, 89 insertions(+), 28 deletions(-) + +commit ea693288d3ebb9cca0673a29d33b6e212d7883b6 +Author: Ell +Date: Wed May 15 09:59:03 2019 -0400 + + app: add gimp_brush_core_get_brush_pixmap() + + In GimpBrushCore, replace the private + gimp_brush_core_transform_pixmap() function with a public + gimp_brush_core_get_brush_pixmap() function, which, similarly to + gimp_brush_core_get_brush_mask(), returns the transformed brush + pixmap, and can be used by subclasses. + + (cherry picked from commit fcd19a2aeb655a1f5455122d11233c204934641c) + + app/paint/gimpbrushcore.c | 59 + ++++++++++++++++++++++------------------------- + app/paint/gimpbrushcore.h | 3 +++ + 2 files changed, 30 insertions(+), 32 deletions(-) + +commit 24e956714ca65d73235bd8232666dfbeb024b972 +Author: Ell +Date: Wed May 15 09:44:41 2019 -0400 + + app: don't modify paint buffer when pasting to canvas + + We now have enough machinery in gimppaintcore-loops to avoid + modifying the paint buffer in gimp_paint_core_paste() in the no- + applicator case, by using the same set of algorithms as + gimp_paint_core_replace(). Other than reducing the number of + different code paths we have, this is both more efficient, and + allows us to reuse the paint buffer across dabs, as done in the + following commits. + + Implement gimp_paint_core_replace() in terms of + gimp_paint_core_paste(). We keep the two functions separate, since + their implementation is still differnet when using an applicator. + + Suppress the paint-buffer-modifying algorithms in + gimppaintcore-loops, but keep them around; using the same logic for + normal painting as we use for REPLACE painting is possible due to + the fact that all our current non-REPLACE modes treat alpha values + and mask values interchangeably. In the future we might have modes + that distinguish between alpha and mask values, requiring the old + algorithms. + + (cherry picked from commit f24bca515652f17c653ba2902639e4323fbea150) + + app/paint/gimppaintcore-loops.cc | 6 +-- + app/paint/gimppaintcore.c | 84 + ++++++---------------------------------- + 2 files changed, 14 insertions(+), 76 deletions(-) + +commit a526bacbadf8c8aea1e22a330b73a0e80e0cd6c8 +Author: Carles Ferrando Garcia +Date: Tue May 14 19:41:47 2019 +0000 + + Update Catalan translation + + po-python/ca.po | 366 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 354 insertions(+), 12 deletions(-) + +commit 6d6bf8ec72e2254cb6be0aa2ab31a06cd90e3bc7 +Author: Sabri Ünal +Date: Fri May 10 14:23:25 2019 +0000 + + Issue #3140 - Export as DDS dialog does not have an Export button + + (cherry picked from commit d7a4d5c6a8423e7e23aaeb0eefc9a31a6866381c) + + plug-ins/file-dds/ddswrite.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 071b560feef9737f91a6a0541c46cdfd524bd386 +Author: Piotr Drąg +Date: Sun May 12 15:05:54 2019 +0200 + + Update Polish translation + + po/pl.po | 478 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 261 insertions(+), 217 deletions(-) + +commit 64f67894eb708863bd46702a14eef7d92a686bd9 +Author: Sabri Ünal +Date: Sun May 12 08:13:03 2019 +0000 + + Update Turkish translation + + po-python/tr.po | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +commit 31ee6864bd5020d6a281b0902d7dfaf5d9f24226 +Author: Sabri Ünal +Date: Sun May 12 08:11:12 2019 +0000 + + Update Turkish translation + + po-script-fu/tr.po | 64 + +++++++++++++++++++++++++++--------------------------- + 1 file changed, 32 insertions(+), 32 deletions(-) + +commit ae7f7943358c946872b6d2a6b7c0f63c4b6bd0ad +Author: Sabri Ünal +Date: Sun May 12 08:10:52 2019 +0000 + + Update Turkish translation + + po-libgimp/tr.po | 66 + ++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 35 insertions(+), 31 deletions(-) + +commit 692e09c1197de114179e808ab5ea2662c2a5ff3d +Author: Sabri Ünal +Date: Sun May 12 08:08:49 2019 +0000 + + Update Turkish translation + + po/tr.po | 1425 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 760 insertions(+), 665 deletions(-) + +commit 25721e39e6d30a780ca1cb23b9f3b04938fd7139 +Author: Ell +Date: Sat May 11 05:10:16 2019 -0400 + + app: in GimpBrushCore, free old paint buffer before allocating new one + + In gimp_brush_core_get_paint_buffer(), when allocating a new paint + buffer, clear the old buffer *before* allocating the new one, to + reduce the amount of simultaneously allocated memory. + + (cherry picked from commit bea1a44672f864a46d7d743d64a350436fd6307d) + + app/paint/gimpbrushcore.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 3097978f4e03b4bfdb3a49f3fce51fb98dc2fa9c +Author: Ell +Date: Sat May 11 05:09:45 2019 -0400 + + app: in GimpDashboard, improve legend logic + + (cherry picked from commit 088827e5630d3dc5187bdbfceb4ee62eda238357) + + app/widgets/gimpdashboard.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit e7d9344880763f264b37285e34b468b4906da099 +Author: Jehan +Date: Fri May 10 01:38:33 2019 +0900 + + app, libgimp, pdb: s/procesures/procedures/ + + While we are at it, another typo was missed. + + (cherry picked from commit 45f37b9b6395ae2b6d39a620a829c196a0559846) + + app/pdb/fileops-cmds.c | 2 +- + libgimp/gimpfileops_pdb.c | 2 +- + pdb/groups/fileops.pdb | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +commit ce92efa82e884d77ee950cdde4fb3b4f6d956c73 +Author: luz.paz +Date: Fri Apr 19 22:51:06 2019 -0400 + + Add a few more misc. source comment typos + + + (cherry picked from commit 1c91b8d97eb7a8f159b5c232f9159328df6558e3) + + app/display/gimptoolrectangle.c | 2 +- + app/widgets/gimpviewrendererdrawable.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 9a60382f69ffa5615acb7c34f79911e8627f680d +Author: luz.paz +Date: Sat Apr 6 10:36:03 2019 -0400 + + Misc. typos + + Found via `codespell` + + (cherry picked from commit 86edc31b11092aeaf846834ed7720851ee39e5e3) + + app/config/gimpdialogconfig.c | 2 +- + app/core/gimpcurve.c | 2 +- + app/pdb/fileops-cmds.c | 2 +- + app/pdb/image-cmds.c | 4 ++-- + icons/Color/scalable/gimp-tool-fuzzy-select.svg | 2 +- + libgimp/gimpfileops_pdb.c | 2 +- + libgimp/gimpimage_pdb.c | 4 ++-- + libgimpbase/gimpmetadata.c | 2 +- + pdb/groups/fileops.pdb | 2 +- + pdb/groups/image.pdb | 4 ++-- + 10 files changed, 13 insertions(+), 13 deletions(-) + +commit 6c1199a4d41d1c4dfce3c3d25f68bd44b93f5ccb +Author: Jehan +Date: Thu May 9 12:34:21 2019 +0900 + + desktop: prepare an AppData release tag for 2.10.12. + + (cherry picked from commit f4c6fd0ee4d8202c2f69e1e0e66a6488f52e2f4b) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 31 + +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +commit 616838e1c6a55eab999cd78be5a4a559a4afa019 +Author: Carles Ferrando Garcia +Date: Wed May 8 20:40:51 2019 +0000 + + Update Catalan translation + + po-script-fu/ca.po | 303 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 166 insertions(+), 137 deletions(-) + +commit 168080fc5ca49f08580887274f5bafd62596d53d +Author: Carles Ferrando Garcia +Date: Wed May 8 20:36:54 2019 +0000 + + Update Catalan translation + + po/ca.po | 7298 + +++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 4173 insertions(+), 3125 deletions(-) + +commit 635b33837e64df1a22ceefaadbf47819df7cf1bc +Author: Carles Ferrando Garcia +Date: Wed May 8 20:34:21 2019 +0000 + + Update Catalan translation + + po-plug-ins/ca.po | 3622 + ++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 2193 insertions(+), 1429 deletions(-) + +commit 89495f457d31421687a1bee870c7bfa61e6d56e7 +Author: Ell +Date: Wed May 8 04:15:47 2019 -0400 + + app: add tile-alloc-total variable to the dashboard + + Add a tile-alloc-total varaible to the dashboard's memory and misc + groups, showing the total amount of memory used by the tile + allocator (see commit + gegl@137e66e45138e8316f6403e53e8aa9a02ad523e7.) + + (cherry picked from commit cf54f790fd8da952844e77e0cbcb0e589dd6bbac) + + app/widgets/gimpdashboard.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +commit cb4e9b92bce0f5e86be74c5b70ab3530070d1d67 +Author: Ell +Date: Wed May 8 03:58:09 2019 -0400 + + app: in GimpDashboard, don't show legend for groups with no meter + + in GimpDashboard, don't show field legend colors in groups without + a meter. + + (cherry picked from commit 8434ae42a3a9a18f0d31222bee400c17891cd218) + + app/widgets/gimpdashboard.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ec067b83ad8b884f57e1f29092ddc13f1d8a74f6 +Author: Ell +Date: Wed May 8 04:19:49 2019 -0400 + + configure.ac: require GEGL >= 0.4.16 + + (cherry picked from commit 29d575c0332255b2edfafaf521efb98322e95fde) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 2d91f1ca0239ec3b384eaf1e1f4495de49a42519 +Author: Ell +Date: Wed May 8 03:32:39 2019 -0400 + + Issue #3353 - Gimp 2.10.10 freezes while changing Background color ... + + ... in LCh colorspace + + In gimptoolbox-color-area, when setting the context's background + color in response to a color-dialog change, block the right signal + handler, to avoid re-setting the color dialog's color, which would + cause the GtkAdjustment's "value-changed" signal (assuming it was + the source of the change) to be restarted if the new value doesn't + match the current one exactly, which can happen due to conversion + errors. + + (cherry picked from commit c7a29e5f98ecb833bf2d87a1df9963a633c6fbe7) + + app/widgets/gimptoolbox-color-area.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 7b1af1f0cb686ea6b7d2d917ec6c4b91ea0ec934 +Author: Jehan +Date: Wed May 8 13:58:45 2019 +0900 + + app: round curve point position when displaying it as int. + + Int casting results to truncation. First this is a bit + counter-intuitive + as we usually expect rounding to the nearest integer. Moreover + with the + new GUI updates on curve and curve tool, we end up with a mismatch as + the new Input/Output spin buttons where indeed showing rounded + integers + whereas the coordinate indicator was showing truncated integers. + + Make all these show same rounded value. + + app/widgets/gimpcurveview.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +commit 5a3e9d1f002836055b2ba8f41f90b19e8bfd8bf5 +Author: Jehan +Date: Tue May 7 18:49:35 2019 +0900 + + Issue 1878: Eyedrop tool doesn't work when screen composing (KWin)... + + ... is enabled. + + (cherry picked from commit b29d1ea6ff0dec77cfd206049ef88353e09abbe0) + + libgimpwidgets/gimppickbutton-kwin.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit a7fcbf61222c1e5d216aa5b207f155d0ffef0783 +Author: Jehan +Date: Tue May 7 18:12:47 2019 +0900 + + Issue #835: warnings building with Clang. + + Fix an error (introduced in commit 3bf2a3c1660). + + (cherry picked from commit 90bc9e10f14644a2bf6c0071c0762d5f34f0957d) + + app/menus/windows-menu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit c5f7bac2ba2121636bbf84d5e4af4d11e98fa511 +Author: Jehan +Date: Mon May 6 13:14:06 2019 +0900 + + plug-ins: follow the assigned profile TRC. + + GIMP should not convert assigned profile to sRGB just because + we stored + as linear on the XCF. In other words, we should not look at the image + precision to decide whether to export as linear (previously only 8-bit + linear images), but at the profile TRC. There are basically 3 cases: + (1) We don't save a profile, then convert to sRGB whatever the source + precision (because readers would assume sRGB for a no-profile jpeg). + (2) We save the default profiles: convert to sRGB because it's usually + a better choice for 8-bit formats and even working at 32-bit float + *linear* doesn't mean you want to export as 8-bit int *linear*. As the + image creator made no explicit export choice, we make an acceptable + default one. + (3) We save an explicitly assigned profile: keep the profile TRC, + don't + convert! + + Note that this apparently won't work perfectly right now, as GIMP + replaces the original TRC with the linear default TRC when + converting to + linear. So the expected TRC is lost in such case when you have not + explicitly reset the correct profile. Yet this is on GIMP side + and this + part of the issue should be fixed with the space invasion merge. For + now, this is how the plug-in should work. + + This is based on my late discussion with Ell. Please everyone, and Ell + especially, review! :-) + + plug-ins/file-jpeg/jpeg-save.c | 91 + ++++++++++++++++-------------------------- + 1 file changed, 34 insertions(+), 57 deletions(-) + +commit eec0983e5d17e4b1937c806e78a5f4077cec94ea +Author: Tim Sabsch +Date: Sun May 5 14:48:28 2019 +0000 + + Update German translation + + po-plug-ins/de.po | 436 + +++++++++++++++++++++++++++--------------------------- + 1 file changed, 216 insertions(+), 220 deletions(-) + +commit d85f3f7a1e7b1392ead5dfd808080598472c910b +Author: Tim Sabsch +Date: Sun May 5 14:45:06 2019 +0000 + + Update German translation + + po-libgimp/de.po | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +commit 2d2eed34cd09b9c1c54015acabbecd2dc36dd14d +Author: Michael Natterer +Date: Sun May 5 16:23:40 2019 +0200 + + Issue #3251 - Windows menu shows doc's old name not new + + In windows-actions.c, connect to GimpDisplayShell's "notify::title" + and update the action label when it changes. + + (cherry picked from commit c9b10ff3bf86723a9c6cae6a40049c00f813c6dd) + + app/actions/windows-actions.c | 123 + +++++++++++++++++++++++++----------------- + 1 file changed, 75 insertions(+), 48 deletions(-) + +commit f16f3b56d47ad03f97eadf673a14e881ecde2b43 +Author: Michael Natterer +Date: Sun May 5 15:36:07 2019 +0200 + + app: don't dereference NULL image pointer + + windows_menu_display_query_tooltip(): bail out if "image" is + NULL. Can't happen currently but did happen temporarily while hacking + on related code. Better safe than sorry. + + (cherry picked from commit 3bf2a3c16603f434521cf94c1e2fd7d745a18794) + + app/menus/windows-menu.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 2548dd1acfcba6ff219b8bd6183d1aecd6f90d14 +Author: Ell +Date: Wed May 1 15:32:18 2019 -0400 + + tools: in performance-log-viewer.py, fix deprecation warnings + + (cherry picked from commit b1077a903e5159aa6f70de20335552c529ce7225) + + tools/performance-log-viewer.py | 67 + +++++++++++++++++++++++------------------ + 1 file changed, 37 insertions(+), 30 deletions(-) + +commit aa9d16a82206bbb1880c0b7dfd306d77a7a8f2d6 +Author: Ell +Date: Tue Apr 30 16:45:12 2019 -0400 + + Issue #3306 - Memory leak using Crop tool + + In gimp_canvas_passe_partout_get_extents(), free the inner region + after XORing it with the outer region. + + (cherry picked from commit 84e183e5ed1842199f0a2af03abce0d316b07e4b) + + app/display/gimpcanvaspassepartout.c | 2 ++ + 1 file changed, 2 insertions(+) + +commit 504a632433ea41f12d609d315287beb75a1c962b +Author: Ell +Date: Tue Apr 30 16:41:43 2019 -0400 + + Issue #3304 - Invalid write when halting filter tool with controller + + In GimpFilterTool, properly clean up the controller's widget weak- + pointer and signal-handlers upon destruction, to avoid invalid + memory access when the widget is destroyed, if the widget outlives + the controller. + + (cherry picked from commit 068df34a273aa5542f854e1c627386c6f2ad7750) + + app/tools/gimpfiltertool-widgets.c | 113 + ++++++++++++++++++++++--------------- + 1 file changed, 68 insertions(+), 45 deletions(-) + +commit 3e2738e944ce2a122dd328a59559288c6fe265e6 +Author: Ell +Date: Tue Apr 30 14:27:19 2019 -0400 + + app: in gimp-gegl-nodes, set underlying operation + + In the vairous gimp_gegl_create_foo_node() functions, set the + parent node's underlying operation node, so that + gimp_gegl_apply_cached_operation() avoids duplicating the source + buffer when applying these nodes (all underlying operations are + currently point ops.) + + (cherry picked from commit 928e5957e3e3b4fb6cd4c98991978b328986c46f) + + app/gegl/gimp-gegl-nodes.c | 4 ++++ + 1 file changed, 4 insertions(+) + +commit a0f1f93e85de5f80fc5e6b3a8c15c91899c6377c +Author: Piotr Drąg +Date: Sun Apr 28 16:19:40 2019 +0200 + + Update Polish translation + + po-libgimp/pl.po | 489 + +++++++++++++++++++++++++++---------------------------- + 1 file changed, 238 insertions(+), 251 deletions(-) + +commit 3e1e1f2d1b3df7bc17feacb44bbf3b9d53ab75a2 +Author: Jehan +Date: Sun Apr 28 15:27:53 2019 +0900 + + Issue 2949: Newly Installed Fonts not Registering. + + Apparently Microsoft added just recently the feature to install user + font (as opposed to system-wide fonts), without administration rights + (yes, only now, how crazy is that?). Right now GIMP does not see fonts + there. + + We have an upstream report at fontconfig where such a default search + path should happen. + See: https://gitlab.freedesktop.org/fontconfig/fontconfig/issues/144 + Until it gets fixed there, let's just add the user fonts dir ourselves + in GIMP. This code should get killed later. + + Notes: + - I renamed various DEFAULT_* data macros to GIMP_DEFAULT_* because + DEFAULT_PALETTE was conflicting with another macro in Windows API! + - Also I removed the DATADIR macro set under app/config/ because it is + also conflicting and anyway we use it in no files on this level. + - This is not perfectly tested on Windows. Please everyone with + Windows + access, could you build and test if it works fine before release? + + (cherry picked from commit 88f97aedef56d2158ec37619598db77e84ca02ed) + + app/config/Makefile.am | 1 - + app/config/gimpcoreconfig.c | 104 + ++++++++++++++++++++++++++++++++++++-------- + 2 files changed, 86 insertions(+), 19 deletions(-) + +commit c03f16caf2cb6da49861c1b61dda8211c6b65855 +Author: Jehan +Date: Fri Apr 26 00:56:12 2019 +0900 + + Issue #3309: Translation of gimp installer for MS Windows. + + Several of our own translations of the Windows installer are unused + because Inno Setup corresponding translations are marked "unofficial". + This mostly means that the language files for these are probably + old and + unmaintained, hence outdated. And these files are not bundled together + with Inno Setup release (though still hosted in their repo). + + Nevertheless it doesn't make sense that we would just waste the + work of + our translators here. Maybe Inno Setup localization is not complete, + so + what? At best it could even encourage translators to contribute + upstream + to Inno Setup. Let's just enable all our current localizations of the + installer and see how it goes! + + (cherry picked from commit 01f258faec2dd0faff575eb59a28ea77c76c11ac) + + build/windows/installer/gimp3264.iss | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit c009de14acc06362f06c7829bfc659d2ee633202 +Author: Jehan +Date: Thu Apr 25 14:28:55 2019 +0900 + + plug-ins: fix TIFF linear export. + + The export code was checking the existence of a profile variable which + was never set. In other words, it seems we were always converting TIFF + pixels to non-linear, even when we were also exporting a linear + profile. + + Note that is not useful anymore to check profile existence as we + now use + the effective profile (which always exists). So we just have to + check if + the "save profile" option is on. + + (cherry picked from commit 062195117c7fa5f738f7cf2a15aac45c5f1d2ae7) + + plug-ins/file-tiff/file-tiff-save.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +commit dd2632bc85af9c083afc4319f39f08aa145d5bd4 +Author: Ell +Date: Fri Apr 26 03:35:20 2019 -0400 + + app: flush image when committing free-select tool + + In GimpFreeSelectTool, flush the image when committing the tool, if + the seletion is created at the time of the commit (i.e., if the + polygon is not closed prior to the commit). + + (cherry picked from commit 71c624c5aba2b007f4b6ebd0cfe7dcfd46ab3508) + + app/tools/gimpfreeselecttool.c | 67 + +++++++++++++++++++++++------------------- + 1 file changed, 37 insertions(+), 30 deletions(-) + +commit ee7419cb0118af380eab31678084cc1b6ad82c92 +Author: Balázs Úr +Date: Fri Apr 26 05:56:55 2019 +0000 + + Update Hungarian translation + + po/hu.po | 806 + +++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 424 insertions(+), 382 deletions(-) + +commit 38c1ee315d77e15c825f2a8c116adde4c84662fc +Author: Balázs Úr +Date: Fri Apr 26 05:53:25 2019 +0000 + + Update Hungarian translation + + po-plug-ins/hu.po | 386 + +++++++++++++++++++++++++----------------------------- + 1 file changed, 176 insertions(+), 210 deletions(-) + +commit 469ce04f835eac11c8a6f121936b5afe0f7ce7b7 +Author: Balázs Úr +Date: Fri Apr 26 05:50:44 2019 +0000 + + Update Hungarian translation + + po-libgimp/hu.po | 278 + ++++++++++++++++++------------------------------------- + 1 file changed, 91 insertions(+), 187 deletions(-) + +commit 30788227cc451190327560a2ae46f2222c92953c +Author: Ell +Date: Thu Apr 25 09:51:38 2019 -0400 + + app: increase GimpSelectionTools idle priority to avoid flickering + + In gimp_selection_tool_start_change(), increase the priority of the + idle source used for hiding the selection so that it's run before + the canvas is redrawn, to avoid flickering the previous selection. + + (cherry picked from commit 3369958525bd301cd3887dd55c354593d07fba54) + + app/tools/gimpselectiontool.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +commit 03cb47f871368887a2fea6b33fd58104f4ee1793 +Author: Ell +Date: Thu Apr 25 07:07:13 2019 -0400 + + app: fix dirty mask/action in the rectangle- and free-select tools + + In GimpRectangleSelectTool and GimpFreeSelectTool, make sure the + tool is committed when the selection is dirty. + + (cherry picked from commit 6a2bea733b90d04aee914b74c0453ee7d5c40b59) + + app/tools/gimpfreeselecttool.c | 15 +++++++++++++-- + app/tools/gimprectangleselecttool.c | 2 ++ + 2 files changed, 15 insertions(+), 2 deletions(-) + +commit 95f468fec260d839f388a384f78377e70a67133d +Author: Ell +Date: Thu Apr 25 05:56:06 2019 -0400 + + Issue #2328 - Free Select tool should create preliminary marching + ants selection + + In GimpFreeSelectTool, use gimp_selection_tool_{start,end}_change() + to create the selection as soon as the polygon is closed, and + update it when the polygon, or the relevant tool-options, change, + similarly to GimpRectangleSelectTool. + + (cherry picked from commit 476833b55304e7a37513b830d1f3b8b34dde67c3) + + app/tools/gimpfreeselecttool.c | 135 + +++++++++++++++++++++++++++++++++++------ + 1 file changed, 117 insertions(+), 18 deletions(-) + +commit 9cc8d2403d89a910716c8141e8c61e4a93e49db0 +Author: Ell +Date: Thu Apr 25 05:40:24 2019 -0400 + + app: factor-out common free/fg-select logic into GimpPolygonSelectTool + + We currently derive GimpForegroundSelectTool from + GimpFreeSelectTool, which prevents us from making changes that are + limited to the free-select tool. + + Factor out the common free-select and foreground-select logic into + a new GimpPolygonSelectTool base-class, and derive both from this + class. + + (cherry picked from commit afab7deaa3321dee4cb366cbe1d35ecf0ffe68a9) + + app/tools/Makefile.am | 6 +- + app/tools/gimpforegroundselecttool.c | 110 +++---- + app/tools/gimpforegroundselecttool.h | 25 +- + app/tools/gimpfreeselecttool.c | 466 ++++------------------------- + app/tools/gimpfreeselecttool.h | 21 +- + app/tools/gimppolygonselecttool.c | 558 + +++++++++++++++++++++++++++++++++++ + app/tools/gimppolygonselecttool.h | 69 +++++ + 7 files changed, 756 insertions(+), 499 deletions(-) + +commit 67f44f3e34a4fe185798da4ca48878c115ad4219 +Author: Ell +Date: Thu Apr 25 04:25:31 2019 -0400 + + app: add GimpToolPolygon::change-complete signal + + ... which is emitted when finishing a change to the polygon, + similarly to GimpToolRectangle::change-complete. + + (cherry picked from commit 134ff92fe0b30466dfdd846207682c4474f7041d) + + app/display/gimptoolpolygon.c | 37 ++++++++++++++++++++++++++++++++++++- + app/display/gimptoolpolygon.h | 3 +++ + 2 files changed, 39 insertions(+), 1 deletion(-) + +commit c161b0cccb2e3f47fe3bbbd52e9ef3cd6fbda50a +Author: Ell +Date: Thu Apr 25 04:24:58 2019 -0400 + + app: add gimp_tool_polygon_is_closed() + + (cherry picked from commit f84f1d89dc382ea8179a08f11f3e646c67151066) + + app/display/gimptoolpolygon.c | 12 ++++++++++++ + app/display/gimptoolpolygon.h | 1 + + 2 files changed, 13 insertions(+) + +commit 7d4ea79a9885c87086fd000a00fcad2e363862ae +Author: Ell +Date: Thu Apr 25 04:24:07 2019 -0400 + + app: allow passing NULL pointers to gimp_tool_polygon_get_points() + + (cherry picked from commit e8c915af939ce8ac5697975b99f7d2e74faf4a6d) + + app/display/gimptoolpolygon.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +commit fb1e552acd16eaa9c35c53bb55f9394b53a494f3 +Author: Ell +Date: Thu Apr 25 04:18:47 2019 -0400 + + app: remove unused field from GimpPolygonSelectToolPrivate + + (cherry picked from commit eda421e852e1885f15e965622ff7c843010623bd) + + app/display/gimptoolpolygon.c | 3 --- + 1 file changed, 3 deletions(-) + +commit 655af1dd44b9d5b59e91752e90215937ab8a4fd1 +Author: Ell +Date: Thu Apr 25 04:05:12 2019 -0400 + + app: move undo/redo logic for GimpRectangleSelectTool to + GimpSelectionTool + + Move GimpRectangleSelectTool's image undo/redo logic to + GimpSelectionTool, by adding a pair of + gimp_selection_tool_{start,end}_change() functions. These + functions should be called by subclasses before/after changing the + selection, having the functions take care of undoing/redoing the + existing selection as necessary. This allows us to reuse this + logic in other selection tools, specifically, the free-select tool. + + (cherry picked from commit 4612105e522d90550fa24f75b462b19026140363) + + app/tools/gimprectangleselecttool.c | 196 +++++--------------------- + app/tools/gimpselectiontool.c | 273 + +++++++++++++++++++++++++++++++++++- + app/tools/gimpselectiontool.h | 28 ++-- + 3 files changed, 327 insertions(+), 170 deletions(-) + +commit ee3bc11e61363c22837e837c6bc1ce45eb9dfc6e +Author: Jehan +Date: Tue Mar 19 22:26:08 2019 +0100 + + Issue #3129: Split sentence on gimpexport.c. + + (cherry picked from commit 4dcda7ffb8a92ee286ec6828513852ededfa8f7b) + + libgimp/gimpexport.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 45784e9f50edfb25d076883d6d578955cebdd27f +Author: Sabri Ünal +Date: Wed Apr 24 21:31:26 2019 +0000 + + Issue #3220 - "How to Use Dialogs" does not open any page. + + plug-ins/script-fu/scripts/gimp-online.scm | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 776e2cba8f610067f033bd096c6d21c41a9e0eb7 +Author: Tobias Ellinghaus +Date: Tue Apr 23 15:57:34 2019 +0200 + + plug-ins: Move layer option up in TIFF dialog + + This was requested by mitch. + + (cherry picked from commit 5ddfe36d96be84d65de2bf218f2696fed350382d) + + plug-ins/ui/plug-in-file-tiff.ui | 34 +++++++++++++++++----------------- + 1 file changed, 17 insertions(+), 17 deletions(-) + +commit 95ea4e4272f9f081bddc04892aab2559ab22dc02 +Author: Ell +Date: Sun Apr 21 09:59:24 2019 -0400 + + Issue #3284 - Wrong layer renamed when switching images + + In gimp_container_tree_view_clear_items(), temporarily unset the + tree-view's model before clearing it, so that name editing is + stopped beforehand. Otherwise, name editing is stopped once the + corresponding item is removed from the store, causing us to rename + the wrong item. + + (cherry picked from commit bb8648a2e9e9c10c69e5e7983f545413b2f6edc8) + + app/widgets/gimpcontainertreeview.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +commit 7780f6169d18f561dccc033d67274032050b5574 +Author: Piotr Drąg +Date: Sun Apr 21 13:25:17 2019 +0200 + + Update Polish translation + + po-plug-ins/pl.po | 174 +++++++++++--------- + po/pl.po | 484 + ++++++++++++++++++++++++++++-------------------------- + 2 files changed, 349 insertions(+), 309 deletions(-) + +commit 7ef388a5111ae14586600c855f09f69c73cbea30 +Author: Alexandre Prokoudine +Date: Sun Apr 21 00:02:44 2019 +0300 + + Update Russian translation + + po/ru.po | 1511 + ++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 818 insertions(+), 693 deletions(-) + +commit 6de686bd50a6874a8c043950cc9ebaae1551cd41 +Author: Ell +Date: Sat Apr 20 14:30:43 2019 -0400 + + Issue #3275 - Crash when opening an image after closing existing image + + In gimp_open_dialog_set_image(), use a weak pointer for storing the + current image, to avoid a segfault in file_open_dialog_response() + if the active image at the time of the open action has been closed + before confirming the dialog. + + (cherry picked from commit b01113741e35f68e3a40b792e0daa2676f7eafd2) + + app/dialogs/file-open-dialog.c | 11 +++++++---- + app/widgets/gimpopendialog.c | 38 + ++++++++++++++++++++++++++++++++++++-- + 2 files changed, 43 insertions(+), 6 deletions(-) + +commit 49f3319f19b3bc5e398fd91e360e60edf33e35be +Author: Ell +Date: Fri Apr 19 15:27:30 2019 -0400 + + app: don't leak curve in gimp_curves_config_save_cruft() + + (cherry picked from commit eda8b717d5e94c6ee4879495a87db138c7b40832) + + app/operations/gimpcurvesconfig.c | 2 ++ + 1 file changed, 2 insertions(+) + +commit 023a6f790017203f44f103356439e3c51679e63c +Author: Aron Xu +Date: Sat Apr 20 01:37:01 2019 +0800 + + Fix a terminology in zh_CN translation + + po/zh_CN.po | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 5684d5f85fd2c1ac9be106ad146e1ba1375716c2 +Author: Aron Xu +Date: Sat Apr 20 01:29:28 2019 +0800 + + Update zh_CN translation of po-script-fu + + po-script-fu/zh_CN.po | 750 + ++++++++++++++++++++++++++------------------------ + 1 file changed, 386 insertions(+), 364 deletions(-) + +commit 9a1663fac5b5b56f6ec333fe777ad3747df5fa39 +Author: lumingzh +Date: Sat Apr 20 01:12:06 2019 +0800 + + Update zh_CN translation of po-tips + + po-tips/zh_CN.po | 313 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 157 insertions(+), 156 deletions(-) + +commit 7d1656d079d6dbe313c6d9d92eefb35143546081 +Author: lumingzh +Date: Sat Apr 20 01:07:46 2019 +0800 + + Update zh_CN translation of po-libgimp + + po-libgimp/zh_CN.po | 2478 + ++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 1747 insertions(+), 731 deletions(-) + +commit a6501e4ba2521b441d5dc93f676c9d88a9b8d86f +Author: Mingye Wang +Date: Sat Apr 20 00:51:21 2019 +0800 + + Update zh_CN translation of po-tags + + po-tags/zh_CN.po | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +commit da0183a516e5769db10b0f230ef019cd8758974f +Author: Ell +Date: Fri Apr 19 11:33:04 2019 -0400 + + app: fix spin-button width in the Curves tool + + In the Curves tool, explicitly set the point-coordinate spin- + buttons' width-chars, so that their size remains fixed when their + range changes. + + (cherry picked from commit 8fc94184a89d11dab0bb4396b397643337727ab4) + + app/tools/gimpcurvestool.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit f8eddc8696b2efa6a768daaad36a7974358aee50 +Author: Ell +Date: Fri Apr 19 11:22:57 2019 -0400 + + app: fix last commit + + (cherry picked from commit f6d76ff34254f5ace70c5c814c21684bdc7dec0c) + + app/tools/gimpcurvestool.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +commit 168fa1509197e93a3dc826b927fa9f605ab62ced +Author: Ell +Date: Fri Apr 19 11:09:51 2019 -0400 + + app: fix Curves tool numeric-entry range/precision for > 8-bpc images + + In the Curves tool, when the image precision is greater than 8-bpc, + use a 0.00-100.00 range for the point-coordinate spin-buttons, + instead of a 0-255 range. + + (cherry picked from commit be719f9070a1143855e7da97bdd75e6313f62543) + + app/tools/gimpcurvestool.c | 42 + ++++++++++++++++++++++++++++++------------ + app/tools/gimpcurvestool.h | 1 + + 2 files changed, 31 insertions(+), 12 deletions(-) + +commit 001acb7d18a8e47ce9ab9dbd65d7df7b6d292c12 +Author: Ell +Date: Fri Apr 19 10:09:08 2019 -0400 + + app: add smooth/corner curve-point types + + Allow setting the type of GimpCurve control-points to either SMOOTH + or CORNER. Smooth points produce a smooth curve, while corner + points produce a sharp curve (previously, all points were smooth). + + In GimpCureView, display corner points using a diamond shape, + instead of a circle. + + In the Curves tool, allow changing the curve's point types. + + (cherry picked from commit 33e47c85a21dddccf67b4aa83692f5d372886884) + + app/core/core-enums.c | 29 +++ + app/core/core-enums.h | 11 ++ + app/core/gimpcurve.c | 206 + +++++++++++++++++---- + app/core/gimpcurve.h | 132 +++++++------ + app/tools/gimpcurvestool.c | 75 +++++++- + app/tools/gimpcurvestool.h | 1 + + app/widgets/gimpcurveview.c | 52 +++++- + app/widgets/gimpcurveview.h | 1 + + icons/Color/16/gimp-curve-point-corner.png | Bin 0 -> 422 bytes + icons/Color/16/gimp-curve-point-smooth.png | Bin 0 -> 541 bytes + icons/Color/scalable/gimp-curve-point-corner.svg | 113 +++++++++++ + icons/Color/scalable/gimp-curve-point-smooth.svg | 115 ++++++++++++ + icons/Legacy/16/gimp-curve-point-corner.png | Bin 0 -> 124 bytes + icons/Legacy/16/gimp-curve-point-smooth.png | Bin 0 -> 155 bytes + icons/Symbolic/16/gimp-curve-point-corner.png | Bin 0 -> 306 bytes + icons/Symbolic/16/gimp-curve-point-smooth.png | Bin 0 -> 387 bytes + .../Symbolic/scalable/gimp-curve-point-corner.svg | 94 ++++++++++ + .../Symbolic/scalable/gimp-curve-point-smooth.svg | 95 ++++++++++ + icons/icon-list.mk | 4 + + 19 files changed, 820 insertions(+), 108 deletions(-) + +commit f473af3c46bd4e717d8216c58de4b8d8f12c50a2 +Author: Ell +Date: Fri Apr 19 07:30:39 2019 -0400 + + Issue #1528 - Allow precise or numeric input in color curves tool + + Add input/output spin-buttons to the Curves tool, which allow + setting the selected point's coordinates numerically. + + (cherry picked from commit 5140d903b8267c0cc631f28c499b15ee2c08fdc6) + + app/tools/gimpcurvestool.c | 170 + +++++++++++++++++++++++++++++++++++++++++++-- + app/tools/gimpcurvestool.h | 3 + + 2 files changed, 166 insertions(+), 7 deletions(-) + +commit 9861ea8e8787c16bced8c06ed254860036ee52a9 +Author: Ell +Date: Fri Apr 19 07:29:40 2019 -0400 + + app: add "selection-changed" signal to GimpCruveView + + ... which gets emitted when the selected point changes. + + (cherry picked from commit 91ecca7e10c0bcc18479984c91a190c1e08e49f9) + + app/widgets/gimpcurveview.c | 31 +++++++++++++++++++++++++++++-- + app/widgets/gimpcurveview.h | 11 ++++++++--- + 2 files changed, 37 insertions(+), 5 deletions(-) + +commit b5da9918f1d7b90ebb230602cbfd9c4569449a61 +Author: Ell +Date: Fri Apr 19 04:24:24 2019 -0400 + + app: streamline GimpCurve + + In GimpCurve, replace the use of a fixed-length control-point array + with a dynamically-sized array. Adapt GimpCurve's interface, and + the rest of the code. + + In addition to simplifying the code, this fixes a bug where the + curve object could be broken by moving the mouse too fast (yep...), + and allows more accurate point placement, both in the GUI editor, + and through canvas interaction in the Curves tool (see issue #814). + + (cherry picked from commit b6d829a1b2f71d03185f0f7ed7c6b80fc0c2dfe6) + + app/core/gimpcurve.c | 372 + +++++++++++++++++++++++--------------- + app/core/gimpcurve.h | 16 +- + app/operations/gimpcurvesconfig.c | 81 ++++----- + app/operations/gimplevelsconfig.c | 17 +- + app/tools/gimpcurvestool.c | 39 ++-- + app/widgets/gimpcurveview.c | 218 ++++++++++++---------- + 6 files changed, 415 insertions(+), 328 deletions(-) + +commit f141fc4064a92a34dc11b97b8c5b1442e00313ad +Author: Ell +Date: Fri Apr 19 05:42:09 2019 -0400 + + app: fix gimp_operation_levels_map_input() for negative values + + ... when gamma != 1 + + (cherry picked from commit dc6ca2cf9a99d652fdda40d11b916eb725dd9ec4) + + app/operations/gimpoperationlevels.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 1158c0c10f84ccebb4b113c57750ad93ae95450c +Author: Aron Xu +Date: Fri Apr 19 15:52:43 2019 +0800 + + Update zh_CN translation of po-windows-installer + + po-windows-installer/zh_CN.po | 163 + ++++++++++++++++++++++-------------------- + 1 file changed, 84 insertions(+), 79 deletions(-) + +commit 0a05a2354e611feea222b00c87aa88f1a7415ade +Author: lumingzh +Date: Fri Apr 19 15:47:02 2019 +0800 + + Update zh_CN translation + + po/zh_CN.po | 9692 + +++++++++++++++++++++++++++++++++++------------------------ + 1 file changed, 5709 insertions(+), 3983 deletions(-) + +commit 345a9a89faf244f469523fca91348bdfa86c54ab +Author: Julien Hardelin +Date: Thu Apr 18 16:16:17 2019 +0000 + + Update French translation + + po-python/fr.po | 425 + ++++++++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 382 insertions(+), 43 deletions(-) + +commit 65a2a2fd8b7c899160b1986e49459ab93ab5cea3 +Author: Ell +Date: Wed Apr 17 17:48:49 2019 -0400 + + app: in GimpCurveView, snap to curve when holding Ctrl + + In GimpCurveView, when holding down Ctrl while adding/dragging a + point, snap the y-coordinate to the original curve (at the start of + the drag). This is particularly useful for adding points along the + curve, without changing their y-coordinate. + + Likewise, have the coordinate indicator show the snapped + coordinate. + + (cherry picked from commit 8357c9ad645b6ecff6882b39b828440ccfe004e6) + + app/widgets/gimpcurveview.c | 15 +++++++++++++++ + app/widgets/gimpcurveview.h | 1 + + 2 files changed, 16 insertions(+) + +commit 1ecb868821da0f3d3499591df4d159e169a93d6a +Author: Ell +Date: Wed Apr 17 17:33:24 2019 -0400 + + app: in GimpCurveView, use relative motion when dragging point + + In GimpCurveView, when dragging an existing curve point, don't + immediately move the point to the cursor position uppon button + press, but rather move it relative to its current position as the + cursor moves. This allows selecting a point without moving it, and + adjusting its position more easily. + + Additionally, when the cursor hovers above a point, or when + dargging a point, have the coordinate indicator show the point's + position, rather than the cursor's. + + (cherry picked from commit 0b9737a3ed8cac8b49c18c9c6fa7bd8bd3343c8b) + + app/widgets/gimpcurveview.c | 40 ++++++++++++++++++++++++++++++++-------- + app/widgets/gimpcurveview.h | 2 ++ + 2 files changed, 34 insertions(+), 8 deletions(-) + +commit 8e9034cab811717f612b10ec6fc86a9b39bb2627 +Author: Sabri Ünal +Date: Wed Apr 17 19:23:19 2019 +0000 + + Update Turkish translation + + po-windows-installer/tr.po | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 7cb6df0db6004f0769b1c23be356d68a20e22e04 +Author: Sabri Ünal +Date: Wed Apr 17 19:01:38 2019 +0000 + + Update Turkish translation + + po-libgimp/tr.po | 88 + ++++++++++++++++++++++++++++---------------------------- + 1 file changed, 44 insertions(+), 44 deletions(-) + +commit a0948e81648324539515316e5248c7d6cbfb60e5 +Author: Sabri Ünal +Date: Wed Apr 17 19:01:18 2019 +0000 + + Update Turkish translation + + po-python/tr.po | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +commit 27f20dd99a2fb96ada51fbc423e88ca54d14854e +Author: Sabri Ünal +Date: Wed Apr 17 19:00:57 2019 +0000 + + Update Turkish translation + + po-tips/tr.po | 108 + +++++++++++++++++++++++++++++----------------------------- + 1 file changed, 54 insertions(+), 54 deletions(-) + +commit e17860fada92d6d3bb535ec3012b382ed453fd76 +Author: Sabri Ünal +Date: Wed Apr 17 18:58:44 2019 +0000 + + Update Turkish translation + + po-script-fu/tr.po | 35 ++++++++++++++++++----------------- + 1 file changed, 18 insertions(+), 17 deletions(-) + +commit 0ee8e5f17a97367b44f0e547dd53c77f7656e7a0 +Author: Ell +Date: Wed Apr 17 09:55:35 2019 -0400 + + app: add incremental mode to the Dodge/Burn tool + + Add an "Incremental" option to the Dodge/Burn tool, which, + similarly to the Paintbrush, Pencil, and Eraser tools, applies the + effect incrementally as the pointer moves. + + (cherry picked from commit 83184d16260ba3e8f0a5920ac439cd4d0dc1efeb) + + app/paint/gimpdodgeburn.c | 11 +++++++++-- + app/tools/gimppaintoptions-gui.c | 3 ++- + 2 files changed, 11 insertions(+), 3 deletions(-) + +commit 8009f62a773b175f16e752c03b3d778979433a8e +Author: Tim Sabsch +Date: Tue Apr 16 11:34:37 2019 +0000 + + Update German translation + + po/de.po | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit 9335c26b6858d88679fcdeb60f45bb3437d5b950 +Author: Tobias Ellinghaus +Date: Tue Apr 16 10:04:16 2019 +0200 + + plug-ins: Add layer support to TIFF writing + + (cherry picked from commit 7584969453ae39a79905f61930ef7a432319fe07) + + plug-ins/file-tiff/file-tiff-io.c | 28 ++- + plug-ins/file-tiff/file-tiff-load.c | 113 +++++---- + plug-ins/file-tiff/file-tiff-load.h | 1 + + plug-ins/file-tiff/file-tiff-save.c | 466 + ++++++++++++++++++++++++++---------- + plug-ins/file-tiff/file-tiff-save.h | 18 +- + plug-ins/file-tiff/file-tiff.c | 91 +++---- + plug-ins/ui/plug-in-file-tiff.ui | 26 +- + 7 files changed, 503 insertions(+), 240 deletions(-) + +commit d1d002250040ddacda39b1e802d5194a2805ed4a +Author: Jehan +Date: Mon Apr 15 23:52:53 2019 +0200 + + plug-ins: export linear WebP if and only if the work image was + 8-bit... + + ... linear itself AND if we export the profile. + Implement similar logics to WebP export as I did to JPEG in my + previous + commit. + + (cherry picked from commit b9458f8a6ee0a33703fc8712d07d3188ef6e9b69) + + plug-ins/file-webp/file-webp-save.c | 39 + ++++++++++++++++++++++++++++--------- + 1 file changed, 30 insertions(+), 9 deletions(-) + +commit ec132fa396116e7c27ea13401137492c6d8598fd +Author: Jehan +Date: Mon Apr 15 22:44:36 2019 +0200 + + plug-ins: export linear JPEG if and only if the work image was + 8-bit... + + ... linear itself AND if we export the profile. + + In most cases we want to save 8-bit image formats (here JPEG) as + non-linear, even though the work image may have been linear itself + (yet + with higher bit depth). The reasons are shadow posterization on + low bit + depth, and the fact that JPEG compression was designed for + perceptually + uniform RGB and introduces shadow artifacts with linear RGB (see + #1070, + message by Elle Stone). The only exception is when the creator was + working explicitly on 8-bit linear (not higher bit depth) AND if we + export the profile (otherwise most loaders around assume sRGB). In + such + a case, let's consider the creator knows what one is doing and + keep the + exported image linear. + + Similar logics is already used in PNG exporter (though a bit of a + variant since PNG supports 16-bit so it is instead: 8-bit linear + without + profile is promoted to 16-bit non-linear, and kept 8-bit linear with + profile). + + (cherry picked from commit 5f4cf53519b999f5c327746bd87c2186d6b1f7fa) + + plug-ins/file-jpeg/jpeg-save.c | 76 + +++++++++++++++++++++++++++++------------- + 1 file changed, 52 insertions(+), 24 deletions(-) + +commit bfe24a3b5059f000c66c3c410b69e93a7d815e42 +Author: Jehan +Date: Sun Apr 14 22:18:36 2019 +0200 + + Issue #3253: exporting to webp from 32-bit float linear image... + + ... produces incorrect result. + Similar to previous fixes to JPEG and PNG exporters. Here WebP always + export 8-bit per channel colors, so let's always keep it non-linear. + Simply when the original data was linear, if we save the profile, + convert it to sRGB before exporting. + + (cherry picked from commit 7a4b313b12b8bdea4cbd8003719fc6b255a707ff) + + plug-ins/file-webp/file-webp-save.c | 34 + ++++++++++++++++++++++++++++++---- + 1 file changed, 30 insertions(+), 4 deletions(-) + +commit b9ed4a7f14020363c5f800b3a05269c3262dbc21 +Author: Tim Sabsch +Date: Sat Apr 13 18:21:15 2019 +0000 + + Update German translation + + po-libgimp/de.po | 443 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 224 insertions(+), 219 deletions(-) + +commit 1c3317db707b6fc1fc7aa96a7b17f3f851ddcdc3 +Author: Jehan +Date: Fri Apr 12 17:14:57 2019 +0200 + + plug-ins: GimpPrecision has no NON_LINEAR and PERCEPTUAL values. + + Fixing commit 532866007d. + + plug-ins/file-jpeg/jpeg-save.c | 19 ++++++------------- + 1 file changed, 6 insertions(+), 13 deletions(-) + +commit 2608a7e2d6b2d1add4b3b139ee67cc4404417f5f +Author: Michael Natterer +Date: Fri Apr 12 16:30:29 2019 +0200 + + app: remove defines GIMP_BRUSH_FILE_VERSION and + GIMP_PATTERN_FILE_VERSION + + they were unused and wrong. Also clean up the brush and pattern + headers a bit. + + (cherry picked from commit aee6d44b6142c6413c753a3442173fe4fc6d1f1b) + + app/core/gimpbrush-header.h | 18 +++++++++--------- + app/core/gimppattern-header.h | 16 ++++++++-------- + 2 files changed, 17 insertions(+), 17 deletions(-) + +commit dcc297b6f75dee81f7ff3f0ae1f20481c2ff07af +Author: Jehan +Date: Fri Apr 12 14:48:15 2019 +0200 + + Issue #3224: Fill by Line Art Detection Bug (Fatal Error with Crash). + + Typos in gimp_pickable_contiguous_region_by_line_art(). + Thanks to Massimo for debugging these! + + (cherry picked from commit 5d5ced88a8f057172c57eebf216775871a99e182) + + app/core/gimppickable-contiguous-region.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 3ee3ca9c71833ed7a62923fd5af74d2d9968b082 +Author: Jehan +Date: Fri Apr 12 14:33:19 2019 +0200 + + Issue #3193: Wrong colors after exporting 8bpc RGB png from 32f... + + ... linear XCF. + When choosing a specific pixel format (other than "Automatic"), we + always export as non-linear. Therefore if we were going to save + a linear + profile, make sure we also convert it to sRGB too. + + (cherry picked from commit c5fae74ac1c70eb4789e7aaacd4d216b74878b78) + + plug-ins/common/file-png.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +commit 532866007d0f189f8bea5f149f979e8ebb53de23 +Author: Jehan +Date: Fri Apr 12 12:57:18 2019 +0200 + + Issue #1070: exporting to jpeg from 32-bit float linear image... + + ... produces jpeg in linear color space. + The problem was that we were anyway always exporting to non-linear + while + attaching a linear profile. A first approach would have been to export + in linear instead when the work image is linear, as proposed by + mitch in + a first version of the patch. Yet as Elle Stone notes, it is not + a great + idea to export 8-bit images as linear. + Instead let's continue to always export as non-linear, as we were + already doing. Yet when we also save a profile, and this one was + originally linear, let's convert it to sRGB TRC before exporting. + + (cherry picked from commit 8594275bb7a807feae997146d6dbf3ca72a1a0b1) + + plug-ins/file-jpeg/jpeg-save.c | 47 + +++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 44 insertions(+), 3 deletions(-) + +commit 1baba460db7a6a954c29fe41425075b5134f37c4 +Author: Rodrigo Lledó +Date: Thu Apr 11 11:13:48 2019 +0000 + + Update Spanish translation + + po-plug-ins/es.po | 330 + +++++++++++++++++++++++------------------------------- + 1 file changed, 143 insertions(+), 187 deletions(-) + +commit 84bc29b77ab76828ada4dab566a915c9f26faae4 +Author: Kukuh Syafaat +Date: Thu Apr 11 10:47:02 2019 +0000 + + Update Indonesian translation + + po-tips/id.po | 70 + +++++++++++++++++++++++++++-------------------------------- + 1 file changed, 32 insertions(+), 38 deletions(-) + +commit ed030168c265f25d62a4233f5f1beb755ac1e0b5 +Author: Kukuh Syafaat +Date: Thu Apr 11 10:29:36 2019 +0000 + + Update Indonesian translation + + po-windows-installer/id.po | 207 + ++++++++++++++++++++++++--------------------- + 1 file changed, 109 insertions(+), 98 deletions(-) + +commit dd5c4458c995c8d37cfd5fb0c3e867beed346a4a +Author: Michael Natterer +Date: Tue Apr 9 23:11:55 2019 +0200 + + app: fix legacy .gpb parsing code in gimp_brush_load_brush() + + Only seek back to after the end of the actual brush if a following + pattern was *not* found. Got this logic wrong in the original port of + the plug-in code. + + (cherry picked from commit 40863bffdd66c6402ce3c6be3d93fb82b9bfd5fe) + + app/core/gimpbrush-load.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +commit 5eb033d30b89cc6ccd57f8bd82c909501abf5474 +Author: Rodrigo Lledó +Date: Tue Apr 9 10:28:46 2019 +0000 + + Update Spanish translation + + po/es.po | 434 + +++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 228 insertions(+), 206 deletions(-) + +commit e073e0c55fe34acba76789dbb0ac7d5d6d6bce45 +Author: Emin Tufan Çetin +Date: Mon Apr 8 16:26:24 2019 +0000 + + Update Turkish translation + + po/tr.po | 628 + ++++++++++++++++++++++++++++++++------------------------------- + 1 file changed, 318 insertions(+), 310 deletions(-) + +commit d1ee74b250cbad118f917b962859a5de2c7c637e +Author: Michael Natterer +Date: Sun Apr 7 18:54:50 2019 +0200 + + configure.ac: post-release version bump to 2.10.11 + + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 596f8557499a9b531160b350547874aa66270d64 +Author: Michael Natterer +Date: Sun Apr 7 18:30:13 2019 +0200 + + configure.ac: bump versions for the 2.10.10 release + + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit bdb2584b634a77d1b2cd77b0590e220413c80157 +Author: Michael Natterer +Date: Sun Apr 7 14:22:12 2019 +0200 + + etc, docs: regenerate default gimprc and its manpage + + docs/gimprc.5.in | 24 +++++++++++++++++++++--- + etc/gimprc.in | 15 ++++++++++++--- + 2 files changed, 33 insertions(+), 6 deletions(-) + +commit 896a7b5a122a6226fb85fcde05d0fbb45137a325 +Author: Michael Natterer +Date: Sun Apr 7 13:36:45 2019 +0200 + + NEWS: some fixes + + NEWS | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +commit 6ec62f2d17f848e7896c1edf7e29bcfa16de57e4 +Author: Jehan +Date: Sat Apr 6 12:32:39 2019 +0200 + + desktop: prepare the GIMP 2.10.10 release. + + Hopefully it will happen! :-) + + (cherry picked from commit e0b958b22cdf08722461369dc585b8ca2b03ab9c) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 054da8591121d4da70822afbd5286fd759f3e8bc +Author: Jehan +Date: Sat Apr 6 11:23:50 2019 +0200 + + po: always end the "Keywords" list with a semicolon! + + It is written in a translator comment and is important because + it breaks + some automatic validation tools along the way to packaging. Thanks for + following the translation comments! :-) + + po/mr.po | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ce2e7420a89ca39fc90fe384748ed079cf498b74 +Author: Ell +Date: Sat Apr 6 05:19:33 2019 -0400 + + Issue #2665 - Settings changes in Rectangle and Ellipse selection + don't "stick" + + In GimpRectangleSelectTool, update the selection upon changes to + the "antialias", "feather", "feather-radius", "round-corners", and + "corner-radius" options, so that they take effect immediately, + without having to change the selection bounds. + + (cherry picked from commit 2da6cefa3f5e301a0b6cc7674f377ecec0725eb7) + + app/tools/gimprectangleselecttool.c | 134 + ++++++++++++++++++++++-------------- + 1 file changed, 81 insertions(+), 53 deletions(-) + +commit 91642674cea929e8a9643361504e00256073306a +Author: Christian Kirbach +Date: Thu Apr 4 23:20:26 2019 +0000 + + Update German translation + + po/de.po | 4470 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 2582 insertions(+), 1888 deletions(-) + +commit abc2726c067c656d65625a50798796e0e674217a +Author: Marco Ciampa +Date: Thu Apr 4 16:12:59 2019 +0200 + + Updated Italian translation + + po-python/it.po | 363 + +++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 331 insertions(+), 32 deletions(-) + +commit 69b056a0b5b99e7e650f1855f9cf464901b9f8c0 +Author: Jehan +Date: Mon Apr 1 17:04:56 2019 +0200 + + INSTALL: update info about libmypaint. + + Since recently, one can also install the new "libmypaint-v1" + branch. It + is not so different, but has several fixes. Among them, it fixes + building with recent automake. + + (cherry picked from commit 1028345b7b298ebb9be1af7d65b94d9b60ed9294) + + INSTALL.in | 3 +++ + 1 file changed, 3 insertions(+) + +commit 027e5ed5b0cb63cebe389fb523493db6654f17fe +Author: Marco Ciampa +Date: Mon Apr 1 12:57:48 2019 +0200 + + Updated Italian translation + + po/it.po | 269 + +++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 143 insertions(+), 126 deletions(-) + +commit 8c7e2a9e03ffb3ebbf385350231a81eee59b26cd +Author: Ell +Date: Sun Mar 31 14:54:30 2019 -0400 + + Issue #3025 - "File/New" doesn't honor "precision" choice ... + + ... for "Edit/Preferences/Default Image" + + In GimpTemplateEditor, don't use gimp_prop_enum_combo_box_new() for + the "Precision" combo-box, and rather synchronize the combo-box and + the template manually, since we only want to update the "Gamma" + combo-box according to the precision when it changes through the + UI, and not when the template's precision otherwise changes. + + This fixes an issue where we'd always set the default gamma value + when resetting the editor's template, overwriting the template's + original gamma value. + + (cherry picked from commit 033082dd9a0850a86c1b0b76187afd4725df4e9f) + + app/widgets/gimptemplateeditor.c | 47 + +++++++++++++++++++++++++++++++++++----- + 1 file changed, 41 insertions(+), 6 deletions(-) + +commit 7e26d664642853d66f7cff2a772c25b1ee5fbf2b +Author: Rūdolfs Mazurs +Date: Sun Mar 31 13:28:24 2019 +0000 + + Update Latvian translation + + po-python/lv.po | 409 + ++++++++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 369 insertions(+), 40 deletions(-) + +commit 3c674c937cf53dfd65cdef6f10cce08ac568c963 +Author: Rūdolfs Mazurs +Date: Sun Mar 31 12:24:10 2019 +0000 + + Update Latvian translation + + po/lv.po | 5678 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 3313 insertions(+), 2365 deletions(-) + +commit 402c1f1f7bb9defb870cdc12db5942d14bfc635c +Author: Rūdolfs Mazurs +Date: Sun Mar 31 09:40:41 2019 +0000 + + Update Latvian translation + + po-plug-ins/lv.po | 1474 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 806 insertions(+), 668 deletions(-) + +commit 4204dab5f690a360b8b60b55051431bf231bae44 +Author: Rūdolfs Mazurs +Date: Sun Mar 31 09:25:15 2019 +0000 + + Update Latvian translation + + po-libgimp/lv.po | 453 + +++++++++++++++++++++++++++---------------------------- + 1 file changed, 223 insertions(+), 230 deletions(-) + +commit f810d59928f2bd6cf3a94850ee9ee2581b808b02 +Author: Rūdolfs Mazurs +Date: Sun Mar 31 09:21:39 2019 +0000 + + Update Latvian translation + + po-script-fu/lv.po | 300 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 165 insertions(+), 135 deletions(-) + +commit 522e497457d35816cb13468c5a27e81b9309d4fb +Author: Ell +Date: Sun Mar 31 04:59:23 2019 -0400 + + app: in GimpChunkIterator, avoid preparing rect before merging + + In GimpChunkIterator, avoid preparing the current rect before + merging it back to the iterator's region, to save some work. + + Additionally, strengthen the iterator's invariants and simplify + code. + + (cherry picked from commit c95502266ada77d9f1ab43631581dd4c393cd3f0) + + app/core/gimpchunkiterator.c | 51 + ++++++++++++++++++++++---------------------- + 1 file changed, 25 insertions(+), 26 deletions(-) + +commit 1cdd32955e87f27a8f3041f42e734ba3515966f2 +Author: Ell +Date: Sat Mar 30 19:24:00 2019 -0400 + + app: fix image-window UI-manager update while a projection is being + rendered + + Set the priority of the image window's UI-manager update idle + slightly higher than the projection idle priority, so that the + image actions are updated during projection rendering. + + (cherry picked from commit 50aaeef6a0fed11b8c291fd956c0ca36f3d9eaa2) + + app/display/gimpimagewindow.c | 8 ++++++-- + app/gimp-priorities.h | 3 ++- + 2 files changed, 8 insertions(+), 3 deletions(-) + +commit 191c9812f39d8035eaf0570c5cc51e382b2dc583 +Author: Ell +Date: Sat Mar 30 16:36:16 2019 -0400 + + app: small fix to GimpChunkIterator + + (cherry picked from commit d182c41f8f19da2f9f13176c4adbe33d65ff2113) + + app/core/gimpchunkiterator.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit c5d34b249605e5d1269f0ef8044e4de7f994af8e +Author: Ell +Date: Sat Mar 30 12:01:06 2019 -0400 + + app: in gimp:fill-source, align result to tile grid + + In gimp:fill-source, align the result rect to the drawable buffer's + tile grid, so that all tiles are COWed for solid-color fills. + + (cherry picked from commit fa31854a66f5317867a8dbe2137969444ad59c93) + + app/operations/gimpoperationfillsource.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +commit 9c9467e076a2c24b8fa536de5c5890a7f39abb2a +Author: Ell +Date: Sat Mar 30 11:11:10 2019 -0400 + + Issue #2090 - Crash when using transform tools + + In GimpTransformGridTool, avoid producing non-finite coordinate + and angle values. In particular, this fixes a crash in + gimp_transform_grid_tool_get_cursor() as a result of a NaN angle + value being converted to a negative integer, hitting an assert. + + (cherry picked from commit be7906c05cc0437428d157cdd2db2cf208670d7c) + + app/display/gimptooltransformgrid.c | 26 ++++++++++++++++++-------- + 1 file changed, 18 insertions(+), 8 deletions(-) + +commit 4d2a5d1a945685bc8a8b4256cd93cee9554e4d14 +Author: Ell +Date: Sat Mar 30 10:01:03 2019 -0400 + + app: more responsiveness improvements to GimpChunkIterator + + Improve GimpChunkIterator's responsiveness to changes in processing + speed. + + (cherry picked from commit 91f4c809d8c0ae7366f10fd1d182923370feae4b) + + app/core/gimpchunkiterator.c | 60 + ++++++++++++++++++++++++++++++++++---------- + 1 file changed, 47 insertions(+), 13 deletions(-) + +commit 1096bdfd5312dd0d9772765a5b6e8612a0b7aadd +Author: Piotr Drąg +Date: Sat Mar 30 14:34:50 2019 +0100 + + Update Polish translation + + po/pl.po | 187 + +++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 97 insertions(+), 90 deletions(-) + +commit f0b5080f83faa81cb9afb954eaf1e3d6441b7399 +Author: Ell +Date: Fri Mar 29 04:47:24 2019 -0400 + + app: don't invalidate viewable preview when thawed unless explicitly + requested + + In GimpViewable, don't invalidate the preview when thawed, unless + there was an explicit call to gimp_viewable_invalidate_preview() + while it was frozen. This avoids invalidating the previews of an + invisible drawable's ancestors when the drawable's preview is + frozen/thawed. + + (cherry picked from commit 9dabad4cb9d1cfc14cd07a6ac7c739033acdc622) + + app/core/gimpviewable.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +commit c8ea2f45f9fe50ac2211b7bac4e24790720ab43a +Author: Ell +Date: Thu Mar 28 16:52:08 2019 -0400 + + app: avoid risky alloca() in gimp_brush_save() + + Replace an arbitrarily-sized g_alloca() with g_malloc() in + gimp_brush_save(). + + (cherry picked from commit 24ed9dbdf5414864103e3433ffd0c58995696ecb) + + app/core/gimpbrush-save.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit c347f8770c689a4fa38e62a5d1edb6cddbb0fb7b +Author: Ell +Date: Thu Mar 28 12:35:32 2019 -0400 + + app: avoid freezing image preview if drawable is not attached + + When freezing/thawing a top-level drawable's preview, only freeze/ + thaw the image preview if the drawable is attached. + + (cherry picked from commit de36e33347815e78aee34f7fcd5cb3e22414af03) + + app/core/gimpdrawable.c | 10 ++++++---- + app/core/gimplayermask.c | 10 ++++++---- + 2 files changed, 12 insertions(+), 8 deletions(-) + +commit b9ca245736968d1e2bf00ea0631ed963513a5a35 +Author: Ell +Date: Thu Mar 28 12:03:49 2019 -0400 + + app: when freezing a drawable's preview, freeze ancestors' previews + + Add GimpViewable::preview_{freeze,thaw}() virtual functions, which + get called when the viewable's preview is frozen/thawed. Implement + the functions in GimpDrawable, recursively freezing the parent + drawable's preview (or the image's preview, for top-level + drawables) while the drawable's preview is frozen. For layer + masks, freeze the associated layer's parent. + + This avoids updating layer-group/image previews while painting on, + or applying a filter to, a descendant layer. This both reduces + lag, and fixes a discrepancy between the layer's preview, which + isn't updated, and its parents' previews. + + (cherry picked from commit e2ea2e4a8283d655453dee3bf270ce03f1f22b81) + + app/core/gimpdrawable.c | 26 ++++++++++++++++++++++++++ + app/core/gimplayermask.c | 40 ++++++++++++++++++++++++++++++++++++++++ + app/core/gimpviewable.c | 13 ++++++++++++- + app/core/gimpviewable.h | 3 +++ + 4 files changed, 81 insertions(+), 1 deletion(-) + +commit 273a0c4395ed04ea11277485c8e90b39d224f0ef +Author: Jehan +Date: Thu Mar 28 14:06:17 2019 +0100 + + app: change antialias feature in fill by line art into Feather Edges. + + This was actually more of a feathering feature I added earlier, and we + already have a function for that: gimp_gegl_apply_feather(). This is + using a gaussian blur, just as what I was doing anyway. This + commit also + adds the "Feather Radius" scale, similar to other tools with the + "Feather Edges". So that makes it consistent (and more useful as + you can + adapt to your needs). + + (cherry picked from commit d821b088e2e40c46498aca13c938700bf48e5521) + + app/core/gimpdrawable-bucket-fill.c | 36 +++++++------------------ + app/core/gimpfilloptions.c | 53 + +++++++++++++++++++++++++++++++++++++ + app/core/gimpfilloptions.h | 6 +++++ + app/tools/gimpbucketfilloptions.c | 40 +++++++++++++++++++++++++--- + app/tools/gimpbucketfilloptions.h | 2 ++ + app/tools/gimpbucketfilltool.c | 4 +++ + 6 files changed, 110 insertions(+), 31 deletions(-) + +commit aa7fc3f08d81be9007e31afb461cb9d938f1b5c0 +Author: Ell +Date: Wed Mar 27 18:07:04 2019 -0400 + + app: add GTK+ patch allowing controlling combo-box popup style + + Add a GTK+ patch providing a "popup-style" combo-box style + property, allowing to manually control the combo-box popup-style. + Use the LIST style for all combo-boxes in our system-wide gtkrc + file; this only has effect if GTK+ was built with the above patch. + + build/patches/gtk+-2.24-gimp-issue-2828-0002.patch | 766 + +++++++++++++++++++++ + etc/gtkrc | 13 + + 2 files changed, 779 insertions(+) + +commit 1749fd995c5ab3e6059775dc27890e5646c10d12 +Author: Ell +Date: Wed Mar 27 17:52:45 2019 -0400 + + app: add GTK+ patch to fix list-style combo-box popup width + + Add a GTK+ patch that makes sure that list-style combo-box popups + are never narrower than their content. This fixes the popup width + when using the System theme on Windows, for combo-boxes whose popup + cell-layout is wider than the combo-box cell-layout. It is also + required for the next commit. + + build/patches/gtk+-2.24-gimp-issue-2828-0001.patch | 73 + ++++++++++++++++++++++ + themes/System/gtkrc | 1 - + 2 files changed, 73 insertions(+), 1 deletion(-) + +commit 7edbad3144a0a68210ebcc3f2947d916072172ba +Author: Ell +Date: Wed Mar 27 18:01:34 2019 -0400 + + app: include system gtkrc file in themerc + + Include the system-wide gtkrc file, in addition to the user- + specific gtkrc file, in the generated themerc file, instead of + copying the former into the latter when creating the user's + gimpdir. This allows us to modify the system-wide gtkrc file, and + having the changes take effect in existing installations. + + app/core/gimp-user-install.c | 1 - + app/gui/themes.c | 100 + +++++++++++++++++++++++++++---------------- + 2 files changed, 63 insertions(+), 38 deletions(-) + +commit f19ebb6269267d86b6251306eb8a1308c17915cf +Author: Ell +Date: Wed Mar 27 16:50:57 2019 -0400 + + app: revert combo-box drop-down changes + + Revert the use of gtk_combo_box_set_wrap_width() to change the + combo-box drop-down style, except for the status-bar unit combo. + See https://gitlab.gnome.org/GNOME/gimp/issues/2828#note_421312 for + the rationale. + + This reverts commits 1d984542e9a4673dc81baa3d60d389e3bb3c2d03, + 68a33ab5bda1470453d5856afc5bc499631b6da7, and + 6dfca83c2a5ee8d053150e85e2047702d67eb4e7. + + (cherry picked from commit 846d242f301b6ee8d62169d18bca61eaa3057a8b) + + app/display/gimpscalecombobox.c | 3 --- + app/display/gimpstatusbar.c | 3 +++ + app/widgets/gimpcontainercombobox.c | 3 --- + app/widgets/gimplanguagecombobox.c | 3 --- + libgimpwidgets/gimpcolorprofilecombobox.c | 3 --- + libgimpwidgets/gimpintcombobox.c | 3 --- + libgimpwidgets/gimpstringcombobox.c | 3 --- + libgimpwidgets/gimpunitcombobox.c | 3 --- + 8 files changed, 3 insertions(+), 21 deletions(-) + +commit 9063b937fe1b667e46769770e5938de01aa7c6f3 +Author: Ell +Date: Wed Mar 27 15:38:06 2019 -0400 + + app: use gimp:fill-source in gimp_drawable_edit_fill() + + In gimp_drawable_edit_fill(), when performing a non-direct fill, + use a GimpDrawableFilter with gimp:fill-source, added in the + previous commit, instead of using gimp_drawable_apply_buffer() with + an intermediate fill buffer. This avoids allocating a full-size + fill buffer, which may occupy a lot of space in pattern fills. + + (cherry picked from commit 234f76b6fb7402dda6cd1ab4a95c33c666768002) + + app/core/gimpdrawable-edit.c | 52 + +++++++++++++++++++++++++------------------- + 1 file changed, 30 insertions(+), 22 deletions(-) + +commit 75822d3d7bacd0932821e9907633d55fd31a8825 +Author: Ell +Date: Wed Mar 27 15:10:53 2019 -0400 + + app: add gimp:fill-source operation + + Add a new gimp:fill-source operation, which can act as a source + node for fill operations, instead of a fill buffer. The op takes + a GimpFillOptions object, a drawable, and a pattern offset, and + uses gimp_fill_options_create_buffer() to produce its output. + + This allows performing the entire fill operation in chunks as a + graph, instead of allocating a full-size fill buffer, which can + can occupy a lot of space for pattern fills. + + (cherry picked from commit 6b0337e384e396adf74ac979cf2864958f7841ee) + + app/operations/Makefile.am | 2 + + app/operations/gimp-operations.c | 2 + + app/operations/gimpoperationfillsource.c | 248 + +++++++++++++++++++++++++++++++ + app/operations/gimpoperationfillsource.h | 55 +++++++ + 4 files changed, 307 insertions(+) + +commit 2341117517050e5ffffbb36f77e5235078ade831 +Author: Ell +Date: Wed Mar 27 14:50:20 2019 -0400 + + app: improve gimp_drawable_fill_buffer() for patterns + + In gimp_drawable_fill_buffer(), when the fill-source is a pattern, + avoid going through an intermediate buffer when there's no profile + transform, and use the destination-buffer format for the + intermediate buffer, instead of the pattern format, when there is a + profile transform. + + (cherry picked from commit 3c1634ee0d5a1d6bbf448253abe9b88b2172a2c2) + + app/core/gimpdrawable-fill.c | 34 +++++++++++++++++++++------------- + 1 file changed, 21 insertions(+), 13 deletions(-) + +commit 1416571389dfd867c13c20b83640e504fd174573 +Author: Ell +Date: Wed Mar 27 12:33:21 2019 -0400 + + app: use compositing format for fill buffer + + Add gimp_fill_options_get_format(), which returns the format to be + used for the fill buffer; this is the same format used during + compositing. Use this format in gimp_fill_options_create_buffer(), + instead of the drawable format. + + This fixes the result of fill operations when the fill color/ + pattern is not representable in the drawable format, and speeds up + color fills by avoiding color-conversion for the fill buffer during + processing. + + (cherry picked from commit 245a17c79f81413a47d9df309b20af46e8f15c44) + + app/core/gimpfilloptions.c | 21 ++++++++++++++++++++- + app/core/gimpfilloptions.h | 3 +++ + 2 files changed, 23 insertions(+), 1 deletion(-) + +commit 6e06e5e85644af954f01c1bdc9d0aa68513619e4 +Author: Ell +Date: Wed Mar 27 15:06:32 2019 -0400 + + app: in GimpDrawableFilter, set underlying operation + + In GimpDrawableFilter, use + gimp_gegl_node_set_underlying_operation() to the the input + operation node as the underlying operation of the filter node. + + (cherry picked from commit b0dfc1e7c721263a8b293513ebb22aa3b7dd0348) + + app/core/gimpdrawablefilter.c | 1 + + 1 file changed, 1 insertion(+) + +commit b5b5d717086ec1e68d0230380ec1c09c926804fe +Author: Ell +Date: Wed Mar 27 15:04:27 2019 -0400 + + app: use underlying operation in gimp_gegl_apply_cached_operation() + + In gimp_gegl_apply_cached_operation(), use the underlying + operation, as returned from + gimp_gegl_node_get_underlying_operation(), for testing whether the + operation is a point operation, for the purpose of avoiding + duplicating the input buffer. Likewise, avoid duplicating the + buffer when the underlying operation is a source operation. + + (cherry picked from commit 213b126c6e515361c90d5ce8e85d2f1ed323c74e) + + app/gegl/gimp-gegl-apply-operation.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +commit 5e212fe0dd46c01e14e30dd9a1f8e7f7fbeb7bc3 +Author: Ell +Date: Wed Mar 27 14:58:30 2019 -0400 + + app: add gimp_gegl_node_{set,get}_underlying_oepration() + + ... which allow setting/getting the "underlying operation" node of + a graph node. For example, GimpDrawableFilter constructs a complex + graph around a given operation node, which would be the underlying + operation of the graph. This allows querying the properties of the + underlying operation, given only the graph. + + In recursive cases, gimp_gegl_node_get_underlying_operation() + returns the most-nested underlying operation; when no underlying + operation has been set, gimp_gegl_node_get_underlying_operation() + returns the input node. + + (cherry picked from commit eb5e473665679bff527e279585e6b8181e1bdf56) + + app/gegl/gimp-gegl-utils.c | 27 +++++++++++++++++++++++++++ + app/gegl/gimp-gegl-utils.h | 36 ++++++++++++++++++++---------------- + 2 files changed, 47 insertions(+), 16 deletions(-) + +commit 3518ab919784885dd5bc24947db3cad72c62ea74 +Author: Ell +Date: Wed Mar 27 14:31:14 2019 -0400 + + app: add gimp_gegl_node_is_source_operation() + + ... which determines if a node is a source operation. + + (cherry picked from commit ff13e55c16bd936b9d88635e885dbfc282c9a9d3) + + app/gegl/gimp-gegl-utils.c | 15 +++++++++++++++ + app/gegl/gimp-gegl-utils.h | 1 + + 2 files changed, 16 insertions(+) + +commit 55569d512ee606b26d6489bd12a3162969f9a40d +Author: Ell +Date: Wed Mar 27 12:29:25 2019 -0400 + + app: in gimp_drawable_apply_buffer(), work in chunks + + In gimp_drawable_real_apply_buffer(), use GimpChunkIterator to blit + the applicator's output to the drawable's buffer in chunks, to + minimize the space used for intermediate results. + + (cherry picked from commit 8f845d3a51ceb8a742bed0e83c893171ed506604) + + app/core/gimpdrawable-combine.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +commit 7c6125ce67cdf77ae8924cc0b7692ad72d88661c +Author: Ell +Date: Wed Mar 27 11:10:26 2019 -0400 + + app: improve responsiveness of GimpChunkIterator + + In GimpChunkIterator, redajust the target area at each step, + instead of at each iteration, to adapt more quickly to the current + processing speed. To avoid creating uneven chunks as a result, + only change the chunk height at the beginning of rows, unless the + resulting area would be more than twice as big as the target area. + + (cherry picked from commit e904b712425e6e099389149d6a2a8055de9f13b3) + + app/core/gimpchunkiterator.c | 204 + ++++++++++++++++++++++++++----------------- + 1 file changed, 123 insertions(+), 81 deletions(-) + +commit f81c1cac10c550838b676f52e1242fd97576a1a0 +Author: Ell +Date: Tue Mar 26 04:54:59 2019 -0400 + + app: preserve projection priority rect across structure/bounds changes + + In GimpProjection, store the priority rect in image coordinates, + and only convert it to projectable coordinates when initializing + the chunk-iterator's priority rect. This allows us to preserve the + priority rect across projectable structure/bounds changes. + + (cherry picked from commit 9d80ccc3e66ef1d9367bd1cdc80d7f01b076fe90) + + app/core/gimpprojection.c | 80 + +++++++++++++++++++---------------------------- + 1 file changed, 33 insertions(+), 47 deletions(-) + +commit 6473daa05bae41dd01af221a9f269e4addab8511 +Author: Daniel Korostil +Date: Mon Mar 25 21:33:10 2019 +0000 + + Update Ukrainian translation + + po-plug-ins/uk.po | 1483 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 819 insertions(+), 664 deletions(-) + +commit bb747c58ed0da729b9fc4a880b8ef40aef989ce4 +Author: Daniel Korostil +Date: Mon Mar 25 20:06:19 2019 +0000 + + Update Ukrainian translation + + po-script-fu/uk.po | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +commit 03c98739bdda3d939c86aac9ea302197502553d7 +Author: Daniel Korostil +Date: Mon Mar 25 20:05:04 2019 +0000 + + Update Ukrainian translation + + po/uk.po | 151 + ++++++++++++++++++++++----------------------------------------- + 1 file changed, 52 insertions(+), 99 deletions(-) + +commit 7d3b9353273fcd34dd2ed38abdabe0123d5955f1 +Author: Daniel Korostil +Date: Mon Mar 25 20:03:58 2019 +0000 + + Update Ukrainian translation + + po-windows-installer/uk.po | 32 ++++++++++++++++++-------------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +commit b9375a3873f4d397df50d74c833c6f645a807bec +Author: Daniel Korostil +Date: Mon Mar 25 20:01:52 2019 +0000 + + Update Ukrainian translation + + po-python/uk.po | 352 + +++++++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 348 insertions(+), 4 deletions(-) + +commit 81afa73bf33b509b0c117c79d0ec8e3751d79a1f +Author: Dimitris Spingos +Date: Mon Mar 25 19:44:45 2019 +0200 + + Updated Greek translation + + po-plug-ins/el.po | 1477 ++++++++-------- + po-python/el.po | 403 ++++- + po-script-fu/el.po | 300 ++-- + po/el.po | 4743 + ++++++++++++++++++++++++++++++---------------------- + 4 files changed, 4049 insertions(+), 2874 deletions(-) + +commit ebaf09e7cc6a579709962315e08aae433f017cd0 +Author: Ell +Date: Mon Mar 25 09:14:06 2019 -0400 + + app: don't disable filter format conversion if != drawable format + + In gimp_drawable_merge_filter(), don't disable the filter + applicator's output-format conversion node if the output format is + different than the drawable's format, since it may change the + result. + + (cherry picked from commit 30da2f3d6fbf426b6a7b90074552efdd6515c23b) + + app/core/gimpdrawable-filters.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 269f2ca0fc78aa94c58d12da66df81f1c101a504 +Author: Ell +Date: Mon Mar 25 08:58:03 2019 -0400 + + app: skip cache and format conversion when merging a drawable filter + + In gimp_drawable_merge_filter(), disable the filter applicator's + cache and output-format conversion nodes before processing the + uncached region of the filter, so that the result is written + directly to the drawable's buffer. + + (cherry picked from commit 733a6ec01c53ec7c2be26e9af3da7b0c8b4c9497) + + app/core/gimpdrawable-filters.c | 26 ++++++++++++++++++++++---- + app/core/gimpdrawable-operation.c | 8 +------- + app/gegl/gimpapplicator.c | 16 ++++++++++++++++ + app/gegl/gimpapplicator.h | 2 ++ + 4 files changed, 41 insertions(+), 11 deletions(-) + +commit fef2c3423a4a9737ae650a4762137adc8e769510 +Author: Ell +Date: Sun Mar 24 14:41:28 2019 -0400 + + app: small fix to gimp_gegl_mask_combine_ellipse_rect() + + (cherry picked from commit d4689441fe139b60d54b1a4ca298b5b8bdd1358e) + + app/gegl/gimp-gegl-mask-combine.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 9ac360a125750996eda1e6ca093eb1188ee52514 +Author: Ell +Date: Sun Mar 24 03:06:19 2019 -0400 + + Issue #3142 - Filters on-canvas preview doesn't work ... + + ... immediately after an image precision change + + When flushing a projection, make sure it has a buffer, instead of + bailing if it doesn't. We rely on the image projection's "update" + signal to update the display after certain operations that free the + buffer, which would previously fail to happen, and cause subsequent + flushes to be ignored until the buffer is explicitly accessed. + + This fixes commit b07f8102738d8225c1e77e6b4f68ae12468d8a2b. + + (cherry picked from commit 106df3b794cfb52e3ac5e716e462427a4fae5507) + + app/core/gimpprojection.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 8f3da0e8c4959ec0d1b82890cef23cccf5aa44cd +Author: Alex Samorukov +Date: Sat Mar 23 23:33:07 2019 +0000 + + Allow compilation on MacOS 10.5 leopard: issues #2923, #2924 and #2925 + + app/core/gimpimage-convert-indexed.c | 32 + ++++++++++++++++---------------- + app/widgets/gimpcriticaldialog.c | 3 ++- + app/widgets/gimpselectiondata.c | 14 +++++++------- + libgimpwidgets/gimppickbutton-quartz.c | 5 +++++ + plug-ins/common/web-browser.c | 3 ++- + 5 files changed, 32 insertions(+), 25 deletions(-) + +commit f540f6c0a568e4c24869a012bedbf1ee59ac2930 +Author: Martin Srebotnjak +Date: Fri Mar 22 21:51:38 2019 +0100 + + Updated Slovenian translation + + po-python/sl.po | 371 + ++++++++++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 335 insertions(+), 36 deletions(-) + +commit 6a38c58cda51e396e2fcc394ccc00f6adbc593f6 +Author: Martin Srebotnjak +Date: Fri Mar 22 21:50:49 2019 +0100 + + Updated Slovenian translation + + po-script-fu/sl.po | 291 + ++++++++++++++++++++++++++++------------------------- + 1 file changed, 155 insertions(+), 136 deletions(-) + +commit e5026997b943d83574bd897725a8dd9f3032468a +Author: Martin Srebotnjak +Date: Fri Mar 22 21:46:14 2019 +0100 + + Updated Slovenian translation + + po-plug-ins/sl.po | 1386 + +++++++++++++++++++++++++++-------------------------- + 1 file changed, 719 insertions(+), 667 deletions(-) + +commit f1c5b3978af5f05ae617d2fa1639911c478fb698 +Author: Martin Srebotnjak +Date: Fri Mar 22 21:34:43 2019 +0100 + + Updated Slovenian translation + + po-libgimp/sl.po | 444 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 225 insertions(+), 219 deletions(-) + +commit 041107f80187e1a8444172b67ad8e76091659d15 +Author: Martin Srebotnjak +Date: Fri Mar 22 21:28:18 2019 +0100 + + Updated Slovenian translation + + po/sl.po | 4984 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 2860 insertions(+), 2124 deletions(-) + +commit b8d94342ca3394c2cf93b9f6b1982323564ad819 +Author: Ell +Date: Wed Mar 20 17:45:03 2019 -0400 + + Issue #3134 - Deleting last layer of group not updating image + + In gimp_group_layer_get_size(), make sure to always set *width and + *height, even when the group is empty, so that when the function is + called through gimp_projectable_get_size() by the group's + projection, the correct size is reported. This makes sure we + update the correct area when the group becomes empty. + + (cherry picked from commit a712308f20a216f98a03dbd60db0476aedfad2fe) + + app/core/gimpgrouplayer.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +commit dc8fad6ac67fbd0515049eb1a449c4d81d2fa23d +Author: Ell +Date: Wed Mar 20 15:30:35 2019 -0400 + + app: improve gimpchannel-{combine,select} + + In gimpchannel-select, move some of the common functionality of the + various gimp_channel_select_foo() functions to gimpchannel-combine. + Furthermore, don't special-case CHANNEL_OP_INTERSECT, but rather + pass it over to gimpchannel-combine, which is now prepared to + handle it in all functions, as per the previous commits. + + In gimpchannel-combine, factor out the common functionality of the + various gimp_channel_combine_foo() functions into a pair of + gimp_channel_combine_{start,end}() functions, which are called + before/after the actual gimp_gegl_mask_combine_foo() function, + respectively. In particular, these functions deal with calculating + the new channel bounds. Previously, the various + gimp_gegl_mask_combine_foo() functions would implicitly invalidate + the channel bounds (since commit + d0ae244fe85a502446fe9c7b82afdf1b89498966), rendering the bounds- + recalculation code ineffective. This avoids manually recalculating + the bounds in many cases, speeding up selection operations. + + (cherry picked from commit 8e77347cac1fa4e2ed70578cc2ac88bb4558ee06) + + app/core/gimpchannel-combine.c | 443 + ++++++++++++++++++++++++++++++++--------- + app/core/gimpchannel-select.c | 64 ++---- + 2 files changed, 374 insertions(+), 133 deletions(-) + +commit fa79923d4b46757c000eba8b87acc919904f8f0d +Author: Ell +Date: Wed Mar 20 09:39:40 2019 -0400 + + app: improve gimp_gegl_mask_combine_buffer() + + Simplify code, use gimp_gegl_buffer_copy() for CHANNEL_OP_REPLACE + when possible, improve value clipping, and parallelize processing. + + (cherry picked from commit a227c8e94d1463a031096bd5aa6007090e59aace) + + app/gegl/gimp-gegl-mask-combine.cc | 203 + ++++++++++++++++++++----------------- + 1 file changed, 111 insertions(+), 92 deletions(-) + +commit 9b76ea070d2b8da7185beb70de7d467ab60579db +Author: Ell +Date: Wed Mar 20 09:27:08 2019 -0400 + + app: improve gimp_gegl_mask_combine_ellipse[_rect]() + + Improve gimp_gegl_mask_combine_ellipse_rect() -- the funciton + responsible for rendering ellipse/rounded-rectangle selections. + + Most notably, this commit significantly improves the function's + performance, by identifying whole tiles, whole rows, or parts of a + row, that are fully inside, or fully outside, the ellipse, and + filling them in bulk, instead of calculating the anti-aliasing + value at each pixel, which is now only done along the + circumference. + + This commit also improves anti-aliasing, by more accurately + approximating the distance from a pixel to the ellipse, and by + normalizing the distance according to the pixel's cross-section + length in the direction of the said point. In particular, we + guarantee that pixels that are fully inside/outside the ellipse + have a value of 1/0, respectively, facilitating the aforementioned + optimization. + + Additionally, this commit fixes various edge cases where several + primitives coincide at a single pixel (in the rounded-rectangle + case), adds support for CHANNEL_OP_INTERSECT, and parallelizes + processing. + + (cherry picked from commit 1044342393e1da6048b948f3a02f70acd591dfae) + + app/core/gimpchannel-combine.c | 45 +-- + app/core/gimpchannel-combine.h | 4 +- + app/gegl/gimp-gegl-mask-combine.cc | 600 + ++++++++++++++++++++++--------------- + app/gegl/gimp-gegl-mask-combine.h | 4 +- + 4 files changed, 365 insertions(+), 288 deletions(-) + +commit d0a3d81c4a8c978e4b28455ae18c13e4d30c0666 +Author: Ell +Date: Tue Mar 19 16:17:47 2019 -0400 + + app: improve gimp_gegl_mask_combine_rect() + + Simplify the code, and add support for CHANNEL_OP_INTERSECT. + + (cherry picked from commit 8a6e1c907dbb9bc790257dee4485f947798b125d) + + app/gegl/gimp-gegl-mask-combine.cc | 37 + ++++++++++++++++++++++++------------- + 1 file changed, 24 insertions(+), 13 deletions(-) + +commit 15e8b53f0ca8b0df3d70a1d0466051d53080fbca +Author: Ell +Date: Tue Mar 19 16:11:34 2019 -0400 + + app: convert gimp-gegl-mask-combine to C++ + + ... in preparation for next commits. + + (cherry picked from commit 5198d3c32dbd86855131aa7e7811c99df539c34d) + + app/gegl/Makefile.am | 2 +- + ...-gegl-mask-combine.c => gimp-gegl-mask-combine.cc} | 19 + ++++++++++++------- + 2 files changed, 13 insertions(+), 8 deletions(-) + +commit 2a2ad4324d564192791beb6b780744db33d7fdc0 +Author: Ell +Date: Wed Mar 20 09:18:12 2019 -0400 + + app: add gimp_babl_format_change_{component_type,linear}() + + ... which change a format's component-type/TRC, without otherwise + affecting it. + + app/gegl/gimp-babl.c | 26 +++++++++++++++++++++ + app/gegl/gimp-babl.h | 65 + ++++++++++++++++++++++++++++------------------------ + 2 files changed, 61 insertions(+), 30 deletions(-) + +commit 5c70b827d24d8dc935285db6f94bfc7da544bc53 +Author: Ell +Date: Wed Mar 20 09:14:40 2019 -0400 + + app: add gimp_babl_is_bounded() + + ... which takes a GimpPrecision, and determines if its values are + bounded to the [0,1] range (which is currently only true for + integer precisions). + + (cherry picked from commit d7f12c9d26ea4be506d6d512ebab3bb30786d7ee) + + app/gegl/gimp-babl.c | 19 +++++++++++++++++++ + app/gegl/gimp-babl.h | 1 + + 2 files changed, 20 insertions(+) + +commit 2675e944e30cca27d757b00ebb5129c6e891bfc5 +Author: Jehan +Date: Wed Mar 20 15:05:22 2019 +0100 + + app: make gimp_prop_gui_chain_toggled() less error-prone. + + Don't assume that "toggled" signal means that toggle status actually + changed. + + Though issue #3133 got fixed with my previous commit, let's make + sure we + never create several GBinding for the same GimpChain by always + checking + existence of a previous one after a "toggled" signal. + Also only create a GBinding object if one doesn't already exist. + + (cherry picked from commit 9042e85f3c4b000a3f1f43ad09c1f97e6418c999) + + app/propgui/gimppropgui-generic.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +commit 2ebf56850eaf86727f1022b10b2a7322de94bf6e +Author: Jehan +Date: Wed Mar 20 14:48:40 2019 +0100 + + Issue #3133: Gimp freezes after selecting a filter preset and... + + ... clicking a GimpChain. + Since commit c0c055b4e9, gimp_chain_button_set_active() emits the + "toggled" signal. There is no need to emit it separately from + GimpOperationTool when setting presets with + gimp_operation_tool_set_config(). + + In particular, since the "toggled" signal was even sent + unconditionnally + here, our code was ending creating several GBinding for the same 2 + adjustments, which was creating an infinite loop. + + (cherry picked from commit 03dc24455ab1616204a9ec8fd20070d4411b0b2c) + + app/tools/gimpoperationtool.c | 4 ---- + 1 file changed, 4 deletions(-) + +commit 199f9f13a5a25e20de6ddd53ef55c009cfe324d7 +Author: Jehan +Date: Wed Mar 20 14:02:11 2019 +0100 + + app: fixes g_object_unref: assertion 'G_IS_OBJECT (object)' failed. + + "binding" data can be set to NULL. Do not assume it is a proper + object. + + Also I was tempted to use g_object_set_data() to simply free the + GBinding object on setting a new data, but such object will also be + freed when the widget is destroyed by default. So that would also + end up + in double destruction. Instead just keep current logics. + + This CRITICAL was reported in #3133 but this is not the main bug. + + (cherry picked from commit af6760b3c95694f33455a2d542eb9cb817290bde) + + app/propgui/gimppropgui-generic.c | 12 +++--------- + 1 file changed, 3 insertions(+), 9 deletions(-) + +commit 66c47ee23f6f375f36870fe9ad6f38c3ee82374e +Author: Jehan +Date: Tue Mar 19 12:46:12 2019 +0100 + + plug-ins: do not needlessly free/malloc() buffer of same size. + + In an animated WebP, chances that layers/frame have the same size is + high. It is uneeded to free then malloc again a buffer at each frame, + unless we need more allocated memory. + This is probably not so significant, but still feels nicer. + + (cherry picked from commit 0b68ce81821eebd459e1ed8b2fa9daa09b62c291) + + plug-ins/file-webp/file-webp-save.c | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +commit 4721643176c940c7844e3670c8b7cfcd5dde7faf +Author: Snehalata B Shirude +Date: Mon Mar 18 16:29:18 2019 +0000 + + Update Marathi translation + + po-tips/mr.po | 142 + ++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 79 insertions(+), 63 deletions(-) + +commit 520b4fbf186cccef7c2de5beb84e14f8e69cb06c +Author: Snehalata B Shirude +Date: Mon Mar 18 16:27:13 2019 +0000 + + Update Marathi translation + + po-python/mr.po | 50 +++++++++++++++++++++++++++----------------------- + 1 file changed, 27 insertions(+), 23 deletions(-) + +commit c15069797e2cd6c4897a41fd1556685117540771 +Author: Snehalata B Shirude +Date: Mon Mar 18 11:12:37 2019 +0000 + + Update Marathi translation + + po-plug-ins/mr.po | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +commit 2a83ce9abea63b895dc56609e2725ab8d500f5e9 +Author: Snehalata B Shirude +Date: Mon Mar 18 11:11:30 2019 +0000 + + Update Marathi translation + + po-libgimp/mr.po | 148 + ++++++++++++++++++++++++++----------------------------- + 1 file changed, 70 insertions(+), 78 deletions(-) + +commit 9cf206f350f499b6a95a11522246435a9543e87f +Author: Snehalata B Shirude +Date: Mon Mar 18 10:38:01 2019 +0000 + + Update Marathi translation + + po-windows-installer/mr.po | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +commit 05e9bb7b3fe052b8b330ee7a5ada8fda042424cb +Author: Snehalata B Shirude +Date: Mon Mar 18 10:35:58 2019 +0000 + + Update Marathi translation + + po/mr.po | 136 + +++++++++++++++++++++++++++++++-------------------------------- + 1 file changed, 68 insertions(+), 68 deletions(-) + +commit b46d89257a00f06cbd7f26312e4cf27fded891a9 +Author: Snehalata B Shirude +Date: Fri Mar 15 12:25:58 2019 +0000 + + Update Marathi translation + + po-plug-ins/mr.po | 315 + +++++++++++++++++++++++++----------------------------- + 1 file changed, 147 insertions(+), 168 deletions(-) + +commit 6956a403d0cc2edd1e234e84f84c906b9121a2ba +Author: Snehalata B Shirude +Date: Fri Mar 15 12:24:03 2019 +0000 + + Update Marathi translation + + po/mr.po | 50 +++++++++++++++++++++++++++++++++----------------- + 1 file changed, 33 insertions(+), 17 deletions(-) + +commit d67359f096e49df10067f656c4e321f1173518df +Author: Marco Ciampa +Date: Thu Mar 14 17:50:11 2019 +0100 + + Updated Italian translation + + po-plug-ins/it.po | 273 +++++++--------- + po/it.po | 964 + +++++++++++++++++++++++++++++------------------------- + 2 files changed, 639 insertions(+), 598 deletions(-) + +commit 28860c12dc8ea6209c78b3756924b842f573cc21 +Author: Sveinn í Felli +Date: Thu Mar 14 07:59:52 2019 +0000 + + Update Icelandic translation + + po/is.po | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit e3fe627faa1c7c17e0cb5519e72ffac34486ac4d +Author: Piotr Drąg +Date: Wed Mar 13 18:11:45 2019 +0100 + + Update Polish translation + + po/pl.po | 32 ++++++++++++++++++-------------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +commit f8aec23e336e88f88b1d7dbee0305f7ab2182887 +Author: Ell +Date: Wed Mar 13 10:53:56 2019 -0400 + + app: avoid pushing undo while updating colormap entries + + In GimpColormapEditor, while updating a colormap entry, only push + an undo step when confirming the new color. + + (cherry picked from commit 506f412a05a76c5c16a24cf32c90f3b680b96da8) + + app/widgets/gimpcolormapeditor.c | 37 + ++++++++++++++++++++++++++++++------- + 1 file changed, 30 insertions(+), 7 deletions(-) + +commit 68f5bc63b6f1f4eb07de6df0d0e79f90470e00b0 +Author: Ell +Date: Wed Mar 13 10:09:49 2019 -0400 + + app: more "Readjust" improvements + + ... technical stuff. + + (cherry picked from commit 1e89c161c52040bb407179d9cf4ed8a55c050265) + + app/tools/gimptransformgridtool.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +commit 1b9fdddc7839e6d395900993fca2752c4e283994 +Author: Sveinn í Felli +Date: Wed Mar 13 11:01:44 2019 +0000 + + Update Icelandic translation + + po-script-fu/is.po | 78 + +++++++++++++++++++++++++++--------------------------- + 1 file changed, 39 insertions(+), 39 deletions(-) + +commit 4d08f75978b7c10a1ebbd1b68fa6565180c26dbc +Author: Sveinn í Felli +Date: Wed Mar 13 10:48:33 2019 +0000 + + Update Icelandic translation + + po-plug-ins/is.po | 143 + ++++++++++++++++++++++++++---------------------------- + 1 file changed, 70 insertions(+), 73 deletions(-) + +commit b3e34ccbaeb54c716d40c96a6e471d2acd102f69 +Author: Sabri Ünal +Date: Wed Mar 13 10:38:08 2019 +0000 + + Update Turkish translation + + po/tr.po | 17453 + +++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 8859 insertions(+), 8594 deletions(-) + +commit 282adfd581828643eaa48086a881e84487e39f68 +Author: Sveinn í Felli +Date: Wed Mar 13 10:06:58 2019 +0000 + + Update Icelandic translation + + po-windows-installer/is.po | 29 +++++++++++++++++++---------- + 1 file changed, 19 insertions(+), 10 deletions(-) + +commit 3c68387e1574038363f1cd0e9d4b9beb34895b95 +Author: Sveinn í Felli +Date: Wed Mar 13 09:55:23 2019 +0000 + + Update Icelandic translation + + po/is.po | 234 + +++++++++++++++++++++++++++++++++++++++------------------------ + 1 file changed, 145 insertions(+), 89 deletions(-) + +commit 581944253c67a6543600a8fccb7f631b51b15702 +Author: Ell +Date: Wed Mar 13 05:20:51 2019 -0400 + + app: improve transform-tools readjustment + + In GimpTransformGridTool, extend the functionality of the + "Readjust" button, such that if the transformation is already + adjusted to the view (i.e., when the button is clicked the second + time), readjust the transformation to the item bounds (as when + using the tool "normally"), and vice versa. This allows switching + back and forth between "normal" mode, and "adjusted-to-view" mode. + + Additionally, disable readjustment when the current transforamtion + is invalid, and show an error when readjustment results in an + invalid transformation. + + (cherry picked from commit a3fa3b6181fc9edbc7ca2e0e102ae79566f42d33) + + app/tools/gimptransformgridtool.c | 132 + +++++++++++++++++++++++++++++++++----- + 1 file changed, 115 insertions(+), 17 deletions(-) + +commit f6229c921a2ae32af7aa2ddcabe42ef8475df957 +Author: Ell +Date: Wed Mar 13 04:17:47 2019 -0400 + + app: in gimp_tool_gui_set_response_sensitive(), allow non-existent ID + + In gimp_tool_gui_set_response_sensitive(), silently ignore non- + existent response IDs, instead of emitting a CRITICAL, to match the + behavior of GtkDialog and GimpOverlayDialog. This simplifies code + with optional dialog responses. + + (cherry picked from commit 92216a635ad1f8adf10c39bf075c95c5612ad86f) + + app/display/gimptoolgui.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 9ee3ee9aaa34539545f23a8cef2548a2b7d2fbdd +Author: Balázs Meskó +Date: Wed Mar 13 08:39:59 2019 +0000 + + Update Hungarian translation + + po/hu.po | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit c0be74d7f157cd28391c4b2026426fb1936bd187 +Author: Piotr Drąg +Date: Tue Mar 12 18:54:46 2019 +0100 + + Update Polish translation + + po/pl.po | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +commit 942da1ad668a4dc49f19bf9e66f4026425415039 +Author: Sveinn í Felli +Date: Tue Mar 12 14:35:33 2019 +0000 + + Update Icelandic translation + + po-python/is.po | 328 + +++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 316 insertions(+), 12 deletions(-) + +commit 0857c5025db07b103ed54696e78f1b6ebd67c1e1 +Author: Sveinn í Felli +Date: Tue Mar 12 14:25:36 2019 +0000 + + Update Icelandic translation + + po-script-fu/is.po | 307 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 166 insertions(+), 141 deletions(-) + +commit 578ed98227b1c824e1011d7a48377616fe5ef606 +Author: Sveinn í Felli +Date: Tue Mar 12 14:18:56 2019 +0000 + + Update Icelandic translation + + po-plug-ins/is.po | 1693 + ++++++++++++++++++++++++++++------------------------- + 1 file changed, 904 insertions(+), 789 deletions(-) + +commit 90ee7635ce874152828330f2c4e9fe7d813ae390 +Author: Sveinn í Felli +Date: Tue Mar 12 14:14:57 2019 +0000 + + Update Icelandic translation + + po-libgimp/is.po | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit 9e64f4ac7d575c0024c469d1c803239737724d0b +Author: Daniel Korostil +Date: Tue Mar 12 07:18:12 2019 +0000 + + Update Ukrainian translation + + po/uk.po | 2661 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 1542 insertions(+), 1119 deletions(-) + +commit 78877be607bc44add138ddabf903882a938157ec +Author: Alexandre Prokoudine +Date: Mon Mar 11 23:46:55 2019 +0300 + + Tools presets -> Tool Pre_sets. Fixes #3092 + + app/actions/dialogs-actions.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 1c143228f0090897f09c43d84275fee4dd02832b +Author: Sveinn í Felli +Date: Mon Mar 11 19:47:02 2019 +0000 + + Update Icelandic translation + + po-libgimp/is.po | 562 + +++++++++++++++++++++++++++++++------------------------ + 1 file changed, 313 insertions(+), 249 deletions(-) + +commit 790c4b002e2492e7d960ca467538ba78dd38cd94 +Author: Sveinn í Felli +Date: Mon Mar 11 19:03:05 2019 +0000 + + Update Icelandic translation + + po/is.po | 8329 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 4903 insertions(+), 3426 deletions(-) + +commit 0f4b010199260261b6542d9b480eb4fdd7098edb +Author: Alan Mortensen +Date: Mon Mar 11 19:54:29 2019 +0100 + + Updated Danish translation + + po/da.po | 691 + +++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 380 insertions(+), 311 deletions(-) + +commit 3be1a9840814d0d1a0aeb1c2a9b41fde0bd7a15a +Author: Ell +Date: Mon Mar 11 12:35:00 2019 -0400 + + libgimpwidgets: fix arithmetic-expression supprot in GimpSizeEntry + + ... after commit 8b3c7ae1930e30ba7f760e5371346e32bb6fda20. + + (cherry picked from commit 81a361be73abdae9e50616174289ece96fd0ce6a) + + libgimpwidgets/gimpsizeentry.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 914d06547d48688a0873f5469ddd98b5f3f2214a +Author: Anders Jonsson +Date: Mon Mar 11 16:56:29 2019 +0000 + + Update Swedish translation + + po-plug-ins/sv.po | 299 + +++++++++++++++++++++++++----------------------------- + 1 file changed, 139 insertions(+), 160 deletions(-) + +commit 3e08bd7e2280cd9abc4c60b84fbfb9edebbecd2a +Author: Piotr Drąg +Date: Mon Mar 11 11:39:12 2019 +0100 + + Update Polish translation + + po-plug-ins/pl.po | 260 + +++++++++++++++++++++--------------------------------- + 1 file changed, 103 insertions(+), 157 deletions(-) + +commit 18a2f576bbd9e1be155886b8da206f282f9053e0 +Author: Jehan +Date: Mon Mar 11 10:45:28 2019 +0100 + + plug-ins: try to clarify "save-transp-pixels" argument of tiff saving. + + This argument should actually say "Do not store premultiplied channel + values", which is what the TIFF spec calls "Unassociated alpha" (vs. + "associated alpha" when values are stored premultiplied by alpha). + + Now I can see where the current description is coming from, which is + that any color with alpha 0 (totally masked) ends up as RGBA value (0, + 0, 0, 0), in other words, the color information is completely + lost. Yet + this label is not very helpful to understand what the checkbox really + does. I decided to not just change it altogether as people would have + gotten used to this for years, but at least adding completary + information in API and tooltip in GUI. + + plug-ins/file-tiff/file-tiff.c | 2 +- + plug-ins/ui/plug-in-file-tiff.ui | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit 23ffa791f0f3855a63a1f1018d78743e3ced2a2a +Author: Nathan Follens +Date: Sun Mar 10 13:40:26 2019 +0000 + + Update Dutch translation + + po/nl.po | 10509 + ++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 5574 insertions(+), 4935 deletions(-) + +commit f27bf95e05224025fa3c8c8c8f531ec3818cdd78 +Author: Ell +Date: Sat Mar 9 07:00:08 2019 -0500 + + Issue #2557 - Gimp Can't Rotate By More Than 180 Degree + + When the "wrap" property of a GimpSpinButton is TRUE, wrap-around + out-of-range values entered through the spin-button's text entry, + instead of clamping them. Since we're using GimpSpinButton + everywhere since last commit, this applies to all our angle-entry + spin buttons (including spin scales). + + (cherry picked from commit 8b3c7ae1930e30ba7f760e5371346e32bb6fda20) + + libgimpwidgets/gimpspinbutton.c | 64 + ++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 60 insertions(+), 4 deletions(-) + +commit 2dad85b84f780cde7effe4858d35d9892e93de8d +Author: Ell +Date: Sat Mar 9 06:55:58 2019 -0500 + + libgimpwidgets, app, plug-ins: use GimpSpinButton everywhere + + Replace all direct uses of GtkSpinButton with GimpSpinButton, so + that its modified behavior extends to all our spin buttons. + + app/dialogs/convert-indexed-dialog.c | 2 +- + app/dialogs/layer-options-dialog.c | 4 ++-- + app/dialogs/offset-dialog.c | 2 +- + app/dialogs/print-size-dialog.c | 8 ++++---- + app/dialogs/resize-dialog.c | 2 +- + app/display/gimpdisplayshell-rotate-dialog.c | 2 +- + app/display/gimpdisplayshell-scale-dialog.c | 6 +++--- + app/tools/gimpgradienttool-editor.c | 2 +- + app/tools/gimprectangleoptions.c | 2 +- + app/tools/gimprotatetool.c | 4 ++-- + app/widgets/gimpcolormapeditor.c | 4 ++-- + app/widgets/gimphistogrambox.c | 4 ++-- + app/widgets/gimppaletteeditor.c | 2 +- + app/widgets/gimpspinscale.c | 2 +- + app/widgets/gimpspinscale.h | 4 ++-- + app/widgets/gimptemplateeditor.c | 8 ++++---- + app/widgets/gimptextstyleeditor.c | 8 ++++---- + libgimpwidgets/gimpmemsizeentry.c | 3 ++- + libgimpwidgets/gimppropwidgets.c | 3 ++- + libgimpwidgets/gimpquerybox.c | 5 +++-- + libgimpwidgets/gimpscaleentry.c | 3 ++- + libgimpwidgets/gimpwidgets.c | 8 ++++---- + plug-ins/common/curve-bend.c | 2 +- + plug-ins/common/file-gbr.c | 2 +- + plug-ins/common/file-gih.c | 12 ++++++------ + plug-ins/common/file-html-table.c | 6 +++--- + plug-ins/common/file-mng.c | 2 +- + plug-ins/common/file-pdf-load.c | 4 ++-- + plug-ins/common/file-ps.c | 16 ++++++++-------- + plug-ins/common/file-svg.c | 6 +++--- + plug-ins/common/file-wmf.c | 6 +++--- + plug-ins/common/file-xbm.c | 4 ++-- + plug-ins/common/file-xmc.c | 6 +++--- + plug-ins/common/film.c | 4 ++-- + plug-ins/common/sample-colorize.c | 10 +++++----- + plug-ins/common/smooth-palette.c | 2 +- + plug-ins/common/unit-editor.c | 4 ++-- + plug-ins/common/web-page.c | 2 +- + plug-ins/file-dds/ddswrite.c | 6 +++--- + plug-ins/file-fli/fli-gimp.c | 8 ++++---- + plug-ins/file-jpeg/jpeg-save.c | 2 +- + plug-ins/file-webp/file-webp-dialog.c | 4 ++-- + plug-ins/ifs-compose/ifs-compose.c | 2 +- + plug-ins/imagemap/imap_table.c | 2 +- + plug-ins/lighting/lighting-ui.c | 4 ++-- + plug-ins/map-object/map-object-ui.c | 4 ++-- + plug-ins/print/print-page-layout.c | 12 ++++++------ + plug-ins/screenshot/screenshot.c | 4 ++-- + plug-ins/script-fu/script-fu-interface.c | 6 +++--- + plug-ins/ui/plug-in-file-gif.ui | 2 +- + plug-ins/ui/plug-in-file-png.ui | 2 +- + 51 files changed, 119 insertions(+), 115 deletions(-) + +commit 633f6c1f94c0ac3bf97755c9c1a0e11d9fdd3891 +Author: Snehalata B Shirude +Date: Sat Mar 9 12:08:33 2019 +0000 + + Update Marathi translation + + po/mr.po | 3762 + +++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 2105 insertions(+), 1657 deletions(-) + +commit 1e725b3dbd697efcdfc2fc1dd72b61b2ffe74360 +Author: Jehan +Date: Sat Mar 9 12:28:21 2019 +0100 + + NEWS: keep up-to-date. + + NEWS | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +commit a0a7ead13faf9db5ca4e741aac087f48764a3ae9 +Author: Jehan +Date: Sat Mar 9 12:17:46 2019 +0100 + + plug-ins: tiff exporting can now support INDEXED*. + + Not sure what this @image_types parameter of gimp_install_procedure() + is + used for. Exporting was working find with INDEXEDA image even when not + advertized by this function. Let's update this anyway. + + plug-ins/file-tiff/file-tiff.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 72e86545f08ab6d7737f2543ed479f62ccf08fd8 +Author: Jehan +Date: Sat Mar 9 00:25:29 2019 +0100 + + Issue #102: TIFF plugin doesn't handle indexed images with alpha... + + ... channel. + + plug-ins/file-tiff/file-tiff-save.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +commit c4621601f6b8e96ccac6f2864e25541998e3a080 +Author: Snehalata B Shirude +Date: Sat Mar 9 11:04:02 2019 +0000 + + Update Marathi translation + + po-python/mr.po | 336 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 327 insertions(+), 9 deletions(-) + +commit 34e41e90873127a64cad547fca751c9ca2b87f04 +Author: Snehalata B Shirude +Date: Sat Mar 9 11:02:45 2019 +0000 + + Update Marathi translation + + po-script-fu/mr.po | 105 + ++++++++++++++++++++++++++++------------------------- + 1 file changed, 55 insertions(+), 50 deletions(-) + +commit c6ef13d63fd1ed95ca9cae90fdf4860b7128d451 +Author: Asier Sarasua Garmendia +Date: Sat Mar 9 10:01:29 2019 +0000 + + Update Basque translation + + po-plug-ins/eu.po | 2604 + ++++++++++++++++++++++++++--------------------------- + 1 file changed, 1289 insertions(+), 1315 deletions(-) + +commit 8cf4b03868e0a1b7d22f078065a01ed38cbf6325 +Author: Asier Sarasua Garmendia +Date: Sat Mar 9 09:02:32 2019 +0000 + + Update Basque translation + + po/eu.po | 10395 + +++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 5924 insertions(+), 4471 deletions(-) + +commit 0ffebd18eba6b2660581f3bed8e11a6a589a6541 +Author: Piotr Drąg +Date: Fri Mar 8 19:42:37 2019 +0100 + + Update Polish translation + + po/pl.po | 120 + +++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 62 insertions(+), 58 deletions(-) + +commit 1ecbb94b2cedcb154b762e4ab36d411cfe8a7c1f +Author: Ell +Date: Fri Mar 8 08:30:11 2019 -0500 + + app: take transform-grid handle size into account when readjusting + + In the unified-transform, scale, and perspective tools, take the + maximal transform-grid handle size into account when readjusting + the transform, so that the handles themselves are fully within view + under arbitrary rotation, rather than just the corners. + + (cherry picked from commit 46e16e175c4cdc9da715ec50a8b793ea9688525d) + + app/display/gimptooltransformgrid.h | 5 +++++ + app/tools/gimpperspectivetool.c | 4 +++- + app/tools/gimpscaletool.c | 4 +++- + app/tools/gimpunifiedtransformtool.c | 4 +++- + 4 files changed, 14 insertions(+), 3 deletions(-) + +commit 127fc91603d6eac81c4588594abce9e042f3c682 +Author: Jehan +Date: Fri Mar 8 17:29:16 2019 +0100 + + Issue #3087: error compiling for Windows. + + s/THREAD_MODE_ABOVE_NORMAL/THREAD_PRIORITY_ABOVE_NORMAL/ + Thanks to Sylvie Alexandre for noticing and searching this. + + (cherry picked from commit ebc3ef3c5ed9a893de36bd5ecc1802023555bc74) + + app/core/gimp-parallel.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 628960ed22662e19045fafe840df900c8358fdd3 +Author: Jehan +Date: Fri Mar 8 16:32:53 2019 +0100 + + app: update fg/bg colors when committing a colormap change. + + Since the color being currently edited is the selected one, it makes + sense that the fg/bg color should be updated appropriately. + + app/widgets/gimpcolormapeditor.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +commit d378f193903f8cd82fa7b17f2c5535c8ddd59d79 +Author: Jehan +Date: Fri Mar 8 16:04:20 2019 +0100 + + app: fix a critical on a colormap editor callback. + + On color update, check the existence of a context image before + trying to + update the colormap. + + app/widgets/gimpcolormapeditor.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit c91d37eff23418767ba9b1b2bdf5815a639f169c +Author: Jehan +Date: Thu Mar 7 18:43:05 2019 +0100 + + Issue #3041: Color Picker no longer selects Colormap entry. + + Do not take "Sample merge" into account when picking colors in a + single-layer image. The reason is to be able to get the index + information on indexed image. This information is lost otherwise when + using the whole image as a pickable. + + Of course, other exceptions are possible, when you'd pick exactly a + colormap color, but I don't think it's worth making the code + extra-complicated for these. My previous commit will anyway already + select the right color in the colormap on common cases. Though it will + still fail to select the right index when several indexes store + the same + color, on a multi-layer image, if you check "Sample merged" while the + right index was not the first one amongst the duplicates. + + (cherry picked from commit 31b2b55b28f6dbca66dcf3421c4eae69cfdc2ed8) + + app/core/gimpimage-pick-color.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +commit eabe27421d53993fee5203c5a7a8b081dba9cdb4 +Author: Jehan +Date: Thu Mar 7 16:55:26 2019 +0100 + + Issue #3041: Color Picker no longer selects Colormap entry. + + This is not the ultimate fix yet, but at least a first improvement. + + (cherry picked from commit e47185bf117c513298681aaed62ce5b504e9662a) + + app/tools/gimpcolortool.c | 33 +++++++++++++++++++++++++++------ + 1 file changed, 27 insertions(+), 6 deletions(-) + +commit 01990c405989cf1315c04201cbb4c16c237d9358 +Author: Jehan +Date: Thu Mar 7 15:45:17 2019 +0100 + + app: direct on-canvas color preview when editing a colormap color. + + (cherry picked from commit a24957a80468249a303c7bf9b87a5b53e0fb6f57) + + app/widgets/gimpcolormapeditor.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +commit 812a0f66890bd6b301836c62a25c96bc4a882134 +Author: Jehan +Date: Thu Mar 7 00:02:56 2019 +0100 + + app: save the accurate color in the colormap palette when possible. + + The colormap saves colors as unsigned char, which can be very + inaccurate + compared to high precision colors. When adding colors from GimpRGB + into + the colormap, use the original value to fill the colormap palette + instead of making a round trip conversion from double to uchar, then + back to double. + + This also fixes a direct bug I encountered when adding the current + foreground color in the image colormap. Yet the GimpFgBgEditor or the + GimpColorHistory would still show the color out-of-gamut in cases when + the returned RGB after the roundtrip was not close enough to the + original RGB (even despite using an epsilon in GimpPalette code). + + (cherry picked from commit f1cca8ee2e62a19a3603ecd1acc8cc9d8fdc344f) + + app/core/gimpimage-colormap.c | 32 ++++++++++++++++++++------------ + 1 file changed, 20 insertions(+), 12 deletions(-) + +commit ecd13f145612b616799de5e2d9daf6a3e1f65823 +Author: Jehan +Date: Wed Mar 6 23:51:23 2019 +0100 + + app: update the GimpFgBgEditor when image colormap is updated. + + (cherry picked from commit 8e8b4e82c14071d4d84a8350b07dc0968e747fb4) + + app/widgets/gimpfgbgeditor.c | 27 ++++++++++++++++++++++++--- + 1 file changed, 24 insertions(+), 3 deletions(-) + +commit 7812aced5dd01904a7f6360fa8b257656147f5aa +Author: Jehan +Date: Fri Mar 8 15:12:04 2019 +0100 + + Issue #1256: Import damages colormap numbering. + + Do not remove fully transparent colors from the PNG palette. + + plug-ins/common/file-png.c | 22 ++-------------------- + 1 file changed, 2 insertions(+), 20 deletions(-) + +commit bee8543f444ef815fac654a7b1d33321bc87fe6d +Author: Balázs Meskó +Date: Fri Mar 8 10:31:15 2019 +0000 + + Update Hungarian translation + + po/hu.po | 74 + ++++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 39 insertions(+), 35 deletions(-) + +commit 1ff6d653a26a730d7ed31907839bd0257b1ef381 +Author: Snehalata B Shirude +Date: Fri Mar 8 10:04:19 2019 +0000 + + Update Marathi translation + + po-tips/mr.po | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +commit 225b8ac920505e93e6893a2ca1f85d2b44a66b89 +Author: Snehalata B Shirude +Date: Fri Mar 8 10:02:20 2019 +0000 + + Update Marathi translation + + po-libgimp/mr.po | 100 + +++++++++++++++++++++++++++---------------------------- + 1 file changed, 50 insertions(+), 50 deletions(-) + +commit 114db4f60aeac5967aa2404a54582e026bad8316 +Author: Nathan Follens +Date: Fri Mar 8 09:57:16 2019 +0000 + + Update Dutch translation + + po-libgimp/nl.po | 376 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 191 insertions(+), 185 deletions(-) + +commit ba3d090882035e5e7a4ed85fb8d2da20ccc2c787 +Author: Balázs Meskó +Date: Fri Mar 8 09:33:46 2019 +0000 + + Update Hungarian translation + + po/hu.po | 762 + +++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 420 insertions(+), 342 deletions(-) + +commit 79f030cf6c3cc421b14b3cbeb1abf53c295512a7 +Author: Ell +Date: Fri Mar 8 03:44:26 2019 -0500 + + app: fix mnemonic of transform-tools "Readjust" button + + ... and make sure that the tool implements readjust() when + receiving a RESPONSE_READJUST. + + (cherry picked from commit 5ff38e68591298dd6b524c56ac888781b2361dc9) + + app/tools/gimptransformgridtool.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +commit 47a44106c224f0e23f040baa9249302450d00eca +Author: Ell +Date: Thu Mar 7 17:42:52 2019 -0500 + + app: reset overall transformation in transform tools + + In GimpTransformGridTool, reset both transform directions in + response to the "Reset" button, so that the overall transformation + is restored to the identity. Previously, we would only reset the + active transform direction (possibly compensating in the opposite + direction, if both directions are linked). This was intentional, + but it's probably a bit too confusing, especially in conjunction + with the newly added "Readjust" button. Let's just go back to + resetting everything. + + (cherry picked from commit 2c67b291904423f09fd4350081063447af8b85ab) + + app/tools/gimptransformgridtool.c | 47 + ++++++++++++++++++++++++++------------- + 1 file changed, 32 insertions(+), 15 deletions(-) + +commit 21f76df21b4915eafcff032a0cc2b96558c4ea50 +Author: Ell +Date: Thu Mar 7 16:44:32 2019 -0500 + + app: fix unused variable warning in gimp_tool_gui_new() + + ... due to commit b23fae86f00c42e8ca2edcf7e480db88ca665aed. + + (cherry picked from commit 7fb1d05ca4cea6a9c04e6ab7081cfe5e6a01f460) + + app/display/gimptoolgui.c | 1 - + 1 file changed, 1 deletion(-) + +commit ab060c110f979319f5c6c0eca4aeb8da23d489cc +Author: Ell +Date: Thu Mar 7 16:18:03 2019 -0500 + + app: implement readjust() in various transform tools + + Implement GimpTransformGridTool::radjust(), added in the previous + commit, in various transform tools: + + The unified-transform, scale, and perspective tools readjust the + transformation such that the grid is centered relative to the view, + and its handles are fully within view under arbitrary rotation. + + The rotate tool readjusts the transformation such that the pivot is + centered, and the grid is unrotated, relative to the view. + + (cherry picked from commit 5e5118c1db86b01ae7bdfba1e0f81d587538385d) + + app/tools/gimpperspectivetool.c | 35 +++++++++++++++++++++++++++++++ + app/tools/gimprotatetool.c | 21 +++++++++++++++++++ + app/tools/gimpscaletool.c | 26 +++++++++++++++++++++++ + app/tools/gimpunifiedtransformtool.c | 40 + ++++++++++++++++++++++++++++++++++++ + 4 files changed, 122 insertions(+) + +commit a8292fedf262bf1815eea81b9d5ec08936cf1052 +Author: Ell +Date: Thu Mar 7 16:05:18 2019 -0500 + + app: add "Readjust" function to transform-grid tools + + Add an optional GimpTransformGridTool::radjust() virtual function, + which subclasses can implement to radjust the transformation based + on the current state of the display, such that it's easy to + control. This is especially useful when the image is zoomed-in, + and the transform handles, which are initially across the layer + bounds, are out of view. + + When a transform tool implements radjust(), show a "Readjust" + button in the tool GUI. While readjusting the transformation, we + modify the opposite transformation such that the overall transform + remains unchanged, as if both transform-directions were linked, so + that only the transform grid is readjusted. + + (cherry picked from commit 5055dd10d5107fde376a9f79d29fc12b697bca4c) + + app/tools/gimptransformgridtool.c | 43 + +++++++++++++++++++++++++++++++-------- + app/tools/gimptransformgridtool.h | 1 + + 2 files changed, 35 insertions(+), 9 deletions(-) + +commit 8d20b7c66521c782d3b1fbad5af4fa2eed8be1f1 +Author: Ell +Date: Thu Mar 7 16:01:08 2019 -0500 + + app: add gimp_tool_gui_add_button() + + In GimpToolGui, add gimp_tool_gui_add_button() and + gimp_tool_gui_add_buttons_valist(), which allow adding dialog + buttons after construction. + + (cherry picked from commit b23fae86f00c42e8ca2edcf7e480db88ca665aed) + + app/display/gimptoolgui.c | 146 + +++++++++++++++++++++++++++++++--------------- + app/display/gimptoolgui.h | 6 ++ + 2 files changed, 104 insertions(+), 48 deletions(-) + +commit ffe71f6c5b573f6b5f57c8a7c2f810a00ff06edf +Author: Alexandre Franke +Date: Thu Mar 7 11:40:45 2019 +0000 + + Update French translation + + po/fr.po | 3594 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 2099 insertions(+), 1495 deletions(-) + +commit dfca360b66044929aab94c34a3e5e2465c884c88 +Author: Ell +Date: Wed Mar 6 15:39:29 2019 -0500 + + app: various fixes to last commit + + (cherry picked from commit caad9ca649c1fed5e6bc9fb7a252b5eae8397c0c) + + app/core/gimplineart.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +commit 977c4d071822d296a1fa91363d2847a12be6a65d +Author: Ell +Date: Wed Mar 6 15:04:39 2019 -0500 + + app: allow canceling line-art computation + + Line-art computation can take a long time, and it's therefore + desirable for it to be interruptable. While we do cancel the line- + art async when its result is no longer needed, most parts of the + computation don't respond to the cancelation request, leaving the + async operation running in the background, blocking subsequent + async operations. + + Implement cancelation support of line-art computation, by passing + down the async object to the various functions, and periodically + checking for its cancelation at various points. When the async is + canceled, we quickly abort the operation. + + Even though cancelation now happens relatively quickly, some parts + of the computation are still uninterruptable and may incur some + latency, so we avoid waiting for the async opration to be aborted + after cancelation, as we did before. + + (cherry picked from commit d8e69d66bc91d3def9efe58e9c4d9ab5c52df2a5) + + app/core/gimplineart.c | 710 + +++++++++++++++++++++++++++++++++---------------- + 1 file changed, 480 insertions(+), 230 deletions(-) + +commit f73e13f49ff1ea2696e09de118fa6276e3391778 +Author: Sabri Ünal +Date: Wed Mar 6 19:06:27 2019 +0000 + + Update Turkish translation + + po-tips/tr.po | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit c6f2ad1bcef0e8eb9c72fb4a2accbec3494c48db +Author: Sabri Ünal +Date: Wed Mar 6 19:05:07 2019 +0000 + + Update Turkish translation + + po-script-fu/tr.po | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +commit f84135e3d9ce9cd25f3f2be079546fb1bf7f475f +Author: Sabri Ünal +Date: Wed Mar 6 18:57:21 2019 +0000 + + Update Turkish translation + + po-libgimp/tr.po | 1983 + ++++++++++++++++++++++++++++++++++-------------------- + 1 file changed, 1270 insertions(+), 713 deletions(-) + +commit 7eae8e8cda3197909d1b955b465a98357d1afd04 +Author: Piotr Drąg +Date: Wed Mar 6 19:09:48 2019 +0100 + + Update Polish translation + + po/pl.po | 422 + +++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 237 insertions(+), 185 deletions(-) + +commit 8014fa6ba834e37be5444997d7f5b613ea62a7c4 +Author: Rodrigo Lledó +Date: Wed Mar 6 11:06:39 2019 +0000 + + Update Spanish translation + + po/es.po | 390 + ++++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 224 insertions(+), 166 deletions(-) + +commit d918502aacab54f261cce9793bc99911d3b1a8e2 +Author: Ell +Date: Wed Mar 6 04:50:03 2019 -0500 + + app: use gimp_gegl_buffer_copy() in various places + + ... instead of gegl_buffer_copy(). The former parallelizes the + format conversion. + + (cherry picked from commit bb7f61c9192e4827cd9c80dfe71e6d133eb6fda1) + + app/core/gimplineart.c | 4 +++- + app/gegl/gimptilehandlervalidate.c | 5 +++-- + app/paint/gimppaintcore.c | 20 +++++++++++--------- + 3 files changed, 17 insertions(+), 12 deletions(-) + +commit 9bbcaac5d27ae2772b0abb7f3b552a44699d95e6 +Author: Sabri Ünal +Date: Wed Mar 6 10:55:30 2019 +0000 + + Update Turkish translation + + po-script-fu/tr.po | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +commit 5f7d6951f7abec2282f0d0f61ac8c17c22cf2c90 +Author: Sabri Ünal +Date: Wed Mar 6 10:54:15 2019 +0000 + + Update Turkish translation + + po-python/tr.po | 24 +++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +commit 44703c03ff2387d7a837dbc6bf5ec9a9e6bdecd5 +Author: Sabri Ünal +Date: Wed Mar 6 10:51:51 2019 +0000 + + Update Turkish translation + + po-windows-installer/tr.po | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit cd159e4bd4c25a5b062fc37c3685dd611ee8933f +Author: Ell +Date: Wed Mar 6 00:06:42 2019 -0500 + + app: fix indepndent-async thread priority on Windows + + Positive and negative priorities got swapped... + + (cherry picked from commit 190095c97b35b11cffe040b67ea98a6f2fbe2517) + + app/core/gimp-parallel.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 08be2affee791bfebe240f9965a7398131c266f5 +Author: Ell +Date: Tue Mar 5 23:57:22 2019 -0500 + + app: use independent async for resolving performance-log symbol + information + + ... so that performance logs can be recorded during long-running + async operations, without those operations blocking the + finalization of the log. + + (cherry picked from commit 652a2a90cf74d2599b9d56119086b02b13dda7be) + + app/widgets/gimpdashboard.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +commit a3cd8c96d0f51ce97378474527f7d55b13594572 +Author: Ell +Date: Tue Mar 5 23:39:43 2019 -0500 + + app: add gimp_parallel_run_async_independent_full() + + ... which is equivalent to gimp_parallel_run_async_independent(), + except that it takes an additional "priority" parameter, which + specifies the task's priority, with 0 being the default priority, + and lower values indicating higher priority. Unlike + gimp_parallel_run_async_full(), the priority parameter doesn't + directly control the task's priority in a queue, but rather, we use + it to control the priority of the task's dedicated thread, on + supported platforms (previously, all independent async tasks would + run with low priority.) + + Use low priority when loading fonts, which can take a long time, to + keep the existing behavior. + + (cherry picked from commit fa2e4dcce0bd5e27022b18ceb9f49b2cec2e9fec) + + app/core/gimp-parallel.cc | 27 ++++++++++++++++++---- + app/core/gimp-parallel.h | 56 + ++++++++++++++++++++++++++++------------------ + app/text/gimpfontfactory.c | 3 ++- + 3 files changed, 59 insertions(+), 27 deletions(-) + +commit a465b9c3414ba7f4c83e98c3484f1d34e9421705 +Author: Ell +Date: Tue Mar 5 16:39:07 2019 -0500 + + app: clean up last commit + + Remove gimp_item_tree_clear(), added in last commit, and move its + code to gimp_item_tree_dispose(). Likewise, in + gimp_image_dispose(), use g_object_run_dispose() on the image item- + trees, instead of gimp_item_tree_clear(). + + (cherry picked from commit b0de51b6bfbebd1fa798fa7faab4d1ad64bc5e86) + + app/core/gimpimage.c | 6 +++--- + app/core/gimpitemtree.c | 29 +++++++++-------------------- + app/core/gimpitemtree.h | 2 -- + 3 files changed, 12 insertions(+), 25 deletions(-) + +commit c76076ec144ca33a0bcedc71c817aaa14963870a +Author: Ell +Date: Tue Mar 5 09:29:02 2019 -0500 + + app: fix segfault when closing an image with a floating selection + + Add gimp_item_tree_clear(), which removes all the items of a + GimpItemTree, and clear the layers/channels/vectors item trees in + gimp_image_dispose(), *before* finalizing the image, so that the + corresponding items' desctructors are called while the image is + still alive. In particular, this allows the destructors to safely + call gimp_item_is_attached(), which happens when the image has a + floating selection, since commit + 8d4e5e0ff714a30f2f0a00058d46e376828b80bf. + + (cherry picked from commit d7e3a1e22653127fd7b0a390ba42ed6d954a25e7) + + app/core/gimpimage.c | 9 +++------ + app/core/gimpitemtree.c | 30 ++++++++++++++++++++++++++++++ + app/core/gimpitemtree.h | 2 ++ + 3 files changed, 35 insertions(+), 6 deletions(-) + +commit 4eda127c525bb68104ef04bddff2f798945a54d0 +Author: Ell +Date: Tue Mar 5 08:41:58 2019 -0500 + + Issue #3062 - Picking by hue using "Select by Color" goes awry ... + + ... in GIMP 2.10.9 from git + + In gimppickable-contiguous-region's pixel_difference() function, + which is used, among other things, by the select-by-color and + fuzzy-select tools, when selecting by LCh/HSV hue, treat a pair of + colors as inifinitely far apart if one of them has positive chroma/ + saturation, and the other has chroma/saturation that's very close + to 0; conversely, treat a pair of colors as equal if both of them + have chroma/sautation that's close to 0. + + As a result, when the seed color is saturated, gray pixels are + never selected, while when the seed color is desaturated, all, and + only, gray pixels are selected. + + (cherry picked from commit 9886b69dacfd200bcd34e7e25f01f8c7c7983b97) + + app/core/gimppickable-contiguous-region.cc | 54 + +++++++++++++++++++++++++++--- + 1 file changed, 50 insertions(+), 4 deletions(-) + +commit 9173e4227404c487e298f876bc3f033a10d0b5eb +Author: Sabri Ünal +Date: Tue Mar 5 00:35:09 2019 +0000 + + Update Turkish translation + + po-windows-installer/tr.po | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +commit 51585bb8028242d3f955e583f35bd93f66f8c188 +Author: Sabri Ünal +Date: Tue Mar 5 00:32:28 2019 +0000 + + Update Turkish translation + + po-tips/tr.po | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 71800dfd21ac0ebfe65304bc168eb2390b5bd1af +Author: Sabri Ünal +Date: Mon Mar 4 22:18:28 2019 +0000 + + Update Turkish translation + + po-tips/tr.po | 376 + +++++++++++++++++++++++++++++----------------------------- + 1 file changed, 190 insertions(+), 186 deletions(-) + +commit 00c3c068d0254007dbd8b94efec3406dd34ac446 +Author: Sabri Ünal +Date: Mon Mar 4 22:12:56 2019 +0000 + + Update Turkish translation + + po-python/tr.po | 442 + +++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 387 insertions(+), 55 deletions(-) + +commit 28232d166ce5c5671d616de47fd181c9f5c2ad97 +Author: Sabri Ünal +Date: Mon Mar 4 21:34:57 2019 +0000 + + Update Turkish translation + + po-script-fu/tr.po | 2581 + +++++++++++++++++++++++++++++++--------------------- + 1 file changed, 1537 insertions(+), 1044 deletions(-) + +commit 07080d4540aadb7aa940c213947d6a7c99a18ea9 +Author: Jehan +Date: Mon Mar 4 18:28:11 2019 +0100 + + app: improve line art filling when clicking on a line art closure. + + When clicking on a line art pixel, only this pixel gets colored, which + is fine for actual (original) line art pixels. But on generated ones + (closure pixels, which are internal only), you end up with a single + pixel colored while the whole surrounding area is empty. This + feels like + a bug (even though it was not one technically) as you have no way to + guess you are clicking on a closure pixel. + Instead, when this happens, simulate clicks on all neighbour pixels, + hence potentially coloring all surrounding regions, which is most + likely + what you wanted. + + (cherry picked from commit f310db6c2154cdbfae8dea929042ddfc73280e1e) + + app/core/gimplineart.c | 4 +- + app/core/gimppickable-contiguous-region.cc | 116 + ++++++++++++++++++++++++----- + 2 files changed, 98 insertions(+), 22 deletions(-) + +commit 03744e30128df00b7177d738eb60a30519a31bc6 +Author: Jehan +Date: Mon Mar 4 16:55:51 2019 +0100 + + app: fixing the line art GimpBusyBox visibility. + + Commit bc187cc5cc was a bit wrong as it was possible to get some race + conditions when changing settings quickly in a short time frame. + + (cherry picked from commit 3a317e72aa3d6f4ebfdae7d476224705f894f77b) + + app/core/gimplineart.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit a9ec675b77e772583152345c64a56706e4c12051 +Author: Jehan +Date: Mon Mar 4 16:43:19 2019 +0100 + + app: avoid useless line art closure recomputation. + + On various property changes, only recompute the line art when the + property actually changed. Also add a gimp_line_art_bind_gap_length() + to + avoid computing twice the line art when changing both type of closure + (splines and segments) together, as is currently the case. + + (cherry picked from commit c0996241f684e5ea51464e459353db94f366032b) + + app/core/gimplineart.c | 40 + ++++++++++++++++++++++++++++++++-------- + app/core/gimplineart.h | 3 +++ + app/tools/gimpbucketfilltool.c | 23 +++++++++++------------ + 3 files changed, 46 insertions(+), 20 deletions(-) + +commit 4233d9096104c1fc99721d38889bf699feb104a9 +Author: Jehan +Date: Mon Mar 4 16:06:59 2019 +0100 + + app: add a GimpBusyBox near the "Line Art Detection" label in… + + … Bucket Fill tool options. + This will provide feedback when the line art closure is being + computed, + which may be useful on big images where it may take some + time. Otherwise + painter may be left hanging without knowing what takes time. + + (cherry picked from commit bc187cc5cca56134ba475cea1d3c0b16d2d527b9) + + app/core/gimplineart.c | 30 +++++++++++++ + app/core/gimplineart.h | 5 +++ + app/tools/gimpbucketfilloptions.c | 88 + ++++++++++++++++++++++++--------------- + app/tools/gimpbucketfilloptions.h | 1 + + app/tools/gimpbucketfilltool.c | 25 +++++++++++ + 5 files changed, 115 insertions(+), 34 deletions(-) + +commit 6ae957088a95d16bc8df6c70708f28354edb87ad +Author: Ell +Date: Mon Mar 4 08:25:39 2019 -0500 + + app: improve warp-tool cropped-area calculation + + In the warp tool, use the gegl:map-relative node to calculate the + affected drawable area to which the filter is cropped, to account + for box filtering. + + (cherry picked from commit daa09ef602d17dc5ab23df1bb43c82c83d0e694b) + + app/tools/gimpwarptool.c | 208 + ++++++++++++++++++++++++++--------------------- + 1 file changed, 114 insertions(+), 94 deletions(-) + +commit 2758a34b0280ffac7a06413676039e28b6926d7d +Author: Jehan +Date: Sun Mar 3 19:27:04 2019 +0100 + + desktop: update a bit the 2.10.10 appdata. + + (cherry picked from commit 8fce349c4e1c3cff39f4dc5ef58f02524ecd2c95) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +commit 0ed30bd1ebf692b6ca200c3c137d2f62ec42d889 +Author: Rodrigo Lledó +Date: Mon Mar 4 09:34:37 2019 +0000 + + Update Spanish translation + + po/es.po | 485 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 261 insertions(+), 224 deletions(-) + +commit 1f641546a9202ae8e5d96fc7f02fac53d6b785de +Author: Anders Jonsson +Date: Sun Mar 3 21:40:06 2019 +0000 + + Update Swedish translation + + po/sv.po | 4006 + ++++++++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 2305 insertions(+), 1701 deletions(-) + +commit 9b6604d011e6d64f855c4bf1ee481dce3a02e6eb +Author: Ell +Date: Sun Mar 3 16:18:50 2019 -0500 + + app: add "real-time preview" option to the warp tool + + Add a "real-time preview" option to the warp tool, which, when + toggled, causes the preview to be rendered synchronously during + motion. This is slower, but gives better feedback. + + (cherry picked from commit a93af839fe10534310a2446cd7d824b64337b4b0) + + app/tools/gimpwarpoptions.c | 18 ++++++++++++++++++ + app/tools/gimpwarpoptions.h | 1 + + app/tools/gimpwarptool.c | 33 ++++++++++++++++++++++++++++----- + 3 files changed, 47 insertions(+), 5 deletions(-) + +commit 40a9b38b5c34fa20c99711d6bcddcd58c2e33ef3 +Author: Ell +Date: Sun Mar 3 15:53:28 2019 -0500 + + app: properly implement "spacing" option in the warp tool + + The "spacing" option of the warp tool used to be handled by the + gegl:warp op, and have little effect. Instead, implement it in the + warp tool directly, having the same effect as the other paint + tools. + + Having a properly-working "spacing" option allows us to use EXACT + motion mode without cirppling down performance, which means that + the stroke now follows the pointer exactly, even when processing + takes a while. + + Decrease the default "spacing" value to 10. + + (cherry picked from commit e8a39d5c49a01d128f4237100845eda7523255f0) + + app/tools/gimpwarpoptions.c | 2 +- + app/tools/gimpwarptool.c | 139 + +++++++++++++++++++++++++++++--------------- + app/tools/gimpwarptool.h | 6 +- + 3 files changed, 98 insertions(+), 49 deletions(-) + +commit 579562f177f56f87369f2321e235a31711d9f425 +Author: Ell +Date: Sun Mar 3 14:10:40 2019 -0500 + + app: improve warp-tool invalidated-area calculation + + In the warp tool, use the gegl:map-relative node to calculate the + invalidated drawable area when the displacement field changes, to + account for box filtering. + + (cherry picked from commit aa915280841f7e660d08696fe6a4f56d909ff120) + + app/tools/gimpwarptool.c | 27 ++++++++++++++++++++++++--- + 1 file changed, 24 insertions(+), 3 deletions(-) + +commit bf89b9a9bcc917f1cd8ad2fb64a8793ed7d93e00 +Author: Ell +Date: Sun Mar 3 09:10:06 2019 -0500 + + app: set warp-tool scroll-lock to TRUE + + In the warp tool, set scroll-lock to TRUE, so that the image isn't + scrolled when click-dragging outside the canvas, like the rest of + our paint tools. + + (cherry picked from commit 6acedc7ebb4d6b5f6484f85908d46ff690463608) + + app/tools/gimpwarptool.c | 1 + + 1 file changed, 1 insertion(+) + +commit 1c66573f4c96e59a986854310e74d29ec9eed7ea +Author: Michael Natterer +Date: Sun Mar 3 20:25:40 2019 +0100 + + app: optimize gimp_layer_invalidate_boundary() + + Don't mess with the image's mask if the layer is not attached or not + visible. + + app/core/gimplayer.c | 29 ++++++++++++++--------------- + 1 file changed, 14 insertions(+), 15 deletions(-) + +commit 4ee74a11e69bbf8f66cf98b6a2c0dcd3b0da986b +Author: Michael Natterer +Date: Sun Mar 3 19:42:22 2019 +0100 + + app, plug-ins: move brush pipe saving from the file-gih plug-in to + the core + + As with .gbr and .pat, only the actual saving code, not the export + logic and GUI. + + (cherry picked from commit cc7e07fecbee37a7698af90f206fd6f414fefa4b) + + app/file-data/file-data-gih.c | 185 +++++++++++----------- + app/file-data/file-data.c | 101 +++++++++++- + plug-ins/common/file-gih.c | 351 + +++++++++--------------------------------- + 3 files changed, 254 insertions(+), 383 deletions(-) + +commit 2071f3bdc62c6d285b8e69e43f1431b7c633a77a +Author: Michael Natterer +Date: Wed Feb 27 00:06:17 2019 +0100 + + app: factor file_gbr_drawable_to_brush() out of + file_gbr_image_to_brush() + + (cherry picked from commit 861106a0b3e9d30e5f5622a52036e463d4f44176) + + app/file-data/file-data-gbr.c | 121 + ++++++++++++++++++++++++------------------ + app/file-data/file-data-gbr.h | 32 ++++++----- + 2 files changed, 87 insertions(+), 66 deletions(-) + +commit d8aab75c096aeebf4d8cce80b180883f875aaf72 +Author: Michael Natterer +Date: Tue Feb 26 23:53:11 2019 +0100 + + app: factor out gimp_brush_pipe_set_params() from + gimp_brush_pipe_load() + + (cherry picked from commit e742b4a95b780f1623c7af059dfa53ef1b1b6c1b) + + app/core/gimpbrushpipe-load.c | 103 + ++++++++++-------------------------------- + app/core/gimpbrushpipe.c | 86 +++++++++++++++++++++++++++++++++++ + app/core/gimpbrushpipe.h | 5 +- + 3 files changed, 114 insertions(+), 80 deletions(-) + +commit 590f58abffe46ebc2ac68924dcef168c21fc5c2b +Author: Jehan +Date: Sun Mar 3 19:20:33 2019 +0100 + + NEWS: keep up-to-date. + + Not updated for a while, let's try to keep the file up-to-date so that + we can make relevant news. If I forgot stuff, please add them! :-) + + NEWS | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +commit d50abef3a2f5a5ee5efc0d643b8dc8b8004b335a +Author: sabri ünal +Date: Sun Mar 3 16:50:21 2019 +0000 + + #3050 - Four tooltips marked as translatable + + (cherry picked from commit 3c646978786fae412e10730252a34d167323f202) + + app/config/gimprc-blurbs.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +commit 15038f8f3d9281fc37e5e2fa7d833e3746ac3d5f +Author: Alan Mortensen +Date: Sun Mar 3 15:08:38 2019 +0100 + + Updated Danish translation of gimp-python + + po-python/da.po | 360 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 347 insertions(+), 13 deletions(-) + +commit fe0cf5777c53a2820d71946a8cac1d38e81a3ff1 +Author: Alan Mortensen +Date: Sun Mar 3 15:08:16 2019 +0100 + + Updated Danish translation of gimp-plug-ins + + po-plug-ins/da.po | 542 + ++++++++++++++++++++++++++++-------------------------- + 1 file changed, 281 insertions(+), 261 deletions(-) + +commit 481cca3907512a4a591f4e6a2db1f27936636c8f +Author: Alan Mortensen +Date: Sun Mar 3 15:07:10 2019 +0100 + + Updated Danish translation of gimp-libgimp + + po-libgimp/da.po | 44 ++++++++++++++++++++++---------------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +commit 92ec0bb6a3f4099e81536abfb7a9d6e5bcb46cd7 +Author: Alan Mortensen +Date: Sun Mar 3 15:05:58 2019 +0100 + + Updated Danish translation + + po/da.po | 1852 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 986 insertions(+), 866 deletions(-) + +commit 1705333141291a49a85b806831c9796a743a325d +Author: Ell +Date: Sun Mar 3 04:21:34 2019 -0500 + + libgimpwidgets: fix gimp_spin_button_new_with_range() + + (cherry picked from commit 19eb9fb1bfa2946111c47bb65a473ba9c92da6d5) + + libgimpwidgets/gimpspinbutton.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d81d8577981f36006a08b8e2a2ea9fd171c3dec9 +Author: Ell +Date: Sat Mar 2 16:20:01 2019 -0500 + + app: in gimppaintcore-loops, improve CanvasBufferIterator algorithm + helper-class + + In gimppaintcore-loops, fix the CanvasBufferIterator algorithm + helper-class so that it may appear more than twice in the + hierarchy, and integrate it into the normal dispatch-dependency + system, instead of having dependent algorithms inherit it directly. + + (cherry picked from commit 03810861d2164eda997cbc5bf5a88b64519e2ccc) + + app/paint/gimppaintcore-loops.cc | 292 + ++++++++++++++++++++------------------- + 1 file changed, 148 insertions(+), 144 deletions(-) + +commit 7f6e522a358c84a5804909d5c0702b8e870aeb77 +Author: Ell +Date: Fri Mar 1 14:33:04 2019 -0500 + + app: in gimp:mask-components, don't forward empty aux when mask is + fully set + + ... since the result needs to be fully opaque in this case, rather + than fully transparent. + + (cherry picked from commit 371e35eeb84c195fde6aa397a799582854e00752) + + app/operations/gimpoperationmaskcomponents.cc | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +commit ef44c72556297b78dc7c5caa067ca8de049107b3 +Author: Nathan Follens +Date: Fri Mar 1 19:34:41 2019 +0000 + + Update Dutch translation + + po-tips/nl.po | 345 + ++++++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 275 insertions(+), 70 deletions(-) + +commit 0eac712bc4552316576637102206556204adce19 +Author: Piotr Drąg +Date: Fri Mar 1 20:24:53 2019 +0100 + + Update Polish translation + + po/pl.po | 197 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 106 insertions(+), 91 deletions(-) + +commit a3db1857fcc37fe2c594454892d22751eab7add5 +Author: Ell +Date: Fri Mar 1 14:15:13 2019 -0500 + + app: small fix to last commit + + (cherry picked from commit a8a29312dea8699a1beb3058018990d3b09dc410) + + app/operations/gimpoperationmaskcomponents.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 37797f9de496f11fcaede0bcb15d201613601077 +Author: Ell +Date: Fri Mar 1 14:07:03 2019 -0500 + + app: fix toggling-off of alpha channel + + Fix gimp:mask-components to use full-oapcity value for the alpha + component when it's masked-in and there's no "aux" input, so that + the image is rendered with full opacity when the alpha channel's + visiblity is toggled off, as per bug #143315. + + (cherry picked from commit 6419ed32460fcad47464cd97980a5a61eb86d297) + + app/operations/gimpoperationmaskcomponents.cc | 123 + ++++++++++++++++++++------ + app/operations/gimpoperationmaskcomponents.h | 2 + + 2 files changed, 97 insertions(+), 28 deletions(-) + +commit 1d05c286c546bee042d823c0429b78058eb7c740 +Author: Ell +Date: Fri Mar 1 13:51:25 2019 -0500 + + app: disable spec. 8-bpp version of gimp:mask-components for + non-little-endian platforms + + (cherry picked from commit 6166f9ae144aa880a9def65ada23f66e1fb103c0) + + app/operations/gimpoperationmaskcomponents.cc | 4 ++++ + 1 file changed, 4 insertions(+) + +commit d70a030b752d7e6eeb672b009837d54a928c9a9d +Author: Nathan Follens +Date: Fri Mar 1 17:08:57 2019 +0000 + + Update Dutch translation + + po-windows-installer/nl.po | 282 + +++++++++++++++++++++++++-------------------- + 1 file changed, 154 insertions(+), 128 deletions(-) + +commit efc632948f98250601e96143409da7e2b7528233 +Author: Ell +Date: Fri Mar 1 09:04:26 2019 -0500 + + Issue #3037 - Crash in transform tools after transforming an + out-of-bounds selection + + In GimpTransformGridTool, fix weak-pointer initialization for the + out-of-bounds selection-boundary canvas item, to avoid a CRITICAL + when initializing the tool in transform-selection mode for a fully + out-of-bounds selection, and a subsequent segfault when re- + initializing the tool. + + (cherry picked from commit 1e739675a4b0c6787b49c3fb32d583b511645964) + + app/tools/gimptransformgridtool.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 7c7eb399b74fa92fc86f19809816ad1b3f28f4e2 +Author: Øyvind Kolås +Date: Fri Mar 1 14:42:11 2019 +0100 + + build,app: require GEGL-0.4.14 + + (cherry picked from commit dde7108584474875450baa5f4acc2e07285b720f) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 67efe67ed87bdd0fd2e85f0f87bdf7a1df874296 +Author: Øyvind Kolås +Date: Fri Mar 1 14:41:47 2019 +0100 + + build,app: require babl-0.1.62 + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 5a62d61885b718c1a97eff18c3e24291a9a2035a +Author: Ell +Date: Thu Feb 28 15:43:05 2019 -0500 + + app: add "Constrain handles", "Around center" options to the + perspective tool + + Add "Constrain handles" and "Around center" options to the + perspective-transform tool's GUI, which are similar to the + corresponding options of the unified-transform tool. Both of these + options can already be controlled using Shift and Ctrl, + respectively, through the transform-grid widget, so we might as + well provide GUI toggles for them. + + (cherry picked from commit ac4b0fe9f18cb3c59d6b46f788aed849b8f9a65f) + + app/tools/gimptransformgridoptions.c | 32 + ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +commit 41790aef4bd2f5ea4f274c7f5a03f85fe94de585 +Author: Ell +Date: Thu Feb 28 15:21:40 2019 -0500 + + app: fix transform-grid center point for non-affine transforms; + use as pivot + + In GimpToolTransformGrid, use the transformed center-point of the + original polygon as the position of the center-point handle, and as + a snapping point for the pivot when "cornersnap" is TRUE, instead + of using the center-point of the transformed polygon. These two + points are different for non-affine transformations. + + Furthermore, when "use-pivot-handle" is FALSE, use the center- + point as the reference point when transforming around the pivot, + instead of the pivot itself, which might not be set. In + particular, this fixes transformation around the pivot in the + perspective tool. + + Don't explicitly set the center-point as the pivot in the scale + tool, since this commit makes it unnecessary. + + (cherry picked from commit cdc5782b3c0ad94beff4682e67503e86b15a6b78) + + app/display/gimptooltransformgrid.c | 58 + +++++++++++++++++++++++++++++-------- + app/tools/gimpscaletool.c | 12 +++----- + 2 files changed, 50 insertions(+), 20 deletions(-) + +commit 3f43c2ea12de4ef89bd2d4e1ad556a278bc9d7fb +Author: Ell +Date: Thu Feb 28 09:19:55 2019 -0500 + + Issue #1554 - Select by Color tool does not select pixel(s) or area(s) + of pixel(s) + + In gimp_pickable_contiguous_region_by_color(), add a small epsilon + to the threshold value, to allow for small errors due to the input + color and pickable pixel-colors being converted to the common + format through different paths. + + While we *could* special-case threshold == 0 when the input color + comes from the same pickable, as is the case for the select-by- + color tool, and perform an exact comparison in the original format, + in the more general case the input color can come from an arbitrary + source, such as a plug-in. + + (cherry picked from commit a6c79770c31cf8ec082904e570ec309315130812) + + app/core/gimppickable-contiguous-region.cc | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +commit bf793f9d59f742f63c6a89858687dc06a08ea299 +Author: Ell +Date: Wed Feb 27 03:40:24 2019 -0500 + + build: add Korean translation to the Windows installer + + ... but keep it disabled, since the Inno Setup Korean translation + is unofficial. + + (cherry picked from commit fe35294ee7c244367dfb4963b7c72c86d0e3ac44) + + build/windows/installer/gimp3264.iss | 1 + + build/windows/installer/lang/Makefile.am | 1 + + 2 files changed, 2 insertions(+) + +commit 7bdb482eff89c4c97c84b9a577becd19fe891281 +Author: Ell +Date: Wed Feb 27 03:37:16 2019 -0500 + + po-windows-installer: technical fix to the Korean translations + + ender's name can't be encoded in the target encoding -> use ASCII. + + (cherry picked from commit ee49487cf285b50fa13d19037ff906a3c3bda8ca) + + po-windows-installer/ko.po | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 297e4410d4d084fe223a81111ed995833e894ece +Author: Sangdo Jun +Date: Wed Feb 27 07:26:57 2019 +0000 + + Add Korean translation + + po-windows-installer/LINGUAS | 1 + + po-windows-installer/ko.po | 384 + +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 385 insertions(+) + +commit 3f1e91fa0bb000c2e68a64bd8ce4db8af2efc334 +Author: Ell +Date: Mon Feb 25 11:10:17 2019 -0500 + + app: in gimp_gegl_create_flatten_node(), explicitly set color + node format + + In gimp_gegl_create_flatten_node(), explicitly set the output + format of the background gegl:color node according to the composite + space, so that no conversion is required during compositing. + + (cherry picked from commit ebf2c2fef13edf77524b906e39b2fc1d1aa686a6) + + app/gegl/gimp-gegl-nodes.c | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 5d37a244dd389d49013e032978b3fa34ae3a0fc7 +Author: Ell +Date: Mon Feb 25 04:49:04 2019 -0500 + + app: merge layers in chunks, and show progress + + In gimp_image_merge_layers() -- the internal function used by the + various layer-merging/flattenning functions -- process the merged- + layer graph in chunks, using gimp_gegl_apply_operation(), instead + of in one go, using gegl_node_blit_buffer(). Processing in chunks + better utilizes the cache, since it reduces the size of + intermediate buffers, reducing the chances of hitting the swap when + merging large images (see, for example, issue #3012.) + + Additionally, this allows us to show progress indication. Have the + relevant gimpimage-merge functions take a GimpProgress, and pass it + down to gimp_image_merge_layers(). Adapt all callers. + + (cherry picked from commit e83d8ac4f2dc0260e97a91938ae3e34608efdbf9) + + app/actions/image-commands.c | 33 +++++++++++------- + app/actions/layers-commands.c | 9 +++-- + app/core/gimpimage-merge.c | 62 + ++++++++++++++++++++++----------- + app/core/gimpimage-merge.h | 5 ++- + app/dialogs/image-merge-layers-dialog.c | 3 +- + app/dialogs/image-merge-layers-dialog.h | 3 +- + app/file/file-open.c | 3 +- + app/pdb/image-cmds.c | 8 +++-- + pdb/groups/image.pdb | 8 +++-- + 9 files changed, 89 insertions(+), 45 deletions(-) + +commit 9de3b475ae733933ef7e93394ac972751c6462b4 +Author: Ell +Date: Mon Feb 25 04:39:46 2019 -0500 + + app: streamline action_data_get_foo() functions + + Streamline the various action_data_get_foo() functions, by having + each function only match the action data directly against its + specific type(s), and use the other functions to match the action + data against their corresponding types, instead of having each + function directly exhaust all possible matches. + + Other than reducing depulication, it fixes certain cases in which + some action_data_get_foo() functions would fail to find a match, + even though one exists, since they failed to exhaust all the + options. + + (cherry picked from commit 914200f3adce71e513aae57008dc82de1ea6b205) + + app/actions/actions.c | 226 + ++++++++++++++++++++++++++++---------------------- + 1 file changed, 125 insertions(+), 101 deletions(-) + +commit 86e97536d312ca2266f2a012d9401e20f0aa1c9b +Author: Ell +Date: Sun Feb 24 13:11:33 2019 -0500 + + app: in GimpSmudge, avoid copying brush pixmap when flow = 0 + + In GimpSmudge, avoid copying the brush's dab to the paint buffer + when using a pixmap brush if the flow parameter is 0 -- it has no + effect in this case. + + (cherry picked from commit fb5987fd571fca862c7230e15d7a5315db4dd9c9) + + app/paint/gimpsmudge.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2ea7deff4d62ef43b5de44562b7dab60e4b10a44 +Author: Ell +Date: Sun Feb 24 12:54:58 2019 -0500 + + app: improve gimp_brush_core_color_area_with_pixmap() + + Reimplement gimp_brush_core_color_area_with_pixmap(), which copies + the brush's dab to the paint buffer when using a pixmap brush, in + terms of gimp-gegl-loops. This simplifies the functions, + parallelizes processing, and transparently handles float brushes. + + Replace the "mode" parameter of the function with an "apply_mask" + parameter, which specifies whether to apply the brush's mask to + the dab as part of copying. Avoid applying the mask in + GimpPaintbrush; previously, we would erroneously apply the mask + twice when using the paintbrush tool: once when copying the + dab to the paint buffer, and again when pasting the paint buffer + to the canvas. + + We still apply the mask in GimpSmudge, which results in the same + double-application behavior, however, this might be less practical + to fix. + + (cherry picked from commit 5b09af4390403152bde3a971ca49b8231cbf7914) + + app/paint/gimpbrushcore.c | 227 + +++++++++++---------------------------------- + app/paint/gimpbrushcore.h | 2 +- + app/paint/gimppaintbrush.c | 2 +- + app/paint/gimpsmudge.c | 2 +- + 4 files changed, 57 insertions(+), 176 deletions(-) + +commit 25ed35af628a7b6cc1b60ced67f0381d87f3e4f2 +Author: Ell +Date: Sun Feb 24 12:46:34 2019 -0500 + + app: accept a const GimpTempBuf in more temp-buf functions + + In gimp_temp_buf_{ref,unref}(), and + gimp_temp_buf_create_{buffer,pixmap}(), accept a const GimpTempBuf + argument. + + (cherry picked from commit 0a1e62768a891380ab04d8b53412b76257b39322) + + app/core/gimptempbuf.c | 23 +++++++++++------------ + app/core/gimptempbuf.h | 8 ++++---- + 2 files changed, 15 insertions(+), 16 deletions(-) + +commit 8b1a5ea0c23bfa0d75151ef5f95f7c3d3b35fa30 +Author: Piotr Drąg +Date: Sun Feb 24 17:37:21 2019 +0100 + + Update Polish translation + + po/pl.po | 148 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 80 insertions(+), 68 deletions(-) + +commit db675d84e34a70d323093a8125ef9816e4d76d93 +Author: Michael Natterer +Date: Sat Feb 23 19:41:06 2019 +0100 + + app: implement saving of GimpBrushPipes in the core + + Just the GimpData::save() and ::copy() part that is needed to + duplicate and rename them, the image-to-pipe logic from the export + plug-in remains to be ported. + + (cherry picked from commit aee097fa9723a16fcd989ad99a34b8bc2d26b326) + + app/core/Makefile.am | 2 + + app/core/gimp-data-factories.c | 2 +- + app/core/gimpbrushpipe-save.c | 59 ++++++++++++++++++++++ + app/core/gimpbrushpipe-save.h | 28 +++++++++++ + app/core/gimpbrushpipe.c | 108 + +++++++++++++++++++++++++++++++++-------- + 5 files changed, 179 insertions(+), 20 deletions(-) + +commit 50a09d24514fc4f49513ed5ea0349387ac458676 +Author: Ell +Date: Fri Feb 22 11:51:30 2019 -0500 + + Issue #2997 - Error importing PCX + + Commit dc069e424ab0c38c3f4ed0d91630a4d3768ae457 removed the + assumption that 1-bpp PCX files are B&W, in favor of using the + provided palette, which is (supposedly?) the correct behavior. + However, there are evidently B&W files that do not specify a + palette, resulting in an all-black image (i.e., a 2-color indexed + image, whose both palette entries are black). Since other + software, including older versions of GIMP, load such files + "correctly", let's fix this by falling back to a B&W palette when + the provded palette is uniform. + + (cherry picked from commit 11defa4271d518f1233972a86505037a4d8f6287) + + plug-ins/common/file-pcx.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +commit a5130c7f668df3a98fba2591ad8ba9754c567bc2 +Author: Ell +Date: Fri Feb 22 08:38:05 2019 -0500 + + app: add temp-buf-total varaible to the dashboard + + Add a temp-buf-total variable to the dashboard's misc group, + showing the total size of all GimpTempBuf objects. + + (cherry picked from commit bb645bae1762d70158f7ff62fe349a02dcfc5b22) + + app/core/gimptempbuf.c | 23 +++++++++++++++++ + app/core/gimptempbuf.h | 60 + ++++++++++++++++++++++++--------------------- + app/widgets/gimpdashboard.c | 17 +++++++++++++ + 3 files changed, 72 insertions(+), 28 deletions(-) + +commit 66e15ba261873e6e9b98d2df6dab72b53192dda2 +Author: Balázs Úr +Date: Thu Feb 21 19:33:58 2019 +0000 + + Update Hungarian translation + + po/hu.po | 165 + ++++++++++++++++++++++++++++++++------------------------------- + 1 file changed, 85 insertions(+), 80 deletions(-) + +commit bf9298735369ca3cfdb540d9132d2605f32ebc74 +Author: Ell +Date: Thu Feb 21 13:07:34 2019 -0500 + + app: replace use of deprecated dont-cache/no_cache with cache-policy + + Replace the use of the deprecated GeglNode::dont-cache property, + and GeglOperationClass::no_cache field, with GeglNode::cache-policy + and GeglOperationClass::cache_policy, respectively. + + See commit gegl@7f24430cda0d8c3eff311868823d445edc2a4e12. + + (cherry picked from commit 7489f0aeceb09ea73fd18a71df4b7e6b6718e2eb) + + app/operations/gimpoperationbuffersourcevalidate.c | 2 +- + app/operations/gimpoperationcagecoefcalc.c | 2 +- + app/operations/gimpoperationcagetransform.c | 1 - + app/paint/gimpperspectiveclone.c | 2 +- + 4 files changed, 3 insertions(+), 4 deletions(-) + +commit 972c7ddedea89cf80f8a87435432b2d84d3efc88 +Author: Piotr Drąg +Date: Thu Feb 21 17:35:00 2019 +0100 + + Update Polish translation + + po-windows-installer/pl.po | 14 +++--- + po/pl.po | 119 + +++++++++++++++++++++++---------------------- + 2 files changed, 69 insertions(+), 64 deletions(-) + +commit 061bce7b7a008a33c4b03aa1234983b8674b50b9 +Author: Alexandre Prokoudine +Date: Thu Feb 21 01:41:01 2019 +0300 + + Update Russian translation + + po-libgimp/ru.po | 584 ++++++++------- + po/ru.po | 2078 + ++++++++++++++++++++++++++++++------------------------ + 2 files changed, 1462 insertions(+), 1200 deletions(-) + +commit 088418e004033088315ce2a1ca8a9cba6aaf3e9b +Author: Michael Natterer +Date: Tue Feb 19 23:27:28 2019 +0100 + + app, plug-ins: move file-gih-load from the file-gih plug-in to + the core + + (cherry picked from commit 52adaa1963431e0638f4cf44693b39726437ef0c) + + app/file-data/Makefile.am | 2 + + app/file-data/file-data-gih.c | 343 ++++++++++++++++++++++++++++++++++ + app/file-data/file-data-gih.h | 37 ++++ + app/file-data/file-data.c | 65 +++++++ + plug-ins/common/file-gih.c | 418 + +----------------------------------------- + po/POTFILES.in | 1 + + 6 files changed, 449 insertions(+), 417 deletions(-) + +commit 45d3a054a4d8941d5b620e07244fbf05c7d76283 +Author: Michael Natterer +Date: Tue Feb 19 23:24:41 2019 +0100 + + app: some changes to GimpBrushPipe in preparation for .git image + loading + + - don't clear the names of the individual brushes, we need them for + a load -> save roundtrip + - for the same reason, and for convenience, store the parameter string + in the object + - clean up gimp_brush_pipe_finalize() + + (cherry picked from commit 4b456e60792619663010f892a294aa7a8080fc79) + + app/core/gimpbrushpipe-load.c | 9 +++------ + app/core/gimpbrushpipe.c | 26 +++++--------------------- + app/core/gimpbrushpipe.h | 2 ++ + 3 files changed, 10 insertions(+), 27 deletions(-) + +commit 4944e61bc3941f56e2b8c5735f01878cbc11d49e +Author: Michael Natterer +Date: Tue Feb 19 23:16:22 2019 +0100 + + app: cosmetic change in file_pat_load_invoker() + + just for consistency with file_gbr_load_invoker(). + + (cherry picked from commit 7aa99c8cf080f00ef4f375671a2169151dcc9163) + + app/file-data/file-data-pat.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +commit 13de8f2982ef7302b04c54fb90d86385b0565490 +Author: Michael Natterer +Date: Tue Feb 19 23:13:59 2019 +0100 + + app: factor file_gbr_brush_to_layer() out of the brush-to-image logic + + of file-gbr-load, and add some layer handling magic that doesn't + change a thing for simple brushes, but is needed for loading brush + pipes. + + (cherry picked from commit 3b89ae40d0817e4cc71ba400fcde6d01583d8909) + + app/file-data/file-data-gbr.c | 119 + +++++++++++++++++++++++++++++------------- + app/file-data/file-data-gbr.h | 27 +++++----- + 2 files changed, 99 insertions(+), 47 deletions(-) + +commit a07c086d7aa199f6b148c094ca25d6d2eb3df752 +Author: Ryuta Fujii +Date: Mon Feb 18 14:42:36 2019 +0000 + + Update Japanese translation + + po/ja.po | 180 + +++++++++++++++++---------------------------------------------- + 1 file changed, 49 insertions(+), 131 deletions(-) + +commit fc45f1189ee7d7fe143234d5b19904dda93effe6 +Author: Balázs Meskó +Date: Mon Feb 18 12:16:00 2019 +0000 + + Update Hungarian translation + + po/hu.po | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +commit de73cd294dd352c9ca1272764effa694574105e4 +Author: Balázs Meskó +Date: Mon Feb 18 06:54:42 2019 +0000 + + Update Hungarian translation + + po/hu.po | 3466 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 1857 insertions(+), 1609 deletions(-) + +commit bfa6285d238bac6ea30dd25269cb5283d13f6936 +Author: Ell +Date: Sun Feb 17 13:33:10 2019 -0500 + + plug-ins: in file-dds, rename endian.h to endian_rw.h ... + + ... to avoid clashing with the system + + Thanks Andrea! + + (cherry picked from commit b5a34c3190d62294dc0316c93251baa807b26c9e) + + plug-ins/file-dds/Makefile.am | 2 +- + plug-ins/file-dds/ddsread.c | 2 +- + plug-ins/file-dds/ddswrite.c | 2 +- + plug-ins/file-dds/dxt.c | 2 +- + plug-ins/file-dds/{endian.h => endian_rw.h} | 4 ++-- + 5 files changed, 6 insertions(+), 6 deletions(-) + +commit cbd712a2bd2325cc2535595ea6a61b65fd69e72e +Author: Ell +Date: Sun Feb 17 04:11:16 2019 -0500 + + app: in operation tool, update scrolled-window size request when + options-box size changes + + In GimpOperationTool, update the options scrolled-window size + request whenever the size of the options box changes, instead of + only when constructing the GUI, since the options layout may change + dynamically through GUM, or through a custom GUI. + + app/tools/gimpoperationtool.c | 194 + +++++++++++++++++++++++------------------- + 1 file changed, 105 insertions(+), 89 deletions(-) + +commit de1ac871e953478f92fdd2c2c71ab3dcc7269eab +Author: Ell +Date: Sat Feb 16 14:09:35 2019 -0500 + + app: in gimppaintcore-loops, unsuppress + COMBINE_PAINT_MASK_TO_CANVAS_BUFFER algorithm + + In gimppaintcore-loops, unsuppress the + COMBINE_PAINT_MASK_TO_CANVAS_BUFFER algorithm (partially + reverts commit b717ead1abd487f663668ac131883dff0ffe4557.) + + In gimp_paint_core_paste() it's always used together with + CANVAS_BUFFER_TO_PAINT_BUF_ALPHA, which matches a combined + algorithm, preventing it from being called, however, it can still + be called through gimp_paint_core_replace(), which uses it + together with CANVAS_BUFFER_TO_COMP_MASK, which doesn't have a + combined algorithm. We can, however, filter out + CANVAS_BUFFER_TO_PAINT_BUF_ALPHA whenver + COMBINE_PAINT_MASK_TO_CANVAS_BUFFER is matched (since the combined + algorithm will be matched beforehand when both algorithms are + included). + + (cherry picked from commit 6fe57a946bd22cdc04eff2529497550752b7c773) + + app/paint/gimppaintcore-loops.cc | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 99130068bcae26c775b2892a04f043fcc644d6c7 +Author: Ell +Date: Sat Feb 16 13:06:24 2019 -0500 + + app: in gimp:mask-components, handle half-precision float formats + + (cherry picked from commit 7c1429d1fff162f2a69b4675533cc89f012dadef) + + app/operations/gimpoperationmaskcomponents.cc | 4 ++++ + 1 file changed, 4 insertions(+) + +commit d424a418cbbe3664e5899fb39fca2a3544e37401 +Author: Ell +Date: Sat Feb 16 13:01:18 2019 -0500 + + app: in GimpDrawableFilter, don't mask-out alpha comonent for + alpha-less drawables + + In gimp_drawable_filter_sync_affect(), don't mask-out the filter's + alpha component when the drawable doesn't have an alpha channel, + since this is no longer necessary -- we now explicitly convert the + output to the drawable format as part of the graph -- and it + prevents the gimp:mask-components node from becoming a NOP. + + (cherry picked from commit af2c7d1b33a61ca3928620d0ce9834f1f34193e1) + + app/core/gimpdrawablefilter.c | 15 +++------------ + 1 file changed, 3 insertions(+), 12 deletions(-) + +commit 353951a44f888d6629eb8fb3a8bef8f6b6703bf2 +Author: Ell +Date: Sat Feb 16 12:56:20 2019 -0500 + + app: set/clear component-mask alpha-bit of alpha-less drawables, + to make mask uniform + + In gimp_drawable_get_active_mask(), when the drawable doesn't have + an alpha channel, set or clear the mask's alpha bit, according to + the state of the other bits, so that it never gets in the way of a + fully set/clear mask. The value of the alpha bit doesn't matter + when there's no alpha channel, however, having a uniform mask + allows us to skip component masking altogether. + + Additionally, provide a default implementation for + GimpDrawable::get_active_mask() which returns a full mask, and + remove the equivalent implementation for GimpChannel. + + (cherry picked from commit 1b900bfa166c240d08002325d74c1a39518e8f6f) + + app/core/gimpchannel.c | 10 ---------- + app/core/gimpdrawable.c | 34 ++++++++++++++++++++++++++++------ + 2 files changed, 28 insertions(+), 16 deletions(-) + +commit a04e24ce3e050a29c4b374097deb1f40adca0c24 +Author: Michael Natterer +Date: Sat Feb 16 19:00:04 2019 +0100 + + app, plug-ins: move brush (gbr) saving to the core + + just the export logic remains in the plug-in, just as for patterns. + + (cherry picked from commit 90164c4951222349b712d33ea043c870c9b0b44f) + + app/file-data/file-data-gbr.c | 129 ++++++++++++++++++++++++++--- + app/file-data/file-data.c | 87 +++++++++++++++++++- + plug-ins/common/file-gbr.c | 184 + ++++++------------------------------------ + 3 files changed, 229 insertions(+), 171 deletions(-) + +commit c08932cab043c3181b991e5dc0a78984d7783a7d +Author: Michael Natterer +Date: Sat Feb 16 18:59:01 2019 +0100 + + plug-ins: forward the error message if file-pat-save-internal fails + + (cherry picked from commit b71ebcea6df689668cfa08938973487613417da6) + + plug-ins/common/file-pat.c | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 339368f562380e1242d6ec2fac102ae6e592e1eb +Author: Ell +Date: Sat Feb 16 10:09:34 2019 -0500 + + app: in gimppaintcore-loops, in MaskComponents::finalize_step(), + chain up + + (cherry picked from commit e513e9e0543b691941a9162f56aa79e11e71ab66) + + app/paint/gimppaintcore-loops.cc | 2 ++ + 1 file changed, 2 insertions(+) + +commit b6546536f961195cf0c7ec2c8d3dfbf4dd033446 +Author: Ell +Date: Sat Feb 16 09:43:34 2019 -0500 + + app: use MASK_COMPONENTS algorithm in + gimp_paint_core_{paste,replace}() + + Remove the mask_components_onto() gimppaintcore-loops function, and + the GimpPaintCore::comp_buffer member. Instead, in + gimp_paint_core_paste() and gimp_paint_core_replace(), use the + MASK_COMPONENTS algorithm, added in the previous commit. + + (cherry picked from commit c7d8d9ba2ee7e1ecd47d3220b0951ebb7189609b) + + app/paint/gimppaintcore-loops.cc | 73 ------------------------------- + app/paint/gimppaintcore-loops.h | 7 --- + app/paint/gimppaintcore.c | 94 + +++++++++++++--------------------------- + app/paint/gimppaintcore.h | 1 - + 4 files changed, 29 insertions(+), 146 deletions(-) + +commit c4430b3debe36d1bdce612b329a8d7c7818e579a +Author: Ell +Date: Sat Feb 16 09:37:45 2019 -0500 + + app: in gimppaintcore-loops, add MASK_COMPONENTS algorithm + + In gimppaintcore-loops, add a new MASK_COMPONENTS algorithm, which + masks the output of compositing into the destination buffer, + according to a component mask. The algorithm uses the same code as + gimp:mask-comopnents, and can be used as part of a + gimp_paint_core_loops_process() pipeline, instead of using a + separate function. + + (cherry picked from commit 08fa46ea41d3af367c215fdde9271358834ea7e9) + + app/paint/gimppaintcore-loops.cc | 135 + ++++++++++++++++++++++++++++++++++++++- + app/paint/gimppaintcore-loops.h | 5 +- + 2 files changed, 138 insertions(+), 2 deletions(-) + +commit fe69dd2207fb5bda30b11c68bb277a1a812fe68e +Author: Ell +Date: Sat Feb 16 08:56:11 2019 -0500 + + app: in gimppaintcore-loops, add [Temp]CompBuffer algorithm + helper-classes + + In gimppaintcore-loops, add a CompBuffer algorithm helper-class, + which provides access to the output buffer used for compositing, + to be used by the DO_LAYER_BLEND algorithm instead of the + destination buffer. + + CompVuffer itself doesn't provide the storage for the buffer; this + is rather the responsibility of the algorithms that use it. The + TempCompBuffer algorithm helper-class provides temporary storage + for the compositing buffer, and can be used by algorithms that need + a temporary buffer. + + (cherry picked from commit 858f30a609e40ceffd5129a5547f549aeda7d60d) + + app/paint/gimppaintcore-loops.cc | 168 + ++++++++++++++++++++++++++++++++++++--- + 1 file changed, 156 insertions(+), 12 deletions(-) + +commit ada815673ed239f772042f60ce0008e40cf492d9 +Author: Ell +Date: Sat Feb 16 08:36:15 2019 -0500 + + app: in gimppaintcore-loops, mark algorithms as mandatory/suppressed + + In gimppaintcore-loops, use {Mandatory,Supressed}AlgorithmDispatch, + added in the previous commit, to mark certain algorithms as always + occuring, or never occuring, in all hierarchies. + + (cherry picked from commit b717ead1abd487f663668ac131883dff0ffe4557) + + app/paint/gimppaintcore-loops.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 8b86296979c7c9ea73211e57ae1b276157a8fc27 +Author: Ell +Date: Sat Feb 16 08:31:09 2019 -0500 + + app: in gimppaintcore-loops, add + {Mandatory,Suppressed}AlgorithmDispatch + + In gimppaintcore-loops, add MandatoryAlgorithmDispatch and + SuppressedAlgorithmDispatch class templates, which implement + dispatch functions suitable for algorithms which are always part of + the hierarchy, or never part of the hierarchy, respectively. Using + one of these classes as the dispatch function for a given algorithm + verifies that the algorithm is/isn't included in the requested- + algorithm set, but doesn't otherwise increase the number of + instanciated hierarchies, since only one of these cases has to be + handled. + + (cherry picked from commit fc7ffc71a3673222d41e6613ffc7512277c97f24) + + app/paint/gimppaintcore-loops.cc | 79 + ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +commit 3dd2cf27b5ce3a96e0230cc2cd42036e0d6ea8ab +Author: Ell +Date: Sat Feb 16 08:26:47 2019 -0500 + + app: in gimppaintcore-loops, remove individual-algorithm functions + + In gimppaintcore-loops, remove the individual-algorithm convenience + functions, which are merely wrappers around + gimp_paint_core_loops_process(), and aren't used anywhere anymore. + This allows us to avoid instanciating certain algorithm-hierarchies + which aren't used in practice, as will be done by the following + commits. + + (cherry picked from commit 95761db5574cad38ff3afecd0713da25a5598cec) + + app/paint/gimppaintcore-loops.cc | 133 + --------------------------------------- + app/paint/gimppaintcore-loops.h | 49 +++------------ + 2 files changed, 9 insertions(+), 173 deletions(-) + +commit 02b8845a2f2938fba233b8a54b1513ffddb96309 +Author: Ell +Date: Sat Feb 16 08:19:06 2019 -0500 + + app: improve gimp:mask-components + + Add specialized versions of gimp:mask-components for 8-, 16-, and + 32-bpc formats, to improve efficiency, and to preserve the contents + of masked-out components exactly. + + Provide public functions for format-selection and processing, which + we'll use in the painting code, instead of reimplementing component + masking. + + (cherry picked from commit ee156b8fd63aacbc2b72b90e863aa4a929e52d75) + + app/operations/gimpoperationmaskcomponents.cc | 283 + +++++++++++++++++++++++--- + app/operations/gimpoperationmaskcomponents.h | 12 +- + 2 files changed, 261 insertions(+), 34 deletions(-) + +commit 3925ae5fbb3f28e8c28e25fad24a95b044b258a4 +Author: Ell +Date: Sat Feb 16 04:30:39 2019 -0500 + + app: convert gimp:mask-components to C++ + + ... in preperation for next commit. + + (cherry picked from commit a7f7a485bd68e45cab07fe3c730743f716b596cf) + + app/operations/Makefile.am | 2 +- + ...maskcomponents.c => gimpoperationmaskcomponents.cc} | 18 + ++++++++++++------ + 2 files changed, 13 insertions(+), 7 deletions(-) + +commit 07dd124e06f18b645390d88fe50e161b88233fe4 +Author: Snehalata B Shirude +Date: Sat Feb 16 08:44:53 2019 +0000 + + Update Marathi translation + + po-plug-ins/mr.po | 663 + +++++++++++++++++++++++++++--------------------------- + 1 file changed, 336 insertions(+), 327 deletions(-) + +commit 08746b7e26d6b2518640b25da04d74c0c036a01e +Author: Snehalata B Shirude +Date: Sat Feb 16 08:40:56 2019 +0000 + + Update Marathi translation + + po-windows-installer/mr.po | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 83552620eb40b7bb6cfc801cd74388152a0490a7 +Author: Ell +Date: Fri Feb 15 12:28:30 2019 -0500 + + app: in gimp_gegl_apply_cached_operation(), don't dup src buffer + for point ops + + In gimp_gegl_apply_cached_operation(), when the source and + destination buffers are the same, avoid duplicating the source + buffer when the applied operation is a point operation, since + applying it in chunks from/to the same buffer is not a problem in + this case. + + (cherry picked from commit 0d21f2469b412b660073b9ed1eff37b5f7320a60) + + app/gegl/gimp-gegl-apply-operation.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +commit bd67478b49516519068d72c3321c22981ac047a4 +Author: Ell +Date: Fri Feb 15 12:27:55 2019 -0500 + + app: add gimp_gegl_node_is_point_operation() + + ... which takes a GeglNode, and determines if the associated + operation is a point operation. + + Use in GimpFilterTool, instead of performing the same check + manually. + + (cherry picked from commit 918f2e75bda4e8c93ceb76a78fdbd81a21181759) + + app/gegl/gimp-gegl-utils.c | 18 ++++++++++++++++++ + app/gegl/gimp-gegl-utils.h | 2 ++ + app/tools/gimpfiltertool.c | 35 ++++++++++++++++------------------- + 3 files changed, 36 insertions(+), 19 deletions(-) + +commit ace1229823db2849482d45059315851f7f494d56 +Author: Ryuta Fujii +Date: Fri Feb 15 14:10:20 2019 +0000 + + Update Japanese translation + + po/ja.po | 97 + ++++++++++++++++++---------------------------------------------- + 1 file changed, 27 insertions(+), 70 deletions(-) + +commit 9a30591b1e419a3de22b361741284617229c1325 +Author: Piotr Drąg +Date: Thu Feb 14 19:48:31 2019 +0100 + + Update Polish translation + + po/pl.po | 50 +++++++++++++++++++++++++------------------------- + 1 file changed, 25 insertions(+), 25 deletions(-) + +commit 3847404db676fbd1d9f48e43bfa57424aa221a3a +Author: Ell +Date: Thu Feb 14 11:08:45 2019 -0500 + + app: #include in gimpoperationreplace.c + + ... for memset(). + + (cherry picked from commit 0cf77b0a3baebc43f0572a6052ae4b65b08ecd51) + + app/operations/layer-modes/gimpoperationreplace.c | 2 ++ + 1 file changed, 2 insertions(+) + +commit d4976dfc6195c16392408f775a22b627453182d2 +Author: Ell +Date: Thu Feb 14 10:31:29 2019 -0500 + + app: change behavior of REPLACE mode for fully-transparent pixels + + When the result of compositing has an alpha value of 0, the + corresponding color value is not mathematically defined. + Currently, all out layer modes opt to preserve the destination's + color value in this case. However, REPLACE mode is different + enough to warrant a different behavior: + + Unlike the other layer modes, when the compositing opacity + approaches 0 or 1, the output color value approaches the + destination or source color values, respectively, regardless of the + output alpha value. When the opacity doesn't approach 0 or 1, the + output color value generally doesn't approach a limit as the output + alpha value approaches 0, however, when both the destination and + source alpha values are equal, the output color value is always a + simple linear interpolation between the destination and source + color values, according to the opacity. In other words, this means + that it's reasonable to simply use the above linear interpolation + for the output color value, whenever the output alpha value is 0. + + Since filters are commonly combined with the input using REPALCE + mode with full opacity, this has the effect that filters may now + modify the color values of fully-transparent pixels. This is + generally desirable, IMO, especially for point filters. Indeed, + painting with REPLACE mode (i.e., with tools that use + gimp_paint_core_replace()) behaved excatly as described above, and + had this property, before we switched gimp_paint_core_replace() to + use the common compositing code; this created a discrepancy between + painting and applying filters, which is now gone. + + A side effect of this change is that we can now turn gimp:replace + into a NOP when the opacity is 100% and there's no mask, which + avoids the compositing step when applying filters. We could + previously only apply this optimization to PASS_THROUGH mode, which + is a subclass of REPLACE mode. + + Note that the discussion above concerns the UNION composite mode, + which is the only mode we currently use REPLACE in. We modify the + rest of the composite modes to match the new behavior: + CLIP_TO_BACKDROP always preserves the color values of the + destionation, CLIP_TO_LAYER always preserves the color values of + the source, and INTERSECTION always produces fully-zeroed pixels. + + (cherry picked from commit 27e8f452b34d4b4a570e8c9c3469b3b13e54dd61) + + .../layer-modes/gimpoperationpassthrough.c | 51 ------------ + app/operations/layer-modes/gimpoperationreplace.c | 93 + ++++++++++++++-------- + 2 files changed, 58 insertions(+), 86 deletions(-) + +commit f844965455700f04e202a7d439f72c7c1ea73dd6 +Author: Ell +Date: Thu Feb 14 08:09:16 2019 -0500 + + app: remove gimp_gegl_replace() + + Remove gimp_gegl_replace(), which is not used anywhere since the + last commit. It's redundant with the rest of our compositing code, + in particular, gimp:replace and gimp:mask-components. + + (cherry picked from commit d2f84131734f97747762660bd05f0fbe4c5d2bb4) + + app/gegl/gimp-gegl-loops.cc | 127 + -------------------------------------------- + app/gegl/gimp-gegl-loops.h | 11 ---- + 2 files changed, 138 deletions(-) + +commit 116e8a6d4ba70e0178e510096cb06d9a66cc6994 +Author: Ell +Date: Thu Feb 14 07:56:54 2019 -0500 + + app: remove gimp_drawable_replace_buffer() + + Remove gimp_drawable_replace_buffer(), which is no longer used + anywhere since commits ddb69b77a7610f578f5143af1cdeed2c27d4f3e4 and + 3451ffb62cc3365d13d494586177ca2e1dacd4bf. This eliminates + redundancy, since all compositing is now done through the layer- + mode code. + + Furthermore, gimp_drawable_replace_buffer() used the drawable's + active-component array, whose layout depends on the image mode, as + an argument to gimp_gegl_replace(), which always expects an RGBA + component array, resulting in broken component masking in non-RGB + images. + + (cherry picked from commit 2074accb60b2591b36cb86cc557da87cab2dd84d) + + app/core/gimpdrawable-combine.c | 133 + ---------------------------------------- + app/core/gimpdrawable-combine.h | 36 ++++------- + app/core/gimpdrawable.c | 26 -------- + app/core/gimpdrawable.h | 20 ------ + 4 files changed, 13 insertions(+), 202 deletions(-) + +commit 0fef0a55f888556cd5b1c9ac808d89799064abd7 +Author: Jehan +Date: Thu Feb 14 13:46:09 2019 +0100 + + app: gimp_edgel_region_area() may return < 0 for non-closed zones. + + The algorithm to compute a zone area by following its border only + works + well for fully closed zones. It may return negative values otherwise. + Let's just assume the created zone is big in this case (which may + or may + not be the case, but this is the safe case as it does not prevent + closure creation). + + (cherry picked from commit 0636c302a394158c2fa36e39fec872569f6de4f2) + + app/core/gimplineart.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +commit daf7754e7a6fcf24152e748fbd7d11073c649361 +Author: Rodrigo Lledó +Date: Thu Feb 14 12:17:52 2019 +0000 + + Update Spanish translation + + po/es.po | 136 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 73 insertions(+), 63 deletions(-) + +commit e24f9eec3741119eaa71d495487521dbbafdcfa5 +Author: Jehan +Date: Thu Feb 14 12:09:29 2019 +0100 + + Issue #2961: minor coding style fix. + + Missing space, and anyway let's use named parameters, which makes the + code better self-documented. + + (cherry picked from commit 35eff00e2ea489923a3c1452d364a234076ac8ad) + + plug-ins/pygimp/gimpfu.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 648703b6f0e50cb4a44b07b0c127ee576cd6c586 +Author: Alexandre Prokoudine +Date: Thu Feb 14 01:18:09 2019 +0300 + + Remap Linear Invert's mnemonic to I to remove a duplicate with Levels + + app/actions/filters-actions.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 04052fe3a47663b4a88aff032465de07efd7f29e +Author: Ell +Date: Wed Feb 13 13:24:50 2019 -0500 + + app: in GimpOperationTool, add scrollbar width to scrolled-window + requisition + + ... so that the scrollbar doesn't cover the child in wide UIs. + + app/tools/gimpoperationtool.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +commit 0a82bd1109cbcfacec30f141dbb9f33be6606ad2 +Author: Ell +Date: Wed Feb 13 12:32:03 2019 -0500 + + app: in gimpbrushcore-loops, fix memory corruption in clear_edges() + + s/width/height/ + + Fixes commit 9d19e804edea2e6d116f220868e8a5b836cc5ab2. + + (cherry picked from commit 72d4977574fd0cfff2dce2be02079fa4fc97211d) + + app/paint/gimpbrushcore-loops.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 3ff2285ba834a6fe9fd0532576bf689765a89585 +Author: Veerasamy Sevagen +Date: Tue Feb 12 15:09:19 2019 +0000 + + Setting the FileChooserbutton to a fixed width. + + (cherry picked from commit 582801ccc5a3b3e49b1284554863239ce193f862) + (cherry picked from commit b44c2e935278c2ef42ed1e6110bf9e7add3e1d58) + (cherry picked from commit cdd686f7529cfc52a0225a8a94b7395e3f1921ec) + (cherry picked from commit 3b818f6a9b0a6669f26856f6b534711d0d47471b) + (cherry picked from commit c7d5438b491166cd410bb315d8109beb1a6a5f13) + + Note from reviewer: sorry, this was not squashed on master! Gitlab + failed to squash even though the "squash commits" checkbox was + checked! + So it ended as a 5-commit 1-line change! + :-/ + + plug-ins/pygimp/gimpfu.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 18d86cccbecc952bfaac16f1173873e4472ffae7 +Author: Jehan +Date: Wed Feb 13 16:30:17 2019 +0100 + + NEWS: update. + + NEWS | 3 +++ + 1 file changed, 3 insertions(+) + +commit b409a781cdd6626bc93279c0da6dd2de6edf1cbf +Author: Jehan +Date: Wed Feb 13 15:48:27 2019 +0100 + + app: out-of-gamut for grayscale images too in GimpFgBgEditor. + + I realized that the same issue as for indexed images could also + apply to + grayscale. If your fg/bg colors are not gray, it should not be + expected + for them to be paintable. So let's give the out-of-gamut hint. + + (cherry picked from commit 9090de96f2ec1aa48e3f2fb4750b0e4d4227f4e2) + + app/widgets/gimpfgbgeditor.c | 58 + +++++++++++++++++++++++++++++--------------- + 1 file changed, 39 insertions(+), 19 deletions(-) + +commit e48c239459786566e1b5352f49847e1b86b45c6b +Author: Jehan +Date: Wed Feb 13 15:19:07 2019 +0100 + + app: GimpFgBgEditor displays the out-of-gamut color for indexed + images. + + If a color is not within the indexed image's palette, we can + consider it + to be out-of-gamut too (the gamut of such image being its palette). + + This is a first step towards fixing #2938. Basically currently opening + indexed images is not made obvious and you can end up thinking GIMP is + broken as when you try to paint with a given FG or BG color, you + may get + a completely different color on the canvas. And it is not obvious to + realize why. Now at least, the FG/BG color will tell you when + the color + you are trying to paint with is not within the accepted palette. + + (cherry picked from commit 7b4c96d03d863c021b2386168b07cc65ee6577f2) + + app/widgets/gimpfgbgeditor.c | 51 + +++++++++++++++++++++++++++++++++++++++----- + app/widgets/gimpfgbgeditor.h | 2 ++ + 2 files changed, 48 insertions(+), 5 deletions(-) + +commit 3fd78441ba8fa5f2e366f8a604b9bc2520f2d9bb +Author: Jehan +Date: Tue Feb 12 13:44:01 2019 +0100 + + app: pack color picker and hexadecimal entry on same line in Color + dock. + + There was no use to pack them on 2 lines. It was just making the FG/BG + widget overly huge, and the color picker button and hex entry + very long. + This was just ugly. Also this dock is tall enough, so packing things + horizontally when it makes sense is better. + + (cherry picked from commit 45adf9d0d79b302e5bf4f1dda2b7d8dbc04d2cb8) + + app/widgets/gimpcoloreditor.c | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +commit 4774bd3b489c298ceda21974c28e1f1d46a839d0 +Author: Jehan +Date: Mon Feb 11 15:17:36 2019 +0100 + + app: s/plug-In/plug-in in a GUI-visible string. + + (cherry picked from commit a6aa14bca28a96b9558d01150f1172bc7e26bf12) + + app/file/file-open.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit b2fa4c6299cec75ecc9fd6669d445006655e87a3 +Author: Jehan +Date: Sat Feb 9 23:40:47 2019 +0100 + + app: clarify function to validate line art closure. + + Add some comments and string docs as it is not that obvious to + understand the whole logics, invert the return value (returning TRUE + when the closure line is accepted, instead of the opposite) and rename + it to more appropriate gimp_line_art_allow_closure(). + + (cherry picked from commit c4beca8c906927dee28055a4b5fccc9d89c4bf31) + + app/core/gimplineart.c | 69 + ++++++++++++++++++++++++++++++++++---------------- + 1 file changed, 47 insertions(+), 22 deletions(-) + +commit b2181d0fb253b9ba6f4ff64a6ded3e3b69eccb83 +Author: Jehan +Date: Sat Feb 9 23:13:58 2019 +0100 + + app: proper signedness for return value of gimp_edgel_region_area(). + + It is just weird to return a negative area and multiply it by -1. + Just apply the proper signs from the start. + + (cherry picked from commit 14e74244035eff84b2fc0661e9c7637d14006019) + + app/core/gimplineart.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit f5225f22116e58baed4c72d1e8e7beb4280e6031 +Author: Jehan +Date: Sat Feb 9 22:26:46 2019 +0100 + + app: error messages should happen on button press, not initialization. + + The initialization is sometimes done when switching tools (for + instance + when selecting the active tool by command), not always on button + press. + So the error output behavior was inconsistent, and worse, the tool was + sometimes not forbidden to run when it should have been. + Just run all the checks (layer groups, locks and whatnot) on button + press. + + (cherry picked from commit 74cfd4fd950befdb3f4d6315f6b216920197186e) + + app/tools/gimpbucketfilltool.c | 87 + +++++++++++++++--------------------------- + 1 file changed, 31 insertions(+), 56 deletions(-) + +commit 8e5e7306ca1e4d1f5e6e4d360e811d747fd95d30 +Author: Ryuta Fujii +Date: Wed Feb 13 14:11:46 2019 +0000 + + Update Japanese translation + + po/ja.po | 85 + +++++++++++++++++++++++----------------------------------------- + 1 file changed, 30 insertions(+), 55 deletions(-) + +commit 4cf6d1291274ef0ca979e419e32a05a4b0b633c3 +Author: Marco Ciampa +Date: Wed Feb 13 11:29:44 2019 +0100 + + Updated Italian translation + + po/it.po | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit 64d0e29e42076322f6e06e5d3d496a55d1cdb3a5 +Author: Michael Natterer +Date: Tue Feb 12 21:38:22 2019 +0100 + + plug-ins: remove stuff that is no longer needed from file-pat + + (cherry picked from commit 52b1348f3d204840962ce4a1645fc94af24b9a4a) + + plug-ins/common/file-pat.c | 3 --- + 1 file changed, 3 deletions(-) + +commit 7ce90a19b47198bf0f578a8d31329aa9e7ca2215 +Author: Michael Natterer +Date: Tue Feb 12 21:27:13 2019 +0100 + + app, plug-ins: move pattern saving to the core + + but only the actual saving code, not the export magic and dialog. + + Add new internal procedure file-pat-save-internal which is not + registered as a file procedure and always works non-interactively on + the passed arguments and only saves the passed drawable. Use the new + internal procedure from the file-pat-save code and remove all file + writing code from the plug-in. + + This way all pattern file writing code duplication is killed, while + the whole export mechanism is completely unchanged. + + (cherry picked from commit b29ecfb5daf1e27314a878814b796670bd482a45) + + app/file-data/file-data-pat.c | 54 +++++++++++--- + app/file-data/file-data.c | 81 +++++++++++++++++++++ + plug-ins/common/file-pat.c | 164 + +++++++----------------------------------- + 3 files changed, 151 insertions(+), 148 deletions(-) + +commit 376b053129432fef66afd7144585522d9066b8bb +Author: Michael Natterer +Date: Tue Feb 12 21:15:35 2019 +0100 + + app: don't unref a NULL GimpTempBuf in gimp_pattern_copy() + + (cherry picked from commit a95c1690911e20b45e95d20a3218205fcebcab52) + + app/core/gimppattern.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +commit 4c6b1813713bf0d862f2a0e0ac72d42c9e57c1ee +Author: Ell +Date: Tue Feb 12 13:53:18 2019 -0500 + + app: link tests against libappfile-data + + (cherry picked from commit 68b22d45ed9ffad2fcb170bfabace9be79706039) + + app/config/Makefile.am | 1 + + app/tests/Makefile.am | 1 + + 2 files changed, 2 insertions(+) + +commit cf76b7f73cdb36098497b0c4a09ec496288ecd9a +Author: Michael Natterer +Date: Tue Feb 12 18:51:47 2019 +0100 + + app: implement brush saving in the core + + and enable duplicating the clipboard brush, like for patterns. + + (cherry picked from commit 1e6b26e83a59f656543b0ea7edbd5bc630c1f4fd) + + app/core/Makefile.am | 2 + + app/core/gimp-data-factories.c | 2 +- + app/core/gimpbrush-save.c | 103 + +++++++++++++++++++++++++++++++++++++++++ + app/core/gimpbrush-save.h | 28 +++++++++++ + app/core/gimpbrush.c | 27 +++++++++++ + app/core/gimpbrushclipboard.c | 15 ++---- + app/core/gimpbrushpipe.c | 4 ++ + 7 files changed, 170 insertions(+), 11 deletions(-) + +commit 3dc01393eb74d2af878c1d9f064501f11c0d3b92 +Author: Michael Natterer +Date: Tue Feb 12 18:45:49 2019 +0100 + + app: remove useless includes from gimppattern-save.c + + (cherry picked from commit 831d04ec7ed1118b8085c793bc7773ad3dac09f9) + + app/core/gimppattern-save.c | 5 ----- + 1 file changed, 5 deletions(-) + +commit 88787d905b30058e4841b138349da63884dccdc2 +Author: Ell +Date: Tue Feb 12 10:02:32 2019 -0500 + + m4macros: remove binreloc.m4 from EXTRA_DIST + + ... it was removed by commit + 4d84c1d7ee5bdd2d9cf8bfb07455547856ae6b59. + + (cherry picked from commit 74a151a1245287a7538e0ac9a3818e159d89394d) + + m4macros/Makefile.am | 1 - + 1 file changed, 1 deletion(-) + +commit ecc456556e6451c7f31045d8d96f72f04e90f206 +Author: Ell +Date: Tue Feb 12 09:35:35 2019 -0500 + + app: in gimppaintcore-loops, fix indentation in + DispatchPaintMaskToCompMask + + (cherry picked from commit 4ecbf33e18b56a65b5695bb1834fb691695aba14) + + app/paint/gimppaintcore-loops.cc | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +commit 62db4174dcb92b903ecafc8a9d6b29d237c7da89 +Author: Ryuta Fujii +Date: Tue Feb 12 14:30:29 2019 +0000 + + Update Japanese translation + + po/ja.po | 129 + +++++++++++++++++++-------------------------------------------- + 1 file changed, 39 insertions(+), 90 deletions(-) + +commit 0066a470139ff86c6745ee1a34563f075fc65b3f +Author: Ell +Date: Tue Feb 12 07:58:08 2019 -0500 + + app: in gimp_paint_core_replace(), improve applicator path + + In the applicator path of gimp_paint_core_replace(), actually use + the paint-core's applicator, instead of using + gimp_drawable_replace_buffer(). This improves speed, consolidates + code, and fixes some cases in which the latter is broken. + + Furthermore, when using CONSTANT paint application-mode, use the + paint-core's undo_buffer as the compositing source, rather than the + drawable buffer, which is the correct behavior. + + (cherry picked from commit 3451ffb62cc3365d13d494586177ca2e1dacd4bf) + + app/paint/gimppaintcore.c | 141 + ++++++++++++++++++++++++++++++++++------------ + 1 file changed, 104 insertions(+), 37 deletions(-) + +commit 940197e2d7ba3caf0c641baeba54149e08840dee +Author: Ell +Date: Tue Feb 12 06:43:01 2019 -0500 + + app: in gimp_paint_core_paste(), don't copy paint mask when combining + to canvas buffer + + In the applicator path of gimp_paint_core_paste(), use the paint + mask directly when combining it to the canvas buffer, rather than + using a copy of it, since it's not being modified. + + Fix some comments. + + (cherry picked from commit ba6713bbe1cffc1a68fe9e3ee2b3da51e1d30e50) + + app/paint/gimppaintcore.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +commit 1c2fe3b51c782b08b20559fa9c5573281ec58223 +Author: Ell +Date: Tue Feb 12 06:25:28 2019 -0500 + + app: implement gimp_paint_core_replace() using + gimp_paint_core_loops_process() + + Implement the no-applicator path of gimp_paint_core_replace() in + terms of gimp_paint_core_loops_process(), using the algorithms + added in the previous commit, instead of using + gimp_drawable_replace_buffer(). This improves speed, consolidates + code, and fixes some cases in which the latter is broken. + + Furthermore, when using CONSTANT paint application-mode, use the + paint-core's undo_buffer as the compositing source, rather than the + drawable buffer, which is the correct behavior. + + (cherry picked from commit ddb69b77a7610f578f5143af1cdeed2c27d4f3e4) + + app/paint/gimppaintcore.c | 155 + ++++++++++++++++++++++++++++++++++------------ + 1 file changed, 115 insertions(+), 40 deletions(-) + +commit f5222ce4d00ecb75eec8c1a123bb5a3e867f8e32 +Author: Ell +Date: Tue Feb 12 06:11:31 2019 -0500 + + app: in gimppaintcore-loops, add + {CANVAS_BUFFER,PAINT_MASK}_TO_COMP_MASK algorithms + + In gimppaintcore-loops, add CANVAS_BUFFER_TO_COMP_MASK and + PAINT_MASK_TO_COMP_MASK paint algorithms, which copy the canvas + buffer and the paint mask, respectively, to the compositing mask. + When there is an image mask buffer, the algorithms additionally + combine the copied mask with the mask buffer. When possible, the + algorithms use the canvas buffer/paint mask data directly as the + compositing mask data, instead of copying. + + These algorithms are necessary in order to implement + gimp_paint_core_replace() in terms of + gimp_paint_core_loops_process(), which is done by the next commit. + + (cherry picked from commit 183a55613ecfa0d45d8fddb6e09cf6a787432cfb) + + app/paint/gimppaintcore-loops.cc | 390 + ++++++++++++++++++++++++++++++++++++++- + app/paint/gimppaintcore-loops.h | 4 +- + 2 files changed, 389 insertions(+), 5 deletions(-) + +commit d4cb2d57bdbe7828bbf1b3a7f2a490ad9fa8acb1 +Author: Ell +Date: Mon Feb 11 03:42:16 2019 -0500 + + app: in gimppaintcore-loops, make sure dest_buffer is the primary + iterator buffer + + In gimppaintcore-loops, in the DO_LAYER_BLEND paint algorithm, and + in the CanvasBufferIterator algorithm helper-class, initialize the + iterator *before* initializing the base class, to make sure that + dest_buffer, or canvas_buffer, respectively and in that order, is + the primary buffer of the iterator. In particular, this avoids the + mask buffer being the primary buffer. This is desirable, since + most of the buffers we iterate over are tile-aligned to the dest/ + canvas buffers. + + (cherry picked from commit f9c072c328666d332ea46212336394f33d474072) + + app/paint/gimppaintcore-loops.cc | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +commit aa7de29f9ecb318be908ad93e5e6a01d94f8d663 +Author: Ell +Date: Tue Feb 12 05:16:16 2019 -0500 + + app: in gimppaintcore-loops, add MaskBufferIterator algorithm + helper-class + + In gimppaintcore-loops, add a MaskBufferIterator algorithm helper- + class, which provides read-only iterator access to the mask buffer, + if one is used. + + Use the new class in DoLayerBlend, instead of manually managing the + mask-buffer iterator. + + (cherry picked from commit 44281ce2c47c7d4dfc16fbb9c1bd359993e53571) + + app/paint/gimppaintcore-loops.cc | 152 + ++++++++++++++++++++++++++++++++------- + 1 file changed, 126 insertions(+), 26 deletions(-) + +commit 5a4ffd44955ee7b3951938efe26a5461a1e87704 +Author: Ell +Date: Tue Feb 12 03:02:22 2019 -0500 + + app: in gimppaintcore-loops, add [Temp]CompMask algorithm + helper-classes + + In gimppaintcore-loops, add a CompMask algorithm helper-class, + which provides access to the mask buffer used for compositing, to + be used by the DO_LAYER_BLEND algorithm instead of the image mask + buffer. + + CompMask itself doesn't provide the storage for the mask; this is + rather the responsibility of the algorithms that use it. The + TempCompMask algorithm helper-class provides temporary storage for + the compositing mask, and can be used by algorithms that need a + temporary mask. + + (cherry picked from commit e36847febb936797f9de15e834df3bd0bdeca290) + + app/paint/gimppaintcore-loops.cc | 154 + +++++++++++++++++++++++++++++++++++---- + 1 file changed, 141 insertions(+), 13 deletions(-) + +commit 1a06fa0c0ab7fad29e293e4c37fa8beb710e771b +Author: Ell +Date: Tue Feb 12 03:56:34 2019 -0500 + + app: in gimppaintcore-loops, name algorithms more consistently + + In gimppaintcore-loops, make the algorithm names, and the names of + the corresponding functions, more consistent. + + (cherry picked from commit d23e239fedb12c1fa454fa753edb6587e39a5a65) + + app/paint/gimppaintcore-loops.cc | 104 + ++++++++++++++++++++------------------- + app/paint/gimppaintcore-loops.h | 90 ++++++++++++++++----------------- + app/paint/gimppaintcore.c | 20 ++++---- + 3 files changed, 108 insertions(+), 106 deletions(-) + +commit 470f760eee9d18e2256820651f369bd75cb38d12 +Author: Ell +Date: Mon Feb 11 15:40:56 2019 -0500 + + app: in gimppaintcore-loops, allow specifying dependencies to + BasicDispatch + + ... which are dispatched before the algorithm, as in + AlgorithmDispatch. + + (cherry picked from commit 2788444df4d1849cbb7041e144e37253da834def) + + app/paint/gimppaintcore-loops.cc | 38 + ++++++++++++++++++++++++++++++++++---- + 1 file changed, 34 insertions(+), 4 deletions(-) + +commit 197ec919cdeca6a6611ac0367565e0b53f952d74 +Author: Ell +Date: Mon Feb 11 13:56:01 2019 -0500 + + app: in gimppaintcore-loops, add finalize[_step]() algorithm functions + + In gimppaintcore-loops, add finalize() and finalize_step() + algorithm functions, which get called at the end of processing the + entire area, and at the end of processing each chunk, respectively. + Algorithms can use these functions to clean up allocated resources. + + (cherry picked from commit 4d2ce154008a248d77af1dc5c0680480c764d249) + + app/paint/gimppaintcore-loops.cc | 42 + ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +commit deda2699b9945fff56034ed3cba1653e83a8cd78 +Author: Ell +Date: Mon Feb 11 09:27:04 2019 -0500 + + app: in gimppaintcore-loops, make CanvasBufferIterator self-contained + + In gimppaintcore-loops, make the CanvasBufferIterator algorithm + helper-class self-contained, not having to rely on AlgorithmBase. + + (cherry picked from commit 0d1f7241121e4c027402eb5c9fce5aa6e8cd997e) + + app/paint/gimppaintcore-loops.cc | 31 ++++++++++++++++++++++++------- + 1 file changed, 24 insertions(+), 7 deletions(-) + +commit 9a13c66d964d77609fa33148ff3ddcc0a6081ba1 +Author: Ell +Date: Mon Feb 11 03:36:36 2019 -0500 + + app: in gimppaintcore-loops, use dynamic iterator indices + + In the various gimppaintcore-loops algorithms, assign each + algorithm's iterator indices dynamically, rather than statically, + so that algorithms have more freedom in the way they initialize the + iterator. + + (cherry picked from commit 9d1d21e7162464e9fe6afbc33e8e9ee3acb9d48b) + + app/paint/gimppaintcore-loops.cc | 89 + ++++++++++++++++++++++------------------ + 1 file changed, 49 insertions(+), 40 deletions(-) + +commit 162250c3fb695e587443372f4747bb8470de65c1 +Author: Marco Ciampa +Date: Tue Feb 12 14:25:25 2019 +0100 + + Typo in Italian translation + + po-python/it.po | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 5dc78980f4c1061ca58eab85ec5df47e55a3fff7 +Author: Michael Natterer +Date: Mon Feb 11 20:56:07 2019 +0100 + + app: implement pattern saving in the core + + Add GimpData::save() implementation to GimpPattern, and change some + glue code to make patterns editable. + + Also implement GimpData::duplicate() in GimpPatternClipboard, which + makes it possible to simply copy an area and duplicate the clipboard + pattern to create a new persistent pattern. + + (cherry picked from commit e93fd73face1e0e26484c9f5f9a938479ac61a9d) + + app/core/Makefile.am | 2 + + app/core/gimp-data-factories.c | 2 +- + app/core/gimppattern-save.c | 81 + ++++++++++++++++++++++++++++++++++++ + app/core/gimppattern-save.h | 28 +++++++++++++ + app/core/gimppattern.c | 2 + + app/core/gimppatternclipboard.c | 14 ++----- + app/widgets/gimppatternfactoryview.c | 1 - + 7 files changed, 118 insertions(+), 12 deletions(-) + +commit 1595f9f4e3a27dfcf1408785bb2f291dcdbb22c0 +Author: Marco Ciampa +Date: Mon Feb 11 20:59:57 2019 +0100 + + Updated Italian translation + + po/it.po | 100 + +++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 53 insertions(+), 47 deletions(-) + +commit 4f00422bd4545b0ae42afd7373c500f557df2542 +Author: Michael Natterer +Date: Mon Feb 11 20:51:28 2019 +0100 + + app: add an "Open as Image" button to the brushes dialog + + It was probably forgotten to add in the first place (the patterns + dialog also has one). + + (cherry picked from commit fc609d12dd7b5bd998102cb71bd0bc22fbd8f870) + + app/widgets/gimpbrushfactoryview.c | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 2c53aa20678e8bc2ba066ad627d8fdc8d0b14439 +Author: Piotr Drąg +Date: Mon Feb 11 20:27:34 2019 +0100 + + Update Polish translation + + po/pl.po | 75 + +++++++++++++++++++++++++++++----------------------------------- + 1 file changed, 34 insertions(+), 41 deletions(-) + +commit b3797079bcff5798c515a799a91b4b39212ef12f +Author: Michael Natterer +Date: Mon Feb 11 18:35:07 2019 +0100 + + app: move file-pat-load from the file-pat plug-in to the core + + (cherry picked from commit 410ffc435ea0e04908e277fc46ce5fbbc41a836a) + + app/file-data/Makefile.am | 4 +- + app/file-data/file-data-pat.c | 229 + +++++++++++++++++++++++++++++++++++++++ + app/file-data/file-data-pat.h | 37 +++++++ + app/file-data/file-data.c | 63 +++++++++++ + plug-ins/common/file-pat.c | 247 + +----------------------------------------- + po/POTFILES.in | 1 + + 6 files changed, 334 insertions(+), 247 deletions(-) + +commit 616f5aac11847af1e477fefcea1b9361254201b3 +Author: Michael Natterer +Date: Mon Feb 11 12:45:06 2019 +0100 + + app, plug-ins: start consolidating brush and pattern loading/saving + code + + We currently have brush and pattern I/O code in both the core and + plug-ins. This commit starts removing plug-in code in favor of having + one copy of the code in the core, much like XCF loading and saving is + implemented. + + Add app/file-data/ module with file procedure registering code, for + now just with an implementation of file-gbr-load. + + Remove the file-gbr-load code from the file-gbr plug-in. + + (cherry picked from commit a4e77e57f61473f3cfda2ba1d87a0a5dd1035270) + + app/Makefile.am | 2 + + app/core/gimp.c | 3 + + app/file-data/.gitignore | 7 + + app/file-data/Makefile.am | 20 +++ + app/file-data/file-data-gbr.c | 240 ++++++++++++++++++++++++++ + app/file-data/file-data-gbr.h | 37 +++++ + app/file-data/file-data.c | 118 +++++++++++++ + app/file-data/file-data.h | 26 +++ + configure.ac | 1 + + plug-ins/common/file-gbr.c | 379 + +----------------------------------------- + po/POTFILES.in | 3 + + 11 files changed, 458 insertions(+), 378 deletions(-) + +commit 270bd716582eb265840bb1ec652c9fc3d2cb9158 +Author: Michael Natterer +Date: Mon Feb 11 12:31:18 2019 +0100 + + app: add all missing festures from file-gbr-load to the core brush + loader + + gimp_brush_load_brush(): support legacy gpb brushes, and port a check + for gsize overflow what was added to the plug-in. + + (cherry picked from commit 619f99928045c494b5671af1426569e352008bb9) + + app/core/gimpbrush-load.c | 83 + +++++++++++++++++++++++++++++++++++------------ + 1 file changed, 63 insertions(+), 20 deletions(-) + +commit c05a700ccc893c4d303f48f1b7daa101ca460bb3 +Author: Rodrigo Lledó +Date: Mon Feb 11 07:53:20 2019 +0000 + + Update Spanish translation + + po-python/es.po | 84 + ++++++++++++++++++++++++++++++++++++++++----------------- + 1 file changed, 59 insertions(+), 25 deletions(-) + +commit c000c7a8a2888cdb66676c79b1f6ff12f936eab2 +Author: Rodrigo Lledó +Date: Mon Feb 11 07:51:36 2019 +0000 + + Update Spanish translation + + po-script-fu/es.po | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +commit dfe1dbe0adbc4cd0c98afc8568ff0bd4bd541d21 +Author: Rodrigo Lledó +Date: Mon Feb 11 07:49:19 2019 +0000 + + Update Spanish translation + + po/es.po | 1173 + ++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 615 insertions(+), 558 deletions(-) + +commit 4a7988b9fad948a594d4cec76f80fb1168f5a1be +Author: Ell +Date: Sun Feb 10 17:47:51 2019 -0500 + + Issue #2935 - GIMP 2.10 - options hidden in filter dialogs + + gtk2 doesn't propagate the child requisition to the scrolled-window + requisition, so we have to do it manually. + + app/tools/gimpoperationtool.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit c1e84c494da66710885da7ff59a3a63d549e90a0 +Author: Balázs Úr +Date: Sun Feb 10 17:37:22 2019 +0000 + + Update Hungarian translation + + po-python/hu.po | 439 + +++++++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 372 insertions(+), 67 deletions(-) + +commit 2465aecb4fef81186829e56476af8171080faa1b +Author: Ryuta Fujii +Date: Sun Feb 10 15:46:38 2019 +0000 + + Update Japanese translation + + po/ja.po | 129 + ++++++++++++++++++++------------------------------------------- + 1 file changed, 41 insertions(+), 88 deletions(-) + +commit b7c16642736f3e9758142a63cba18b172c6150de +Author: Ryuta Fujii +Date: Sat Feb 9 13:02:04 2019 +0000 + + Update Japanese translation + + po/ja.po | 177 + ++++++++++++++++++++------------------------------------------- + 1 file changed, 55 insertions(+), 122 deletions(-) + +commit d501a28807c8ed23934404eaed875d431204c264 +Author: Marco Ciampa +Date: Sat Feb 9 01:18:28 2019 +0100 + + Updated Italian translation + + po-plug-ins/it.po | 41 +- + po-script-fu/it.po | 14 +- + po/it.po | 1414 + +++++++++++++++++++++++++++------------------------- + 3 files changed, 753 insertions(+), 716 deletions(-) + +commit d115e1d777194844dd1b0afbb6c58eb8d9e95398 +Author: Piotr Drąg +Date: Fri Feb 8 20:36:16 2019 +0100 + + Update Polish translation + + po/pl.po | 1125 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 598 insertions(+), 527 deletions(-) + +commit 0f727e4f2f48674dcd867e36a975041dace1fb5a +Author: Ell +Date: Fri Feb 8 08:07:05 2019 -0500 + + libgimpwidgets: in GimpChainButton, emit "toggled" signal when + "active" changes + + In GimpChainButton, emit the "toggled" signal whenever the chain + button's "active" property changes, either due to user interaction, + or programatically. Previously, it would only get emitted when the + button was actually clicked. + + In particular, this fixes an issue where the aspect ratio of a + coordinates size-entry won't get updated when its chain button got + toggled programatically, as can happen with the scale tool. + + (cherry picked from commit c0c055b4e975e7524051e6b5b96a6eda0ac30d34) + + libgimpwidgets/gimpchainbutton.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +commit d815f355bb421a73c51b19f8c6d5346afaebb69c +Author: Jehan +Date: Fri Feb 8 12:48:08 2019 +0100 + + Issue #2922: Some sentences appear untranslated. + + Again some missing context when requesting the strings (while + they were + declared with context in static NC_()). + Also some mixup with some zoom actions strings declared with different + context in the same GimpEnumActionEntry. + + (cherry picked from commit 6c5b6c613553abde18556f8470c36654170e288f) + + app/actions/layers-actions.c | 4 ++-- + app/actions/tools-actions.c | 4 ++-- + app/actions/view-actions.c | 14 +++++++------- + 3 files changed, 11 insertions(+), 11 deletions(-) + +commit 95be8d076902f44e60877c6bd3c43e8ed5d26883 +Author: Jehan +Date: Fri Feb 8 13:30:39 2019 +0100 + + po: fix msgctxt s/view-action/view-zoom-action/ for 5 strings. + + Similar to master commit cb025cec6403597c9ec767cba2adc6adcf74219f, + except that I just redid the search-replace as there were annoying + conflict merge (was easier to just redo massive search-replace). + + po/ar.po | 10 +++++----- + po/ca.po | 10 +++++----- + po/ca@valencia.po | 10 +++++----- + po/cs.po | 10 +++++----- + po/da.po | 10 +++++----- + po/de.po | 10 +++++----- + po/el.po | 10 +++++----- + po/en_GB.po | 10 +++++----- + po/eo.po | 10 +++++----- + po/es.po | 10 +++++----- + po/eu.po | 10 +++++----- + po/fi.po | 10 +++++----- + po/fr.po | 10 +++++----- + po/gl.po | 10 +++++----- + po/hr.po | 10 +++++----- + po/hu.po | 10 +++++----- + po/id.po | 10 +++++----- + po/is.po | 10 +++++----- + po/it.po | 10 +++++----- + po/ja.po | 10 +++++----- + po/ko.po | 10 +++++----- + po/lv.po | 10 +++++----- + po/mr.po | 10 +++++----- + po/ne.po | 10 +++++----- + po/nl.po | 10 +++++----- + po/pl.po | 10 +++++----- + po/pt_BR.po | 10 +++++----- + po/ro.po | 10 +++++----- + po/ru.po | 10 +++++----- + po/sl.po | 10 +++++----- + po/sr.po | 10 +++++----- + po/sv.po | 10 +++++----- + po/tr.po | 10 +++++----- + po/uk.po | 10 +++++----- + po/zh_CN.po | 10 +++++----- + po/zh_TW.po | 10 +++++----- + 36 files changed, 180 insertions(+), 180 deletions(-) + +commit 7067c3add38658e070cab0d005033b375171d59b +Author: Jehan +Date: Fri Feb 8 11:40:49 2019 +0100 + + Issue #2922: Some sentences appear untranslated. + + (cherry picked from commit 60c334a77901ba38fb437d809383cad7b6164e3b) + + app/actions/tools-actions.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +commit 7909409a03e6154a6dd14038c001456f26d4b133 +Author: Balázs Meskó +Date: Thu Feb 7 23:33:44 2019 +0000 + + Update Hungarian translation + + po-plug-ins/hu.po | 3670 + ++++++++++------------------------------------------- + 1 file changed, 683 insertions(+), 2987 deletions(-) + +commit 66803bc2b3705830cf2f262fdd42e994aa990008 +Author: Jehan +Date: Thu Feb 7 18:16:01 2019 +0100 + + po: fix the context (msgctx) for 2 strings. + + See commit f8f3a74971. + The context change was basically a bug fix, and nothing changed in the + original string, nor its actual GUI context/usage. Therefore there + is no + need to invalidate the translations (mark it "fuzzy", which would be + what would happen automatically after this change) for the 43 + languages + which already translated these. Let's just search-and-replace all + the po + files with the correct context. + + For the record, I got the green light from several translators on + gnome-i18n ML so let's fix. :-) + + (cherry picked from commit 7a5e5be35e9b20e494767726fc58216c74125a08) + + po/ar.po | 4 ++-- + po/bs.po | 4 ++-- + po/ca.po | 4 ++-- + po/ca@valencia.po | 4 ++-- + po/cs.po | 4 ++-- + po/da.po | 4 ++-- + po/de.po | 4 ++-- + po/el.po | 4 ++-- + po/en_GB.po | 4 ++-- + po/eo.po | 4 ++-- + po/es.po | 4 ++-- + po/eu.po | 4 ++-- + po/fi.po | 4 ++-- + po/fr.po | 4 ++-- + po/gd.po | 4 ++-- + po/gl.po | 4 ++-- + po/hr.po | 4 ++-- + po/hu.po | 4 ++-- + po/id.po | 4 ++-- + po/is.po | 4 ++-- + po/it.po | 4 ++-- + po/ja.po | 4 ++-- + po/kk.po | 4 ++-- + po/ko.po | 4 ++-- + po/lv.po | 4 ++-- + po/mr.po | 4 ++-- + po/ne.po | 4 ++-- + po/nl.po | 4 ++-- + po/nn.po | 4 ++-- + po/pa.po | 4 ++-- + po/pl.po | 4 ++-- + po/pt.po | 4 ++-- + po/pt_BR.po | 4 ++-- + po/ro.po | 4 ++-- + po/ru.po | 4 ++-- + po/sk.po | 4 ++-- + po/sl.po | 4 ++-- + po/sr.po | 4 ++-- + po/sv.po | 4 ++-- + po/tr.po | 4 ++-- + po/uk.po | 4 ++-- + po/zh_CN.po | 4 ++-- + po/zh_TW.po | 4 ++-- + 43 files changed, 86 insertions(+), 86 deletions(-) + +commit 5796cc46e4f690afc72cfb914a7db33180b982e9 +Author: Jehan +Date: Thu Feb 7 17:44:35 2019 +0100 + + app: improve line art bucket fill by filling unsignificant areas. + + The line art imaginary segments/splines are not added when they create + too small zones, unless when these are just too small + ("unsignificant"). + Why the original algorithm keeps such micro-zones is because there may + be such zones created when several splines or segments are leaving + from + a same key point (and we don't necessarily won't to forbid this). Also + we had cases when using very spiky brushes (for the line art) would + create many zones, and such micro-zones would appear just too often + (whereas with very smooth lines, they are much rarer, if not totally + absent most of the time). + Also it is to be noted that the original paper would call these + "unsignificant" indeed, but these are definitely significant for the + artists. Therefore having to "fix" the filling afterwards (with + a brush + for instance) kind of defeat the whole purpose of this tool. + + I already had code which would special-case (fill) 1-pixel zones + in the + end, but bigger micro zones could appear (up to 4 pixels in the + current + code, but this could change). Also I don't want to use the "Remove + Holes" (gimp:flood) operation as I want to make sure I remove only + micro-holes created by the line art closure code (not micro-holes from + original line arts in particular). + + This code takes care of this issue by filling the micro-holes with + imaginary line art pixels, which may later be potentially bucket + filled + when water-filling the line art. + + (cherry picked from commit 72092fbdbc76ed400e9866ffd5d601aea8352e75) + + app/core/gimpdrawable-bucket-fill.c | 56 +++----------------- + app/core/gimplineart.c | 103 + ++++++++++++++++++++++++++++++------ + 2 files changed, 93 insertions(+), 66 deletions(-) + +commit 1825392dbe8b82c15f6dc8e3415b4e58a0e4afe2 +Author: Jehan +Date: Thu Feb 7 15:41:55 2019 +0100 + + Issue #2920: Dilate and Erode filters' tooltips are untranslatable. + + Wrong message contexts! + + (cherry picked from commit f8f3a74971685ef79d78a108568ea50d61444b6e) + + app/actions/filters-actions.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit f5e652d7629dbed3fe74760be08ce8d954a4c421 +Author: Ell +Date: Thu Feb 7 09:12:53 2019 -0500 + + app, pdb: use compat formats for brushes/patterns in plug-ins + + Plug-ins are not prepared to handle high-precision brushes/ + patterns, even when they're otherwise aware of high-precision + drawables, so make sure to always use compat formats when + communicating brush/pattern data to plug-ins. + + Allowing plug-ins to handle high-precision brush/pattern data would + require some additional API. + + (cherry picked from commit 82c449496e55ace64527f21ceb91f63b5bdd1fc9) + + app/gegl/gimp-babl-compat.c | 10 ++++++++++ + app/gegl/gimp-babl-compat.h | 1 + + app/pdb/brush-cmds.c | 44 + +++++++++++++++++++++++++++++++---------- + app/pdb/pattern-cmds.c | 22 +++++++++++++++++---- + app/widgets/gimpbrushselect.c | 15 ++++++++++++-- + app/widgets/gimppatternselect.c | 16 +++++++++++++-- + pdb/groups/brush.pdb | 44 + +++++++++++++++++++++++++++++++---------- + pdb/groups/pattern.pdb | 22 +++++++++++++++++---- + 8 files changed, 142 insertions(+), 32 deletions(-) + +commit 57b81c424bba95407cc4c55f25c7623a5cc58b7d +Author: sabri ünal +Date: Sun Feb 3 18:00:18 2019 +0000 + + 2 sentences are marked as translatable. + + These sentences are in use on "Preferences - Interface - Help + System" page. + + (cherry picked from commit 18537706605e9012903605266a8d00bdefcab934) + + app/config/gimprc-blurbs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 29706cdc48db52b4570d652ff950e97cf4928e46 +Author: Jehan +Date: Tue Feb 5 15:00:44 2019 +0100 + + libgimpbase: style cleanup of gimpreloc.c + + No code change, only indentation fixed and opening braces on + newline, as + per our style guidelines. + + (cherry picked from commit 757b8bba7d8821a586e3a0eedc27183b4af2501f) + + libgimpbase/gimpreloc.c | 580 + +++++++++++++++++++++++++----------------------- + 1 file changed, 299 insertions(+), 281 deletions(-) + +commit 6645cc7ba53baba2a30d044a3b1e3fa500e25d03 +Author: Jehan +Date: Tue Feb 5 14:21:23 2019 +0100 + + app, libgimpbase: --enable-relocatable-bundle replaces + --enable-binreloc + + Older --enable-binreloc configure option had basically the same + purpose + as the newer --enable-relocatable-bundle, though the old binreloc was + only used for gimpenv.c code. + As a consequence, commit 10ce702188 was still not working fine since + gimp_installation_directory_file() also need binreloc enabled (to be + actually relocatable). + + Let's get rid of this whole mess, by implying we want binreloc code to + be used when --enable-relocatable-bundle is ON. We don't need the + m4macros anymore, since AM_BINRELOC was basically just checking that + `/proc/self/maps` was present. But anyway being present at compile + time + does not mean it will be at runtime (nor the opposite). So this + test is + not that useful. The binreloc code will anyway fallback gracefully to + the non-binreloc code (i.e. trying to use build-time install paths) if + the procfs is lacking at runtime. + + (cherry picked from commit 4d84c1d7ee5bdd2d9cf8bfb07455547856ae6b59) + + INSTALL.in | 5 ---- + acinclude.m4 | 1 - + app/Makefile.am | 2 +- + configure.ac | 4 +-- + libgimpbase/Makefile.am | 1 - + libgimpbase/gimpreloc.c | 12 ++++---- + m4macros/binreloc.m4 | 75 + ------------------------------------------------- + 7 files changed, 8 insertions(+), 92 deletions(-) + +commit a335eb0e6e29fed5fb777a5d56c366ea8064d887 +Author: Ell +Date: Wed Feb 6 16:47:13 2019 -0500 + + app: avoid unnecessary calls to gimp_temp_buf_data_clear() + + Avoid unnecessary calls to gimp_temp_buf_data_clear() in various + places, where either the entire buffer is being written to, or most + of it is, only requiring clearing the edges. + + (cherry picked from commit 9d19e804edea2e6d116f220868e8a5b836cc5ab2) + + app/core/gimpbrush.c | 1 - + app/paint/gimpbrushcore-loops.cc | 77 + ++++++++++++++++++++++++++++++++-------- + 2 files changed, 63 insertions(+), 15 deletions(-) + +commit 1e0530e7309e48af0df9f1423a2611c2d6cb6835 +Author: Ell +Date: Wed Feb 6 15:12:26 2019 -0500 + + app: small fix in gimpbrushcore-kernels.h + + (cherry picked from commit cbe40000f58c9b25d081ce2144dd13a51c2fc488) + + app/paint/gimpbrushcore-kernels.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d39634a91432bdc7a6e7a96b6bdaa4b8efaecdd1 +Author: Ell +Date: Wed Feb 6 14:15:47 2019 -0500 + + Issue #2372 - Reduced quality of the Parametric brush in 2.10 + + Promote the precision of generated brushes to 32-bit float, and + modify brush preview generation, and gimpbrushcore-loops, to handle + float brushes. This avoids posterization in large brushes. + + Note that non-generated brushes are still uint8. + + (cherry picked from commit 8ef1113dee378e66182e13dcf924b32b7dd816c4) + + app/core/gimpbrush.c | 15 +- + app/core/gimpbrushgenerated.c | 24 +- + app/paint/gimpbrushcore-kernels.h | 126 ++++++--- + app/paint/gimpbrushcore-loops.cc | 554 + +++++++++++++++++++++++++------------- + 4 files changed, 481 insertions(+), 238 deletions(-) + +commit 8f31f8fa3c4c0c0610ac721edd5861225bc3a3fe +Author: Ell +Date: Wed Feb 6 14:10:05 2019 -0500 + + app: add gimp_temp_buf_{lock,unlock}() + + In GimpTempBuf, add gimp_temp_buf_lock() and gimp_temp_buf_unlock() + functions, which lock/unlock the buffer for data access. Unlike + gimp_temp_buf_get_data(), which returns a direct pointer to the + buffer's data, the new functions take a format parameter and may + return a temporary buffer, allowing the buffer to be accessed using + an arbitrary format. + + (cherry picked from commit 12dde445c4f51c2e45376d934c37cedff3a40e40) + + app/core/gimptempbuf.c | 80 + ++++++++++++++++++++++++++++++++++++++++++++++++++ + app/core/gimptempbuf.h | 6 ++++ + 2 files changed, 86 insertions(+) + +commit d4e87e4d51aa507741022c4e2ea3e3e39fdd6072 +Author: Ryuta Fujii +Date: Wed Feb 6 13:36:59 2019 +0000 + + Update Japanese translation + + po/ja.po | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit 99c59d92f363ba71fdbdd0f81fa520ea281393a7 +Author: Ryuta Fujii +Date: Wed Feb 6 13:07:58 2019 +0000 + + Update Japanese translation + + po/ja.po | 471 + ++++++++++++++++++++++++++++++--------------------------------- + 1 file changed, 223 insertions(+), 248 deletions(-) + +commit c9f47356699c140c926ea1fa4c5fa9a1b79a3d69 +Author: Ell +Date: Tue Feb 5 16:58:24 2019 -0500 + + libgimp: avoid libgimp tile-cache in the plug-in tile backend + + In GimpTileBackendPlugin, avoid storing read/written tiles in the + libgimp tile-cache, since caching is already done by GEGL. + + (cherry picked from commit 47dcd4b93a82750435fae70004d39eeb069fb0ac) + + libgimp/gimptile.c | 17 +++++++++++++---- + libgimp/gimptile.h | 3 ++- + libgimp/gimptilebackendplugin.c | 6 ++---- + 3 files changed, 17 insertions(+), 9 deletions(-) + +commit 17f355e9f339e422429a5a24414384c1287edcee +Author: Rodrigo Lledó +Date: Tue Feb 5 14:22:37 2019 +0000 + + Update Spanish translation + + po-script-fu/es.po | 71 + +++++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 51 insertions(+), 20 deletions(-) + +commit f936089bcb4b5ac7a161ccc90bafc79c4ab511e5 +Author: Rodrigo Lledó +Date: Tue Feb 5 14:21:34 2019 +0000 + + Update Spanish translation + + po-python/es.po | 1272 + ++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 793 insertions(+), 479 deletions(-) + +commit 8878ff5f04f7e94788da1097b2ce6854746c54e1 +Author: Ell +Date: Tue Feb 5 04:38:00 2019 -0500 + + app: in GimpRotateTool, improve fuzzy comparisons + + (cherry picked from commit b002f20412bd3c0c8c84ddb75f30141f8f91caa8) + + app/tools/gimprotatetool.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +commit a7716624203d0018cc677b06cfd094e58b9dc5f6 +Author: Ell +Date: Tue Feb 5 03:47:49 2019 -0500 + + libgimpwidgets: add gimp_chain_button_{get,size}_icon_size() + to gimpwidgets.def + + (cherry picked from commit 07145108020948b53b0a58759d0bfd966552fc03) + + libgimpwidgets/gimpwidgets.def | 2 ++ + 1 file changed, 2 insertions(+) + +commit b5caa2ea2ec540484e4022da9fa17bb62bdfc1ec +Author: Ell +Date: Tue Feb 5 02:48:50 2019 -0500 + + po-python: add spyro_plus.py copy to POTFILES.skip + + (cherry picked from commit 1b2ec2961cee1e59fd058bdcd77e23d93e6a27c8) + + po-python/POTFILES.skip | 1 + + 1 file changed, 1 insertion(+) + +commit d368fabf8e8a864808d85b2a2361d6adf1f636bb +Author: Balázs Meskó +Date: Mon Feb 4 22:55:15 2019 +0000 + + Update Hungarian translation + + po-script-fu/hu.po | 297 + ++++++++++++++++++++++++++++------------------------- + 1 file changed, 157 insertions(+), 140 deletions(-) + +commit da6086636b3e9e3d70afaff77653216543a9bcfa +Author: Ell +Date: Mon Feb 4 15:33:44 2019 -0500 + + app: in GimpTransformGridTool, allow linking forward/backward + transforms + + Add a GimpTransformGridTool::matrix_to_info() virtual function, + which should extract the tool-specific transformation parameters + given a transformation matrix, and the old parameter set (which is + needed in some tools, to derive the parameters that aren't encoded + in the matrix, such as the pivot point). The transformation matrix + can be any combination of matrices calculated by the tool, and + their inverses. Subclasses should only implement this function if + every such matrix can be mapped back to transformation parameters. + This is currently the case for all the transform-grid tools, except + for the shear tool (since it only supports shearing along one of + the horizontal or the vertical directions, however, the combined + matrix may require shearing in both directions). + + When a transform-grid tool implements this function, show a chain- + button between the two transform-direction radio-buttons in the + tool options. When the chain-button is linked, whenever the + transform corresponding to the active direction is modified, adjust + the transform corresponding to the non-active direction such that + the overall transform remains the same. + + One notable workflow that this enables is transforming a layer + while adjusting a different area than its boundary, by first + defining the area while the transform-directions are linked, and + then transforming the area while the transform-directions are + unlinked. + + (cherry picked from commit 39e23267f7ac76030f02e5f68bf7994a02bb81a1) + + app/tools/gimphandletransformtool.c | 29 ++++++++++++ + app/tools/gimpperspectivetool.c | 31 ++++++++++++ + app/tools/gimprotatetool.c | 44 +++++++++++++++++ + app/tools/gimpscaletool.c | 26 ++++++++++ + app/tools/gimptransformgridoptions.c | 66 ++++++++++++++++++++++---- + app/tools/gimptransformgridoptions.h | 1 + + app/tools/gimptransformgridtool.c | 92 + +++++++++++++++++++++++++++++++----- + app/tools/gimptransformgridtool.h | 4 ++ + app/tools/gimptransformoptions.c | 2 + + app/tools/gimptransformoptions.h | 1 + + app/tools/gimpunifiedtransformtool.c | 42 ++++++++++++++++ + 11 files changed, 319 insertions(+), 19 deletions(-) + +commit ca0699082826bb5b1ef8e86b0a2074ffca9c2d22 +Author: Ell +Date: Mon Feb 4 10:30:18 2019 -0500 + + app: in GimpTransformGridTool, allow simultaneous forward and + backward transforms + + In GimpTransformGridTool, allow performing simultaneous forward + (normal) and backward (corrective) transforms, by having each + transform direction operate on an independent set of parameters. + In other words, whereas the transform-grid tools previously had a + single transform, which could be applied either normally or + correctively using the "direction" tool-option, they now have two + independent transforms, one applied normally and the other + applied correctively, which are toggled using the "direction" + option. The overall transform is the combination of the backward + transform, followed by the forward transform. + + Another way to think about it, is that the tool transforms a source + shape into a destination shape. The source shape is defined by the + backward transform, and the destination shape is defined by the + forward transform. Wherewas previously only one of these shapes + could be controlled (the other shape always being the item bounds), + it's now possible to control both shapes in a single transform. + The next commit will allow modifying both shapes simultaneously, + making this even more useful. + + Note that since both transforms start off as the identity, using + only one of the transform directions has the same behavior as + before. + + (cherry picked from commit de8e81f81f356a704aba345e87c460e542570155) + + app/tools/gimpgenerictransformtool.c | 52 +++--- + app/tools/gimpgenerictransformtool.h | 2 +- + app/tools/gimphandletransformtool.c | 31 ++-- + app/tools/gimpperspectivetool.c | 29 ++-- + app/tools/gimprotatetool.c | 37 ++-- + app/tools/gimpscaletool.c | 46 ++--- + app/tools/gimpsheartool.c | 36 ++-- + app/tools/gimptransformgridtool.c | 315 + ++++++++++++++++++++++++---------- + app/tools/gimptransformgridtool.h | 20 ++- + app/tools/gimptransformgridtoolundo.c | 24 +-- + app/tools/gimptransformgridtoolundo.h | 2 +- + app/tools/gimptransformtool.c | 52 ++++-- + app/tools/gimptransformtool.h | 21 +-- + app/tools/gimpunifiedtransformtool.c | 33 ++-- + 14 files changed, 434 insertions(+), 266 deletions(-) + +commit b869f1bf5ae8c8ee28f0eb29da1631b9fbe8374b +Author: Ell +Date: Mon Feb 4 12:52:49 2019 -0500 + + libgimpwidgets: add GimpChainButton::active property + + ... which corresponds to the button's toggle state. + + (cherry picked from commit 9c3f150e25e8150862b1e63e82209ebaf73bb439) + + libgimpwidgets/gimpchainbutton.c | 28 +++++++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +commit fcfe6997cd86503904c739fbb2df9dcf89e884cb +Author: Ell +Date: Mon Feb 4 11:57:28 2019 -0500 + + libgimpwidgets: add GimpChainButton::icon-size property + + ... and corresponding gimp_chain_button_{get,size}_icon_size() + functions, which control the chain-button's icon size. + + (cherry picked from commit 8d6c7e42bf254d9334f298fb42aee2b9b1c8d6a2) + + libgimpwidgets/gimpchainbutton.c | 73 + ++++++++++++++++++++++++++++++++++++++-- + libgimpwidgets/gimpchainbutton.h | 14 +++++--- + 2 files changed, 80 insertions(+), 7 deletions(-) + +commit 3ad945c3c89c3624d19f20312dc9da9e2a9ca047 +Author: Ell +Date: Mon Feb 4 10:21:31 2019 -0500 + + app: add GimpTransformToolClass::undo_desc field + + Add an undo_desc field to GimpTransformToolClass, which subclasses + should set to the tool's default undo description. Provide a + default implementation for the get_undo_desc() vfunc, which returns + (a copy of) undo_desc. This simplifies transform tools that have a + static undo descrption, as well as provides a fallback when a + detailed undo description can't be generated (not currently + relevant, but will be used in the next commit). + + (cherry picked from commit d54944065031d3f0850d13bb9e1e38978f1a3981) + + app/tools/gimpfliptool.c | 3 ++- + app/tools/gimphandletransformtool.c | 11 +---------- + app/tools/gimpmeasuretool.c | 1 + + app/tools/gimpperspectivetool.c | 11 +---------- + app/tools/gimprotatetool.c | 1 + + app/tools/gimpscaletool.c | 1 + + app/tools/gimpsheartool.c | 1 + + app/tools/gimptransformtool.c | 26 +++++++++++++++++--------- + app/tools/gimptransformtool.h | 1 + + app/tools/gimpunifiedtransformtool.c | 11 +---------- + 10 files changed, 27 insertions(+), 40 deletions(-) + +commit b0ab2b65644b4b086ecd5e24c4a55b9b7a5f5df8 +Author: Ell +Date: Mon Feb 4 09:52:53 2019 -0500 + + app: improve rotate-tool undo description + + When rotating an item around its center using the rotate tool, + i.e., if the pivot point hasn't been moved, don't include the pivot + coordinates in the undo description. + + (cherry picked from commit 44c8a1f27494c6717bda6664f53e81b7b47090d5) + + app/tools/gimprotatetool.c | 20 +++++++++++++++----- + 1 file changed, 15 insertions(+), 5 deletions(-) + +commit 054d441a8dceeb20fed44e5e197c4f9af9752488 +Author: Ell +Date: Mon Feb 4 09:43:54 2019 -0500 + + app: improve measure-tool undo description + + When straightening an item using the measure-tool, include the + orientation and angle in the undo description. + + (cherry picked from commit b95bf3fb9339522b204b13449f7301ee5a51cce9) + + app/tools/gimpmeasuretool.c | 31 ++++++++++++++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +commit b820d508274a3443ed7ea56f7bded94476da2aaa +Author: Ell +Date: Mon Feb 4 09:40:15 2019 -0500 + + app: add GimpToolCompass::effective-orientation property + + In GimpToolCompass, add a read-only "effective-orientation" + property, which returns the actual orientation of the compass; in + particular, if the "orientation" property is set to AUTO, + "effective-orientation" returns HORIZONTAL or VERTICAL, depending + on the current compass direction. In 3-point mode, the property + always returns AUTO. + + (cherry picked from commit 40c091317865feac875dd935b2496f4eadcfb8bc) + + app/display/gimptoolcompass.c | 52 + ++++++++++++++++++++++++++++++++----------- + 1 file changed, 39 insertions(+), 13 deletions(-) + +commit 6495b6d02338e6b1ff38af27178619d34c2b7db4 +Author: Ell +Date: Mon Feb 4 09:36:35 2019 -0500 + + app: fix CRITICALs in GimpMeasureTool + + In GimpMeasureTool, don't try to access the "straighten" button + when halting the tool if it's NULL, which can happen when the + measure tool is selected upon startup, but is changed before its + tool-options GUI is constructed. + + (cherry picked from commit 7fc5698f32a144336d6732eabb1a0cdc7be8f757) + + app/tools/gimpmeasuretool.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +commit d6bb1efd77c2e18a0cacdf5b8f51b4ccf901fd09 +Author: Ell +Date: Mon Feb 4 04:58:34 2019 -0500 + + app: in gimp_transform_matrix_generic(), apply matrix even if invalid + + In gimp_transform_matrix_generic(), apply the resulting matrix even + if the transformation is invalid, since GimpGenericTransformTool + relies on the matrix to properly update the transform-grid widget. + + (cherry picked from commit 59ef222c7fc549fe0f34ae74413bc2548ee11356) + + app/core/gimp-transform-utils.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +commit d74df147661358f6da34fd0ccfd9a1cf438f57db +Author: Ell +Date: Mon Feb 4 04:43:52 2019 -0500 + + app: in GimpTransformGridTool, fix layer hiding + + In GimpTransformGridTool, when the "show-preview" tool-option + changes, don't take the transform validity into account when + deciding whether to hide the current layer -- it should only affect + the visibility of the preview, not the layer. + + (cherry picked from commit 7ed512040ef2063ca6cf8997e2656311e8121b4c) + + app/tools/gimptransformgridtool.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 08c540ebc149a60d99f33763fc9068dd98e4ba37 +Author: Ell +Date: Mon Feb 4 02:55:54 2019 -0500 + + app: in GimpTransformGridTool, remove flip-tool hack + + The flip tool is not a subclass of GimpTransformGridTool; this is + a leftover from GimpTransformTool. + + (cherry picked from commit 3ebda874b5f02842e3f8b7e2845492fb5e3e1477) + + app/tools/gimptransformgridtool.c | 7 ------- + 1 file changed, 7 deletions(-) + +commit 32c185c7eea6296b027946cbeac5ec9ec0a4f0b4 +Author: Jehan +Date: Mon Feb 4 22:18:12 2019 +0100 + + Issue #2906: Icon Theme bar strings have no translations. + + app/widgets/gimpiconsizescale.c | 46 + ++++++++++++++++++++++++++--------------- + 1 file changed, 29 insertions(+), 17 deletions(-) + +commit dc431bbcc1e7ea226f9d884ab3dfd43f9066ffa0 +Author: Rodrigo Lledó +Date: Mon Feb 4 10:42:45 2019 +0000 + + Update Spanish translation + + po-plug-ins/es.po | 1327 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 730 insertions(+), 597 deletions(-) + +commit 79454e2bf36da42e7f3977fd10cf9c69f3ba0872 +Author: Michael Natterer +Date: Mon Feb 4 10:38:43 2019 +0100 + + app: in the prefs dialog, warn that OpenCL support is experimental + + (cherry picked from commit 7998a27b8b0fe993b4f82c3fe2fc747ece75f6f8) + + app/dialogs/preferences-dialog.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit 6be6ea0e92f6c66f6db7ef48d7f4330cb608e860 +Author: Michael Natterer +Date: Sun Feb 3 18:06:01 2019 +0100 + + Issue #2898 - Gegl Operations - Color Wrap does not fit the screen + on... + + ...1920*1980 resolution + + In GimpOperationTool, make the generated GUI scrollable if it is + higher than half the monitor's workarea. This is meant as a last + resort for generated GUIs that do not have a custom constructor that + makes them usable using better layouts. + + (cherry picked from commit b85d7c233487006a381ff678389431d51e2618aa) + + app/tools/gimpoperationtool.c | 55 + +++++++++++++++++++++++++++++++++++++------ + app/tools/gimpoperationtool.h | 1 + + 2 files changed, 49 insertions(+), 7 deletions(-) + +commit b9a0bd4d62a704eb28e94c69e49a21851bf90320 +Author: Michael Natterer +Date: Sun Feb 3 14:50:48 2019 +0100 + + libgimpbase: add gimp_installation_directory_file() to gimpbase.def + + (cherry picked from commit c71a887b08ea1eb00965ba514c5557a4d135a567) + + libgimpbase/gimpbase.def | 1 + + 1 file changed, 1 insertion(+) + +commit 53de34230f4845b7fbbd11327cc81966727b48da +Author: Jehan +Date: Sat Feb 2 18:47:58 2019 +0100 + + libgimpbase: add gimp_installation_directory_file(). + + Fix previous commit. + Ahahah! Mitch, you didn't test your code before pushing! Bad boy! :P + + (cherry picked from commit db00616888856dce1558fa5f68c77807cf27b3b9) + + (Bad Jehan didn't pick this to 2.10) + + libgimpbase/gimpenv.c | 30 +++++++++++++++++++++++++ + libgimpbase/gimpenv.h | 62 + ++++++++++++++++++++++++++------------------------- + 2 files changed, 62 insertions(+), 30 deletions(-) + +commit a8f58acb9a54fa7db08ede0256b1b2e5266fa8be +Author: Michael Natterer +Date: Sat Feb 2 17:07:03 2019 +0100 + + Issue #2867 - iso-codes location is not relocatable on all platforms + + gimplanguagestore-parser.c (parse_iso_codes): instead of + special-casing Windows and OS X, use ENABLE_RELOCATABLE_RESOURCES and + find the package relative to ${gimp_installation_directory}, so + relocating it works on all platforms (also flatpack, snap, whatever), + given the --enable-relocatable-bundle configure switch is used. + + (cherry picked from commit 10ce702188caa50a05c049d28bb9140832eb3949) + + app/widgets/gimplanguagestore-parser.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +commit 48dba7a0e189a1123304b3bcdb229bc7454c9967 +Author: Piotr Drąg +Date: Sat Feb 2 14:44:24 2019 +0100 + + Update Polish translation + + po-python/pl.po | 385 + ++++++++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 360 insertions(+), 25 deletions(-) + +commit eeeab8b209512ab32b02bdc915dd13d099be99b8 +Author: Elad Shahar +Date: Fri Feb 1 00:05:45 2019 +0200 + + Add i18n support for python spyrogimp plugin + + (cherry picked from commit f6e414575628189a51582f34284bd4356b659f85) + + plug-ins/pygimp/plug-ins/spyro_plus.py | 178 + ++++++++++++++++++--------------- + po-python/POTFILES.in | 1 + + 2 files changed, 96 insertions(+), 83 deletions(-) + +commit d2bc3eeb5da57b2a3957083a78511510bcd53525 +Author: Ell +Date: Fri Feb 1 07:30:18 2019 -0500 + + plug-ins: in pygimp/plug-ins/Makefile.am, simplify plug-in copy recipe + + (cherry picked from commit 12dbd4aee2dea284583bc3b2dd30da62e7513056) + + plug-ins/pygimp/plug-ins/Makefile.am | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +commit 5f398c0888ea56b448ea8b0c416f8c8e673929db +Author: Ell +Date: Fri Feb 1 04:27:33 2019 -0500 + + plug-ins: in pygimp/plug-ins/Makefile.am, re-copy plug-ins upon change + + In pygimp/plug-ins/Makefile.am, fix the rule for the generated + plug-in files, so that they're re-copied to the build dir upon + changes to the source plug-in files. + + (cherry picked from commit 95044d86b3a30295ff315a449e1f33f7ae9a208d) + + plug-ins/pygimp/plug-ins/Makefile.am | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit 301691b3ed977c4f045e8ee5408bbfe70d6c0ba2 +Author: Ell +Date: Fri Feb 1 04:23:25 2019 -0500 + + plug-ins: in pygimp/plug-ins/Makesfile.am, use tab-width of 8 + + ... like the rest of our makefiles. + + (cherry picked from commit bcd9f55526890e8da2ff17b76021d5ffb1b5d229) + + plug-ins/pygimp/plug-ins/Makefile.am | 56 + ++++++++++++++++++------------------ + 1 file changed, 28 insertions(+), 28 deletions(-) + +commit 377cae8ecc582700cfdcf2454a64d4fcac47e1e8 +Author: Ryuta Fujii +Date: Thu Jan 31 16:09:03 2019 +0000 + + Update Japanese translation + + po/ja.po | 117 + ++++++++++++++++++++------------------------------------------- + 1 file changed, 36 insertions(+), 81 deletions(-) + +commit 297971e4478b0434ab20672c5f50f64c6e097cc6 +Author: Ell +Date: Thu Jan 31 09:06:22 2019 -0500 + + app: fix CRITICAL when initializing the perspective-clone tool + + When initializaing the perspective-clone tool in paint mode, the + GimpDrawTool may already be active, causing the call to + gimp_draw_tool_start() to fail with a CRITICAL. Stop the draw tool + first, if active, to avoid that. + + (cherry picked from commit 07d2d5af5a0fb11ba544fddedba0481e2a91651f) + + app/tools/gimpperspectiveclonetool.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 5e6378a7a3fb4575efacd3a16a0b165de133296a +Author: Ell +Date: Thu Jan 31 06:18:17 2019 -0500 + + app: in perspective-clone tool, fix cursor presicion; hide brush + while adjusting + + In GimpPerspectiveCloneTool, use PIXEL_CENTER cursor precision + while setting the source, and place the source-position handle at + the center of the selected pixel, rather than at its top-left + corner. + + Additionally, disable the paint tool while adjusting the + perspective, so that the brush outline isn't shown, and the cursor + precision remains SUBPIXEL, even if the hard-edge option is + toggled. + + (cherry picked from commit e5c9314a88aa20331027537b350a8c4f37a35da4) + + app/tools/gimpperspectiveclonetool.c | 69 + +++++++++++++++++++++++++++++++++--- + app/tools/gimpperspectiveclonetool.h | 24 +++++++------ + 2 files changed, 77 insertions(+), 16 deletions(-) + +commit 5c3532609ff8235217d06ee23fbf62b9ad865cae +Author: Ell +Date: Thu Jan 31 06:15:53 2019 -0500 + + app: add gimp_paint_tool_set_active() + + ... which can use to enable/disable certain aspects of + GimpPaintTool (in particular, brush-outline drawing). Should be + used by subclasses, to temporarily disable the paint tool while in + a non-paint mode (currently, this is only needed by the + perspective-clone tool; see next commit.) + + (cherry picked from commit 05dd5029ee368d4bf6115822c0b68638d536f2cc) + + app/tools/gimppainttool.c | 50 + +++++++++++++++++++++++++++++++++++++---------- + app/tools/gimppainttool.h | 4 ++++ + 2 files changed, 44 insertions(+), 10 deletions(-) + +commit 028b0f39ddbedf743e07d5129a797156bdc01286 +Author: Ell +Date: Thu Jan 31 05:36:26 2019 -0500 + + app: fix cursor precision of source tools + + In GimpCloneTool, set the cursor precision to PIXEL_CENTER while + setting a source, and have GimpBrushTool snap the brush outline to + pixel centers. + + (cherry picked from commit ef2818231f43a39b0e2e118a71fa2645a773fa2f) + + app/tools/gimpbrushtool.c | 9 +++++---- + app/tools/gimpsourcetool.c | 8 ++++++++ + app/tools/gimpsourcetool.h | 21 +++++++++++---------- + 3 files changed, 24 insertions(+), 14 deletions(-) + +commit b1c977e5100b2f211c9fa78e327bccd6162bb9aa +Author: Ell +Date: Thu Jan 31 04:37:56 2019 -0500 + + app: fix cursor precision of various tools + + Fix the cursor precision of the cage-transform, foreground-select, + n-point deformation, and warp-transform tools. + + (cherry picked from commit 158705e4ef7be28bff83f51df48985b8f4ea6718) + + app/tools/gimpcagetool.c | 2 ++ + app/tools/gimpforegroundselecttool.c | 13 ++++++++++++- + app/tools/gimpnpointdeformationtool.c | 2 ++ + app/tools/gimpwarptool.c | 2 ++ + 4 files changed, 18 insertions(+), 1 deletion(-) + +commit b81723042e7e5c7f977b696e829a335b2a6467ae +Author: Michael Natterer +Date: Wed Jan 30 18:47:51 2019 +0100 + + plug-ins: add .gitignore file to file-dds + + (cherry picked from commit 0fa98d484916ec6df5dc9773d3f1a7040f1b39fe) + + plug-ins/file-dds/.gitignore | 7 +++++++ + 1 file changed, 7 insertions(+) + +commit 6f8c8ce56de328cba3441d1fe5dc68e201134933 +Author: Ryuta Fujii +Date: Wed Jan 30 14:28:48 2019 +0000 + + Update Japanese translation + + po/ja.po | 87 + ++++++++++++++++++++-------------------------------------------- + 1 file changed, 27 insertions(+), 60 deletions(-) + +commit 61d2e8ad7489555c317603ff90a40341b3ceed28 +Author: Rodrigo Lledó +Date: Wed Jan 30 07:44:44 2019 +0000 + + Update Spanish translation + + po/es.po | 470 + ++++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 271 insertions(+), 199 deletions(-) + +commit 94d176d7f5c817675f4fe59c3fd1417a56bb72a9 +Author: Ryuta Fujii +Date: Mon Jan 28 17:27:09 2019 +0000 + + Update Japanese translation + + po/ja.po | 64 + ++++++++++++++++++++++++---------------------------------------- + 1 file changed, 24 insertions(+), 40 deletions(-) + +commit 90d76267a7034972ffc9a89b29901a6ac8e560b0 +Author: Alan Mortensen +Date: Mon Jan 28 13:12:49 2019 +0100 + + Updated Danish translation of gimp-plug-ins + + po-plug-ins/da.po | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 0d950bd289c3c5056bfcafe0d9a3bdcf1b00c08c +Author: Ryuta Fujii +Date: Sun Jan 27 15:24:58 2019 +0000 + + Update Japanese translation + + po/ja.po | 64 + +++++++++++++++++++++++++--------------------------------------- + 1 file changed, 25 insertions(+), 39 deletions(-) + +commit 17e360e34b61bb5fea59c29e78bc167c28f0d137 +Author: Michael Natterer +Date: Sun Jan 27 13:24:06 2019 +0100 + + Issue #2863 - Improve error reporting for scripts + + gimp_plug_in_handle_proc_install(): print the procedure name when + bailing out of a wrong proc install call. For an obsolete full-path + menu label, also print the label. Original patch by Liam Quin. + + (cherry picked from commit 07e3c1c15bf120fb42fe88fdf90d1122b19d714e) + + In libgimp, add a note to gimp_install_procedure() stating that + passing a full menu path as "menu_label" is deprecated. + + app/plug-in/gimpplugin-message.c | 25 +++++++++++++++++++++---- + libgimp/gimp.c | 3 +++ + 2 files changed, 24 insertions(+), 4 deletions(-) + +commit 316c47da6f1cd9720f44ed68e4eb3f4e9f5358ed +Author: Balázs Meskó +Date: Sat Jan 26 21:37:53 2019 +0000 + + Update Hungarian translation + + po/hu.po | 1822 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 979 insertions(+), 843 deletions(-) + +commit b70a37a33df95738f834ebaf1551fa53bf0fb106 +Author: Balázs Meskó +Date: Sat Jan 26 21:32:21 2019 +0000 + + Update Hungarian translation + + po-libgimp/hu.po | 434 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 219 insertions(+), 215 deletions(-) + +commit d11d094d020a3b255f748cbbd753e03e4a6bdf4d +Author: Ell +Date: Sat Jan 26 15:55:52 2019 -0500 + + Issue #1119 - Unable to give a layer group as a parameter to a + python script + + Pickle gimp.GroupLayer the same way we pickle gimp.Layer, so that + layer-group parameters can be properly saved. + + All credit goes to Ofnuts :) + + (cherry picked from commit b295a33aafe122f318e591546c1a2a9217fd19af) + + plug-ins/pygimp/gimpshelf.py | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +commit 25d39eea9db55691e6cd846afb7f33c22f746b8a +Author: Ryuta Fujii +Date: Sat Jan 26 16:17:08 2019 +0000 + + Update Japanese translation + + po/ja.po | 96 + ++++++++++++++++++---------------------------------------------- + 1 file changed, 27 insertions(+), 69 deletions(-) + +commit 69712634a134f720f7a49c4fe445da17a9bea7b3 +Author: Jehan +Date: Sat Jan 26 14:47:56 2019 +0100 + + plug-ins: optionally define several float.h macros for MinGW64. + + This plug-in failed to cross-build because these macros were not + defined + in the `float.h` of my MinGW64 environment (and they are used in some + ilmbase headers). Just define them ourselves if they are absent. I do + this only on MinGW environment because these should really be + defined on + Linux (and other UNIX-like, I guess?) and if they are not, we may + have a + bigger issue. + + (cherry picked from commit 7a7ecda4f170b1fead91bcc53d014d47c563513e) + + plug-ins/file-exr/openexr-wrapper.cc | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +commit dc01a613ffa344ce62f08e19c6f5fd42b260cc89 +Author: Piotr Drąg +Date: Sat Jan 26 11:24:51 2019 +0100 + + Update Polish translation + + po-script-fu/pl.po | 21 ++-- + po/pl.po | 296 + ++++++++++++++++++++++++++--------------------------- + 2 files changed, 156 insertions(+), 161 deletions(-) + +commit d79916f598ec7cd2c1957f91b648d0e7aedc5ed6 +Author: Jehan +Date: Fri Jan 25 21:45:17 2019 +0100 + + NEWS: keep up-to-date. + + NEWS | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +commit aaf581669598b0173a90d514b73186cf725b5cd2 +Author: Salamandar +Date: Fri Jan 25 00:05:09 2019 +0000 + + Undo some (unnecessary) changes. + + (cherry picked from commit fc657184a09eb52c0851c99092112170007871b3) + + app/config/Makefile.am | 2 +- + plug-ins/help/Makefile.am | 2 +- + tools/Makefile.am | 2 +- + tools/gimptool.c | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +commit 0436dd1cb768d6f38f952eb1ddc037567fbf54c2 +Author: Félix Piédallu +Date: Tue Jan 30 17:27:43 2018 +0100 + + (source modifs) Fix: Rename macros as it conflicts with Mingw headers. + * DATADIR -> GIMPDATADIR + * SYSCONFDIR -> GIMPSYSCONFDIR + * DATADIR -> SYSDATADIR (tools/) + + (cherry picked from commit fc8303dd0a0efab10409303519c5605437dff14e) + + app/config/Makefile.am | 2 +- + libgimpbase/Makefile.am | 4 ++-- + libgimpbase/gimpenv.c | 12 +++--------- + libgimpbase/gimputils.c | 10 ++-------- + plug-ins/help/Makefile.am | 2 +- + tools/Makefile.am | 2 +- + tools/gimptool.c | 2 +- + 7 files changed, 11 insertions(+), 23 deletions(-) + +commit ffe2fb478ca2bfb7adf0d5c3d556221db18cd8f9 +Author: Jehan +Date: Fri Jan 25 18:21:05 2019 +0100 + + app: new gimp_spin_scale_set_constrain_drag() and use it on paint... + + ... tools' brush options. + After discussions, it turned out that many people disliked that + the spin + scale for brush size (and some other options) get you fractional + values. + How often do you actually need to get a 4.32 pixel-size brush? And + even + how meaningful is it? On the other hand, you usually want a 4 or a 5 + pixel size brush and it's nearly impossible to get (exactly) + by dragging + the scale widget. + It is so annoying that some even resort to edit the value with + keyboard! + So I am adding an optional "constrain" feature to GimpSpinScale. It + will + still be possible to get fractional values when constraining is + on, for + instance with keyboard edit (the arrow incrementation also will + keep any + fractional part). So the interaction for such scales is simply + reversed + so that you get integers easily, and fractional parts with a bit more + effort. + + It is not turned on by default (some feature actually need precision + and + we don't want to break the sliders for these) and for the time + being, I + only applied it to all the brush settings in paint tools. Now that it + exist, we may want to apply this to more scales in various parts of + GIMP. + + (cherry picked from commit bff3903f377ea26471c00bff132c5dbb99b04c29) + + app/tools/gimppaintoptions-gui.c | 2 ++ + app/widgets/gimpspinscale.c | 37 + +++++++++++++++++++++++++++++++++++++ + app/widgets/gimpspinscale.h | 3 +++ + 3 files changed, 42 insertions(+) + +commit 699933470ad454b9694dc74ac0963e783462d555 +Author: Jehan +Date: Fri Jan 25 01:07:39 2019 +0100 + + libgimpbase: display thread id of the calling thread as hexadecimal... + + ... on macOS. + The debugger running on macOS is usually lldb and (from the reports we + get) it looks like lldb displays the tid as hexadecimal on macOS + (whereas lldb displays decimal tid on Linux! I know, it's confusing, + yet + consistent with crash report experience!). So let's just do the same, + making it easy to quickly copy-search in order to look up the crashing + thread (without having to convert from decimal to hexa). + This is a bit of an approximation as I imagine we could have gdb on + macOS or whatever edge case. Let's say it's good for the common + case and + still not an error otherwise (just a base conversion away). + + (cherry picked from commit 8e0135362e4da74e171ebb2515b1f472fc61ba35) + + libgimpbase/gimputils.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +commit 5a0e4554be35511ff4dee96f721de4a6096b7ee0 +Author: Tim Sabsch +Date: Fri Jan 25 10:26:01 2019 +0000 + + Update German translation + + po-plug-ins/de.po | 1666 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 902 insertions(+), 764 deletions(-) + +commit 71fe5c00fd9949dce4bc94da22439012e9f42210 +Author: Ell +Date: Fri Jan 25 04:26:08 2019 -0500 + + plug-ins: avoid running pygimp plug-ins derived from gimpplugin.plugin + at each startup + + When initializing a pygimp plug-in derived from gimpplugin.plugin, + only pass the plug-in's init() and quit() functions to gimp.main() + if the plug-in actually implements them, instead of passing the + default NOP versions. This avoids plug-ins that don't implement + init() from being registered as having an init function, causing + them to be run at each startup. + + (cherry picked from commit 9851bc89621ba39f561561359bdf54ff391c4617) + + plug-ins/pygimp/gimpplugin.py | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +commit 9c6c064356cb17bb05a61c7d20732b6fa8dcd73b +Author: sabri ünal +Date: Thu Jan 24 22:11:31 2019 +0000 + + Typo + + (cherry picked from commit 43fb9242ca5f034860b07c2ddfb6a45431cff72e) + + plug-ins/common/border-average.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 7a0a3f47c7199171818f7b71436b4654ce440dc7 +Author: Anders Jonsson +Date: Thu Jan 24 21:48:38 2019 +0000 + + Update Swedish translation + + po-script-fu/sv.po | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +commit ada21f2594c13c2cd85f452edc14dea574dfd158 +Author: Jehan +Date: Thu Jan 24 16:14:46 2019 +0100 + + Issue #2848: some sentences have no translation. + + (cherry picked from commit f508e24f0c0a53f5c99b2aa4784d25d213eaa686) + + app/operations/gimpoperationsemiflatten.c | 4 ++-- + app/operations/gimpoperationthresholdalpha.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +commit 8361377b4327f1c697c3c5240ec72200c6692114 +Author: Ell +Date: Thu Jan 24 04:03:44 2019 -0500 + + app: in gimp:gradient, improve adaptive supersampling + + In gimp:gradient, when using adaptive supersampling, render the + gradient tile-by-tile, using an iterator, instead of row-by-row. + This significantly improves performance, while also avoiding the + assumption that gimp_adaptive_supersample_area() works row-by-row. + + Additionally, when not using supersampling, use a single GRand + instance, since the separation to distinct seed and per-tile + instances, which was a threading optimization (commit + 7f39e412545e2e45fb3386a0941f33de3d1a66ba), is no longer needed. + + (cherry picked from commit 2cd7938f02a92c63d57c7dc94e9bb93cdea9af05) + + app/operations/gimpoperationgradient.c | 95 + ++++++++++++++-------------------- + 1 file changed, 40 insertions(+), 55 deletions(-) + +commit 14030fc930299da8f5f076b9db06a3235b8300f2 +Author: Ell +Date: Thu Jan 24 03:32:22 2019 -0500 + + libgimpcolor: in gimp_adaptive_supersample_area(), use scratch + allocator + + In gimp_adaptive_supersample_area(), use the scratch allocator for + temporary buffers, instead of the regular allocator. + + (cherry picked from commit 33d5eb20903ff3dc686d169aabc600ae873094c3) + + libgimpcolor/gimpadaptivesupersample.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit 676936f9a71b8d7e6052d797222e68d3815645a9 +Author: Jehan +Date: Thu Jan 24 01:36:27 2019 +0100 + + NEWS: update. + + NEWS | 2 ++ + 1 file changed, 2 insertions(+) + +commit ead5e8e84b14c2983131e915f138f573a7ac3c48 +Author: Jehan +Date: Thu Jan 24 01:35:11 2019 +0100 + + desktop: add rewrite of the Spyrogimp in appdata tag. + + It is well worth listing there! + + (cherry picked from commit c3061c13d4fa9b960901acf6f2fa9ecdab2a382b) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 4 ++++ + 1 file changed, 4 insertions(+) + +commit 8729f9a6620d7e8a2c1666446cf0194be6b83709 +Author: Jehan +Date: Thu Jan 24 01:05:02 2019 +0100 + + plug-ins: clean the new Spyrogimp, rename it and remove old version... + + ... from menus. + The script-fu version is still available through pdb (for scripts) and + even in the action search. But in menus, only the new Python version + will be shown. Also update the description and name of the old version + to make clear it is deprecated in favor of the new plug-in. + Finally rename the new version to simply "plug-in-spyrogimp" (dropping + the "-plus" part as we should consider it as a replacement rather than + as another plug-in, which the "plus" would imply). Anyway the old one + was called "script-fu-spyrogimp", so there is no name clash. + + While at it, do some trailing whitespace cleaning in the new plug-in. + + plug-ins/pygimp/plug-ins/spyro_plus.py | 78 + ++++++++++++++++---------------- + plug-ins/script-fu/scripts/spyrogimp.scm | 7 +-- + 2 files changed, 41 insertions(+), 44 deletions(-) + +commit 529583430d641ade0ada35278bd01abf083bcb39 +Author: Elad Shahar +Date: Thu Jan 24 00:31:15 2019 +0100 + + plug-ins: Spyrogimp plugin rewrite. + + Comment by reviewer (Jehan): + + This was submitted through gimp-developer mailing list, by the same + author as the original Spyrogimp in script-fu, but this time in + Python. + + It does more than the original plug-in, with some automatic preview + (by + drawing directly on a temporary layer, not as a GEGL preview), + and using + the current tool options (current brush, etc.). The new API is similar + yet different. The much evolved possibilities makes that I don't think + it is worth trying to map 1-1 the new API to the old one, so I + just let + the old plug-in next to the new one, with a different name. + + Note finally that the author also contributed a new Spyrograph + operation + to GEGL, yet with the comment: "The GEGL spyrograph operation is very + basic, and untested from gimp. I intend to keep developing it, since I + thought that on-canvas interaction would be very + user-friendly. However, + I am not sure I will be able to get it work in a way that makes the + on-canvas interaction interactive enough. + + Even if I do, it will not do what the Python plugin can do. It will be + much more basic." + + So let's just integrate this evolved version of Spyrogimp for now. :-) + See: + https://mail.gnome.org/archives/gimp-developer-list/2018-September/msg00008.html + + plug-ins/pygimp/plug-ins/Makefile.am | 4 +- + plug-ins/pygimp/plug-ins/spyro_plus.py | 1807 + ++++++++++++++++++++++++++++++++ + 2 files changed, 1810 insertions(+), 1 deletion(-) + +commit 18bcb415733d0771bff347b58e48b37da75e9690 +Author: Jehan +Date: Wed Jan 23 23:42:17 2019 +0100 + + desktop: check that no remaining TODOs are in appdata file. + + Before each release, we prepare now a tag. Unfortunately + some + details are not always well known for sure in advance, in particular + the + date of release (plans are made to be broken!), and also the release + news URL (since the date is in the URL). So I usually leave them + as TODO + and until now, I never forgot to update them just before release. But + there is always the risk of forgetting. + Now the test for the appdata files will also look for remaining "TODO" + in the file if and only if the micro version is even (which means + it's a + release). So we won't ever forget to update the file as long as we run + `make check` on the last commit (which we always do obviously). + + (cherry picked from commit c0fed5afd6d1bfae9852f6d1694a426cb80d9557) + + configure.ac | 1 + + desktop/Makefile.am | 6 ++++-- + desktop/test-appdata.sh | 6 ------ + desktop/test-appdata.sh.in | 15 +++++++++++++++ + 4 files changed, 20 insertions(+), 8 deletions(-) + +commit b0c0895b482495b8aac6eab35209d40cc9efff7e +Author: Jehan +Date: Wed Jan 23 23:03:36 2019 +0100 + + desktop: reenable validity tests for appdata file. + + It got disabled in commit 0492157dd2 because of a bug in + appstream-util, + which has been fixed for monthes now. Let's just reenable it and + assume + anyone who wants to run a `make check` on GIMP uses a recent + distribution with up-to-date system tools. + + (cherry picked from commit f83271a39b19bc52a88c6bc66df99d519498e340) + + desktop/test-appdata.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ec09eaca8adf6dd1afc02aecf75a4a990fc71f45 +Author: Jehan +Date: Wed Jan 23 22:37:39 2019 +0100 + + desktop: add tags to . + + Cf. recent update of the appstream spec. + See also: https://github.com/ximion/appstream/pull/160 + + (cherry picked from commit cfe1941ac7ab1de50e5554001c04d2ee8cedb012) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +commit a5147bd49f83c123f110bc2e7ab4d976bb756403 +Author: Harald H +Date: Sun Aug 19 16:09:13 2018 +0000 + + Added OARS + + (cherry picked from commit 42dd3fd93959947f69cf6085db0c3c35e59dbcf5) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 1 + + 1 file changed, 1 insertion(+) + +commit 24598bb5b606fff93a56cd3c201ba04d1a4d1b0c +Author: Jehan +Date: Sun Aug 26 15:11:52 2018 +0200 + + desktop: add missing filter in notes. + + Also remove '.' at end of some
  • . `appstream-util` does not + like full + stops in item lists, when in `validate-strict` mode (we don't test in + this mode, but it is worth improving the warning list a bit). + + (cherry picked from commit 7a7de5c61caaceee43006b06dc24477587f17b8c) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +commit 1bc7d5d01f4adf01627f2afe8a126c7e45804a62 +Author: Ell +Date: Wed Jan 23 16:35:18 2019 -0500 + + devel-docs: in performance-logs.md, fix markers-page section number + + (cherry picked from commit 4fefa44574f192f7738a7783df44182f425b0f80) + + devel-docs/performance-logs/performance-logs.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 34c732df1d0c6f1ee3d4c28e7475053fd45a7102 +Author: Ell +Date: Wed Jan 23 16:29:10 2019 -0500 + + tools: in performance-log-viewer.py, add markers view + + Add a "markers" page to the performance-log viewer, which lists + the event markers contained in the log, and allows navigating + between them. + + Update docs accordingly. + + (cherry picked from commit dafb63fd661d02779b2e9011b5916fb26a8df4b9) + + devel-docs/performance-logs/performance-logs.md | 55 +++++---- + tools/performance-log-viewer.py | 142 + ++++++++++++++++++++++-- + 2 files changed, 167 insertions(+), 30 deletions(-) + +commit dcc48167705188fe6a2564ca375921437d6ea6c7 +Author: Ell +Date: Wed Jan 23 16:27:30 2019 -0500 + + tools: in performance-log-viewer.py, some cleanup + + (cherry picked from commit 5a6548a4b6aabf35bc4d985d0fc5876d5fb4255c) + + tools/performance-log-viewer.py | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +commit 2052de6c3d3b80be7709b431fd42191f01316ad2 +Author: Ell +Date: Wed Jan 23 15:45:48 2019 -0500 + + devel-docs: fix performance-log profile-view function-column + description + + (cherry picked from commit 17270bb3aa66744351cd317bff4d746667fb5f70) + + devel-docs/performance-logs/performance-logs.md | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit e7ec1aa550aace6f951baed472c90559d5733a52 +Author: Jehan +Date: Wed Jan 23 16:49:23 2019 +0100 + + app: allow more motion events with line art bucket fill. + + When a fill zone was a bit too segmented, you'd want to just stroke + across it. But it was leaving some pieces uncolored, even though the + pointer dragged through it! The exact motion mode allows more events. + + Note: I don't set it in the similar color filling (where it could have + been useful too) mostly because it is harder to remove events then + (even + if a point was already filled, it could still serve as a seed for more + filling if threshold > 0), thus implied too much processing. Anyway in + all my tests, it was more a problem for line art filling anyway. + + (cherry picked from commit 9c13058d548817eb8fa19d152170e88c593c9ef7) + + app/tools/gimpbucketfilltool.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +commit d317fc6dbea255b0f543b0e0113c204cc2a3b835 +Author: Ryuta Fujii +Date: Wed Jan 23 17:04:23 2019 +0000 + + Update Japanese translation + + po/ja.po | 43 +++++++++++++++++-------------------------- + 1 file changed, 17 insertions(+), 26 deletions(-) + +commit 0ca82d41f34ae121853147e413641b96ee1ff4b2 +Author: Anders Jonsson +Date: Tue Jan 22 21:48:33 2019 +0000 + + Update Swedish translation + + po-plug-ins/sv.po | 306 + ++++++++++++++++++++++++++++++++++++------------------ + 1 file changed, 203 insertions(+), 103 deletions(-) + +commit 9ad93d170b362032e7ace6d91074cef5cc01b2b4 +Author: Anders Jonsson +Date: Tue Jan 22 21:42:29 2019 +0000 + + Update Swedish translation + + po-script-fu/sv.po | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +commit 97b905dc0afe5b527ee949c53746c465dfbb4b35 +Author: Alexandre Prokoudine +Date: Tue Jan 22 19:34:33 2019 +0000 + + Update NEWS + + NEWS | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 2d2aef533a1b65162c8a36af22fd48e2f7206aa8 +Author: Alan Mortensen +Date: Tue Jan 22 16:26:32 2019 +0100 + + Updated Danish translation of gimp-script-fu + + po-script-fu/da.po | 286 + ++++++++++++++++++++++++++++------------------------- + 1 file changed, 154 insertions(+), 132 deletions(-) + +commit e30011590e1c6d62191913db58c765794de05089 +Author: Alan Mortensen +Date: Tue Jan 22 16:26:30 2019 +0100 + + Updated Danish translation of gimp-plug-ins + + po-plug-ins/da.po | 1923 + ++++++++++++++++++++++++++++------------------------- + 1 file changed, 999 insertions(+), 924 deletions(-) + +commit 8b66cf0053e7bb7fb654fdb6a5ba8633de5e1ff9 +Author: Alan Mortensen +Date: Tue Jan 22 16:26:28 2019 +0100 + + Updated Danish translation of gimp-libgimp + + po-libgimp/da.po | 411 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 208 insertions(+), 203 deletions(-) + +commit aad44f0abf16ee60e9703b60bbc68007874b0290 +Author: Ryuta Fujii +Date: Tue Jan 22 12:47:38 2019 +0000 + + Update Japanese translation + + po/ja.po | 572 + ++++++++++++++++++++++++++++++++++++++------------------------- + 1 file changed, 347 insertions(+), 225 deletions(-) + +commit 6a65e762673f04a39309a6f49f872f6b6912035d +Author: Ell +Date: Mon Jan 21 11:36:22 2019 -0500 + + app: fix rectangle-select tool rounded-corners option + + In GimpToolRectangle, fix the type of the cornder_radius field, so + that non-integer radii are properly displayed. + + In GimpRectangleSelectOptions and GimpToolRectangle, increase the + maximal corner radius. + + (cherry picked from commit a4726960126fa6a6ab887abd79d8de96bc02525c) + + app/display/gimptoolrectangle.c | 4 ++-- + app/tools/gimprectangleselectoptions.c | 6 +++++- + 2 files changed, 7 insertions(+), 3 deletions(-) + +commit dbe80835ae22d97db26dc58dc48a6fe78538d214 +Author: Alan Mortensen +Date: Mon Jan 21 14:06:13 2019 +0100 + + Updated Danish translation + + po/da.po | 3601 + +++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 2029 insertions(+), 1572 deletions(-) + +commit 9adf6c89ffe224209bba2bd8d0b9b853e1659aba +Author: Michael Natterer +Date: Sun Jan 20 17:48:04 2019 +0100 + + Issue #1942 - Smudge Tool with Sample Merged Option + + Add a Sample Merged option to smudge, a lot like for heal, just needed + tweaking in more places. + + (cherry picked from commit 34cad3a06e5c5f77b964534fe436082e48301684) + + app/paint/gimpsmudge.c | 113 + ++++++++++++++++++++++++++++++------------ + app/paint/gimpsmudgeoptions.c | 14 ++++++ + app/paint/gimpsmudgeoptions.h | 1 + + app/tools/gimpsmudgetool.c | 4 ++ + 4 files changed, 100 insertions(+), 32 deletions(-) + +commit bcc44a08b137a788b180682536b225ff8e28f762 +Author: Ell +Date: Sun Jan 20 10:33:00 2019 -0500 + + app: fix indentation in gimpmaskundo.h + + (cherry picked from commit 4db566f0e1ae2c5921057cf5f5dd44bf6ac77c10) + + app/core/gimpmaskundo.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 8324aeba8280c93946c980ab5f72618238010e6f +Author: Ell +Date: Sun Jan 20 09:58:05 2019 -0500 + + app: align mask-undo buffer to tile grid + + In GimpMaskUndo, align the copied region to the source buffer's + tile grid, so that all copied tiles are COWed. + + (cherry picked from commit 7cd768f3d8b17ccf5625c0a0edd6126f23e4b05d) + + app/core/gimpmaskundo.c | 103 + +++++++++++++++++++++++++----------------------- + app/core/gimpmaskundo.h | 9 +++-- + 2 files changed, 59 insertions(+), 53 deletions(-) + +commit 78e9f0935740c663036e3b563c292eb51d5964de +Author: Jehan +Date: Sun Jan 20 16:02:19 2019 +0100 + + NEWS: keep up to date. + + NEWS | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +commit 10aa988afa5587e209b8fed908a149fac8c907cb +Author: Jehan +Date: Sun Jan 20 13:08:36 2019 +0100 + + Issue #2828: Scrolling up with a mouse within a drop-down list. + + We were doing it all the wrong way, fixing one combo box object at a + time. So this commit basically reverses commits 68a33ab5bd, 6dfca83c2a + and a9a979b2d0 and instead runs the same code in the class code. This + way, all objects based on these base classes will have the fix from + scratch. + These improved various other drop-down lists (I found some of + them, and + probably not all) as I fixed all GIMP custom widgets based on + GtkComboBox. + + Note that it has to be run after filling the list apparently (I + had the + problem especially with GimpIntComboBox if running in the _init() + code, + then the list widget showed wrong). + + (cherry picked from commit 1d984542e9a4673dc81baa3d60d389e3bb3c2d03) + + app/dialogs/image-new-dialog.c | 2 -- + app/display/gimpscalecombobox.c | 3 +++ + app/display/gimpstatusbar.c | 1 - + app/widgets/gimpcontainercombobox.c | 3 +++ + app/widgets/gimplanguagecombobox.c | 3 +++ + app/widgets/gimplayermodecombobox.c | 4 ---- + libgimpwidgets/gimpcolorprofilecombobox.c | 3 +++ + libgimpwidgets/gimpintcombobox.c | 3 +++ + libgimpwidgets/gimpstringcombobox.c | 3 +++ + libgimpwidgets/gimpunitcombobox.c | 3 +++ + 10 files changed, 21 insertions(+), 7 deletions(-) + +commit cbf5e7af7a76b5f695fa6ef42ae78ad454693b02 +Author: Jehan +Date: Sun Jan 20 11:51:21 2019 +0100 + + Issue #2828: Scrolling up with a mouse within a drop-down list. + + Just another instance of the GtkCombo issue, same as #2642. + + (cherry picked from commit 68a33ab5bda1470453d5856afc5bc499631b6da7) + + app/dialogs/image-new-dialog.c | 2 ++ + 1 file changed, 2 insertions(+) + +commit 951a388cc3865dd3cdf9443bc417d15685c34abd +Author: oschlueter +Date: Sat Jan 19 21:26:53 2019 +0000 + + fixed typo in deprecation warning in gimpdrawable.h + + libgimp/gimpdrawable.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit a94b1389ed8c9615717824e65c6b0f7f40e67fcb +Author: Michael Natterer +Date: Sat Jan 19 18:29:04 2019 +0100 + + Issue #266 - Healing Tool - Sample Merged option does not work + + gimp_heal_motion(): when sample merged is active, simply initialize + the paint buffer from the projection instead of the drawable. + + (cherry picked from commit e269a837cd4a7f86bdfec593742a79f705acda59) + + app/paint/gimpheal.c | 33 +++++++++++++++++++++++++++------ + 1 file changed, 27 insertions(+), 6 deletions(-) + +commit a16a95f4308b74d35f0957933c6052486a877b03 +Author: Ryuta Fujii +Date: Fri Jan 18 22:46:45 2019 +0000 + + Update Japanese translation + + po/ja.po | 67 + ++++++++++++++++------------------------------------------------ + 1 file changed, 16 insertions(+), 51 deletions(-) + +commit 4c22313ce6a0814c66d7ea42e1d07defc4cf3bfc +Author: Ell +Date: Thu Jan 17 15:05:37 2019 -0500 + + app: improve gimp_channel_clear() + + When clearing a channel, do nothing if the channel is already + empty; otherwise, align the cleared rectangle to the channel + buffer's tile grid, so that all affected tiles are dropped, rather + than zeroed. Furthermore, only update the affected region of the + channel. + + (cherry picked from commit ac5e4f4c33688498e197a75e40a82f7349253af8) + + app/core/gimpchannel.c | 33 +++++++++++++++++++++++++-------- + 1 file changed, 25 insertions(+), 8 deletions(-) + +commit 858dbf6e18a635713319638bd80afea5a97afc49 +Author: Ell +Date: Thu Jan 17 14:17:57 2019 -0500 + + app: update drawable filter upon alpha-lock change + + In GimpDrawableFilter, when operating on a layer, update the filter + when the layer's lock-alpha flag changes. + + (cherry picked from commit de4e7b477054803419d1648da49e3075102d24b8) + + app/core/gimpdrawablefilter.c | 99 + +++++++++++++++++++++++++++---------------- + 1 file changed, 62 insertions(+), 37 deletions(-) + +commit 8064a17e86a00f3b608b87af213b46e2b51909e7 +Author: Jehan +Date: Thu Jan 17 13:48:40 2019 +0100 + + app: fix a crash in bucket fill tool. + + In commit c71b4916af, I forgot to disconnect signals on the bucket + fill + options at finalization, leading the software to crash on an + non-existing tool. + + (cherry picked from commit 0a952a3429b01223d7db5ccdbdb2fef0235bb516) + + app/tools/gimpbucketfilltool.c | 1 + + 1 file changed, 1 insertion(+) + +commit 7700cb3d81507d015a9ccc77dd7b11fe0ff13458 +Author: Jehan +Date: Thu Jan 17 13:21:18 2019 +0100 + + app: improved fix to commit 036ccc70cf. + + After discussion with Sébastien Fourey and David Tschumperlé, it was + decided that a better fix for the edge case raised in #2785 was to + add a + keypoint anyway, even if the point and none of its neigbours have a + positive smoothed curvature, yet they have a positive raw + curvature. In + such case, we use the local maximum raw curvature instead of the local + maximum smoothed curvature. + + (cherry picked from commit aa042586208e65b39bd66efb846abac5f8f61928) + + app/core/gimplineart.c | 40 ++++++++++++++++++++++++++++++---------- + 1 file changed, 30 insertions(+), 10 deletions(-) + +commit b35941ec4c1dfa0dd4ab2e1e0f35d436afd8d2ba +Author: Jehan +Date: Sun Jan 13 17:40:25 2019 +0100 + + Issue #2785: Fill by line art detection produces Segmentation fault... + + ... with some images. + + (cherry picked from commit 036ccc70cf5e6d5da24749485c0747c2c95eeef7) + + app/core/gimplineart.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +commit 61a6b15a666bef9a7ee9d1286664f5e14ac5e7e1 +Author: Ell +Date: Thu Jan 17 09:26:54 2019 -0500 + + app: parallelize gimp_pickable_contiguous_region_by_color() + + ... by distributing the processed area over multiple threads. + + (cherry picked from commit 8571d7812f01a8ae89cfd36a825b3dccf2e2ec2d) + + app/core/gimppickable-contiguous-region.cc | 72 + +++++++++++++++++------------- + 1 file changed, 41 insertions(+), 31 deletions(-) + +commit cfd1d0463f43d940a7ac925c32608dcdbfb35d4d +Author: Ell +Date: Thu Jan 17 09:12:31 2019 -0500 + + app: convert gimppickable-contiguous-region to C++ + + (cherry picked from commit 9719dff9c55c2331a3bb5d01ecc96e8a825fce6a) + + app/core/Makefile.am | 2 +- + ...ontiguous-region.c => gimppickable-contiguous-region.cc} | 13 + +++++++++---- + 2 files changed, 10 insertions(+), 5 deletions(-) + +commit ad969f02856b2d4816d6928e37fb89ee7887991e +Author: Piotr Drąg +Date: Thu Jan 17 11:49:35 2019 +0100 + + Update Polish translation + + po-plug-ins/pl.po | 21 +++--- + po/pl.po | 198 + ++++++++++++++++++++++++++++++++---------------------- + 2 files changed, 128 insertions(+), 91 deletions(-) + +commit e67d6752e8224bbd8dcc910a86346f6133bc8088 +Author: sabri ünal +Date: Tue Jan 15 18:12:15 2019 +0000 + + Odd ` characters corrected as '. + + (cherry picked from commit 76302b6634ef78035ea7b7a0e220751fa5a0ed43) + + .../selection-to-path/selection-to-path-dialog.c | 34 + +++++++++++----------- + 1 file changed, 17 insertions(+), 17 deletions(-) + +commit 940cf5f196284730e5cde0f03f7f1f39e7d7ed66 +Author: Ell +Date: Wed Jan 16 03:55:18 2019 -0500 + + tools: in performance-log-viewer.py, allow inverting selection + + In the perofmance-log viewer, add header-bar buttons to clear and + invert the selection, and allow inverting the selection by ctrl- + right-clicking on the sample-selection area. + + Update the docs. + + (cherry picked from commit b74c33db5ce5d3706353f65741a3f96e40e375e7) + + devel-docs/performance-logs/performance-logs.md | 21 +++++++++--- + tools/performance-log-viewer.py | 44 + +++++++++++++++++++++++-- + 2 files changed, 57 insertions(+), 8 deletions(-) + +commit 46c5e8f33f6b37d916a042920bc319cf3436c166 +Author: Ell +Date: Wed Jan 16 03:01:06 2019 -0500 + + tools: in performane-log-viewer.py, change sample-selection icon + + (cherry picked from commit f246a0197b1174cf0f2408d80546badcbe812294) + + tools/performance-log-viewer.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 1e4df259c13aa96de5189b32b210fdfefecf7e9a +Author: Marco Ciampa +Date: Wed Jan 16 09:38:38 2019 +0100 + + Updated Italian translation + + po/it.po | 189 + +++++++++++++++++++++++++++++++++++++++++---------------------- + 1 file changed, 122 insertions(+), 67 deletions(-) + +commit 32fdd693242ad803758347a2833036b115af772f +Author: Jehan +Date: Tue Jan 15 16:06:50 2019 +0100 + + app: add the concept of line art source to Bucket Fill tool. + + Additionally to sample merge and active layer, now we can only use the + layer above or below the active layer as line art source. + + The line art fill is meant to work on drawing lines. Though sample + merge + still is ok in many cases, the more you fill with colors, the more the + line art computation becomes unecessarily complex. Also when you use a + lot of layers with some of them already filled with colors, it + makes it + impossible to colorize some line art zones with the tool. Moreover you + just don't want to have to hide every layers out there to colorize one + layer (especially background layers and such as you may want to + see the + result with your background). + Thus we want to be able to set the source as a unique layer, while it + not being necessarily the active one (because you want lines and + colors + on different layers). In this case, I am assuming that the color + and the + line layers are next to each other (most common organization). + + (cherry picked from commit c71b4916af34402e74c47f72663232cd17124070) + + app/core/gimplineart.c | 6 ++ + app/core/gimplineart.h | 5 +- + app/tools/gimpbucketfilloptions.c | 26 +++++- + app/tools/gimpbucketfilloptions.h | 1 + + app/tools/gimpbucketfilltool.c | 186 + ++++++++++++++++++++++++-------------- + app/tools/tools-enums.c | 33 +++++++ + app/tools/tools-enums.h | 22 +++++ + 7 files changed, 206 insertions(+), 73 deletions(-) + +commit 83fd5557174fcebb3635c5fd32a979c844770bed +Author: Ryuta Fujii +Date: Tue Jan 15 16:20:10 2019 +0000 + + Update Japanese translation + + po/ja.po | 48 +++++++++++++----------------------------------- + 1 file changed, 13 insertions(+), 35 deletions(-) + +commit 2247fce313f29a6e38c964cdfb7cb9e03aedb241 +Author: Ell +Date: Tue Jan 15 02:33:48 2019 -0500 + + libgimp: in GimpTileBackendPlugin, change default tile multiplier to 1 + + In GimpTileBackendPlugin, change the default tile multiplier, + specifying the ratio between the backend tile-size, and GIMP's + tile-size, from 2 to 1. Since we're reading/writing each GIMP tile + using a separate command anyway, using a large multiplier doesn't + provide any benefits, while it does have drawbacks. In particular, + it reduces the chance that a write operation will affect an entire + tile, which allows us to avoid reading the tile data from GIMP. + + (cherry picked from commit a5e2945b684b0edb0c39bf89467a258c7e5bf25d) + + libgimp/gimptilebackendplugin.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 535fd3a04100dd9afecd8bca7b6d6b099d6f9e1e +Author: Ell +Date: Tue Jan 15 02:28:42 2019 -0500 + + libgimp: in GimpTileBackendPlugin, don't read tile data upon TILE_SET + + Add an internal _gimp_tile_ref_noinit() function, which increases + the ref-count of a tile *without* initializing its data (in + particular, without reading its data from GIMP, or zeroing it.) + Use this function, instead of gimp_tile_ref(), when storing a tile + in GimpTileBackendPlugin, to avoid unnecessarily reading the tile + data from GIMP. + + (cherry picked from commit 5ffdb9aa415c90b97c1ece736977717c50c1da1c) + + libgimp/gimptile.c | 13 +++++++++++++ + libgimp/gimptile.h | 2 ++ + libgimp/gimptilebackendplugin.c | 2 +- + 3 files changed, 16 insertions(+), 1 deletion(-) + +commit 76e88e392c1a149f54fde28857ffedcb54c505a0 +Author: Ryuta Fujii +Date: Mon Jan 14 16:33:49 2019 +0000 + + Update Japanese translation + + po/ja.po | 114 + ++++++++++++++++++++------------------------------------------- + 1 file changed, 35 insertions(+), 79 deletions(-) + +commit 88cd4e205f98cd7f123bbf20ee5ee9883200ac12 +Author: Rodrigo Lledó +Date: Mon Jan 14 07:28:01 2019 +0000 + + Update Spanish translation + + po/es.po | 1100 + ++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 610 insertions(+), 490 deletions(-) + +commit 228d8fbef5b7adf2247da9d6e6de084f07b1a9cd +Author: Marco Ciampa +Date: Mon Jan 14 00:53:56 2019 +0100 + + Updated Italian translation + + po-plug-ins/it.po | 14 +-- + po/it.po | 285 + ++++++++++++++++++++++++++++++++++++------------------ + 2 files changed, 198 insertions(+), 101 deletions(-) + +commit fe5ee0f7b86466bd0b156a851b93bdab1d447bf2 +Author: Ell +Date: Sun Jan 13 08:20:40 2019 -0500 + + app: clear priority rect when finishing projection rendering + + In gimp_projection_finish_draw(), clear the chunk iterator's + priority rect before finishing rendering, since it's not needed at + this point, and this is slightly more efficient. + + (cherry picked from commit 9833da3431b8bb466924897152faf807974a6842) + + app/core/gimpprojection.c | 2 ++ + 1 file changed, 2 insertions(+) + +commit 293ae15b726003abe3317d94c12a83fcd697f906 +Author: Ell +Date: Sun Jan 13 08:02:32 2019 -0500 + + app: in filter tools, allow toggling on-canvas controller visibility + + In GimpFilterTool, when the filter uses an on-canvas controller, + provide a toggle in the tool's filter-options dialog allowing to + toggle the controller's visibility. This allows getting the + controller out of the way when unneeded. + + (cherry picked from commit 33c22ae2a395d0685513cdcfd366afcd26e9bfef) + + app/tools/gimpfilteroptions.c | 16 ++++++++++++++++ + app/tools/gimpfilteroptions.h | 1 + + app/tools/gimpfiltertool.c | 36 ++++++++++++++++++++++++++++++++---- + app/tools/gimpfiltertool.h | 1 + + 4 files changed, 50 insertions(+), 4 deletions(-) + +commit c8628862058364774bdfbf9b0904a426951f8a02 +Author: Ell +Date: Sun Jan 13 07:57:19 2019 -0500 + + app: add gimp_tool_widget_{get,set}_visible() + + Add mew gimp_tool_widget_{get,set}_visible() functions, which allow + setting the visibility of a tool widget. While the widget is + invisible, it ignores all events. + + (cherry picked from commit f1a7abaef9b5dbff56f2058b1e829095d46c2a71) + + app/display/gimptoolwidget.c | 124 + ++++++++++++++++++++++++++++++++----------- + app/display/gimptoolwidget.h | 4 ++ + 2 files changed, 98 insertions(+), 30 deletions(-) + +commit 3ac417afe4713c9faccc716e766f82f3643fd849 +Author: Ell +Date: Sun Jan 13 07:53:12 2019 -0500 + + app: in GimpDrawTool, avoid CRITICAL on widget signal if not active + + In GimpDrawTool, do nothing in the tool-widget signal handlers if + the draw-tool isn't active, to avoid CRITICALs due to a NULL + display. This can happen if a widget is set before the tool is + started. + + (cherry picked from commit 34e6c8734b791ad403cbf17d6d139cc41e6fd253) + + app/tools/gimpdrawtool.c | 26 +++++++++++++++----------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +commit 8477c3d3cdf563fa4a213a813d19d0585d5e1187 +Author: Ell +Date: Sun Jan 13 03:39:00 2019 -0500 + + app: in GimpToolPath, use gimp_tool_message() instead of _set_status() + ... + + ... when trying to edit a locked path. + + (cherry picked from commit a9883e98e384be1648d1575ba0b0506196d11565) + + app/display/gimptoolpath.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 300166a6125bb7f232a3d03133eb981e52b604c9 +Author: Ell +Date: Sun Jan 13 03:38:19 2019 -0500 + + app: handle GimpToolWidget::message in GimpDrawTool + + ... by forwarding the message to the tool. + + (cherry picked from commit 265071f34bc7dd3c40686f70da4d6cf97cfe6207) + + app/tools/gimpdrawtool.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +commit 52a8dadaf643618d5da673d7f2387f8b0f907e3c +Author: Ell +Date: Sun Jan 13 03:37:01 2019 -0500 + + app: handle GimpToolWidget::message in GimpToolWidgetGroup + + ... by forwarding the message, if the emitting widget has focus. + + (cherry picked from commit 00a06f94be6a1c86a8d0056f4656480d9a706bbd) + + app/display/gimptoolwidgetgroup.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +commit 38c74c2809df94af7906260483b321c9eeba77ed +Author: Ell +Date: Sun Jan 13 03:28:39 2019 -0500 + + app: add gimp_tool_widget_message[_literal]() + + Add a GimpToolWidget::message signal, which can be emitted by tool + widgets to display a message, instead of using the ::status signal. + + Add corresponding gimp_tool_widget_message[_literal]() functions. + + (cherry picked from commit 1ac4b85ce058f2a50462517b2012673601752f17) + + app/display/gimptoolwidget.c | 46 + ++++++++++++++++++++++++++++++++++++++++++++ + app/display/gimptoolwidget.h | 8 ++++++++ + 2 files changed, 54 insertions(+) + +commit 10f22753e4eef24d0c1ac746e21671daca12622b +Author: Ell +Date: Sun Jan 13 03:24:14 2019 -0500 + + app: in gimpdrawtool.c, s/rectangle/widget/ in tool-widget signal + handlers + + (cherry picked from commit 749dc3c465a4c79f15e1780f08d82d2eb28c54e9) + + app/tools/gimpdrawtool.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 0da466a47cc1dbfd33f2431c90b89b33f69d64fe +Author: Piotr Drąg +Date: Sun Jan 13 12:35:40 2019 +0100 + + Update Polish translation + + po-plug-ins/pl.po | 12 +-- + po/pl.po | 240 + +++++++++++++++++++++++++++++++++++++++--------------- + 2 files changed, 179 insertions(+), 73 deletions(-) + +commit 78392316db415bc675b5a438d88a222541dfa8df +Author: Jehan +Date: Sat Jan 12 17:17:58 2019 +0100 + + Issue #2786: a few string issues. + + Several en_GB to en_US. + + Also "Show a preview of the transform_grided image". "grided" + should be + "gridded", but I also have a problem with the underscore. Should it be + "transform-gridded"? Even so, does it really make sense? + I chose to just read "Show a preview of the transformed image", + which I + think is simpler and the most understandable (we don't need to + leak the + implementation with a transform grid into the human read text IMO). If + anyone think that was not the right choice, feel free to propose + otherwise. + Thanks to Bruce Cowan for noticing these. + + (cherry picked from commit f9c170dfbdb88aa5b303f95b70c67b6595f48245) + + app/tools/gimptransformgridoptions.c | 2 +- + plug-ins/metadata/metadata-tags.h | 6 +++--- + plug-ins/ui/plug-in-metadata-editor.ui | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +commit 4fd8e4841f5ba142341229d875437c5c2e36d77c +Author: Jehan +Date: Sat Jan 12 00:26:19 2019 +0100 + + app: add link to Smart Colorization scientific paper. + + This is sometimes asked, and myself also need to find it from time to + time. I may as well put the link inside the code comments, where it is + just easy to find! + + (cherry picked from commit 005bc1406bede5abdae965a52b636b8c511f7e6f) + + app/core/gimplineart.c | 1 + + 1 file changed, 1 insertion(+) + +commit 944d509e78bf823a281b5ca2b7cb7865ae2d59f1 +Author: Jehan +Date: Fri Jan 11 14:35:03 2019 +0100 + + desktop: prepare GIMP 2.10.10 appdata. + + (cherry picked from commit 388f6da1c4b3ab9a73f60a06cae353f2beddca8f) + + desktop/org.gimp.GIMP.appdata.xml.in.in | 64 + +++++++++++++++++++++++++++++++++ + 1 file changed, 64 insertions(+) + +commit dfc038e6509850338c99ce503d576b053ba8fb7d +Author: Ell +Date: Sat Jan 12 08:29:16 2019 -0500 + + app: in gimp_projection_chunk_render_start(), properly invalidate + preview + + In gimp_projection_chunk_render_start(), when the current + projection rendering is complete, but not finalized yet, and no new + rendering is started (since the current update region is empty), + make sure to invalidate the projectable's preview, since it + normally happens when rendering is finalized, which doesn't happen + in this case. + + (cherry picked from commit 42845c9462821f0d95b002a8e44efe90e42fb173) + + app/core/gimpprojection.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +commit f21eec319fdc5ed70df0b42a24228b5462c7abad +Author: Ell +Date: Sat Jan 12 08:14:40 2019 -0500 + + app: in gimp_projection_chunk_render_start(), don't leak empty region + + In gimp_projection_chunk_render_start(), don't leak the current + update region when it's empty, but not NULL, and properly shut down + the idle source. + + (cherry picked from commit 0e5de9576056a3262638e1aa5a23ca2a450d335a) + + app/core/gimpprojection.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +commit 3f2815922fe77404222d95adfe45c9554c44be7b +Author: Bruce Cowan +Date: Sat Jan 12 12:41:52 2019 +0000 + + Update British English translation + + po-plug-ins/en_GB.po | 12325 + ++++++++++++++++++++++++++++--------------------- + 1 file changed, 7078 insertions(+), 5247 deletions(-) + +commit 4cbec7608ff0d77d80af69c889eb99247cc5ee44 +Author: Ell +Date: Sat Jan 12 07:30:43 2019 -0500 + + app: fix gimp_chunk_iterator_set_priority_rect() + + (cherry picked from commit 942e1aa9390b163b0b4a2998a582bb0b850710e3) + + app/core/gimpchunkiterator.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 9bf833b0e466fa796579f392dc6cc31d3a57391e +Author: Piotr Drąg +Date: Sat Jan 12 13:11:15 2019 +0100 + + Update Polish translation + + po/pl.po | 64 + ++++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 36 insertions(+), 28 deletions(-) + +commit 797469d3512ded303a5cf2960e1c402b7a98b09b +Author: Bruce Cowan +Date: Sat Jan 12 12:03:58 2019 +0000 + + Update British English translation + + po/en_GB.po | 36139 + ++++++++++++++++++++++++++++++++++++++-------------------- + 1 file changed, 23461 insertions(+), 12678 deletions(-) + +commit 5e00c26295732fc7dbb754efbe63bdbff08c95fc +Author: Ell +Date: Sat Jan 12 06:16:18 2019 -0500 + + app: in gimpchunkiterator.c, #include + + ... for qsort(). + + (cherry picked from commit a61f29e30a549b6136225935f9a438d4b41617af) + + app/core/gimpchunkiterator.c | 2 ++ + 1 file changed, 2 insertions(+) + +commit cc59bce82e3b3f672e46809bbe9a78e10bbc408e +Author: Ell +Date: Sat Jan 12 05:48:03 2019 -0500 + + Issue #440 - libgimp/gimptilebackendplugin.c provides no pyramid + + In GimpTileBackendPlugin, return NULL when fetching z>0 tiles, + instead of simply ignoring the z coordinate, so that the mipmapped + tile is rendered locally. Likewise, avoid storing z>0 tiles. + + Note that this is suboptimal, since all the necessary level-0 tiles + need to be sent to the buffer as a result. Ideally, we should + extend the wire protocol to handle mipmapped tiles. + + (cherry picked from commit d0ae39f017ae0f23f0d3f16220a11c32fad3291c) + + libgimp/gimptilebackendplugin.c | 25 ++++++++++++++++++------- + 1 file changed, 18 insertions(+), 7 deletions(-) + +commit aa0df19af22839840e76bbe31a2367032e5291bd +Author: Ell +Date: Sat Jan 12 05:11:47 2019 -0500 + + app: in gimp_gegl_apply_cached_operation(), s/cancellable/cancelable/ + + To align gimp-2-10 with master, and fix commit + 50dc4571cbb8a30ac8b9d576fc8c167a92ef6dad. + + app/gegl/gimp-gegl-apply-operation.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit 7547df54dd544cdc080d3d958e0f85bd71961605 +Author: Ell +Date: Sat Jan 12 04:48:17 2019 -0500 + + app: in the gradient tool, halt gradient editor before committing + filter + + In the gradient tool, halt the gradient editor before committing + the filter, so that its image-flush idle source is removed before + applying the operation, to avoid flushing the image, and hence + restarting its projection rendering, during application. + + (cherry picked from commit 2256ab22f755dc181d86c193380399d54b08ca4c) + + app/tools/gimpgradienttool.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit bb4b1e3f1f132fa2ea19b9da4947dceab1b7d6ed +Author: Ell +Date: Sat Jan 12 03:46:18 2019 -0500 + + app: in gimp_tile_handler_validate_buffer_copy(), temporarily remove + source handler + + In gimp_tile_handler_validate_buffer_copy(), temporarily remove the + source buffer's validate handler, is exists, so that the subsequent + gegl_buffer_copy() can use fast tile copying, using the TILE_COPY + command. GEGL currently only uses TILE_COPY when the source buffer + doesn't have any user-provided tile handlers. + + (cherry picked from commit f4750a0ae7610bbb4e76117335075505c981ab4a) + + app/gegl/gimptilehandlervalidate.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +commit 50dc4571cbb8a30ac8b9d576fc8c167a92ef6dad +Author: Ell +Date: Sat Jan 12 03:41:00 2019 -0500 + + app: use GimpChunkIterator in gimp_gegl_apply_cached_operation() + + In gimp_gegl_apply_cached_operation(), replace the use of + GeglProcessor with GimpChunkIterator, so that we use the same + chunking logic as for rendering projections. This has the + advantage of better chunk alignment to the tile grid and dynamic + chunk sizing, which improve performance. + + Use chunking even when there's no progress indication, since it + generally results in better cache locality. + + (cherry picked from commit 4110f7b7b186a609d9ca9882cf1f8a1e12b4169e) + + app/gegl/gimp-gegl-apply-operation.c | 133 + ++++++++++++++--------------------- + 1 file changed, 54 insertions(+), 79 deletions(-) + +commit b62e3fd3b8f731d3ca187d75afe1e497734143c1 +Author: Ell +Date: Sat Jan 12 03:36:09 2019 -0500 + + app: use GimpChunkIterator in GimpProjection + + Replace the custom chunking logic of GimpProjection with + GimpChunkIterator, added in the previous commit. + + (cherry picked from commit 246e782858151f8956ee970da8ca2fe6811e841d) + + app/core/gimpprojection.c | 630 + ++++++++-------------------------------------- + 1 file changed, 107 insertions(+), 523 deletions(-) + +commit 8c31ed6f02a86b01762fda70e400e8f0f02f0993 +Author: Ell +Date: Sat Jan 12 03:12:13 2019 -0500 + + app: add GimpChunkIterator + + Factor out the region-chunking logic of GimpProjection into a new + GimpChunkIterator type, providing a generic mechanism for iterating + over a cairo region in discrete chunks. The iterator doesn't + perform any processing itself, but rather dispenses rectangular + chunks, which the user then processes. + + Iteration is broken into intervals, the duration of which is + configurable. Each iteration begins with a call to + gimp_chunk_iterator_next(), after which + gimp_chunk_iterator_get_rect() should be called in succession to + fetch a rectangle to process, until it returns FALSE, which marks + the end of the iteration. Updates to the UI should take place in + the interval between iterations, but not during an iteration. The + iterator dynamically adjusts the chunk size according to processing + speed, in order to match the target iteration interval. + + The iterator can be given a priority rectangle, which is processed + before the rest of the region. It can also be given a + representative tile rectangle, defining a regular tile grid; + dispensed chunks are aligned to the tile grid as much as possible. + + (cherry picked from commit ba9ce34e1009f6abb4c2762fe482b86ae225163c) + + app/core/Makefile.am | 2 + + app/core/core-types.h | 1 + + app/core/gimpchunkiterator.c | 473 + +++++++++++++++++++++++++++++++++++++++++++ + app/core/gimpchunkiterator.h | 44 ++++ + 4 files changed, 520 insertions(+) + +commit 075363f182b62b8934beb4054f1cfe9b94f0cbee +Author: Piotr Drąg +Date: Wed Jan 9 19:53:10 2019 +0100 + + Update POTFILES.in + + po/POTFILES.in | 1 + + 1 file changed, 1 insertion(+) + +commit 5122b8658802673e1f0c3641c06f7cf9ee69bcdb +Author: Jehan +Date: Fri Jan 11 13:20:38 2019 +0100 + + NEWS: update. + + NEWS | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +commit 0055a2ad733515a64f2dca137b639d83628883de +Author: Jehan +Date: Wed Jan 9 00:11:37 2019 +0100 + + app: push a temporary status when picking layer with alt-midclick. + + Though the layer list will also show updated, it is much easier + to look + at the layer name in the status bar whose position never changes. + Anyway it makes sense to just show a temporary status info message + giving the picked layer name, making it all the easier to find + the layer + you are looking for. + + (cherry picked from commit 496bc02b491450a062abbd7a1ff7523a345bfbb7) + + app/display/gimpdisplayshell-tool-events.c | 29 + +++++++++++++++++++++++++---- + 1 file changed, 25 insertions(+), 4 deletions(-) + +commit a0361ec5359e534be8f12032a856c987e76f9bb1 +Author: sabri ünal +Date: Thu Jan 10 10:33:07 2019 +0000 + + Annotation for translators. + + (cherry picked from commit 74df62034a499bc015c690ff605ca1407483789a) + + app/actions/view-actions.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 24f32e6305bf76a90380911cc0b93658820e48c4 +Author: Marco Ciampa +Date: Thu Jan 10 11:45:53 2019 +0100 + + Updated Italian translation + + po-plug-ins/it.po | 272 +++++++++++++++++++--------- + po/it.po | 522 + +++++++++++++++++++++++++++++------------------------- + 2 files changed, 462 insertions(+), 332 deletions(-) + +commit a16d06a40a02dcb85ad7827587038193b9043dca +Author: Ell +Date: Wed Jan 9 13:33:06 2019 -0500 + + Issue #1824 - Crash on 2.10.4 using tablet + + In GimpTool, track the last-seen pointer coordinates, modifier + state, and event time, during button_press() and motion() events + and use those to synthesize a button_release() event when + comitting/halting the tool, if the tool is still active, and a + matching button_release() event has not been received. + + The paint tools (as well as other tools) require each + button_press() event to be matched by a button_release() event in + order to properly finish their operation, but one isn't organically + generated when switching tools due to a device change. + + (cherry picked from commit 9b25611857c75e922dd10f3d933807b0c7cd44ed) + + app/tools/gimptool.c | 34 ++++++++++++++++++++++++++++++++++ + app/tools/gimptool.h | 6 ++++++ + 2 files changed, 40 insertions(+) + +commit 0a22773a179219b70ae9bc34bbd4ac25105da27b +Author: Ryuta Fujii +Date: Wed Jan 9 13:12:42 2019 +0000 + + Update Japanese translation + + po/ja.po | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +commit 51ecd3e14ee2ef42990732ccbd55fa7f05a9e1c2 +Author: Ryuta Fujii +Date: Wed Jan 9 13:06:31 2019 +0000 + + Update Japanese translation + + po/ja.po | 48 +++++++++++++----------------------------------- + 1 file changed, 13 insertions(+), 35 deletions(-) + +commit 5bbfbd1dbac2fcb78edc6c390e4e7087072c61fc +Author: Jehan +Date: Tue Jan 8 23:34:25 2019 +0100 + + Issue #572: Key combinations not working with non English layout. + + It is most likely the same issue as old bug 769550 (on bugzilla), + which + is fixed with GTK+ 2.24.32. Back then, this version of GTK+ had + not been + released yet so we only added a warning in the configure script. See + also commit b7345863. + Now it has been a year since GTK+ 2.24.32 has been released and any + distribution can just upgrade a micro version of GTK+ for bugfix. So + let's just drop the warning and do a hard requirement to 2.24.32. + + configure.ac | 90 + +++++++++++++++++++++--------------------------------------- + 1 file changed, 31 insertions(+), 59 deletions(-) + +commit f7cb9b92e835d763dddd98dae4513cacb5898aae +Author: Ryuta Fujii +Date: Tue Jan 8 15:41:32 2019 +0000 + + Update Japanese translation + + po/ja.po | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit b7793b9c21a7c8606791dbf7938870abe3e98a47 +Author: Ryuta Fujii +Date: Tue Jan 8 15:26:52 2019 +0000 + + Update Japanese translation + + po/ja.po | 228 + +++++++++++++++++++++++++-------------------------------------- + 1 file changed, 89 insertions(+), 139 deletions(-) + +commit d2b98a670000cc8d901d76fe85f474cbd0b58296 +Author: Ell +Date: Tue Jan 8 10:00:51 2019 -0500 + + app: in gimp_display_empty(), clear image of matching contexts + + In gimp_display_empty(), clear the image of all contexts whose + display is the current display, so that, in particular, when + subsequently updating the action groups, which causes certain + actions to be activated, the image that used to be bound to the + display is not found through the user context. This avoids re- + validating the image projection when closing the last image, + postponing image destruction. + + (cherry picked from commit 49e57f8d6e392833503e213fc653735b9765d43b) + + app/display/gimpdisplay.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +commit b1fe1675cebaad7806a55db68874d471fe62aac0 +Author: Jehan +Date: Tue Jan 8 14:19:11 2019 +0100 + + app: expect event == NULL in gimp_device_info_get_event_coords(). + + The problem was not happening with the master code of + gimpdisplayshell-tool-events.c, but I encountered it in gimp-2-10 with + the layer picking code. + Even then, it still works, but I need to protect calls to + gdk_event_get_axis() to avoid CRITICALs. + + app/widgets/gimpdeviceinfo-coords.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2dc3e1813c7dc5116c9c97144321a061ecef2f29 +Author: Jehan +Date: Sat Dec 22 19:09:34 2018 +0100 + + app: make layer picking a generic modifier of the shell. + + Instead of having layer picking only on paint tools with alt-click, + make + it available everywhere with alt-middle click. Moving through + layers is + also a way to navigate an image, so it actually makes sense to be with + other modifiers (panning, zooming, rotating), while making the feature + more generic (this is definitely useful whatever the selected tool). + + (cherry picked from commit 4c337353a034a1de779e9fc01b26192d47a0ced5) + + app/display/gimpdisplayshell-tool-events.c | 54 +++++++++++++++++++++-- + app/display/gimpdisplayshell.h | 2 + + app/tools/gimppainttool.c | 71 + +++--------------------------- + app/tools/gimppainttool.h | 3 -- + 4 files changed, 59 insertions(+), 71 deletions(-) + +commit e0be9bdef2e312a52443d0a6a56355045c2934c4 +Author: Jehan +Date: Sat Dec 15 23:34:07 2018 +0100 + + app: alt-click to pick a layer will loop through candidate layers. + + If you click on a zone filled in several visible layers, you don't + necessarily want the top layer. You may want one below. With this + change, as long as you hold alt, you will loop through all candidate + layers from top to bottom (then looping back top when reaching the + bottom). + In a first alt-click, you will always end up to the top candidate. + + (cherry picked from commit 90e9eb3fcaaaf1b5482c88605532aa719635504b) + + app/core/gimpimage-pick-item.c | 46 + +++++++++++++++++++++++++++++++++--------- + app/core/gimpimage-pick-item.h | 3 ++- + app/pdb/image-cmds.c | 2 +- + app/tools/gimpmovetool.c | 6 ++++-- + app/tools/gimppainttool.c | 18 +++++++++++++---- + app/tools/gimppainttool.h | 1 + + app/tools/gimpselectiontool.c | 2 +- + pdb/groups/image.pdb | 2 +- + 8 files changed, 60 insertions(+), 20 deletions(-) + +commit 5790c809c15ec6b4bc45fc99d6797663ec25ddac +Author: Jehan +Date: Sat Dec 15 22:55:12 2018 +0100 + + app: allow picking layer in paint tools on alt-click. + + When working with a lot of layers, it is common to have to switch + easily + between layers. And having to go back to the layer list is annoying + and + also sometimes not practical at all when you can't find easily + the right + layer. This is a first step in an experiment for such a feature, + worked + together with Aryeom as advisor (and originator of the feature idea). + For now I apply this only to paint tools, though we are considering + having it as a generic modifier too, working whatever the tool. Yet we + wouldn't be able to use alt-left click (as it is used already in some + tools). + How it works is simply that in any paint tool, alt-click allows to + switch to the topmost layer having a visible pixel at the clicked + position. + + (cherry picked from commit 3b59e6f61ef44b9400865031871c5387840cd71e) + + app/tools/gimppainttool.c | 61 + +++++++++++++++++++++++++++++++++++++++++------ + app/tools/gimppainttool.h | 2 ++ + 2 files changed, 56 insertions(+), 7 deletions(-) + +commit b2b88d7caf0107e5f8ead3b7fcf02b89d2f14966 +Author: Snehalata B Shirude +Date: Tue Jan 8 10:54:37 2019 +0000 + + Update Marathi translation + + po/mr.po | 5887 + ++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 3011 insertions(+), 2876 deletions(-) + +commit 752589f42dc950a251304a8eaa52b37b48f97a72 +Author: Michael Natterer +Date: Mon Jan 7 21:35:54 2019 +0100 + + libgimpbase: more metadata fixes for tags that can appear multiple + times + + gimp_metadata_add() which is used to set blobs or EXIF, XMP and IPTC + on a GimpMetadata also needs the logic to set "multiple" tags in one + go, or it will lose all but the first one. + + (cherry picked from commit 479fd5b24bd1c354c7e7a3bd9f4fc6b87cb775bf) + + libgimpbase/gimpmetadata.c | 100 + +++++++++++++++++++++++++++++---------------- + 1 file changed, 65 insertions(+), 35 deletions(-) + +commit 926e28b2195e6e8601c08e272b4d97236c3c5e63 +Author: Ell +Date: Mon Jan 7 04:22:43 2019 -0500 + + app: in bucket fill tool, don't calculate line art of layer groups + + ... since they can't be used with the fill tool. + + (cherry picked from commit 4b4fffbd0e21f0682ec9e39ab6ed40913513f3f9) + + app/tools/gimpbucketfilltool.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 101e2d0b4803c18e63ead0c1a5a76e199e78b777 +Author: Michael Natterer +Date: Mon Jan 7 03:34:41 2019 +0100 + + Issue #1004 - file-pdf-load crashes in lcms when opening PDF with... + + ...color profile + + Require poppler >= 0.50 which is the first version that has the fix. + + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 44fd7157b6ad2127e27afc577fc57e6e4f3e1d2b +Author: Michael Natterer +Date: Mon Jan 7 00:48:43 2019 +0100 + + plug-ins: s/g_printf/g_printerr/ in sgi.c + + plug-ins/file-sgi/sgi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 58e266ed8bfde4b23288595faaf8acd38a3ca540 +Author: Michael Natterer +Date: Mon Jan 7 00:41:31 2019 +0100 + + Issue #1358 - Increase default size of "extended input device + dialog"... + + ...for a less cramped look + + With a minimum width of 300 instead of 200 pixels, much less device + names are cut off and need horizontal scrolling. + + (cherry picked from commit ee6b629fa382295a48ba3d7d146d22da59212a16) + + app/widgets/gimpdeviceeditor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 0f2f95d97b1ff00a1efa9a6b13f472730d65c116 +Author: Ell +Date: Sun Jan 6 16:26:35 2019 -0500 + + app: in bucket-fill tool, avoid calculating line art when not in + line-art mode + + In the bucket-fill tool, don't pre-calculate the line art when not + using a line-art fill area. Also, misc. cleanup. + + (cherry picked from commit 823d4a0d2451b242d33dcd56adf661ed08597546) + + app/core/gimplineart.c | 21 +++-- + app/tools/gimpbucketfilltool.c | 205 + ++++++++++++++++------------------------- + 2 files changed, 89 insertions(+), 137 deletions(-) + +commit 778faaddc81169188c6a56432131f78fe5ee2f65 +Author: Jernej Simončič +Date: Sun Jan 6 21:35:50 2019 +0100 + + Installer: handle missing install directory of GIMP <2.8 better when + there's still uninstall info in Registry + + build/windows/installer/gimp3264.iss | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +commit b6f11ee6352db56731862ff2ad30853fdb9ca3c2 +Author: Jernej Simončič +Date: Sun Jan 6 20:50:15 2019 +0100 + + Installer: remove dead code + + build/windows/installer/gimp3264.iss | 186 + +++++++---------------------------- + 1 file changed, 34 insertions(+), 152 deletions(-) + +commit 0fee22cb9dde8e3068096c262bedb8afbb5b15bd +Author: Piotr Drąg +Date: Sun Jan 6 19:21:11 2019 +0100 + + Update Polish translation + + po-plug-ins/pl.po | 278 +++++++++++++------- + po/pl.po | 763 + +++++++++++++++++++++++++++--------------------------- + 2 files changed, 569 insertions(+), 472 deletions(-) + +commit 4f9bde3bad007cc32f0328da8387653ffa99c6fe +Author: Michael Natterer +Date: Sun Jan 6 17:46:31 2019 +0100 + + Issue #1437 - 2.10 Image Metadata "keywords" corrupt + + We were not taking into account tags that can appear multiple times, + such as "keyword", they are handled by gexiv2 with the + get_tag_multiple() and set_tag_multiple() functions. + + gimp_metadata_deserialize_text(): when deserializing our XML format, + check if a tag is already set on the metadata as "multiple" and if yes + retrieve it, append the new value and set it again. + + gimp_image_metadata_save_finish(): take care of "multiple" values when + copying tags to new metadata created for saving. + + This should preserve all values across an "import, edit, export". + + Thing will still break when using the metadata editor, it doesn't + handle multiple values at all, but that code is very hard to + understand. + + (cherry picked from commit d708ac0b214b9bec72e2038e5e7835fc72e5c884) + + libgimp/gimpimagemetadata.c | 49 + ++++++++++++++++++++++++++++++--------------- + libgimpbase/gimpmetadata.c | 48 + ++++++++++++++++++++++++++++++++++---------- + 2 files changed, 70 insertions(+), 27 deletions(-) + +commit fbf73bee70beaee74ce3bf2f4731ee5c3980008b +Author: Ell +Date: Sun Jan 6 07:06:49 2019 -0500 + + app: remove gimp-scratch; replace with gegl-scratch + + The scratch allocator has been moved to GEGL (commit + gegl@b99032d799dda3436ffa8c1cc28f8b0d34fb965d). Remove gimp- + scratch, and replace all its uses with gegl-scratch. + + (cherry picked from commit 889e2e26eecc3ad4daffc6e05e86c18fd1e07895) + + app/Makefile.am | 3 +- + app/config/Makefile.am | 3 +- + app/core/Makefile.am | 2 - + app/core/gimp-scratch.c | 100 ------------- + app/core/gimp-scratch.h | 166 + --------------------- + .../layer-modes/gimpoperationlayermode-blend.c | 6 +- + app/tests/Makefile.am | 3 +- + app/widgets/gimpdashboard.c | 5 +- + 8 files changed, 7 insertions(+), 281 deletions(-) + +commit f82eab6c95339955bba1b3c4fe3a68e1756d137e +Author: Alexandre Prokoudine +Date: Sun Jan 6 04:44:59 2019 +0300 + + Update NEWS + + NEWS | 37 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 36 insertions(+), 1 deletion(-) + +commit 68b53d84f8d3bbf0d5f1c3112e6883ffc53bb551 +Author: Alexandre Prokoudine +Date: Sun Jan 6 04:16:19 2019 +0300 + + Update Russian translation + + po-plug-ins/ru.po | 558 ++++++++------ + po/ru.po | 2182 + ++++++++++++++++++++++++++++------------------------- + 2 files changed, 1498 insertions(+), 1242 deletions(-) + +commit c924d60ea61e7b78e830d5bee6ed3282a3585b24 +Author: Michael Natterer +Date: Sat Jan 5 14:56:24 2019 +0100 + + Issue #1793 - Move Guillotine tool to a different position in menus... + + ...(and rename it) + + Move it into the "Crop" group of the image menu and call it "Slice + Using Guides". + + (cherry picked from commit 5fffadba54acc356bafb0683a6c322ee514519bd) + + plug-ins/common/guillotine.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 30d5fb48519a0d3ab9e19e96d555c3d61fb50ae6 +Author: Michael Natterer +Date: Sat Jan 5 14:16:55 2019 +0100 + + Issue #2260 - "Selection to Path Advanced Settings" dialog not + scrollable + + Put the entire dialog content into a scrolled window. + + (cherry picked from commit 99764000be445e76a1e4ba41434d392ab9446ae2) + + .../selection-to-path/selection-to-path-dialog.c | 20 + +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +commit c6bf7b04f79a0e50ada932abf721390a4bd5d7bd +Author: Snehalata B Shirude +Date: Sat Jan 5 08:32:24 2019 +0000 + + Update Marathi translation + + po-plug-ins/mr.po | 459 + +++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 280 insertions(+), 179 deletions(-) + +commit 25983b8280785a997080c0f2a2877130bd700a4f +Author: Michael Natterer +Date: Sat Jan 5 02:57:23 2019 +0100 + + Issue #2751 - Some Translated sentences appear as Untranslated + + Pass the translation context to gimp_action_group_add_*_actions() to + make action translations work. + + (cherry picked from commit 55219ffaf19029107d7b3463ad34738bbaa0c3b7) + + app/actions/context-actions.c | 66 + +++++++++++++++++++++---------------------- + 1 file changed, 33 insertions(+), 33 deletions(-) + +commit 73597b4efda51ad4fb0bed69b7eb253c4178bf57 +Author: Ryuta Fujii +Date: Fri Jan 4 21:32:06 2019 +0000 + + Update Japanese translation + + po/ja.po | 11093 + ++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 6149 insertions(+), 4944 deletions(-) + +commit 7fa5fbab94b958552b108992ad5df1663976afc6 +Author: Ell +Date: Fri Jan 4 15:30:25 2019 -0500 + + app: small fix to last commit + + (cherry picked from commit d39ac2a3f0a4ad57b07e05f0fc163293f030a365) + + app/widgets/gimphistogrameditor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit a299270f8a45b896a47af92ee8ccc723609fc783 +Author: Ell +Date: Fri Jan 4 15:13:43 2019 -0500 + + app: in GimpHistogramEditor, don't block when creating bg histogram + + In GimpHistogramEditor, when the drawable preview is frozen, don't + duplicate the main histogram as the bg histogram if calculation is + still ongoing, since this will block until histogram calculation is + complete. In particular, this creates a noticeable stall when + beginning a paint stroke while the histogram is being calculated. + + Instead, defer the creation of the bg histogram to the completion + of the calculation of the main histogram. + + (cherry picked from commit 471efee769555a04782f7f9fe544b256b229753a) + + app/widgets/gimphistogrameditor.c | 69 + ++++++++++++++++++++++++++++++++++----- + app/widgets/gimphistogrameditor.h | 5 ++- + 2 files changed, 65 insertions(+), 9 deletions(-) + +commit f08065b8c6cff7409efca992bf2ed817ab5dcd4b +Author: Ell +Date: Fri Jan 4 13:01:37 2019 -0500 + + app: write floating-point vars using C locale in performance logs + + When writing floating-point instrumentation variables in + performance logs, always use the C locale, rather than the current + locale. + + (cherry picked from commit 626208b17cc23158a5251bd43aa638e75a98fbe8) + + app/widgets/gimpdashboard.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +commit adbac2f6804d6b6be018e8dfe2958734d13d6d54 +Author: Michael Natterer +Date: Fri Jan 4 18:11:12 2019 +0100 + + Issue #2446 - DPI information is not stored in clipboard + + Set the "x-dpi" and "y-dpi" options on the GdkPixbuf set on the + clipboard. There is not much more we can do, getting that value across + the clipboard is out of our control, but at least we set the vlaues + now. + + (cherry picked from commit a0263f40d0824be031b3ddb450eb8009ad864b26) + + app/widgets/gimpclipboard.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +commit 8dee1276f1ff121324c5af87f2f0e450ccfaa8ca +Author: Michael Natterer +Date: Fri Jan 4 15:55:47 2019 +0100 + + pdb: deprecate gimp_get_theme_dir() and gimp_get_icon_theme_dir() + + They are unused in 2.10 and master, and we do this via the plug-in + config message anyway. + + app/pdb/gimprc-cmds.c | 12 ++++++++---- + libgimp/gimpgimprc_pdb.c | 4 ++++ + libgimp/gimpgimprc_pdb.h | 2 ++ + pdb/groups/gimprc.pdb | 2 ++ + 4 files changed, 16 insertions(+), 4 deletions(-) + +commit 490d565b57d934724c5a60f2def0d9f7b0bcb674 +Author: Michael Natterer +Date: Fri Jan 4 14:36:10 2019 +0100 + + Issue #2136 - On-image UI for adjusting Linear Motion Blur + + Add on-canvas GUI (simple lines) for circular, linear and zoom motion + blur. The restrictions in the interaction show pretty well that there + is room for improvement here, the line is just a bit too generic, but + it's better than nothing. + + (cherry picked from commit 39406b83d3a0dc6ccbc5f041b81423469f045371) + + app/propgui/Makefile.am | 6 + + 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.c | 9 ++ + po/POTFILES.in | 3 + + 9 files changed, 565 insertions(+) + +commit 5f00ee2a7d1107fdd75ab2eb7a7d30ec7c913db9 +Author: Ell +Date: Fri Jan 4 08:10:55 2019 -0500 + + app: in GimpHistogramEditor, cancel ongoing async upon update + + In gimp_histogram_editor_update(), cancel any ongoing histogram- + calculation async before restarting the idle source. The async + will have been canceled anyway when recalculating the histogram + once the idle source is run, but we can cancel it as soon as we + know the histogram is outdated. + + (cherry picked from commit 5561da87e6f17336b91aa59c39ebae97d8967838) + + app/widgets/gimphistogrameditor.c | 7 +++++++ + app/widgets/gimphistogrameditor.h | 1 + + 2 files changed, 8 insertions(+) + +commit 8142fe4d1c15a2464bb0822c40f83b04ff2d6088 +Author: Ell +Date: Fri Jan 4 07:49:50 2019 -0500 + + app: In GimpHistogram, align copied buffer region to tile rect + + In gimp_histogram_calculate_async(), align the copied region of the + drawable and mask buffers to the tile grid, so that all copied + tiles are COWed. + + (cherry picked from commit d56d663eaaf3849d574efceaa08cf225bea603ed) + + app/core/gimphistogram.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +commit 23cc540f62ec9d5852b14979fc338905c21cf90b +Author: Michael Natterer +Date: Thu Jan 3 22:19:48 2019 +0100 + + Issue #2540 - block width / height slider of the pixelize filter + don't... + + ...have the same scaling + + gimp_prop_widget_new_from_pspec(): when restricting the scale to the + actual op area for pixel-coordinate and pixel-distance properties, + only use the max value in the axis direction for pixel-coordinate; for + pixel-distance make sure we use the same value on both axes, simply + use MAX (area.width, area.height). + + (cherry picked from commit 0a1ecdf4ee16b91915b8e4f4611246edcbc99103) + + app/propgui/gimppropgui.c | 48 + +++++++++++++++++++++++++++-------------------- + 1 file changed, 28 insertions(+), 20 deletions(-) + +commit 75682c165054c1e66babf0370f7e2a0ac14ec60a +Author: Michael Natterer +Date: Thu Jan 3 16:46:00 2019 +0100 + + Issue #1538 - Crash when adding file to already opened image and... + + ...closing this image while the file is being loaded + + Ref the image around all calls to file_open_layers() and + gimp_image_add_layers() so it stays around even if the user closes the + display in the meantime. + + (cherry picked from commit fc4add7c2b1e91d3721db6cf068cea059aacc19b) + + app/dialogs/file-open-dialog.c | 20 ++++++++++++++++--- + app/display/gimpdisplayshell-dnd.c | 39 + ++++++++++++++++++++++++++------------ + app/widgets/gimplayertreeview.c | 4 ++++ + 3 files changed, 48 insertions(+), 15 deletions(-) + +commit 4a294f6d6a22ce840388f5dc3ba8429191539b2d +Author: Michael Natterer +Date: Thu Jan 3 15:20:33 2019 +0100 + + tools: fix perl warning about unescaped left brace in gimp-mkenums + + Simply use "\{" instead of "{" for matching a literal "{". + + tools/gimp-mkenums | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 84942d2614d31e02863a270a7ae8830a3deeece9 +Author: Michael Natterer +Date: Thu Jan 3 15:07:26 2019 +0100 + + AUTHORS: regenerated + + AUTHORS | 1 - + 1 file changed, 1 deletion(-) + +commit 4572da549188fe9021eeb1a7f28aeeeb0b14dc0e +Author: Michael Natterer +Date: Thu Jan 3 15:02:55 2019 +0100 + + Issue #2685 - Crash when distributing layers horizontally + + Fix xcf-load.c to correct out-of-range item offsets (simply set them + to 0), so XCF files that are broken this way can still be recovered. + + This doesn't fix the original bug, just recovering the crash images. + + (cherry picked from commit b9265e7cde21cd6416dd18f338b87a7a66e21006) + + app/xcf/xcf-load.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +commit 422486409adabce8ff03f4bc83a8c510623075f5 +Author: gaaned92 +Date: Sat Dec 29 22:56:27 2018 +0100 + + Issue #2716 - Windows: Rawtherapee plugin cannot be installed + + On Windows, prevent RawTherapee from opening a console window + + (cherry picked from commit 9e82ace409a477cc29f6c66b14f00ffe4949f867) + + plug-ins/file-raw/file-rawtherapee.c | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 3117eb799b752d02827dce0bb3ecfa9c5b3ed4db +Author: Snehalata B Shirude +Date: Thu Jan 3 08:04:14 2019 +0000 + + Update Marathi translation + + po-libgimp/mr.po | 400 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 207 insertions(+), 193 deletions(-) + +commit 6c0cc5e0423da95166517574ab15d4c6f1972412 +Author: Jehan +Date: Thu Jan 3 00:18:19 2019 +0100 + + authors.xml: my name was in duplicate. + + Leaving the one without the surname. My surname is not a secret + at all, + but I really don't consider it necessary anyway. + Well if some day, another dev came in with the same first name, + it will + still be time to update this file. :-) + + (cherry picked from commit be0d41e1ad0c85a072c95302dc2d320f20760ffc) + + authors.xml | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +commit 582caf5a4a845798f43fb903cc3ce2d9d7a35f33 +Author: Jehan +Date: Wed Jan 2 22:51:31 2019 +0100 + + Issue #2611: Cannot open certain .pdf files in GIMP. + + I am unsure of the problem, but it is fixed by using + poppler_document_new_from_gfile() instead of giving the contents of a + GMappedFile to poppler_document_new_from_data(). + Using GFile is anyway usually prefered so I don't dig up more and just + make this change. + + (cherry picked from commit a89e50305468f498f27d0b98948cb22b1495f5f6) + + plug-ins/common/file-pdf-load.c | 27 +++++++-------------------- + 1 file changed, 7 insertions(+), 20 deletions(-) + +commit d75a4615bda54e15b534a4ea74eaa2a22d17dec9 +Author: Jehan +Date: Wed Jan 2 21:32:17 2019 +0100 + + plug-ins: a lot of coding style cleanup in file-dds. + + Still a lot more to do but pushing WIP to not have it wasted if more + commits come in from others. + Also got rid of some global variables. + + (cherry picked from commit ff2d22d915b9a94c491bde33fa2fa14f8cb740e2) + + plug-ins/file-dds/dds.c | 450 +++--- + plug-ins/file-dds/dds.h | 40 +- + plug-ins/file-dds/ddsplugin.h | 93 +- + plug-ins/file-dds/ddsread.c | 2171 +++++++++++++------------- + plug-ins/file-dds/ddswrite.c | 3374 + +++++++++++++++++++++-------------------- + 5 files changed, 3143 insertions(+), 2985 deletions(-) + +commit ffbe7282be9a5278adbf31d19e6e510f52080fc4 +Author: Michael Natterer +Date: Wed Jan 2 21:25:07 2019 +0100 + + Issue #2617 - Export to C image artifacts + + As correctly spotted by Royce Pipkins, the buffer for the drawable's + pixel lines was too small. + + Also fix the plug-in to hardcode "R'G'B'[A] u8" so it won't misbehave + on high bit-depth images, and make it work on all sorts of drawables, + not only "RGB*" (it will still always export RGB images). + + (cherry picked from commit 74c9d835e8bd28f31663a41fc0e668dc9da7483d) + + plug-ins/common/file-csource.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +commit fb36495f1e24be0037205263b6a95a67fb5a580e +Author: Jehan +Date: Wed Jan 2 20:46:44 2019 +0100 + + po-python: add the new file-dds plug-in to POTFILES.skip. + + Thanks to Piotr Drąg for noting it. + + (cherry picked from commit e20930e1ff1d3ac351ec721fc09d8fe21d6cd0ad) + + po-python/POTFILES.skip | 1 + + 1 file changed, 1 insertion(+) + +commit 188c82ddc8d7cba1a9bbb8ecc427c8d4e68a774d +Author: Jehan +Date: Wed Jan 2 20:39:50 2019 +0100 + + plug-ins: "Export as DDS" is translatable. + + Thanks to Piotr Drąg for raising my overlooking this. + + (cherry picked from commit d1de204dbc63a046dcdf8bc53964dc0cc861a5c8) + + plug-ins/file-dds/ddswrite.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit c45362c57d2a4865bb4f992c3be56c01d4891173 +Author: Michael Natterer +Date: Wed Jan 2 20:14:30 2019 +0100 + + po-plug-ins, po-script-fu: update for the DDS plug-in + + (cherry picked from commit 11ed7ee68b81996ac24bcaa425e73359fe7a57da) + + po-plug-ins/POTFILES.in | 1 + + po-script-fu/POTFILES.skip | 1 + + 2 files changed, 2 insertions(+) + +commit c8b663e1b3a0518464cc2efd3df553d641131642 +Author: Jehan +Date: Wed Jan 2 19:34:40 2019 +0100 + + plug-ins: get rid of "Show this dialog" checkbox in file-dds. + + None of our load plug-ins have such a checkbox, so this is not + consistent. Moreover one you uncheck it, you just can't get back the + dialog until next GIMP restart. That's very bad usability. + + (cherry picked from commit 948608e6586b0a8ff6fa341ead3da5e02d0f5a1b) + + plug-ins/file-dds/dds.c | 3 +-- + plug-ins/file-dds/ddsplugin.h | 1 - + plug-ins/file-dds/ddsread.c | 9 +-------- + 3 files changed, 2 insertions(+), 11 deletions(-) + +commit 80667aa73f2b15a2f8b1fe0d7db3d2418b698121 +Author: Jehan +Date: Wed Jan 2 19:14:30 2019 +0100 + + plug-ins: s/Save as DDS/Export as DDS/ + + Let's make the GUI title consistent with our naming. + + (cherry picked from commit 7e68aa37c1d2601c8abaf2fe046f2ea7c5fa03f7) + + plug-ins/file-dds/ddswrite.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 4c4c54af2dffd6d01a8fc456118dd417856f99aa +Author: Jehan +Date: Wed Nov 14 15:25:00 2018 +0100 + + plug-ins: fix a few more warnings. + + There are still a bunch of warnings, especially regarding usage of + GtkTable, GtkMisc and some unused functions. + + (cherry picked from commit 8f49fad3dac9199efe1e5e9c6a66a128bb8c3dbe) + + plug-ins/file-dds/ddswrite.c | 18 +++++++++--------- + plug-ins/file-dds/mipmap.c | 20 +++++++++----------- + 2 files changed, 18 insertions(+), 20 deletions(-) + +commit a6385fa09671727d84e70adb2e2ba637be5b8e04 +Author: Jehan +Date: Wed Nov 14 15:06:54 2018 +0100 + + plug-ins: make the file-dds plug-in load correctly. + + This commit fixes the following error: + + > attempted to install a procedure with a full menu path as menu + label, + > this is not supported any longer. + + Instead, if we want new items in menu, we should use + gimp_plugin_menu_register(). I add the calls, yet comment them out, + since I don't think we want these various conversion calls into the + provided filters. The proper way for data conversion should be through + babl/GEGL. + + (cherry picked from commit 67a80c8667e3e3c12011682dec634669fc8a8d86) + + plug-ins/file-dds/dds.c | 86 + +++++++++++++++++++++++++------------------------ + 1 file changed, 44 insertions(+), 42 deletions(-) + +commit 4c8f8f45c054b081d5607154698863e75b220f6e +Author: Jehan +Date: Wed Nov 14 14:56:15 2018 +0100 + + plug-ins: make file-dds compile. + + This is still very broken and it doesn't load well. But at least it + compiles! + All build errors (and a few warnings though not all) fixed. + + (cherry picked from commit df8a20956bab70297cbb0f3fcfa8d4b9f18c06fd) + + plug-ins/file-dds/dds.c | 10 +++++- + plug-ins/file-dds/ddsread.c | 39 ++++++++++++---------- + plug-ins/file-dds/ddswrite.c | 77 + +++++++++++++++++++++++--------------------- + plug-ins/file-dds/mktables.c | 6 ++-- + 4 files changed, 76 insertions(+), 56 deletions(-) + +commit 91c6f27cf2bcf6b5e701a8f428ebd6e57cd270f6 +Author: Alexandre Prokoudine +Date: Tue Nov 13 03:07:41 2018 +0300 + + file-dds: mark some user-vsible messages for translation, expose + them to PO files + + Unfortunately, no items of comboboxes are currently translatble + due to the way the code is written. + + (cherry picked from commit 2f3504e06af35efaaaa184c641ea254853042a5b) + + plug-ins/file-dds/ddsread.c | 8 ++++---- + plug-ins/file-dds/ddswrite.c | 32 ++++++++++++++++---------------- + po-plug-ins/POTFILES.in | 2 ++ + 3 files changed, 22 insertions(+), 20 deletions(-) + +commit 994368d68d8942f65553add40bebcebbc8c55c9e +Author: Massimo Valentini +Date: Tue Nov 13 02:44:11 2018 +0300 + + file-dds: support for DXT2/DXT4, initial build and GEGL-related fixes + + (cherry picked from commit d25348ca2473a2efe26824b41daa32319e349e7e) + + plug-ins/file-dds/color.c | 1 + + plug-ins/file-dds/ddsread.c | 26 ++++++++++++++++++++++---- + plug-ins/file-dds/mipmap.c | 4 ++-- + plug-ins/file-dds/misc.c | 13 +++++++------ + 4 files changed, 32 insertions(+), 12 deletions(-) + +commit 369884cf9c55761124038f7b4076a6df8ca23fa1 +Author: Alexandre Prokoudine +Date: Tue Nov 13 02:35:07 2018 +0300 + + file-dds: add original source code of the DDS plug-in + + This is the code from the 'gimp-2.9' branch of the plug-in, + integrated into GIMP's build system. + + (cherry picked from commit 79bc2dc1effa820396e15e79f8ed56ffb0a85933) + + configure.ac | 1 + + plug-ins/Makefile.am | 1 + + plug-ins/file-dds/COPYING | 339 +++++++ + plug-ins/file-dds/LICENSE | 21 + + plug-ins/file-dds/LICENSE.nvtt | 23 + + plug-ins/file-dds/Makefile.am | 65 ++ + plug-ins/file-dds/README | 29 + + plug-ins/file-dds/README.dxt | 175 ++++ + plug-ins/file-dds/TODO | 7 + + plug-ins/file-dds/color.c | 55 ++ + plug-ins/file-dds/color.h | 91 ++ + plug-ins/file-dds/dds.c | 373 +++++++ + plug-ins/file-dds/dds.h | 327 +++++++ + plug-ins/file-dds/ddsplugin.h | 78 ++ + plug-ins/file-dds/ddsread.c | 1235 +++++++++++++++++++++++ + plug-ins/file-dds/ddswrite.c | 2122 + ++++++++++++++++++++++++++++++++++++++++ + plug-ins/file-dds/dxt.c | 1412 ++++++++++++++++++++++++++ + plug-ins/file-dds/dxt.h | 41 + + plug-ins/file-dds/dxt_tables.h | 216 ++++ + plug-ins/file-dds/endian.h | 71 ++ + plug-ins/file-dds/imath.h | 67 ++ + plug-ins/file-dds/mipmap.c | 1013 +++++++++++++++++++ + plug-ins/file-dds/mipmap.h | 47 + + plug-ins/file-dds/misc.c | 252 +++++ + plug-ins/file-dds/misc.h | 30 + + plug-ins/file-dds/mktables.c | 128 +++ + plug-ins/file-dds/vec.h | 226 +++++ + 27 files changed, 8445 insertions(+) + +commit c4e6c3add267d453206b59a5ad946a71fc3ff985 +Author: Michael Natterer +Date: Wed Jan 2 16:34:20 2019 +0100 + + app: wrap some overly long lines in gimpbucketfilltool.c + + and some other pedantic style fixes. + + (cherry picked from commit 292903b78d0e5d85cca790cd821c332b3d20f6b7) + + app/tools/gimpbucketfilltool.c | 62 + +++++++++++++++++++++++++++++------------- + 1 file changed, 43 insertions(+), 19 deletions(-) + +commit fca2e84f4faceafc4fe07c6b28a2c69369d60427 +Author: Michael Natterer +Date: Wed Jan 2 15:47:31 2019 +0100 + + app, libgimpbase: move enum GimpBucketFillArea to the core + + The whole bucket fill specific enum stuff is on its way out, so let's + keep this one out of libgimp for now until we decide how to present + line art filling in the PDB. + + (cherry picked from commit 368f2e596af0fa84d7a107615b711321f2fabee9) + + app/tools/tools-enums.c | 31 +++++++++++++++++++++++++++++++ + app/tools/tools-enums.h | 20 ++++++++++++++++++++ + libgimp/gimpenums.c.tail | 2 -- + libgimpbase/gimpbase.def | 1 - + libgimpbase/gimpbaseenums.c | 32 -------------------------------- + libgimpbase/gimpbaseenums.h | 20 -------------------- + pdb/enums.pl | 10 ---------- + 7 files changed, 51 insertions(+), 65 deletions(-) + +commit e666041d726dae5b5ad47b9f178ec2b139f73cc2 +Author: Ell +Date: Wed Jan 2 07:37:33 2019 -0500 + + app: in GimpTileHandlerValidate, avoid fetching/copying whole tiles + + In GimpTileHandlerValidate, when rendering a whole tile in respone + to a TILE_GET command, use gegl_tile_handler_get_tile() to get the + tile without preserving its data, so that we avoid unnecessarily + fetching the tile from storage, or copying its data during + uncloning. + + (cherry picked from commit 78ed038fcadc4dbe70a36c94556b952c05c8f8ac) + + app/gegl/gimptilehandlervalidate.c | 152 + +++++++++++++++++++------------------ + 1 file changed, 77 insertions(+), 75 deletions(-) + +commit 2b542fca1e6076561853ff651c29019caeb8a7ed +Author: Snehalata B Shirude +Date: Wed Jan 2 08:59:31 2019 +0000 + + Update Marathi translation + + po-plug-ins/mr.po | 1622 + ++++++++++++++++++++++++++--------------------------- + 1 file changed, 804 insertions(+), 818 deletions(-) + +commit a27f7c77244ea2b3dc89672768c882f251dfc9f7 +Author: Michael Natterer +Date: Wed Jan 2 01:45:41 2019 +0100 + + Issue #1788 - Inconsistency between FG color and selected color in... + + ...palette views despite selected color being in the currently + selected pallette + + As suggested by Massimo, changing the color comparison EPSILON in + gimppalette.c from 1e-10 to 1e-6 fixes this, and is really small + enough. + + Also, generally clean up color comparison epsilons: + + - use a #define, not hardcoded values for all uses of + gimp_rgb[a]_distance() + - call the #defines RGB_EPSILON and RGBA_EPSILON + - make them all 1e-6 or larger + + (cherry picked from commit abd7cbfc8ddc24b0b430cda99c777a91b0531dae) + + app/actions/channels-commands.c | 17 ++++++++++------- + app/actions/quick-mask-commands.c | 5 ++++- + app/core/gimpchannel.c | 4 +++- + app/core/gimpcontext.c | 7 ++++--- + app/core/gimppalette.c | 12 +++++++----- + app/core/gimppalettemru.c | 6 ++++-- + app/widgets/gimpcolorframe.c | 4 +++- + app/widgets/gimpcolormapeditor.c | 8 ++++---- + app/widgets/gimpcolorpanel.c | 4 +++- + app/widgets/gimpviewrenderer.c | 4 +++- + libgimpwidgets/gimpcolorarea.c | 3 ++- + 11 files changed, 47 insertions(+), 27 deletions(-) + +commit d6ca6d510e845ecf6eb004e343ce33da853bc356 +Author: Michael Natterer +Date: Wed Jan 2 00:14:35 2019 +0100 + + Issue #2667 - When copying a text layer, paste should create a new... + + ...text layer, not an image of the text + + In gimp_edit_paste_get_layer(), when pasting as floating selection, + collapse the pasted layer into an ordinary layer only if it's a group + layer. There is nothing that speaks against having a floating text + layer, it works just fine. + + (cherry picked from commit 8a4aacb52f52b11bedd879ea03271b7ca540099d) + + app/core/gimp-edit.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +commit d7b9cb4e71092c242bde3b44e1794f5434340ab9 +Author: Michael Natterer +Date: Tue Jan 1 20:26:07 2019 +0100 + + libgimp: use G_N_ELEMENTS() in gimp_image_metadata_save_finish() + + intead of hardcoding the array length. + + (cherry picked from commit d8732909151a3c21d23eadc6d7872297e9cdb14c) + + libgimp/gimpimagemetadata.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +commit 0f4cbb070f9a691c6132e04a6adb9f1c3b006675 +Author: Michael Natterer +Date: Tue Jan 1 19:21:52 2019 +0100 + + configure.ac: require babl >= 0.1.61 + + (cherry picked from commit b24cc6e7fae43f0236c167903a5fa4c7dfc9d18a) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit d90fd01b32ffcfaa87b3f8b9525340bb49957949 +Author: Ell +Date: Tue Jan 1 09:10:06 2019 -0500 + + app: show message when attempting to change layers while there is + a floating sel. + + In GimpDrawableTreeView, show an error message when attempting to + select a different drawable while the image has an active floating + selection. In GimpLayerTreeView, also blink the editor button-row + when this happens, as a hint that the floating selection can be + committed/canceled through the buttons (we already highlight the + relevant ones.) + + (cherry picked from commit 070e10eda77a868bfdde4457fe86f39178b2e18e) + + app/widgets/gimpdrawabletreeview.c | 18 +++++++++++++++--- + app/widgets/gimplayertreeview.c | 10 ++++++++++ + 2 files changed, 25 insertions(+), 3 deletions(-) + +commit 2114d63c1816a0fa50aedafa03b014b13cac607e +Author: Ell +Date: Tue Jan 1 07:28:36 2019 -0500 + + plug-ins: in image-map, fix spin-scale adjustment + + Fix deprecated use of non-zero page size for a spin-button + GtkAdjustment, and increase the page increment. + + (cherry picked from commit 192bc9536c01770734a8c30e191c5ff93160ac83) + + plug-ins/imagemap/imap_table.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 563036eab671fcd0f8b2ff5a8a91afefe880212f +Author: Ell +Date: Tue Jan 1 06:51:45 2019 -0500 + + app: more optimizations to gimp_gegl_mask_bounds() + + Another inner-loop logic improvement. + + (cherry picked from commit 3351174ebeab7446835ab68572d4d952a1cc810a) + + app/gegl/gimp-gegl-mask.c | 93 + +++++++++++++++++++++++++---------------------- + 1 file changed, 49 insertions(+), 44 deletions(-) + +commit ae54ed2d6803cc6e5c7ef15ae76660fdc47e2ac4 +Author: Ell +Date: Tue Jan 1 06:29:22 2019 -0500 + + app: optimize gimp_gegl_mask_bounds() + + ... by using the mask's native format, and improving the inner-loop + logic. + + (cherry picked from commit 819a5352f68696236890c5540518a8b210feb35d) + + app/gegl/gimp-gegl-mask.c | 136 + +++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 110 insertions(+), 26 deletions(-) + +commit 5807ca820bce8d6c9acbb114199d9d28e44271da +Author: Ell +Date: Tue Jan 1 06:28:12 2019 -0500 + + app: optimize gimp_gegl_mask_is_empty() + + ... by using the mask's native format, and gegl_memeq_zero(). + + (cherry picked from commit 986a298a07fdcd2c1fb3d5fcb5a967920ed9944d) + + app/gegl/gimp-gegl-mask.c | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +commit dd2a4cd97f6056f6f4fcc8eb27093fc51edb5389 +Author: Anders Jonsson +Date: Mon Dec 31 23:40:22 2018 +0000 + + Update Swedish translation + + po-libgimp/sv.po | 479 + ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 247 insertions(+), 232 deletions(-) + +commit 25e119754991fd3dd966936fa7f7085c2bb8fcc7 +Author: Michael Natterer +Date: Mon Dec 31 22:22:25 2018 +0100 + + cursors: add -HiDPI variants of all cursors + + This includes gimp-tool-cursors-x2.xcf and cursorname-x2.png files, + they are simply 72x72 upscaled versions of the 36x36 fies with no + changes. + + Anyone can now improve on cursors without having to deal with the + bulid process. + + (cherry picked from commit ad5bf1dee7847a3f4af095efe5dafdc45166f2ae) + + cursors/Makefile.am | 88 + ++++++++++++++++++++++++++++-- + cursors/cursor-bad-x2.png | Bin 0 -> 626 bytes + cursors/cursor-color-picker-x2.png | Bin 0 -> 462 bytes + cursors/cursor-corner-bottom-left-x2.png | Bin 0 -> 324 bytes + cursors/cursor-corner-bottom-right-x2.png | Bin 0 -> 258 bytes + cursors/cursor-corner-bottom-x2.png | Bin 0 -> 315 bytes + cursors/cursor-corner-left-x2.png | Bin 0 -> 309 bytes + cursors/cursor-corner-right-x2.png | Bin 0 -> 310 bytes + cursors/cursor-corner-top-left-x2.png | Bin 0 -> 427 bytes + cursors/cursor-corner-top-right-x2.png | Bin 0 -> 352 bytes + cursors/cursor-corner-top-x2.png | Bin 0 -> 311 bytes + cursors/cursor-crosshair-small-x2.png | Bin 0 -> 269 bytes + cursors/cursor-crosshair-x2.png | Bin 0 -> 290 bytes + cursors/cursor-mouse-x2.png | Bin 0 -> 415 bytes + cursors/cursor-move-x2.png | Bin 0 -> 395 bytes + cursors/cursor-none-x2.png | Bin 0 -> 100 bytes + cursors/cursor-side-bottom-left-x2.png | Bin 0 -> 471 bytes + cursors/cursor-side-bottom-right-x2.png | Bin 0 -> 450 bytes + cursors/cursor-side-bottom-x2.png | Bin 0 -> 287 bytes + cursors/cursor-side-left-x2.png | Bin 0 -> 332 bytes + cursors/cursor-side-right-x2.png | Bin 0 -> 335 bytes + cursors/cursor-side-top-left-x2.png | Bin 0 -> 446 bytes + cursors/cursor-side-top-right-x2.png | Bin 0 -> 463 bytes + cursors/cursor-side-top-x2.png | Bin 0 -> 293 bytes + cursors/cursor-zoom-x2.png | Bin 0 -> 895 bytes + cursors/gimp-tool-cursors-x2.xcf | Bin 0 -> 167642 bytes + cursors/modifier-anchor-x2.png | Bin 0 -> 274 bytes + cursors/modifier-background-x2.png | Bin 0 -> 189 bytes + cursors/modifier-bad-x2.png | Bin 0 -> 223 bytes + cursors/modifier-control-x2.png | Bin 0 -> 179 bytes + cursors/modifier-foreground-x2.png | Bin 0 -> 195 bytes + cursors/modifier-intersect-x2.png | Bin 0 -> 168 bytes + cursors/modifier-join-x2.png | Bin 0 -> 184 bytes + cursors/modifier-minus-x2.png | Bin 0 -> 140 bytes + cursors/modifier-move-x2.png | Bin 0 -> 201 bytes + cursors/modifier-pattern-x2.png | Bin 0 -> 188 bytes + cursors/modifier-plus-x2.png | Bin 0 -> 176 bytes + cursors/modifier-resize-x2.png | Bin 0 -> 214 bytes + cursors/modifier-rotate-x2.png | Bin 0 -> 213 bytes + cursors/modifier-select-x2.png | Bin 0 -> 211 bytes + cursors/modifier-zoom-x2.png | Bin 0 -> 192 bytes + cursors/tool-airbrush-x2.png | Bin 0 -> 627 bytes + cursors/tool-blur-x2.png | Bin 0 -> 908 bytes + cursors/tool-bucket-fill-x2.png | Bin 0 -> 824 bytes + cursors/tool-burn-x2.png | Bin 0 -> 500 bytes + cursors/tool-clone-x2.png | Bin 0 -> 629 bytes + cursors/tool-color-picker-x2.png | Bin 0 -> 460 bytes + cursors/tool-crop-x2.png | Bin 0 -> 565 bytes + cursors/tool-dodge-x2.png | Bin 0 -> 766 bytes + cursors/tool-ellipse-select-x2.png | Bin 0 -> 363 bytes + cursors/tool-eraser-x2.png | Bin 0 -> 645 bytes + cursors/tool-flip-horizontal-x2.png | Bin 0 -> 360 bytes + cursors/tool-flip-vertical-x2.png | Bin 0 -> 416 bytes + cursors/tool-free-select-x2.png | Bin 0 -> 883 bytes + cursors/tool-fuzzy-select-x2.png | Bin 0 -> 892 bytes + cursors/tool-gradient-x2.png | Bin 0 -> 455 bytes + cursors/tool-hand-x2.png | Bin 0 -> 653 bytes + cursors/tool-heal-x2.png | Bin 0 -> 1089 bytes + cursors/tool-ink-x2.png | Bin 0 -> 902 bytes + cursors/tool-iscissors-x2.png | Bin 0 -> 838 bytes + cursors/tool-measure-x2.png | Bin 0 -> 716 bytes + cursors/tool-move-x2.png | Bin 0 -> 391 bytes + cursors/tool-paintbrush-x2.png | Bin 0 -> 794 bytes + cursors/tool-paths-anchor-x2.png | Bin 0 -> 809 bytes + cursors/tool-paths-control-x2.png | Bin 0 -> 714 bytes + cursors/tool-paths-old-x2.png | Bin 0 -> 692 bytes + cursors/tool-paths-segment-x2.png | Bin 0 -> 1124 bytes + cursors/tool-paths-x2.png | Bin 0 -> 650 bytes + cursors/tool-pencil-x2.png | Bin 0 -> 574 bytes + cursors/tool-perspective-x2.png | Bin 0 -> 595 bytes + cursors/tool-polygon-select-x2.png | Bin 0 -> 395 bytes + cursors/tool-rect-select-x2.png | Bin 0 -> 199 bytes + cursors/tool-resize-x2.png | Bin 0 -> 522 bytes + cursors/tool-rotate-x2.png | Bin 0 -> 633 bytes + cursors/tool-shear-x2.png | Bin 0 -> 514 bytes + cursors/tool-smudge-x2.png | Bin 0 -> 804 bytes + cursors/tool-text-x2.png | Bin 0 -> 328 bytes + cursors/tool-warp-x2.png | Bin 0 -> 710 bytes + cursors/tool-zoom-x2.png | Bin 0 -> 785 bytes + 79 files changed, 83 insertions(+), 5 deletions(-) + +commit 1c43c6f98939b536a034e8b9301f5a49bb311fbb +Author: Michael Natterer +Date: Mon Dec 31 19:11:22 2018 +0100 + + app: fix last gimpcursor.c commit: g_printerr() needs a newline + + (cherry picked from commit 20225a22687869b6ee0ba66673cce1fc1c4395a0) + + app/widgets/gimpcursor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 8ed8def107fccf540d406514cb640266cc7ba490 +Author: Michael Natterer +Date: Mon Dec 31 16:10:46 2018 +0100 + + app: support cursors with a scale factor of 2 for HiDPI + + If the scale factor is 2 or larger, look for cursor images named + "filename-x2.png" and use them instead. Guess HiDPI based on the + monitor resolution being > 250 ppi, ugly but so what. + + Manually scale up the default cursor if there is no "x2" image, using + NEAREST interpolation, which looks better than smooth scaling on HiDPI + monitors. + + Next: adding better HiDPI cursor images. + + (cherry picked from commit 8ff8f1d44266142683e754efa9473ab5d049e92c) + + app/widgets/gimpcursor.c | 323 + ++++++++++++++++++++++++++++------------------- + 1 file changed, 192 insertions(+), 131 deletions(-) + +commit 1706950400fc003750c99ac0e34803232965a288 +Author: Michael Natterer +Date: Mon Dec 31 14:04:04 2018 +0100 + + app: pass a GdkWindow not GdkDisplay to gimp_cursor_new() + + Preparation for adding HiDPI cursors. + + (cherry picked from commit 47ff7e1467fb69fbf39fc3b3ef3752ac869599fd) + + app/widgets/gimpcursor.c | 16 +++++++++++----- + app/widgets/gimpcursor.h | 2 +- + app/widgets/gimpdialogfactory.c | 37 + +++++++++++++++---------------------- + 3 files changed, 27 insertions(+), 28 deletions(-) + +commit e61188e666a24dc51b7de1d6be59c8dce68156ba +Author: Ell +Date: Mon Dec 31 05:33:01 2018 -0500 + + app: small fix to gimp_drawable_edit_fill() + + (cherry picked from commit 3738ff3ffdd2eae07275c1d7f5eebdd34713b587) + + app/core/gimpdrawable-edit.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 480d2aa588715ac62fe1fedf95cfed09f71ae782 +Author: Ell +Date: Mon Dec 31 04:37:50 2018 -0500 + + app: show error on attempt to clear/cut layer with locked alpha + channel + + Show an error message, and blink the layers-dialog lock-box, when + attempting to clear/cut a layer with a locked alpha channel. + + (cherry picked from commit e967e5fa9efa77af1886416dc6097242478b985e) + + app/actions/edit-commands.c | 59 + ++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 47 insertions(+), 12 deletions(-) + +commit 5d905d1d653affc4e23d7c362b3b3a72d79002c9 +Author: Ell +Date: Mon Dec 31 04:26:25 2018 -0500 + + app: in gimp_drawable_edit_fill(), make trivial alpha-only fill a NOP + + In gimp_drawable_edit_fill(), if the fill only affects the alpha + channel, and if the drawable has no alpha channel, or the alpha + channel is masked out, do nothing, instead of unnecessarily + performing the fill, which has no effect. + + (cherry picked from commit 6384ff01b62f1371759bb40bf68ed648f6f34bdc) + + app/core/gimpdrawable-edit.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +commit 38e89da69705963eeae548eb2059576ca1e0b692 +Author: Snehalata B Shirude +Date: Mon Dec 31 08:35:56 2018 +0000 + + Update Marathi translation + + po-script-fu/mr.po | 105 + +++++++++++++++++++++++++++++++++-------------------- + 1 file changed, 65 insertions(+), 40 deletions(-) + +commit 5198681493ae742bc7ae883d649f90bbd16ed150 +Author: Anders Jonsson +Date: Sun Dec 30 21:07:08 2018 +0000 + + Update Swedish translation + + po-script-fu/sv.po | 280 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 151 insertions(+), 129 deletions(-) + +commit 90a8819072fb4dd9549014f0e52d9bde1940009a +Author: Anders Jonsson +Date: Sun Dec 30 20:45:40 2018 +0000 + + Update Swedish translation + + po-plug-ins/sv.po | 1000 + +++++++++++++++++++++++++++-------------------------- + 1 file changed, 509 insertions(+), 491 deletions(-) + +commit cf4889548e2d7ae083dc33ee83d129f0b0e154d9 +Author: Marco Ciampa +Date: Sun Dec 30 19:01:08 2018 +0100 + + Updated Italian translation + + po/it.po | 361 + +++++++++++++++++++++++++++++---------------------------------- + 1 file changed, 168 insertions(+), 193 deletions(-) + +commit 9058e4bd75eae10320b5f18aba93fb117bbba908 +Author: Ell +Date: Sun Dec 30 07:47:47 2018 -0500 + + app: in gimp_gegl_apply_cached_operation(), intersect cached rects + with dest rect + + In gimp_gegl_apply_cached_operation(), intersect the cached rects + with the dest rect, so that we don't unnecessarily (if not + erroneously) copy cached regions outside the dest rect to the dest + buffer. This can happen when the op's applicator crop-rect changes + dynamically. + + (cherry picked from commit d9b4ffe09e055ee5109e104605c060ce09c77fa0) + + app/gegl/gimp-gegl-apply-operation.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +commit f8a5c58d41c43b75d059ae836359bb15348fbeec +Author: Ell +Date: Sun Dec 30 05:10:28 2018 -0500 + + app: in warp tool, crop filter to stroke bounds + + In the warp tool, set the drawable-filter's crop area to the + combined stroke bounds, so that, when comitting the tool, only this + area is processed, instead of the entire drawable area. + + (cherry picked from commit e06c4643dcf555f2308bee70bf1991fa028b24b4) + + app/tools/gimpwarptool.c | 97 + ++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 81 insertions(+), 16 deletions(-) + +commit a330a94b944c7ad72655fcc027c8c503b0932637 +Author: Ell +Date: Sun Dec 30 05:04:58 2018 -0500 + + app: in GimpDrawableFilter, only update crop/preview area when + updating whole drawable + + In GimpDrawableFilter, when updating the entire filter area, only + update the crop/preview area (as set by + gimp_drawable_filter_set_{crop,preview}()), instead of the entire + drawable. + + (cherry picked from commit b846ffed194aecab00afd2854662a53a7388cdde) + + app/core/gimpdrawablefilter.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +commit 05f74b6f4047681ed02e3f81c82bd80c1702a369 +Author: Ell +Date: Sun Dec 30 04:46:10 2018 -0500 + + app: add gimp_drawable_filter_set_crop() + + Add gimp_drawable_filter_set_crop(), which allows setting an output + crop rectangle for the filter; anything outside the rectangle + doesn't get filtered. The crop area is combined with the preview + area to determine the filtered area during preview, however, unlike + the preview area, the crop area remains in effect while committing + the filter. + + Consequently, when merging a drawable filter, if the filter has a + crop, only process the cropped area. + + (cherry picked from commit 5c27d14fdfd151f26b8aab4ddd77e10376278c1e) + + app/core/gimpdrawable-filters.c | 164 + +++++++++++++++++++++------------------- + app/core/gimpdrawablefilter.c | 156 + ++++++++++++++++++++++++++------------ + app/core/gimpdrawablefilter.h | 3 + + 3 files changed, 196 insertions(+), 127 deletions(-) + +commit 81bef8cd506e3b24c0fa27ab51e87436af8ab4c0 +Author: Ell +Date: Sun Dec 30 03:33:56 2018 -0500 + + app: rename gimp_applicator_set_preview() to _set_crop(); add + _get_crop() + + We're going to use GimpApplicator's output crop for more than just + split previews. Rename gimp_applicator_set_preview() to + gimp_applicator_set_crop(), and add gimp_applicator_get_crop(), + which returns the output crop rectangle, or NULL if cropping is + disabled. + + (cherry picked from commit 7534ae53d69302ef6142a90c93cf45cc9b1257ba) + + app/core/gimpdrawable-filters.c | 5 ++-- + app/core/gimpdrawablefilter.c | 5 ++-- + app/gegl/gimpapplicator.c | 54 + +++++++++++++++++++++++------------------ + app/gegl/gimpapplicator.h | 10 ++++---- + 4 files changed, 40 insertions(+), 34 deletions(-) + +commit 0685c05cfbb9ca17fa4501d7c1756f5c5163538d +Author: Ell +Date: Sat Dec 29 20:54:35 2018 -0500 + + app: in GimpFilterTool, make region combo insensitive when selection + is empty + + (cherry picked from commit 093e017df80c19ad981bd3338814aa4021241d09) + + app/tools/gimpfiltertool.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +commit 3e1a6315832118f2cd4ecc3b5ab362571ad53995 +Author: Ell +Date: Sat Dec 29 20:22:32 2018 -0500 + + app: in GimpFilterTool, show region combo for non-point ops + + In GimpFilterTool, show the region combo when applying a non-point + op, as well as when applying a position-dependent point op. The + result of non-point ops may depend on the choice of input region, + even if the op is not position-dependent. + + (cherry picked from commit 7949fd9a2867648772a0e4e15908c9666d749f3f) + + app/tools/gimpfiltertool.c | 35 ++++++++++++++++++++--------------- + 1 file changed, 20 insertions(+), 15 deletions(-) + +commit bb039c84019c0663d918e8677113510f3a1f4464 +Author: Ell +Date: Sat Dec 29 14:23:38 2018 -0500 + + app: use GimpDrawableFilter in gimp_drawable_apply_operation() + + In gimp_drawable_apply_operation(), use a temporary + GimpDrawableFilter to apply the operation, instead of using a + shadow buffer. This renders and composits the op directly into the + drawable buffer, avoiding an intermediate buffer, requiring less + space and speeding up processing. + + (cherry picked from commit b201f735628f2c6662a6167f20dd67faef74e3d8) + + app/core/gimpdrawable-operation.c | 29 ++++++++++++++--------------- + 1 file changed, 14 insertions(+), 15 deletions(-) + +commit b4c1c94db044dee982318ea048b7e6e568228efa +Author: Ell +Date: Sat Dec 29 14:12:51 2018 -0500 + + app: in GimpApplicator, allow enabling cache/preview after + construction; remove preview cache + + Remove the use_split_preview and use_result_cache parameters of + gimp_applicator_new(), and allow enabling/disabling the cache + (through gimp_applicator_set_cache()) and the preview crop (through + gimp_applicator_set_preview()) after construction. + + Move the preview crop node after the result cache, and remove the + separate preview cache node. This eliminates an extra cache + buffer, reducing the space consumed by filters, and speeds up split + preview, since the cached result now includes the output + compositing. + + (cherry picked from commit ab52dc6bcae61abdd7c7a4f124ef518f3f30f086) + + app/core/gimpdrawable-combine.c | 2 +- + app/core/gimpdrawable-filters.c | 5 +- + app/core/gimpdrawable-floating-selection.c | 4 +- + app/core/gimpdrawablefilter.c | 4 +- + app/gegl/gimpapplicator.c | 208 + +++++++++++++---------------- + app/gegl/gimpapplicator.h | 28 ++-- + app/paint/gimppaintcore.c | 2 +- + 7 files changed, 114 insertions(+), 139 deletions(-) + +commit b4813417b16d6b96fe53d562e5eba638793d78ca +Author: Daniel Korostil +Date: Sat Dec 29 14:52:33 2018 +0000 + + Update Ukrainian translation + + po-script-fu/uk.po | 509 + ++++++++++++++++++++++++++++++----------------------- + 1 file changed, 284 insertions(+), 225 deletions(-) + +commit 8703aab4e3b83b83bda3f2b10d7df18c09f7dc5e +Author: Daniel Korostil +Date: Sat Dec 29 14:51:21 2018 +0000 + + Update Ukrainian translation + + po-libgimp/uk.po | 517 + +++++++++++++++++++++++++++---------------------------- + 1 file changed, 257 insertions(+), 260 deletions(-) + +commit 9fe42d7c0f6970c94f03644c04f4378bcc51834a +Author: Daniel Korostil +Date: Sat Dec 29 14:44:29 2018 +0000 + + Update Ukrainian translation + + po/uk.po | 2375 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 1279 insertions(+), 1096 deletions(-) + +commit 20093484e71503537408e90c1cb4cbb04b740927 +Author: Rodrigo Lledó +Date: Sat Dec 29 10:39:34 2018 +0000 + + Update Spanish translation + + po/es.po | 1893 + ++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 1026 insertions(+), 867 deletions(-) + +commit 112868500acc8fe71db0c24f5e76f441d06bdb92 +Author: Snehalata B Shirude +Date: Sat Dec 29 10:22:44 2018 +0000 + + Update Marathi translation + + po-windows-installer/mr.po | 144 + ++++++++++++++++++++++++--------------------- + 1 file changed, 78 insertions(+), 66 deletions(-) + +commit c650c51cbcadd4703533b384b57e469eb2a87cf8 +Author: Ell +Date: Fri Dec 28 08:29:10 2018 -0500 + + app: in gimp_drawable_merge_filter(), align undo rect to tile grid + + In gimp_drawable_merge_filter(), align the region copied to the + undo buffer to the drawable buffer's tile grid, so that the copied + tiles are COWed. + + (cherry picked from commit cba4bc4781aa2e5f023c4c50464748f8748f898d) + + app/core/gimpdrawable-filters.c | 23 ++++++++++++++++------- + 1 file changed, 16 insertions(+), 7 deletions(-) + +commit 96a16b8db77c25fdbfc4a854f392b2344f653485 +Author: Ell +Date: Fri Dec 28 02:19:02 2018 -0500 + + Revert "Bug 796090 - (wrong) true-color preview of GEGL filter + ops, ..." + + We now perform the conversion of filter output to the drawable + format as part of the individual filter nodes (see the last few + commits), so there's no need for another conversion after the + filter stack. + + This reverts commit d6e0ca505440596e81593cf9ec0fac4f0d9e2ba8. + + (cherry picked from commit 95393722ccd70c9741b4b5fea57d5cd175b9cd83) + + app/core/gimpdrawable-private.h | 1 - + app/core/gimpdrawable.c | 45 + ++++++----------------------------------- + 2 files changed, 6 insertions(+), 40 deletions(-) + +commit 8415bc7d94181d0ab1f2d30ac48e7e6a786b88ca +Author: Ell +Date: Fri Dec 28 03:31:27 2018 -0500 + + app: cache result of floating selections + + Use an output cache for floating-selection filters, to speed up + anchoring. + + (cherry picked from commit 3f45e893bf5a9fe0908309a178c12bd486b3e308) + + app/core/gimpdrawable-floating-selection.c | 4 +++- + app/core/gimplayer-floating-selection.c | 13 +++---------- + 2 files changed, 6 insertions(+), 11 deletions(-) + +commit 20d6db79c204de4a300e81ddce65250461efc144 +Author: Ell +Date: Fri Dec 28 03:26:20 2018 -0500 + + app: use drawable format as floating-sel applicator output format + + Set the output format of floating-selection applicators to the + target drawable format. We're going to remove the global + GipDrawable convert-format node, which we use to get correct + previews for indexed drawables, so that each filter now has to do + its own format conversion. + + (cherry picked from commit 0560c5a6fe11e42be12ce862ab84dcaa8940c47a) + + app/core/gimpdrawable-floating-selection.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +commit 37b0832c2dabf8241221b00c6fd40cdffc8b5243 +Author: Ell +Date: Fri Dec 28 02:13:07 2018 -0500 + + app: in GimpDrawableFilter, use the drawable format as the cache + format + + In GimpDrawableFilter, set the applicator's output format to the + drawable format, so that the cache uses the drawable format, and so + copying the cached result to the drawble's buffer when comitting + the filter becomes much cheaper, and, in particular, doesn't + require reading tiles out of the swap. This notably improves + commit speed in large images, at the expense of requiring a few + extra conversions during preview. + + (cherry picked from commit 8e57ee22657726aa2b6b381d7f61cab825e81dda) + + app/core/gimpdrawablefilter.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +commit ad6fff33b66e1aa8387bc629a066531177b73d6b +Author: Ell +Date: Fri Dec 28 02:09:11 2018 -0500 + + app: add gimp_applicator_set_output_format() + + In GimpApplicator, add gimp_applicator_set_output_format(), which + can be used to explicitly set the format of the result. In + particular, this allows controlling the output cache format, which + can speed up the merging of cached filters. + + (cherry picked from commit b93df0311f716ec952110a406b6ec5f40177ab85) + + app/gegl/gimpapplicator.c | 33 +++++++++++++++++ + app/gegl/gimpapplicator.h | 92 + +++++++++++++++++++++++++---------------------- + 2 files changed, 82 insertions(+), 43 deletions(-) + +commit 7c1d5b7a3edc90bbfb92bc210c4035938dfe13e4 +Author: Ell +Date: Fri Dec 28 02:06:40 2018 -0500 + + app: add GimpDrawable::format-changed signal + + ... which is emitted when the drawable's format is changed. + + (cherry picked from commit 85e454bae800f216e5e47c4be5b9f6e99b9b505a) + + app/core/gimpdrawable.c | 47 + ++++++++++++++++++++++++++++++++++++----------- + app/core/gimpdrawable.h | 2 +- + 2 files changed, 37 insertions(+), 12 deletions(-) + +commit 13c6fe5db684db2d0e1284b67e2d8418b4d447d3 +Author: Ell +Date: Thu Dec 27 17:07:46 2018 -0500 + + app: in GimpLineArt, use "invalidate-preview" signal of input viewable + + In GimpLineArt, use the "invalidate-preview" signal of the input + viewable, instead of its "painted" or "rendered" signals, for + asynchronously computing the line art. Subsequently, remove the + aforementioned signals from GimpDrawable and GimpProjection, + respectively. This simplifies the code, and reduces the number of + signals. + + (cherry picked from commit ef9b1f6694b9f44f6b55fabfb5898a4b35c5b88f) + + app/core/gimpdrawable.c | 30 ------------- + app/core/gimpdrawable.h | 1 - + app/core/gimplineart.c | 112 + ++++++++++++++++++++-------------------------- + app/core/gimpprojection.c | 11 ----- + app/core/gimpprojection.h | 13 +++--- + 5 files changed, 54 insertions(+), 113 deletions(-) + +commit cbffd9dfb6ee00645131bae97b188af67b7987ee +Author: Ell +Date: Thu Dec 27 13:59:31 2018 -0500 + + app: remove gimp_applicator_dup_apply_buffer() + + ... nothing uses it after last commit. + + (cherry picked from commit 12e83350bf562f2dc7f49acd9bb62f8d2587fab9) + + app/gegl/gimpapplicator.c | 40 +++------------------------------------- + app/gegl/gimpapplicator.h | 4 ---- + 2 files changed, 3 insertions(+), 41 deletions(-) + +commit c466b6108e7e966cc9ec7741b088f2a27b9a4b71 +Author: lillolollo +Date: Fri Dec 14 01:24:20 2018 +0000 + + Fix Problem found in ./libgimpbase/gimpbase.def + the following symbols are in the library, + but are not listed in the .def-file: + + gimp_bucket_fill_area_get_type + + (cherry picked from commit 132a378bed4611e95f4bcdbc46aaecbc9fca821b) + + libgimpbase/gimpbase.def | 1 + + 1 file changed, 1 insertion(+) + +commit 23ae869f21a6b009a6ef042ca9607cf9c95b9817 +Author: Ell +Date: Thu Dec 27 11:16:04 2018 -0500 + + app: remove "Edit -> Fade..." + + This commit completely removes the "Edit -> Fade..." feature, + because... + + - The main reason is that "fade" requires us to keep two buffers, + instead of one, for each fadeable undo step, doubling (or worse, + since the extra buffer might have higher precision than the + drawable) the space consumed by these steps. This has notable + impact when editing large images. This overhead is incurred even + when not actually using "fade", and since it seems to be very + rarely used, this is too wasteful. + + - "Fade" is broken in 2.10: when comitting a filter, we copy the + cached parts of the result into the apply buffer. However, the + result cache sits after the mode node, while the apply buffer + should contain the result of the filter *before* the mode node, + which can lead to wrong results in the general case. + + - The same behavior can be trivially achieved "manually", by + duplicating the layer, editing the duplicate, and changing its + opacity/mode. + + - If we really want this feature, now that most filters are GEGL + ops, it makes more sense to just add opacity/mode options to the + filter tool, instead of having this be a separate step. + + (cherry picked from commit ed7ea51fb739d1651946fd122a147f5d8415b7ff) + + app/actions/edit-actions.c | 28 ---- + app/actions/edit-commands.c | 23 --- + app/actions/edit-commands.h | 3 - + app/core/Makefile.am | 2 - + app/core/gimpdrawable-combine.c | 28 ---- + app/core/gimpdrawable-edit.c | 52 ++----- + app/core/gimpdrawable-filters.c | 52 ------- + app/core/gimpdrawable.c | 15 +- + app/core/gimpdrawableundo.c | 1 - + app/core/gimpdrawableundo.h | 10 -- + app/core/gimpimage-fade.c | 79 ---------- + app/core/gimpimage-fade.h | 26 ---- + app/core/gimpimage-undo.c | 29 ---- + app/core/gimpimage-undo.h | 2 - + app/dialogs/Makefile.am | 2 - + app/dialogs/fade-dialog.c | 214 + -------------------------- + app/dialogs/fade-dialog.h | 26 ---- + app/gimpcore.def | 2 - + app/operations/layer-modes/gimp-layer-modes.c | 11 +- + app/operations/operations-enums.c | 2 - + app/operations/operations-enums.h | 4 +- + app/widgets/gimphelp-ids.h | 1 - + devel-docs/app/app-docs.sgml | 1 - + devel-docs/app/app-sections.txt | 8 - + menus/image-menu.xml.in | 1 - + po/POTFILES.in | 1 - + 26 files changed, 16 insertions(+), 607 deletions(-) + +commit f60fe04842949764e58ba9e128fc7fb0aa176e2d +Author: Sabri Ünal +Date: Tue Dec 25 16:37:08 2018 +0000 + + Update Turkish translation + + po-windows-installer/tr.po | 133 + ++++++++++++++++++++++++--------------------- + 1 file changed, 71 insertions(+), 62 deletions(-) + +commit 59eb563aa138df91b22a8193ce1f3c65b2f7fe07 +Author: Piotr Drąg +Date: Mon Dec 24 14:42:12 2018 +0100 + + Update Polish translation + + po/pl.po | 68 + ++++++++++++++++++++++++++++++++++++---------------------------- + 1 file changed, 38 insertions(+), 30 deletions(-) + +commit e001f344e5d911ef03b989cf058a9d229e007f17 +Author: Jehan +Date: Mon Dec 24 13:21:12 2018 +0100 + + app: rename and merge the spline and segment length properties... + + ... in GimpBucketFillOptions for the line art algorithm. + + Inside GimpLineArt, there are still 2 properties, but we don't + show them + anymore in the Bucket Fill tool options. One of the main reason is + probably that it's hard to differentiate their usage. One is to close + with curved lines, the other with straight segments. Yet we don't + actually have any control on one or the other. All one knows is + that you + can have "holes" in your drawing of a given size and you want them + close-like for filling. Only reason I can see to have 2 types + of closure + is whether you'd want to totally disable one type of closure (then you + set it to 0). But this is a very limited reason for making the options + less understandable overall, IMO. + So for the time being, let's show up only a single option which sets + both properties in GimpLineArt. As patdavid says "it makes sense as a + first pass". + + Also rename the option to shorter/simpler "Maximum gap length". Thanks + to patdavid and pippin for helping on figuring out this better label! + + Finally I am bumping the default for the gaps to 100px. The original + values were ok for the basic small images used in demos, but not for + real life image where it was always too short (even 100px may still be + too short actually, but much better than the 20 and 60px from + before!). + + (cherry picked from commit 503775a5a04160c8ebb8ef8b242c5228e69cba4c) + + app/core/gimplineart.c | 4 ++-- + app/tools/gimpbucketfilloptions.c | 44 + +++++++++++---------------------------- + app/tools/gimpbucketfilloptions.h | 3 +-- + app/tools/gimpbucketfilltool.c | 8 +++---- + 4 files changed, 19 insertions(+), 40 deletions(-) + +commit b67ecb3d305547cabb8318524b49010ab931aa72 +Author: Piotr Drąg +Date: Sun Dec 23 13:53:04 2018 +0100 + + Update Polish translation + + po-libgimp/pl.po | 479 + ++++++++++++++++++++++++++++--------------------------- + po/pl.po | 173 +++++++++++++------- + 2 files changed, 359 insertions(+), 293 deletions(-) + +commit 769a6f73cc538f8eec1bcc7f5b267175a403355c +Author: Michael Natterer +Date: Sat Dec 22 21:06:50 2018 +0100 + + app: fix capitalization of the "Line Art Detection" frame + + (cherry picked from commit 822f1b9090642e4c899d2108fb87eb935267b3e4) + + app/tools/gimpbucketfilloptions.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 3d9ad63c340acbc9858d78ed78970f612b600800 +Author: Alexandre Prokoudine +Date: Fri Dec 21 01:49:44 2018 +0300 + + Unified transform: enable Constraint:Scale by default + + app/tools/gimptransformgridoptions.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d70aeea5500da26606173634070a8233e3fa57c3 +Author: Ell +Date: Thu Dec 20 08:29:33 2018 -0500 + + app: improve gradient color-sampling speed + + Improve the speed of gimp_gradient_get_color_at(), which is used by + gimp:gradient during processing when the gradient cache is too big, + by disabling type checking, and inlining and avoiding some function + calls. + + (cherry picked from commit 93f4b187044acc4cfa32c52589891ba7e3ab3de7) + + app/core/gimpgradient.c | 23 ++++++++++++++++------- + 1 file changed, 16 insertions(+), 7 deletions(-) + +commit 3943530c5076fe9ecee14c59a2260d7cb7543204 +Author: Jehan +Date: Thu Dec 20 10:54:55 2018 +0100 + + NEWS: add various recently merged changes. + + NEWS | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +commit 36376b50e97592f6fa88f3eb8318c9fa568f1822 +Author: Marco Ciampa +Date: Wed Dec 19 21:54:47 2018 +0100 + + Updated Italian translation + + po-libgimp/it.po | 544 +++++++++++++++-------------- + po-plug-ins/it.po | 999 + ++++++++++++++++++++++++++--------------------------- + po-script-fu/it.po | 283 ++++++++------- + po/it.po | 176 ++++++---- + 4 files changed, 1042 insertions(+), 960 deletions(-) + +commit 3e1791247bcb99cc2c820f37b187ca352573c18e +Author: Piotr Drąg +Date: Sat Dec 8 17:30:44 2018 +0100 + + Update POTFILES.in + + po/POTFILES.in | 1 + + 1 file changed, 1 insertion(+) + +commit 3ef6c2fb962e4d9c49b79d6afb102c2f81cca5c8 +Author: Jehan +Date: Wed Dec 19 16:25:04 2018 +0100 + + app, pdb: more gimp-2-10 only tweak for the smart-colorization. + + Again some changes because of the different PDB file organization + between branches. + + app/pdb/edit-cmds.c | 6 ++---- + pdb/groups/edit.pdb | 6 ++---- + 2 files changed, 4 insertions(+), 8 deletions(-) + +commit 487c01f8d1848944cd173d59a38ce965ec54bef5 +Author: Jehan +Date: Wed Dec 19 16:01:06 2018 +0100 + + app: fix type warning. + + I missed this warning when reviewing commit a9a979b2d0. + My bad! + + (cherry picked from commit 182786b4fbfe6ead1cd146a4261487f829d144b2) + + app/display/gimpstatusbar.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ef12064655b879a28f0aaaeb7068d76d5bac1354 +Author: Jehan +Date: Mon Dec 17 15:38:15 2018 +0100 + + app: allow setting line art spline and segment length to 0. + + Practically it means that the algorithm won't close line art anymore + with both settings at 0. This can nevertheless still be a very useful + tool when you have a drawing style with well-closed lines. In such a + case, you will still profit from the color flooding under the line art + part of the algorithm. + Moreover with such well-closed zones from start, you don't get the + over-segmentation anymore and the threaded processing will be faster + obviously. + + (cherry picked from commit 0a2d0661689ef48a21fbaa2e0713a5a86a43e968) + + app/core/gimplineart.c | 310 + ++++++++++++++++++++------------------ + app/tools/gimpbucketfilloptions.c | 4 +- + 2 files changed, 165 insertions(+), 149 deletions(-) + +commit 2d409f2d0e6d4caeb7f10b3f338241f5bd07c963 +Author: Jehan +Date: Sat Dec 15 21:47:14 2018 +0100 + + Issue #2664: add a tooltip to "better compression" checkbox. + + Zlib is a "better" compression in the meaning that it is a more + advanced + and complex algorithm than RLE. And in most cases, it should end up in + smaller file sizes. But as any algorithm, there may be cases when the + expectations are not met (worst cases or such). That's the nature + of the + maths. Still we should not make the checkbox text over-complicated (it + is not the place to teach algorithmic), yet we can at least add + a small + tooltip text. + + (cherry picked from commit c3ac722995667b27c2b315dd395f726e940553c7) + + app/widgets/gimpsavedialog.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 86b9738286a7f6cfd6e6c2146ae4ba47f07dbd66 +Author: Jehan +Date: Tue Dec 11 18:05:12 2018 +0100 + + app: do not make line art bucket fill a GimpSelectCriterion anymore. + + This was my initial choice, but the more I think about it, the less + I am + sure this was the right choice. There was some common code (as I was + making a common composite bucket fill once the line art was + generated), + but there is also a lot of different code and the functions were + filled + of exception when we were doing a line art fill. Also though there + is a + bit of color works (the way we decide whether a pixel is part of a + stroke or not, though currently this is basic grayscale threshold), + this + is really not the same as other criterions. In particular this + was made + obvious on the Select by Color tool where the line art criterion was + completely meaningless and would have had to be opted-out! + + This commit split a bit the code. Instead of finding the line art + in the + criterion list, I add a third choice to the "Fill whole + selection"/"Fill + similar colors" radio. In turn I create a new GimpBucketFillArea type + with the 3 choices, and remove line art value from + GimpSelectCriterion. + + I am not fully happy yet of this code, as it creates a bit of + duplicate + code, and I would appreciate to move some code away from + gimpdrawable-* + and gimppickable-* files. This may happen later. I break the work in + pieces to not get too messy. + Also this removes access to the smart colorization from the API, but + that's probably ok as I prefer to not freeze options too early in the + process since API needs to be stable. Probably we should get a concept + of experimental API. + + (cherry picked from commit cd924f453ac149b55b3c8cfbafb9212950551e9e) + + app/core/gimpchannel-select.c | 1 - + app/core/gimpdrawable-bucket-fill.c | 269 +++++++++++++++++++----- + app/core/gimpdrawable-bucket-fill.h | 59 +++--- + app/core/gimppickable-contiguous-region.c | 327 + +++++++++++++++--------------- + app/core/gimppickable-contiguous-region.h | 5 +- + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilloptions.c | 119 +++++------ + app/tools/gimpbucketfilloptions.h | 2 +- + app/tools/gimpbucketfilltool.c | 131 ++++++------ + app/tools/gimpfuzzyselecttool.c | 2 +- + data/tool-presets/FX/Fill-Paper.gtp | 2 +- + libgimp/gimpenums.c.tail | 2 + + libgimpbase/gimpbaseenums.c | 34 +++- + libgimpbase/gimpbaseenums.h | 22 +- + libgimpwidgets/gimppropwidgets.c | 3 +- + pdb/enums.pl | 16 +- + pdb/groups/drawable_edit.pdb | 2 +- + 17 files changed, 622 insertions(+), 376 deletions(-) + +commit 7ea2ca26ff7af8de870909775bb6270db37be51e +Author: Jehan +Date: Tue Dec 11 16:05:41 2018 +0100 + + Issue #2495: different code for Windows and Linux on duplicate + devices. + + After discussing with Mitch, it turn out commit 717c183a3e was fixing + (or rather working around) actual issues of broken device/usb stack + issues on Linux, as expected. + Nevertheless on Windows, this broke in turn many tablets (see commit + ce24e16083). Therefore we do a very ugly #ifdef to bail from duplicate + devices on Windows whereas we continue on Linux. This fix and + difference + of behavior is completely empirical, rather than based on actual good + logics, so that's quite annoying, but well… not much choice here. + + Also note that since we had no report of breakage on other OSes + (such as + macOS/BSD), at least that I know of, I let them with the Linux code + path. + + (cherry picked from commit 74a7a5d3e2cbc1f070d548cc455bb68d9317964d) + + app/widgets/gimpdeviceinfo.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +commit e645cbb386011f9c25fb1a88341ec3c058a336a4 +Author: Jehan +Date: Tue Dec 11 12:51:10 2018 +0100 + + devel-docs: 0 at end of layer and channel pointer list is a pointer + too. + + (cherry picked from commit 3e967ed02c336ec1a2e0bd6586da71751e5ec79b) + + devel-docs/xcf.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit fec11389cb4a1863fcad7d4506f1c77e85adcd17 +Author: Jehan +Date: Sat Dec 8 10:54:38 2018 +0100 + + app: allow switching fill type with Alt even when Pattern fill + selected. + + Currently in bucket fill tool, the modifier was only switching fg + to bg + and bg to fg, and was doing nothing when pattern was set. I make it + switch to fg as well (and remember which was the original value). + + (cherry picked from commit 5a157bf1ba662891e485b79fbd619736e7669288) + + app/tools/gimpbucketfilltool.c | 34 +++++++++++++++++++++++----------- + 1 file changed, 23 insertions(+), 11 deletions(-) + +commit a68524d43ba97ec8ba9a468c483cdf65ca67acdb +Author: Jehan +Date: Sat Dec 1 18:33:51 2018 +0100 + + app: reorganize the line art code inside a GimpLineArt object. + + The code was too much spread out, in core and tool code, and also + it was + made too specific to fill. I'll want to reuse this code at least + in the + fuzzy select tool. This will avoid code duplication, and also + make this + new process more self-contained and simpler to review later (the + algorithm also has a lot of settings and it is much cleaner to + have them + as properties rather than passing these as parameters through many + functions). + + The refactoring may not be finished; that's at least a first step. + + (cherry picked from commit db18c679f382f5e0647e8204760f95659ea96126) + + app/core/core-types.h | 1 + + app/core/gimpchannel-select.c | 3 +- + app/core/gimpdrawable-bucket-fill.c | 27 +- + app/core/gimpdrawable-bucket-fill.h | 14 +- + app/core/gimplineart.c | 750 + +++++++++++++++++++++++++++--- + app/core/gimplineart.h | 52 ++- + app/core/gimppickable-contiguous-region.c | 272 +---------- + app/core/gimppickable-contiguous-region.h | 25 +- + app/pdb/drawable-edit-cmds.c | 3 +- + app/tools/gimpbucketfilltool.c | 295 ++++-------- + app/tools/gimpfuzzyselecttool.c | 3 +- + pdb/groups/drawable_edit.pdb | 3 +- + 12 files changed, 826 insertions(+), 622 deletions(-) + +commit 4937d1d8d37f8cbc1e36b15218e1897dc06a613b +Author: Ell +Date: Fri Nov 30 04:10:01 2018 -0500 + + app: use gimp_async_add_callback_for_object() in the bucket-fill tool + + ... for the same reason as commit + 7c00cf498af3de9b2d9ef4208a3d3169cae64c2c. + + (cherry picked from commit 6103f0e5d040094dd72554d1992f724f808bb0f4) + + app/tools/gimpbucketfilltool.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +commit b1236403cf63c4d38c72a043ecb306f5f174ac56 +Author: Jehan +Date: Thu Nov 29 17:29:42 2018 +0100 + + app: add the segment and spline max length options for line art. + + We can't just hardcode this. On huge images in particular, you'll want + to increase this value. + + (cherry picked from commit d71efdec207141cb6b9e860248cf4550bbf61e10) + + app/core/gimpchannel-select.c | 2 +- + app/core/gimpdrawable-bucket-fill.c | 8 ++++++ + app/core/gimpdrawable-bucket-fill.h | 4 +++ + app/core/gimppickable-contiguous-region.c | 35 +++++++++++++++------- + app/core/gimppickable-contiguous-region.h | 6 ++++ + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilloptions.c | 48 + +++++++++++++++++++++++++++++++ + app/tools/gimpbucketfilloptions.h | 2 ++ + app/tools/gimpbucketfilltool.c | 4 +++ + app/tools/gimpfuzzyselecttool.c | 2 +- + pdb/groups/drawable_edit.pdb | 2 +- + 11 files changed, 100 insertions(+), 15 deletions(-) + +commit 1471da20b756bee29b0696463c6d6bb22bb1ecce +Author: Jehan +Date: Thu Nov 29 14:12:39 2018 +0100 + + app: some code cleaning in gimplineart. + + In particular, make simpler code in a few places, taking abyss value + into account (rather than checking the position). + + (cherry picked from commit f7a4ce105164d36f97e2bf49fc7170b68d4f1273) + + app/core/gimplineart.c | 144 + ++++++++++++++++++------------------------------- + 1 file changed, 52 insertions(+), 92 deletions(-) + +commit 21e776b9117ba2102ca8cadf0ff8da614e808281 +Author: Jehan +Date: Wed Nov 28 14:36:08 2018 +0100 + + app, pdb: more gimp-2-10 only tweak for smart colorization. + + Because of the file organization of PDB different between the 2.10 and + master branches. + + app/pdb/edit-cmds.c | 8 ++++---- + pdb/groups/edit.pdb | 6 ++++-- + 2 files changed, 8 insertions(+), 6 deletions(-) + +commit 73b348de1c743997ce8a7d9636d45efd3c609599 +Author: Jehan +Date: Tue Nov 27 17:25:05 2018 +0100 + + app: make GimpBucketFillTool a GimpColorTool. + + In particular, it allows to easily color pick. This just makes + sense as + the bucket fill is definitely what one could call a "color tool", and + being able to easily change color without having to constantly + switch to + color picker tool nor open a color chooser dialog is a must. + + The fill type option (FG/BG/Pattern) was already mapped to the common + toggle behavior key (Ctrl on Linux), which is commonly used for + switching to color picker on paint tools. So I decided to remap + the fill + type switch to GDK_MOD1_MASK (Alt on Linux) to keep consistent with + other tools (at the price of a change for anyone used to this + modifier, + though I doubt it was that much used). + I also made possible to combine the 2 modifiers (so you could pick the + foreground or background color with ctrl and ctrl-alt). + + (cherry picked from commit 5d4281944fa085f64b7e6423bca2b9f31b400905) + + app/tools/gimpbucketfilloptions.c | 2 +- + app/tools/gimpbucketfilltool.c | 88 + ++++++++++++++++++++++++++++++++++++++- + app/tools/gimpbucketfilltool.h | 6 +-- + 3 files changed, 90 insertions(+), 6 deletions(-) + +commit 439a44a613fdb16c6b2075893bbdd8baaaac0d65 +Author: Jehan +Date: Tue Nov 27 15:12:18 2018 +0100 + + app: flood isolated pixels in smart colorization fill. + + The smart colorization was leaving irritating single pixels in between + colorized regions, after growing and combining. So let's just flood + these. We don't flood bigger regions (and in particular don't use + gimp_gegl_apply_flood()) on purpose, because there may be small yet + actual regions inside regions which we'd want in other colors. 1-pixel + regions is the extreme case where chances that one wanted it + filled are + just higher. + + (cherry picked from commit 744d67939da27e72a331c57e65d43375691b8689) + + app/core/gimpdrawable-bucket-fill.c | 47 + +++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +commit 4fe5dc5d42c2a87fb40333f7b42f50eae0c7bfd9 +Author: Jehan +Date: Tue Nov 27 14:59:35 2018 +0100 + + app: radius map actually not useful during smart colorization + grow step. + + The distance map has all the information we need already. Also we will + actually grow up to the max radius pixel (middle pixel of a stroke). + After discussing with Aryeom, we realized it was better to fill + a stroke + fully (for cases of overflowing, I already added the "Maximum growing + size" property anyway). + + (cherry picked from commit 6bec0bc82d1811863c79d41a3f6c2af3dceeb44a) + + app/core/gimpchannel-select.c | 2 +- + app/core/gimpdrawable-bucket-fill.c | 6 +-- + app/core/gimpdrawable-bucket-fill.h | 2 - + app/core/gimplineart.c | 15 ++---- + app/core/gimplineart.h | 3 +- + app/core/gimppickable-contiguous-region.c | 88 + +++++++++++-------------------- + app/core/gimppickable-contiguous-region.h | 5 +- + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilltool.c | 10 +--- + app/tools/gimpfuzzyselecttool.c | 2 +- + pdb/groups/drawable_edit.pdb | 2 +- + 11 files changed, 46 insertions(+), 91 deletions(-) + +commit c4988efdb03769a41abedbfcb2f1e801b2e083be +Author: Jehan +Date: Mon Nov 26 12:33:45 2018 +0100 + + app: add possibility to antialias line art colorization. + + (cherry picked from commit d2f9549c9fad2fd1874f3abb562a9b06009f51f5) + + app/core/gimpdrawable-bucket-fill.c | 28 ++++++++++++++++++++++++++++ + app/tools/gimpbucketfilloptions.c | 12 ++++-------- + 2 files changed, 32 insertions(+), 8 deletions(-) + +commit 53fdd19d84235a506c649d60b89d27b87a818e71 +Author: Jehan +Date: Mon Nov 26 12:26:54 2018 +0100 + + app: simpler code with gegl_node_blit(). + + No need to go through an intermediate GeglBuffer when unneeded. + + (cherry picked from commit c32b0ecc92a6accd5800796846762370d08b4d28) + + app/core/gimplineart.c | 22 +++++----------------- + 1 file changed, 5 insertions(+), 17 deletions(-) + +commit b00580d4b1e69588176fa872f2df5b47739c8911 +Author: Jehan +Date: Thu Nov 22 18:13:58 2018 +0100 + + app: add "line-art-max-grow" property to the bucket fill options. + + When flooding the line art, we may overflood it in sample merge (which + would use color in the line art computation). And if having all colors + on the same layer, this would go over other colors (making the wrong + impression that the line art leaked). + This new option is mostly to keep some control over the mask growth. + Usually a few pixels is enough for most styles of drawing (though we + could technically allow for very wide strokes). + + (cherry picked from commit eb042e6c8769c7938fc18a7bc6b872969650089d) + + app/core/gimpchannel-select.c | 2 +- + app/core/gimpdrawable-bucket-fill.c | 8 +++- + app/core/gimpdrawable-bucket-fill.h | 6 ++- + app/core/gimppickable-contiguous-region.c | 68 + +++++++++++++++++-------------- + app/core/gimppickable-contiguous-region.h | 7 ++-- + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilloptions.c | 24 +++++++++++ + app/tools/gimpbucketfilloptions.h | 1 + + app/tools/gimpbucketfilltool.c | 1 + + app/tools/gimpfuzzyselecttool.c | 2 +- + pdb/groups/drawable_edit.pdb | 2 +- + 11 files changed, 82 insertions(+), 41 deletions(-) + +commit 706fe079c209f8fff3897c1c0d1e7a1fcda9a764 +Author: Jehan +Date: Thu Nov 22 16:17:19 2018 +0100 + + app: make sure we reset tool modifier state before saving options. + + I had this funny behavior when I was quitting GIMP with the active + tool + using modifiers (for instance bucket fill). Each time I'd quit with + ctrl-q (and if the image is not dirty), the options would use + the value + from the modifier state and be saved as-is. Hence at next restart, the + default value was always different! + + (cherry picked from commit dd3d9ab3dd5497812681b02f8c32bbedc7270be1) + + app/gui/gui.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +commit 19c1f2654e2b15a99a4cebce530aaa2d80b7dae7 +Author: Jehan +Date: Thu Nov 22 14:32:59 2018 +0100 + + app: properly (bucket) fill created splines and segments in line art. + + For this, I needed distmap of the closed version of the line art + (after + splines and segments are created). This will result in invisible + stroke + borders added when flooding in the end. These invisible borders will + have a thickness of 0.0, which means that flooding will stop at once + after these single pixels are filled, which makes it quick, and is + perfect since created splines and segments are 1-pixel thick anyway. + Only downside is having to run "gegl:distance-transform" a second + time, + but this still stays fast. + + (cherry picked from commit 5a4754f32b8e08f71d8cb521acdf45bdbc0e802a) + + app/core/gimplineart.c | 44 + +++++++++++++++++++++++-------- + app/core/gimppickable-contiguous-region.c | 6 ++--- + 2 files changed, 36 insertions(+), 14 deletions(-) + +commit e5bea42b6f7018faee40bd251b652b9f7b7df9b9 +Author: Jehan +Date: Wed Nov 21 15:52:51 2018 +0100 + + app: replace gegl:watershed-transform with custom algorithm. + + We don't really need to flow every line art pixel and this new + implementation is simpler (because we don't actually need + over-featured + watershedding), and a lot lot faster, making the line art bucket fill + now very reactive. + For this, I am keeping the computed distance map, as well as local + thickness map around to be used when flooding the line art pixels + (basically I try to flood half the stroke thickness). + + Note that there are still some issues with this new implementation + as it + doesn't properly flood yet created (i.e. invisible) splines and + segments, and in particular the ones between 2 colored sections. I am + going to fix this next. + + (cherry picked from commit 3467acf096fab265d55e2882b91b870da5db975b) + + app/core/gimpchannel-select.c | 2 +- + app/core/gimpdrawable-bucket-fill.c | 9 +- + app/core/gimpdrawable-bucket-fill.h | 4 + + app/core/gimplineart.c | 68 ++++--- + app/core/gimplineart.h | 32 +-- + app/core/gimppickable-contiguous-region.c | 327 + +++++++++++++++++++++++++----- + app/core/gimppickable-contiguous-region.h | 12 +- + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilltool.c | 31 ++- + app/tools/gimpfuzzyselecttool.c | 2 +- + pdb/groups/drawable_edit.pdb | 2 +- + 11 files changed, 383 insertions(+), 108 deletions(-) + +commit e9b8122e1886707df73790c88fd55e52492be258 +Author: Jehan +Date: Tue Nov 20 17:49:12 2018 +0100 + + app: fix a line art leak in bucket fill tool. + + Introduced in commit b4e12fbbbb: + gimp_pickable_contiguous_region_prepare_line_art_async() was running + gimp_pickable_flush(), which provokes the "rendered" signal on the + image projection when a change occured. As a result, it was calling + gimp_bucket_fill_compute_line_art() within itself and since + tool->priv->async was not set yet, none of the call were + canceled. Hence + the same line art is computed twice, but one is leaked. + Make sure we block this signal handler as a solution. + + (cherry picked from commit 36c885a6df2ffb91e7e3c67ec85eacd570d9763c) + + app/tools/gimpbucketfilltool.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +commit 2934293664359fb02e2327381d8380e49ce54e8a +Author: Jehan +Date: Tue Nov 20 16:00:01 2018 +0100 + + app: priority map now unneeded for gegl:watershed-transform. + + This commit is based on GEGL master as I just made the auxiliary + buffer + of gegl:watershed-transform optional for basic cases. + It doesn't necessarily makes the whole operation that much faster + according to my tests, but it makes the code simpler as creating this + priority map was quite unnecessary. + + (cherry picked from commit 963eef82076461c36baaae69885dadff945f2f82) + + app/core/gimppickable-contiguous-region.c | 33 + ++++--------------------------- + 1 file changed, 4 insertions(+), 29 deletions(-) + +commit 3c0f1307cce5b204ff22d2ff5cf5479f9cc0db52 +Author: Ell +Date: Mon Nov 19 14:48:26 2018 -0500 + + app: add gimp_pickable_contiguous_region_prepare_line_art_async() ... + + ... and use in bucket-fill tool + + Add gimp_pickable_contiguous_region_prepare_line_art_async(), which + computes a line-art asynchronously, and use it in the bucket-fill + tool, instead of having the tool create the async op. + + This allows the async to keep running even after the pickable dies, + since we only need the pickable's buffer, and not the pickable + itself. Previously, we reffed the pickable for the duration of the + async, but we could still segfault when unreffing it, if the + pickable was a drawable, and its parent image had already died. + + Furthermore, let the async work on a copy of the pickable's buffer, + rather than the pickable's buffer directly. This avoids some race + conditions when the pickable is the image (i.e., when "sample + merged" is active), since then we're using image projection's + buffer, which is generally unsafe to use in different threads + concurrently. + + Also, s/! has_alpha/has_alpha/ when looking for transparent pixels, + and quit early, at least during this stage, if the async in + canceled. + + (cherry picked from commit b4e12fbbbba8d607abdbe353e424c8386d2a3f11) + + app/core/gimppickable-contiguous-region.c | 140 + +++++++++++++++++++++++++----- + app/core/gimppickable-contiguous-region.h | 43 +++++---- + app/tools/gimpbucketfilltool.c | 60 +++---------- + 3 files changed, 155 insertions(+), 88 deletions(-) + +commit 3e5e0e2d373980998d38fa520d1d2ebb6eb37d33 +Author: Ell +Date: Mon Nov 19 14:25:51 2018 -0500 + + app: in bucket-fill tool, cancel async on tool destruction + + When computing line-art, don't ref the bucket-fill tool in the + async data, and rather cancel any ongoing async upon tool + destruction, so that the async callback doesn't attept to touch the + now-dead tool. This avoids segfaulting in the async callback when + switching to a different tool, while a line-art async operation is + active. + + Additionally, always cancel any previous async operation in + gimp_bucket_fill_compute_line_art(), even if not starting a new + one. + + (cherry picked from commit 663a6c701131c17e1222083560ed1b1102087e6b) + + app/tools/gimpbucketfilltool.c | 31 ++++++++++++++++++++----------- + 1 file changed, 20 insertions(+), 11 deletions(-) + +commit 17789431528ae637d626c5a1042ba1287222a95c +Author: Ell +Date: Mon Nov 19 10:54:05 2018 -0500 + + app: in bucket-fill tool, fix potential leak when computing line-art + + In the line-art async function, pass ownership over the resulting + buffer to the async object, so that the buffer is properly freed in + case the async in canceled after line-art computation is complete, + but before the completion callback is called. + + Also, clear the tool's async pointer in the completion callback, to + avoid leaking the last issued async. + + (cherry picked from commit 2e45c4c8c889d25f3ce3c441e09e02a0dd2a31d3) + + app/tools/gimpbucketfilltool.c | 14 ++++---------- + 1 file changed, 4 insertions(+), 10 deletions(-) + +commit e996020e19535a6f56a9edef4112368f51afbebd +Author: Ell +Date: Mon Nov 19 10:50:48 2018 -0500 + + app: in the bucket-fill tool, avoid CRITICALs when computing + line-art ... + + ... when the current image/drawable are NULL. + + (cherry picked from commit 4575949cdfb209cc642c74f4b540dd1fbc42dbe4) + + app/tools/gimpbucketfilltool.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +commit a83bbf8249fa8334f87e16791dbc270f0f6b6738 +Author: Jehan +Date: Mon Nov 19 14:44:26 2018 +0100 + + app: end point detection uses both the end point rate and clamped + value. + + (cherry picked from commit 79571231c5adce73f336978f585c9b94d2e09769) + + app/core/gimplineart.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit 4c3fcdb6bbd2a38bb369efae9399119afe2dbffc +Author: Jehan +Date: Mon Nov 19 14:13:03 2018 +0100 + + app: better handle drawable and image update for line art computation. + + The "update" signal on drawable or projection can actually be emitted + many times for a single painting event. Just add new signals + ("painted" + on GimpDrawable and "rendered" on GimpProjection) which are emitted + once + for a single update (from user point of view), at the end, after + actual + rendering is done (i.e. after the various "update" signals). + + Also better support the sample merge vs current drawable paths for + bucket fill. + + (cherry picked from commit 047265333cff7e13db173db8d0bddc6279e9e318) + + app/core/gimpdrawable.c | 30 +++++++++++++++++++++++ + app/core/gimpdrawable.h | 1 + + app/core/gimpprojection.c | 11 +++++++++ + app/core/gimpprojection.h | 13 +++++----- + app/tools/gimpbucketfilltool.c | 54 + +++++++++++++++++++++++++++++------------- + 5 files changed, 86 insertions(+), 23 deletions(-) + +commit 6155889315fd47bc5e0aa43c9f066d7aed5021e4 +Author: Jehan +Date: Sun Nov 18 18:31:08 2018 +0100 + + app: remove now useless erosion size option. + + Since commit b00037b850, erosion size is not used anymore, as + this step + has been removed, and the end point detection now uses local thickness + of strokes instead. + + (cherry picked from commit 3f58a38574773bcc1005693126ba9376fdf97b86) + + app/core/gimpchannel-select.c | 2 +- + app/core/gimpdrawable-bucket-fill.c | 5 +---- + app/core/gimpdrawable-bucket-fill.h | 2 -- + app/core/gimplineart.c | 5 ----- + app/core/gimplineart.h | 1 - + app/core/gimppickable-contiguous-region.c | 7 ++----- + app/core/gimppickable-contiguous-region.h | 4 +--- + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilloptions.c | 24 ------------------------ + app/tools/gimpbucketfilloptions.h | 1 - + app/tools/gimpbucketfilltool.c | 11 +---------- + app/tools/gimpfuzzyselecttool.c | 2 +- + pdb/groups/drawable_edit.pdb | 2 +- + 13 files changed, 9 insertions(+), 59 deletions(-) + +commit c1c544f88243e2e48e767ea1809159be1e10d6f4 +Author: Jehan +Date: Fri Nov 16 19:54:38 2018 +0100 + + app: improve end point detection for smart colorization. + + Previous algorithm was relying on strokes of small radius to detect + points of interest. In order to work with various sizes of strokes, we + were computing an approximate median stroke thickness, then using this + median value to erode the binary line art. + + Unfortunately this was not working that well for very fat strokes, and + also it was potentially opening holes in the line art. These + holes were + usually filled back later during the spline and segment creations. Yet + it could not be totally assured, and we had some experience where + color + filling would leak out of line art zones without any holes from the + start (which is the opposite of where this new feature is supposed to + go)! + + This updated code computes instead some radius estimate for every + border + point of strokes, and the detection of end points uses this + information + of local thickness. Using local approximation is obviously much more + accurate than a single thickness approximation for the whole drawing, + while not making the processing slower (in particular since we got rid + of the quite expensive erosion step). + This fixes the aforementionned issues (i.e. work better with fat + strokes + and do not create invisible holes in closed lines), and also is not + subject to the problem of mistakenly increasing median radius when you + color fill in sample merge mode (i.e. using also the color data in the + input)! + Also it is algorithmically less intensive, which is obviously + very good. + + This new version of the algorithm is a reimplementation in GIMP of new + code by Sébastien Fourey and David Tschumperlé, as a result of + our many + discussions and tests with the previous algorithm. + + Note that we had various tests, experiments and propositions to + try and + improve these issues. Skeletonization was evoked, but would have been + most likely much slower. Simpler erosion based solely on local radius + was also a possibility but it may have created too much noise + (skeleton + barbs), with high curvature, hence may have created too many new + artificial endpoints. + This new version also creates more endpoints though (and does not seem + to lose any previously detected endpoints), which may be a bit + annoying + yet acceptable with the new bucket fill stroking interaction. In any + case, on simple examples, it seems to do the job quite well. + + (cherry picked from commit b00037b8500bee6ba7fcf1082c8c4c707fdfb1bc) + + app/core/gimplineart.c | 772 + +++++++++++++------------------------------------ + 1 file changed, 199 insertions(+), 573 deletions(-) + +commit ee582784b709b879693e44281a12d8087c28f226 +Author: Jehan +Date: Fri Nov 16 13:41:12 2018 +0100 + + app: force the image flush after a selection fill. + + Other bucket fills are now done as filter until committed, but basic + selection fill is still done automatically. So let's make sure the + canvas is updated immediately (as it used to be before my changes). + + (cherry picked from commit 287d90ba9e90573f41e78226ad3a235933348eb5) + + app/tools/gimpbucketfilltool.c | 1 + + 1 file changed, 1 insertion(+) + +commit 8fb83d77512d822f711792a4d8aa1b345363aaa5 +Author: Jehan +Date: Thu Nov 15 13:56:54 2018 +0100 + + app: fix uninitialized variables. + + (cherry picked from commit 255f2e1cdf973825c330e7df13a5ee716541ee7b) + + app/core/gimppickable-contiguous-region.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +commit fd0f5c50a58c12949f8a605d10dfc7dc36bd9ede +Author: Jehan +Date: Mon Nov 12 15:28:02 2018 +0100 + + app: recompute line art if needed after a bucket fill commit. + + (cherry picked from commit 969143c4366b2e025cc0a46363a3c96722b3468f) + + app/tools/gimpbucketfilltool.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +commit b6227b27874930d0dffbecb4441d46de5c663b79 +Author: Jehan +Date: Mon Nov 12 11:31:27 2018 +0100 + + app: update gimp-2-10 for bucket fill changes for smart colorization. + + Some differences in PDB between gimp-2-10 and master branches. + + app/pdb/edit-cmds.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit 6b1d7969edcffd55d5251d195378385ea3ef18b0 +Author: Jehan +Date: Wed Nov 7 14:22:10 2018 +0100 + + app: edit the bucket fill tool options with new line art options. + + I have not added all the options for this new tool yet, but this sets + the base. I also added a bit of TODO for several places where we + need to + make it settable, in particular the fuzzy select tool, but also simply + PDB calls (this will need to be a PDB context settings. + + Maybe also I will want to make some LineArtOptions struct in order not + to have infinite list of parameters to functions. And at some + point, it + may also be worth splitting a bit process with other type of + selection/fill (since they barely share any settings anyway). + + Finally I take the opportunity to document a little more the + parameters + to gimp_lineart_close(), which can still be improved later (I should + have documented these straight away when I re-implemented this + all from + G'Mic code, as I am a bit fuzzy on some details now and will need to + re-understand code). + + (cherry picked from commit 824af12438838b1521cd6d3afb67e9c7369c3cf4) + + app/core/gimpchannel-select.c | 1 + + app/core/gimpdrawable-bucket-fill.c | 7 ++ + app/core/gimpdrawable-bucket-fill.h | 4 + + app/core/gimplineart.c | 28 ++++--- + app/core/gimppickable-contiguous-region.c | 15 ++-- + app/core/gimppickable-contiguous-region.h | 6 +- + app/pdb/drawable-edit-cmds.c | 1 + + app/tools/gimpbucketfilloptions.c | 129 + +++++++++++++++++++++++++----- + app/tools/gimpbucketfilloptions.h | 31 ++++--- + app/tools/gimpbucketfilltool.c | 29 +++++-- + app/tools/gimpfuzzyselecttool.c | 1 + + pdb/groups/drawable_edit.pdb | 1 + + 12 files changed, 199 insertions(+), 54 deletions(-) + +commit 808cdc98801b0d1f8c849d40bbd430df374bd14c +Author: Jehan +Date: Sun Nov 4 16:22:56 2018 +0100 + + app: bucket fill tool with a "paint-style" interaction. + + Rather than just having a click interaction, let's allow to "paint" + with + the bucket fill. This is very useful for the new "line art" + colorization + since it tends to over-segment the drawing. Therefore being able to + stroke through the canvas (rather than click, up, move, click, etc.) + makes the process much simpler. This is also faster since we + don't have + to recompute the line art while a filling is in-progress. + Note that this new behavior is not only for the line art mode, + but also + any other fill criterion, for which it can also be useful. + + Last change of behavior as a side effect: it is possible to cancel the + tool changes the usual GIMP way (for instance by right clicking when + releasing the mouse button). + + (cherry picked from commit e1c4050617f4efb3a3bfa889f2b016596d1ce762) + + app/core/gimpdrawable-bucket-fill.c | 190 ++++++++++---- + app/core/gimpdrawable-bucket-fill.h | 35 ++- + app/core/gimppickable-contiguous-region.c | 18 +- + app/tools/gimpbucketfilltool.c | 421 + ++++++++++++++++++++++++------ + 4 files changed, 526 insertions(+), 138 deletions(-) + +commit 0e1d8ef695cd03efc2d4a657631a87205248ec15 +Author: Jehan +Date: Sun Nov 4 14:29:16 2018 +0100 + + app: make line art pre-computation in threads. + + This makes the speed sensation of the tool much faster as line art can + be computed in dead time when you start the tool or when you move the + pointer. + + (cherry picked from commit a3cda4abbe229ad5d4d512122c5089d2a77f7ba0) + + app/tools/gimpbucketfilltool.c | 108 + ++++++++++++++++++++++++++++++++++++----- + 1 file changed, 95 insertions(+), 13 deletions(-) + +commit 32f0a49ecc0f44540ebd4bf99d7ae9c78d9d4d39 +Author: Jehan +Date: Sat Nov 3 18:40:50 2018 +0100 + + app: compute line art in advance. + + Right now, this is mostly meaningless as it is still done + sequentially. + But I am mostly preparing the field to pre-compute the line art as + background thread. + + (cherry picked from commit f246f404940d7e2e54ea2a863b38970b86e198cc) + + app/core/gimpchannel-select.c | 1 + + app/core/gimpdrawable-bucket-fill.c | 2 + + app/core/gimpdrawable-bucket-fill.h | 1 + + app/core/gimppickable-contiguous-region.c | 201 ++++++++++++++-------- + app/core/gimppickable-contiguous-region.h | 31 ++-- + app/pdb/drawable-edit-cmds.c | 2 +- + app/tools/gimpbucketfilltool.c | 270 + ++++++++++++++++++++++++++++-- + app/tools/gimpbucketfilltool.h | 5 +- + app/tools/gimpfuzzyselecttool.c | 2 +- + pdb/groups/drawable_edit.pdb | 2 +- + 10 files changed, 421 insertions(+), 96 deletions(-) + +commit 6dc959d376b9534837653ef0276b9e4fe2d49899 +Author: Jehan +Date: Sun Nov 4 11:11:25 2018 +0100 + + devel-docs: small update for GEGL buffer leak debugging. + + (cherry picked from commit b9de1076ed32a692f06801f677f85b2e9ec1aa5e) + + devel-docs/debugging-tips.txt | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +commit 0cf524e35101a2653cc53645e2996bf004cfd116 +Author: Jehan +Date: Wed Oct 31 10:14:20 2018 +0100 + + app: fix line art labellization. + + The older labelling based off CImg code was broken (probably + because of + me, from my port). Anyway I realized what it was trying to do was too + generic, which is why we had to fix the result later (labeling all + non-stroke pixels as 0, etc.). Instead I just implemented a simpler + labelling and only look for stroke regions. It still over-label a bit + the painting but a lot less, and is much faster. + + (cherry picked from commit 93a49951a0423658af04333820451e5a6d001edf) + + app/core/gimplineart.c | 264 + +++++++++++++++++++++---------------------------- + 1 file changed, 114 insertions(+), 150 deletions(-) + +commit 4da35468bc8d12fc2b7d648ae332b501a4094f38 +Author: Jehan +Date: Sat Oct 27 11:29:55 2018 +0200 + + app: better use GeglBufferIterator! + + I don't actually need to loop through borders first. This is what the + abyss policy is for, and I can simply check the iterator position to + verify I am within buffer boundaries or not. + This simplifies the code a lot. + + (cherry picked from commit c4ff81540dbf4fba118b2c70cd13b05034f93575) + + app/core/gimplineart.c | 195 + +++---------------------------------------------- + 1 file changed, 9 insertions(+), 186 deletions(-) + +commit 8557cd8cfbaacaddbfff88cb8a8640da5f03f07d +Author: Jehan +Date: Fri Oct 26 14:57:55 2018 +0200 + + app: directly update the mask buffer with gegl_node_blit_buffer(). + + No need to create a temporary buffer for this. + + (cherry picked from commit f02993fb9cd6a4e26251676b1e0b110276f4282e) + + app/core/gimppickable-contiguous-region.c | 13 +------------ + 1 file changed, 1 insertion(+), 12 deletions(-) + +commit 8dc6bcafa3e7cf76e4cdea8c44a3bb44960dc59f +Author: Jehan +Date: Fri Oct 26 13:38:57 2018 +0200 + + app: create a simple priority map for line art selection flooding. + + We actually don't need to compute distance map. I just make the + simplest + priority map, with 1 any line art pixel and 0 any other pixel (in mask + or not), lowest priority being propagated first. + And let the flooding begin! + + (cherry picked from commit 410c747509685a3359f8cc5468ce24e73ecab6eb) + + app/core/gimppickable-contiguous-region.c | 80 + ++++++++++--------------------- + 1 file changed, 24 insertions(+), 56 deletions(-) + +commit 687d058416f0e10f09e2dd4e4403d2ec0b80cc6c +Author: Jehan +Date: Fri Oct 26 13:15:50 2018 +0200 + + app: with recent gegl:watershed-transform, no need for intermediate... + + ... labels buffer. + We can watershed directly the mask buffer being correctly flagged. + This commit relies on merge request gegl!8 being accepted and merged. + + (cherry picked from commit e905ea7ba2c348515816c28747f1c8008cf6b143) + + app/core/gimppickable-contiguous-region.c | 73 + ++++++++++++------------------- + 1 file changed, 28 insertions(+), 45 deletions(-) + +commit 3b0a312e7e20515011613d7d36e4d08191d00492 +Author: Jehan +Date: Wed Oct 17 17:21:26 2018 +0200 + + app: use char array for temporary data (rather than a GEGL buffer). + + Also use more GeglBufferIterator on input GEGL buffer. + Using a char array is much less expensive and accelerated the line + erosion a lot! + Moving to GeglBufferIterator is not finished, but I do in steps. + + (cherry picked from commit 0c80f8a7182a646f7c1d7eb869f2b7f940ce2b20) + + app/core/gimplineart.c | 158 + ++++++++++++++++++++++++++----------------------- + 1 file changed, 84 insertions(+), 74 deletions(-) + +commit 325398fcf9b0e5b5666960d8ba11ab275bf0ec90 +Author: Jehan +Date: Wed Oct 17 15:53:03 2018 +0200 + + app: use simpler allocated variables. + + Allocating double-level arrays is just very inefficient. + + (cherry picked from commit f975f15ec0dfa267ae5d409443067b911e81345f) + + app/core/gimplineart.c | 165 + +++++++++++++++++++++++-------------------------- + 1 file changed, 79 insertions(+), 86 deletions(-) + +commit 95bbb2625269e43eeb67b4bbbad1e898cfe3ba30 +Author: Jehan +Date: Wed Oct 17 15:04:30 2018 +0200 + + app: make visited into single-level allocated array. + + (cherry picked from commit f19181dcf8043946ecfa31400023216bc78bdbd0) + + app/core/gimplineart.c | 104 + ++++++++++++++++++++++++------------------------- + 1 file changed, 52 insertions(+), 52 deletions(-) + +commit 71ec6a9849a3ab3ebfea09daa854ceb99ff8368a +Author: Jehan +Date: Sun Oct 14 16:51:47 2018 +0200 + + app: fix stroke labels in gimp_lineart_estimate_stroke_width(). + + I must make sure that stroke pixels are labelled 0 and non-stroke + other + than 0. + + (cherry picked from commit 910d7934f5bc90f48af4dda3d06953db029df733) + + app/core/gimplineart.c | 48 + ++++++++++++++++++++++++++---------------------- + 1 file changed, 26 insertions(+), 22 deletions(-) + +commit b1a3792aae268b7ef3d6cc8483d4666cd97478f4 +Author: Jehan +Date: Sun Oct 14 15:30:38 2018 +0200 + + app: use more GeglBufferIterator. + + In this case, it makes the code a bit more messy, but hopefully more + efficient. + + (cherry picked from commit 1822ea399a3c03db043aea87937e5545ffcd43a9) + + app/core/gimplineart.c | 260 + +++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 228 insertions(+), 32 deletions(-) + +commit 6dbd12a91549b600239f67f9e24324d5c921bae5 +Author: Jehan +Date: Thu Oct 11 18:18:52 2018 +0200 + + app: use GeglBufferIterator rather than gegl_buffer_sample|set(). + + (cherry picked from commit 041a8f1eec67d63bf4df1acbae436c109bb91641) + + app/core/gimplineart.c | 136 + ++++++++++++++++++++++++++++--------------------- + 1 file changed, 78 insertions(+), 58 deletions(-) + +commit 13466ba7e7af7e30547fa18a5221491c8c3b545b +Author: Jehan +Date: Thu Oct 11 16:02:44 2018 +0200 + + app: babl types returned by choose_format() must be float! + + (cherry picked from commit fcd038eb1680d080f5c0257750a8724ba93326d0) + + app/core/gimppickable-contiguous-region.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 239be8ecc84216c183a7e19a07cdcfb390d12e6e +Author: Jehan +Date: Thu Oct 11 15:32:19 2018 +0200 + + app: implement second step for line art selection/filling. + + When filling colors in line arts, you don't want to leave space + between + the strokes and the color, which usually happen with any of the + current + selection methods. + A "KISS" trick is usually to grow your selection a few pixels before + filling (adding an additional step in colorization process), which + obviously does not handle all cases (depending on drawing style and + stroke size, you may need to grow more or less) as it doesn't + take into + account actual stroke geometry. + + Instead, I label the selection and the "rest" differently and + leave the + pixel strokes unlabelled. Then I let these unlabelled pixels be + flooded + by the "gegl:watershed-transform" operation. + Note that this second step is different from the second step from the + GREYC research paper, as they use their own watershed algorithm taking + color spots as sources to color the whole image at once. This is a + different workflow from the one using bucket fill with a single color + source. + + (cherry picked from commit 8502b4e7431761c487107ffd49022b2ccd3585ff) + + app/core/gimppickable-contiguous-region.c | 150 + +++++++++++++++++++++++++++++- + 1 file changed, 146 insertions(+), 4 deletions(-) + +commit bde56bdfdcdddad37c2812876ab253310461b061 +Author: Jehan +Date: Wed Oct 10 20:28:47 2018 +0200 + + app, libgimpbase: add GIMP_SELECT_CRITERION_LINE_ART selection type. + + This commit implements part of the research paper "A Fast and + Efficient + Semi-guided Algorithm for Flat Coloring Line-arts" from the GREYC (the + people from G'Mic). It is meant to select regions from drawn + sketchs in + a "smart" way, in particular it tries to close non-perfectly closed + regions, which is a common headache for digital painters and + colorists. + + The implementation is not finished as it needs some watersheding + as well + so that the selected area does not leave "holes" near stroke borders. + The research paper proposes a new watersheding algorithm, but I + may not + have to implement it, as it is more focused on automatic colorization + with prepared spots (instead of bucket fill-type interaction). + + This will be used in particular with the fuzzy select and bucket fill + tools. + + Note that this first version is a bit slow once we get to big images, + but I hope to be able to optimize this. + Also no options from the algorithm are made available in the GUI yet. + + (cherry picked from commit 8ed12b1b9830f8c1956b408f994be6c81bd8f21a) + + app/core/Makefile.am | 2 + + app/core/gimplineart.c | 2250 + +++++++++++++++++++++++++++++ + app/core/gimplineart.h | 43 + + app/core/gimppickable-contiguous-region.c | 66 +- + libgimpbase/gimpbaseenums.c | 2 + + libgimpbase/gimpbaseenums.h | 2 + + libgimpwidgets/gimppropwidgets.c | 5 +- + pdb/enums.pl | 6 +- + 8 files changed, 2371 insertions(+), 5 deletions(-) + +commit 9a6aa316810b433fdaf96bb832f095f8212013bf +Author: Jehan +Date: Wed Dec 19 14:51:03 2018 +0100 + + app: show the layer mode dropdown list properly. + + Showing it was only displaying the top modes, with a lot of top space, + and you had to slowly scroll down the list. This is the same as #2642 + (as Alexandre noted in a comment), so I just use the same "fix" + though I + don't fully understand it. It feels more of a side effect of + gtk_combo_box_set_wrap_width() working around a bug of GtkComboBox. So + if anyone has a better fix and understand the issue, feel free + to patch + (maybe GTK+ directly?). In the meantime, it works well enough. :-) + + (cherry picked from commit 6dfca83c2a5ee8d053150e85e2047702d67eb4e7) + + app/widgets/gimplayermodecombobox.c | 4 ++++ + 1 file changed, 4 insertions(+) + +commit e17daf66e286d4465db4b602aec02c0db31723d5 +Author: Kevin Stoffler +Date: Tue Dec 18 16:03:33 2018 +0000 + + app: add gtk_combo_box_set_wrap_width for scale menu + + (cherry picked from commit a9a979b2d0a9a3ef25d63c90a1401994cca2a818) + + app/display/gimpstatusbar.c | 1 + + 1 file changed, 1 insertion(+) + +commit 4e54ac9cdd641eeb402b2f6778446da3aaeec15e +Author: Ell +Date: Mon Dec 17 06:31:42 2018 -0500 + + app: update definition of "{cache,swap}-compressed" dashboard vars + + Update the definition of the "cache-compressed" and "swap- + compressed" dashboard variables, to reflect the changes made by + GEGL commit gegl@dc22e997757ab91c180244d5290d094d2ea8572f. + + (cherry picked from commit fda53f9c18b0fbdf805b523409641a95e39ebbd1) + + app/widgets/gimpdashboard.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit deb20726f1f0f6f415983b7b1158c4bd35407dc6 +Author: Piotr Drąg +Date: Sun Dec 16 12:13:08 2018 +0100 + + Update Polish translation + + po/pl.po | 1415 + ++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 739 insertions(+), 676 deletions(-) + +commit 7c9629c01f7a30ee9d956f4c2616053766f0e783 +Author: Marco Ciampa +Date: Thu Dec 13 15:56:03 2018 +0100 + + Updated Italian translation + + po/it.po | 1417 + ++++++++++++++++++++++++++++++++------------------------------ + 1 file changed, 740 insertions(+), 677 deletions(-) + +commit fd297f943b7590655dddffa2eb27ac6c42853c28 +Author: Ell +Date: Wed Dec 12 11:17:49 2018 -0500 + + app: in the warp tool, blink behavior combo when the current behavior + is invalid + + In the warp tool, when the warp is empty and the current behavior + has no effect as a result (i.e., when it's ERASE or SMOOTH), show + an error message in the status bar, and blink the behavior combo + widget in the tool options, to hint at the source of the error. + + (cherry picked from commit 7958387d54c41bad17ed373adf510717c830248a) + + app/tools/gimpwarpoptions.c | 2 ++ + app/tools/gimpwarpoptions.h | 1 + + app/tools/gimpwarptool.c | 35 +++++++++++++++++++++++++++++++++++ + 3 files changed, 38 insertions(+) + +commit 6f4e233cf026ef02d993d993ab16619cb3f70dd9 +Author: Ell +Date: Wed Dec 12 10:51:04 2018 -0500 + + app: in the warp tool, blink stroke frame when no events are selected + + In the warp tool, when no stroke events are selected, blink the + stroke frame widget in the tool options, in addition to showing an + error message in the status bar, to hint at the source of the + error. + + (cherry picked from commit 17cc44a7be4557e15b9ddc0fb5d1023178201364) + + app/tools/gimpwarpoptions.c | 2 ++ + app/tools/gimpwarpoptions.h | 1 + + app/tools/gimpwarptool.c | 2 ++ + 3 files changed, 5 insertions(+) + +commit a8ef7db6161e738e2f2cadbf95bb9429ff0d31ec +Author: Ell +Date: Wed Dec 12 11:13:48 2018 -0500 + + app: s/GEGL_WARP_BEHAVIOR/GIMP_WARP_BEHAVIOR/ + + The enumerators of the GimpWarpBehavior enum, except for MOVE, had + a GEGL_ prefix, rather than a GIMP_ prefix, for some reason. + + Change all of them to GIMP_. + + (cherry picked from commit 2085cb4a378f8ceec7b65e30b244989f6a653bc8) + + app/tools/gimpwarptool.c | 12 ++++++------ + app/tools/tools-enums.c | 26 +++++++++++++------------- + app/tools/tools-enums.h | 12 ++++++------ + 3 files changed, 25 insertions(+), 25 deletions(-) + +commit df475195d441512431d479de8b3256a02c3192c9 +Author: Ell +Date: Mon Dec 10 14:14:34 2018 -0500 + + app: in gimp_widget_blink_cancel(), avoid redrawing non-blinking + widget + + Since commit fe139e566293337fa8e7a4ae64e2e00dc6fe3eaa, when + blinking a widget, we cancel blinking for all its ancestors. Avoid + redrawing all the ancestors as a result, unless they're actually + blinking. This prevents some noticeable lag when blinking a + widget. + + (cherry picked from commit 5a2dee29d76b3d65d41804875581dee30500f270) + + app/widgets/gimpwidgets-utils.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +commit a8841a3c07170f88e672b58420c5fd83ccc045fa +Author: Ell +Date: Mon Dec 10 08:22:50 2018 -0500 + + app: in all tools, blink lock box when the current item is locked + + In all tools, when the current item can't be edited due to its lock + mask, use gimp_tools_blink_lock_box(), added in the previous + commit,to blink the lock box of the corresponding dockable, in + addition to showing an error message in the status bar, to hint at + the source of the error. + + (cherry picked from commit 637105b962916bae90f3f71235b378ea79769504) + + app/display/gimptoolpath.c | 8 ++++++++ + app/tools/gimpbucketfilltool.c | 3 +++ + app/tools/gimpcagetool.c | 3 +++ + app/tools/gimpcroptool.c | 3 +++ + app/tools/gimpeditselectiontool.c | 2 ++ + app/tools/gimpfiltertool.c | 3 +++ + app/tools/gimpgradienttool.c | 3 +++ + app/tools/gimpmovetool.c | 2 ++ + app/tools/gimppainttool.c | 15 +++++++++++---- + app/tools/gimpselectiontool.c | 4 ++++ + app/tools/gimptransformtool.c | 3 +++ + app/tools/gimpwarptool.c | 3 +++ + 12 files changed, 48 insertions(+), 4 deletions(-) + +commit c237ee3c23de27104893f26940e6399cb97fbee6 +Author: Ell +Date: Mon Dec 10 08:00:32 2018 -0500 + + app: add gimp_tools_blink_lock_box() + + Add gimp_tools_blink_lock_box() utility function, in a new + gimptools-utils.c file, which takes a GimpItem, and blinks the + GimpItemTreeView lock-box of the corresponding dockable. This can + be used to hint that the item's lock toggles are preventing it from + being edited. + + (cherry picked from commit 9bdaec3a498305c0186df22b436b3368b940aa2a) + + app/tools/Makefile.am | 2 ++ + app/tools/gimptools-utils.c | 80 + +++++++++++++++++++++++++++++++++++++++++++++ + app/tools/gimptools-utils.h | 26 +++++++++++++++ + 3 files changed, 108 insertions(+) + +commit 66a9dffa2e273f8e1c83d1856bdc6d9a95f01948 +Author: Ell +Date: Mon Dec 10 07:57:02 2018 -0500 + + app: in gimp_widget_blink(), cancel blinking of parent widgets + + When blinking a widget using gimp_widget_blink(), cancel the + blinking of all its ancestors, to reduce visual clutter. + + (cherry picked from commit fe139e566293337fa8e7a4ae64e2e00dc6fe3eaa) + + app/widgets/gimpwidgets-utils.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit ace9b12205b5a5f418a62b916a3ebace236b2a37 +Author: Ell +Date: Mon Dec 10 07:51:39 2018 -0500 + + app: in the paint tools, blink mode box when the current mode + is invalid + + In the paint tools, when the current paint mode is invalid, i.e., + when it requires an alpha channel, but the the current drawable has + no alpha channel, or its alpha channel is locked, blink the paint- + mode box widget in the tool options, in addition to showing an + error message in the status bar, to hint at the source of the + error. + + (cherry picked from commit 464bf1b0a9ccbc64064a63dec4727371b1109b20) + + app/tools/gimppaintoptions-gui.c | 10 ++++++++++ + app/tools/gimppaintoptions-gui.h | 4 +++- + app/tools/gimppainttool.c | 14 ++++++++++++++ + 3 files changed, 27 insertions(+), 1 deletion(-) + +commit a42f2cb2e7e769d6ed8a701d8dbc94708f5c8a85 +Author: Ell +Date: Mon Dec 10 07:39:32 2018 -0500 + + app: in the transform tools, blink type box when there's no item + to transform + + In the transform tools, when there is no item of the selected type + to transform, blink the move-type box widget in the tool options, + in addition to showing an error message in the status bar, to hint + at the source of the error. + + (cherry picked from commit 17412aa234f87b0236ae8692fa856deaf6e8a14f) + + app/tools/gimptransformoptions.c | 17 ++++++++++------- + app/tools/gimptransformoptions.h | 13 ++++++++----- + app/tools/gimptransformtool.c | 3 +++ + 3 files changed, 21 insertions(+), 12 deletions(-) + +commit 92c6f87391d93592c316b7c3eaaf17ed2cd7005b +Author: Ell +Date: Mon Dec 10 07:34:27 2018 -0500 + + app: in the move tool, blink type box when there's no item to move + + In the move tool, when there is no item of the selected type to + move, blink the move-type box widget in the tool options, in + addition to showing an error message in the status bar, to hint at + the source of the error. + + (cherry picked from commit c9bc3d7a09978d61cc6c2ddb8497c3116a1884f8) + + app/tools/gimpeditselectiontool.c | 14 ++++++++------ + app/tools/gimpeditselectiontool.h | 3 ++- + app/tools/gimpmoveoptions.c | 17 ++++++++++------- + app/tools/gimpmoveoptions.h | 3 +++ + app/tools/gimpmovetool.c | 11 ++++++----- + 5 files changed, 29 insertions(+), 19 deletions(-) + +commit 02ccab9e7e71c27bb805b45390490e756061495b +Author: Ell +Date: Mon Dec 10 07:42:52 2018 -0500 + + app: in the selection tools, blink mode box when the current operation + is invalid + + In the selection tools, when the selected operation is invalid, + i.e., when trying to subtract-from or intersect-with an empty + selection, blink the selection-mode box widget in the tool options, + in addition to showing an error message in the status bar, to hint + at the source of the error. + + (cherry picked from commit f990e416097becee782e711e0933f1d3ee40e2fe) + + app/tools/gimpselectionoptions.c | 2 ++ + app/tools/gimpselectionoptions.h | 1 + + app/tools/gimpselectiontool.c | 10 +++++++--- + 3 files changed, 10 insertions(+), 3 deletions(-) + +commit 95deaa7bf301e6d1a2971b97e0c364f2345280ed +Author: Michael Natterer +Date: Fri Dec 7 12:11:25 2018 +0100 + + app: clean up size group code in the preferences dialog + + (cherry picked from commit 0efa00932b19243861cd3fad9a6028de5295ac0e) + + app/dialogs/preferences-dialog.c | 43 + +++++++++++++++++++--------------------- + 1 file changed, 20 insertions(+), 23 deletions(-) + +commit acc15e614af0cbc3d416484ea2a0bd93e72ddb0b +Author: Richard McLean +Date: Wed Dec 5 21:59:19 2018 +0300 + + Issue #1299 - Add selection of default export file type + + Patch cleaned up by Alexandre Prokoudine and Michael Natterer. + + (cherry picked from commit bfbad0a5cae2dd4b29fc0ee8915194fde62fdcb4) + + app/config/config-enums.c | 41 + ++++++++++++++++++++++++++++++++++++++++ + app/config/config-enums.h | 17 +++++++++++++++++ + app/config/gimpcoreconfig.c | 15 +++++++++++++++ + app/config/gimpcoreconfig.h | 1 + + app/config/gimprc-blurbs.h | 3 +++ + app/dialogs/preferences-dialog.c | 33 +++++++++++++++++++++++++------- + app/widgets/gimpexportdialog.c | 22 ++++++++++++++++++--- + 7 files changed, 122 insertions(+), 10 deletions(-) + +commit e68fdd537dcbcdd1e80afbe4f02afe91704a63eb +Author: Marco Ciampa +Date: Sun Dec 9 19:47:19 2018 +0100 + + Updated Italian translation + + po/it.po | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit d797592223072807667669c5362cb03534592a51 +Author: Piotr Drąg +Date: Sun Dec 9 12:23:02 2018 +0100 + + Update Polish translation + + po-plug-ins/pl.po | 1075 + ++++++++++++++++++++++++++--------------------------- + po/pl.po | 130 ++++--- + 2 files changed, 603 insertions(+), 602 deletions(-) + +commit a2c20b15392d456b36afdc9074d1176f02403b29 +Author: Ell +Date: Sat Dec 8 05:59:05 2018 -0500 + + Issue #2635 - Segfault when using measuring tool + + In gimp_tool_compass_update_angle(), use fuzzy comparisson when + determining whether to update the angle properties, to avoid + infinite recursion due to floating-point inaccuracies. In + partcicular, on x86, when using the x87 FPU rather than SSE, the + floating-point registers are 80-bit, while the properties are + stored as 64-bit, which can create small discrepancies between the + calculated angles and the stored values. + + (cherry picked from commit ad831dbc6d4a0ac0ad05b0b17f94373605692d61) + + app/display/gimptoolcompass.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit ebe209f463caa7448caa3d0d3ccac05c73238202 +Author: Marco Ciampa +Date: Fri Dec 7 12:12:02 2018 +0100 + + Fixed term for unsharp mask in italian + + po/it.po | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +commit 18bc8b893398bb33ed5b78e45d94aede231172f1 +Author: Marco Ciampa +Date: Fri Dec 7 10:14:18 2018 +0100 + + Updated Italian translation + + po/it.po | 265 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 142 insertions(+), 123 deletions(-) + +commit bffe0c6965875bcb5ec77cfa9a6ee962d38e0fc7 +Author: Jehan +Date: Fri Dec 7 00:44:00 2018 +0100 + + NEWS: update. + + NEWS | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit 96d67fd1b20ab1ef80948243630cca686742edb1 +Author: Jehan +Date: Fri Dec 7 00:01:07 2018 +0100 + + Issue #2495: many tablets broken by GIMP 2.10.8. + + We had many reports of tablets from various brands (Huion, Gaomon, + XP-Pen…) broken in the last release (though working fine when + downgrading to 2.10.6). Latest Huion drivers seem to fix the issue + (according to at least one report), but this is not the case for other + tablets. + + Though unable to test myself, provided stderr logs indicate that + we hit + the case when 2 devices with the same name are registered. Therefore + this commit is basically reverting commit 717c183a3e (though + keeping and + completing the comments). I don't think there is an ultimate solution + here but with this regression, experience shows us there seem to be a + lot more breakage when overwriting the device with newer occurences + (at + least on Windows). It is unclear though if commit 717c183a3e was also + supposed to fix another case actually encountered. If so, we will need + to get an even more advanced solution. + + (cherry picked from commit ce24e16083211f1b859a172d24306f7d834939b1) + + app/widgets/gimpdeviceinfo.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +commit 741a659a12ff221bd5ec83914db8b6f09df7bf78 +Author: Skal +Date: Wed Nov 28 13:37:39 2018 +0100 + + webpmux: fix memory leak by calling WebPMuxDelete() + + (cherry picked from commit e9200d2c00493606aff2b0ce4927f5dcfe859c00) + + plug-ins/file-webp/file-webp-load.c | 8 +++++++- + plug-ins/file-webp/file-webp-save.c | 2 ++ + 2 files changed, 9 insertions(+), 1 deletion(-) + +commit ab851924dd116a7937c5e5e0c876fdfb58125af0 +Author: Jehan +Date: Tue Nov 27 12:27:20 2018 +0100 + + plug-ins: make various usage of g_file_replace() safer. + + As I did on app/, finalizing an output stream also implicitly flushes + and closes it. Hence if an export ended with an error, we'd end + up with + incomplete data file (possibly overwriting a previously exported + image). + Only 2 plug-ins I haven't fixed yet are file-tiff-io and + file-gif-save. + The later one don't even clean up its memory (which somehow is + good here + as at least the output stream is never finalized hence sane files are + not overwritten in case of errors). As for the former (TIFF + plug-in), it + doesn't even seem to have any error control AFAICS, apart from + printing + error messages on standard error output. + + (cherry picked from commit 66ec467217572efb8549bdd6b02fa231861da4b6) + + plug-ins/common/file-cel.c | 6 ++++++ + plug-ins/common/file-csource.c | 6 ++++++ + plug-ins/common/file-gbr.c | 18 ++++++++++++++++++ + plug-ins/common/file-gih.c | 12 ++++++++++++ + plug-ins/common/file-header.c | 6 ++++++ + plug-ins/common/file-heif.c | 6 ++++++ + plug-ins/common/file-html-table.c | 6 ++++++ + plug-ins/common/file-pat.c | 18 ++++++++++++++++++ + plug-ins/common/file-pix.c | 11 +++++++++++ + plug-ins/common/file-pnm.c | 9 +++++++++ + plug-ins/common/file-ps.c | 6 ++++++ + plug-ins/common/file-xbm.c | 6 ++++++ + plug-ins/common/file-xwd.c | 9 +++++++++ + 13 files changed, 119 insertions(+) + +commit 48e14ef3b981b471e90f02584424a485b09fab4c +Author: Jehan +Date: Mon Nov 26 15:40:38 2018 +0100 + + app, libgimpconfig: make various usage of g_file_replace() safer. + + When an error occurs, we want to prevent overwriting any previous + version of the file by incomplete contents. So run + g_output_stream_close() with a cancelled GCancellable to do so. + See also discussion in #2565. + + (cherry picked from commit 613bf7c5abaa58ec9148fead136f8a23cf166954) + + app/core/gimp-internal-data.c | 26 +++++++++++++++++--------- + app/core/gimp-tags.c | 16 ++++++++++++++-- + app/core/gimpdata.c | 26 +++++++++++++++++--------- + app/core/gimpgradient-save.c | 8 ++++++++ + app/core/gimptagcache.c | 15 +++++++++++++-- + app/gui/themes.c | 17 +++++++++++++++-- + app/pdb/gimppdb-query.c | 7 +++++++ + app/tools/gimpfiltertool-settings.c | 8 ++++++++ + app/vectors/gimpvectors-export.c | 8 ++++++++ + app/widgets/gimpdashboard.c | 20 +++++++++++++++++++- + app/widgets/gimptextbuffer.c | 8 ++++++++ + libgimpconfig/gimpconfigwriter.c | 7 +++++++ + 12 files changed, 141 insertions(+), 25 deletions(-) + +commit c9da44bed3d8a537c14bb9a8b879e143b39dc980 +Author: Jehan +Date: Mon Nov 26 14:10:37 2018 +0100 + + app: do no overwite XCF when an error occurred at saving time. + + We can cancel a file overwrite at the last second when closing the + stream by setting a cancelled cancellable. Current code was simply not + closing the stream, but this was not enough as overwriting was + happening + anyway (probably when finalizing). + This will allow much safe saving process since we would not be + overwriting a previously sane XCF file when an error occurred + (either in + our code or a memory error, or whatnot). + See also discussion in #2565. + + (cherry picked from commit 076b53511a46e2a798033c03249e940dc3e95f4b) + + app/xcf/xcf.c | 25 +++++++++++++++++++------ + 1 file changed, 19 insertions(+), 6 deletions(-) + +commit 61a181933c1b002476d79a23c383b8c02dc2da9d +Author: Ell +Date: Thu Dec 6 08:44:23 2018 -0500 + + app: in GimpProjection, fix reinit. of current row when chunk + height changes + + In GimpProjection's chunk renderer, when the chunk height changes + in the middle of a row, we need to merge the remainder of the + current render area back into the renderer's update region, and + refetch the remainder of the row as the new render area, so that we + don't miss any unrendered area, or re-render already-rendered area, + due to the change in chunk height. However, we should previously + fail to verify that the fetched area is, in fact, the remainder of + the current row, which could cause us to render the wrong area, + missing parts of the update region. + + Fix this, by breaking up some of the chunk-renderer fucntions into + smaller sub-functions, and using those in order to explicitly set + the new render area to the remainder of the current row when the + chunk height changes. This also avoids erroneously merging the + unflushed update region of the projection into the renderer's + update region. + + (cherry picked from commit c9c2397b0d384717187870bad95373aff065cf39) + + app/core/gimpprojection.c | 132 + ++++++++++++++++++++++++++++++---------------- + 1 file changed, 87 insertions(+), 45 deletions(-) + +commit 09863478dbae467c50b72e7af0783e81c443f1c5 +Author: Ell +Date: Wed Dec 5 13:39:00 2018 -0500 + + Revert "app: save images with fractional grid coordinates as + version-10 XCFs" + + Actually, image grids are saved as parasites, so even though older + GIMP versions round their coordinates upon loading, they maintain + the fractional coordinates when re-saving the image, hence bumping + the XCF version is not really necessary. + + This reverts commit 13119efda33a7aba323dc13e6a56207a15a9f000. + + (cherry picked from commit 411ddb7e485ab05f032adeef7ea9369edc1d27a0) + + app/core/gimpimage.c | 20 -------------------- + 1 file changed, 20 deletions(-) + +commit 6ca294abe03dd9f60c1dc0a499ac18243e99ac7b +Author: Ell +Date: Wed Dec 5 13:28:04 2018 -0500 + + app: save images with fractional grid coordinates as version-10 XCFs + + Fractional-coordinate support for image grids was added in commit + 1572bccc9f1f781b720bb030730f34a1f59c498b, right before the + introduction of XCF version 10. While images with fractional grid + coordinates can be loaded with earilier versions of GIMP, the grid + coordinates are rounded to the nearest integer. + + Bump the minimal XCF version when saving images with fractional + grid coordinates to 10, which should have been the case all along. + + (cherry picked from commit a90322278dbf4cc7b63c41d6bcc18b537dd8b109) + + app/core/gimpimage.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +commit 0a2aac7ce12ae72a7195f0ac2ce111de87b18f84 +Author: Michael Natterer +Date: Wed Dec 5 00:33:59 2018 +0100 + + libgimp: actually use the path expanded in the previous commit + + (cherry picked from commit 799f6b14bb7bf8fa1eaa6cf3aae122502f7a2a2e) + + libgimp/gimp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit f6350e1b5a79849bb2019463de6bbff24ee2f491 +Author: Michael Natterer +Date: Tue Dec 4 19:41:06 2018 +0100 + + libgimp: need to expand config->swap_path in gimp_config() + + or the file system will be polluted with folders called + "${gimp_cache_path}". + + (cherry picked from commit cc835e877d55922d248a890e94344f71e15d5cf3) + + libgimp/gimp.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +commit b06ffe4334a6050c6d8a623d0888b5e093bd1a38 +Author: Ell +Date: Tue Dec 4 11:57:40 2018 -0500 + + Issue #2604 - XCF saving bug in xcf_save_buffer() + + The NULL terminator of the tile-offset array of dummy buffer-levels + is erroneously written as an int32, instead of an offset, even in + version-11+ XCFs, in which offsets are 64-bit. + + Since the dummy levels aren't actually used by GIMP, we're going to + keep these fields as int32 as an exception, in order to remain + consistent with existing XCFs, and just add a comment in the code, + and update the docs. If we ever make use of the higher buffer + levels, we should change these fields to offsets, and bump the XCF + version. + + (cherry picked from commit 2168d91cf7a569baaefefe0cc840a24f1bc81272) + + app/xcf/xcf-save.c | 9 +++++++++ + devel-docs/xcf.txt | 7 +++++-- + 2 files changed, 14 insertions(+), 2 deletions(-) + +commit 9baae75c5ceb0b7166f662abda03ac29c06f15ae +Author: Michael Natterer +Date: Tue Dec 4 17:28:24 2018 +0100 + + Integrate the logic of profile saving with metadata saving + + Add flag GIMP_METADATA_SAVE_COLOR_PROFILE to GimpMetadataSaveFlags and + initialize it from gimp_export_color_profile() in + gimp_image_metadata_save_prepare(). + + Adapt all plug-ins to use the bit from the suggested export flags and + pass the actually used value back to + gimp_image_metadata_save_finish(). + + This changes no behavior at all but creates hooks on the libgimp side + that are called with the context of an image before and after the + actual export, which might become useful later. Also, consistency + is good even though the color profile is not strictly "metadata". + + (cherry picked from commit c667fdc5c0b0d4e55cf814439e9865ce60a6677e) + + libgimp/gimpimagemetadata.c | 22 ++++++++++++++++++---- + libgimpbase/gimpmetadata.h | 21 ++++++++++++--------- + plug-ins/common/file-png.c | 7 ++++++- + plug-ins/file-jpeg/jpeg.c | 7 ++++++- + plug-ins/file-tiff/file-tiff.c | 7 ++++++- + 5 files changed, 48 insertions(+), 16 deletions(-) + +commit 4cdfbee64eb2b9172098ccc171ae559bfbd76ebc +Author: Alexandre Prokoudine +Date: Tue Dec 4 01:32:54 2018 +0300 + + Update Russian translation + + po-script-fu/ru.po | 517 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 285 insertions(+), 232 deletions(-) + +commit 86fd5e3b67f5244e3da4afd6481d80576bc6f5f7 +Author: Alexandre Prokoudine +Date: Tue Dec 4 01:01:31 2018 +0300 + + Update Russian translation + + po-plug-ins/ru.po | 1192 + +++++++++++++++++++++++++++-------------------------- + po/ru.po | 1148 + ++++++++++++++++++++++++++++----------------------- + 2 files changed, 1230 insertions(+), 1110 deletions(-) + +commit a865f5e83ebcb517d9b4f93d92ba96462da24863 +Author: Michael Natterer +Date: Mon Dec 3 19:41:56 2018 +0100 + + Issue #701 - Add the ability to embed the GIMP built-in sRGB + profile... + + ...upon exporting an image + + Add a "Save color profile" toggle and always honor it. + + (partially cherry picked from commit + 7f9379cb3281efdcc3823e2c2dd7258a3a986641) + + plug-ins/file-tiff/file-tiff-save.c | 24 ++++++++++++++++++------ + plug-ins/file-tiff/file-tiff-save.h | 1 + + plug-ins/file-tiff/file-tiff.c | 4 +++- + plug-ins/ui/plug-in-file-tiff.ui | 24 +++++++++++++++++++----- + 4 files changed, 41 insertions(+), 12 deletions(-) + +commit b25117376b1f6f9cfec11acdb934db78e98731f6 +Author: Michael Natterer +Date: Mon Dec 3 19:24:52 2018 +0100 + + plug-ins: always save the color profile in file-psd + + by using gimp_image_get_effective_color_profile() instead of just + _get_color_profile(). Don't look at the preference setting because PSD + should behave more like "save" than "export" and save everything. + + (cherry picked from commit a35b243f9b1c5334ab3783e7219bf8aaf693ed78) + + plug-ins/file-psd/psd-save.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit fa74fe78f9757a82e03773210e9630e289a560af +Author: Ell +Date: Mon Dec 3 09:52:49 2018 -0500 + + app: don't use direct-buffer fill when some components are masked + + (cherry picked from commit 83250eb57c46b5ca2b7bbc3353bbc7a14b806a80) + + app/core/gimpdrawable-edit.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 03a4fafe30ef354b6653fb9bea99610f8c83d6fd +Author: Ell +Date: Mon Dec 3 09:51:15 2018 -0500 + + app: mark REPLACE and PASS_THROUGH modes as trivial + + (cherry picked from commit f508ec8b894bf1cac4b632667581e28de7c17ad1) + + app/operations/layer-modes/gimp-layer-modes.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +commit 2f5bbe9416f09140499a68e07e474e05e4a19824 +Author: Michael Natterer +Date: Mon Dec 3 12:55:04 2018 +0100 + + Issue #701 - Add the ability to embed the GIMP built-in sRGB + profile... + + ...upon exporting an image + + Add a "Save color profile" toggle to the PNG dialog and always + honor it. + + (cherry picked from commit 7ed93452e8d2714cda2432fb46878aacfc9d5baa) + + plug-ins/common/file-png.c | 64 + +++++++++++++++++++++++++---------------- + plug-ins/ui/plug-in-file-png.ui | 24 +++++++++++++--- + 2 files changed, 59 insertions(+), 29 deletions(-) + +commit 8a90d8b8b271007b170735cc18fe66394fc1d71d +Author: Michael Natterer +Date: Mon Dec 3 12:19:41 2018 +0100 + + Issue #1297 - Unhide the items hidden in the Advanced drop-down + file... + + ...export dialogs + + Move most stuff out of the "Advanced" expander, only nerdish encoding + options are left there. + + Issue #701: Add a "Save color profile" toggle which honors the default + value configured in preferences and always saves the profile when + enabled. + + (cherry picked from commit 540cfa961171a2f8d26c183ce2291f4c507e9813) + + plug-ins/file-jpeg/jpeg-save.c | 346 + +++++++++++++++++++++-------------------- + plug-ins/file-jpeg/jpeg-save.h | 3 +- + plug-ins/file-jpeg/jpeg.c | 1 + + 3 files changed, 184 insertions(+), 166 deletions(-) + +commit 5b857dbb4b0c90c949ae9ebd6a2ea69f3875c3c3 +Author: Ell +Date: Sun Dec 2 11:00:14 2018 -0500 + + libgimpcolor, app, modules, plug-ins: remove GEGL_ITERATOR2 define + + ... it's no longer needed, since GEGL commit + gegl@9dcd2cde63f95a080bf16a58c10e9ffbdd99aace. + + Partially reverts commits: + + 6fca9959c7376ee58ccd5c7be8f0d9d69551c912 + cc10af72ccdd6c916fcb52407670c22aa32a1ae9 + 49c53568d7e9b865f5b49be8f052d783511c90c7 + 8edbc0d4912f7e9933b7463c9a75ed49298c04e2 + 29f63616d27a1e69dce7ebdc080250e24a0458f2 + 3a2014984d35377067a83e86465979d43a6a152d + ee48ec6877c2ca1997372e8734d53a37d10ef703 + 4165a315d59ddb5cce73663b7a8af3f394ae044d + 764085278fe351d3915c838c669d7ec50a3fb5bd + b7633c722e35e281afe9f8cabed50c846f29ee10 + 6ab12061b7331bdd6ea69e1ce262ebce4b97a64b + 754a3c5b18f314349c25a56c6c0421a613cdfc18 + 22b4b647bd607ad2cf365fa9ca6a2721cdcd174e + 55b3438328e3bc9c6a33fc681825805792bf04b6 + c6d23add6539321b04c21a9787fa9069df67549e + f03a84d607efe64207b7e7586e722130a5416ea9 + 822f9f0d2b23e0b5f287c7cdcfe1c10e9a43d9c4 + 95358ca1fafb1865d3b7040198ec53e7769506e6 + cdda37f4ee556c09263f23f0366a3ce7d44604a9 + 41e8035635292ce09007bf8f5fdaf9cc960c60e8 + 6761da42b2302341a773fb65f9005ac398b9bff1 + fb5d7832a8bfb20a5bc8e349c4920732fe2bd494 + 97ed7817d86c8a55e1a407cefce3d4ade1af3d42 + 46e90365784092df948df35e17b3925e3df4bd80 + ea9c5e6a4908cc39d3645cf6012c2773ee894c19 + 24fbdfb5917e0db3b312bc413eef1469811253d5 + beb4ecb2383a49c3928ed4e5a1c4766af47f0951 + 4b77831e036453e21f62edffa46b6c6b00b39a01 + fcf113a39cd93390136bf40ca2d0a50c35200b13 + 567ffe94ff3bc1c1c7cb2a9267fa33595899197d + + (cherry picked from commit 053e5edc93c13e9c7a02403b95be5426bfbc7a1d) + + app/core/gimpdrawable-transform.c | 2 -- + app/core/gimphistogram.c | 2 -- + app/core/gimpimage-convert-indexed.c | 1 - + app/core/gimppalette-import.c | 1 - + app/core/gimppickable-contiguous-region.c | 1 - + app/core/gimpscanconvert.c | 1 - + app/gegl/gimp-gegl-loops.cc | 1 - + app/gegl/gimp-gegl-mask-combine.c | 1 - + app/gegl/gimp-gegl-mask.c | 1 - + app/operations/gimpoperationcagecoefcalc.c | 1 - + app/operations/gimpoperationcagetransform.c | 1 - + app/operations/gimpoperationgradient.c | 1 - + app/operations/gimpoperationmaskcomponents.c | 27 + ++++++--------------------- + app/paint/gimpbrushcore.c | 1 - + app/paint/gimpheal.c | 1 - + app/paint/gimpink.c | 1 - + app/paint/gimpmybrushsurface.c | 1 - + app/paint/gimppaintcore-loops.cc | 1 - + app/tools/gimpiscissorstool.c | 1 - + libgimpcolor/gimpcolortransform.c | 1 - + modules/display-filter-clip-warning.c | 1 - + modules/display-filter-color-blind.c | 1 - + modules/display-filter-gamma.c | 1 - + modules/display-filter-high-contrast.c | 1 - + plug-ins/common/border-average.c | 1 - + plug-ins/common/colormap-remap.c | 1 - + plug-ins/common/compose.c | 1 - + plug-ins/common/decompose.c | 1 - + plug-ins/common/file-png.c | 1 - + plug-ins/common/gradient-map.c | 1 - + plug-ins/common/qbist.c | 1 - + plug-ins/file-psd/psd-load.c | 1 - + plug-ins/file-psd/psd-save.c | 1 - + plug-ins/file-tiff/file-tiff-load.c | 1 - + plug-ins/ifs-compose/ifs-compose.c | 1 - + plug-ins/pagecurl/pagecurl.c | 1 - + plug-ins/print/print-draw-page.c | 1 - + plug-ins/screenshot/screenshot-x11.c | 1 - + 38 files changed, 6 insertions(+), 60 deletions(-) + +commit edbda43028e7cc3d3ebae900baccb7f75fc2f009 +Author: Ell +Date: Sun Dec 2 09:44:52 2018 -0500 + + app: add "direct" parameter to gimp_projection_flush_now() + + Add a boolean "direct" parameter to gimp_projection_flush_now(), + which specifies if the projection buffer should only be invalidated + (FALSE), or rendered directly (TRUE). + + Pass TRUE when flushing the projection during painting, so that the + affected regions are rendered in a single step, instead of tile-by- + tile. We previously only invalidated the projection buffer, but + since we synchronously flush the display right after that, the + invalidated regions would still get rendered, albeit less + efficiently. + + Likewise, pass TRUE when benchmarking the projection through the + debug action, and avoid flushing the display, to more accurately + measure the render time. + + (cherry picked from commit dac9bfe3342a0ac23da759e382d732e606796197) + + app/actions/debug-commands.c | 6 +++--- + app/core/gimpprojection.c | 19 +++++++++++-------- + app/core/gimpprojection.h | 3 ++- + app/tools/gimppainttool-paint.c | 6 +++--- + 4 files changed, 19 insertions(+), 15 deletions(-) + +commit fb2db7fe29211c91a5505ef7cd67e2eba3ba3f7b +Author: Piotr Drąg +Date: Sun Dec 2 12:41:42 2018 +0100 + + Update Polish translation + + po/pl.po | 1015 + +++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 544 insertions(+), 471 deletions(-) + +commit 33a015abef088805b0fc78032ca97cbaec6970be +Author: Ell +Date: Sun Dec 2 03:47:22 2018 -0500 + + app: fix iterated-over area in gimp_gegl_clear() + + Iterate over the per-thread area, not the full region. + + (cherry picked from commit 06923d0f650260e8c522bb1eb03146dc560f59fa) + + app/gegl/gimp-gegl-loops.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 98bf8319b6980827cb8612f075bc9b538f0af015 +Author: Ell +Date: Sun Dec 2 03:41:16 2018 -0500 + + app: another fix to drawable direct-fill criterion + + Don't use a direct-buffer fill when filling using a pattern with + alpha, even when the mode is subtractive. + + (cherry picked from commit 42b82419b17d56e4fb15400a5ce1a50a2701efa3) + + app/core/gimpdrawable-edit.c | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +commit 61b1e7a3482c844e9a28538f4f833823aca8eeb8 +Author: Ell +Date: Sun Dec 2 03:22:41 2018 -0500 + + app: fix drawable direct-fill criterion + + Don't use a direct-buffer fill if the mode is subtractive, and the + composite region includes the source. Currently, this never + actually happens. + + (cherry picked from commit 660f53d300426ee9cb8bae178c2cfef166f5c164) + + app/core/gimpdrawable-edit.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +commit 8a131e97f19c9455168f23f7501e75893a8e8cbb +Author: Ell +Date: Sun Dec 2 02:59:24 2018 -0500 + + app: optimize simple whole-drawable fill/clear + + In gimp_drawable_edit_fill(), when filling/clearing the whole + drawable, without any special compositing (i.e., when there's no + selection, the opacity is 100%, and the layer mode is trivial), + fill/clear the drawable's buffer directly, without using an + applicator. This makes such operations much faster, especially in + big images. + + (cherry picked from commit dd8268c0a25a8b5df5a06bd9b0d841f4c6915e2a) + + app/core/gimpdrawable-edit.c | 179 + ++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 158 insertions(+), 21 deletions(-) + +commit 5d536afdeddc91c48b9c12cdc9155d705a38dfaf +Author: Ell +Date: Sun Dec 2 03:05:36 2018 -0500 + + app: add gimp_fill_options_fill_buffer() + + ... which is similar to gimp_fill_options_create_buffer(), however, + it fills an existing buffer, instead of creating a new buffer. + + Implement gimp_fill_options_create_buffer() in terms of the new + function. + + (cherry picked from commit 45fc4cb4f9552b3a9b315a86ee8e272c08732654) + + app/core/gimpfilloptions.c | 24 +++++++++++++++++++++--- + app/core/gimpfilloptions.h | 5 +++++ + 2 files changed, 26 insertions(+), 3 deletions(-) + +commit 2dda622a20a1e4afee438f01f66674ec1e798f32 +Author: Ell +Date: Sun Dec 2 02:55:05 2018 -0500 + + app: add gimp_layer_mode_is_trivial() + + Add a TRIVIAL layer-mode flag, and corresponding + gimp_layer_mode_is_trivial() function, which indicates if the blend + function of a given layer mode is trivial, i.e., either never + modifies the source pixels (for non-subtractive modes), or always + clears the destination pixels (for subtractive modes). + + (cherry picked from commit 8adec5fb3a36e4212d2556046be50ef1d3219a56) + + app/operations/layer-modes/gimp-layer-modes.c | 31 + +++++++++++++++++++++------ + app/operations/layer-modes/gimp-layer-modes.h | 1 + + app/operations/operations-enums.h | 3 ++- + 3 files changed, 27 insertions(+), 8 deletions(-) + +commit c4e51b9a4580f0569206fb0bd8305946302ceaab +Author: Ell +Date: Sun Dec 2 02:47:06 2018 -0500 + + app: add gimp_gegl_clear() + + ... which clears the alpha component of a given buffer region, + i.e., it makes the region transparent, while preserving color + information. This corresponds to the "edit-clear" action. + + (cherry picked from commit 2e3eab7fbdf110f0bd2673408807e947b581f2a5) + + app/gegl/gimp-gegl-loops.cc | 52 + +++++++++++++++++++++++++++++++++++++++++++++ + app/gegl/gimp-gegl-loops.h | 3 +++ + 2 files changed, 55 insertions(+) + +commit f7f454341615c06b43f61f75e2417dc652b76ca5 +Author: Ell +Date: Sun Dec 2 02:40:27 2018 -0500 + + app: in GimpPaintCore, align copied undo rect to tile grid + + In gimp_paint_core_finish(), when copying the relevant region of + the cached undo buffer into a new buffer, align the region to the + buffer's tile grid, so that all copied tiles are COWed. This + avoids lag when finishing a stroke. + + (cherry picked from commit 861f356b6354669781efbe9b3d342d2722bbf8f0) + + app/paint/gimppaintcore.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +commit 782d54a615f3e8e841218eb46c2c9330dad67582 +Author: Ell +Date: Sun Dec 2 02:36:17 2018 -0500 + + app: align drawable undo rectangle to tile grid + + When creating a drawable undo from the drawable's buffer, align the + copied rectangle to the buffer's tile grid, so that all the copied + tiles are COWed, saving memory and gaining speed. + + Add applied_x and applied_y fields to GimpDrawableUndo, specifying + the position at which to apply the applied_buffer, so that we apply + it in the right place, even if the undo rect has changed due to + alignment. + + (cherry picked from commit bb9dd049fb570353a7fc40e3af7d887d3a6be580) + + app/core/gimpdrawable.c | 36 +++++++++++++++++++++++++++++++++--- + app/core/gimpdrawableundo.h | 2 ++ + app/core/gimpimage-fade.c | 6 ++---- + 3 files changed, 37 insertions(+), 7 deletions(-) + +commit 6e82635d4fc4fad65514ffb719980bd4151ca36c +Author: Ell +Date: Sun Dec 2 02:33:20 2018 -0500 + + app: use gimp_gegl_rectangle_align_to_tile_grid() in + gimp:buffer-source-validate + + ... instead of custom code. + + (cherry picked from commit ce3a6c4fd60aba26d4541884c27542440d14d7d0) + + app/operations/gimpoperationbuffersourcevalidate.c | 22 + +++------------------- + 1 file changed, 3 insertions(+), 19 deletions(-) + +commit 2b1435568991a632faa8ca8e96f08ef569a08e67 +Author: Ell +Date: Sun Dec 2 02:30:55 2018 -0500 + + app: add gimp_gegl_rectangle_align_to_tile_grid() + + ... which expands a GeglRectangle, such that it's aligned to the + tile grid of a given GeglBuffer. + + (cherry picked from commit 69e5f783c57c8b18e5f009c13fbf802d2b2d6d89) + + app/gegl/gimp-gegl-utils.c | 36 ++++++++++++++++++++++++++++++++++++ + app/gegl/gimp-gegl-utils.h | 26 +++++++++++++++----------- + 2 files changed, 51 insertions(+), 11 deletions(-) + +commit 05684909297ddcda5e62fbc4a59e5aab221bf442 +Author: Ell +Date: Sat Dec 1 10:01:51 2018 -0500 + + app: a few improvements to gimp-scratch + + Simplify code a bit, and add branch-prediction annotations. + + (cherry picked from commit f27b38808f5bc1c375839ed972e3b1e289d33052) + + app/core/gimp-scratch.h | 32 +++++++++++++------------------- + 1 file changed, 13 insertions(+), 19 deletions(-) + +commit 54e93cefb33219980df70c07f3f3b7c9517b1a95 +Author: Michael Natterer +Date: Sat Dec 1 15:15:10 2018 +0100 + + Issue #1297 - Unhide the items hidden in the Advanced drop-down + file... + + ...export dialogs + + Remove the "Advanced" expanders from the PNG and TIFF export + dialogs. This does not reorder anything in the GUI yet, the + dialogs are still ugly. + + plug-ins/ui/plug-in-file-png.ui | 131 +++++++++++++++----------------- + plug-ins/ui/plug-in-file-tiff.ui | 160 + ++++++++++++++------------------------- + 2 files changed, 117 insertions(+), 174 deletions(-) + +commit a30087e83ed7dae97fe9122dc1e722feda1aa3fd +Author: Ell +Date: Sat Dec 1 08:24:37 2018 -0500 + + app: sigh, another gimp-scratch link fix, in config/ + + That's the last one :) + + (cherry picked from commit 67d595f651e256e01f9acf4c2bde3b47cb8197c0) + + app/config/Makefile.am | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 1a6281961e492fd481938f447ba7135ab83bd46a +Author: Ell +Date: Sat Dec 1 07:19:57 2018 -0500 + + app: fix gimp-scratch linking in tests/ + + app/tests/Makefile.am | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 5b5027dee17035b14ab0d5d6aaf057bf2a9dd9f0 +Author: Ell +Date: Sat Dec 1 06:21:59 2018 -0500 + + app: fix gimp-scratch linking + + (cherry picked from commit ae6bebb9815fc3dad76efba5dd1b09c403c604e7) + + app/Makefile.am | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 806db2e64ce50d6d0811b1da65beca1de477ef6c +Author: Ell +Date: Sat Dec 1 06:05:47 2018 -0500 + + app: #include in gimp-scratch.h + + ... for memset(). + + (cherry picked from commit 8c9eb1c743db0e42aadca292fba3d91333ba2297) + + app/core/gimp-scratch.h | 3 +++ + 1 file changed, 3 insertions(+) + +commit 59a3b9e5c0b596606841ff7b33eca49aadd2d63e +Author: Ell +Date: Sat Dec 1 05:57:25 2018 -0500 + + app: fix definition signature of gimp_scratch_get_total() + + (cherry picked from commit 5d1a79a34f7e0c5efeb3672a3df24283c39b31af) + + app/core/gimp-scratch.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 595218f1d4a822c3ad92071dfed266b13763a641 +Author: Ell +Date: Sat Dec 1 05:35:38 2018 -0500 + + app: in Luminance mode, cache RGBA -> Y fish + + (cherry picked from commit dbab0b557dfc589b2da7511ef5c2ba02f72a4aa3) + + app/operations/layer-modes/gimpoperationlayermode-blend.c | 14 + +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +commit 963f036a4aa202024e2ec136364e48c4a2456d38 +Author: Ell +Date: Sat Dec 1 05:31:37 2018 -0500 + + app: in Luminance mode, replace VLAs with gimp-scratch + + In the Luminance layer-mode, use the scratch allocator for + allocating temporary buffers, instead of using VLAs. + GimpOperationLayerMode already allocates data on the stack, + calculated as not to overflow the stack on any platform, so having + any of its descendants also allocate big buffers on the stack is + risky. + + (cherry picked from commit 70b7316ebc49b4b5e1681b45f7c67d734905e42b) + + .../layer-modes/gimpoperationlayermode-blend.c | 31 + +++++++++++++--------- + 1 file changed, 19 insertions(+), 12 deletions(-) + +commit c3ce696ce0829c4394c62e2373ef615a6bf36a36 +Author: Ell +Date: Sat Dec 1 05:28:49 2018 -0500 + + app: add scratch-total variable to the dashboard + + Add a scratch-total variable to the dashboard's misc group, showing + the total amount of memory used by the scratch allocator. + + (cherry picked from commit 698d1af798d48e4a9b5f403d8b8b147c25fb7e82) + + app/core/gimp-scratch.c | 10 ++++++++++ + app/core/gimp-scratch.h | 5 +++++ + app/widgets/gimpdashboard.c | 14 ++++++++++++++ + 3 files changed, 29 insertions(+) + +commit 8593eb88fdeb8233d88f54b8ec49a2914133475a +Author: Ell +Date: Sat Dec 1 05:18:24 2018 -0500 + + app: add gimp-scratch allocator + + gimp-scratch is a fast memory allocator (on the order of magnitude + of alloca()), suitable for small (up to a few megabytes), short- + lived (usually, bound to the current stack-frame) allocations. + Unlike alloca(), gimp-scratch doesn't use the stack, and is + therefore safer, and will also serve bigger requests, by falling- + back to malloc(). + + The allocator itself is very simple: We keep a per-thread stack of + cached memory blocks (allocated using the normal allocator). When + serving an allocation request, we simply pop the top block off the + stack, and return it. If the block is too small, we replace it with + a big-enough block. When the block is freed, we push it back to + the top of the stack (note that even though each thread uses a + separate stack, blocks can be migrated between threads, i.e., + allocated on one thread, and freed on another thread, although this + is not really an intended usage pattern.) The idea is that the + stacks will ultimately stabalize to contain blocks that can serve + all the encountered allocation patterns, without needing to reisze + any of the blocks; as a consequence, the amount of scratch memory + allocated at any given time should really be kept to a minimum. + + (cherry picked from commit a8a86552851f23b2f976e1a6f26982cf2fcb7f9d) + + app/core/Makefile.am | 2 + + app/core/gimp-scratch.c | 90 ++++++++++++++++++++++++++ + app/core/gimp-scratch.h | 164 + ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 256 insertions(+) + +commit 60c7c2e9375944253c1aa7b2b17b7d98941dedad +Author: Claude Paroz +Date: Sat Dec 1 11:08:04 2018 +0100 + + Updated French translation + + po-script-fu/fr.po | 282 + +++++++++++++++++++++++++++++------------------------ + 1 file changed, 152 insertions(+), 130 deletions(-) + +commit f217accbf4f94531ff68b37fad7e842f8d11700d +Author: Claude Paroz +Date: Sat Dec 1 09:57:06 2018 +0000 + + Update French translation + + po/fr.po | 6590 + +++++++++++++++++++++++++++++++++++--------------------------- + 1 file changed, 3711 insertions(+), 2879 deletions(-) + +commit d86d01798045ff32b5e2e82525ffd2024a82a76c +Author: Ell +Date: Sat Dec 1 04:29:49 2018 -0500 + + app: in GimpBacktrace Linux backend, don't leak backtrace when + dropping threads + + Should have been part of commit + a29d040db52706d4e26f3b7955d5e1677528702e. + + (cherry picked from commit 01f940990260146a860f6956ebd962b45b0fc961) + + app/core/gimpbacktrace-linux.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit 31acea2cf7791450db8957c72dce7fb469ccb17d +Author: Ell +Date: Sat Dec 1 03:51:12 2018 -0500 + + app: in GimpBacktrace Linux backend, make blacklisted_thread_names + const + + (cherry picked from commit c749097dcc0f70f4986dd030980f9e1575a73b5f) + + app/core/gimpbacktrace-linux.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit b493cb51f4361e8eadf3139dc27ca03c0ebf28e7 +Author: Ell +Date: Fri Nov 30 03:32:33 2018 -0500 + + app: use gimp_async_add_callback_for_object() in various places + + Use gimp_async_add_callback_for_object(), added in the previous + commit, instead of gimp_async_add_callback(), in cases where the + destructor of the object owning the async doesn't wait for the + async to finish. This avoids leaking such ongoing asyncs on + shutdown, during which gimp-parallel either finishes or aborts the + asyncs: if at this point an async has any registered callbacks, an + idle source is added for running the callbacks, extending the + lifetime of the async; however, since we're not getting back into + the main loop, the idle is never run, and the async (and any + associated resources) are never freed. + + (cherry picked from commit 7c00cf498af3de9b2d9ef4208a3d3169cae64c2c) + + app/text/gimpfontfactory.c | 8 +++++--- + app/widgets/gimpviewrendererdrawable.c | 3 ++- + 2 files changed, 7 insertions(+), 4 deletions(-) + +commit 34cb63a04844e88dc2c4b78b3e5cb2ef628d5032 +Author: Ell +Date: Fri Nov 30 03:28:18 2018 -0500 + + app: add gimp_async_add_callback_for_object() + + ... which is similar to gimp_async_add_callback(), taking an + additional GObject argument. The object is kept alive for the + duration of the callback, and the callback is automatically removed + when the object is destroyed (if it hasn't been already called). + + This is analogous to g_signal_connect_object(), compared to + g_signal_connect(). + + (cherry picked from commit 49fd2847ac6ed02963a3feed31f3e704ee217d55) + + app/core/gimpasync.c | 130 + +++++++++++++++++++++++++++++++++++++++++++++------ + app/core/gimpasync.h | 46 +++++++++--------- + 2 files changed, 142 insertions(+), 34 deletions(-) + +commit 9001d658048416f50164e954c22e69f65d4e4713 +Author: Ell +Date: Fri Nov 30 03:09:21 2018 -0500 + + app: unref async when removing last callback if idle is pending + + In gimp_async_remove_callback(), if removing the last callback + while the callback idle-source is already pending, cancel the idle + source and unref the async object (the async is reffed when adding + the idle source.) + + (cherry picked from commit a779dd38494e89eb91c4dc1fe23fcc5de321137f) + + app/core/gimpasync.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +commit d20efd357a2370f2c3eefe08ad6a1b675a33c747 +Author: Alexandre Prokoudine +Date: Thu Nov 29 00:09:02 2018 +0300 + + Update NEWS + + NEWS | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +commit b0b541d67009d5f730d43475cdb0c9f0d14c15ee +Author: Ell +Date: Wed Nov 28 13:19:08 2018 -0500 + + app: use gimp_tile_handler_validate_validate() in + gimp:buffer-source-validate + + Use gimp_tile_handler_validate_validate(), added in the commit + before last, in gimp:buffer-source-validate, in order to pre-render + the necessary region of the buffer, instead of performing the + validation implicitly by iterating over the region. This is both + simpler, and, more importantly, allows us to render the entire + region in a single chunk, instead of tile-by-tile, which can be + considerably more efficient, especially with high thread counts. + + This essentially extends the dynamic sizing of rendered projection + chunks to layer groups, which are rendered through + gimp:buffer-source-validate, rather than just the main image + projection. + + (cherry picked from commit 83dd94ba6a12d39458943421a728dfbff020e18b) + + app/operations/gimpoperationbuffersourcevalidate.c | 103 + +++++---------------- + 1 file changed, 21 insertions(+), 82 deletions(-) + +commit 378dc07c7b2e7dc849a1a62fbec4471200155954 +Author: Ell +Date: Wed Nov 28 13:08:57 2018 -0500 + + app: use gimp_tile_handler_validate_validate() in GimpProjection + + Use gimp_tile_handler_validate_validate(), added in the last + commit, in GimpProjection, in order to render the projection, + instead of separately invalidating the buffer, undoing the + invalidation, and then rendering the graph. This is more + efficient, and more idiomatic. + + (cherry picked from commit d6f0ca5531e73f8f002b41e700b71a166e5fe5ae) + + app/core/gimpprojection.c | 32 ++++++++++++++------------------ + 1 file changed, 14 insertions(+), 18 deletions(-) + +commit a4fb3e11d67792592e9c90719e98a33bc5466295 +Author: Ell +Date: Wed Nov 28 13:04:46 2018 -0500 + + app: add gimp_tile_handler_validate_validate() + + ... which validates a given rectangle directly into the buffer, + possibly intersecting it with the dirty region. This is more + efficient than either invalidating, un-invalidating, and rendering + a given rect, as we're doing in GimpProjection, or validating the + buffer tile-by-tile, as we're doing in gimp:buffer-source-validate. + + (cherry picked from commit 82a60997d485f4df5f39c97fe10bcae7893dd235) + + app/gegl/gimptilehandlervalidate.c | 74 + +++++++++++++++++++++++++++++++++++--- + app/gegl/gimptilehandlervalidate.h | 5 +++ + 2 files changed, 74 insertions(+), 5 deletions(-) + +commit 50b3c2b8ba58a7536f1219e755dc03807848e7cb +Author: Ell +Date: Tue Nov 27 13:49:38 2018 -0500 + + app: add GimpTileHandlerValidate::validate_buffer() vfunc + + ... which is similar to the ::validate() vfunc, however, it should + render the result to the provided GeglBuffer, instead of to a + memory buffer. + + Provide a default implementation, which uses + gegl_node_blit_buffer() if the default ::validate() implementation + is used, or, otherwise, calls uses + gegl_buffer_linear_{open,close}(), and passes the returned memory + buffer to ::validate(). + + (cherry picked from commit 0ad41cfe0c15aaea3d910bc57353c872760f2dd1) + + app/gegl/gimptilehandlervalidate.c | 80 + ++++++++++++++++++++++++++------------ + app/gegl/gimptilehandlervalidate.h | 17 ++++---- + 2 files changed, 66 insertions(+), 31 deletions(-) + +commit cc81f66f12d16719b5da0b716bfe03af4ba2c7cb +Author: Ell +Date: Tue Nov 27 13:32:55 2018 -0500 + + app: add GimpTileHandlerValidate::{begin,end}_validate() vfuncs + + Add begin_validate() and end_validate() virtual functions, and + corresponding free functions, to GimpTileHandlerValidate. These + functions are called before/after validation happens, and should + perform any necessary steps to prepare for validation. The default + implementation suspends validation on tile access, so that the + assigned buffer may be accessed without causing validation. + + Implement the new functions in GimpTileHandlerProjectable, by + calling gimp_projectable_begin_render() and + gimp_projectable_end_render(), respectively, instead of calling + these functions in the ::validate() implementation (which, in turn, + allows us to use the default ::validate() implementation.) + + In GimpProjection, use the new functions in place of + gimp_projectable_{begin,end}_render(). + + (cherry picked from commit 5a623fc54b08c5aa4e0b03cf117357447e6955ce) + + app/core/gimpprojection.c | 8 ++-- + app/core/gimptilehandlerprojectable.c | 32 ++++++------- + app/gegl/gimptilehandlervalidate.c | 87 + ++++++++++++++++++++++++++--------- + app/gegl/gimptilehandlervalidate.h | 16 +++++-- + 4 files changed, 96 insertions(+), 47 deletions(-) + +commit 18815512f2b1e45c43201c5b5c8178100a558d7e +Author: Ell +Date: Wed Nov 28 12:59:10 2018 -0500 + + app: avoid starting the chunk renderer while finishing drawing + a projection + + In gimp_projection_finish_draw(), make sure we don't accidentally + re-start the chunk renderer idle source while running the remaining + iterations, in case the chunk height changes, and we need to reinit + the renderer state. + + (cherry picked from commit 8a47b6819439d16c140c40fbb14f95ad6ede0d3a) + + app/core/gimpprojection.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +commit 6ab3ecaca2d2532c7791c8a9f23319cb08556116 +Author: Ell +Date: Wed Nov 28 12:53:52 2018 -0500 + + app: avoid flushing bufferless projections + + Don't needlessly flush projections whose buffer hasn't been + allocated yet. This can happen when opening an image, in which + case the image is flushed before its projection has a buffer. + + (cherry picked from commit b07f8102738d8225c1e77e6b4f68ae12468d8a2b) + + app/core/gimpprojection.c | 3 +++ + 1 file changed, 3 insertions(+) + +commit 22b11da8916ae3d4c19f496cd1a480cd1857415a +Author: Daniel Mustieles +Date: Wed Nov 28 15:17:18 2018 +0000 + + Update Spanish translation + + po/es.po | 924 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 494 insertions(+), 430 deletions(-) + +commit fc7d61a6ea5d2188cdd8143056ab9bf748ffe124 +Author: Marco Ciampa +Date: Sat Nov 24 18:31:39 2018 +0100 + + Updated Italian translation + + po/it.po | 940 + ++++++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 510 insertions(+), 430 deletions(-) + +commit 90292953c3875868bcdc44320bf184d0b715024f +Author: Ell +Date: Mon Nov 19 11:46:19 2018 -0500 + + app: in gimp-parallel, boost priority of waited-upon asyncs + + When an async that was created through + gimp_parallel_run_async[_full](), and whose execution is still + pending, is being waited-upon, maximize its priority so that it + gets executed before all other pending asyncs. + + Note that we deliberately don't simply execute the async in the + calling thread in this case, to allow timed-waits to fail (which is + especially important for gimp_wait()). + + (cherry picked from commit 62baffed984f5505b77896a104d269e0060047cc) + + app/core/gimp-parallel.cc | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +commit 8222124a969239c65a78fcc9253287cff8c9c225 +Author: Ell +Date: Mon Nov 19 11:44:59 2018 -0500 + + app: add GimpAsync::waiting signal + + ... which is emitted when the async is being waited-upon, blocking + execution. + + (cherry picked from commit 965da12b350c0844395050a706efe98ee49decd4) + + app/core/gimpasync.c | 43 ++++++++++++++++++++++++++++++++++++------- + app/core/gimpasync.h | 3 +++ + 2 files changed, 39 insertions(+), 7 deletions(-) + +commit 79a44eda6d5d4957b883826d506d77e79a61278b +Author: Ell +Date: Wed Nov 14 10:58:58 2018 -0500 + + app: more gimp-parallel fixes + + Fix indentation in gimp-parallel.{cc,h}. + + Remove unused typedefs in gimp-parallel.h. + + s/Gimp/Gegl/ in function-type cast in gimphistogram.c. + + (cherry picked from commit 05a4437d9aaf73b040c3a9c1b13fcad1ec0b77f5) + + app/core/gimp-parallel.cc | 32 ++++++++++++++++---------------- + app/core/gimp-parallel.h | 39 +++++++++++++++------------------------ + app/core/gimphistogram.c | 2 +- + 3 files changed, 32 insertions(+), 41 deletions(-) + +commit 8fe66459508d5b34f5f76f40023cfb5555722b13 +Author: Ell +Date: Wed Nov 14 10:53:44 2018 -0500 + + app: indentation fix in gimp-parallel.cc + + (cherry picked from commit 115fc174f24e3c613de125936d2b823c3be4789b) + + app/core/gimp-parallel.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d4312fd5f745fa18378c5178a70349ffc5ea7412 +Author: Ell +Date: Wed Nov 14 10:49:52 2018 -0500 + + app: remove gimp_parallel_distribute(); use gegl_parallel_distribute() + + The parallel_distribute() family of functions has been migrated to + GEGL. Remove the gimp_parallel_distribute() functions from + gimp-parallel, and replace all uses of these functions with the + corresponding gegl_parallel_distrubte() functions. + + (cherry picked from commit 2736cee5772723cb4fd70cb7d6881256c8f4ca99) + + app/core/gimp-parallel.cc | 272 + --------------------------------------- + app/core/gimp-parallel.h | 66 ---------- + app/core/gimpbrush-transform.cc | 27 ++-- + app/core/gimphistogram.c | 8 +- + app/gegl/gimp-gegl-loops.cc | 60 +++++---- + app/paint/gimpbrushcore-loops.cc | 29 ++--- + app/paint/gimppaintcore-loops.cc | 15 ++- + 7 files changed, 75 insertions(+), 402 deletions(-) + +commit ac4ded137f755811151007bf9c234f94073374aa +Author: Ell +Date: Sat Nov 10 16:23:56 2018 -0500 + + configure.ac: require GEGL >= 0.4.13 + + (cherry picked from commit 43e3939d4a7ba8db0dade91bbba8b4659618ccd4) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 63e86da6f3b696d448415c0c8c9bea83bdd68845 +Author: Oleksii Samorukov +Date: Fri Nov 23 23:15:25 2018 +0100 + + tests: OSX - activate GIMP window when tests are running + + If window is not focused gimp_test_utils_synthesize_key_event would + fail. + + app/Makefile.am | 7 +++++++ + app/tests.c | 15 +++++++++++++++ + 2 files changed, 22 insertions(+) + +commit 9ae19eb8ec04adc28cdbe4045068a4f446ffa708 +Author: Oleksii Samorukov +Date: Fri Nov 23 23:06:12 2018 +0100 + + tests: implement gimp_test_utils_synthesize_key_event on OSX/QUARTZ + + app/tests/Makefile.am | 7 ++++ + app/tests/gimp-app-test-utils.c | 77 + ++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 83 insertions(+), 1 deletion(-) + +commit 40a08dcf48981d84a6d44807bc721dc121bec99b +Author: Oleksii Samorukov +Date: Fri Nov 23 22:32:32 2018 +0100 + + tests: fix clang compilation error on test-eevl.c + + libgimpwidgets/test-eevl.c | 71 + +++++++++++++++++++++++----------------------- + 1 file changed, 35 insertions(+), 36 deletions(-) + +commit 4b647c52e1c913fdfd95e6939f8c1427f146fa0a +Author: Ell +Date: Fri Nov 23 08:42:35 2018 -0500 + + Issue #2553 - Can't Move Imported or Pasted Path + + Initialize the X/Y tilt fields of improted/pasted path control + points to 0, instead of 0.5, which is the normal value for these + fields in paths. This avoids calculating bogus distances when + trying to pick the path, causing picking to fail. + + (cherry picked from commit 0a123a81a3335f180e1e4aaecf63ee1253d7a6fb) + + app/vectors/gimpvectors-import.c | 45 + ++++++++++++++++++++++++++-------------- + 1 file changed, 29 insertions(+), 16 deletions(-) + +commit 62c52742cd8d6de3c60f3381e6913939b31cd4d7 +Author: Alex Samorukov +Date: Fri Nov 23 11:39:21 2018 +0100 + + do not activate OSX menu if tests are running to prevent crash + + app/gui/gui.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +commit d650e9f81f3362ce4e93af10f334d49c2a354d03 +Author: Alex Samorukov +Date: Fri Nov 23 10:45:39 2018 +0100 + + add to fix test link on OSX + + app/tests/Makefile.am | 1 + + 1 file changed, 1 insertion(+) + +commit a3b5a7fb325ef31a30ea8dc3750ac56f4feb9289 +Author: Alexandre Prokoudine +Date: Fri Nov 23 01:51:29 2018 +0300 + + Update NEWS + + NEWS | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +commit 465b4b10fb0b909a69a77abfade2bdfe951987e3 +Author: Alex Samorukov +Date: Thu Nov 22 13:45:30 2018 +0000 + + Enable hires retina support for the GTK2/OSX build + + app/display/gimpdisplayshell-draw.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +commit 0a39f362b52c8c12646a134f74daf6f66456e92c +Author: Ell +Date: Mon Nov 19 16:13:07 2018 -0500 + + libgimpbase, libgimp, app: pass misc. GEGL config to plug-ins + + Pass the GEGL tile-cache size, swap path, and thread-count to plug- + ins as part of their config, and have libgimp set the plug-in's + GeglConfig accordingly upon initialization. + + app/plug-in/gimppluginmanager-call.c | 3 +++ + libgimp/gimp.c | 3 +++ + libgimpbase/gimpprotocol.c | 30 ++++++++++++++++++++++++++++++ + libgimpbase/gimpprotocol.h | 7 ++++++- + 4 files changed, 42 insertions(+), 1 deletion(-) + +commit 7de86c63e66266440449e665228c9475d73d455d +Author: Ell +Date: Mon Nov 19 16:06:28 2018 -0500 + + libgimpbase: add _gimp_wire_{read,write}_int64() + + ... for reading/writing guint64 data in the wire protocol. + + (cherry picked from commit 77c24ca16c521b1c4db0c4f45554bba0ae7a85cc) + + libgimpbase/gimpwire.c | 49 + +++++++++++++++++++++++++++++++++++++++++++++++++ + libgimpbase/gimpwire.h | 8 ++++++++ + 2 files changed, 57 insertions(+) + +commit 4d542a4ba83cb673a4df0529fc7c528508c86c48 +Author: Ell +Date: Mon Nov 19 16:31:04 2018 -0500 + + libgimpbase: don't leak GPConfig::icon_theme_dir + + (cherry picked from commit 9afa42ab9134757169247fbee762cd8545d2ceaf) + + libgimpbase/gimpprotocol.c | 1 + + 1 file changed, 1 insertion(+) + +commit 46d476869985013ea3e620240eaaf445bb3bc5e3 +Author: Ell +Date: Mon Nov 19 09:27:01 2018 -0500 + + libgimpwidgets: install gimpspinbutton.h + + Added in commit 7ab9ee268637093902ec243a30d6a96437eba3fc. + + (cherry picked from commit 03a928409a46c1255dae6e238cc0d89a07fe76bb) + + libgimpwidgets/Makefile.am | 1 + + 1 file changed, 1 insertion(+) + +commit ab48f12fd68378640960faa3017a1051241712f4 +Author: Michael Natterer +Date: Sun Nov 18 18:10:34 2018 +0100 + + Issue #2224 - Use the "Swap folder" setting for the GEGL cache + + Move swap/cache and temporary files out the GIMP user config dir: + + libgimpbase: add gimp_cache_directory() and gimp_temp_directory() + which return the new default values inside XDG_CACHE_HOME and the + system temp directory. Like all directories from gimpenv.[ch] the + values can be overridden by environment variables. Improve API docs + for all functions returning directories. + + Add new config file substitutions ${gimp_cache_dir} and + ${gimp_temp_dir}. + + Document all the new stuff in the gimp and gimprc manpages. + + app: default "swap-path" and "temp-path" to the new config file + substitutions. On startup and config changes, make sure that the swap + and temp directories actually exist. + + In the preferences dialog, add reset buttons to all file path pages. + + (cherry picked from commit a29f73bd9abb20a1074196811f9c8be9508a52ba) + + app/config/gimpconfig-dump.c | 8 +++ + app/config/gimpgeglconfig.c | 4 +- + app/dialogs/preferences-dialog.c | 107 +++++++++++++++++++++++++++----- + app/gegl/gimp-gegl.c | 53 +++++++++++++++- + docs/gimp.1.in | 8 +++ + libgimpbase/gimpbase.def | 2 + + libgimpbase/gimpenv.c | 129 + ++++++++++++++++++++++++++++++++++++--- + libgimpbase/gimpenv.h | 2 + + libgimpconfig/gimpconfig-path.c | 6 ++ + 9 files changed, 291 insertions(+), 28 deletions(-) + +commit 5bab4a2c4aa154c26b5e0eedf3bf51d8dd1fb987 +Author: Piotr Drąg +Date: Sun Nov 18 13:23:40 2018 +0100 + + Update Polish translation + + po-plug-ins/pl.po | 832 + +++++++++++++++++++++++++++-------------------------- + po-script-fu/pl.po | 277 +++++++++--------- + po/pl.po | 54 ++-- + 3 files changed, 597 insertions(+), 566 deletions(-) + +commit dc7bbe971d3a737e5d644b6e80741a520e6f24d4 +Author: Alexandre Prokoudine +Date: Fri Nov 16 03:14:14 2018 +0300 + + Update NEWS + + NEWS | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +commit 70a15348422748cee9f7402735c15b642e2e343f +Author: Alexandre Prokoudine +Date: Wed Nov 14 09:17:21 2018 +0000 + + Help menu: various updates + + Commented out the inactive plug-ins registry: no point linking to + a dead page. + + Added links to the roadmap page, main wiki page, and the bug + tracker. Placed + the link to the bug tracker one level up from '/Help/GIMP + Online' + to make it more visible. + + plug-ins/script-fu/scripts/gimp-online.scm | 75 + +++++++++++++++++++++++++----- + 1 file changed, 63 insertions(+), 12 deletions(-) + +commit 6f61a7d431e46b3fa985c22124e6640ce42143b2 +Author: Ell +Date: Thu Nov 15 12:54:25 2018 -0500 + + configure.ac: require babl >= 0.1.60 + + (cherry picked from commit 3821ce4bcd3745fbd88c72c5423181eedb65da6b) + + app/sanity.c | 2 +- + configure.ac | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 5b93eee52a656ac3fd5829b49c62184f221337d5 +Author: Ell +Date: Thu Nov 15 12:48:23 2018 -0500 + + app: in scale tool, scale around center even when using numeric input + + In the scale tool, when the "around center" option is toggled, + scale the item around its center not only through canvas + interaction, but also when entering width/height values through the + tool GUI. + + (cherry picked from commit 786bfa517120f772eddf64d453ab12734d24fba4) + + app/tools/gimpscaletool.c | 22 ++++++++++++++++++++-- + 1 file changed, 20 insertions(+), 2 deletions(-) + +commit 4991603b2a6d698df015658a82da705959a664f5 +Author: Jernej Simončič +Date: Wed Nov 14 17:28:49 2018 +0100 + + Installer: remove LIBTHAI_DICTDIR + environment variable (no longer needed, see + https://gitlab.gnome.org/GNOME/gimp/issues/2496#note_365235 ) + + build/windows/installer/gimp3264.iss | 2 -- + 1 file changed, 2 deletions(-) + +commit fd2008c5c23f491ec820457ec73b418ea80ad8f8 +Author: Jehan +Date: Wed Nov 14 13:44:02 2018 +0100 + + NEWS: update. + + NEWS | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 84ce89a9bef518f7b531d1ed2e71bed66015e37d +Author: Jehan +Date: Wed Nov 14 13:20:04 2018 +0100 + + Issue #2501: Confusing wording in "Export Image as PDF" dialog. + + Nothing said what was going to be the order of the page, except by + testing. Now there will be an explicit text, which will be + automatically + updated when checking the "reverse order" box. + + (cherry picked from commit afe1de950fa73fc58ad671b3253b3608b622fbdf) + + plug-ins/common/file-pdf-save.c | 55 + +++++++++++++++++++++++++++++++---------- + 1 file changed, 42 insertions(+), 13 deletions(-) + +commit cee9a1a1c6e754fb708973bd2308b6b70e113fce +Author: Alexandre Prokoudine +Date: Wed Nov 14 12:27:54 2018 +0000 + + Update NEWS + + NEWS | 36 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +commit cd55cb3102d15f348d1353a7f4e9a32c498ed500 +Author: ONO Yoshio +Date: Tue Nov 13 11:30:49 2018 +0900 + + Issue #2489 - Gimp 2.10.8 layer menu text along path + + Related #2064 - text along path not working with vertical text. + + (cherry picked from commit a2da1cd596b90d431d3f754cab02eeb9fdf202c5) + + app/actions/layers-commands.c | 36 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +commit ec300891d3b785fccad8003eb21a01f7ba274cf6 +Author: Adam Ostruszka +Date: Tue Nov 13 01:21:31 2018 +0300 + + Update Czech translation + + po/cs.po | 31781 + ++++++++++++++++++++++++++++++++++++++++--------------------- + 1 file changed, 21115 insertions(+), 10666 deletions(-) + +commit 920f66a4202c070915977de811706a8d9081dcd6 +Author: Daniel Korostil +Date: Mon Nov 12 20:08:58 2018 +0000 + + Update Ukrainian translation + + po/uk.po | 46 ++++++++++++++++++++++++---------------------- + 1 file changed, 24 insertions(+), 22 deletions(-) + +commit 936d9523124fc4a2361b5dd5814c15f40668aeab +Author: Daniel Korostil +Date: Mon Nov 12 20:07:47 2018 +0000 + + Update Ukrainian translation + + po-plug-ins/uk.po | 975 + +++++++++++++++++++++++++++--------------------------- + 1 file changed, 495 insertions(+), 480 deletions(-) + +commit 9ae2e4932d12166714ea7c36fc7afa80fc2d587b +Author: Ell +Date: Sun Nov 11 05:45:07 2018 -0500 + + configure.ac: escape backslash chars in compiler version string + + When constructing CC_VERSION, escape backslash characters in the + compiler version string, so that they don't get interpreted as + escape sequences by the compiler. This is especially important on + Windows, where the version string of MinGW may contain backslash + characters as part of paths. + + (cherry picked from commit c0b107531e65e47149c248449ca7bac8d20632ee) + + configure.ac | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +commit 7ca3fe6576a5dfb472b9debbcf99e11d8fc41282 +Author: Ell +Date: Sun Nov 11 02:10:44 2018 -0500 + + Issue #2473 - Transforming a layer doesn't properly transform its mask + + In gimp_drawable_transform_buffer_affine(), avoid modifying the + clipping mode when transforming layer masks, since this function is + used (among other things) to transform layer masks together with + their layer, in which case they should use the same clipping mode + as the layer. + + This fixes a regression introduced by commit + 2ae823ba2b852c8f6d36a462db6b71662ffe61f0, causing layer masks to be + transformed with a mismatched clipping mode during layer + transforms, leading to discrepencies between the transformed layer + and the transformed mask. + + This commit merely reverts the necessary part of above commit, + fixing the regression, though note that this code is really up for + some serious refactoring: the logic for determining which clipping + mode to use when is spread all over the place. + + (cherry picked from commit 45fc30caa7f9ed6e0483dfee221e719650dae0b4) + + app/core/gimpdrawable-transform.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +commit 47103dc28fb9070229244f40d3e3f2caaad142b9 +Author: Jernej Simončič +Date: Sat Nov 10 22:20:12 2018 +0100 + + Installer: remove lib\gegl-0.2 + + build/windows/installer/gimp3264.iss | 2 ++ + 1 file changed, 2 insertions(+) + +commit ed08c1edede1beb47da51bd80f6d3677c2f45a42 +Author: Jernej Simončič +Date: Sat Nov 10 22:05:42 2018 +0100 + + Installer: include debug symbols for babl/gegl DLLs in lib\* + + build/windows/installer/files.isi | 1 + + 1 file changed, 1 insertion(+) + +commit 3a584ca15102ddca174931cb1b4cdb80250c4950 +Author: Ell +Date: Sat Nov 10 06:36:04 2018 -0500 + + Issue #2470 - Spacing between grid lines does not stay at 1px ... + + ... as I would like it to. + + Use GimpSpinButton, added in the previous commit, in GimpSizeEntry, + instead of GtkSpinButton. This avoids updating the spin-buttons' + adjustment values when they lose focus, truncating the value if it + can't be accurately displayed using the corresponding spin-button's + digit count. Since size-entries can have multiple spin-buttons + using different units, this prevents the value from changing when + entring a value using one unit, and then shifting the focus to, but + not changing, another unit. + + libgimpwidgets/gimpsizeentry.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit d928ed72520e084b8e3d00e4d15fbbfa0b3f2e3e +Author: Ell +Date: Sat Nov 10 06:26:46 2018 -0500 + + libgimpwidgets: add GimpSpinButton + + GimpSpinButton is a drop-in replacement for (and a subclass of) + GtkSpinButton. Unlike GtkSpinButton, it avoids updating the + adjustment value when losing focus, unless the entry text has + changed. This prevents accidental loss of precision, when the + adjustment value can't be accurately displayed in the entry. + + Note that libgimpwidgets already defines a (deprecated) + gimp_spin_button_new() function. This commit stays compatible with + the old function, by defining GimpSpinButton's _new() function as + gimp_spin_button_new_(), and defining a variadic + gimp_spin_button_new() macro, which expands to either the old or + the new function, based on the number of arguments, so that either + function can be used transparently as gimp_spin_button_new(). This + is all gone in master. + + devel-docs/libgimpwidgets/libgimpwidgets-docs.sgml | 4 + + .../libgimpwidgets/libgimpwidgets-sections.txt | 17 ++ + devel-docs/libgimpwidgets/libgimpwidgets.types | 1 + + libgimpwidgets/Makefile.am | 2 + + libgimpwidgets/gimpspinbutton.c | 226 + +++++++++++++++++++++ + libgimpwidgets/gimpspinbutton.h | 90 ++++++++ + libgimpwidgets/gimpwidgets.def | 3 + + libgimpwidgets/gimpwidgets.h | 1 + + libgimpwidgets/gimpwidgetstypes.h | 1 + + 9 files changed, 345 insertions(+) + +commit 934d896fa8488582e6c550fed0e12d86b0965c85 +Author: Ell +Date: Sat Nov 10 02:51:07 2018 -0500 + + tools: in performance-log-viewer.py, fix "function()" predicate ... + + ... when not specifying a thread-ID + + (cherry picked from commit 84227fbfec87101ff83f94da01df37c211594e75) + + tools/performance-log-viewer.py | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +commit dd52d8d7a5becca99c14d15e00e2faefb0255496 +Author: Ell +Date: Sat Nov 10 02:44:56 2018 -0500 + + app: in performance logs, add new-lines between variable definitions + + (cherry picked from commit c7f17307028ff8ffa98fcaae2833d25f839639c5) + + app/widgets/gimpdashboard.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 3fd5b50f948f9217400d6a11599f0f7eb7e8b9ed +Author: Jernej Simončič +Date: Sat Nov 10 00:45:43 2018 +0100 + + Installer: proper fix for libthai + + build/windows/installer/gimp3264.iss | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +commit e40a5b9d4cfcc732609391e89c10e4305c1c5891 +Author: Jernej Simončič +Date: Fri Nov 9 22:39:08 2018 +0100 + + Installer: remove Thai locale temporarily (causes crash on Windows 7) + + build/windows/installer/gimp3264.iss | 3 +++ + 1 file changed, 3 insertions(+) + +commit 628a5ecb790a926ab6ef153bb793f2b0172fff3a +Author: Jernej Simončič +Date: Fri Nov 9 22:29:32 2018 +0100 + + Installer: include extra debug symbols, Ghostscript 9.25 + + build/windows/installer/files.isi | 3 +++ + build/windows/installer/gimp3264.iss | 6 +++--- + 2 files changed, 6 insertions(+), 3 deletions(-) + +commit 8e1e6dfb83a396e442a9585c8a785d22002eb82a +Author: Ell +Date: Fri Nov 9 02:12:20 2018 -0500 + + tools: add performance-log-coalesce.py to EXTRA_DIST + + (cherry picked from commit c61138f8f0c4273bd9c27fabed7e72d8417122d9) + + tools/Makefile.am | 1 + + 1 file changed, 1 insertion(+) + +commit ee8f162151b7eea62ad467b3927410a146073ddd +Author: Michael Natterer +Date: Thu Nov 8 20:33:33 2018 +0100 + + configure.ac: post-release version bump to 2.10.9 + + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + commit a967e8d2c2f334d99bda26389d2ce7bbef90c764 Author: Michael Natterer Date: Thu Nov 8 19:34:21 2018 +0100 diff --git a/INSTALL b/INSTALL index 259c616de9..e56fcaefac 100644 --- a/INSTALL +++ b/INSTALL @@ -38,8 +38,8 @@ header files installed. You also need gettext version 0.19 or over. Earlier gettext had issues with script-fu localization, ending up in incomplete GIMP localization. - 3. You need to have GEGL version 0.4.12 or newer and babl version - 0.1.58 or newer. You can get them from https://gegl.org/ or clone + 3. You need to have GEGL version 0.4.16 or newer and babl version + 0.1.66 or newer. You can get them from https://gegl.org/ or clone them from the GNOME git repository: https://gitlab.gnome.org/GNOME/babl.git @@ -49,7 +49,7 @@ header files installed. engine "gegl:matting-levin" and OpenEXR library for OpenEXR format support. - 4. You need to have installed GTK+ version 2.24.10 or newer. + 4. You need to have installed GTK+ version 2.24.32 or newer. GIMP also needs a recent version of GLib (>= 2.54.2), GDK-Pixbuf (>= 2.30.8), and Pango (>= 1.29.4). Sources for these can be grabbed from ftp://ftp.gtk.org/. @@ -85,6 +85,9 @@ header files installed. If installing from repository, do not install the master branch! Checkout the tag "v1.3.0" instead, or simply install from a tarball or from your favorite package manager. + You may also checkout the "libmypaint-v1" branch, which is the + development branch for libmypaint v1.x and has some more recent + fixes. 11. We also need the mypaint-brushes data package: @@ -141,23 +144,23 @@ header files installed. Package Name Version ATK 2.2.0 - babl 0.1.58 + babl 0.1.66 cairo 1.12.2 Fontconfig 2.12.4 freetype2 2.1.7 GDK-PixBuf 2.30.8 - GEGL 0.4.12 + GEGL 0.4.16 GIO GLib 2.54.2 glib-networking - GTK+ 2.24.10 + GTK+ 2.24.32 HarfBuzz 0.9.19 libbzip2 libjpeg liblzma 5.0.0 libmypaint 1.3.0 libpng 1.6.25 - libpoppler-glib 0.44.0 + libpoppler-glib 0.50.0 librsvg 2.40.6 libtiff Little CMS 2.8 @@ -204,8 +207,8 @@ packages are included below. Here is an illustration of commands that might be used to build and install GIMP. The actual configuration, compilation and installation output is not shown. - % tar xvfz gimp-2.10.8.tar.gz # unpack the sources - % cd gimp-2.10.8 # change to the toplevel directory + % tar xvfz gimp-2.10.12.tar.gz # unpack the sources + % cd gimp-2.10.12 # change to the toplevel directory % ./configure # run the `configure' script % make # build GIMP % make install # install GIMP @@ -252,11 +255,6 @@ These are: in the user's home directory) to ~/.config/DIR/2.10. If DIR is an absolute path, the directory will be changed to DIR. - --enable-binreloc. When compiled for Linux with this option enabled, - GIMP will be binary relocatable. Plug-ins and data files will - be searched relative to the gimp binary instead of in the paths - defined at compile time. - --with-shm=[none|sysv|posix|auto]. This option allows you to specify how image data is transported between the core and plug-ins. Usually the best way to do this is detected automatically. diff --git a/INSTALL.in b/INSTALL.in index 46cfb33387..505b190a24 100644 --- a/INSTALL.in +++ b/INSTALL.in @@ -85,6 +85,9 @@ header files installed. If installing from repository, do not install the master branch! Checkout the tag "v1.3.0" instead, or simply install from a tarball or from your favorite package manager. + You may also checkout the "libmypaint-v1" branch, which is the + development branch for libmypaint v1.x and has some more recent + fixes. 11. We also need the mypaint-brushes data package: @@ -252,11 +255,6 @@ These are: in the user's home directory) to ~/.config/DIR/@GIMP_APP_VERSION@. If DIR is an absolute path, the directory will be changed to DIR. - --enable-binreloc. When compiled for Linux with this option enabled, - GIMP will be binary relocatable. Plug-ins and data files will - be searched relative to the gimp binary instead of in the paths - defined at compile time. - --with-shm=[none|sysv|posix|auto]. This option allows you to specify how image data is transported between the core and plug-ins. Usually the best way to do this is detected automatically. diff --git a/Makefile.in b/Makefile.in index b94f00acaf..63f1e9bb6c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -212,8 +211,8 @@ am__DIST_COMMON = $(srcdir)/INSTALL.in $(srcdir)/Makefile.in \ $(srcdir)/config.h.in $(srcdir)/gimp-zip.in \ $(srcdir)/gimp.pc.in $(srcdir)/gimpthumb.pc.in \ $(srcdir)/gimpui.pc.in AUTHORS COPYING ChangeLog INSTALL NEWS \ - README compile config.guess config.sub install-sh ltmain.sh \ - missing py-compile + README compile config.guess config.sub depcomp install-sh \ + ltmain.sh missing py-compile DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -278,8 +277,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -409,7 +406,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -548,8 +544,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/NEWS b/NEWS index 584d2f628c..06ffdf2964 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,300 @@ we do allow some new features here, if they are not too invasive. Otherwise, this branch is only for bug-fixes. +Overview of Changes from GIMP 2.10.10 to GIMP 2.10.12 +===================================================== + +Core: + + - Add an "Incremental" option to the Dodge/Burn tool, which, + similarly to the Paintbrush, Pencil, and Eraser tools, applies the + effect incrementally as the pointer moves. + - Curves now have a concept of smooth vs corner points: smooth + points produce a smooth curve, while corner points produce a sharp + curve (previously, all points were smooth). + - Search the user font directory path on Windows (since Windows 10, + non-admin users have the ability to install fonts). This is only a + temporary hack until fontconfig adds proper upstream support: + https://gitlab.freedesktop.org/fontconfig/fontconfig/issues/144 + - In gimp_brush_core_get_paint_buffer(), when allocating a new paint + buffer, clear the old buffer *before* allocating the new one, to + reduce the amount of simultaneously allocated memory. + - In GimpPaintbrush, avoid refilling the paint buffer at each dab if + the paint color/pixmap hasn't changed, hence allowing faster + painting in some specific cases. + - Add hygon cpu detection and enable MMX/SSE support. + - Add a GimpSymmetry::get_transform() virtual function and a + corresponding gimp_symmetry_get_transform() function which return + the brush transform corresponding to a given symmetry stroke in + terms of the rotation angle and reflection flag (in contrast to + gimp_symmetry_get_operation() which returns the same transforation + in terms of a GeglNode). This allows us to simplify, fix (artifact + bugs, etc.), and improve the painting-code performance for several + symmetry painting cases. + - New gimp-brush-pipe-spacing parasite set by core code to preserve + GIH brush's spacing across reloads. + - Add a new Offset filter tool, as a front-end to gimp:offset. The + tool replaces, and provides the same interface as the drawable- + offset dialog while also providing live preview and on-canvas + interaction. + +Plug-ins: + + - Exported profile and data format strategy updated in several file + plug-ins. + + * When not saving a profile, we always export as sRGB data, as most + viewers would not display the image properly otherwise. + * When saving a profile: + * If a profile was manually assigned, we always export to the + assigned format, hence pixel data is converted accordingly, + whatever the work format. + * If no profile was assigned: + * If the export format support high-bit depth, we export the + work format as-is. + * If the export format is 8-bit max, we convert the work + format to sRGB, except if the work format is 8-bit linear. + + The following plug-ins were updated: JPEG, PNG, TIFF and WebP. + + - TIFF plug-in now supports layer export. + - Add accelerator on "Save color _profile" item in JPEG export so + that all items in the dialog have keyboard accelerators. + - Add a "Save color profile" checkbox in WebP export and remove the + "Advanced Options" expander (all metadata and profile writing + options are now directly visible). + - Process cases of non-conformant TIFF file with extra channels + while ExtraSamples is not set by assuming the first extra channel + is a non-premultiplied alpha channel and outputting a warning. In + the future, this should be updated further by a dialog asking what + to do with the non-defined dialog (consider as premultiplied or + non-premultiplied alpha channel, or just save as extra channels). + +Tools: + + - Add input/output spin-buttons to the Curves tool, which allow + setting the selected point's coordinates numerically. When the image + precision is greater than 8-bpc, use a 0.00-100.00 range for the + point-coordinate spin-buttons instead of a 0-255 range. + - In the Curves tool, allow changing the curve's point types (cf. new + curve corner points in Core section). Corner points are displayed + with a diamond shape instead of a circle. + - Free Select tool now creates a selection as soon as the polygon is + closed. This selection is updated when the polygon, or the relevant + tool-options, change, similarly to GimpRectangleSelectTool. + - Derive GimpEraser from GimpPaintbrush, instead of directly from + GimpBrushCore. + This allows GimpEraser to reuse the paint-buffer content across + dabs, improving performance. + - Allow moving an intersecting pair of guides with the Move tool. + +User interface: + + - In GimpCurveView, when dragging an existing curve point, don't + immediately move the point to the cursor position upon button + press, but rather move it relative to its current position as the + cursor moves. This allows selecting a point without moving it, and + adjusting its position more easily. + - when the cursor hovers above a point in GimpCurveView, or when + dragging a point, have the coordinate indicator show the point's + position, rather than the cursor's. + - In GimpCurveView, when holding down Ctrl while adding/dragging a + point, snap the y-coordinate to the original curve (at the start + of the drag). This is particularly useful for adding points along + the curve. + - In GimpDashboard, don't show legend for groups with no meter. + - Add a tile-alloc-total variable to the dashboard's memory and misc + groups, showing the total amount of memory used by the tile + allocator. + - Add a "Save Keyboard Shortcuts Now" button to the Configure + Keyboard Shortcuts dialog. + +Installers: + + - Windows: enable InnoSetup "unofficial" translations. Otherwise we + had a bunch of wasted work from our translators of several + languages whose translations were never used in the installer. + +Translations: + + - 12 translations were updated: Catalan, Chinese, French, German, + Hungarian, Indonesian, Italian, Polish, Russian, Spanish, Swedish + and Turkish. + + +Overview of Changes from GIMP 2.10.8 to GIMP 2.10.10 +==================================================== + +Core: + + - Add gimp-scratch allocator, a fast memory allocator (on the order of + magnitude of alloca()), suitable for small (up to a few megabytes), + short-lived (usually, bound to the current stack-frame) allocations. + Unlike alloca(), gimp-scratch doesn't use the stack, and is therefore + safer, and will also serve bigger requests, by falling-back to malloc(). + - In gimp_drawable_transform_buffer_affine(), avoid modifying the + clipping mode when transforming layer masks, since this function + is used (among other things) to transform layer masks together with + their layer, in which case they should use the same clipping mode + as the layer. This fixes a regression introduced by commit 2ae823ba, + causing layer masks to be transformed with a mismatched clipping mode + during layer transforms, leading to discrepencies between the + transformed layer and the transformed mask. + - Moved swap/cache and temporary files out the GIMP user config dir and + added new config file substitutions ${gimp_cache_dir} and + ${gimp_temp_dir}. + - Pass the GEGL tile-cache size, swap path, and thread-count to plug-ins + as part of their config, and have libgimp set the plug-in's GeglConfig + accordingly upon initialization. + - Layer groups are now rendered in bigger chunks rather than tile-by-tile + (which used to pretty much eliminate multithreading for groups), which + improves the rendering speed. + - Make saving/exporting files more robust to errors. In particular if + an error occurs during the process (be it a bug, a memory error, or + anything else), GIMP won't overwrite anymore any existing file with + incomplete contents, so that you won't end up with no valid files at + all. + - Fix a regression on support of various graphics tablet. + - Remove the "Edit -> Fade..." feature: it makes GIMP use two buffers + instead of one (east into system resources), it's broken in 2.10, and + we can make the UX better for filters. + - New generic canvas modifier 'Alt + middle click' allowing to pick + layers by clicking on pixels. The available layers will be looped + through (starting from the upper one) while Alt key is hold and the + picked layer name will be temporarily displayed in the status bar. + - When clearing a channel, do nothing if the channel is already empty; + otherwise, align the cleared rectangle to the channel buffer's tile + grid, so that all affected tiles are dropped, rather than zeroed. + Furthermore, only update the affected region of the channel. + - Brush and pattern saving logics has been moved to core code (instead + of plug-in). + - Clipboard brushes and pattern can now be duplicated. + - Parametric brushes are now 32-bit float to avoid posterization on + large brushes. + Note: raster brushes are still 8-bit and plug-ins only have access + to 8-bit versions of high-precision brushes/patterns. New API will + be required to handle high-precision data. + - On-canvas preview while editing a color in the colormap of an + indexed image. + +User interface: + + - Enabled HiDPI/Retina support for the GTK2/OSX build, fixes blurry icons. + - Add a tooltip to the "better compression" checkbox in save dialog to + make it clearer it does not mean that the file size is necessarily + smaller in every cases. In particular some best/worst case are + possible when an algorithm less efficient in general may end up + better on a particular image. + - Add basic support for cursors with a scale factor of 2 for HiDPI + (artwork to be updated). + - Foreground and background color icons, as well as color history will + now display out-of-gamut warning on indexed images for colors + outside of the palette, as well as on grayscale images, for non-gray + colors. + - Pack color picker and hexadecimal entry on same line in Color dock. + - Add an "Open as Image" button to the brushes dialog. + +Usability: + + - Attempting to transform locked layers or paint on them now results in + blinking around the status bar (where the warning message is displayed) + and around the toolbar where lock toggles are. The same applies to + attempting to move a selection where there is none — GIMP will blink + around the toolbar where the moving target (layer, selection, path) + is chosen. + - GIMP now allows selecting default export file type for new projects. + The choice is limited to PNG, JPEG, WebP, PSD, ORA, TIFF, BMP. + - GimpSpinScale widget now has an optional feature to constrain the + value to integer when dragging with a pointer (even if the scale + allows for fractional numbers), set with new function + gimp_spin_scale_set_constrain_drag(). + This is useful for settings where fractional numbers are technically + possible, yet most common use case are with integers (such as pixel + sizes, angles in degrees, etc.) so you want the easy interface to be + constrained. Fractional numbers are still settable, for instance by + keyboard edit; and arrow incrementation won't drop fraction parts. + This is currently only activated for brush options in paint tools. + +Tools: + + - In scale tool, scale around center even when using numeric input. + - New algorithm in the Bucket Fill tool when selecting the affected + area "Fill by line art detection", based off the G'Mic algorithm for + "smart colorization": https://hal.archives-ouvertes.fr/hal-01891876 + In a few words, it identifies painted pixels (either based on + grayscale or opacity values) and tries to close line arts to allow + filling even with not perfectly closed zones; the second step of the + algorithm will flood the colors under line art pixels to prevent + "holes" in the filling. + It is possible to control a max size (in pixels) for the flooding, + as well as max length of closing segments and splines. + - The Bucket Fill tool got new interaction allowing to hold the click + and move the mouse to fill based on several seed zones (for "Fill + by line art detection" as well as "Fill similar colors"). You can + now cancel the fill in progress with right click as in other tools. + - The Bucket Fill tool now allows color picking with ctrl-click, same + as every painting tool. It will pick either the foreground or + background color depending on the selected Fill Type. The ctrl-alt + modifier combination is also possible to pick the non-Fill Type + color. + - In the Bucket Fill tool, the Alt modifier will now switch to + "FG color fill" when "Pattern fill" was set (instead of doing + nothing). + - In the Unified Transform tool, default to preserving aspect ratio + when scaling up or down. + - In the Healing tool, "Sample merged" now also work for the target + pixels, allowing to draw in empty layers. + - Selection by color is now parallelized, hence improving speed of the + Select by Color tool (and any other processing which may share this + piece of code now or in the future). + - Add "Constrain handles" and "Around center" options to the + perspective-transform tool's GUI, which are similar to the + corresponding options of the unified-transform tool. + - Improve color picking on indexed image to always select an indexed + color corresponding to the picked pixel in the colormap. + +Plug-ins: + + - file-pdf-save GUI now clearly indicates the order the layers will be + used to make multi-page PDFs. + - Add DDS loading/exporting plug-in originally developed by Shawn Kirst + and Arne Reuter. + - Rename the Guillotine plug-in to Slice Using Guides. + - Add a new option saving a color profile when exporting PNG, JPEG, TIFF. + Always save it when exporting to PSD. + - Remove the "Advanced" expanders from the PNG and TIFF export dialogs. + - Full rewrite of the Spyrogimp plug-in with much more options and + better interaction. + - Indexed TIFF with alpha channel now supported. + +Filters: + + - Add on-canvas GUI (simple lines) for circular, linear, and zoom motion + blur. + +Help: + + - Link to the bugtracker directly from the Help menu, also link to the + wiki and the roadmap. Remove the link to currently disabled + registry.gimp.org. + +Installers: + + - Windows: proper fix for libthai to stop GIMP from crashing in the + Thai locale. + +Translations: + + - Czech, Danish, French, Italian, Japanese, Marathi, Polish, Russian, + Spanish, Swedish, Ukrainian. + +Build: + + - Bumping GTK+ dependency to the micro update GTK+ 2.24.32 to handle + several bugs on Windows (broken shortcuts on non-latin layouts and + broken vector icons). + + Overview of Changes from GIMP 2.10.6 to GIMP 2.10.8 =================================================== @@ -17,6 +311,7 @@ Core: projection asynchronously, rather than using a fixed chunk size. This provides a better trade-off between throughput and responsiveness dynamically, based on how fast the processing is. + - Add xyY color space to the color spaces for sampling colors. Tools: @@ -39,6 +334,8 @@ Tools: drawable and mask extents. This fixes wrong application position when painting on a drawable whose origin is above/to the left of the image's origin, and there's a selection active. + - New type of gradient interpolation called 'Step' for making multi-color + hard-edge gradient fills. Plug-ins: @@ -47,7 +344,7 @@ Plug-ins: - Improve RawTherapee discovery by looking up registry key (should become useful with RawTherapee 5.5 and more). -Usability: +Usability and UI: - Compatibility information in the Save dialog is now more understandable. The minimum GIMP version for the XCF file is always written down when it @@ -56,6 +353,12 @@ Usability: makes it more discoverable. The warning on compression is now displayed as its own text under the checkbox and not as additional text to the minimum GIMP version label. + - In all themes, fix the color of selected text, while editing a tree-view's + item text (such as when renaming a layer), by overriding tree-view + specific styling with the global text-entry style, for nested text entries + inside tree-views. The text would previously use the same color as the + selection background, making it unreadable. + - Add option in the Windows menu to hide the image tab bar. CLI: @@ -99,7 +402,8 @@ Debugging: Translations: - Updated translations: Danish, Dutch, Finnish, German, Hungarian, - Italian, Polish, Portuguese (Brazil), Spanish, Swedish, Ukrainian. + Italian, Marathi, Polish, Portuguese (Brazil), Russian, Spanish, + Swedish, Ukrainian. Overview of Changes from GIMP 2.10.4 to GIMP 2.10.6 diff --git a/acinclude.m4 b/acinclude.m4 index 2984fe97cf..f0ceffb4d5 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -4,6 +4,5 @@ m4_include([m4macros/ax_cxx_compile_stdcxx.m4]) m4_include([m4macros/ax_gcc_func_attribute.m4]) m4_include([m4macros/ax_prog_cc_for_build.m4]) m4_include([m4macros/ax_prog_perl_version.m4]) -m4_include([m4macros/binreloc.m4]) m4_include([m4macros/detectcflags.m4]) m4_include([m4macros/pythondev.m4]) diff --git a/aclocal.m4 b/aclocal.m4 index 8eabce1ff2..05acaa6e4a 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1498,18 +1498,11 @@ AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR -m4_include([m4macros/gtk-doc.m4]) -m4_include([m4macros/intltool.m4]) -m4_include([m4macros/libtool.m4]) -m4_include([m4macros/ltoptions.m4]) -m4_include([m4macros/ltsugar.m4]) -m4_include([m4macros/ltversion.m4]) -m4_include([m4macros/lt~obsolete.m4]) # Configure paths for GLIB # Owen Taylor 1997-2001 # Increment this whenever this file is changed. -#serial 1 +#serial 3 dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject, @@ -1519,10 +1512,16 @@ AC_DEFUN([AM_PATH_GLIB_2_0], [dnl dnl Get the cflags and libraries from pkg-config dnl + +dnl We can't use PKG_PREREQ because that needs 0.29. +m4_ifndef([PKG_PROG_PKG_CONFIG], + [pkg.m4 version 0.28 or later is required]) + AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run a test GLIB program], , enable_glibtest=yes) - pkg_config_args=glib-2.0 + min_glib_version=ifelse([$1], [], [2.0.0], [$1]) + pkg_config_args="glib-2.0 >= $min_glib_version" for module in . $4 do case "$module" in @@ -1553,7 +1552,15 @@ AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run PKG_CONFIG=no fi - min_glib_version=ifelse([$1], ,2.0.0,$1) + dnl For GLIB_CFLAGS and GLIB_LIBS + PKG_CHECK_MODULES([GLIB], [$pkg_config_args], [:], [:]) + + dnl For the tools + PKG_CHECK_VAR([GLIB_GENMARSHAL], [glib-2.0], [glib_genmarshal]) + PKG_CHECK_VAR([GOBJECT_QUERY], [glib-2.0], [gobject_query]) + PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.0], [glib_mkenums]) + PKG_CHECK_VAR([GLIB_COMPILE_RESOURCES], [gio-2.0], [glib_compile_resources]) + AC_MSG_CHECKING(for GLIB - version >= $min_glib_version) if test x$PKG_CONFIG != xno ; then @@ -1571,13 +1578,6 @@ AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run fi if test x"$no_glib" = x ; then - GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` - GOBJECT_QUERY=`$PKG_CONFIG --variable=gobject_query glib-2.0` - GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` - GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable=glib_compile_resources gio-2.0` - - GLIB_CFLAGS=`$PKG_CONFIG --cflags $pkg_config_args` - GLIB_LIBS=`$PKG_CONFIG --libs $pkg_config_args` glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \ @@ -1711,12 +1711,6 @@ main (void) GLIB_COMPILE_RESOURCES="" ifelse([$3], , :, [$3]) fi - AC_SUBST(GLIB_CFLAGS) - AC_SUBST(GLIB_LIBS) - AC_SUBST(GLIB_GENMARSHAL) - AC_SUBST(GOBJECT_QUERY) - AC_SUBST(GLIB_MKENUMS) - AC_SUBST(GLIB_COMPILE_RESOURCES) rm -f conf.glibtest ]) @@ -2210,6 +2204,10 @@ fi]) # Configure paths for GTK+ # Owen Taylor 1997-2001 +# Version number used by aclocal, see `info automake Serials`. +# Increment on every change. +#serial 1 + dnl AM_PATH_GTK_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) dnl Test for GTK+, and define GTK_CFLAGS and GTK_LIBS, if gthread is specified in MODULES, dnl pass to pkg-config @@ -2393,4 +2391,11 @@ main () rm -f conf.gtktest ]) +m4_include([m4macros/gtk-doc.m4]) +m4_include([m4macros/intltool.m4]) +m4_include([m4macros/libtool.m4]) +m4_include([m4macros/ltoptions.m4]) +m4_include([m4macros/ltsugar.m4]) +m4_include([m4macros/ltversion.m4]) +m4_include([m4macros/lt~obsolete.m4]) m4_include([acinclude.m4]) diff --git a/app-tools/Makefile.in b/app-tools/Makefile.in index 09567c9b51..c616428153 100644 --- a/app-tools/Makefile.in +++ b/app-tools/Makefile.in @@ -107,7 +107,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -198,8 +197,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -329,7 +326,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -468,8 +464,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/Makefile.am b/app/Makefile.am index 86aec105f2..8c92170eb7 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -1,5 +1,11 @@ ## 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 @@ -22,6 +28,7 @@ SUBDIRS = \ plug-in \ xcf \ file \ + file-data \ pdb \ widgets \ propgui \ @@ -92,6 +99,9 @@ libpsapi = -lpsapi # for GimpBacktrace libdbghelp = -ldbghelp +# for I_RpcExceptionFilter() +librpcrt4 = -lrpcrt4 + if HAVE_EXCHNDL exchndl = -lexchndl endif @@ -100,7 +110,7 @@ else libm = -lm endif -if USE_BINRELOC +if ENABLE_RELOCATABLE_RESOURCES munix = -Wl,-rpath '-Wl,$$ORIGIN/../lib' endif @@ -125,6 +135,7 @@ AM_CPPFLAGS = \ $(LCMS_CFLAGS) \ $(GEXIV2_CFLAGS) \ $(psapi_cflags) \ + $(xobjective_c) \ -I$(includedir) \ -I$(builddir)/gui @@ -152,6 +163,7 @@ gimpconsoleldadd = \ vectors/libappvectors.a \ core/libappcore.a \ file/libappfile.a \ + file-data/libappfile-data.a \ text/libapptext.a \ paint/libapppaint.a \ operations/libappoperations.a \ @@ -187,7 +199,8 @@ gimpconsoleldadd = \ $(libm) \ $(libdl) \ $(libpsapi) \ - $(libdbghelp) + $(libdbghelp) \ + $(librpcrt4) gimp_@GIMP_APP_VERSION@_LDFLAGS = \ $(AM_LDFLAGS) \ diff --git a/app/Makefile.in b/app/Makefile.in index 501ff81193..307cdb1802 100644 --- a/app/Makefile.in +++ b/app/Makefile.in @@ -111,7 +111,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -144,8 +143,8 @@ 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 \ - text/libapptext.a paint/libapppaint.a \ - operations/libappoperations.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) \ @@ -161,7 +160,7 @@ am__DEPENDENCIES_2 = xcf/libappxcf.a pdb/libappinternal-procs.a \ $(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 \ @@ -346,8 +345,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -477,7 +474,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -616,8 +612,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ @@ -740,6 +734,9 @@ 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 @@ -762,6 +759,7 @@ SUBDIRS = \ plug-in \ xcf \ file \ + file-data \ pdb \ widgets \ propgui \ @@ -815,9 +813,12 @@ gimp_@GIMP_APP_VERSION@_SOURCES = $(libapp_sources) main.c # 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 -@USE_BINRELOC_TRUE@munix = -Wl,-rpath '-Wl,$$ORIGIN/../lib' +@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 @@ -836,6 +837,7 @@ AM_CPPFLAGS = \ $(LCMS_CFLAGS) \ $(GEXIV2_CFLAGS) \ $(psapi_cflags) \ + $(xobjective_c) \ -I$(includedir) \ -I$(builddir)/gui @@ -864,6 +866,7 @@ gimpconsoleldadd = \ vectors/libappvectors.a \ core/libappcore.a \ file/libappfile.a \ + file-data/libappfile-data.a \ text/libapptext.a \ paint/libapppaint.a \ operations/libappoperations.a \ @@ -899,7 +902,8 @@ gimpconsoleldadd = \ $(libm) \ $(libdl) \ $(libpsapi) \ - $(libdbghelp) + $(libdbghelp) \ + $(librpcrt4) gimp_@GIMP_APP_VERSION@_LDFLAGS = \ $(AM_LDFLAGS) \ diff --git a/app/actions/Makefile.in b/app/actions/Makefile.in index 7c022f54e6..3a59030a14 100644 --- a/app/actions/Makefile.in +++ b/app/actions/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -329,8 +328,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -460,7 +457,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -599,8 +595,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/actions/actions.c b/app/actions/actions.c index 157b21224d..cf8d6dca1c 100644 --- a/app/actions/actions.c +++ b/app/actions/actions.c @@ -280,172 +280,196 @@ actions_exit (Gimp *gimp) Gimp * action_data_get_gimp (gpointer data) { - GimpContext *context = NULL; + Gimp *result = NULL; + static gboolean recursion = FALSE; - if (! data) + if (! data || recursion) return NULL; - if (GIMP_IS_DISPLAY (data)) - return ((GimpDisplay *) data)->gimp; - else if (GIMP_IS_IMAGE_WINDOW (data)) + recursion = TRUE; + + if (GIMP_IS_GIMP (data)) + result = data; + + if (! result) { - GimpDisplayShell *shell = gimp_image_window_get_active_shell (data); - return shell ? shell->display->gimp : NULL; + GimpDisplay *display = action_data_get_display (data); + + if (display) + result = display->gimp; } - else if (GIMP_IS_GIMP (data)) - return data; - else if (GIMP_IS_DOCK (data)) - context = gimp_dock_get_context (((GimpDock *) data)); - else if (GIMP_IS_DOCK_WINDOW (data)) - context = gimp_dock_window_get_context (((GimpDockWindow *) data)); - else if (GIMP_IS_CONTAINER_VIEW (data)) - context = gimp_container_view_get_context ((GimpContainerView *) data); - else if (GIMP_IS_CONTAINER_EDITOR (data)) - context = gimp_container_view_get_context (((GimpContainerEditor *) data)->view); - else if (GIMP_IS_IMAGE_EDITOR (data)) - context = ((GimpImageEditor *) data)->context; - else if (GIMP_IS_NAVIGATION_EDITOR (data)) - context = ((GimpNavigationEditor *) data)->context; - if (context) - return context->gimp; + if (! result) + { + GimpContext *context = action_data_get_context (data); - return NULL; + if (context) + result = context->gimp; + } + + recursion = FALSE; + + return result; } GimpContext * action_data_get_context (gpointer data) { - if (! data) + GimpContext *result = NULL; + static gboolean recursion = FALSE; + + if (! data || recursion) return NULL; - if (GIMP_IS_DISPLAY (data)) - return gimp_get_user_context (((GimpDisplay *) data)->gimp); - else if (GIMP_IS_IMAGE_WINDOW (data)) - { - GimpDisplayShell *shell = gimp_image_window_get_active_shell (data); - return shell ? gimp_get_user_context (shell->display->gimp) : NULL; - } - else if (GIMP_IS_GIMP (data)) - return gimp_get_user_context (data); - else if (GIMP_IS_DOCK (data)) - return gimp_dock_get_context ((GimpDock *) data); - else if (GIMP_IS_DOCK_WINDOW (data)) - return gimp_dock_window_get_context (((GimpDockWindow *) data)); - else if (GIMP_IS_CONTAINER_VIEW (data)) - return gimp_container_view_get_context ((GimpContainerView *) data); - else if (GIMP_IS_CONTAINER_EDITOR (data)) - return gimp_container_view_get_context (((GimpContainerEditor *) data)->view); - else if (GIMP_IS_IMAGE_EDITOR (data)) - return ((GimpImageEditor *) data)->context; - else if (GIMP_IS_NAVIGATION_EDITOR (data)) - return ((GimpNavigationEditor *) data)->context; + recursion = TRUE; - return NULL; + 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) { - GimpContext *context = NULL; - GimpDisplay *display = NULL; + GimpImage *result = NULL; + static gboolean recursion = FALSE; - if (! data) + if (! data || recursion) return NULL; - if (GIMP_IS_DISPLAY (data)) - display = (GimpDisplay *) data; - else if (GIMP_IS_IMAGE_WINDOW (data)) - { - GimpDisplayShell *shell = gimp_image_window_get_active_shell (data); - display = shell ? shell->display : NULL; - } - else if (GIMP_IS_GIMP (data)) - context = gimp_get_user_context (data); - else if (GIMP_IS_DOCK (data)) - context = gimp_dock_get_context ((GimpDock *) data); - else if (GIMP_IS_DOCK_WINDOW (data)) - context = gimp_dock_window_get_context (((GimpDockWindow *) data)); - else if (GIMP_IS_ITEM_TREE_VIEW (data)) - return gimp_item_tree_view_get_image ((GimpItemTreeView *) data); + 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)) - return ((GimpImageEditor *) data)->image; - else if (GIMP_IS_NAVIGATION_EDITOR (data)) - context = ((GimpNavigationEditor *) data)->context; + result = ((GimpImageEditor *) data)->image; - if (context) - return gimp_context_get_image (context); - else if (display) - return gimp_display_get_image (display); + if (! result) + { + GimpDisplay *display = action_data_get_display (data); - return NULL; + 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) { - GimpContext *context = NULL; + GimpDisplay *result = NULL; + static gboolean recursion = FALSE; - if (! data) + if (! data || recursion) return NULL; + recursion = TRUE; + if (GIMP_IS_DISPLAY (data)) - return data; + result = data; else if (GIMP_IS_IMAGE_WINDOW (data)) { GimpDisplayShell *shell = gimp_image_window_get_active_shell (data); - return shell ? shell->display : NULL; + result = shell ? shell->display : NULL; } - else if (GIMP_IS_GIMP (data)) - context = gimp_get_user_context (data); - else if (GIMP_IS_DOCK (data)) - context = gimp_dock_get_context ((GimpDock *) data); - else if (GIMP_IS_DOCK_WINDOW (data)) - context = gimp_dock_window_get_context (((GimpDockWindow *) data)); - else if (GIMP_IS_IMAGE_EDITOR (data)) - context = ((GimpImageEditor *) data)->context; - else if (GIMP_IS_NAVIGATION_EDITOR (data)) - context = ((GimpNavigationEditor *) data)->context; - if (context) - return gimp_context_get_display (context); + if (! result) + { + GimpContext *context = action_data_get_context (data); - return NULL; + if (context) + result = gimp_context_get_display (context); + } + + recursion = FALSE; + + return result; } GimpDisplayShell * action_data_get_shell (gpointer data) { - GimpDisplay *display = NULL; - GimpDisplayShell *shell = NULL; + GimpDisplayShell *result = NULL; + static gboolean recursion = FALSE; - display = action_data_get_display (data); + if (! data || recursion) + return NULL; - if (display) - shell = gimp_display_get_shell (display); + recursion = TRUE; - return shell; + 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) { - GimpDisplay *display = NULL; + GtkWidget *result = NULL; + static gboolean recursion = FALSE; - if (! data) + if (! data || recursion) return NULL; - if (GIMP_IS_DISPLAY (data)) - display = data; - else if (GIMP_IS_GIMP (data)) - display = gimp_context_get_display (gimp_get_user_context (data)); - else if (GTK_IS_WIDGET (data)) - return data; + recursion = TRUE; - if (display) - return GTK_WIDGET (gimp_display_get_shell (display)); + if (GTK_IS_WIDGET (data)) + result = data; - return dialogs_get_toolbox (); + 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 diff --git a/app/actions/channels-commands.c b/app/actions/channels-commands.c index d7ffc1636c..842634cd99 100644 --- a/app/actions/channels-commands.c +++ b/app/actions/channels-commands.c @@ -54,6 +54,9 @@ #include "gimp-intl.h" +#define RGBA_EPSILON 1e-6 + + /* local function prototypes */ static void channels_new_callback (GtkWidget *dialog, @@ -504,12 +507,12 @@ channels_edit_attributes_callback (GtkWidget *dialog, { GimpItem *item = GIMP_ITEM (channel); - if (strcmp (channel_name, gimp_object_get_name (channel)) || - gimp_rgba_distance (channel_color, &channel->color) > 0.0001 || - 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) || + 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, @@ -519,7 +522,7 @@ channels_edit_attributes_callback (GtkWidget *dialog, 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) > 0.0001) + 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)) diff --git a/app/actions/context-actions.c b/app/actions/context-actions.c index 1180ec5d35..4138bfc7a6 100644 --- a/app/actions/context-actions.c +++ b/app/actions/context-actions.c @@ -1095,145 +1095,145 @@ context_actions_setup (GimpActionGroup *group) context_actions, G_N_ELEMENTS (context_actions)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_palette_foreground_actions, G_N_ELEMENTS (context_palette_foreground_actions), G_CALLBACK (context_palette_foreground_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_palette_background_actions, G_N_ELEMENTS (context_palette_background_actions), G_CALLBACK (context_palette_background_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_colormap_foreground_actions, G_N_ELEMENTS (context_colormap_foreground_actions), G_CALLBACK (context_colormap_foreground_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_colormap_background_actions, G_N_ELEMENTS (context_colormap_background_actions), G_CALLBACK (context_colormap_background_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_swatch_foreground_actions, G_N_ELEMENTS (context_swatch_foreground_actions), G_CALLBACK (context_swatch_foreground_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_swatch_background_actions, G_N_ELEMENTS (context_swatch_background_actions), G_CALLBACK (context_swatch_background_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_foreground_red_actions, G_N_ELEMENTS (context_foreground_red_actions), G_CALLBACK (context_foreground_red_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_foreground_green_actions, G_N_ELEMENTS (context_foreground_green_actions), G_CALLBACK (context_foreground_green_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_foreground_blue_actions, G_N_ELEMENTS (context_foreground_blue_actions), G_CALLBACK (context_foreground_blue_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_foreground_hue_actions, G_N_ELEMENTS (context_foreground_hue_actions), G_CALLBACK (context_foreground_hue_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_foreground_saturation_actions, G_N_ELEMENTS (context_foreground_saturation_actions), G_CALLBACK (context_foreground_saturation_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_foreground_value_actions, G_N_ELEMENTS (context_foreground_value_actions), G_CALLBACK (context_foreground_value_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_background_red_actions, G_N_ELEMENTS (context_background_red_actions), G_CALLBACK (context_background_red_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_background_green_actions, G_N_ELEMENTS (context_background_green_actions), G_CALLBACK (context_background_green_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_background_blue_actions, G_N_ELEMENTS (context_background_blue_actions), G_CALLBACK (context_background_blue_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_background_hue_actions, G_N_ELEMENTS (context_background_hue_actions), G_CALLBACK (context_background_hue_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_background_saturation_actions, G_N_ELEMENTS (context_background_saturation_actions), G_CALLBACK (context_background_saturation_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_background_value_actions, G_N_ELEMENTS (context_background_value_actions), G_CALLBACK (context_background_value_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_opacity_actions, G_N_ELEMENTS (context_opacity_actions), G_CALLBACK (context_opacity_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_paint_mode_actions, G_N_ELEMENTS (context_paint_mode_actions), G_CALLBACK (context_paint_mode_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_tool_select_actions, G_N_ELEMENTS (context_tool_select_actions), G_CALLBACK (context_tool_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_select_actions, G_N_ELEMENTS (context_brush_select_actions), G_CALLBACK (context_brush_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_pattern_select_actions, G_N_ELEMENTS (context_pattern_select_actions), G_CALLBACK (context_pattern_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_palette_select_actions, G_N_ELEMENTS (context_palette_select_actions), G_CALLBACK (context_palette_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_gradient_select_actions, G_N_ELEMENTS (context_gradient_select_actions), G_CALLBACK (context_gradient_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_font_select_actions, G_N_ELEMENTS (context_font_select_actions), G_CALLBACK (context_font_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_spacing_actions, G_N_ELEMENTS (context_brush_spacing_actions), G_CALLBACK (context_brush_spacing_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_shape_actions, G_N_ELEMENTS (context_brush_shape_actions), G_CALLBACK (context_brush_shape_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_radius_actions, G_N_ELEMENTS (context_brush_radius_actions), G_CALLBACK (context_brush_radius_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_spikes_actions, G_N_ELEMENTS (context_brush_spikes_actions), G_CALLBACK (context_brush_spikes_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_hardness_actions, G_N_ELEMENTS (context_brush_hardness_actions), G_CALLBACK (context_brush_hardness_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_aspect_actions, G_N_ELEMENTS (context_brush_aspect_actions), G_CALLBACK (context_brush_aspect_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "context-action", context_brush_angle_actions, G_N_ELEMENTS (context_brush_angle_actions), G_CALLBACK (context_brush_angle_cmd_callback)); diff --git a/app/actions/debug-commands.c b/app/actions/debug-commands.c index 19bb95b574..dc9bcace5d 100644 --- a/app/actions/debug-commands.c +++ b/app/actions/debug-commands.c @@ -286,15 +286,15 @@ debug_benchmark_projection (GimpDisplay *display) { GimpProjection *projection = gimp_image_get_projection (image); + gimp_projection_stop_rendering (projection); + GIMP_TIMER_START (); gimp_image_invalidate (image, 0, 0, gimp_image_get_width (image), gimp_image_get_height (image)); - gimp_projection_flush_now (projection); - - gimp_display_flush_now (display); + gimp_projection_flush_now (projection, TRUE); GIMP_TIMER_END ("Validation of the entire projection"); diff --git a/app/actions/dialogs-actions.c b/app/actions/dialogs-actions.c index 7f0f3924fb..a9fdf504a8 100644 --- a/app/actions/dialogs-actions.c +++ b/app/actions/dialogs-actions.c @@ -188,13 +188,13 @@ const GimpStringActionEntry dialogs_dockable_actions[] = GIMP_HELP_PALETTE_DIALOG }, { "dialogs-palette-editor", GIMP_ICON_PALETTE, - NC_("dialogs-action", "Palette Editor"), NULL, + 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 presets"), NULL, + 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 }, diff --git a/app/actions/drawable-actions.c b/app/actions/drawable-actions.c index 541350a008..bd421ee873 100644 --- a/app/actions/drawable-actions.c +++ b/app/actions/drawable-actions.c @@ -51,14 +51,7 @@ static const GimpActionEntry drawable_actions[] = NC_("drawable-action", "_White Balance"), NULL, NC_("drawable-action", "Automatic white balance correction"), G_CALLBACK (drawable_levels_stretch_cmd_callback), - GIMP_HELP_LAYER_WHITE_BALANCE }, - - { "drawable-offset", NULL, - NC_("drawable-action", "_Offset..."), "O", - NC_("drawable-action", - "Shift the pixels, optionally wrapping them at the borders"), - G_CALLBACK (drawable_offset_cmd_callback), - GIMP_HELP_LAYER_OFFSET } + GIMP_HELP_LAYER_WHITE_BALANCE } }; static const GimpToggleActionEntry drawable_toggle_actions[] = @@ -215,7 +208,6 @@ drawable_actions_update (GimpActionGroup *group, SET_SENSITIVE ("drawable-equalize", writable && !children); SET_SENSITIVE ("drawable-levels-stretch", writable && !children && is_rgb); - SET_SENSITIVE ("drawable-offset", writable && !children); SET_SENSITIVE ("drawable-visible", drawable); SET_SENSITIVE ("drawable-linked", drawable); diff --git a/app/actions/drawable-commands.c b/app/actions/drawable-commands.c index 96d1d85952..03afd7141d 100644 --- a/app/actions/drawable-commands.c +++ b/app/actions/drawable-commands.c @@ -27,7 +27,6 @@ #include "core/gimp.h" #include "core/gimpdrawable-equalize.h" #include "core/gimpdrawable-levels.h" -#include "core/gimpdrawable-offset.h" #include "core/gimpdrawable-operation.h" #include "core/gimpimage.h" #include "core/gimpimage-undo.h" @@ -37,7 +36,6 @@ #include "core/gimpprogress.h" #include "dialogs/dialogs.h" -#include "dialogs/offset-dialog.h" #include "actions.h" #include "drawable-commands.h" @@ -45,17 +43,6 @@ #include "gimp-intl.h" -/* local function prototypes */ - -static void drawable_offset_callback (GtkWidget *dialog, - GimpDrawable *drawable, - GimpContext *context, - gboolean wrap_around, - GimpOffsetType fill_type, - gint offset_x, - gint offset_y); - - /* public functions */ void @@ -95,35 +82,6 @@ drawable_levels_stretch_cmd_callback (GtkAction *action, gimp_image_flush (image); } -void -drawable_offset_cmd_callback (GtkAction *action, - gpointer data) -{ - GimpImage *image; - GimpDrawable *drawable; - GtkWidget *widget; - GtkWidget *dialog; - return_if_no_drawable (image, drawable, data); - return_if_no_widget (widget, data); - -#define OFFSET_DIALOG_KEY "gimp-offset-dialog" - - dialog = dialogs_get_dialog (G_OBJECT (drawable), OFFSET_DIALOG_KEY); - - if (! dialog) - { - dialog = offset_dialog_new (drawable, action_data_get_context (data), - widget, - drawable_offset_callback, - NULL); - - dialogs_attach_dialog (G_OBJECT (drawable), - OFFSET_DIALOG_KEY, dialog); - } - - gtk_window_present (GTK_WINDOW (dialog)); -} - void drawable_linked_cmd_callback (GtkAction *action, gpointer data) @@ -336,25 +294,3 @@ drawable_rotate_cmd_callback (GtkAction *action, gimp_image_flush (image); } - - -/* private functions */ - -static void -drawable_offset_callback (GtkWidget *dialog, - GimpDrawable *drawable, - GimpContext *context, - gboolean wrap_around, - GimpOffsetType fill_type, - gint offset_x, - gint offset_y) -{ - GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); - - gimp_drawable_offset (drawable, context, - wrap_around, fill_type, - offset_x, offset_y); - gimp_image_flush (image); - - gtk_widget_destroy (dialog); -} diff --git a/app/actions/drawable-commands.h b/app/actions/drawable-commands.h index ef072c97f9..b678faf359 100644 --- a/app/actions/drawable-commands.h +++ b/app/actions/drawable-commands.h @@ -23,8 +23,6 @@ void drawable_equalize_cmd_callback (GtkAction *action, gpointer data); void drawable_levels_stretch_cmd_callback (GtkAction *action, gpointer data); -void drawable_offset_cmd_callback (GtkAction *action, - gpointer data); void drawable_linked_cmd_callback (GtkAction *action, gpointer data); diff --git a/app/actions/edit-actions.c b/app/actions/edit-actions.c index e1f3ead0f6..9a1d732481 100644 --- a/app/actions/edit-actions.c +++ b/app/actions/edit-actions.c @@ -27,7 +27,6 @@ #include "core/gimp.h" #include "core/gimpchannel.h" #include "core/gimpcontext.h" -#include "core/gimpdrawableundo.h" #include "core/gimpimage.h" #include "core/gimpimage-undo.h" #include "core/gimplayer.h" @@ -101,13 +100,6 @@ static const GimpActionEntry edit_actions[] = G_CALLBACK (edit_undo_clear_cmd_callback), GIMP_HELP_EDIT_UNDO_CLEAR }, - { "edit-fade", GIMP_ICON_EDIT_UNDO, - NC_("edit-action", "_Fade..."), NULL, - NC_("edit-action", - "Modify paint mode and opacity of the last pixel manipulation"), - G_CALLBACK (edit_fade_cmd_callback), - GIMP_HELP_EDIT_FADE }, - { "edit-cut", GIMP_ICON_EDIT_CUT, NC_("edit-action", "Cu_t"), "X", NC_("edit-action", "Move the selected pixels to the clipboard"), @@ -305,11 +297,9 @@ edit_actions_update (GimpActionGroup *group, GimpDrawable *drawable = NULL; gchar *undo_name = NULL; gchar *redo_name = NULL; - gchar *fade_name = NULL; gboolean writable = FALSE; gboolean children = FALSE; gboolean undo_enabled = FALSE; - gboolean fade_enabled = FALSE; if (image) { @@ -351,21 +341,6 @@ edit_actions_update (GimpActionGroup *group, else if (redo) redo_name = g_strdup_printf (_("_Redo %s"), gimp_object_get_name (redo)); - - undo = gimp_image_undo_get_fadeable (image); - - if (GIMP_IS_DRAWABLE_UNDO (undo) && - GIMP_DRAWABLE_UNDO (undo)->applied_buffer) - { - fade_enabled = TRUE; - } - - if (fade_enabled) - { - fade_name = - g_strdup_printf (_("_Fade %s..."), - gimp_object_get_name (undo)); - } } } @@ -377,18 +352,15 @@ edit_actions_update (GimpActionGroup *group, SET_LABEL ("edit-undo", undo_name ? undo_name : _("_Undo")); SET_LABEL ("edit-redo", redo_name ? redo_name : _("_Redo")); - SET_LABEL ("edit-fade", fade_name ? fade_name : _("_Fade...")); 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)); - SET_SENSITIVE ("edit-fade", fade_enabled && fade_name); g_free (undo_name); g_free (redo_name); - g_free (fade_name); SET_SENSITIVE ("edit-cut", writable && !children); SET_SENSITIVE ("edit-copy", drawable); diff --git a/app/actions/edit-commands.c b/app/actions/edit-commands.c index 019898fc59..6645de486a 100644 --- a/app/actions/edit-commands.c +++ b/app/actions/edit-commands.c @@ -53,10 +53,9 @@ #include "display/gimpdisplayshell.h" #include "display/gimpdisplayshell-transform.h" +#include "tools/gimptools-utils.h" #include "tools/tool_manager.h" -#include "dialogs/fade-dialog.h" - #include "actions.h" #include "edit-commands.h" @@ -65,18 +64,20 @@ /* local function prototypes */ -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); +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 */ @@ -204,27 +205,6 @@ edit_undo_clear_cmd_callback (GtkAction *action, gtk_widget_destroy (dialog); } -void -edit_fade_cmd_callback (GtkAction *action, - gpointer data) -{ - GimpImage *image; - GtkWidget *widget; - GtkWidget *dialog; - return_if_no_image (image, data); - return_if_no_widget (widget, data); - - dialog = fade_dialog_new (image, widget); - - if (dialog) - { - g_signal_connect_object (image, "disconnect", - G_CALLBACK (gtk_widget_destroy), - dialog, G_CONNECT_SWAPPED); - gtk_widget_show (dialog); - } -} - void edit_cut_cmd_callback (GtkAction *action, gpointer data) @@ -235,6 +215,9 @@ edit_cut_cmd_callback (GtkAction *action, 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); @@ -481,6 +464,9 @@ edit_clear_cmd_callback (GtkAction *action, 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); } @@ -521,6 +507,32 @@ edit_fill_cmd_callback (GtkAction *action, /* 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, diff --git a/app/actions/edit-commands.h b/app/actions/edit-commands.h index e3abac1ffa..b56af6b9d2 100644 --- a/app/actions/edit-commands.h +++ b/app/actions/edit-commands.h @@ -30,9 +30,6 @@ void edit_strong_redo_cmd_callback (GtkAction *action, void edit_undo_clear_cmd_callback (GtkAction *action, gpointer data); -void edit_fade_cmd_callback (GtkAction *action, - gpointer data); - void edit_cut_cmd_callback (GtkAction *action, gpointer data); void edit_copy_cmd_callback (GtkAction *action, diff --git a/app/actions/filters-actions.c b/app/actions/filters-actions.c index 2be4ad4d1a..7e2930993d 100644 --- a/app/actions/filters-actions.c +++ b/app/actions/filters-actions.c @@ -111,7 +111,7 @@ static const GimpStringActionEntry filters_actions[] = GIMP_HELP_FILTER_COLOR_ENHANCE }, { "filters-invert-linear", GIMP_ICON_INVERT, - NC_("filters-action", "_Linear Invert"), NULL, NULL, + NC_("filters-action", "L_inear Invert"), NULL, NULL, "gegl:invert-linear", GIMP_HELP_FILTER_INVERT_LINEAR }, @@ -135,7 +135,7 @@ static const GimpStringActionEntry filters_settings_actions[] = { { "filters-dilate", GIMP_ICON_GEGL, NC_("filters-action", "_Dilate"), NULL, - NC_("drawable-action", "Grow lighter areas of the image"), + NC_("filters-action", "Grow lighter areas of the image"), "gegl:value-propagate\n" "(mode white)" "(lower-threshold 0.000000)" @@ -151,7 +151,7 @@ static const GimpStringActionEntry filters_settings_actions[] = { "filters-erode", GIMP_ICON_GEGL, NC_("filters-action", "_Erode"), NULL, - NC_("drawable-action", "Grow darker areas of the image"), + NC_("filters-action", "Grow darker areas of the image"), "gegl:value-propagate\n" "(mode black)" "(lower-threshold 0.000000)" @@ -523,6 +523,11 @@ static const GimpStringActionEntry filters_interactive_actions[] = "gegl:noise-spread", GIMP_HELP_FILTER_NOISE_SPREAD }, + { "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", @@ -922,6 +927,7 @@ filters_actions_update (GimpActionGroup *group, SET_SENSITIVE ("filters-noise-slur", writable); SET_SENSITIVE ("filters-noise-solid", writable); SET_SENSITIVE ("filters-noise-spread", writable); + SET_SENSITIVE ("filters-offset", writable); SET_SENSITIVE ("filters-oilify", writable); SET_SENSITIVE ("filters-panorama-projection", writable); SET_SENSITIVE ("filters-photocopy", writable); diff --git a/app/actions/gimpgeglprocedure.c b/app/actions/gimpgeglprocedure.c index 2630448417..2e4c5e438c 100644 --- a/app/actions/gimpgeglprocedure.c +++ b/app/actions/gimpgeglprocedure.c @@ -339,6 +339,10 @@ gimp_gegl_procedure_execute_async (GimpProcedure *procedure, { tool_name = "gimp-threshold-tool"; } + else if (! strcmp (procedure->original_name, "gimp:offset")) + { + tool_name = "gimp-offset-tool"; + } else { tool_name = "gimp-operation-tool"; diff --git a/app/actions/image-commands.c b/app/actions/image-commands.c index 2559a023c0..130495df6f 100644 --- a/app/actions/image-commands.c +++ b/app/actions/image-commands.c @@ -166,7 +166,8 @@ static void image_merge_layers_callback (GtkWidget *dialog, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible); + gboolean discard_invisible, + gpointer user_data); /* private variables */ @@ -901,10 +902,12 @@ void image_merge_layers_cmd_callback (GtkAction *action, gpointer data) { - GtkWidget *dialog; - GimpImage *image; - GtkWidget *widget; + 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" @@ -922,7 +925,7 @@ image_merge_layers_cmd_callback (GtkAction *action, config->layer_merge_active_group_only, config->layer_merge_discard_invisible, image_merge_layers_callback, - NULL); + display); dialogs_attach_dialog (G_OBJECT (image), MERGE_LAYERS_DIALOG_KEY, dialog); } @@ -934,13 +937,16 @@ void image_flatten_image_cmd_callback (GtkAction *action, gpointer data) { - GimpImage *image; - GtkWidget *widget; - GError *error = NULL; + 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), &error)) + 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, @@ -1263,7 +1269,7 @@ image_profile_assign_callback (GtkWidget *dialog, gimp_image_set_is_color_managed (image, TRUE, TRUE); /* omg... */ - gimp_image_parasite_detach (image, "icc-profile-name"); + gimp_image_parasite_detach (image, "icc-profile-name", TRUE); gimp_image_undo_group_end (image); @@ -1475,9 +1481,11 @@ image_merge_layers_callback (GtkWidget *dialog, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible) + gboolean discard_invisible, + gpointer user_data) { - GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDisplay *display = user_data; g_object_set (config, "layer-merge-type", merge_type, @@ -1489,7 +1497,8 @@ image_merge_layers_callback (GtkWidget *dialog, context, config->layer_merge_type, config->layer_merge_active_group_only, - config->layer_merge_discard_invisible); + config->layer_merge_discard_invisible, + GIMP_PROGRESS (display)); gimp_image_flush (image); diff --git a/app/actions/layers-actions.c b/app/actions/layers-actions.c index d287ff7962..40383b1eb0 100644 --- a/app/actions/layers-actions.c +++ b/app/actions/layers-actions.c @@ -729,12 +729,12 @@ layers_actions_setup (GimpActionGroup *group) G_N_ELEMENTS (layers_select_actions), G_CALLBACK (layers_select_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "layers-action", layers_opacity_actions, G_N_ELEMENTS (layers_opacity_actions), G_CALLBACK (layers_opacity_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "layers-action", layers_mode_actions, G_N_ELEMENTS (layers_mode_actions), G_CALLBACK (layers_mode_cmd_callback)); diff --git a/app/actions/layers-commands.c b/app/actions/layers-commands.c index 482fde9cd9..289a0f0228 100644 --- a/app/actions/layers-commands.c +++ b/app/actions/layers-commands.c @@ -57,6 +57,8 @@ #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" @@ -575,12 +577,15 @@ void layers_merge_down_cmd_callback (GtkAction *action, gpointer data) { - GimpImage *image; - GimpLayer *layer; + 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, NULL); + GIMP_EXPAND_AS_NECESSARY, + GIMP_PROGRESS (display), NULL); gimp_image_flush (image); } @@ -656,12 +661,42 @@ layers_text_along_vectors_cmd_callback (GtkAction *action, 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); - gimp_vectors_warp_vectors (vectors, new_vectors, - 0.5 * gimp_item_get_height (GIMP_ITEM (layer))); + 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); diff --git a/app/actions/quick-mask-commands.c b/app/actions/quick-mask-commands.c index 7bf9a9adad..ef3ee25a1e 100644 --- a/app/actions/quick-mask-commands.c +++ b/app/actions/quick-mask-commands.c @@ -42,6 +42,9 @@ #include "gimp-intl.h" +#define RGBA_EPSILON 1e-6 + + /* local function prototypes */ static void quick_mask_configure_callback (GtkWidget *dialog, @@ -167,7 +170,7 @@ quick_mask_configure_callback (GtkWidget *dialog, gimp_image_get_quick_mask_color (image, &old_color); - if (gimp_rgba_distance (&old_color, channel_color) > 0.0001) + if (gimp_rgba_distance (&old_color, channel_color) > RGBA_EPSILON) { gimp_image_set_quick_mask_color (image, channel_color); gimp_image_flush (image); diff --git a/app/actions/tools-actions.c b/app/actions/tools-actions.c index 6d33848cd6..ec007c63c3 100644 --- a/app/actions/tools-actions.c +++ b/app/actions/tools-actions.c @@ -677,11 +677,11 @@ tools_actions_setup (GimpActionGroup *group) G_N_ELEMENTS (tools_ink_blob_angle_actions), G_CALLBACK (tools_ink_blob_angle_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_airbrush_rate_actions, G_N_ELEMENTS (tools_airbrush_rate_actions), G_CALLBACK (tools_airbrush_rate_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_airbrush_flow_actions, G_N_ELEMENTS (tools_airbrush_flow_actions), G_CALLBACK (tools_airbrush_flow_cmd_callback)); @@ -714,31 +714,31 @@ tools_actions_setup (GimpActionGroup *group) G_N_ELEMENTS (tools_warp_effect_hardness_actions), G_CALLBACK (tools_warp_effect_hardness_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_opacity_actions, G_N_ELEMENTS (tools_opacity_actions), G_CALLBACK (tools_opacity_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_size_actions, G_N_ELEMENTS (tools_size_actions), G_CALLBACK (tools_size_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_aspect_actions, G_N_ELEMENTS (tools_aspect_actions), G_CALLBACK (tools_aspect_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_angle_actions, G_N_ELEMENTS (tools_angle_actions), G_CALLBACK (tools_angle_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_spacing_actions, G_N_ELEMENTS (tools_spacing_actions), G_CALLBACK (tools_spacing_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_hardness_actions, G_N_ELEMENTS (tools_hardness_actions), G_CALLBACK (tools_hardness_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "tools-action", tools_force_actions, G_N_ELEMENTS (tools_force_actions), G_CALLBACK (tools_force_cmd_callback)); diff --git a/app/actions/view-actions.c b/app/actions/view-actions.c index 4dfef28865..3b0203f91c 100644 --- a/app/actions/view-actions.c +++ b/app/actions/view-actions.c @@ -318,17 +318,17 @@ static const GimpToggleActionEntry view_toggle_actions[] = static const GimpEnumActionEntry view_zoom_actions[] = { { "view-zoom", NULL, - NC_("view-action", "Set zoom factor"), NULL, 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-action", "Zoom out as far as possible"), NULL, NULL, + 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-action", "Zoom in as far as possible"), NULL, NULL, + NC_("view-zoom-action", "Zoom in as far as possible"), NULL, NULL, GIMP_ACTION_SELECT_LAST, FALSE, GIMP_HELP_VIEW_ZOOM_IN }, @@ -357,12 +357,12 @@ static const GimpEnumActionEntry view_zoom_actions[] = GIMP_HELP_VIEW_ZOOM_IN }, { "view-zoom-out-skip", GIMP_ICON_ZOOM_OUT, - NC_("view-action", "Zoom out a lot"), NULL, NULL, + 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-action", "Zoom in a lot"), NULL, NULL, + NC_("view-zoom-action", "Zoom in a lot"), NULL, NULL, GIMP_ACTION_SELECT_SKIP_NEXT, FALSE, GIMP_HELP_VIEW_ZOOM_IN } }; @@ -748,12 +748,12 @@ view_actions_setup (GimpActionGroup *group) G_N_ELEMENTS (view_padding_color_actions), G_CALLBACK (view_padding_color_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "view-action", view_scroll_horizontal_actions, G_N_ELEMENTS (view_scroll_horizontal_actions), G_CALLBACK (view_scroll_horizontal_cmd_callback)); - gimp_action_group_add_enum_actions (group, NULL, + gimp_action_group_add_enum_actions (group, "view-action", view_scroll_vertical_actions, G_N_ELEMENTS (view_scroll_vertical_actions), G_CALLBACK (view_scroll_vertical_cmd_callback)); @@ -1133,16 +1133,19 @@ view_actions_set_rotate (GimpActionGroup *group, 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 diff --git a/app/actions/windows-actions.c b/app/actions/windows-actions.c index 4e045c83a4..5b247e4ed0 100644 --- a/app/actions/windows-actions.c +++ b/app/actions/windows-actions.c @@ -41,6 +41,7 @@ #include "widgets/gimphelp-ids.h" #include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" #include "dialogs/dialogs.h" @@ -63,6 +64,9 @@ static void windows_actions_display_reorder (GimpContainer *conta 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, @@ -299,12 +303,17 @@ 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); - if (gimp_display_get_image (display)) - windows_actions_image_notify (display, NULL, group); + g_signal_connect_object (shell, "notify::title", + G_CALLBACK (windows_actions_title_notify), + group, 0); + + windows_actions_image_notify (display, NULL, group); } static void @@ -312,8 +321,16 @@ windows_actions_display_remove (GimpContainer *container, GimpDisplay *display, GimpActionGroup *group) { - GtkAction *action; - gchar *action_name = gimp_display_get_action_name (display); + GimpDisplayShell *shell = gimp_display_get_shell (display); + GtkAction *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 = gtk_action_group_get_action (GTK_ACTION_GROUP (group), action_name); @@ -340,71 +357,81 @@ windows_actions_image_notify (GimpDisplay *display, GimpActionGroup *group) { GimpImage *image = gimp_display_get_image (display); + GtkAction *action; + gchar *action_name; - if (image) + action_name = gimp_display_get_action_name (display); + + action = gtk_action_group_get_action (GTK_ACTION_GROUP (group), + action_name); + + if (! action) { - GtkAction *action; - gchar *action_name = gimp_display_get_action_name (display); + GimpActionEntry entry; + entry.name = action_name; + entry.icon_name = GIMP_ICON_IMAGE; + entry.label = ""; + entry.accelerator = NULL; + entry.tooltip = NULL; + entry.callback = G_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 = gtk_action_group_get_action (GTK_ACTION_GROUP (group), action_name); - if (! action) - { - GimpActionEntry entry; + g_object_set_data (G_OBJECT (action), "display", display); + } - entry.name = action_name; - entry.icon_name = GIMP_ICON_IMAGE; - entry.label = ""; - entry.accelerator = NULL; - entry.tooltip = NULL; - entry.callback = G_CALLBACK (windows_show_display_cmd_callback); - entry.help_id = NULL; + g_free (action_name); - gimp_action_group_add_actions (group, NULL, &entry, 1); + if (image) + { + const gchar *display_name; + gchar *escaped; + gchar *title; - gimp_action_group_set_action_always_show_image (group, action_name, - TRUE); + display_name = gimp_image_get_display_name (image); + escaped = gimp_escape_uline (display_name); - action = gtk_action_group_get_action (GTK_ACTION_GROUP (group), - action_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_data (G_OBJECT (action), "display", display); - } + 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); - { - 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, - "label", title, - "tooltip", gimp_image_get_display_path (image), - "viewable", image, - "context", gimp_get_user_context (group->gimp), - NULL); - - g_free (title); - } - - g_free (action_name); + g_free (title); windows_actions_update_display_accels (group); } else { - windows_actions_display_remove (group->gimp->displays, display, group); + 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) { diff --git a/app/app.c b/app/app.c index f567629029..81e6bdc503 100644 --- a/app/app.c +++ b/app/app.c @@ -56,7 +56,6 @@ #include "core/gimp.h" #include "core/gimp-batch.h" #include "core/gimp-user-install.h" -#include "core/gimpbacktrace.h" #include "core/gimpimage.h" #include "file/file-open.h" @@ -213,12 +212,6 @@ app_run (const gchar *full_prog_name, filenames = NULL; } - /* 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 (); - /* 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 diff --git a/app/config/Makefile.am b/app/config/Makefile.am index c3df49f3bc..04ac126bfb 100644 --- a/app/config/Makefile.am +++ b/app/config/Makefile.am @@ -15,7 +15,6 @@ endif AM_CPPFLAGS = \ -DG_LOG_DOMAIN=\"Gimp-Config\" \ -DGIMP_APP_VERSION_STRING=\"$(GIMP_APP_VERSION)\" \ - -DDATADIR=\""$(datadir)"\" \ -I$(top_builddir) \ -I$(top_srcdir) \ -I$(top_builddir)/app \ @@ -105,6 +104,7 @@ test_config_LDADD = \ ../vectors/libappvectors.a \ ../core/libappcore.a \ ../file/libappfile.a \ + ../file-data/libappfile-data.a \ ../text/libapptext.a \ ../paint/libapppaint.a \ ../gegl/libappgegl.a \ diff --git a/app/config/Makefile.in b/app/config/Makefile.in index 4a22aee996..201d86b218 100644 --- a/app/config/Makefile.in +++ b/app/config/Makefile.in @@ -105,7 +105,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -445,8 +444,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -576,7 +573,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -715,8 +711,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ @@ -849,7 +843,6 @@ libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la AM_CPPFLAGS = \ -DG_LOG_DOMAIN=\"Gimp-Config\" \ -DGIMP_APP_VERSION_STRING=\"$(GIMP_APP_VERSION)\" \ - -DDATADIR=\""$(datadir)"\" \ -I$(top_builddir) \ -I$(top_srcdir) \ -I$(top_builddir)/app \ @@ -930,6 +923,7 @@ test_config_LDADD = \ ../vectors/libappvectors.a \ ../core/libappcore.a \ ../file/libappfile.a \ + ../file-data/libappfile-data.a \ ../text/libapptext.a \ ../paint/libapppaint.a \ ../gegl/libappgegl.a \ diff --git a/app/config/config-enums.c b/app/config/config-enums.c index 9c9d9c22ce..70da0248a1 100644 --- a/app/config/config-enums.c +++ b/app/config/config-enums.c @@ -101,6 +101,47 @@ gimp_cursor_mode_get_type (void) 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) { diff --git a/app/config/config-enums.h b/app/config/config-enums.h index 8ec6dc4d67..3881dbabc7 100644 --- a/app/config/config-enums.h +++ b/app/config/config-enums.h @@ -56,6 +56,23 @@ typedef enum } 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; diff --git a/app/config/gimpconfig-dump.c b/app/config/gimpconfig-dump.c index eb35746dfe..1a2b37654e 100644 --- a/app/config/gimpconfig-dump.c +++ b/app/config/gimpconfig-dump.c @@ -271,6 +271,14 @@ static const gchar *man_page_path = "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[] = diff --git a/app/config/gimpcoreconfig.c b/app/config/gimpcoreconfig.c index bb80fce212..c59b778cd1 100644 --- a/app/config/gimpcoreconfig.c +++ b/app/config/gimpcoreconfig.c @@ -24,6 +24,16 @@ #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" @@ -41,15 +51,15 @@ #include "gimp-intl.h" -#define DEFAULT_BRUSH "2. Hardness 050" -#define DEFAULT_DYNAMICS "Dynamics Off" -#define DEFAULT_PATTERN "Pine" -#define DEFAULT_PALETTE "Default" -#define DEFAULT_GRADIENT "FG to BG (RGB)" -#define DEFAULT_TOOL_PRESET "Current Options" -#define DEFAULT_FONT "Sans-serif" -#define DEFAULT_MYPAINT_BRUSH "Fixme" -#define DEFAULT_COMMENT "Created with GIMP" +#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 @@ -111,6 +121,7 @@ enum 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, @@ -147,6 +158,34 @@ 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) @@ -380,6 +419,36 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass) 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", @@ -401,56 +470,56 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass) "default-brush", "Default brush", DEFAULT_BRUSH_BLURB, - DEFAULT_BRUSH, + GIMP_DEFAULT_BRUSH, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_DYNAMICS, "default-dynamics", "Default dynamics", DEFAULT_DYNAMICS_BLURB, - DEFAULT_DYNAMICS, + 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, - DEFAULT_MYPAINT_BRUSH, + GIMP_DEFAULT_MYPAINT_BRUSH, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_PATTERN, "default-pattern", "Default pattern", DEFAULT_PATTERN_BLURB, - DEFAULT_PATTERN, + GIMP_DEFAULT_PATTERN, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_PALETTE, "default-palette", "Default palette", DEFAULT_PALETTE_BLURB, - DEFAULT_PALETTE, + GIMP_DEFAULT_PALETTE, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_GRADIENT, "default-gradient", "Default gradient", DEFAULT_GRADIENT_BLURB, - DEFAULT_GRADIENT, + 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, - DEFAULT_TOOL_PRESET, + GIMP_DEFAULT_TOOL_PRESET, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_STRING (object_class, PROP_DEFAULT_FONT, "default-font", "Default font", DEFAULT_FONT_BLURB, - DEFAULT_FONT, + GIMP_DEFAULT_FONT, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_GLOBAL_BRUSH, @@ -650,6 +719,14 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass) 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", @@ -711,7 +788,7 @@ gimp_core_config_init (GimpCoreConfig *config) { config->default_image = g_object_new (GIMP_TYPE_TEMPLATE, "name", "Default Image", - "comment", DEFAULT_COMMENT, + "comment", GIMP_DEFAULT_COMMENT, NULL); g_signal_connect (config->default_image, "notify", G_CALLBACK (gimp_core_config_default_image_notify), @@ -986,6 +1063,9 @@ gimp_core_config_set_property (GObject *object, 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; @@ -1191,6 +1271,9 @@ gimp_core_config_get_property (GObject *object, 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; diff --git a/app/config/gimpcoreconfig.h b/app/config/gimpcoreconfig.h index f3ce41f38d..747616f62f 100644 --- a/app/config/gimpcoreconfig.h +++ b/app/config/gimpcoreconfig.h @@ -96,6 +96,7 @@ struct _GimpCoreConfig 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; diff --git a/app/config/gimpdialogconfig.c b/app/config/gimpdialogconfig.c index 766d1d18f4..9a226cf9d5 100644 --- a/app/config/gimpdialogconfig.c +++ b/app/config/gimpdialogconfig.c @@ -185,7 +185,7 @@ gimp_dialog_config_class_init (GimpDialogConfigClass *klass) GIMP_CONFIG_PROP_ENUM (object_class, PROP_IMAGE_CONVERT_PROFILE_INTENT, "image-convert-profile-intent", - "Default rendering intent fo color profile conversion", + "Default rendering intent for color profile conversion", IMAGE_CONVERT_PROFILE_INTENT_BLURB, GIMP_TYPE_COLOR_RENDERING_INTENT, GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, diff --git a/app/config/gimpgeglconfig.c b/app/config/gimpgeglconfig.c index eb15586323..9ccbfa9197 100644 --- a/app/config/gimpgeglconfig.c +++ b/app/config/gimpgeglconfig.c @@ -119,7 +119,7 @@ gimp_gegl_config_class_init (GimpGeglConfigClass *klass) "Temp path", TEMP_PATH_BLURB, GIMP_CONFIG_PATH_DIR, - "${gimp_dir}" G_DIR_SEPARATOR_S "tmp", + "${gimp_temp_dir}", GIMP_PARAM_STATIC_STRINGS | GIMP_CONFIG_PARAM_RESTART); @@ -128,7 +128,7 @@ gimp_gegl_config_class_init (GimpGeglConfigClass *klass) "Swap path", SWAP_PATH_BLURB, GIMP_CONFIG_PATH_DIR, - "${gimp_dir}", + "${gimp_cache_dir}", GIMP_PARAM_STATIC_STRINGS | GIMP_CONFIG_PARAM_RESTART); diff --git a/app/config/gimplangrc.c b/app/config/gimplangrc.c index 597ebcf5b4..34e26f47ab 100644 --- a/app/config/gimplangrc.c +++ b/app/config/gimplangrc.c @@ -149,11 +149,8 @@ gimp_lang_rc_finalize (GObject *object) g_clear_object (&rc->system_gimprc); g_clear_object (&rc->user_gimprc); - if (rc->language) - { - g_free (rc->language); - rc->language = NULL; - } + + g_clear_pointer (&rc->language, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h index 237daca3f9..ec62116686 100644 --- a/app/config/gimprc-blurbs.h +++ b/app/config/gimprc-blurbs.h @@ -97,16 +97,16 @@ _("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." +_("Snap to guides by default in new image windows.") #define SNAP_TO_GRID_BLURB \ -"Snap to the grid by default in new image windows." +_("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." +_("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." +_("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 " \ @@ -204,6 +204,9 @@ _("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.") @@ -468,7 +471,7 @@ _("Sets the folder for temporary storage. Files will appear here " \ "is best if this folder not be one that is shared by other users.") #define THEME_BLURB \ -"The name of the theme to use." +_("The name of the theme to use.") #define THEME_PATH_BLURB \ "Sets the theme search path." @@ -686,10 +689,10 @@ _("When enabled, uses OpenCL for some operations.") "offset problems turning it off helps." #define SEARCH_SHOW_UNAVAILABLE_BLURB \ -"When enabled, a search of actions will also return inactive actions." +_("When enabled, a search of actions will also return inactive actions.") #define ACTION_HISTORY_SIZE_BLURB \ -"The maximum number of actions saved in history." +_("The maximum number of actions saved in history.") #endif /* __GIMP_RC_BLURBS_H__ */ diff --git a/app/core/Makefile.am b/app/core/Makefile.am index b0bc375838..9230078118 100644 --- a/app/core/Makefile.am +++ b/app/core/Makefile.am @@ -111,6 +111,8 @@ libappcore_a_sources = \ gimpbrush-load.c \ gimpbrush-load.h \ gimpbrush-private.h \ + gimpbrush-save.c \ + gimpbrush-save.h \ gimpbrush-transform.cc \ gimpbrush-transform.h \ gimpbrushcache.c \ @@ -127,6 +129,8 @@ libappcore_a_sources = \ gimpbrushpipe.h \ gimpbrushpipe-load.c \ gimpbrushpipe-load.h \ + gimpbrushpipe-save.c \ + gimpbrushpipe-save.h \ gimpbuffer.c \ gimpbuffer.h \ gimpcancelable.c \ @@ -141,6 +145,8 @@ libappcore_a_sources = \ gimpchannelpropundo.h \ gimpchannelundo.c \ gimpchannelundo.h \ + gimpchunkiterator.c \ + gimpchunkiterator.h \ gimpcontainer.c \ gimpcontainer.h \ gimpcontainer-filter.c \ @@ -274,8 +280,6 @@ libappcore_a_sources = \ gimpimage-crop.h \ gimpimage-duplicate.c \ gimpimage-duplicate.h \ - gimpimage-fade.c \ - gimpimage-fade.h \ gimpimage-flip.c \ gimpimage-flip.h \ gimpimage-grid.h \ @@ -353,6 +357,8 @@ libappcore_a_sources = \ gimplayerstack.h \ gimplayerundo.c \ gimplayerundo.h \ + gimplineart.c \ + gimplineart.h \ gimplist.c \ gimplist.h \ gimpmaskundo.c \ @@ -373,6 +379,8 @@ libappcore_a_sources = \ gimppattern-header.h \ gimppattern-load.c \ gimppattern-load.h \ + gimppattern-save.c \ + gimppattern-save.h \ gimppatternclipboard.c \ gimppatternclipboard.h \ gimppalette.c \ @@ -399,7 +407,7 @@ libappcore_a_sources = \ gimppickable.h \ gimppickable-auto-shrink.c \ gimppickable-auto-shrink.h \ - gimppickable-contiguous-region.c \ + gimppickable-contiguous-region.cc \ gimppickable-contiguous-region.h \ gimpprogress.c \ gimpprogress.h \ diff --git a/app/core/Makefile.in b/app/core/Makefile.in index 61c1ac42dd..8db59528f8 100644 --- a/app/core/Makefile.in +++ b/app/core/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -140,14 +139,15 @@ am__objects_2 = gimp.$(OBJEXT) gimp-atomic.$(OBJEXT) \ gimpbacktrace-windows.$(OBJEXT) gimpbezierdesc.$(OBJEXT) \ gimpboundary.$(OBJEXT) gimpbrush.$(OBJEXT) \ gimpbrush-boundary.$(OBJEXT) gimpbrush-load.$(OBJEXT) \ - gimpbrush-transform.$(OBJEXT) gimpbrushcache.$(OBJEXT) \ - gimpbrushclipboard.$(OBJEXT) gimpbrushgenerated.$(OBJEXT) \ - gimpbrushgenerated-load.$(OBJEXT) \ + gimpbrush-save.$(OBJEXT) gimpbrush-transform.$(OBJEXT) \ + gimpbrushcache.$(OBJEXT) gimpbrushclipboard.$(OBJEXT) \ + gimpbrushgenerated.$(OBJEXT) gimpbrushgenerated-load.$(OBJEXT) \ gimpbrushgenerated-save.$(OBJEXT) gimpbrushpipe.$(OBJEXT) \ - gimpbrushpipe-load.$(OBJEXT) gimpbuffer.$(OBJEXT) \ - gimpcancelable.$(OBJEXT) gimpchannel.$(OBJEXT) \ - gimpchannel-combine.$(OBJEXT) gimpchannel-select.$(OBJEXT) \ - gimpchannelpropundo.$(OBJEXT) gimpchannelundo.$(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) \ @@ -183,35 +183,35 @@ am__objects_2 = gimp.$(OBJEXT) gimp-atomic.$(OBJEXT) \ gimpimage-convert-indexed.$(OBJEXT) \ gimpimage-convert-precision.$(OBJEXT) \ gimpimage-convert-type.$(OBJEXT) gimpimage-crop.$(OBJEXT) \ - gimpimage-duplicate.$(OBJEXT) gimpimage-fade.$(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-undo.$(OBJEXT) \ - gimpimage-undo-push.$(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) \ + 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-undo.$(OBJEXT) gimpimage-undo-push.$(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) gimppatternclipboard.$(OBJEXT) \ - gimppalette.$(OBJEXT) gimppalette-import.$(OBJEXT) \ - gimppalette-load.$(OBJEXT) gimppalette-save.$(OBJEXT) \ - gimppalettemru.$(OBJEXT) gimpparamspecs.$(OBJEXT) \ - gimpparamspecs-desc.$(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) \ @@ -272,19 +272,21 @@ am__depfiles_remade = ./$(DEPDIR)/core-enums.Po \ ./$(DEPDIR)/gimpbacktrace-windows.Po \ ./$(DEPDIR)/gimpbezierdesc.Po ./$(DEPDIR)/gimpboundary.Po \ ./$(DEPDIR)/gimpbrush-boundary.Po \ - ./$(DEPDIR)/gimpbrush-load.Po \ + ./$(DEPDIR)/gimpbrush-load.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.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 \ @@ -335,8 +337,8 @@ am__depfiles_remade = ./$(DEPDIR)/core-enums.Po \ ./$(DEPDIR)/gimpimage-convert-type.Po \ ./$(DEPDIR)/gimpimage-crop.Po \ ./$(DEPDIR)/gimpimage-duplicate.Po \ - ./$(DEPDIR)/gimpimage-fade.Po ./$(DEPDIR)/gimpimage-flip.Po \ - ./$(DEPDIR)/gimpimage-grid.Po ./$(DEPDIR)/gimpimage-guides.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 \ @@ -363,18 +365,20 @@ am__depfiles_remade = ./$(DEPDIR)/core-enums.Po \ ./$(DEPDIR)/gimplayermaskpropundo.Po \ ./$(DEPDIR)/gimplayermaskundo.Po \ ./$(DEPDIR)/gimplayerpropundo.Po ./$(DEPDIR)/gimplayerstack.Po \ - ./$(DEPDIR)/gimplayerundo.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)/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.Po \ + ./$(DEPDIR)/gimppattern-load.Po \ + ./$(DEPDIR)/gimppattern-save.Po ./$(DEPDIR)/gimppattern.Po \ ./$(DEPDIR)/gimppatternclipboard.Po \ ./$(DEPDIR)/gimppdbprogress.Po \ ./$(DEPDIR)/gimppickable-auto-shrink.Po \ @@ -493,8 +497,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -624,7 +626,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -763,8 +764,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ @@ -994,6 +993,8 @@ libappcore_a_sources = \ gimpbrush-load.c \ gimpbrush-load.h \ gimpbrush-private.h \ + gimpbrush-save.c \ + gimpbrush-save.h \ gimpbrush-transform.cc \ gimpbrush-transform.h \ gimpbrushcache.c \ @@ -1010,6 +1011,8 @@ libappcore_a_sources = \ gimpbrushpipe.h \ gimpbrushpipe-load.c \ gimpbrushpipe-load.h \ + gimpbrushpipe-save.c \ + gimpbrushpipe-save.h \ gimpbuffer.c \ gimpbuffer.h \ gimpcancelable.c \ @@ -1024,6 +1027,8 @@ libappcore_a_sources = \ gimpchannelpropundo.h \ gimpchannelundo.c \ gimpchannelundo.h \ + gimpchunkiterator.c \ + gimpchunkiterator.h \ gimpcontainer.c \ gimpcontainer.h \ gimpcontainer-filter.c \ @@ -1157,8 +1162,6 @@ libappcore_a_sources = \ gimpimage-crop.h \ gimpimage-duplicate.c \ gimpimage-duplicate.h \ - gimpimage-fade.c \ - gimpimage-fade.h \ gimpimage-flip.c \ gimpimage-flip.h \ gimpimage-grid.h \ @@ -1236,6 +1239,8 @@ libappcore_a_sources = \ gimplayerstack.h \ gimplayerundo.c \ gimplayerundo.h \ + gimplineart.c \ + gimplineart.h \ gimplist.c \ gimplist.h \ gimpmaskundo.c \ @@ -1256,6 +1261,8 @@ libappcore_a_sources = \ gimppattern-header.h \ gimppattern-load.c \ gimppattern-load.h \ + gimppattern-save.c \ + gimppattern-save.h \ gimppatternclipboard.c \ gimppatternclipboard.h \ gimppalette.c \ @@ -1282,7 +1289,7 @@ libappcore_a_sources = \ gimppickable.h \ gimppickable-auto-shrink.c \ gimppickable-auto-shrink.h \ - gimppickable-contiguous-region.c \ + gimppickable-contiguous-region.cc \ gimppickable-contiguous-region.h \ gimpprogress.c \ gimpprogress.h \ @@ -1460,6 +1467,7 @@ distclean-compile: @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-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 @@ -1468,6 +1476,7 @@ distclean-compile: @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 @@ -1476,6 +1485,7 @@ distclean-compile: @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 @@ -1540,7 +1550,6 @@ distclean-compile: @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-fade.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 @@ -1580,6 +1589,7 @@ distclean-compile: @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 @@ -1598,6 +1608,7 @@ distclean-compile: @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 @@ -1858,6 +1869,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/gimpboundary.Po -rm -f ./$(DEPDIR)/gimpbrush-boundary.Po -rm -f ./$(DEPDIR)/gimpbrush-load.Po + -rm -f ./$(DEPDIR)/gimpbrush-save.Po -rm -f ./$(DEPDIR)/gimpbrush-transform.Po -rm -f ./$(DEPDIR)/gimpbrush.Po -rm -f ./$(DEPDIR)/gimpbrushcache.Po @@ -1866,6 +1878,7 @@ distclean: distclean-am -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 @@ -1874,6 +1887,7 @@ distclean: distclean-am -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 @@ -1938,7 +1952,6 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/gimpimage-convert-type.Po -rm -f ./$(DEPDIR)/gimpimage-crop.Po -rm -f ./$(DEPDIR)/gimpimage-duplicate.Po - -rm -f ./$(DEPDIR)/gimpimage-fade.Po -rm -f ./$(DEPDIR)/gimpimage-flip.Po -rm -f ./$(DEPDIR)/gimpimage-grid.Po -rm -f ./$(DEPDIR)/gimpimage-guides.Po @@ -1978,6 +1991,7 @@ distclean: distclean-am -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 @@ -1996,6 +2010,7 @@ distclean: distclean-am -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 @@ -2117,6 +2132,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/gimpboundary.Po -rm -f ./$(DEPDIR)/gimpbrush-boundary.Po -rm -f ./$(DEPDIR)/gimpbrush-load.Po + -rm -f ./$(DEPDIR)/gimpbrush-save.Po -rm -f ./$(DEPDIR)/gimpbrush-transform.Po -rm -f ./$(DEPDIR)/gimpbrush.Po -rm -f ./$(DEPDIR)/gimpbrushcache.Po @@ -2125,6 +2141,7 @@ maintainer-clean: maintainer-clean-am -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 @@ -2133,6 +2150,7 @@ maintainer-clean: maintainer-clean-am -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 @@ -2197,7 +2215,6 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/gimpimage-convert-type.Po -rm -f ./$(DEPDIR)/gimpimage-crop.Po -rm -f ./$(DEPDIR)/gimpimage-duplicate.Po - -rm -f ./$(DEPDIR)/gimpimage-fade.Po -rm -f ./$(DEPDIR)/gimpimage-flip.Po -rm -f ./$(DEPDIR)/gimpimage-grid.Po -rm -f ./$(DEPDIR)/gimpimage-guides.Po @@ -2237,6 +2254,7 @@ maintainer-clean: maintainer-clean-am -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 @@ -2255,6 +2273,7 @@ maintainer-clean: maintainer-clean-am -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 diff --git a/app/core/core-enums.c b/app/core/core-enums.c index aa0db6bb59..1202ca35e3 100644 --- a/app/core/core-enums.c +++ b/app/core/core-enums.c @@ -329,6 +329,35 @@ gimp_convolution_type_get_type (void) 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) { diff --git a/app/core/core-enums.h b/app/core/core-enums.h index 1c063a5bea..7cb5a71c09 100644 --- a/app/core/core-enums.h +++ b/app/core/core-enums.h @@ -176,6 +176,17 @@ typedef enum /*< pdb-skip >*/ } 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; diff --git a/app/core/core-types.h b/app/core/core-types.h index aedd5e37f8..e1816415e4 100644 --- a/app/core/core-types.h +++ b/app/core/core-types.h @@ -187,6 +187,7 @@ typedef struct _GimpHistogram GimpHistogram; typedef struct _GimpIdTable GimpIdTable; typedef struct _GimpImagefile GimpImagefile; typedef struct _GimpInterpreterDB GimpInterpreterDB; +typedef struct _GimpLineArt GimpLineArt; typedef struct _GimpObjectQueue GimpObjectQueue; typedef struct _GimpParasiteList GimpParasiteList; typedef struct _GimpPdbProgress GimpPdbProgress; @@ -213,6 +214,7 @@ typedef struct _GimpWaitable GimpWaitable; /* dummy typede typedef struct _GimpBacktrace GimpBacktrace; typedef struct _GimpBoundSeg GimpBoundSeg; +typedef struct _GimpChunkIterator GimpChunkIterator; typedef struct _GimpCoords GimpCoords; typedef struct _GimpGradientSegment GimpGradientSegment; typedef struct _GimpPaletteEntry GimpPaletteEntry; diff --git a/app/core/gimp-data-factories.c b/app/core/gimp-data-factories.c index 41f4303a2e..69b5e2ab89 100644 --- a/app/core/gimp-data-factories.c +++ b/app/core/gimp-data-factories.c @@ -77,7 +77,7 @@ gimp_data_factories_init (Gimp *gimp) "GIMP Brush", gimp_brush_load, GIMP_BRUSH_FILE_EXTENSION, - FALSE); + TRUE); gimp_data_loader_factory_add_loader (gimp->brush_factory, "GIMP Brush Pixmap", gimp_brush_load, @@ -102,7 +102,7 @@ gimp_data_factories_init (Gimp *gimp) "GIMP Brush Pipe", gimp_brush_pipe_load, GIMP_BRUSH_PIPE_FILE_EXTENSION, - FALSE); + TRUE); gimp->dynamics_factory = gimp_data_loader_factory_new (gimp, @@ -147,7 +147,7 @@ gimp_data_factories_init (Gimp *gimp) "GIMP Pattern", gimp_pattern_load, GIMP_PATTERN_FILE_EXTENSION, - FALSE); + TRUE); gimp_data_loader_factory_add_fallback (gimp->pattern_factory, "Pattern from GdkPixbuf", gimp_pattern_load_pixbuf); diff --git a/app/core/gimp-edit.c b/app/core/gimp-edit.c index 78199f0c55..372ad2dcf7 100644 --- a/app/core/gimp-edit.c +++ b/app/core/gimp-edit.c @@ -30,11 +30,11 @@ #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.h" #include "gimplayer-floating-selection.h" #include "gimplayer-new.h" #include "gimplist.h" @@ -207,6 +207,25 @@ gimp_edit_paste_is_in_place (GimpPasteType paste_type) 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, @@ -232,7 +251,7 @@ gimp_edit_paste_get_layer (GimpImage *image, /* floating pastes always have the pasted-to drawable's format with * alpha; if drawable == NULL, user is pasting into an empty image */ - if (drawable) + 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); @@ -249,11 +268,15 @@ gimp_edit_paste_get_layer (GimpImage *image, 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, force creation of a - * plain layer, so gimp_item_convert() will collapse a - * group layer + /* 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. */ - layer_type = GIMP_TYPE_LAYER; + 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: diff --git a/app/core/gimp-internal-data.c b/app/core/gimp-internal-data.c index d07f309973..6653946f6e 100644 --- a/app/core/gimp-internal-data.c +++ b/app/core/gimp-internal-data.c @@ -288,17 +288,25 @@ gimp_internal_data_save_data_file (Gimp *gimp, success = FALSE; } } - else 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)); + 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); diff --git a/app/core/gimp-parallel.cc b/app/core/gimp-parallel.cc index fc9f5cd4fa..46002c9334 100644 --- a/app/core/gimp-parallel.cc +++ b/app/core/gimp-parallel.cc @@ -45,9 +45,8 @@ extern "C" #include "gimpcancelable.h" -#define GIMP_PARALLEL_MAX_THREADS 64 +#define GIMP_PARALLEL_MAX_THREADS 64 #define GIMP_PARALLEL_RUN_ASYNC_MAX_THREADS 1 -#define GIMP_PARALLEL_DISTRIBUTE_MAX_THREADS GIMP_PARALLEL_MAX_THREADS typedef struct @@ -68,62 +67,33 @@ typedef struct GimpAsync *current_async; } GimpParallelRunAsyncThread; -typedef struct -{ - GimpParallelDistributeFunc func; - gint n; - gpointer user_data; -} GimpParallelDistributeTask; - -typedef struct -{ - GThread *thread; - GMutex mutex; - GCond cond; - - gboolean quit; - - GimpParallelDistributeTask *volatile task; - volatile gint i; -} GimpParallelDistributeThread; - /* local function prototypes */ -static void gimp_parallel_notify_num_processors (GimpGeglConfig *config); +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_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_distribute_set_n_threads (gint n_threads); -static gpointer gimp_parallel_distribute_thread_func (GimpParallelDistributeThread *thread); +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 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; - -static gint gimp_parallel_distribute_n_threads = 1; -static GimpParallelDistributeThread gimp_parallel_distribute_threads[GIMP_PARALLEL_DISTRIBUTE_MAX_THREADS - 1]; - -static GMutex gimp_parallel_distribute_completion_mutex; -static GCond gimp_parallel_distribute_completion_cond; -static volatile gint gimp_parallel_distribute_completion_counter; -static volatile gint gimp_parallel_distribute_busy; +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 */ @@ -191,6 +161,9 @@ gimp_parallel_run_async_full (gint priority, 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); @@ -211,6 +184,14 @@ gimp_parallel_run_async_full (gint priority, GimpAsync * gimp_parallel_run_async_independent (GimpParallelRunAsyncFunc func, gpointer user_data) +{ + return gimp_parallel_run_async_independent_full (0, func, user_data); +} + +GimpAsync * +gimp_parallel_run_async_independent_full (gint priority, + GimpParallelRunAsyncFunc func, + gpointer user_data) { GimpAsync *async; GimpParallelRunAsyncTask *task; @@ -223,6 +204,7 @@ gimp_parallel_run_async_independent (GimpParallelRunAsyncFunc func, task = g_slice_new0 (GimpParallelRunAsyncTask); task->async = GIMP_ASYNC (g_object_ref (async)); + task->priority = priority; task->func = func; task->user_data = user_data; @@ -232,12 +214,22 @@ gimp_parallel_run_async_independent (GimpParallelRunAsyncFunc func, { GimpParallelRunAsyncTask *task = (GimpParallelRunAsyncTask *) data; - /* lower the thread's priority */ + /* adjust the thread's priority */ #if defined (G_OS_WIN32) - SetThreadPriority (GetCurrentThread (), THREAD_MODE_BACKGROUND_BEGIN); + 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__) - nice (+10) != -1; - /* ^-- avoid "unused result" warning */ + if (task->priority) + { + nice (task->priority) != -1; + /* ^-- avoid "unused result" warning */ + } #endif while (gimp_parallel_run_async_execute_task (task)); @@ -257,154 +249,6 @@ gimp_parallel_run_async_independent (GimpParallelRunAsyncFunc func, return async; } -void -gimp_parallel_distribute (gint max_n, - GimpParallelDistributeFunc func, - gpointer user_data) -{ - GimpParallelDistributeTask task; - gint i; - - g_return_if_fail (func != NULL); - - if (max_n == 0) - return; - - if (max_n < 0) - max_n = gimp_parallel_distribute_n_threads; - else - max_n = MIN (max_n, gimp_parallel_distribute_n_threads); - - if (max_n == 1 || - ! g_atomic_int_compare_and_exchange (&gimp_parallel_distribute_busy, - 0, 1)) - { - func (0, 1, user_data); - - return; - } - - task.n = max_n; - task.func = func; - task.user_data = user_data; - - g_atomic_int_set (&gimp_parallel_distribute_completion_counter, task.n - 1); - - for (i = 0; i < task.n - 1; i++) - { - GimpParallelDistributeThread *thread = - &gimp_parallel_distribute_threads[i]; - - g_mutex_lock (&thread->mutex); - - thread->task = &task; - thread->i = i; - - g_cond_signal (&thread->cond); - - g_mutex_unlock (&thread->mutex); - } - - func (i, task.n, user_data); - - if (g_atomic_int_get (&gimp_parallel_distribute_completion_counter)) - { - g_mutex_lock (&gimp_parallel_distribute_completion_mutex); - - while (g_atomic_int_get (&gimp_parallel_distribute_completion_counter)) - { - g_cond_wait (&gimp_parallel_distribute_completion_cond, - &gimp_parallel_distribute_completion_mutex); - } - - g_mutex_unlock (&gimp_parallel_distribute_completion_mutex); - } - - g_atomic_int_set (&gimp_parallel_distribute_busy, 0); -} - -void -gimp_parallel_distribute_range (gsize size, - gsize min_sub_size, - GimpParallelDistributeRangeFunc func, - gpointer user_data) -{ - gsize n = size; - - g_return_if_fail (func != NULL); - - if (size == 0) - return; - - if (min_sub_size > 1) - n /= min_sub_size; - - n = CLAMP (n, 1, gimp_parallel_distribute_n_threads); - - gimp_parallel_distribute (n, [=] (gint i, gint n) - { - gsize offset; - gsize sub_size; - - offset = (2 * i * size + n) / (2 * n); - sub_size = (2 * (i + 1) * size + n) / (2 * n) - offset; - - func (offset, sub_size, user_data); - }); -} - -void -gimp_parallel_distribute_area (const GeglRectangle *area, - gsize min_sub_area, - GimpParallelDistributeAreaFunc func, - gpointer user_data) -{ - gsize n; - - g_return_if_fail (area != NULL); - g_return_if_fail (func != NULL); - - if (area->width <= 0 || area->height <= 0) - return; - - n = (gsize) area->width * (gsize) area->height; - - if (min_sub_area > 1) - n /= min_sub_area; - - n = CLAMP (n, 1, gimp_parallel_distribute_n_threads); - - gimp_parallel_distribute (n, [=] (gint i, gint n) - { - GeglRectangle sub_area; - - if (area->width <= area->height) - { - sub_area.x = area->x; - sub_area.width = area->width; - - sub_area.y = (2 * i * area->height + n) / (2 * n); - sub_area.height = (2 * (i + 1) * area->height + n) / (2 * n); - - sub_area.height -= sub_area.y; - sub_area.y += area->y; - } - else - { - sub_area.y = area->y; - sub_area.height = area->height; - - sub_area.x = (2 * i * area->width + n) / (2 * n); - sub_area.width = (2 * (i + 1) * area->width + n) / (2 * n); - - sub_area.width -= sub_area.x; - sub_area.x += area->x; - } - - func (&sub_area, user_data); - }); -} - /* private functions */ @@ -421,7 +265,6 @@ gimp_parallel_set_n_threads (gint n_threads, gboolean finish_tasks) { gimp_parallel_run_async_set_n_threads (n_threads, finish_tasks); - gimp_parallel_distribute_set_n_threads (n_threads); } static void @@ -679,95 +522,32 @@ gimp_parallel_run_async_cancel (GimpAsync *async) } static void -gimp_parallel_distribute_set_n_threads (gint n_threads) +gimp_parallel_run_async_waiting (GimpAsync *async) { - gint i; + GList *link; - while (! g_atomic_int_compare_and_exchange (&gimp_parallel_distribute_busy, - 0, 1)); + link = (GList *) g_object_get_data (G_OBJECT (async), + "gimp-parallel-run-async-link"); - n_threads = CLAMP (n_threads, 1, GIMP_PARALLEL_DISTRIBUTE_MAX_THREADS); + if (! link) + return; - if (n_threads > gimp_parallel_distribute_n_threads) /* need more threads */ + g_mutex_lock (&gimp_parallel_run_async_mutex); + + link = (GList *) g_object_get_data (G_OBJECT (async), + "gimp-parallel-run-async-link"); + + if (link) { - for (i = gimp_parallel_distribute_n_threads - 1; i < n_threads - 1; i++) - { - GimpParallelDistributeThread *thread = - &gimp_parallel_distribute_threads[i]; + GimpParallelRunAsyncTask *task = (GimpParallelRunAsyncTask *) link->data; - thread->quit = FALSE; - thread->task = NULL; + task->priority = G_MININT; - thread->thread = g_thread_new ( - "worker", - (GThreadFunc) gimp_parallel_distribute_thread_func, - thread); - } - } - else if (n_threads < gimp_parallel_distribute_n_threads) /* need less threads */ - { - for (i = n_threads - 1; i < gimp_parallel_distribute_n_threads - 1; i++) - { - GimpParallelDistributeThread *thread = - &gimp_parallel_distribute_threads[i]; - - g_mutex_lock (&thread->mutex); - - thread->quit = TRUE; - g_cond_signal (&thread->cond); - - g_mutex_unlock (&thread->mutex); - } - - for (i = n_threads - 1; i < gimp_parallel_distribute_n_threads - 1; i++) - { - GimpParallelDistributeThread *thread = - &gimp_parallel_distribute_threads[i]; - - g_thread_join (thread->thread); - } + g_queue_unlink (&gimp_parallel_run_async_queue, link); + g_queue_push_head_link (&gimp_parallel_run_async_queue, link); } - gimp_parallel_distribute_n_threads = n_threads; - - g_atomic_int_set (&gimp_parallel_distribute_busy, 0); -} - -static gpointer -gimp_parallel_distribute_thread_func (GimpParallelDistributeThread *thread) -{ - g_mutex_lock (&thread->mutex); - - while (TRUE) - { - if (thread->quit) - { - break; - } - else if (thread->task) - { - thread->task->func (thread->i, thread->task->n, - thread->task->user_data); - - if (g_atomic_int_dec_and_test ( - &gimp_parallel_distribute_completion_counter)) - { - g_mutex_lock (&gimp_parallel_distribute_completion_mutex); - - g_cond_signal (&gimp_parallel_distribute_completion_cond); - - g_mutex_unlock (&gimp_parallel_distribute_completion_mutex); - } - - thread->task = NULL; - } - - g_cond_wait (&thread->cond, &thread->mutex); - } - - g_mutex_unlock (&thread->mutex); - - return NULL; + g_mutex_unlock (&gimp_parallel_run_async_mutex); } } /* extern "C" */ diff --git a/app/core/gimp-parallel.h b/app/core/gimp-parallel.h index 8a615545cf..4df9efb996 100644 --- a/app/core/gimp-parallel.h +++ b/app/core/gimp-parallel.h @@ -22,42 +22,25 @@ #define __GIMP_PARALLEL_H__ -typedef void (* GimpParallelRunAsyncFunc) (GimpAsync *async, - gpointer user_data); - -typedef void (* GimpParallelDistributeFunc) (gint i, - gint n, - gpointer user_data); -typedef void (* GimpParallelDistributeRangeFunc) (gsize offset, - gsize size, - gpointer user_data); -typedef void (* GimpParallelDistributeAreaFunc) (const GeglRectangle *area, - gpointer user_data); +typedef void (* GimpParallelRunAsyncFunc) (GimpAsync *async, + gpointer user_data); -void gimp_parallel_init (Gimp *gimp); -void gimp_parallel_exit (Gimp *gimp); +void gimp_parallel_init (Gimp *gimp); +void gimp_parallel_exit (Gimp *gimp); -GimpAsync * gimp_parallel_run_async (GimpParallelRunAsyncFunc func, - gpointer user_data); -GimpAsync * gimp_parallel_run_async_full (gint priority, - GimpParallelRunAsyncFunc func, - gpointer user_data, - GDestroyNotify user_data_destroy_func); -GimpAsync * gimp_parallel_run_async_independent (GimpParallelRunAsyncFunc func, - gpointer user_data); +GimpAsync * gimp_parallel_run_async (GimpParallelRunAsyncFunc func, + gpointer user_data); +GimpAsync * gimp_parallel_run_async_full (gint priority, + GimpParallelRunAsyncFunc func, + gpointer user_data, + GDestroyNotify user_data_destroy_func); +GimpAsync * gimp_parallel_run_async_independent (GimpParallelRunAsyncFunc func, + gpointer user_data); +GimpAsync * gimp_parallel_run_async_independent_full (gint priority, + GimpParallelRunAsyncFunc func, + gpointer user_data); -void gimp_parallel_distribute (gint max_n, - GimpParallelDistributeFunc func, - gpointer user_data); -void gimp_parallel_distribute_range (gsize size, - gsize min_sub_size, - GimpParallelDistributeRangeFunc func, - gpointer user_data); -void gimp_parallel_distribute_area (const GeglRectangle *area, - gsize min_sub_area, - GimpParallelDistributeAreaFunc func, - gpointer user_data); #ifdef __cplusplus @@ -141,79 +124,33 @@ gimp_parallel_run_async_full (gint priority, template inline GimpAsync * -gimp_parallel_run_async_independent (ParallelRunAsyncFunc func) +gimp_parallel_run_async_independent_full (gint priority, + ParallelRunAsyncFunc func) { ParallelRunAsyncFunc *func_copy = g_new (ParallelRunAsyncFunc, 1); new (func_copy) ParallelRunAsyncFunc (func); - return gimp_parallel_run_async_independent ([] (GimpAsync *async, - gpointer user_data) - { - ParallelRunAsyncFunc *func_copy = - (ParallelRunAsyncFunc *) user_data; + return gimp_parallel_run_async_independent_full (priority, + [] (GimpAsync *async, + gpointer user_data) + { + ParallelRunAsyncFunc *func_copy = + (ParallelRunAsyncFunc *) user_data; - (*func_copy) (async); + (*func_copy) (async); - func_copy->~ParallelRunAsyncFunc (); - g_free (func_copy); - }, - func_copy); + func_copy->~ParallelRunAsyncFunc (); + g_free (func_copy); + }, + func_copy); } -template -inline void -gimp_parallel_distribute (gint max_n, - ParallelDistributeFunc func) +template +inline GimpAsync * +gimp_parallel_run_async_independent (ParallelRunAsyncFunc func) { - gimp_parallel_distribute (max_n, - [] (gint i, - gint n, - gpointer user_data) - { - ParallelDistributeFunc func_copy ( - *(const ParallelDistributeFunc *) user_data); - - func_copy (i, n); - }, - &func); -} - -template -inline void -gimp_parallel_distribute_range (gsize size, - gsize min_sub_size, - ParallelDistributeRangeFunc func) -{ - gimp_parallel_distribute_range (size, min_sub_size, - [] (gsize offset, - gsize size, - gpointer user_data) - { - ParallelDistributeRangeFunc func_copy ( - *(const ParallelDistributeRangeFunc *) user_data); - - func_copy (offset, size); - }, - &func); -} - -template -inline void -gimp_parallel_distribute_area (const GeglRectangle *area, - gsize min_sub_area, - ParallelDistributeAreaFunc func) -{ - gimp_parallel_distribute_area (area, min_sub_area, - [] (const GeglRectangle *area, - gpointer user_data) - { - ParallelDistributeAreaFunc func_copy ( - *(const ParallelDistributeAreaFunc *) user_data); - - func_copy (area); - }, - &func); + return gimp_parallel_run_async_independent_full (0, func); } } diff --git a/app/core/gimp-tags.c b/app/core/gimp-tags.c index 75d39e8607..5efedb9648 100644 --- a/app/core/gimp-tags.c +++ b/app/core/gimp-tags.c @@ -138,12 +138,24 @@ gimp_tags_user_install (void) else if (! g_output_stream_write_all (output, tags_installer.buf->str, tags_installer.buf->len, - NULL, NULL, &error) || - ! g_output_stream_close (output, NULL, &error)) + 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) diff --git a/app/core/gimp-transform-utils.c b/app/core/gimp-transform-utils.c index 00bcb86ac7..e183807339 100644 --- a/app/core/gimp-transform-utils.c +++ b/app/core/gimp-transform-utils.c @@ -441,8 +441,9 @@ gimp_transform_matrix_generic (GimpMatrix3 *matrix, { GimpMatrix3 trafo; gdouble coeff[8 * 9]; - gboolean negative; + 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); @@ -494,21 +495,26 @@ gimp_transform_matrix_generic (GimpMatrix3 *matrix, trafo.coeff[2][2]; if (fabs (w) <= EPSILON) - return FALSE; + result = FALSE; neg = (w < 0.0); - if (i == 0) - negative = neg; + if (negative < 0) + { + negative = neg; + } else if (neg != negative) - return FALSE; + { + 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) + if (negative > 0) { gint r; gint c; @@ -525,7 +531,7 @@ gimp_transform_matrix_generic (GimpMatrix3 *matrix, /* append the transformation to 'matrix' */ gimp_matrix3_mult (&trafo, matrix); - return TRUE; + return result; } gboolean diff --git a/app/core/gimp-user-install.c b/app/core/gimp-user-install.c index c519ff7e59..2b1c910cd1 100644 --- a/app/core/gimp-user-install.c +++ b/app/core/gimp-user-install.c @@ -88,7 +88,6 @@ static const struct } gimp_user_install_items[] = { - { "gtkrc", USER_INSTALL_COPY }, { "menurc", USER_INSTALL_COPY }, { "brushes", USER_INSTALL_MKDIR }, { "dynamics", USER_INSTALL_MKDIR }, diff --git a/app/core/gimp-utils.c b/app/core/gimp-utils.c index 8be6c3c5d8..4544f2fa61 100644 --- a/app/core/gimp-utils.c +++ b/app/core/gimp-utils.c @@ -858,6 +858,29 @@ gimp_ascii_strtod (const gchar *nptr, 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; +} + /* debug stuff */ diff --git a/app/core/gimp-utils.h b/app/core/gimp-utils.h index 956216e82b..20b8b8afa2 100644 --- a/app/core/gimp-utils.h +++ b/app/core/gimp-utils.h @@ -98,6 +98,9 @@ gboolean gimp_ascii_strtod (const gchar *nptr, gchar **endptr, gdouble *result); +gint gimp_g_list_compare (GList *list1, + GList *list2); + GimpImage * gimp_create_image_from_buffer (Gimp *gimp, GeglBuffer *buffer, const gchar *image_name); diff --git a/app/core/gimp.c b/app/core/gimp.c index 4a5c0e18d2..6192f6fea5 100644 --- a/app/core/gimp.c +++ b/app/core/gimp.c @@ -41,6 +41,7 @@ #include "paint/gimp-paint.h" #include "xcf/xcf.h" +#include "file-data/file-data.h" #include "gimp.h" #include "gimp-contexts.h" @@ -282,6 +283,7 @@ gimp_constructed (GObject *object) gimp->pdb = gimp_pdb_new (gimp); xcf_init (gimp); + file_data_init (gimp); /* create user and default context */ gimp_contexts_init (gimp); @@ -383,6 +385,7 @@ gimp_finalize (GObject *object) g_clear_object (&gimp->tool_info_list); } + file_data_exit (gimp); xcf_exit (gimp); g_clear_object (&gimp->pdb); @@ -978,7 +981,7 @@ gimp_create_image (Gimp *gimp, GIMP_PARASITE_PERSISTENT, strlen (comment) + 1, comment); - gimp_image_parasite_attach (image, parasite); + gimp_image_parasite_attach (image, parasite, FALSE); gimp_parasite_free (parasite); } } diff --git a/app/core/gimpasync.c b/app/core/gimpasync.c index 59a81ca99c..322d57d317 100644 --- a/app/core/gimpasync.c +++ b/app/core/gimpasync.c @@ -27,6 +27,7 @@ #include "gimpasync.h" #include "gimpcancelable.h" +#include "gimpmarshal.h" #include "gimpwaitable.h" @@ -60,13 +61,22 @@ /* #define TIME_ASYNC_OPS */ +enum +{ + WAITING, + LAST_SIGNAL +}; + + typedef struct _GimpAsyncCallbackInfo GimpAsyncCallbackInfo; struct _GimpAsyncCallbackInfo { - GimpAsyncCallback callback; - gpointer data; + GimpAsync *async; + GimpAsyncCallback callback; + gpointer data; + gpointer gobject; }; struct _GimpAsyncPrivate @@ -94,23 +104,26 @@ struct _GimpAsyncPrivate /* local function prototypes */ -static void gimp_async_waitable_iface_init (GimpWaitableInterface *iface); +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_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_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 void gimp_async_cancel (GimpCancelable *cancelable); -static gboolean gimp_async_idle (GimpAsync *async); +static gboolean gimp_async_idle (GimpAsync *async); -static void gimp_async_stop (GimpAsync *async); -static void gimp_async_run_callbacks (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, @@ -122,6 +135,8 @@ G_DEFINE_TYPE_WITH_CODE (GimpAsync, gimp_async, G_TYPE_OBJECT, #define parent_class gimp_async_parent_class +static guint async_signals[LAST_SIGNAL] = { 0 }; + /* local variables */ @@ -136,6 +151,15 @@ 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; } @@ -209,8 +233,13 @@ gimp_async_wait (GimpWaitable *waitable) g_mutex_lock (&async->priv->mutex); - while (! async->priv->stopped) - g_cond_wait (&async->priv->cond, &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); @@ -259,14 +288,19 @@ gimp_async_wait_until (GimpWaitable *waitable, g_mutex_lock (&async->priv->mutex); - while (! async->priv->stopped) + if (! async->priv->stopped) { - if (! g_cond_wait_until (&async->priv->cond, &async->priv->mutex, - end_time)) - { - g_mutex_unlock (&async->priv->mutex); + g_signal_emit (async, async_signals[WAITING], 0); - return FALSE; + 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; + } } } @@ -301,6 +335,33 @@ gimp_async_idle (GimpAsync *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) { @@ -348,8 +409,20 @@ gimp_async_run_callbacks (GimpAsync *async) 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); } @@ -418,7 +491,8 @@ gimp_async_add_callback (GimpAsync *async, return; } - callback_info = g_slice_new (GimpAsyncCallbackInfo); + callback_info = g_slice_new0 (GimpAsyncCallbackInfo); + callback_info->async = async; callback_info->callback = callback; callback_info->data = data; @@ -427,6 +501,57 @@ gimp_async_add_callback (GimpAsync *async, 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. @@ -438,7 +563,8 @@ gimp_async_remove_callback (GimpAsync *async, GimpAsyncCallback callback, gpointer data) { - GList *iter; + GList *iter; + gboolean unref_async = FALSE; g_return_if_fail (GIMP_IS_ASYNC (async)); g_return_if_fail (callback != NULL); @@ -455,6 +581,14 @@ gimp_async_remove_callback (GimpAsync *async, 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); @@ -463,7 +597,18 @@ gimp_async_remove_callback (GimpAsync *async, 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. diff --git a/app/core/gimpasync.h b/app/core/gimpasync.h index 493f2c9c82..0c2a671c94 100644 --- a/app/core/gimpasync.h +++ b/app/core/gimpasync.h @@ -47,42 +47,49 @@ struct _GimpAsync struct _GimpAsyncClass { GObjectClass parent_class; + + /* signals */ + void (* waiting) (GimpAsync *async); }; -GType gimp_async_get_type (void) G_GNUC_CONST; +GType gimp_async_get_type (void) G_GNUC_CONST; -GimpAsync * gimp_async_new (void); +GimpAsync * gimp_async_new (void); -gboolean gimp_async_is_synced (GimpAsync *async); +gboolean gimp_async_is_synced (GimpAsync *async); -void gimp_async_add_callback (GimpAsync *async, - GimpAsyncCallback callback, - gpointer data); -void gimp_async_remove_callback (GimpAsync *async, - GimpAsyncCallback callback, - gpointer data); +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); +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_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); +void gimp_async_abort (GimpAsync *async); -gboolean gimp_async_is_canceled (GimpAsync *async); +gboolean gimp_async_is_canceled (GimpAsync *async); -void gimp_async_cancel_and_wait (GimpAsync *async); +void gimp_async_cancel_and_wait (GimpAsync *async); /* stats */ -gint gimp_async_get_n_running (void); +gint gimp_async_get_n_running (void); #endif /* __GIMP_ASYNC_H__ */ diff --git a/app/core/gimpbacktrace-linux.c b/app/core/gimpbacktrace-linux.c index 3d3c6afc0d..a8593f8a10 100644 --- a/app/core/gimpbacktrace-linux.c +++ b/app/core/gimpbacktrace-linux.c @@ -83,7 +83,6 @@ struct _GimpBacktrace { GimpBacktraceThread *threads; gint n_threads; - gint n_remaining_threads; }; @@ -113,13 +112,14 @@ 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 *blacklisted_thread_names[] = +static const gchar * const blacklisted_thread_names[] = { "gmain", "threaded-ml" @@ -282,7 +282,7 @@ gimp_backtrace_signal_handler (gint signum) thread->n_frames = backtrace ((gpointer *) thread->frames, MAX_N_FRAMES); - g_atomic_int_dec_and_test (&curr_backtrace->n_remaining_threads); + g_atomic_int_dec_and_test (&handler_n_remaining_threads); break; } @@ -412,13 +412,13 @@ gimp_backtrace_new (gboolean include_current_thread) backtrace = g_slice_new (GimpBacktrace); - backtrace->threads = g_new (GimpBacktraceThread, n_threads); - backtrace->n_threads = n_threads; - backtrace->n_remaining_threads = n_threads; + 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_pointer_set (&handler_backtrace, backtrace); + g_atomic_int_set (&handler_n_remaining_threads, n_threads); g_atomic_int_set (&handler_lock, 0); @@ -441,7 +441,7 @@ gimp_backtrace_new (gboolean include_current_thread) start_time = g_get_monotonic_time (); - while (g_atomic_int_get (&backtrace->n_remaining_threads) > 0) + while (g_atomic_int_get (&handler_n_remaining_threads) > 0) { gint64 time = g_get_monotonic_time (); @@ -458,7 +458,7 @@ gimp_backtrace_new (gboolean include_current_thread) g_atomic_int_set (&handler_lock, 0); #if 0 - if (backtrace->n_remaining_threads > 0) + if (handler_n_remaining_threads > 0) { gint j = 0; @@ -500,7 +500,7 @@ gimp_backtrace_new (gboolean include_current_thread) void gimp_backtrace_free (GimpBacktrace *backtrace) { - if (! backtrace || backtrace->n_remaining_threads > 0) + if (! backtrace) return; g_free (backtrace->threads); diff --git a/app/core/gimpbrush-boundary.c b/app/core/gimpbrush-boundary.c index cdc40837b6..9e5cbaaeda 100644 --- a/app/core/gimpbrush-boundary.c +++ b/app/core/gimpbrush-boundary.c @@ -40,7 +40,7 @@ gimp_brush_transform_boundary_exact (GimpBrush *brush, { const GimpTempBuf *mask; - mask = gimp_brush_transform_mask (brush, NULL, + mask = gimp_brush_transform_mask (brush, scale, aspect_ratio, angle, reflect, hardness); diff --git a/app/core/gimpbrush-header.h b/app/core/gimpbrush-header.h index 1633e73ac4..0e66715d07 100644 --- a/app/core/gimpbrush-header.h +++ b/app/core/gimpbrush-header.h @@ -19,11 +19,10 @@ #define __GIMP_BRUSH_HEADER_H__ -#define GIMP_BRUSH_FILE_VERSION 2 -#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 */ +#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 */ @@ -36,14 +35,15 @@ struct _GimpBrushHeader guint32 version; /* brush file version # */ guint32 width; /* width of brush */ guint32 height; /* height of brush */ - guint32 bytes; /* depth of brush in bytes--always 1 */ + 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... +/* 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__ */ +#endif /* __GIMP_BRUSH_HEADER_H__ */ diff --git a/app/core/gimpbrush-load.c b/app/core/gimpbrush-load.c index b9db223c5f..63a453ebc9 100644 --- a/app/core/gimpbrush-load.c +++ b/app/core/gimpbrush-load.c @@ -30,6 +30,7 @@ #include "gimpbrush-header.h" #include "gimpbrush-load.h" #include "gimpbrush-private.h" +#include "gimppattern-header.h" #include "gimptempbuf.h" #include "gimp-intl.h" @@ -138,7 +139,6 @@ gimp_brush_load_brush (GimpContext *context, gsize bn_size; GimpBrushHeader header; gchar *name = NULL; - guchar *pixmap; guchar *mask; gsize bytes_read; gssize i, size; @@ -189,7 +189,8 @@ gimp_brush_load_brush (GimpContext *context, } if (header.width > GIMP_BRUSH_MAX_SIZE || - header.height > 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."), @@ -293,8 +294,69 @@ gimp_brush_load_brush (GimpContext *context, { case 1: success = (g_input_stream_read_all (input, mask, size, - &bytes_read, NULL, error) && + &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 */ @@ -338,26 +400,10 @@ gimp_brush_load_brush (GimpContext *context, } break; - case 3: - /* The obsolete .gbp format had a 3-byte pattern following a - * 1-byte brush, when embedded in a brush pipe, the current code - * tries to load that pattern as a brush, and encounters the '3' - * in the header. - */ - 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.\n" - "This might be an obsolete GIMP brush file, try " - "loading it as image and save it again."), - header.bytes); - return NULL; - break; - case 4: { - guchar buf[8 * 1024]; + guchar *pixmap; + guchar buf[8 * 1024]; brush->priv->pixmap = gimp_temp_buf_new (header.width, header.height, babl_format ("R'G'B' u8")); diff --git a/app/core/gimpbrush-save.c b/app/core/gimpbrush-save.c new file mode 100644 index 0000000000..38d4bcd92f --- /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 0000000000..f400cf07f3 --- /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 index 592331315e..bdc4b4c826 100644 --- a/app/core/gimpbrush-transform.cc +++ b/app/core/gimpbrush-transform.cc @@ -33,14 +33,13 @@ extern "C" #include "gegl/gimp-gegl-loops.h" -#include "gimp-parallel.h" #include "gimpbrush.h" #include "gimpbrush-transform.h" #include "gimptempbuf.h" -#define MIN_PARALLEL_SUB_SIZE 64 -#define MIN_PARALLEL_SUB_AREA (MIN_PARALLEL_SUB_SIZE * MIN_PARALLEL_SUB_SIZE) +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) /* local function prototypes */ @@ -256,9 +255,9 @@ gimp_brush_real_transform_mask (GimpBrush *brush, src_walk_vx_i = (gint) ((src_tl_to_bl_delta_x / dest_height) * int_multiple); src_walk_vy_i = (gint) ((src_tl_to_bl_delta_y / dest_height) * int_multiple); - gimp_parallel_distribute_area (GEGL_RECTANGLE (0, 0, dest_width, dest_height), - MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *area) + 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; @@ -556,9 +555,9 @@ gimp_brush_real_transform_pixmap (GimpBrush *brush, src_walk_vx_i = (gint) ((src_tl_to_bl_delta_x / dest_height) * int_multiple); src_walk_vy_i = (gint) ((src_tl_to_bl_delta_y / dest_height) * int_multiple); - gimp_parallel_distribute_area (GEGL_RECTANGLE (0, 0, dest_width, dest_height), - MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *area) + 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; @@ -808,8 +807,9 @@ gimp_brush_transform_blur (GimpTempBuf *buf, sums = g_new (Sums, width * height * components); - gimp_parallel_distribute_range (height, MIN_PARALLEL_SUB_SIZE, - [=] (gint y0, gint height) + gegl_parallel_distribute_range ( + height, PIXELS_PER_THREAD / width, + [=] (gint y0, gint height) { gint x; gint y; @@ -885,8 +885,9 @@ gimp_brush_transform_blur (GimpTempBuf *buf, } }); - gimp_parallel_distribute_range (width, MIN_PARALLEL_SUB_SIZE, - [=] (gint x0, gint width) + gegl_parallel_distribute_range ( + width, PIXELS_PER_THREAD / height, + [=] (gint x0, gint width) { gint x; gint y; diff --git a/app/core/gimpbrush.c b/app/core/gimpbrush.c index b4791b70c4..ddb1e2be59 100644 --- a/app/core/gimpbrush.c +++ b/app/core/gimpbrush.c @@ -31,6 +31,7 @@ #include "gimpbrush-boundary.h" #include "gimpbrush-load.h" #include "gimpbrush-private.h" +#include "gimpbrush-save.h" #include "gimpbrush-transform.h" #include "gimpbrushcache.h" #include "gimpbrushgenerated.h" @@ -82,6 +83,8 @@ static gchar * gimp_brush_get_description (GimpViewable *vie 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); @@ -134,7 +137,9 @@ gimp_brush_class_init (GimpBrushClass *klass) 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; @@ -270,6 +275,7 @@ gimp_brush_get_new_preview (GimpViewable *viewable, GimpTempBuf *return_buf = NULL; gint mask_width; gint mask_height; + guchar *mask_data; guchar *mask; guchar *buf; gint x, y; @@ -292,14 +298,14 @@ gimp_brush_get_new_preview (GimpViewable *viewable, { GimpBrushGenerated *gen_brush = GIMP_BRUSH_GENERATED (brush); - mask_buf = gimp_brush_transform_mask (brush, NULL, scale, + 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, NULL, scale, + mask_buf = gimp_brush_transform_mask (brush, scale, 0.0, 0.0, FALSE, 1.0); if (! mask_buf) @@ -313,7 +319,7 @@ gimp_brush_get_new_preview (GimpViewable *viewable, } if (pixmap_buf) - pixmap_buf = gimp_brush_transform_pixmap (brush, NULL, scale, + pixmap_buf = gimp_brush_transform_pixmap (brush, scale, 0.0, 0.0, FALSE, 1.0); mask_width = gimp_temp_buf_get_width (mask_buf); @@ -325,14 +331,19 @@ gimp_brush_get_new_preview (GimpViewable *viewable, return_buf = gimp_temp_buf_new (mask_width, mask_height, babl_format ("R'G'B'A u8")); - gimp_temp_buf_data_clear (return_buf); - mask = gimp_temp_buf_get_data (mask_buf); + 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 = gimp_temp_buf_get_data (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++) { @@ -344,6 +355,8 @@ gimp_brush_get_new_preview (GimpViewable *viewable, *buf++ = *mask++; } } + + gimp_temp_buf_unlock (pixmap_buf, pixmap_data); } else { @@ -359,6 +372,8 @@ gimp_brush_get_new_preview (GimpViewable *viewable, } } + gimp_temp_buf_unlock (mask_buf, mask_data); + if (scaled) { gimp_temp_buf_unref ((GimpTempBuf *) mask_buf); @@ -407,6 +422,28 @@ 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) { @@ -596,7 +633,6 @@ gimp_brush_transform_size (GimpBrush *brush, const GimpTempBuf * gimp_brush_transform_mask (GimpBrush *brush, - GeglNode *op, gdouble scale, gdouble aspect_ratio, gdouble angle, @@ -616,7 +652,7 @@ gimp_brush_transform_mask (GimpBrush *brush, &width, &height); mask = gimp_brush_cache_get (brush->priv->mask_cache, - op, width, height, + width, height, scale, aspect_ratio, angle, reflect, hardness); if (! mask) @@ -653,37 +689,9 @@ gimp_brush_transform_mask (GimpBrush *brush, reflect, effective_hardness); - if (op) - { - GeglNode *graph, *source, *target; - GeglBuffer *buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) mask); - - graph = gegl_node_new (); - source = gegl_node_new_child (graph, - "operation", "gegl:buffer-source", - "buffer", buffer, - NULL); - gegl_node_add_child (graph, op); - target = gegl_node_new_child (graph, - "operation", "gegl:write-buffer", - "buffer", buffer, - NULL); - - gegl_node_link_many (source, op, target, NULL); - gegl_node_blit (target, 1.0, - GEGL_RECTANGLE (0, 0, - gegl_buffer_get_width (buffer), - gegl_buffer_get_height (buffer)), - NULL, NULL, 0, GEGL_BLIT_DEFAULT); - - - g_object_unref (graph); - g_object_unref (buffer); - } - gimp_brush_cache_add (brush->priv->mask_cache, (gpointer) mask, - op, width, height, + width, height, scale, aspect_ratio, angle, reflect, effective_hardness); } @@ -692,7 +700,6 @@ gimp_brush_transform_mask (GimpBrush *brush, const GimpTempBuf * gimp_brush_transform_pixmap (GimpBrush *brush, - GeglNode *op, gdouble scale, gdouble aspect_ratio, gdouble angle, @@ -713,7 +720,7 @@ gimp_brush_transform_pixmap (GimpBrush *brush, &width, &height); pixmap = gimp_brush_cache_get (brush->priv->pixmap_cache, - op, width, height, + width, height, scale, aspect_ratio, angle, reflect, hardness); if (! pixmap) @@ -745,36 +752,9 @@ gimp_brush_transform_pixmap (GimpBrush *brush, reflect, effective_hardness); - if (op) - { - GeglNode *graph, *source, *target; - GeglBuffer *buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) pixmap); - - graph = gegl_node_new (); - source = gegl_node_new_child (graph, - "operation", "gegl:buffer-source", - "buffer", buffer, - NULL); - gegl_node_add_child (graph, op); - target = gegl_node_new_child (graph, - "operation", "gegl:write-buffer", - "buffer", buffer, - NULL); - - gegl_node_link_many (source, op, target, NULL); - gegl_node_blit (target, 1.0, - GEGL_RECTANGLE (0, 0, - gegl_buffer_get_width (buffer), - gegl_buffer_get_height (buffer)), - NULL, NULL, 0, GEGL_BLIT_DEFAULT); - - g_object_unref (graph); - g_object_unref (buffer); - } - gimp_brush_cache_add (brush->priv->pixmap_cache, (gpointer) pixmap, - op, width, height, + width, height, scale, aspect_ratio, angle, reflect, effective_hardness); } @@ -803,7 +783,7 @@ gimp_brush_transform_boundary (GimpBrush *brush, width, height); boundary = gimp_brush_cache_get (brush->priv->boundary_cache, - NULL, *width, *height, + *width, *height, scale, aspect_ratio, angle, reflect, hardness); if (! boundary) @@ -826,7 +806,7 @@ gimp_brush_transform_boundary (GimpBrush *brush, if (boundary) gimp_brush_cache_add (brush->priv->boundary_cache, (gpointer) boundary, - NULL, *width, *height, + *width, *height, scale, aspect_ratio, angle, reflect, hardness); } diff --git a/app/core/gimpbrush.h b/app/core/gimpbrush.h index 017cb953e2..b5735f0dd2 100644 --- a/app/core/gimpbrush.h +++ b/app/core/gimpbrush.h @@ -113,14 +113,12 @@ void gimp_brush_transform_size (GimpBrush *brush, gint *width, gint *height); const GimpTempBuf * gimp_brush_transform_mask (GimpBrush *brush, - GeglNode *op, gdouble scale, gdouble aspect_ratio, gdouble angle, gboolean reflect, gdouble hardness); const GimpTempBuf * gimp_brush_transform_pixmap (GimpBrush *brush, - GeglNode *op, gdouble scale, gdouble aspect_ratio, gdouble angle, diff --git a/app/core/gimpbrushcache.c b/app/core/gimpbrushcache.c index 579ff825d9..07ad0082b4 100644 --- a/app/core/gimpbrushcache.c +++ b/app/core/gimpbrushcache.c @@ -44,16 +44,15 @@ typedef struct _GimpBrushCacheUnit GimpBrushCacheUnit; struct _GimpBrushCacheUnit { - gpointer data; + gpointer data; - gint width; - gint height; - gdouble scale; - gdouble aspect_ratio; - gdouble angle; - gboolean reflect; - gdouble hardness; - GeglNode *op; + gint width; + gint height; + gdouble scale; + gdouble aspect_ratio; + gdouble angle; + gboolean reflect; + gdouble hardness; }; @@ -201,7 +200,6 @@ gimp_brush_cache_clear (GimpBrushCache *cache) gconstpointer gimp_brush_cache_get (GimpBrushCache *cache, - GeglNode *op, gint width, gint height, gdouble scale, @@ -225,8 +223,7 @@ gimp_brush_cache_get (GimpBrushCache *cache, unit->aspect_ratio == aspect_ratio && unit->angle == angle && unit->reflect == reflect && - unit->hardness == hardness && - unit->op == op) + unit->hardness == hardness) { if (gimp_log_flags & GIMP_LOG_BRUSH_CACHE) g_printerr ("%c", cache->debug_hit); @@ -251,7 +248,6 @@ gimp_brush_cache_get (GimpBrushCache *cache, void gimp_brush_cache_add (GimpBrushCache *cache, gpointer data, - GeglNode *op, gint width, gint height, gdouble scale, @@ -298,7 +294,6 @@ gimp_brush_cache_add (GimpBrushCache *cache, unit->angle = angle; unit->reflect = reflect; unit->hardness = hardness; - unit->op = op; cache->cached_units = g_list_prepend (cache->cached_units, unit); } diff --git a/app/core/gimpbrushcache.h b/app/core/gimpbrushcache.h index c1b888a115..77221170b4 100644 --- a/app/core/gimpbrushcache.h +++ b/app/core/gimpbrushcache.h @@ -62,7 +62,6 @@ GimpBrushCache * gimp_brush_cache_new (GDestroyNotify data_destory, void gimp_brush_cache_clear (GimpBrushCache *cache); gconstpointer gimp_brush_cache_get (GimpBrushCache *cache, - GeglNode *op, gint width, gint height, gdouble scale, @@ -72,7 +71,6 @@ gconstpointer gimp_brush_cache_get (GimpBrushCache *cache, gdouble hardness); void gimp_brush_cache_add (GimpBrushCache *cache, gpointer data, - GeglNode *op, gint width, gint height, gdouble scale, diff --git a/app/core/gimpbrushclipboard.c b/app/core/gimpbrushclipboard.c index e2acc13e4c..2d4a5e1e0b 100644 --- a/app/core/gimpbrushclipboard.c +++ b/app/core/gimpbrushclipboard.c @@ -57,9 +57,8 @@ static void gimp_brush_clipboard_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); -#if 0 + static GimpData * gimp_brush_clipboard_duplicate (GimpData *data); -#endif static void gimp_brush_clipboard_changed (Gimp *gimp, GimpBrush *brush); @@ -74,17 +73,13 @@ static void gimp_brush_clipboard_class_init (GimpBrushClipboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); -#if 0 GimpDataClass *data_class = GIMP_DATA_CLASS (klass); -#endif 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; -#if 0 data_class->duplicate = gimp_brush_clipboard_duplicate; -#endif g_object_class_install_property (object_class, PROP_GIMP, g_param_spec_object ("gimp", NULL, NULL, @@ -168,15 +163,15 @@ gimp_brush_clipboard_get_property (GObject *object, } } -#if 0 static GimpData * gimp_brush_clipboard_duplicate (GimpData *data) { - GimpBrushClipboard *brush = GIMP_BRUSH_CLIPBOARD (data); + GimpData *new = g_object_new (GIMP_TYPE_BRUSH, NULL); - return gimp_brush_clipboard_new (brush->gimp); + gimp_data_copy (new, data); + + return new; } -#endif GimpData * gimp_brush_clipboard_new (Gimp *gimp, diff --git a/app/core/gimpbrushgenerated.c b/app/core/gimpbrushgenerated.c index 0284bddef6..dacdaa8683 100644 --- a/app/core/gimpbrushgenerated.c +++ b/app/core/gimpbrushgenerated.c @@ -405,11 +405,11 @@ gauss (gdouble f) } /* set up lookup table */ -static guchar * +static gfloat * gimp_brush_generated_calc_lut (gdouble radius, gdouble hardness) { - guchar *lookup; + gfloat *lookup; gint length; gint x; gdouble d; @@ -419,7 +419,7 @@ gimp_brush_generated_calc_lut (gdouble radius, length = OVERSAMPLING * ceil (1 + sqrt (2 * SQR (ceil (radius + 1.0)))); - lookup = g_malloc (length); + lookup = gegl_scratch_new (gfloat, length); sum = 0.0; if ((1.0 - hardness) < 0.0000004) @@ -449,12 +449,12 @@ gimp_brush_generated_calc_lut (gdouble radius, buffer[x % OVERSAMPLING] = gauss (pow (d / radius, exponent)); sum += buffer[x % OVERSAMPLING]; - lookup[x++] = RINT (sum * (255.0 / OVERSAMPLING)); + lookup[x++] = sum / OVERSAMPLING; } while (x < length) { - lookup[x++] = 0; + lookup[x++] = 0.0f; } return lookup; @@ -472,9 +472,9 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush, GimpVector2 *xaxis, GimpVector2 *yaxis) { - guchar *centerp; - guchar *lookup; - guchar a; + gfloat *centerp; + gfloat *lookup; + gfloat a; gint x, y; gdouble c, s, cs, ss; GimpVector2 x_axis; @@ -497,12 +497,12 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush, &s, &c, &x_axis, &y_axis); mask = gimp_temp_buf_new (width, height, - babl_format ("Y u8")); + babl_format ("Y float")); half_width = width / 2; half_height = height / 2; - centerp = gimp_temp_buf_get_data (mask) + + centerp = (gfloat *) gimp_temp_buf_get_data (mask) + half_height * width + half_width; lookup = gimp_brush_generated_calc_lut (radius, hardness); @@ -553,7 +553,7 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush, if (d < radius + 1) a = lookup[(gint) RINT (d * OVERSAMPLING)]; else - a = 0; + a = 0.0f; centerp[y * width + x] = a; @@ -562,7 +562,7 @@ gimp_brush_generated_calc (GimpBrushGenerated *brush, } } - g_free (lookup); + gegl_scratch_free (lookup); if (xaxis) *xaxis = x_axis; diff --git a/app/core/gimpbrushpipe-load.c b/app/core/gimpbrushpipe-load.c index 95c6a19d7b..18ad8989d0 100644 --- a/app/core/gimpbrushpipe-load.c +++ b/app/core/gimpbrushpipe-load.c @@ -21,10 +21,8 @@ #include #include -#include #include "libgimpbase/gimpbase.h" -#include "libgimpbase/gimpparasiteio.h" #include "core-types.h" @@ -42,14 +40,12 @@ gimp_brush_pipe_load (GimpContext *context, GInputStream *input, GError **error) { - GimpBrushPipe *pipe = NULL; - gint i; - gint num_of_brushes = 0; - gint totalcells; - gchar *paramstring; - GString *buffer; - gchar c; - gsize bytes_read; + 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); @@ -105,10 +101,10 @@ gimp_brush_pipe_load (GimpContext *context, if (buffer->len > 0 && buffer->len < 1024) { - num_of_brushes = strtol (buffer->str, ¶mstring, 10); + n_brushes = strtol (buffer->str, ¶mstring, 10); } - if (num_of_brushes < 1) + if (n_brushes < 1) { g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, _("Fatal parse error in brush file '%s': " @@ -122,91 +118,37 @@ gimp_brush_pipe_load (GimpContext *context, while (*paramstring && g_ascii_isspace (*paramstring)) paramstring++; - if (*paramstring) - { - GimpPixPipeParams params; + pipe->brushes = g_new0 (GimpBrush *, n_brushes); - 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); - } - else - { - pipe->dimension = 1; - pipe->rank = g_new (gint, 1); - pipe->rank[0] = num_of_brushes; - pipe->select = g_new (PipeSelectModes, 1); - pipe->select[0] = PIPE_SELECT_INCREMENTAL; - pipe->index = g_new (gint, 1); - pipe->index[0] = 0; - } - - g_string_free (buffer, TRUE); - - 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]; - } - g_return_val_if_fail (pipe->stride[pipe->dimension-1] == 1, NULL); - - pipe->brushes = g_new0 (GimpBrush *, num_of_brushes); - - while (pipe->n_brushes < num_of_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]) - { - gimp_object_set_name (GIMP_OBJECT (pipe->brushes[pipe->n_brushes]), - NULL); - } - else + 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]; diff --git a/app/core/gimpbrushpipe-save.c b/app/core/gimpbrushpipe-save.c new file mode 100644 index 0000000000..1b8e7fb165 --- /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 0000000000..df768555aa --- /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 index d61da56bfe..cca68adf93 100644 --- a/app/core/gimpbrushpipe.c +++ b/app/core/gimpbrushpipe.c @@ -21,6 +21,7 @@ #include #include +#include "libgimpbase/gimpparasiteio.h" #include "libgimpmath/gimpmath.h" #include "core-types.h" @@ -28,28 +29,34 @@ #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 void gimp_brush_pipe_finalize (GObject *object); -static gint64 gimp_brush_pipe_get_memsize (GimpObject *object, - gint64 *gui_size); +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 gboolean gimp_brush_pipe_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); -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); +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); @@ -63,6 +70,7 @@ 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; @@ -71,6 +79,10 @@ gimp_brush_pipe_class_init (GimpBrushPipeClass *klass) 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; @@ -95,16 +107,11 @@ gimp_brush_pipe_finalize (GObject *object) { GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (object); - if (pipe->rank) - { - g_free (pipe->rank); - pipe->rank = NULL; - } - if (pipe->stride) - { - g_free (pipe->stride); - pipe->stride = NULL; - } + 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) { @@ -114,19 +121,7 @@ gimp_brush_pipe_finalize (GObject *object) if (pipe->brushes[i]) g_object_unref (pipe->brushes[i]); - g_free (pipe->brushes); - pipe->brushes = NULL; - } - - if (pipe->select) - { - g_free (pipe->select); - pipe->select = NULL; - } - if (pipe->index) - { - g_free (pipe->index); - pipe->index = NULL; + g_clear_pointer (&pipe->brushes, g_free); } GIMP_BRUSH (pipe)->priv->mask = NULL; @@ -166,6 +161,69 @@ gimp_brush_pipe_get_popup_size (GimpViewable *viewable, 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) { @@ -275,3 +333,88 @@ gimp_brush_pipe_want_null_motion (GimpBrush *brush, 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 index 4f18518af9..9615210392 100644 --- a/app/core/gimpbrushpipe.h +++ b/app/core/gimpbrushpipe.h @@ -61,6 +61,8 @@ struct _GimpBrushPipe * ranks in some odd special case */ GimpBrush **brushes; GimpBrush *current; /* Currently selected brush */ + + gchar *params; /* For pipe <-> image conversion */ }; struct _GimpBrushPipeClass @@ -69,7 +71,10 @@ struct _GimpBrushPipeClass }; -GType gimp_brush_pipe_get_type (void) G_GNUC_CONST; +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/gimpchannel-combine.c b/app/core/gimpchannel-combine.c index 5277bff625..93bc85b610 100644 --- a/app/core/gimpchannel-combine.c +++ b/app/core/gimpchannel-combine.c @@ -28,11 +28,338 @@ #include "core-types.h" #include "gegl/gimp-gegl-mask-combine.h" +#include "gegl/gimp-gegl-utils.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; + + gimp_gegl_rectangle_align_to_tile_grid (&area, &area, buffer); + } + + 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, @@ -41,72 +368,21 @@ gimp_channel_combine_rect (GimpChannel *mask, gint w, gint h) { - GeglBuffer *buffer; + GimpChannelCombineData data; g_return_if_fail (GIMP_IS_CHANNEL (mask)); - buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); - - if (! gimp_gegl_mask_combine_rect (buffer, op, x, y, w, h)) - return; - - gimp_rectangle_intersect (x, y, w, h, - 0, 0, - gimp_item_get_width (GIMP_ITEM (mask)), - gimp_item_get_height (GIMP_ITEM (mask)), - &x, &y, &w, &h); - - /* Determine new boundary */ - if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty) + if (gimp_channel_combine_start (mask, op, GEGL_RECTANGLE (x, y, w, h), + TRUE, TRUE, &data)) { - if (x < mask->x1) - mask->x1 = x; - if (y < mask->y1) - mask->y1 = y; - if ((x + w) > mask->x2) - mask->x2 = (x + w); - if ((y + h) > mask->y2) - mask->y2 = (y + h); - } - else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty) - { - mask->empty = FALSE; - mask->x1 = x; - mask->y1 = y; - mask->x2 = x + w; - mask->y2 = y + h; - } - else - { - mask->bounds_known = FALSE; + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + + gimp_gegl_mask_combine_rect (buffer, op, x, y, w, h); } - mask->x1 = CLAMP (mask->x1, 0, gimp_item_get_width (GIMP_ITEM (mask))); - mask->y1 = CLAMP (mask->y1, 0, gimp_item_get_height (GIMP_ITEM (mask))); - mask->x2 = CLAMP (mask->x2, 0, gimp_item_get_width (GIMP_ITEM (mask))); - mask->y2 = CLAMP (mask->y2, 0, gimp_item_get_height (GIMP_ITEM (mask))); - - gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); + gimp_channel_combine_end (mask, &data); } -/** - * gimp_channel_combine_ellipse: - * @mask: the channel with which to combine the ellipse - * @op: whether to replace, add to, or subtract from the current - * contents - * @x: x coordinate of upper left corner of ellipse - * @y: y coordinate of upper left corner of ellipse - * @w: width of ellipse bounding box - * @h: height of ellipse bounding box - * @antialias: if %TRUE, antialias the ellipse - * - * Mainly used for elliptical selections. If @op is - * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels - * within the ellipse to 255. If @op is %GIMP_CHANNEL_OP_SUBTRACT, - * sets pixels within to zero. If @antialias is %TRUE, pixels that - * impinge on the edge of the ellipse are set to intermediate values, - * depending on how much they overlap. - **/ void gimp_channel_combine_ellipse (GimpChannel *mask, GimpChannelOps op, @@ -120,26 +396,6 @@ gimp_channel_combine_ellipse (GimpChannel *mask, w / 2.0, h / 2.0, antialias); } -/** - * gimp_channel_combine_ellipse_rect: - * @mask: the channel with which to combine the elliptic rect - * @op: whether to replace, add to, or subtract from the current - * contents - * @x: x coordinate of upper left corner of bounding rect - * @y: y coordinate of upper left corner of bounding rect - * @w: width of bounding rect - * @h: height of bounding rect - * @a: elliptic a-constant applied to corners - * @b: elliptic b-constant applied to corners - * @antialias: if %TRUE, antialias the elliptic corners - * - * Used for rounded cornered rectangles and ellipses. If @op is - * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels - * within the ellipse to 255. If @op is %GIMP_CHANNEL_OP_SUBTRACT, - * sets pixels within to zero. If @antialias is %TRUE, pixels that - * impinge on the edge of the ellipse are set to intermediate values, - * depending on how much they overlap. - **/ void gimp_channel_combine_ellipse_rect (GimpChannel *mask, GimpChannelOps op, @@ -147,51 +403,24 @@ gimp_channel_combine_ellipse_rect (GimpChannel *mask, gint y, gint w, gint h, - gdouble a, - gdouble b, + gdouble rx, + gdouble ry, gboolean antialias) { - GeglBuffer *buffer; + GimpChannelCombineData data; g_return_if_fail (GIMP_IS_CHANNEL (mask)); - g_return_if_fail (a >= 0.0 && b >= 0.0); - g_return_if_fail (op != GIMP_CHANNEL_OP_INTERSECT); - buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); - - if (! gimp_gegl_mask_combine_ellipse_rect (buffer, op, x, y, w, h, - a, b, antialias)) - return; - - gimp_rectangle_intersect (x, y, w, h, - 0, 0, - gimp_item_get_width (GIMP_ITEM (mask)), - gimp_item_get_height (GIMP_ITEM (mask)), - &x, &y, &w, &h); - - /* determine new boundary */ - if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty) + if (gimp_channel_combine_start (mask, op, GEGL_RECTANGLE (x, y, w, h), + TRUE, FALSE, &data)) { - if (x < mask->x1) mask->x1 = x; - if (y < mask->y1) mask->y1 = y; + GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); - if ((x + w) > mask->x2) mask->x2 = (x + w); - if ((y + h) > mask->y2) mask->y2 = (y + h); - } - else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty) - { - mask->empty = FALSE; - mask->x1 = x; - mask->y1 = y; - mask->x2 = x + w; - mask->y2 = y + h; - } - else - { - mask->bounds_known = FALSE; + gimp_gegl_mask_combine_ellipse_rect (buffer, op, x, y, w, h, + rx, ry, antialias); } - gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); + gimp_channel_combine_end (mask, &data); } void @@ -219,27 +448,24 @@ gimp_channel_combine_buffer (GimpChannel *mask, gint off_x, gint off_y) { - GeglBuffer *buffer; - gint x, y, w, h; + GimpChannelCombineData data; g_return_if_fail (GIMP_IS_CHANNEL (mask)); g_return_if_fail (GEGL_IS_BUFFER (add_on_buffer)); - buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); + if (gimp_channel_combine_start (mask, op, + GEGL_RECTANGLE ( + off_x, + off_y, + 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)); - if (! gimp_gegl_mask_combine_buffer (buffer, add_on_buffer, - op, off_x, off_y)) - return; + gimp_gegl_mask_combine_buffer (buffer, add_on_buffer, op, + off_x, off_y); + } - gimp_rectangle_intersect (off_x, off_y, - gegl_buffer_get_width (add_on_buffer), - gegl_buffer_get_height (add_on_buffer), - 0, 0, - gimp_item_get_width (GIMP_ITEM (mask)), - gimp_item_get_height (GIMP_ITEM (mask)), - &x, &y, &w, &h); - - mask->bounds_known = FALSE; - - gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); + gimp_channel_combine_end (mask, &data); } diff --git a/app/core/gimpchannel-combine.h b/app/core/gimpchannel-combine.h index 55648bf14b..de1f125c5c 100644 --- a/app/core/gimpchannel-combine.h +++ b/app/core/gimpchannel-combine.h @@ -38,8 +38,8 @@ void gimp_channel_combine_ellipse_rect (GimpChannel *mask, gint y, gint w, gint h, - gdouble a, - gdouble b, + gdouble rx, + gdouble ry, gboolean antialias); void gimp_channel_combine_mask (GimpChannel *mask, GimpChannel *add_on, diff --git a/app/core/gimpchannel-select.c b/app/core/gimpchannel-select.c index e0cfc27354..370511d75a 100644 --- a/app/core/gimpchannel-select.c +++ b/app/core/gimpchannel-select.c @@ -65,14 +65,10 @@ gimp_channel_select_rectangle (GimpChannel *channel, if (push_undo) gimp_channel_push_undo (channel, C_("undo-type", "Rectangle Select")); - /* if applicable, replace the current selection */ - if (op == GIMP_CHANNEL_OP_REPLACE) - gimp_channel_clear (channel, NULL, FALSE); - /* if feathering for rect, make a new mask with the * rectangle and feather that with the old mask */ - if (feather || op == GIMP_CHANNEL_OP_INTERSECT) + if (feather) { GimpItem *item = GIMP_ITEM (channel); GeglBuffer *add_on; @@ -82,12 +78,11 @@ gimp_channel_select_rectangle (GimpChannel *channel, gimp_item_get_height (item)), babl_format ("Y float")); - gimp_gegl_mask_combine_rect (add_on, GIMP_CHANNEL_OP_ADD, x, y, w, h); + gimp_gegl_mask_combine_rect (add_on, GIMP_CHANNEL_OP_REPLACE, x, y, w, h); - if (feather) - gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, - feather_radius_x, - feather_radius_y); + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y); gimp_channel_combine_buffer (channel, add_on, op, 0, 0); g_object_unref (add_on); @@ -117,14 +112,10 @@ gimp_channel_select_ellipse (GimpChannel *channel, if (push_undo) gimp_channel_push_undo (channel, C_("undo-type", "Ellipse Select")); - /* if applicable, replace the current selection */ - if (op == GIMP_CHANNEL_OP_REPLACE) - gimp_channel_clear (channel, NULL, FALSE); - /* if feathering for rect, make a new mask with the * rectangle and feather that with the old mask */ - if (feather || op == GIMP_CHANNEL_OP_INTERSECT) + if (feather) { GimpItem *item = GIMP_ITEM (channel); GeglBuffer *add_on; @@ -134,13 +125,12 @@ gimp_channel_select_ellipse (GimpChannel *channel, gimp_item_get_height (item)), babl_format ("Y float")); - gimp_gegl_mask_combine_ellipse (add_on, GIMP_CHANNEL_OP_ADD, + gimp_gegl_mask_combine_ellipse (add_on, GIMP_CHANNEL_OP_REPLACE, x, y, w, h, antialias); - if (feather) - gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, - feather_radius_x, - feather_radius_y); + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y); gimp_channel_combine_buffer (channel, add_on, op, 0, 0); g_object_unref (add_on); @@ -172,14 +162,10 @@ gimp_channel_select_round_rect (GimpChannel *channel, if (push_undo) gimp_channel_push_undo (channel, C_("undo-type", "Rounded Rectangle Select")); - /* if applicable, replace the current selection */ - if (op == GIMP_CHANNEL_OP_REPLACE) - gimp_channel_clear (channel, NULL, FALSE); - /* if feathering for rect, make a new mask with the * rectangle and feather that with the old mask */ - if (feather || op == GIMP_CHANNEL_OP_INTERSECT) + if (feather) { GimpItem *item = GIMP_ITEM (channel); GeglBuffer *add_on; @@ -189,15 +175,14 @@ gimp_channel_select_round_rect (GimpChannel *channel, gimp_item_get_height (item)), babl_format ("Y float")); - gimp_gegl_mask_combine_ellipse_rect (add_on, GIMP_CHANNEL_OP_ADD, + gimp_gegl_mask_combine_ellipse_rect (add_on, GIMP_CHANNEL_OP_REPLACE, x, y, w, h, corner_radius_x, corner_radius_y, antialias); - if (feather) - gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, - feather_radius_x, - feather_radius_y); + gimp_gegl_apply_feather (add_on, NULL, NULL, add_on, NULL, + feather_radius_x, + feather_radius_y); gimp_channel_combine_buffer (channel, add_on, op, 0, 0); g_object_unref (add_on); @@ -236,10 +221,6 @@ gimp_channel_select_scan_convert (GimpChannel *channel, if (push_undo) gimp_channel_push_undo (channel, undo_desc); - /* if applicable, replace the current selection */ - if (op == GIMP_CHANNEL_OP_REPLACE) - gimp_channel_clear (channel, NULL, FALSE); - item = GIMP_ITEM (channel); add_on = gegl_buffer_new (GEGL_RECTANGLE (0, 0, @@ -346,11 +327,7 @@ gimp_channel_select_buffer (GimpChannel *channel, gimp_channel_push_undo (channel, undo_desc); - /* if applicable, replace the current selection */ - if (op == GIMP_CHANNEL_OP_REPLACE) - gimp_channel_clear (channel, NULL, FALSE); - - if (feather || op == GIMP_CHANNEL_OP_INTERSECT) + if (feather) { GimpItem *item = GIMP_ITEM (channel); GeglBuffer *add_on2; @@ -361,13 +338,12 @@ gimp_channel_select_buffer (GimpChannel *channel, babl_format ("Y float")); gimp_gegl_mask_combine_buffer (add_on2, add_on, - GIMP_CHANNEL_OP_ADD, + GIMP_CHANNEL_OP_REPLACE, offset_x, offset_y); - if (feather) - gimp_gegl_apply_feather (add_on2, NULL, NULL, add_on2, NULL, - feather_radius_x, - feather_radius_y); + gimp_gegl_apply_feather (add_on2, NULL, NULL, add_on2, NULL, + feather_radius_x, + feather_radius_y); gimp_channel_combine_buffer (channel, add_on2, op, 0, 0); g_object_unref (add_on2); diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c index 81a411907d..ba392754d2 100644 --- a/app/core/gimpchannel.c +++ b/app/core/gimpchannel.c @@ -36,6 +36,7 @@ #include "gegl/gimp-gegl-loops.h" #include "gegl/gimp-gegl-mask.h" #include "gegl/gimp-gegl-nodes.h" +#include "gegl/gimp-gegl-utils.h" #include "gimp.h" #include "gimp-utils.h" @@ -59,6 +60,8 @@ #include "gimp-intl.h" +#define RGBA_EPSILON 1e-6 + enum { COLOR_CHANGED, @@ -156,8 +159,6 @@ static void gimp_channel_convert_type (GimpDrawable *drawable, static void gimp_channel_invalidate_boundary (GimpDrawable *drawable); static void gimp_channel_get_active_components (GimpDrawable *drawable, gboolean *active); -static GimpComponentMask - gimp_channel_get_active_mask (GimpDrawable *drawable); static void gimp_channel_set_buffer (GimpDrawable *drawable, gboolean push_undo, @@ -290,7 +291,6 @@ gimp_channel_class_init (GimpChannelClass *klass) 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->get_active_mask = gimp_channel_get_active_mask; drawable_class->set_buffer = gimp_channel_set_buffer; klass->boundary = gimp_channel_real_boundary; @@ -348,17 +348,8 @@ gimp_channel_finalize (GObject *object) { GimpChannel *channel = GIMP_CHANNEL (object); - if (channel->segs_in) - { - g_free (channel->segs_in); - channel->segs_in = NULL; - } - - if (channel->segs_out) - { - g_free (channel->segs_out); - channel->segs_out = NULL; - } + g_clear_pointer (&channel->segs_in, g_free); + g_clear_pointer (&channel->segs_out, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -1006,13 +997,6 @@ gimp_channel_get_active_components (GimpDrawable *drawable, active[ALPHA_G] = FALSE; } -static GimpComponentMask -gimp_channel_get_active_mask (GimpDrawable *drawable) -{ - /* Return all, because that skips the component mask op when painting */ - return GIMP_COMPONENT_MASK_ALL; -} - static void gimp_channel_set_buffer (GimpDrawable *drawable, gboolean push_undo, @@ -1170,14 +1154,10 @@ gimp_channel_real_is_empty (GimpChannel *channel) return FALSE; /* The mask is empty, meaning we can set the bounds as known */ - if (channel->segs_in) - g_free (channel->segs_in); - if (channel->segs_out) - g_free (channel->segs_out); + g_clear_pointer (&channel->segs_in, g_free); + g_clear_pointer (&channel->segs_out, g_free); channel->empty = TRUE; - channel->segs_in = NULL; - channel->segs_out = NULL; channel->num_segs_in = 0; channel->num_segs_out = 0; channel->bounds_known = TRUE; @@ -1253,6 +1233,13 @@ 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) @@ -1261,19 +1248,27 @@ gimp_channel_real_clear (GimpChannel *channel, gimp_channel_push_undo (channel, undo_desc); } - if (channel->bounds_known && ! channel->empty) + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)); + + if (channel->bounds_known) { - gegl_buffer_clear (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), - GEGL_RECTANGLE (channel->x1, channel->y1, - channel->x2 - channel->x1, - channel->y2 - channel->y1)); + rect.x = channel->x1; + rect.y = channel->y1; + rect.width = channel->x2 - channel->x1; + rect.height = channel->y2 - channel->y1; } else { - gegl_buffer_clear (gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), - NULL); + rect.x = 0; + rect.y = 0; + rect.width = gimp_item_get_width (GIMP_ITEM (channel)); + rect.height = gimp_item_get_height (GIMP_ITEM (channel)); } + gimp_gegl_rectangle_align_to_tile_grid (&aligned_rect, &rect, buffer); + + gegl_buffer_clear (buffer, &aligned_rect); + /* we know the bounds */ channel->bounds_known = TRUE; channel->empty = TRUE; @@ -1282,7 +1277,8 @@ gimp_channel_real_clear (GimpChannel *channel, 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); + gimp_drawable_update (GIMP_DRAWABLE (channel), + rect.x, rect.y, rect.width, rect.height); } static void @@ -1695,7 +1691,7 @@ gimp_channel_set_color (GimpChannel *channel, g_return_if_fail (GIMP_IS_CHANNEL (channel)); g_return_if_fail (color != NULL); - if (gimp_rgba_distance (&channel->color, color) > 0.0001) + if (gimp_rgba_distance (&channel->color, color) > RGBA_EPSILON) { if (push_undo && gimp_item_is_attached (GIMP_ITEM (channel))) { diff --git a/app/core/gimpchunkiterator.c b/app/core/gimpchunkiterator.c new file mode 100644 index 0000000000..616bb463cc --- /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 0000000000..e1756f3629 --- /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/gimpcontext.c b/app/core/gimpcontext.c index ff43700f65..37885835af 100644 --- a/app/core/gimpcontext.c +++ b/app/core/gimpcontext.c @@ -57,10 +57,11 @@ #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 @@ -2319,7 +2320,7 @@ static void gimp_context_real_set_foreground (GimpContext *context, const GimpRGB *color) { - if (gimp_rgba_distance (&context->foreground, color) < 0.0001) + if (gimp_rgba_distance (&context->foreground, color) < RGBA_EPSILON) return; context->foreground = *color; @@ -2370,7 +2371,7 @@ static void gimp_context_real_set_background (GimpContext *context, const GimpRGB *color) { - if (gimp_rgba_distance (&context->background, color) < 0.0001) + if (gimp_rgba_distance (&context->background, color) < RGBA_EPSILON) return; context->background = *color; diff --git a/app/core/gimpcurve.c b/app/core/gimpcurve.c index 247158c7ed..f7daa0b334 100644 --- a/app/core/gimpcurve.c +++ b/app/core/gimpcurve.c @@ -37,12 +37,16 @@ #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 }; @@ -155,7 +159,9 @@ gimp_curve_class_init (GimpCurveClass *klass) "n-points", "Number of Points", "The number of points", - 17, 17, 17, 0); + 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); @@ -166,6 +172,17 @@ gimp_curve_class_init (GimpCurveClass *klass) 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", @@ -207,17 +224,11 @@ gimp_curve_finalize (GObject *object) { GimpCurve *curve = GIMP_CURVE (object); - if (curve->points) - { - g_free (curve->points); - curve->points = NULL; - } + g_clear_pointer (&curve->points, g_free); + curve->n_points = 0; - if (curve->samples) - { - g_free (curve->samples); - curve->samples = NULL; - } + g_clear_pointer (&curve->samples, g_free); + curve->n_samples = 0; G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -237,28 +248,108 @@ gimp_curve_set_property (GObject *object, break; case PROP_N_POINTS: - gimp_curve_set_n_points (curve, g_value_get_int (value)); + /* ignored */ break; case PROP_POINTS: { GimpValueArray *array = g_value_get_boxed (value); + GimpCurvePoint *points; gint length; + gint n_points; gint i; if (! array) - break; + { + gimp_curve_clear_points (curve); - length = gimp_value_array_length (array); + break; + } - for (i = 0; i < curve->n_points && i * 2 < length; i++) + length = gimp_value_array_length (array) / 2; + + n_points = 0; + points = g_new (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); - curve->points[i].x = g_value_get_double (x); - curve->points[i].y = g_value_get_double (y); + /* 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_new (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; @@ -281,7 +372,7 @@ gimp_curve_set_property (GObject *object, { GValue *v = gimp_value_array_index (array, i); - curve->samples[i] = g_value_get_double (v); + curve->samples[i] = CLAMP (g_value_get_double (v), 0.0, 1.0); } } break; @@ -333,6 +424,26 @@ gimp_curve_get_property (GObject *object, } 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; @@ -370,7 +481,7 @@ gimp_curve_get_memsize (GimpObject *object, GimpCurve *curve = GIMP_CURVE (object); gint64 memsize = 0; - memsize += curve->n_points * sizeof (GimpVector2); + memsize += curve->n_points * sizeof (GimpCurvePoint); memsize += curve->n_samples * sizeof (gdouble); return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, @@ -484,11 +595,19 @@ gimp_curve_equal (GimpConfig *a, if (a_curve->curve_type != b_curve->curve_type) return FALSE; - if (memcmp (a_curve->points, b_curve->points, - sizeof (GimpVector2) * b_curve->n_points) || + 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) * b_curve->n_samples)) - return FALSE; + sizeof (gdouble) * a_curve->n_samples)) + { + return FALSE; + } return TRUE; } @@ -564,19 +683,22 @@ gimp_curve_reset (GimpCurve *curve, g_object_notify (G_OBJECT (curve), "samples"); - curve->points[0].x = 0.0; - curve->points[0].y = 0.0; + g_free (curve->points); - for (i = 1; i < curve->n_points - 1; i++) - { - curve->points[i].x = -1.0; - curve->points[i].y = -1.0; - } + curve->n_points = 2; + curve->points = g_new (GimpCurvePoint, 2); - curve->points[curve->n_points - 1].x = 1.0; - curve->points[curve->n_points - 1].y = 1.0; + 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) { @@ -599,44 +721,48 @@ gimp_curve_set_curve_type (GimpCurve *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 n_points; gint i; - for (i = 0; i < curve->n_points; i++) - { - curve->points[i].x = -1; - curve->points[i].y = -1; - } + g_free (curve->points); /* pick some points from the curve and make them control * points */ - n_points = CLAMP (9, curve->n_points / 2, curve->n_points); + curve->n_points = 9; + curve->points = g_new (GimpCurvePoint, 9); - for (i = 0; i < n_points; i++) + for (i = 0; i < curve->n_points; i++) { - gint sample = i * (curve->n_samples - 1) / (n_points - 1); - gint point = i * (curve->n_points - 1) / (n_points - 1); + gint sample = i * (curve->n_samples - 1) / (curve->n_points - 1); - curve->points[point].x = ((gdouble) sample / - (gdouble) (curve->n_samples - 1)); - curve->points[point].y = curve->samples[sample]; + 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_dirty (GIMP_DATA (curve)); + gimp_data_thaw (GIMP_DATA (curve)); } } @@ -648,46 +774,6 @@ gimp_curve_get_curve_type (GimpCurve *curve) return curve->curve_type; } -void -gimp_curve_set_n_points (GimpCurve *curve, - gint n_points) -{ - g_return_if_fail (GIMP_IS_CURVE (curve)); - g_return_if_fail (n_points >= 2); - g_return_if_fail (n_points <= 1024); - - if (n_points != curve->n_points) - { - gint i; - - g_object_freeze_notify (G_OBJECT (curve)); - - curve->n_points = n_points; - g_object_notify (G_OBJECT (curve), "n-points"); - - curve->points = g_renew (GimpVector2, curve->points, curve->n_points); - - curve->points[0].x = 0.0; - curve->points[0].y = 0.0; - - for (i = 1; i < curve->n_points - 1; i++) - { - curve->points[i].x = -1.0; - curve->points[i].y = -1.0; - } - - curve->points[curve->n_points - 1].x = 1.0; - curve->points[curve->n_points - 1].y = 1.0; - - g_object_notify (G_OBJECT (curve), "points"); - - if (curve->curve_type == GIMP_CURVE_SMOOTH) - curve->identity = TRUE; - - g_object_thaw_notify (G_OBJECT (curve)); - } -} - gint gimp_curve_get_n_points (GimpCurve *curve) { @@ -736,31 +822,138 @@ gimp_curve_get_n_samples (GimpCurve *curve) } gint -gimp_curve_get_closest_point (GimpCurve *curve, - gdouble x) +gimp_curve_get_point_at (GimpCurve *curve, + gdouble x) { - gint closest_point = 0; - gdouble distance = G_MAXDOUBLE; + gint closest_point = -1; + gdouble distance = EPSILON; gint i; - g_return_val_if_fail (GIMP_IS_CURVE (curve), 0); + g_return_val_if_fail (GIMP_IS_CURVE (curve), -1); for (i = 0; i < curve->n_points; i++) { - if (curve->points[i].x >= 0.0 && - fabs (x - curve->points[i].x) < distance) + gdouble point_distance; + + point_distance = fabs (x - curve->points[i].x); + + if (point_distance <= distance) { - distance = fabs (x - curve->points[i].x); closest_point = i; + distance = point_distance; } } - if (distance > (1.0 / (curve->n_points * 2.0))) - closest_point = ROUND (x * (gdouble) (curve->n_points - 1)); + 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_new (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_new (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, @@ -769,14 +962,15 @@ gimp_curve_set_point (GimpCurve *curve, { g_return_if_fail (GIMP_IS_CURVE (curve)); g_return_if_fail (point >= 0 && point < curve->n_points); - g_return_if_fail (x == -1.0 || (x >= 0 && x <= 1.0)); - g_return_if_fail (y == -1.0 || (y >= 0 && y <= 1.0)); - if (curve->curve_type == GIMP_CURVE_FREE) - return; + curve->points[point].x = CLAMP (x, 0.0, 1.0); + curve->points[point].y = CLAMP (y, 0.0, 1.0); - curve->points[point].x = x; - curve->points[point].y = y; + 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"); @@ -790,40 +984,8 @@ gimp_curve_move_point (GimpCurve *curve, { g_return_if_fail (GIMP_IS_CURVE (curve)); g_return_if_fail (point >= 0 && point < curve->n_points); - g_return_if_fail (y >= 0 && y <= 1.0); - if (curve->curve_type == GIMP_CURVE_FREE) - return; - - curve->points[point].y = y; - - g_object_notify (G_OBJECT (curve), "points"); - - gimp_data_dirty (GIMP_DATA (curve)); -} - -void -gimp_curve_delete_point (GimpCurve *curve, - gint point) -{ - g_return_if_fail (GIMP_IS_CURVE (curve)); - g_return_if_fail (point >= 0 && point < curve->n_points); - - if (point == 0) - { - curve->points[0].x = 0.0; - curve->points[0].y = 0.0; - } - else if (point == curve->n_points - 1) - { - curve->points[curve->n_points - 1].x = 1.0; - curve->points[curve->n_points - 1].y = 1.0; - } - else - { - curve->points[point].x = -1.0; - curve->points[point].y = -1.0; - } + curve->points[point].y = CLAMP (y, 0.0, 1.0); g_object_notify (G_OBJECT (curve), "points"); @@ -839,18 +1001,53 @@ gimp_curve_get_point (GimpCurve *curve, g_return_if_fail (GIMP_IS_CURVE (curve)); g_return_if_fail (point >= 0 && point < curve->n_points); - if (curve->curve_type == GIMP_CURVE_FREE) - { - if (x) *x = -1.0; - if (y) *y = -1.0; - - return; - } - 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, @@ -909,59 +1106,55 @@ gimp_curve_get_uchar (GimpCurve *curve, static void gimp_curve_calculate (GimpCurve *curve) { - gint *points; - gint i; - gint num_pts; - gint p1, p2, p3, p4; + gint i; + gint p1, p2, p3, p4; if (gimp_data_is_frozen (GIMP_DATA (curve))) return; - points = g_newa (gint, curve->n_points); - switch (curve->curve_type) { case GIMP_CURVE_SMOOTH: - /* cycle through the curves */ - num_pts = 0; - for (i = 0; i < curve->n_points; i++) - if (curve->points[i].x >= 0.0) - points[num_pts++] = i; - /* Initialize boundary curve points */ - if (num_pts != 0) + if (curve->n_points > 0) { - GimpVector2 point; - gint boundary; + GimpCurvePoint point; + gint boundary; - point = curve->points[points[0]]; + 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[points[num_pts - 1]]; + 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 < num_pts - 1; i++) + for (i = 0; i < curve->n_points - 1; i++) { - p1 = points[MAX (i - 1, 0)]; - p2 = points[i]; - p3 = points[i + 1]; - p4 = points[MIN (i + 2, num_pts - 1)]; + 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 < num_pts; i++) + for (i = 0; i < curve->n_points; i++) { - gdouble x = curve->points[points[i]].x; - gdouble y = curve->points[points[i]].y; + gdouble x = curve->points[i].x; + gdouble y = curve->points[i].y; curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y; } @@ -1013,7 +1206,16 @@ gimp_curve_plot (GimpCurve *curve, dx = x3 - x0; dy = y3 - y0; - g_return_if_fail (dx > 0); + if (dx <= EPSILON) + { + gint index; + + index = ROUND (x0 * (gdouble) (curve->n_samples - 1)); + + curve->samples[index] = y3; + + return; + } if (p1 == p2 && p3 == p4) { @@ -1061,7 +1263,7 @@ gimp_curve_plot (GimpCurve *curve, /* * finally calculate the y(t) values for the given bezier values. We can - * use homogenously distributed values for t, since x(t) increases linearly. + * use homogeneously distributed values for t, since x(t) increases linearly. */ for (i = 0; i <= ROUND (dx * (gdouble) (curve->n_samples - 1)); i++) { diff --git a/app/core/gimpcurve.h b/app/core/gimpcurve.h index 77f553a074..71654aa184 100644 --- a/app/core/gimpcurve.h +++ b/app/core/gimpcurve.h @@ -30,21 +30,30 @@ #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; + GimpData parent_instance; - GimpCurveType curve_type; + GimpCurveType curve_type; - gint n_points; - GimpVector2 *points; + gint n_points; + GimpCurvePoint *points; - gint n_samples; - gdouble *samples; + gint n_samples; + gdouble *samples; - gboolean identity; /* whether the curve is an identity mapping */ + gboolean identity; /* whether the curve is an identity mapping */ }; struct _GimpCurveClass @@ -53,52 +62,63 @@ struct _GimpCurveClass }; -GType gimp_curve_get_type (void) G_GNUC_CONST; +GType gimp_curve_get_type (void) G_GNUC_CONST; -GimpData * gimp_curve_new (const gchar *name); -GimpData * gimp_curve_get_standard (void); +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_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); +void gimp_curve_set_curve_type (GimpCurve *curve, + GimpCurveType curve_type); +GimpCurveType gimp_curve_get_curve_type (GimpCurve *curve); -void gimp_curve_set_n_points (GimpCurve *curve, - gint n_points); -gint gimp_curve_get_n_points (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); +void gimp_curve_set_n_samples (GimpCurve *curve, + gint n_samples); +gint gimp_curve_get_n_samples (GimpCurve *curve); -gint gimp_curve_get_closest_point (GimpCurve *curve, - gdouble x); +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); -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_delete_point (GimpCurve *curve, - gint point); -void gimp_curve_get_point (GimpCurve *curve, - gint point, - gdouble *x, - gdouble *y); +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); +void gimp_curve_set_curve (GimpCurve *curve, + gdouble x, + gdouble y); -gboolean gimp_curve_is_identity (GimpCurve *curve); +gboolean gimp_curve_is_identity (GimpCurve *curve); -void gimp_curve_get_uchar (GimpCurve *curve, - gint n_samples, - guchar *samples); +void gimp_curve_get_uchar (GimpCurve *curve, + gint n_samples, + guchar *samples); #endif /* __GIMP_CURVE_H__ */ diff --git a/app/core/gimpdata.c b/app/core/gimpdata.c index 4d7388d86f..d8ee11ba07 100644 --- a/app/core/gimpdata.c +++ b/app/core/gimpdata.c @@ -592,17 +592,25 @@ gimp_data_save (GimpData *data, success = FALSE; } } - else 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)); + 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); diff --git a/app/core/gimpdrawable-bucket-fill.c b/app/core/gimpdrawable-bucket-fill.c index a1a181eeb9..e5dda00a77 100644 --- a/app/core/gimpdrawable-bucket-fill.c +++ b/app/core/gimpdrawable-bucket-fill.c @@ -18,6 +18,7 @@ #include "config.h" #include +#define GEGL_ITERATOR2_API #include #include @@ -58,15 +59,11 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable, gdouble seed_x, gdouble seed_y) { - GimpImage *image; - GimpPickable *pickable; - GeglBuffer *buffer; - GeglBuffer *mask_buffer; - 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; + 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))); @@ -74,9 +71,113 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable, 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, 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: + * @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 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; + 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); @@ -90,20 +191,29 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable, /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ - mask_buffer = gimp_pickable_contiguous_region_by_seed (pickable, - antialias, - threshold, - fill_transparent, - fill_criterion, - diagonal_neighbors, - (gint) seed_x, - (gint) seed_y); + 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); + } - gimp_gegl_mask_bounds (mask_buffer, &x, &y, &width, &height); + 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, inersect the region bounds + /* 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 @@ -127,11 +237,12 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable, { /* The fill region and the selection are disjoint; bail. */ - g_object_unref (mask_buffer); + if (! mask_buffer) + g_object_unref (new_mask); gimp_unset_busy (image->gimp); - return; + return NULL; } } @@ -155,7 +266,7 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable, mask_offset_x = x; mask_offset_y = y; - /* translate mask bounds to drawable coords */ + /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } @@ -170,28 +281,210 @@ gimp_drawable_bucket_fill (GimpDrawable *drawable, width, height), -x, -y); - gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, - mask_buffer, - -mask_offset_x, - -mask_offset_y, - 1.0); - g_object_unref (mask_buffer); + gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, + -mask_offset_x, -mask_offset_y, 1.0); - /* 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, x, y); + if (mask_x) + *mask_x = x; + if (mask_y) + *mask_y = y; + if (mask_width) + *mask_width = width; + if (mask_height) + *mask_height = height; - g_object_unref (buffer); - - gimp_drawable_update (drawable, x, y, width, 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); + } + + 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 index f79062a8fb..ef9acfb13f 100644 --- a/app/core/gimpdrawable-bucket-fill.h +++ b/app/core/gimpdrawable-bucket-fill.h @@ -19,15 +19,40 @@ #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); +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 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 index 9a4dccea1c..e48d9dd6e0 100644 --- a/app/core/gimpdrawable-combine.c +++ b/app/core/gimpdrawable-combine.c @@ -17,6 +17,7 @@ #include "config.h" +#include #include #include @@ -25,18 +26,12 @@ #include "core-types.h" #include "gegl/gimpapplicator.h" -#include "gegl/gimp-babl-compat.h" -#include "gegl/gimp-gegl-apply-operation.h" -#include "gegl/gimp-gegl-loops.h" -#include "gegl/gimp-gegl-utils.h" #include "gimp.h" #include "gimpchannel.h" +#include "gimpchunkiterator.h" #include "gimpdrawable-combine.h" -#include "gimpdrawableundo.h" #include "gimpimage.h" -#include "gimpimage-undo.h" -#include "gimptempbuf.h" void @@ -58,6 +53,7 @@ gimp_drawable_real_apply_buffer (GimpDrawable *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; @@ -102,37 +98,11 @@ gimp_drawable_real_apply_buffer (GimpDrawable *drawable, if (push_undo) { - GimpDrawableUndo *undo; - gimp_drawable_push_undo (drawable, undo_desc, NULL, x, y, width, height); - - undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); - - if (undo) - { - undo->paint_mode = mode; - undo->blend_space = blend_space; - undo->composite_space = composite_space; - undo->composite_mode = composite_mode; - undo->opacity = opacity; - - undo->applied_buffer = - gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), - gegl_buffer_get_format (buffer)); - - gimp_gegl_buffer_copy ( - buffer, - GEGL_RECTANGLE (buffer_region->x + (x - base_x), - buffer_region->y + (y - base_y), - width, height), - GEGL_ABYSS_NONE, - undo->applied_buffer, - GEGL_RECTANGLE (0, 0, width, height)); - } } - applicator = gimp_applicator_new (NULL, FALSE, FALSE); + applicator = gimp_applicator_new (NULL); if (mask) { @@ -159,135 +129,16 @@ gimp_drawable_real_apply_buffer (GimpDrawable *drawable, gimp_applicator_set_affect (applicator, gimp_drawable_get_active_mask (drawable)); - gimp_applicator_blit (applicator, GEGL_RECTANGLE (x, y, width, height)); + 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); } - -/* Similar to gimp_drawable_apply_region but works in "replace" mode (i.e. - * transparent pixels in src2 make the result transparent rather than - * opaque. - * - * Takes an additional mask pixel region as well. - */ -void -gimp_drawable_real_replace_buffer (GimpDrawable *drawable, - GeglBuffer *buffer, - const GeglRectangle *buffer_region, - gboolean push_undo, - const gchar *undo_desc, - gdouble opacity, - GeglBuffer *mask_buffer, - const GeglRectangle *mask_buffer_region, - gint dest_x, - gint dest_y) -{ - GimpItem *item = GIMP_ITEM (drawable); - GimpImage *image = gimp_item_get_image (item); - GimpChannel *mask = gimp_image_get_mask (image); - GeglBuffer *drawable_buffer; - GeglRectangle buffer_rect = *buffer_region; - GeglRectangle mask_buffer_rect = *mask_buffer_region; - gint x, y, width, height; - gint offset_x, offset_y; - gboolean active_components[MAX_CHANNELS]; - - /* 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; - - /* configure the active channel array */ - gimp_drawable_get_active_components (drawable, active_components); - - /* 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 (dest_x, dest_y, - buffer_rect.width, buffer_rect.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; - } - } - - /* adjust the original regions according to the application - * offset and size - */ - buffer_rect.x += x - dest_x; - buffer_rect.y += y - dest_y; - buffer_rect.width = width; - buffer_rect.height = height; - - mask_buffer_rect.x += x - dest_x; - mask_buffer_rect.y += y - dest_y; - mask_buffer_rect.width = width; - mask_buffer_rect.height = height; - - /* If the calling procedure specified an undo step... */ - if (push_undo) - gimp_drawable_push_undo (drawable, undo_desc, - NULL, x, y, width, height); - - drawable_buffer = gimp_drawable_get_buffer (drawable); - - if (mask) - { - GeglBuffer *src_buffer; - GeglBuffer *dest_buffer; - - src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); - - dest_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), - gegl_buffer_get_format (src_buffer)); - - gimp_gegl_buffer_copy (src_buffer, - GEGL_RECTANGLE (x + offset_x, y + offset_y, - width, height), - GEGL_ABYSS_NONE, - dest_buffer, - GEGL_RECTANGLE (0, 0, 0, 0)); - - gimp_gegl_combine_mask (mask_buffer, &mask_buffer_rect, - dest_buffer, GEGL_RECTANGLE (0, 0, width, height), - 1.0); - - gimp_gegl_replace (buffer, &buffer_rect, - drawable_buffer, GEGL_RECTANGLE (x, y, width, height), - dest_buffer, GEGL_RECTANGLE (0, 0, width, height), - drawable_buffer, GEGL_RECTANGLE (x, y, width, height), - opacity, - active_components); - - g_object_unref (dest_buffer); - } - else - { - gimp_gegl_replace (buffer, &buffer_rect, - drawable_buffer, GEGL_RECTANGLE (x, y, width, height), - mask_buffer, &mask_buffer_rect, - drawable_buffer, GEGL_RECTANGLE (x, y, width, height), - opacity, - active_components); - } -} diff --git a/app/core/gimpdrawable-combine.h b/app/core/gimpdrawable-combine.h index 6bd0825431..d21d558d32 100644 --- a/app/core/gimpdrawable-combine.h +++ b/app/core/gimpdrawable-combine.h @@ -21,29 +21,19 @@ /* 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); -void gimp_drawable_real_replace_buffer (GimpDrawable *drawable, - GeglBuffer *buffer, - const GeglRectangle *buffer_region, - gboolean push_undo, - const gchar *undo_desc, - gdouble opacity, - GeglBuffer *mask, - const GeglRectangle *mask_region, - gint x, - gint y); +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 index 0cc3f45d67..214b8fb802 100644 --- a/app/core/gimpdrawable-edit.c +++ b/app/core/gimpdrawable-edit.c @@ -24,14 +24,109 @@ #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 @@ -63,36 +158,74 @@ gimp_drawable_edit_fill (GimpDrawable *drawable, GimpFillOptions *options, const gchar *undo_desc) { - GeglBuffer *buffer; + 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 */ + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &x, &y, &width, &height)) + { + return; /* nothing to do, but the fill succeeded */ + } - buffer = gimp_fill_options_create_buffer (options, drawable, - GEGL_RECTANGLE (0, 0, - width, height), - -x, -y); + 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); - gimp_drawable_apply_buffer (drawable, buffer, - GEGL_RECTANGLE (0, 0, width, height), - TRUE, undo_desc, - 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, x, y); + /* 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); - g_object_unref (buffer); + gimp_drawable_update (drawable, x, y, width, height); + } + else + { + GeglNode *operation; + GimpDrawableFilter *filter; + gdouble opacity; + GimpLayerMode mode; + GimpLayerMode composite_mode; - gimp_drawable_update (drawable, x, y, width, height); + 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-fill.c b/app/core/gimpdrawable-fill.c index 7051f9f770..a596d473d0 100644 --- a/app/core/gimpdrawable-fill.c +++ b/app/core/gimpdrawable-fill.c @@ -92,26 +92,34 @@ gimp_drawable_fill_buffer (GimpDrawable *drawable, if (pattern) { - const Babl *format; GeglBuffer *src_buffer; GeglBuffer *dest_buffer; GimpColorProfile *src_profile; GimpColorProfile *dest_profile; src_buffer = gimp_pattern_create_buffer (pattern); - format = gegl_buffer_get_format (src_buffer); - dest_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer), - format); + 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)); - src_profile = gimp_babl_format_get_color_profile (format); - 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); + 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); diff --git a/app/core/gimpdrawable-filters.c b/app/core/gimpdrawable-filters.c index ce281c2f95..b39753ef86 100644 --- a/app/core/gimpdrawable-filters.c +++ b/app/core/gimpdrawable-filters.c @@ -28,16 +28,15 @@ #include "gegl/gimpapplicator.h" #include "gegl/gimp-gegl-apply-operation.h" #include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" #include "gimp-utils.h" #include "gimpdrawable.h" #include "gimpdrawable-filters.h" #include "gimpdrawable-private.h" -#include "gimpdrawableundo.h" #include "gimpfilter.h" #include "gimpfilterstack.h" #include "gimpimage.h" -#include "gimpimage-undo.h" #include "gimpprogress.h" #include "gimpprojection.h" @@ -110,142 +109,125 @@ gimp_drawable_merge_filter (GimpDrawable *drawable, GimpFilter *filter, GimpProgress *progress, const gchar *undo_desc, - gboolean cancellable) + gboolean cancellable, + gboolean update) { - GeglRectangle rect; - gboolean success = TRUE; + GimpImage *image; + GimpApplicator *applicator; + gboolean applicator_cache = FALSE; + const Babl *applicator_output_format = NULL; + GeglBuffer *undo_buffer; + 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); - if (gimp_item_mask_intersect (GIMP_ITEM (drawable), - &rect.x, &rect.y, - &rect.width, &rect.height)) + image = gimp_item_get_image (GIMP_ITEM (drawable)); + applicator = gimp_filter_get_applicator (filter); + + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &rect.x, &rect.y, + &rect.width, &rect.height)) { - GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); - GeglBuffer *undo_buffer; - GimpApplicator *applicator; - GeglBuffer *apply_buffer = NULL; - GeglBuffer *cache = NULL; - GeglRectangle *rects = NULL; - gint n_rects = 0; + return TRUE; + } - undo_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, - rect.width, rect.height), - gimp_drawable_get_format (drawable)); + if (applicator) + { + const GeglRectangle *crop_rect; - gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (rect.x, rect.y, - rect.width, rect.height), + 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 == gimp_drawable_get_format (drawable)) + gimp_applicator_set_output_format (applicator, NULL); + } + + gimp_gegl_rectangle_align_to_tile_grid ( + &undo_rect, + &rect, + gimp_drawable_get_buffer (drawable)); + + 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)); + + if (gimp_gegl_apply_cached_operation (gimp_drawable_get_buffer (drawable), + progress, undo_desc, + gimp_filter_get_node (filter), + gimp_drawable_get_buffer (drawable), + &rect, FALSE, + cache, rects, n_rects, + cancellable)) + { + /* finished successfully */ + + gimp_drawable_push_undo (drawable, undo_desc, undo_buffer, + undo_rect.x, undo_rect.y, + undo_rect.width, undo_rect.height); + } + else + { + /* canceled by the user */ + + gimp_gegl_buffer_copy (undo_buffer, + GEGL_RECTANGLE (0, 0, + undo_rect.width, + undo_rect.height), GEGL_ABYSS_NONE, - undo_buffer, - GEGL_RECTANGLE (0, 0, 0, 0)); + gimp_drawable_get_buffer (drawable), + &undo_rect); - applicator = gimp_filter_get_applicator (filter); + success = FALSE; + } - if (applicator) - { - /* disable the preview crop, this will force-process the - * cached result from the preview cache into the result - * cache, involving only the layer and affect nodes - */ - gimp_applicator_set_preview (applicator, FALSE, - GEGL_RECTANGLE (0, 0, 0, 0)); + g_object_unref (undo_buffer); - /* the apply_buffer will make a copy of the region that is - * actually processed in gimp_gegl_apply_cached_operation() - * below. - */ - apply_buffer = gimp_applicator_dup_apply_buffer (applicator, &rect); + if (cache) + { + g_object_unref (cache); + g_free (rects); + } - /* 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); - - if (cache) - { - gint i; - - for (i = 0; i < n_rects; i++) - { - g_printerr ("valid: %d %d %d %d\n", - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - - /* we have to copy the cached region to the apply_buffer, - * because this region is not going to be processed. - */ - gimp_gegl_buffer_copy (cache, - &rects[i], - GEGL_ABYSS_NONE, - apply_buffer, - GEGL_RECTANGLE (rects[i].x - rect.x, - rects[i].y - rect.y, - 0, 0)); - } - } - - } - - gimp_projection_stop_rendering (gimp_image_get_projection (image)); - - if (gimp_gegl_apply_cached_operation (gimp_drawable_get_buffer (drawable), - progress, undo_desc, - gimp_filter_get_node (filter), - gimp_drawable_get_buffer (drawable), - &rect, FALSE, - cache, rects, n_rects, - cancellable)) - { - /* finished successfully */ - - gimp_drawable_push_undo (drawable, undo_desc, undo_buffer, - rect.x, rect.y, - rect.width, rect.height); - - if (applicator) - { - GimpDrawableUndo *undo; - - undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); - - if (undo) - { - undo->paint_mode = applicator->paint_mode; - undo->opacity = applicator->opacity; - - undo->applied_buffer = apply_buffer; - apply_buffer = NULL; - } - } - } - else - { - /* canceled by the user */ - - gimp_gegl_buffer_copy (undo_buffer, - GEGL_RECTANGLE (0, 0, rect.width, rect.height), - GEGL_ABYSS_NONE, - gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (rect.x, rect.y, 0, 0)); - - success = FALSE; - } - - g_object_unref (undo_buffer); - - if (apply_buffer) - g_object_unref (apply_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); diff --git a/app/core/gimpdrawable-filters.h b/app/core/gimpdrawable-filters.h index f7041e062b..a2c6972d6c 100644 --- a/app/core/gimpdrawable-filters.h +++ b/app/core/gimpdrawable-filters.h @@ -37,7 +37,8 @@ gboolean gimp_drawable_merge_filter (GimpDrawable *drawable, GimpFilter *filter, GimpProgress *progress, const gchar *undo_desc, - gboolean cancellable); + 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 index 37a32fa6eb..ad5c387de9 100644 --- a/app/core/gimpdrawable-floating-selection.c +++ b/app/core/gimpdrawable-floating-selection.c @@ -47,6 +47,8 @@ static void gimp_drawable_sync_fs_filter (GimpDrawable *dr static void gimp_drawable_fs_notify (GimpLayer *fs, const GParamSpec *pspec, 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); @@ -206,7 +208,11 @@ _gimp_drawable_add_floating_sel_filter (GimpDrawable *drawable) gegl_node_add_child (node, fs_source); - private->fs_applicator = gimp_applicator_new (node, FALSE, FALSE); + 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, @@ -223,6 +229,9 @@ _gimp_drawable_add_floating_sel_filter (GimpDrawable *drawable) g_signal_connect (fs, "notify", G_CALLBACK (gimp_drawable_fs_notify), 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); @@ -251,6 +260,9 @@ gimp_drawable_remove_fs_filter (GimpDrawable *drawable) g_signal_handlers_disconnect_by_func (fs, gimp_drawable_fs_notify, 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); @@ -289,8 +301,8 @@ gimp_drawable_sync_fs_filter (GimpDrawable *drawable) 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; + 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))); @@ -331,6 +343,8 @@ gimp_drawable_sync_fs_filter (GimpDrawable *drawable) 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)); } static void @@ -351,6 +365,17 @@ gimp_drawable_fs_notify (GimpLayer *fs, } } +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, diff --git a/app/core/gimpdrawable-offset.c b/app/core/gimpdrawable-offset.c index 9475e8a685..58089c5da5 100644 --- a/app/core/gimpdrawable-offset.c +++ b/app/core/gimpdrawable-offset.c @@ -23,20 +23,13 @@ #include #include -#include "libgimpcolor/gimpcolor.h" -#include "libgimpmath/gimpmath.h" - #include "core-types.h" -#include "gegl/gimp-gegl-loops.h" -#include "gegl/gimp-gegl-utils.h" - #include "gimp.h" #include "gimpcontext.h" #include "gimpdrawable.h" #include "gimpdrawable-offset.h" -#include "gimpimage.h" -#include "gimppickable.h" +#include "gimpdrawable-operation.h" #include "gimp-intl.h" @@ -49,206 +42,42 @@ gimp_drawable_offset (GimpDrawable *drawable, gint offset_x, gint offset_y) { - GimpItem *item; - GeglBuffer *src_buffer; - GeglBuffer *new_buffer; - GeglRectangle src_rect; - GeglRectangle dest_rect; - gint width, height; - gint src_x, src_y; - gint dest_x, dest_y; + GeglNode *node; + gint width; + gint height; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (GIMP_IS_CONTEXT (context)); - item = GIMP_ITEM (drawable); - - width = gimp_item_get_width (item); - height = gimp_item_get_height (item); + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + NULL, NULL, &width, &height)) + { + return; + } if (wrap_around) - { - /* avoid modulo operation on negative values */ - while (offset_x < 0) - offset_x += width; - while (offset_y < 0) - offset_y += height; + fill_type = GIMP_OFFSET_WRAP_AROUND; + if (fill_type == GIMP_OFFSET_WRAP_AROUND) + { offset_x %= width; offset_y %= height; } - else - { - offset_x = CLAMP (offset_x, -width, width); - offset_y = CLAMP (offset_y, -height, height); - } - if (offset_x == 0 && offset_y == 0) + if (offset_x == 0 || offset_y == 0) return; - src_buffer = gimp_drawable_get_buffer (drawable); + node = gegl_node_new_child (NULL, + "operation", "gimp:offset", + "context", context, + "type", fill_type, + "x", offset_x, + "y", offset_y, + NULL); - new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), - gimp_drawable_get_format (drawable)); + gimp_drawable_apply_operation (drawable, NULL, + C_("undo-type", "Offset Drawable"), + node); - if (! wrap_around) - { - if (fill_type == GIMP_OFFSET_BACKGROUND) - { - GimpRGB bg; - GeglColor *color; - - 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 (offset_x >= 0) - { - src_x = 0; - dest_x = offset_x; - width = CLAMP ((width - offset_x), 0, width); - } - else - { - src_x = -offset_x; - dest_x = 0; - width = CLAMP ((width + offset_x), 0, width); - } - - if (offset_y >= 0) - { - src_y = 0; - dest_y = offset_y; - height = CLAMP ((height - offset_y), 0, height); - } - else - { - src_y = -offset_y; - dest_y = 0; - height = CLAMP ((height + offset_y), 0, height); - } - - /* Copy the center region */ - if (width && height) - { - gimp_gegl_buffer_copy (src_buffer, - GEGL_RECTANGLE (src_x, src_y, width, height), - GEGL_ABYSS_NONE, - new_buffer, - GEGL_RECTANGLE (dest_x,dest_y, width, height)); - } - - if (wrap_around) - { - /* Copy appropriately for wrap around */ - - if (offset_x >= 0 && offset_y >= 0) - { - src_x = gimp_item_get_width (item) - offset_x; - src_y = gimp_item_get_height (item) - offset_y; - } - else if (offset_x >= 0 && offset_y < 0) - { - src_x = gimp_item_get_width (item) - offset_x; - src_y = 0; - } - else if (offset_x < 0 && offset_y >= 0) - { - src_x = 0; - src_y = gimp_item_get_height (item) - offset_y; - } - else if (offset_x < 0 && offset_y < 0) - { - src_x = 0; - src_y = 0; - } - - dest_x = (src_x + offset_x) % gimp_item_get_width (item); - if (dest_x < 0) - dest_x = gimp_item_get_width (item) + dest_x; - - dest_y = (src_y + offset_y) % gimp_item_get_height (item); - if (dest_y < 0) - dest_y = gimp_item_get_height (item) + dest_y; - - /* intersecting region */ - if (offset_x != 0 && offset_y != 0) - { - gimp_gegl_buffer_copy ( - src_buffer, - GEGL_RECTANGLE (src_x, src_y, - ABS (offset_x), ABS (offset_y)), - GEGL_ABYSS_NONE, - new_buffer, - GEGL_RECTANGLE (dest_x, dest_y, 0, 0)); - } - - /* X offset */ - if (offset_x != 0) - { - if (offset_y >= 0) - { - src_rect.x = src_x; - src_rect.y = 0; - src_rect.width = ABS (offset_x); - src_rect.height = gimp_item_get_height (item) - ABS (offset_y); - - dest_rect.x = dest_x; - dest_rect.y = dest_y + offset_y; - } - else if (offset_y < 0) - { - src_rect.x = src_x; - src_rect.y = src_y - offset_y; - src_rect.width = ABS (offset_x); - src_rect.height = gimp_item_get_height (item) - ABS (offset_y); - - dest_rect.x = dest_x; - dest_rect.y = 0; - } - - gimp_gegl_buffer_copy (src_buffer, &src_rect, GEGL_ABYSS_NONE, - new_buffer, &dest_rect); - } - - /* X offset */ - if (offset_y != 0) - { - if (offset_x >= 0) - { - src_rect.x = 0; - src_rect.y = src_y; - src_rect.width = gimp_item_get_width (item) - ABS (offset_x); - src_rect.height = ABS (offset_y); - - dest_rect.x = dest_x + offset_x; - dest_rect.y = dest_y; - } - else if (offset_x < 0) - { - src_rect.x = src_x - offset_x; - src_rect.y = src_y; - src_rect.width = gimp_item_get_width (item) - ABS (offset_x); - src_rect.height = ABS (offset_y); - - dest_rect.x = 0; - dest_rect.y = dest_y; - } - - gimp_gegl_buffer_copy (src_buffer, &src_rect, GEGL_ABYSS_NONE, - new_buffer, &dest_rect); - } - } - - gimp_drawable_set_buffer (drawable, - gimp_item_is_attached (item), - C_("undo-type", "Offset Drawable"), - new_buffer); - g_object_unref (new_buffer); + g_object_unref (node); } diff --git a/app/core/gimpdrawable-operation.c b/app/core/gimpdrawable-operation.c index 4f38bfd5e7..d4994fa3b4 100644 --- a/app/core/gimpdrawable-operation.c +++ b/app/core/gimpdrawable-operation.c @@ -27,11 +27,9 @@ #include "core-types.h" -#include "gegl/gimp-gegl-apply-operation.h" - #include "gimpdrawable.h" #include "gimpdrawable-operation.h" -#include "gimpdrawable-shadow.h" +#include "gimpdrawablefilter.h" #include "gimpprogress.h" #include "gimpsettings.h" @@ -44,8 +42,7 @@ gimp_drawable_apply_operation (GimpDrawable *drawable, const gchar *undo_desc, GeglNode *operation) { - GeglBuffer *dest_buffer; - GeglRectangle rect; + GimpDrawableFilter *filter; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); @@ -54,21 +51,17 @@ gimp_drawable_apply_operation (GimpDrawable *drawable, g_return_if_fail (GEGL_IS_NODE (operation)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), - &rect.x, &rect.y, - &rect.width, &rect.height)) - return; + NULL, NULL, NULL, NULL)) + { + return; + } - dest_buffer = gimp_drawable_get_shadow_buffer (drawable); + filter = gimp_drawable_filter_new (drawable, undo_desc, operation, NULL); - gimp_gegl_apply_operation (gimp_drawable_get_buffer (drawable), - progress, undo_desc, - operation, - dest_buffer, &rect, FALSE); + gimp_drawable_filter_apply (filter, NULL); + gimp_drawable_filter_commit (filter, progress, TRUE); - gimp_drawable_merge_shadow_buffer (drawable, TRUE, undo_desc); - gimp_drawable_free_shadow_buffer (drawable); - - gimp_drawable_update (drawable, rect.x, rect.y, rect.width, rect.height); + g_object_unref (filter); if (progress) gimp_progress_end (progress); diff --git a/app/core/gimpdrawable-private.h b/app/core/gimpdrawable-private.h index 67f142f6c7..593f53e0b7 100644 --- a/app/core/gimpdrawable-private.h +++ b/app/core/gimpdrawable-private.h @@ -26,7 +26,6 @@ struct _GimpDrawablePrivate GeglNode *source_node; GeglNode *buffer_source_node; GimpContainer *filter_stack; - GeglNode *convert_format; GimpLayer *floating_selection; GimpFilter *fs_filter; diff --git a/app/core/gimpdrawable-transform.c b/app/core/gimpdrawable-transform.c index ea21da426c..cc1690559c 100644 --- a/app/core/gimpdrawable-transform.c +++ b/app/core/gimpdrawable-transform.c @@ -15,8 +15,6 @@ * along with this program. If not, see . */ -#define GEGL_ITERATOR2_API - #include "config.h" #include @@ -87,10 +85,14 @@ gimp_drawable_transform_get_effective_clip (GimpDrawable *drawable, } else { - GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpChannel *mask = gimp_image_get_mask (image); - if (gimp_channel_is_empty (gimp_image_get_mask (image))) - clip_result = GIMP_TRANSFORM_RESIZE_CLIP; + if (GIMP_CHANNEL (drawable) == mask || + gimp_channel_is_empty (mask)) + { + clip_result = GIMP_TRANSFORM_RESIZE_CLIP; + } } } @@ -144,9 +146,16 @@ gimp_drawable_transform_buffer_affine (GimpDrawable *drawable, u2 = u1 + gegl_buffer_get_width (orig_buffer); v2 = v1 + gegl_buffer_get_height (orig_buffer); - clip_result = gimp_drawable_transform_get_effective_clip (drawable, - orig_buffer, - clip_result); + /* Don't modify the clipping mode of layer masks here, so that, + * when transformed together with their layer, they match the + * layer's clipping mode. + */ + if (G_TYPE_FROM_INSTANCE (drawable) == GIMP_TYPE_CHANNEL) + { + clip_result = gimp_drawable_transform_get_effective_clip (drawable, + orig_buffer, + clip_result); + } /* Find the bounding coordinates of target */ gimp_transform_resize_boundary (&m, clip_result, diff --git a/app/core/gimpdrawable.c b/app/core/gimpdrawable.c index cb4dd55713..7c9544bb03 100644 --- a/app/core/gimpdrawable.c +++ b/app/core/gimpdrawable.c @@ -66,6 +66,7 @@ enum { UPDATE, + FORMAT_CHANGED, ALPHA_CHANGED, LAST_SIGNAL }; @@ -99,6 +100,8 @@ static gint64 gimp_drawable_get_memsize (GimpObject *object, 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); @@ -166,6 +169,11 @@ static gint64 gimp_drawable_real_estimate_memsize (GimpDrawable *drawable, gint width, gint height); +static void gimp_drawable_real_update_all (GimpDrawable *drawable); + +static GimpComponentMask + gimp_drawable_real_get_active_mask (GimpDrawable *drawable); + static void gimp_drawable_real_convert_type (GimpDrawable *drawable, GimpImage *dest_image, const Babl *new_format, @@ -196,6 +204,9 @@ static void gimp_drawable_real_swap_pixels (GimpDrawable *drawable, 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) @@ -231,6 +242,15 @@ gimp_drawable_class_init (GimpDrawableClass *klass) 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), @@ -250,6 +270,8 @@ gimp_drawable_class_init (GimpDrawableClass *klass) 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; @@ -262,14 +284,15 @@ gimp_drawable_class_init (GimpDrawableClass *klass) item_class->transform = gimp_drawable_transform; klass->update = gimp_drawable_real_update; + klass->format_changed = NULL; klass->alpha_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 = NULL; + klass->get_active_mask = gimp_drawable_real_get_active_mask; klass->convert_type = gimp_drawable_real_convert_type; klass->apply_buffer = gimp_drawable_real_apply_buffer; - klass->replace_buffer = gimp_drawable_real_replace_buffer; klass->get_buffer = gimp_drawable_real_get_buffer; klass->set_buffer = gimp_drawable_real_set_buffer; klass->push_undo = gimp_drawable_real_push_undo; @@ -400,6 +423,30 @@ gimp_drawable_get_size (GimpViewable *viewable, 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) { @@ -757,6 +804,19 @@ gimp_drawable_real_estimate_memsize (GimpDrawable *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; +} + /* FIXME: this default impl is currently unused because no subclass * chains up. the goal is to handle the almost identical subclass code * here again. @@ -801,8 +861,9 @@ gimp_drawable_real_set_buffer (GimpDrawable *drawable, gint offset_x, gint offset_y) { - GimpItem *item = GIMP_ITEM (drawable); - gint old_has_alpha = -1; + GimpItem *item = GIMP_ITEM (drawable); + const Babl *old_format = NULL; + gint old_has_alpha = -1; g_object_freeze_notify (G_OBJECT (drawable)); @@ -813,7 +874,10 @@ gimp_drawable_real_set_buffer (GimpDrawable *drawable, drawable, FALSE); if (drawable->private->buffer) - old_has_alpha = gimp_drawable_has_alpha (drawable); + { + old_format = gimp_drawable_get_format (drawable); + old_has_alpha = gimp_drawable_has_alpha (drawable); + } g_set_object (&drawable->private->buffer, buffer); @@ -822,26 +886,14 @@ gimp_drawable_real_set_buffer (GimpDrawable *drawable, "buffer", gimp_drawable_get_buffer (drawable), NULL); - if (drawable->private->convert_format) - { - const Babl *format = gimp_drawable_get_format (drawable); - - if (babl_format_is_palette (format)) - gegl_node_set (drawable->private->convert_format, - "operation", "gegl:convert-format", - "format", gimp_drawable_get_format (drawable), - NULL); - else - gegl_node_set (drawable->private->convert_format, - "operation", "gegl:nop", - NULL); - } - gimp_item_set_offset (item, offset_x, offset_y); gimp_item_set_size (item, gegl_buffer_get_width (buffer), gegl_buffer_get_height (buffer)); + 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); @@ -859,14 +911,29 @@ gimp_drawable_real_push_undo (GimpDrawable *drawable, gint width, gint height) { + GimpImage *image; + if (! buffer) { + GeglBuffer *drawable_buffer = gimp_drawable_get_buffer (drawable); + GeglRectangle drawable_rect; + + gimp_gegl_rectangle_align_to_tile_grid ( + &drawable_rect, + GEGL_RECTANGLE (x, y, width, height), + drawable_buffer); + + 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 ( - gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (x, y, width, height), GEGL_ABYSS_NONE, + drawable_buffer, + &drawable_rect, GEGL_ABYSS_NONE, buffer, GEGL_RECTANGLE (0, 0, 0, 0)); } @@ -875,7 +942,9 @@ gimp_drawable_real_push_undo (GimpDrawable *drawable, g_object_ref (buffer); } - gimp_image_undo_push_drawable (gimp_item_get_image (GIMP_ITEM (drawable)), + image = gimp_item_get_image (GIMP_ITEM (drawable)); + + gimp_image_undo_push_drawable (image, undo_desc, drawable, buffer, x, y); @@ -922,6 +991,18 @@ gimp_drawable_real_get_source_node (GimpDrawable *drawable) 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 */ @@ -1040,11 +1121,11 @@ gimp_drawable_update (GimpDrawable *drawable, } void -gimp_drawable_alpha_changed (GimpDrawable *drawable) +gimp_drawable_update_all (GimpDrawable *drawable) { g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); - g_signal_emit (drawable, gimp_drawable_signals[ALPHA_CHANGED], 0); + GIMP_DRAWABLE_GET_CLASS (drawable)->update_all (drawable); } void @@ -1078,16 +1159,28 @@ gimp_drawable_get_active_components (GimpDrawable *drawable, GimpComponentMask gimp_drawable_get_active_mask (GimpDrawable *drawable) { - GimpDrawableClass *drawable_class; + GimpComponentMask mask; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), 0); - drawable_class = GIMP_DRAWABLE_GET_CLASS (drawable); + mask = GIMP_DRAWABLE_GET_CLASS (drawable)->get_active_mask (drawable); - if (drawable_class->get_active_mask) - return drawable_class->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 0; + return mask; } void @@ -1184,31 +1277,6 @@ gimp_drawable_apply_buffer (GimpDrawable *drawable, base_x, base_y); } -void -gimp_drawable_replace_buffer (GimpDrawable *drawable, - GeglBuffer *buffer, - const GeglRectangle *buffer_region, - gboolean push_undo, - const gchar *undo_desc, - gdouble opacity, - GeglBuffer *mask, - const GeglRectangle *mask_region, - gint x, - gint 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 (GEGL_IS_BUFFER (mask)); - - GIMP_DRAWABLE_GET_CLASS (drawable)->replace_buffer (drawable, buffer, - buffer_region, - push_undo, undo_desc, - opacity, - mask, mask_region, - x, y); -} - GeglBuffer * gimp_drawable_get_buffer (GimpDrawable *drawable) { @@ -1310,11 +1378,10 @@ gimp_drawable_steal_buffer (GimpDrawable *drawable, GeglNode * gimp_drawable_get_source_node (GimpDrawable *drawable) { - const Babl *format; - GeglNode *input; - GeglNode *source; - GeglNode *filter; - GeglNode *output; + GeglNode *input; + GeglNode *source; + GeglNode *filter; + GeglNode *output; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); @@ -1344,27 +1411,10 @@ gimp_drawable_get_source_node (GimpDrawable *drawable) gegl_node_connect_to (source, "output", filter, "input"); - format = gimp_drawable_get_format (drawable); - - if (babl_format_is_palette (format)) - drawable->private->convert_format = - gegl_node_new_child (drawable->private->source_node, - "operation", "gegl:convert-format", - "format", gimp_drawable_get_format (drawable), - NULL); - else - drawable->private->convert_format = - gegl_node_new_child (drawable->private->source_node, - "operation", "gegl:nop", - NULL); - - gegl_node_connect_to (filter, "output", - drawable->private->convert_format, "input"); - output = gegl_node_get_output_proxy (drawable->private->source_node, "output"); - gegl_node_connect_to (drawable->private->convert_format, "output", - output, "input"); + gegl_node_connect_to (filter, "output", + output, "input"); if (gimp_drawable_get_floating_sel (drawable)) _gimp_drawable_add_floating_sel_filter (drawable); diff --git a/app/core/gimpdrawable.h b/app/core/gimpdrawable.h index a36f001c17..4227df098d 100644 --- a/app/core/gimpdrawable.h +++ b/app/core/gimpdrawable.h @@ -50,6 +50,7 @@ struct _GimpDrawableClass gint y, gint width, gint height); + void (* format_changed) (GimpDrawable *drawable); void (* alpha_changed) (GimpDrawable *drawable); /* virtual functions */ @@ -57,6 +58,7 @@ struct _GimpDrawableClass 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); @@ -82,16 +84,6 @@ struct _GimpDrawableClass GeglBuffer *base_buffer, gint base_x, gint base_y); - void (* replace_buffer) (GimpDrawable *drawable, - GeglBuffer *buffer, - const GeglRectangle *buffer_region, - gboolean push_undo, - const gchar *undo_desc, - gdouble opacity, - GeglBuffer *mask, - const GeglRectangle *mask_region, - gint x, - gint y); GeglBuffer * (* get_buffer) (GimpDrawable *drawable); void (* set_buffer) (GimpDrawable *drawable, gboolean push_undo, @@ -135,7 +127,7 @@ void gimp_drawable_update (GimpDrawable *drawable, gint y, gint width, gint height); -void gimp_drawable_alpha_changed (GimpDrawable *drawable); +void gimp_drawable_update_all (GimpDrawable *drawable); void gimp_drawable_invalidate_boundary (GimpDrawable *drawable); void gimp_drawable_get_active_components (GimpDrawable *drawable, @@ -166,16 +158,6 @@ void gimp_drawable_apply_buffer (GimpDrawable *drawable, GeglBuffer *base_buffer, gint base_x, gint base_y); -void gimp_drawable_replace_buffer (GimpDrawable *drawable, - GeglBuffer *buffer, - const GeglRectangle *buffer_region, - gboolean push_undo, - const gchar *undo_desc, - gdouble opacity, - GeglBuffer *mask, - const GeglRectangle *mask_region, - gint x, - gint y); GeglBuffer * gimp_drawable_get_buffer (GimpDrawable *drawable); void gimp_drawable_set_buffer (GimpDrawable *drawable, diff --git a/app/core/gimpdrawablefilter.c b/app/core/gimpdrawablefilter.c index bd0f7e7435..015222f9e4 100644 --- a/app/core/gimpdrawablefilter.c +++ b/app/core/gimpdrawablefilter.c @@ -44,6 +44,7 @@ #include "gimpdrawable-filters.h" #include "gimpdrawablefilter.h" #include "gimpimage.h" +#include "gimplayer.h" #include "gimpmarshal.h" #include "gimpprogress.h" @@ -65,6 +66,8 @@ struct _GimpDrawableFilter gboolean has_input; GimpFilterRegion region; + gboolean crop_enabled; + GeglRectangle crop_rect; gboolean preview_enabled; GimpAlignmentType preview_alignment; gdouble preview_position; @@ -89,37 +92,45 @@ struct _GimpDrawableFilter }; -static void gimp_drawable_filter_dispose (GObject *object); -static void gimp_drawable_filter_finalize (GObject *object); +static void gimp_drawable_filter_dispose (GObject *object); +static void gimp_drawable_filter_finalize (GObject *object); -static void gimp_drawable_filter_sync_region (GimpDrawableFilter *filter); -static void gimp_drawable_filter_sync_preview (GimpDrawableFilter *filter, - gboolean old_enabled, - GimpAlignmentType old_alignment, - gdouble old_position); -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_mask (GimpDrawableFilter *filter); -static void gimp_drawable_filter_sync_transform (GimpDrawableFilter *filter); -static void gimp_drawable_filter_sync_gamma_hack (GimpDrawableFilter *filter); +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_enabled, + GimpAlignmentType old_preview_alignment, + gdouble old_preview_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_filtering (GimpDrawableFilter *filter); -static gboolean gimp_drawable_filter_add_filter (GimpDrawableFilter *filter); -static gboolean gimp_drawable_filter_remove_filter (GimpDrawableFilter *filter); +static gboolean gimp_drawable_filter_is_filtering (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_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_drawable_removed (GimpDrawable *drawable, - GimpDrawableFilter *filter); +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_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) @@ -208,11 +219,14 @@ gimp_drawable_filter_new (GimpDrawable *drawable, 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, TRUE, TRUE); + 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) @@ -287,6 +301,39 @@ gimp_drawable_filter_set_region (GimpDrawableFilter *filter, } } +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_enabled, + filter->preview_alignment, + filter->preview_position, + update); + } +} + void gimp_drawable_filter_set_preview (GimpDrawableFilter *filter, gboolean enabled, @@ -313,9 +360,13 @@ gimp_drawable_filter_set_preview (GimpDrawableFilter *filter, filter->preview_alignment = alignment; filter->preview_position = position; - gimp_drawable_filter_sync_preview (filter, - old_enabled, - old_alignment, old_position); + gimp_drawable_filter_sync_crop (filter, + filter->crop_enabled, + &filter->crop_rect, + old_enabled, + old_alignment, + old_position, + TRUE); } } @@ -422,14 +473,22 @@ gimp_drawable_filter_commit (GimpDrawableFilter *filter, if (gimp_drawable_filter_is_filtering (filter)) { + gimp_drawable_filter_set_preview (filter, FALSE, + filter->preview_alignment, + filter->preview_position); + success = gimp_drawable_merge_filter (filter->drawable, GIMP_FILTER (filter), progress, gimp_object_get_name (filter), - cancellable); + 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); } @@ -505,12 +564,14 @@ gimp_drawable_filter_sync_region (GimpDrawableFilter *filter) } } -static void -gimp_drawable_filter_get_preview_rect (GimpDrawableFilter *filter, - gboolean enabled, - GimpAlignmentType alignment, - gdouble position, - GeglRectangle *rect) +static gboolean +gimp_drawable_filter_get_crop_rect (GimpDrawableFilter *filter, + gboolean crop_enabled, + const GeglRectangle *crop_rect, + gboolean preview_enabled, + GimpAlignmentType preview_alignment, + gdouble preview_position, + GeglRectangle *rect) { gint width; gint height; @@ -523,63 +584,71 @@ gimp_drawable_filter_get_preview_rect (GimpDrawableFilter *filter, width = rect->width; height = rect->height; - if (enabled) + if (preview_enabled) { - switch (alignment) + switch (preview_alignment) { case GIMP_ALIGN_LEFT: - rect->width *= position; + rect->width *= preview_position; break; case GIMP_ALIGN_RIGHT: - rect->width *= (1.0 - position); + rect->width *= (1.0 - preview_position); rect->x = width - rect->width; break; case GIMP_ALIGN_TOP: - rect->height *= position; + rect->height *= preview_position; break; case GIMP_ALIGN_BOTTOM: - rect->height *= (1.0 - position); + rect->height *= (1.0 - preview_position); rect->y = height - rect->height; break; default: - g_return_if_reached (); + g_return_val_if_reached (FALSE); } } + + if (crop_enabled) + gegl_rectangle_intersect (rect, rect, crop_rect); + + return ! gegl_rectangle_equal_coords (rect, 0, 0, width, height); } static void -gimp_drawable_filter_sync_preview (GimpDrawableFilter *filter, - gboolean old_enabled, - GimpAlignmentType old_alignment, - gdouble old_position) +gimp_drawable_filter_sync_crop (GimpDrawableFilter *filter, + gboolean old_crop_enabled, + const GeglRectangle *old_crop_rect, + gboolean old_preview_enabled, + GimpAlignmentType old_preview_alignment, + gdouble old_preview_position, + gboolean update) { GeglRectangle old_rect; GeglRectangle new_rect; + gboolean enabled; - gimp_drawable_filter_get_preview_rect (filter, - old_enabled, - old_alignment, - old_position, - &old_rect); + gimp_drawable_filter_get_crop_rect (filter, + old_crop_enabled, + old_crop_rect, + old_preview_enabled, + old_preview_alignment, + old_preview_position, + &old_rect); - gimp_drawable_filter_get_preview_rect (filter, - filter->preview_enabled, - filter->preview_alignment, - filter->preview_position, - &new_rect); + enabled = gimp_drawable_filter_get_crop_rect (filter, + filter->crop_enabled, + &filter->crop_rect, + filter->preview_enabled, + filter->preview_alignment, + filter->preview_position, + &new_rect); - gimp_applicator_set_preview (filter->applicator, - filter->preview_enabled, - &new_rect); + gimp_applicator_set_crop (filter->applicator, enabled ? &new_rect : NULL); - if (old_rect.x != new_rect.x || - old_rect.y != new_rect.y || - old_rect.width != new_rect.width || - old_rect.height != new_rect.height) + if (update && ! gegl_rectangle_equal (&old_rect, &new_rect)) { cairo_region_t *region; gint n_rects; @@ -637,18 +706,17 @@ gimp_drawable_filter_sync_mode (GimpDrawableFilter *filter) static void gimp_drawable_filter_sync_affect (GimpDrawableFilter *filter) { - GimpComponentMask active_mask; + gimp_applicator_set_affect ( + filter->applicator, + gimp_drawable_get_active_mask (filter->drawable)); +} - active_mask = gimp_drawable_get_active_mask (filter->drawable); - - /* don't let the filter affect the drawable projection's alpha, - * because it can't affect the drawable buffer's alpha either when - * finally merged (see bug #699279) - */ - if (! gimp_drawable_has_alpha (filter->drawable)) - active_mask &= ~GIMP_COMPONENT_MASK_ALPHA; - - gimp_applicator_set_affect (filter->applicator, active_mask); +static void +gimp_drawable_filter_sync_format (GimpDrawableFilter *filter) +{ + gimp_applicator_set_output_format ( + filter->applicator, + gimp_drawable_get_format (filter->drawable)); } static void @@ -847,13 +915,17 @@ gimp_drawable_filter_add_filter (GimpDrawableFilter *filter) gimp_drawable_filter_sync_mask (filter); gimp_drawable_filter_sync_region (filter); - gimp_drawable_filter_sync_preview (filter, - filter->preview_enabled, - filter->preview_alignment, - filter->preview_position); + gimp_drawable_filter_sync_crop (filter, + filter->crop_enabled, + &filter->crop_rect, + filter->preview_enabled, + filter->preview_alignment, + filter->preview_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); @@ -869,10 +941,20 @@ gimp_drawable_filter_add_filter (GimpDrawableFilter *filter) g_signal_connect (image, "profile-changed", G_CALLBACK (gimp_drawable_filter_profile_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; } @@ -886,9 +968,19 @@ gimp_drawable_filter_remove_filter (GimpDrawableFilter *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 (image, gimp_drawable_filter_profile_changed, filter); @@ -918,25 +1010,27 @@ gimp_drawable_filter_update_drawable (GimpDrawableFilter *filter, if (area) { - if (! gimp_rectangle_intersect (area->x, - area->y, - area->width, - area->height, - filter->filter_area.x, - filter->filter_area.y, - filter->filter_area.width, - filter->filter_area.height, - &update_area.x, - &update_area.y, - &update_area.width, - &update_area.height)) + if (! gegl_rectangle_intersect (&update_area, + area, &filter->filter_area)) { return; } } else { - update_area = filter->filter_area; + gimp_drawable_filter_get_crop_rect (filter, + filter->crop_enabled, + &filter->crop_rect, + filter->preview_enabled, + filter->preview_alignment, + filter->preview_position, + &update_area); + + if (! gegl_rectangle_intersect (&update_area, + &update_area, &filter->filter_area)) + { + return; + } } if (update_area.width > 0 && @@ -981,9 +1075,25 @@ gimp_drawable_filter_profile_changed (GimpColorManaged *managed, 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 index bb633322b4..cc3db76b43 100644 --- a/app/core/gimpdrawablefilter.h +++ b/app/core/gimpdrawablefilter.h @@ -57,6 +57,9 @@ GimpDrawableFilter * 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, GimpAlignmentType alignment, diff --git a/app/core/gimpdrawableundo.c b/app/core/gimpdrawableundo.c index f2bf16da54..78b8f3902a 100644 --- a/app/core/gimpdrawableundo.c +++ b/app/core/gimpdrawableundo.c @@ -202,7 +202,6 @@ gimp_drawable_undo_free (GimpUndo *undo, GimpDrawableUndo *drawable_undo = GIMP_DRAWABLE_UNDO (undo); g_clear_object (&drawable_undo->buffer); - g_clear_object (&drawable_undo->applied_buffer); GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); } diff --git a/app/core/gimpdrawableundo.h b/app/core/gimpdrawableundo.h index d87fd1f4c4..5d7269eb7d 100644 --- a/app/core/gimpdrawableundo.h +++ b/app/core/gimpdrawableundo.h @@ -40,14 +40,6 @@ struct _GimpDrawableUndo GeglBuffer *buffer; gint x; gint y; - - /* stuff for "Fade" */ - GeglBuffer *applied_buffer; - GimpLayerMode paint_mode; - GimpLayerColorSpace blend_space; - GimpLayerColorSpace composite_space; - GimpLayerCompositeMode composite_mode; - gdouble opacity; }; struct _GimpDrawableUndoClass diff --git a/app/core/gimpfilloptions.c b/app/core/gimpfilloptions.c index fb42509da0..d10b6e4572 100644 --- a/app/core/gimpfilloptions.c +++ b/app/core/gimpfilloptions.c @@ -29,6 +29,8 @@ #include "core-types.h" +#include "operations/layer-modes/gimp-layer-modes.h" + #include "gimp.h" #include "gimp-palettes.h" #include "gimpdrawable.h" @@ -45,6 +47,8 @@ enum PROP_0, PROP_STYLE, PROP_ANTIALIAS, + PROP_FEATHER, + PROP_FEATHER_RADIUS, PROP_PATTERN_VIEW_TYPE, PROP_PATTERN_VIEW_SIZE }; @@ -56,6 +60,8 @@ struct _GimpFillOptionsPrivate { GimpFillStyle style; gboolean antialias; + gboolean feather; + gdouble feather_radius; GimpViewType pattern_view_type; GimpViewSize pattern_view_size; @@ -112,6 +118,20 @@ gimp_fill_options_class_init (GimpFillOptionsClass *klass) 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, @@ -158,6 +178,12 @@ gimp_fill_options_set_property (GObject *object, 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); @@ -188,6 +214,12 @@ gimp_fill_options_get_property (GObject *object, 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); @@ -275,6 +307,29 @@ gimp_fill_options_set_antialias (GimpFillOptions *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, @@ -404,6 +459,25 @@ gimp_fill_options_get_undo_desc (GimpFillOptions *options) 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, @@ -411,7 +485,7 @@ gimp_fill_options_create_buffer (GimpFillOptions *options, gint pattern_offset_x, gint pattern_offset_y) { - GeglBuffer *buffer; + GeglBuffer *buffer; g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); g_return_val_if_fail (gimp_fill_options_get_style (options) != @@ -422,7 +496,27 @@ gimp_fill_options_create_buffer (GimpFillOptions *options, g_return_val_if_fail (rect != NULL, NULL); buffer = gegl_buffer_new (rect, - gimp_drawable_get_format_with_alpha (drawable)); + 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)) { @@ -451,6 +545,4 @@ gimp_fill_options_create_buffer (GimpFillOptions *options, } break; } - - return buffer; } diff --git a/app/core/gimpfilloptions.h b/app/core/gimpfilloptions.h index f4277c5466..434530be0e 100644 --- a/app/core/gimpfilloptions.h +++ b/app/core/gimpfilloptions.h @@ -60,6 +60,12 @@ gboolean gimp_fill_options_get_antialias (GimpFillOptions *optio 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, @@ -71,11 +77,19 @@ gboolean gimp_fill_options_set_by_fill_mode (GimpFillOptions *optio 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/gimpgradient-save.c b/app/core/gimpgradient-save.c index 9cd9423a95..510f2d68db 100644 --- a/app/core/gimpgradient-save.c +++ b/app/core/gimpgradient-save.c @@ -207,13 +207,21 @@ gimp_gradient_save_pov (GimpGradient *gradient, 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; } diff --git a/app/core/gimpgradient.c b/app/core/gimpgradient.c index b64a19a6a5..8265225c85 100644 --- a/app/core/gimpgradient.c +++ b/app/core/gimpgradient.c @@ -70,7 +70,7 @@ static gint gimp_gradient_compare (GimpData *data1 static gchar * gimp_gradient_get_checksum (GimpTagged *tagged); -static GimpGradientSegment * +static inline GimpGradientSegment * gimp_gradient_get_segment_at_internal (GimpGradient *gradient, GimpGradientSegment *seg, gdouble pos); @@ -460,7 +460,8 @@ gimp_gradient_get_color_at (GimpGradient *gradient, GimpRGB right_color; GimpRGB rgb; - g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); + /* 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); @@ -516,11 +517,19 @@ gimp_gradient_get_color_at (GimpGradient *gradient, /* Get left/right colors */ - gimp_gradient_segment_get_left_flat_color (gradient, - context, seg, &left_color); + 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); + 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 */ @@ -2160,7 +2169,7 @@ gimp_gradient_segment_range_move (GimpGradient *gradient, /* private functions */ -static GimpGradientSegment * +static inline GimpGradientSegment * gimp_gradient_get_segment_at_internal (GimpGradient *gradient, GimpGradientSegment *seg, gdouble pos) diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c index 8dfd386101..471fb157c8 100644 --- a/app/core/gimpgrouplayer.c +++ b/app/core/gimpgrouplayer.c @@ -124,6 +124,7 @@ static gint64 gimp_group_layer_estimate_memsize (GimpDrawable *drawabl 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, @@ -285,6 +286,7 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass) 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; @@ -455,6 +457,7 @@ gimp_group_layer_get_size (GimpViewable *viewable, gint *height) { GimpGroupLayerPrivate *private = GET_PRIVATE (viewable); + gboolean result; if (private->reallocate_width != 0 && private->reallocate_height != 0) @@ -465,13 +468,18 @@ gimp_group_layer_get_size (GimpViewable *viewable, return TRUE; } - /* return the size only if there are children... */ - if (gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children))) - return GIMP_VIEWABLE_CLASS (parent_class)->get_size (viewable, + result = GIMP_VIEWABLE_CLASS (parent_class)->get_size (viewable, width, height); - /* ...otherwise return "no content" */ - return FALSE; + /* if the group is empty, return "no content" through + * gimp_viewable_get_size(), but make sure to set *width and *height anyway, + * so that the correct size is reported to the projection through + * gimp_projectable_get_size(). see issue #3134. + */ + if (gimp_container_is_empty (private->children)) + result = FALSE; + + return result; } static GimpContainer * @@ -739,6 +747,29 @@ gimp_group_layer_estimate_memsize (GimpDrawable *drawable, 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, @@ -758,6 +789,16 @@ gimp_group_layer_translate (GimpLayer *layer, /* redirect stack updates to the drawable, rather than to the projection */ private->direct_update++; + /* if this is a nested group layer, we need to update the child-layers' + * original area *before* updating the group's offset. once we update the + * group's offset, our parent's size will also be updated, and the + * subsequent area-updates of the child layers during translation will be + * clipped to the updated parent bounds, potentially failing to update their + * original area. see issue #3484. + */ + if (gimp_viewable_get_depth (GIMP_VIEWABLE (group)) > 0) + gimp_drawable_update_all (GIMP_DRAWABLE (group)); + gimp_item_get_offset (GIMP_ITEM (group), &x, &y); x += offset_x; diff --git a/app/core/gimphistogram.c b/app/core/gimphistogram.c index f9c5c4ceaf..0abe467412 100644 --- a/app/core/gimphistogram.c +++ b/app/core/gimphistogram.c @@ -17,8 +17,6 @@ * along with this program. If not, see . */ -#define GEGL_ITERATOR2_API - #include "config.h" #include @@ -33,6 +31,7 @@ #include "gegl/gimp-babl.h" #include "gegl/gimp-gegl-loops.h" +#include "gegl/gimp-gegl-utils.h" #include "gimp-atomic.h" #include "gimp-parallel.h" @@ -41,8 +40,8 @@ #include "gimpwaitable.h" -#define MIN_PARALLEL_SUB_SIZE 64 -#define MIN_PARALLEL_SUB_AREA (MIN_PARALLEL_SUB_SIZE * MIN_PARALLEL_SUB_SIZE) +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) enum @@ -313,6 +312,7 @@ gimp_histogram_calculate_async (GimpHistogram *histogram, 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); @@ -321,14 +321,16 @@ gimp_histogram_calculate_async (GimpHistogram *histogram, if (histogram->priv->calculate_async) gimp_async_cancel_and_wait (histogram->priv->calculate_async); + gimp_gegl_rectangle_align_to_tile_grid (&rect, buffer_rect, buffer); + context = g_slice_new0 (CalculateContext); context->histogram = histogram; - context->buffer = gegl_buffer_new (buffer_rect, + context->buffer = gegl_buffer_new (&rect, gegl_buffer_get_format (buffer)); context->buffer_rect = *buffer_rect; - gimp_gegl_buffer_copy (buffer, buffer_rect, GEGL_ABYSS_NONE, + gimp_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE, context->buffer, NULL); if (mask) @@ -338,10 +340,11 @@ gimp_histogram_calculate_async (GimpHistogram *histogram, else context->mask_rect = *gegl_buffer_get_extent (mask); - context->mask = gegl_buffer_new (&context->mask_rect, - gegl_buffer_get_format (mask)); + gimp_gegl_rectangle_align_to_tile_grid (&rect, &context->mask_rect, mask); - gimp_gegl_buffer_copy (mask, &context->mask_rect, GEGL_ABYSS_NONE, + context->mask = gegl_buffer_new (&rect, gegl_buffer_get_format (mask)); + + gimp_gegl_buffer_copy (mask, &rect, GEGL_ABYSS_NONE, context->mask, NULL); } @@ -367,8 +370,7 @@ gimp_histogram_clear_values (GimpHistogram *histogram) if (histogram->priv->values) { - g_free (histogram->priv->values); - histogram->priv->values = NULL; + g_clear_pointer (&histogram->priv->values, g_free); g_object_notify (G_OBJECT (histogram), "values"); } @@ -946,9 +948,9 @@ gimp_histogram_calculate_internal (GimpAsync *async, data.format = format; data.values_list = NULL; - gimp_parallel_distribute_area ( - &context->buffer_rect, MIN_PARALLEL_SUB_AREA, - (GimpParallelDistributeAreaFunc) gimp_histogram_calculate_area, + 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)) diff --git a/app/core/gimpimage-color-profile.c b/app/core/gimpimage-color-profile.c index b004cadbaf..ab8750cbe4 100644 --- a/app/core/gimpimage-color-profile.c +++ b/app/core/gimpimage-color-profile.c @@ -159,11 +159,11 @@ gimp_image_set_icc_parasite (GimpImage *image, g_return_if_fail (gimp_image_validate_icc_parasite (image, icc_parasite, NULL, NULL) == TRUE); - gimp_image_parasite_attach (image, icc_parasite); + gimp_image_parasite_attach (image, icc_parasite, TRUE); } else { - gimp_image_parasite_detach (image, GIMP_ICC_PROFILE_PARASITE_NAME); + gimp_image_parasite_detach (image, GIMP_ICC_PROFILE_PARASITE_NAME, TRUE); } } @@ -421,7 +421,7 @@ gimp_image_convert_color_profile (GimpImage *image, 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"); + gimp_image_parasite_detach (image, "icc-profile-name", TRUE); gimp_image_undo_group_end (image); diff --git a/app/core/gimpimage-colormap.c b/app/core/gimpimage-colormap.c index 8dd666af7f..8bf40a923a 100644 --- a/app/core/gimpimage-colormap.c +++ b/app/core/gimpimage-colormap.c @@ -41,8 +41,9 @@ /* local function prototype */ -static void gimp_image_colormap_set_palette_entry (GimpImage *image, - gint index); +static void gimp_image_colormap_set_palette_entry (GimpImage *image, + const GimpRGB *color, + gint index); /* public functions */ @@ -213,7 +214,7 @@ gimp_image_set_colormap (GimpImage *image, gimp_palette_delete_entry (private->palette, entry); for (i = 0; i < private->n_colors; i++) - gimp_image_colormap_set_palette_entry (image, i); + gimp_image_colormap_set_palette_entry (image, NULL, i); gimp_data_thaw (GIMP_DATA (private->palette)); @@ -293,7 +294,7 @@ gimp_image_set_colormap_entry (GimpImage *image, &private->colormap[color_index * 3 + 2]); if (private->palette) - gimp_image_colormap_set_palette_entry (image, color_index); + gimp_image_colormap_set_palette_entry (image, color, color_index); gimp_image_colormap_changed (image, color_index); } @@ -323,7 +324,7 @@ gimp_image_add_colormap_entry (GimpImage *image, private->n_colors++; if (private->palette) - gimp_image_colormap_set_palette_entry (image, private->n_colors - 1); + gimp_image_colormap_set_palette_entry (image, color, private->n_colors - 1); gimp_image_colormap_changed (image, -1); } @@ -332,18 +333,25 @@ gimp_image_add_colormap_entry (GimpImage *image, /* private functions */ static void -gimp_image_colormap_set_palette_entry (GimpImage *image, - gint index) +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]; - gimp_rgba_set_uchar (&color, - private->colormap[3 * index + 0], - private->colormap[3 * index + 1], - private->colormap[3 * index + 2], - 255); + /* 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); diff --git a/app/core/gimpimage-convert-indexed.c b/app/core/gimpimage-convert-indexed.c index 216b02d9e5..4c9c4a0c63 100644 --- a/app/core/gimpimage-convert-indexed.c +++ b/app/core/gimpimage-convert-indexed.c @@ -136,7 +136,6 @@ #include #include -#define GEGL_ITERATOR2_API #include #include "libgimpcolor/gimpcolor.h" @@ -3595,7 +3594,7 @@ init_error_limit (const int error_freedom) /* Allocate and fill in the error_limiter table */ { gint *table; - gint in, out; + gint inp, out; /* #define STEPSIZE 16 */ /* #define STEPSIZE 200 */ @@ -3609,16 +3608,16 @@ init_error_limit (const int error_freedom) const gint STEPSIZE = 190; - for (in = 0; in < STEPSIZE; in++) + for (inp = 0; inp < STEPSIZE; inp++) { - table[in] = in; - table[-in] = -in; + table[inp] = inp; + table[-inp] = -inp; } - for (; in <= 255; in++) + for (; inp <= 255; inp++) { - table[in] = STEPSIZE; - table[-in] = -STEPSIZE; + table[inp] = STEPSIZE; + table[-inp] = -STEPSIZE; } return (table); @@ -3631,24 +3630,24 @@ init_error_limit (const int error_freedom) /* Map errors 1:1 up to +- STEPSIZE */ out = 0; - for (in = 0; in < STEPSIZE; in++, out++) + for (inp = 0; inp < STEPSIZE; inp++, out++) { - table[in] = out; - table[-in] = -out; + table[inp] = out; + table[-inp] = -out; } /* Map errors 1:2 up to +- 3*STEPSIZE */ - for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) + for (; inp < STEPSIZE*3; inp++, out += (inp&1) ? 0 : 1) { - table[in] = out; - table[-in] = -out; + table[inp] = out; + table[-inp] = -out; } /* Clamp the rest to final out value (which is STEPSIZE*2) */ - for (; in <= 255; in++) + for (; inp <= 255; inp++) { - table[in] = out; - table[-in] = -out; + table[inp] = out; + table[-inp] = -out; } return table; diff --git a/app/core/gimpimage-fade.c b/app/core/gimpimage-fade.c deleted file mode 100644 index 6fd60ea749..0000000000 --- a/app/core/gimpimage-fade.c +++ /dev/null @@ -1,81 +0,0 @@ -/* GIMP - The GNU Image Manipulation Program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You 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 "gimpcontext.h" -#include "gimpdrawable.h" -#include "gimpdrawableundo.h" -#include "gimpimage.h" -#include "gimpimage-fade.h" -#include "gimpimage-undo.h" - - -/* public functions */ - -gboolean -gimp_image_fade (GimpImage *image, - GimpContext *context) -{ - GimpDrawableUndo *undo; - - g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); - g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE); - - undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); - - if (undo && undo->applied_buffer) - { - GimpDrawable *drawable; - GeglBuffer *buffer; - - drawable = GIMP_DRAWABLE (GIMP_ITEM_UNDO (undo)->item); - - g_object_ref (undo); - buffer = g_object_ref (undo->applied_buffer); - - gimp_image_undo (image); - - gimp_drawable_apply_buffer (drawable, buffer, - GEGL_RECTANGLE (0, 0, - gegl_buffer_get_width (undo->buffer), - gegl_buffer_get_height (undo->buffer)), - TRUE, - gimp_object_get_name (undo), - 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, undo->x, undo->y); - - g_object_unref (buffer); - g_object_unref (undo); - - return TRUE; - } - - return FALSE; -} diff --git a/app/core/gimpimage-merge.c b/app/core/gimpimage-merge.c index 5c7d2bace3..2b35e9b199 100644 --- a/app/core/gimpimage-merge.c +++ b/app/core/gimpimage-merge.c @@ -48,6 +48,7 @@ #include "gimpmarshal.h" #include "gimpparasitelist.h" #include "gimppickable.h" +#include "gimpprogress.h" #include "gimpprojectable.h" #include "gimpundostack.h" @@ -58,7 +59,9 @@ static GimpLayer * gimp_image_merge_layers (GimpImage *image, GimpContainer *container, GSList *merge_list, GimpContext *context, - GimpMergeType merge_type); + GimpMergeType merge_type, + const gchar *undo_desc, + GimpProgress *progress); /* public functions */ @@ -68,7 +71,8 @@ gimp_image_merge_visible_layers (GimpImage *image, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible) + gboolean discard_invisible, + GimpProgress *progress) { GimpContainer *container; GList *list; @@ -77,6 +81,7 @@ gimp_image_merge_visible_layers (GimpImage *image, 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) { @@ -126,13 +131,14 @@ gimp_image_merge_visible_layers (GimpImage *image, if (merge_list) { - GimpLayer *layer; + 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, - C_("undo-type", "Merge Visible Layers")); + undo_desc); /* if there's a floating selection, anchor it */ if (gimp_image_get_floating_selection (image)) @@ -140,7 +146,8 @@ gimp_image_merge_visible_layers (GimpImage *image, layer = gimp_image_merge_layers (image, container, - merge_list, context, merge_type); + merge_list, context, merge_type, + undo_desc, progress); g_slist_free (merge_list); if (invisible_list) @@ -164,9 +171,10 @@ gimp_image_merge_visible_layers (GimpImage *image, } GimpLayer * -gimp_image_flatten (GimpImage *image, - GimpContext *context, - GError **error) +gimp_image_flatten (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + GError **error) { GList *list; GSList *merge_list = NULL; @@ -174,6 +182,7 @@ gimp_image_flatten (GimpImage *image, 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); @@ -191,11 +200,13 @@ gimp_image_flatten (GimpImage *image, 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, - C_("undo-type", "Flatten Image")); + undo_desc); /* if there's a floating selection, anchor it */ if (gimp_image_get_floating_selection (image)) @@ -204,7 +215,8 @@ gimp_image_flatten (GimpImage *image, layer = gimp_image_merge_layers (image, gimp_image_get_layers (image), merge_list, context, - GIMP_FLATTEN_IMAGE); + GIMP_FLATTEN_IMAGE, + undo_desc, progress); g_slist_free (merge_list); gimp_image_alpha_changed (image); @@ -226,17 +238,20 @@ 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; + 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)) @@ -299,15 +314,18 @@ gimp_image_merge_down (GimpImage *image, 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, - C_("undo-type", "Merge Down")); + undo_desc); layer = gimp_image_merge_layers (image, gimp_item_get_container (GIMP_ITEM (current_layer)), - merge_list, context, merge_type); + merge_list, context, merge_type, + undo_desc, progress); g_slist_free (merge_list); gimp_image_undo_group_end (image); @@ -457,7 +475,9 @@ gimp_image_merge_layers (GimpImage *image, GimpContainer *container, GSList *merge_list, GimpContext *context, - GimpMergeType merge_type) + GimpMergeType merge_type, + const gchar *undo_desc, + GimpProgress *progress) { GimpLayer *parent; gint x1, y1; @@ -478,6 +498,7 @@ gimp_image_merge_layers (GimpImage *image, 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); @@ -658,9 +679,10 @@ gimp_image_merge_layers (GimpImage *image, gegl_node_disconnect (last_node, "input"); /* Render the graph into the merge layer */ - gegl_node_blit_buffer (offset_node, - gimp_drawable_get_buffer (GIMP_DRAWABLE (merge_layer)), - NULL, 0, GEGL_ABYSS_NONE); + 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) diff --git a/app/core/gimpimage-merge.h b/app/core/gimpimage-merge.h index 4b7a916561..93aab4e53a 100644 --- a/app/core/gimpimage-merge.h +++ b/app/core/gimpimage-merge.h @@ -23,17 +23,20 @@ GimpLayer * gimp_image_merge_visible_layers (GimpImage *image, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible); + 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, diff --git a/app/core/gimpimage-new.c b/app/core/gimpimage-new.c index 02c0fa22cc..4d0fa7baa0 100644 --- a/app/core/gimpimage-new.c +++ b/app/core/gimpimage-new.c @@ -120,7 +120,7 @@ gimp_image_new_from_template (Gimp *gimp, GIMP_PARASITE_PERSISTENT, strlen (comment) + 1, comment); - gimp_image_parasite_attach (image, parasite); + gimp_image_parasite_attach (image, parasite, FALSE); gimp_parasite_free (parasite); } diff --git a/app/core/gimpimage-pick-color.c b/app/core/gimpimage-pick-color.c index c51fdb6022..3154712f1b 100644 --- a/app/core/gimpimage-pick-color.c +++ b/app/core/gimpimage-pick-color.c @@ -22,9 +22,11 @@ #include "core-types.h" +#include "gimpchannel.h" #include "gimpdrawable.h" #include "gimpimage.h" #include "gimpimage-pick-color.h" +#include "gimplayer.h" #include "gimppickable.h" @@ -48,6 +50,25 @@ gimp_image_pick_color (GimpImage *image, 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) diff --git a/app/core/gimpimage-pick-item.c b/app/core/gimpimage-pick-item.c index db196bcdd9..b2257f37ab 100644 --- a/app/core/gimpimage-pick-item.c +++ b/app/core/gimpimage-pick-item.c @@ -41,29 +41,55 @@ GimpLayer * gimp_image_pick_layer (GimpImage *image, gint x, - gint y) + 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); - for (list = all_layers; list; list = g_list_next (list)) + if (previously_picked) { - GimpLayer *layer = list->data; - gint off_x, off_y; + 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++; + } - 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) + while (tries) + { + for (list = all_layers; list; list = g_list_next (list)) { - g_list_free (all_layers); + GimpLayer *layer = list->data; - return layer; + 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); @@ -209,20 +235,18 @@ gimp_image_pick_vectors (GimpImage *image, return ret; } -GimpGuide * -gimp_image_pick_guide (GimpImage *image, - gdouble x, - gdouble y, - gdouble epsilon_x, - gdouble epsilon_y) +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; - g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); - g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL); - for (list = GIMP_IMAGE_GET_PRIVATE (image)->guides; list; list = g_list_next (list)) @@ -234,33 +258,82 @@ gimp_image_pick_guide (GimpImage *image, switch (gimp_guide_get_orientation (guide)) { case GIMP_ORIENTATION_HORIZONTAL: - dist = ABS (position - y); - if (dist < MIN (epsilon_y, mindist)) + if (orientation == GIMP_ORIENTATION_HORIZONTAL || + orientation == GIMP_ORIENTATION_UNKNOWN) { - mindist = dist; - ret = guide; + 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: - dist = ABS (position - x); - if (dist < MIN (epsilon_x, mindist / epsilon_y * epsilon_x)) + if (orientation == GIMP_ORIENTATION_VERTICAL || + orientation == GIMP_ORIENTATION_UNKNOWN) { - mindist = dist * epsilon_y / epsilon_x; - ret = guide; + 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, diff --git a/app/core/gimpimage-pick-item.h b/app/core/gimpimage-pick-item.h index e0c62bf488..46da4ef1ad 100644 --- a/app/core/gimpimage-pick-item.h +++ b/app/core/gimpimage-pick-item.h @@ -21,7 +21,8 @@ GimpLayer * gimp_image_pick_layer (GimpImage *image, gint x, - gint y); + gint y, + GimpLayer *previously_picked); GimpLayer * gimp_image_pick_layer_by_bounds (GimpImage *image, gint x, gint y); @@ -40,6 +41,12 @@ GimpGuide * gimp_image_pick_guide (GimpImage *image, 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, diff --git a/app/core/gimpimage-undo.c b/app/core/gimpimage-undo.c index 6d7b87d4e3..8f3ea4b906 100644 --- a/app/core/gimpimage-undo.c +++ b/app/core/gimpimage-undo.c @@ -26,7 +26,6 @@ #include "gimp.h" #include "gimp-utils.h" -#include "gimpdrawableundo.h" #include "gimpimage.h" #include "gimpimage-private.h" #include "gimpimage-undo.h" @@ -461,34 +460,6 @@ gimp_image_undo_can_compress (GimpImage *image, return NULL; } -GimpUndo * -gimp_image_undo_get_fadeable (GimpImage *image) -{ - GimpImagePrivate *private; - GimpUndo *undo; - - g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); - - private = GIMP_IMAGE_GET_PRIVATE (image); - - undo = gimp_undo_stack_peek (private->undo_stack); - - if (GIMP_IS_UNDO_STACK (undo) && undo->undo_type == GIMP_UNDO_GROUP_PAINT) - { - GimpUndoStack *stack = GIMP_UNDO_STACK (undo); - - if (gimp_undo_stack_get_depth (stack) == 2) - { - undo = gimp_undo_stack_peek (stack); - } - } - - if (GIMP_IS_DRAWABLE_UNDO (undo)) - return undo; - - return NULL; -} - /* private functions */ diff --git a/app/core/gimpimage-undo.h b/app/core/gimpimage-undo.h index 51fe3ba075..34bd2f4ce1 100644 --- a/app/core/gimpimage-undo.h +++ b/app/core/gimpimage-undo.h @@ -53,7 +53,5 @@ GimpUndo * gimp_image_undo_can_compress (GimpImage *image, GType object_type, GimpUndoType undo_type); -GimpUndo * gimp_image_undo_get_fadeable (GimpImage *image); - #endif /* __GIMP_IMAGE__UNDO_H__ */ diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c index 167f5e2646..97b6be0c69 100644 --- a/app/core/gimpimage.c +++ b/app/core/gimpimage.c @@ -1034,12 +1034,9 @@ gimp_image_dispose (GObject *object) gimp_image_channel_remove, image); - gimp_container_foreach (private->layers->container, - (GFunc) gimp_item_removed, NULL); - gimp_container_foreach (private->channels->container, - (GFunc) gimp_item_removed, NULL); - gimp_container_foreach (private->vectors->container, - (GFunc) gimp_item_removed, NULL); + 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); } @@ -1406,6 +1403,7 @@ gimp_image_color_managed_profile_changed (GimpColorManaged *managed) GimpImage *image = GIMP_IMAGE (managed); GimpItemStack *layers = GIMP_ITEM_STACK (gimp_image_get_layers (image)); + gimp_projectable_structure_changed (GIMP_PROJECTABLE (image)); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (image)); gimp_item_stack_profile_changed (layers); } @@ -1569,6 +1567,7 @@ gimp_image_get_graph (GimpProjectable *projectable) gegl_node_new_child (private->graph, "operation", "gimp:mask-components", "mask", mask, + "alpha", 1.0, NULL); gegl_node_connect_to (layers_node, "output", @@ -3596,7 +3595,8 @@ gimp_image_parasite_validate (GimpImage *image, void gimp_image_parasite_attach (GimpImage *image, - const GimpParasite *parasite) + const GimpParasite *parasite, + gboolean push_undo) { GimpImagePrivate *private; GimpParasite copy; @@ -3622,7 +3622,13 @@ gimp_image_parasite_attach (GimpImage *image, builtin = gimp_image_get_builtin_color_profile (image); if (gimp_color_profile_is_equal (profile, builtin)) - gimp_image_parasite_detach (image, GIMP_ICC_PROFILE_PARASITE_NAME); + { + /* 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); } @@ -3635,7 +3641,7 @@ gimp_image_parasite_attach (GimpImage *image, /* 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 (©)) + if (push_undo && gimp_parasite_is_undoable (©)) gimp_image_undo_push_image_parasite (image, C_("undo-type", "Attach Parasite to Image"), ©); @@ -3648,22 +3654,23 @@ gimp_image_parasite_attach (GimpImage *image, */ gimp_parasite_list_add (private->parasites, ©); - if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT)) + if (push_undo && gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT)) { gimp_parasite_shift_parent (©); gimp_parasite_attach (image->gimp, ©); } - g_signal_emit (image, gimp_image_signals[PARASITE_ATTACHED], 0, - name); - 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) + const gchar *name, + gboolean push_undo) { GimpImagePrivate *private; const GimpParasite *parasite; @@ -3676,18 +3683,18 @@ gimp_image_parasite_detach (GimpImage *image, if (! (parasite = gimp_parasite_list_find (private->parasites, name))) return; - if (gimp_parasite_is_undoable (parasite)) + 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); - g_signal_emit (image, gimp_image_signals[PARASITE_DETACHED], 0, - 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); } diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h index 26daa9453c..35ffd87a41 100644 --- a/app/core/gimpimage.h +++ b/app/core/gimpimage.h @@ -315,9 +315,11 @@ gboolean gimp_image_parasite_validate (GimpImage *image, const GimpParasite *parasite, GError **error); void gimp_image_parasite_attach (GimpImage *image, - const GimpParasite *parasite); + const GimpParasite *parasite, + gboolean push_undo); void gimp_image_parasite_detach (GimpImage *image, - const gchar *name); + const gchar *name, + gboolean push_undo); /* tattoos */ diff --git a/app/core/gimpimageundo.c b/app/core/gimpimageundo.c index 9103f53c74..8aa9e111f5 100644 --- a/app/core/gimpimageundo.c +++ b/app/core/gimpimageundo.c @@ -39,7 +39,6 @@ #include "gimpimage-metadata.h" #include "gimpimage-private.h" #include "gimpimageundo.h" -#include "gimpparasitelist.h" enum @@ -501,21 +500,14 @@ gimp_image_undo_pop (GimpUndo *undo, case GIMP_UNDO_PARASITE_REMOVE: { GimpParasite *parasite = image_undo->parasite; - const gchar *name; image_undo->parasite = gimp_parasite_copy (gimp_image_parasite_find (image, image_undo->parasite_name)); if (parasite) - gimp_parasite_list_add (private->parasites, parasite); + gimp_image_parasite_attach (image, parasite, FALSE); else - gimp_parasite_list_remove (private->parasites, - image_undo->parasite_name); - - name = parasite ? parasite->name : image_undo->parasite_name; - - if (strcmp (name, GIMP_ICC_PROFILE_PARASITE_NAME) == 0) - _gimp_image_update_color_profile (image, parasite); + gimp_image_parasite_detach (image, image_undo->parasite_name, FALSE); if (parasite) gimp_parasite_free (parasite); diff --git a/app/core/gimpitem.c b/app/core/gimpitem.c index d7856ef071..1cc0992ad8 100644 --- a/app/core/gimpitem.c +++ b/app/core/gimpitem.c @@ -2133,7 +2133,7 @@ gimp_item_parasite_attach (GimpItem *item, if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT)) { gimp_parasite_shift_parent (©); - gimp_image_parasite_attach (private->image, ©); + gimp_image_parasite_attach (private->image, ©, TRUE); } else if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_GRANDPARENT)) { diff --git a/app/core/gimpitemtree.c b/app/core/gimpitemtree.c index 247f3b8648..7746afa08c 100644 --- a/app/core/gimpitemtree.c +++ b/app/core/gimpitemtree.c @@ -66,6 +66,7 @@ struct _GimpItemTreePrivate /* 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, @@ -96,6 +97,7 @@ gimp_item_tree_class_init (GimpItemTreeClass *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; @@ -158,6 +160,23 @@ gimp_item_tree_constructed (GObject *object) 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) { diff --git a/app/core/gimplayer-floating-selection.c b/app/core/gimplayer-floating-selection.c index 4b4797f4fb..84d1fea0a3 100644 --- a/app/core/gimplayer-floating-selection.c +++ b/app/core/gimplayer-floating-selection.c @@ -132,19 +132,12 @@ floating_sel_anchor (GimpLayer *layer) NULL, NULL, NULL, NULL)) { filter = gimp_drawable_get_floating_sel_filter (drawable); - g_object_ref (filter); } - /* first remove the filter, then merge it, or we will get warnings - * about already connected nodes - */ - gimp_image_remove_layer (image, layer, TRUE, NULL); - if (filter) - { - gimp_drawable_merge_filter (drawable, filter, NULL, NULL, FALSE); - g_object_unref (filter); - } + gimp_drawable_merge_filter (drawable, filter, NULL, NULL, FALSE, FALSE); + + gimp_image_remove_layer (image, layer, TRUE, NULL); gimp_image_undo_group_end (image); diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c index 6e3144389e..af4abbf3d2 100644 --- a/app/core/gimplayer.c +++ b/app/core/gimplayer.c @@ -1425,24 +1425,23 @@ gimp_layer_convert_type (GimpDrawable *drawable, static void gimp_layer_invalidate_boundary (GimpDrawable *drawable) { - GimpLayer *layer = GIMP_LAYER (drawable); - GimpImage *image; - GimpChannel *mask; + GimpLayer *layer = GIMP_LAYER (drawable); - if (! (image = gimp_item_get_image (GIMP_ITEM (layer)))) - return; - - /* Turn the current selection off */ - gimp_image_selection_invalidate (image); - - /* get the selection mask channel */ - mask = gimp_image_get_mask (image); - - /* Only bother with the bounds if there is a selection */ - if (! gimp_channel_is_empty (mask)) + if (gimp_item_is_attached (GIMP_ITEM (drawable)) && + gimp_item_is_visible (GIMP_ITEM (drawable))) { - mask->bounds_known = FALSE; - mask->boundary_known = FALSE; + 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)) @@ -2458,8 +2457,7 @@ gimp_layer_set_floating_sel_drawable (GimpLayer *layer, { if (layer->fs.segs) { - g_free (layer->fs.segs); - layer->fs.segs = NULL; + g_clear_pointer (&layer->fs.segs, g_free); layer->fs.num_segs = 0; } diff --git a/app/core/gimplayermask.c b/app/core/gimplayermask.c index 23e3ba8de5..95b82b929b 100644 --- a/app/core/gimplayermask.c +++ b/app/core/gimplayermask.c @@ -32,6 +32,9 @@ #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); @@ -67,6 +70,9 @@ gimp_layer_mask_class_init (GimpLayerMaskClass *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; @@ -85,6 +91,42 @@ 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) { diff --git a/app/core/gimplineart.c b/app/core/gimplineart.c new file mode 100644 index 0000000000..2fcbf935e8 --- /dev/null +++ b/app/core/gimplineart.c @@ -0,0 +1,2934 @@ +/* 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 "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); + +/* 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); + + 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; + } +} + +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 = 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, + (GimpParallelRunAsyncFunc) 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 *closed = NULL; + gfloat *distmap = NULL; + 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); + } + } + + /* 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 (data->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 (! gimp_async_is_stopped (async)) + { + 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 = 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 - 1)) + { + 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 - 1)) + { + 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; + + 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); + } + + 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 + 1); + + 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) +{ + guchar val; + + if (x < 0 || x >= gegl_buffer_get_width (buffer) || + y < 0 || y >= gegl_buffer_get_height (buffer)) + 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); + gimp_line_art_simple_fill (buffer, x + 1, y); + gimp_line_art_simple_fill (buffer, x - 1, y); + gimp_line_art_simple_fill (buffer, x, y + 1); + gimp_line_art_simple_fill (buffer, x, y - 1); + } +} + +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 0000000000..67376734f6 --- /dev/null +++ b/app/core/gimplineart.h @@ -0,0 +1,72 @@ +/* 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); + +GeglBuffer * gimp_line_art_get (GimpLineArt *line_art, + gfloat **distmap); + +#endif /* __GIMP_LINEART__ */ diff --git a/app/core/gimpmaskundo.c b/app/core/gimpmaskundo.c index 6687ce0dd5..32b72980a0 100644 --- a/app/core/gimpmaskundo.c +++ b/app/core/gimpmaskundo.c @@ -97,7 +97,6 @@ gimp_mask_undo_constructed (GObject *object) GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (object); GimpItem *item; GimpDrawable *drawable; - gint x, y, w, h; G_OBJECT_CLASS (parent_class)->constructed (object); @@ -106,22 +105,31 @@ gimp_mask_undo_constructed (GObject *object) item = GIMP_ITEM_UNDO (object)->item; drawable = GIMP_DRAWABLE (item); - if (gimp_item_bounds (item, &x, &y, &w, &h)) - { - mask_undo->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, w, h), - gimp_drawable_get_format (drawable)); - - gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (x, y, w, h), - GEGL_ABYSS_NONE, - mask_undo->buffer, - GEGL_RECTANGLE (0, 0, 0, 0)); - - mask_undo->x = x; - mask_undo->y = y; - } - 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; + + gimp_gegl_rectangle_align_to_tile_grid (&rect, &mask_undo->bounds, + buffer); + + 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 @@ -182,39 +190,39 @@ 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; - const Babl *format; - gint x, y, w, h; - gint width = 0; - gint height = 0; + 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); - if (gimp_item_bounds (item, &x, &y, &w, &h)) - { - new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, w, h), - gimp_drawable_get_format (drawable)); - - gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (x, y, w, h), - GEGL_ABYSS_NONE, - new_buffer, - GEGL_RECTANGLE (0, 0, 0, 0)); - - gegl_buffer_clear (gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (x, y, w, h)); - } - else - { - new_buffer = NULL; - } - 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); + + gimp_gegl_rectangle_align_to_tile_grid (&rect, &bounds, buffer); + + 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; @@ -223,7 +231,6 @@ gimp_mask_undo_pop (GimpUndo *undo, buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), mask_undo->format); - gegl_buffer_clear (buffer, NULL); gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); g_object_unref (buffer); @@ -231,9 +238,6 @@ gimp_mask_undo_pop (GimpUndo *undo, if (mask_undo->buffer) { - width = gegl_buffer_get_width (mask_undo->buffer); - height = gegl_buffer_get_height (mask_undo->buffer); - gimp_gegl_buffer_copy (mask_undo->buffer, NULL, GEGL_ABYSS_NONE, @@ -249,10 +253,10 @@ gimp_mask_undo_pop (GimpUndo *undo, if (mask_undo->buffer) { channel->empty = FALSE; - channel->x1 = mask_undo->x; - channel->y1 = mask_undo->y; - channel->x2 = mask_undo->x + width; - channel->y2 = mask_undo->y + height; + 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 { @@ -267,10 +271,11 @@ gimp_mask_undo_pop (GimpUndo *undo, channel->bounds_known = TRUE; /* set the new mask undo parameters */ - mask_undo->buffer = new_buffer; - mask_undo->x = x; - mask_undo->y = y; 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); } diff --git a/app/core/gimpmaskundo.h b/app/core/gimpmaskundo.h index 2c8c921192..53b92e8000 100644 --- a/app/core/gimpmaskundo.h +++ b/app/core/gimpmaskundo.h @@ -35,14 +35,15 @@ typedef struct _GimpMaskUndoClass GimpMaskUndoClass; struct _GimpMaskUndo { - GimpItemUndo parent_instance; + GimpItemUndo parent_instance; - gboolean convert_format; + gboolean convert_format; - GeglBuffer *buffer; - gint x; - gint y; - const Babl *format; + const Babl *format; + GeglBuffer *buffer; + GeglRectangle bounds; + gint x; + gint y; }; struct _GimpMaskUndoClass diff --git a/app/core/gimpmybrush.c b/app/core/gimpmybrush.c index b140b13abc..a26c86b227 100644 --- a/app/core/gimpmybrush.c +++ b/app/core/gimpmybrush.c @@ -109,11 +109,7 @@ gimp_mybrush_finalize (GObject *object) { GimpMybrush *brush = GIMP_MYBRUSH (object); - if (brush->priv->brush_json) - { - g_free (brush->priv->brush_json); - brush->priv->brush_json = NULL; - } + g_clear_pointer (&brush->priv->brush_json, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/app/core/gimppalette-import.c b/app/core/gimppalette-import.c index 23c107f3f8..aaa44ead18 100644 --- a/app/core/gimppalette-import.c +++ b/app/core/gimppalette-import.c @@ -18,7 +18,6 @@ #include "config.h" #include -#define GEGL_ITERATOR2_API #include #include diff --git a/app/core/gimppalette.c b/app/core/gimppalette.c index 335bda6f7c..f5b848ca41 100644 --- a/app/core/gimppalette.c +++ b/app/core/gimppalette.c @@ -37,7 +37,9 @@ #include "gimp-intl.h" -#define EPSILON 1e-10 + +#define RGB_EPSILON 1e-6 + /* local function prototypes */ @@ -648,11 +650,11 @@ gimp_palette_find_entry (GimpPalette *palette, for (list = palette->colors; list; list = g_list_next (list)) { entry = (GimpPaletteEntry *) list->data; - if (gimp_rgb_distance (&entry->color, color) < EPSILON) + if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON) return entry; } } - else if (gimp_rgb_distance (&start_from->color, color) < EPSILON) + else if (gimp_rgb_distance (&start_from->color, color) < RGB_EPSILON) { return start_from; } @@ -674,7 +676,7 @@ gimp_palette_find_entry (GimpPalette *palette, if (next) { entry = (GimpPaletteEntry *) next->data; - if (gimp_rgb_distance (&entry->color, color) < EPSILON) + if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON) return entry; next = next->next; @@ -683,7 +685,7 @@ gimp_palette_find_entry (GimpPalette *palette, if (prev) { entry = (GimpPaletteEntry *) prev->data; - if (gimp_rgb_distance (&entry->color, color) < EPSILON) + if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON) return entry; prev = prev->prev; diff --git a/app/core/gimppalettemru.c b/app/core/gimppalettemru.c index 7d34e157b4..fb1157d3c0 100644 --- a/app/core/gimppalettemru.c +++ b/app/core/gimppalettemru.c @@ -34,6 +34,8 @@ #include "gimp-intl.h" +#define RGBA_EPSILON 1e-4 + enum { COLOR_HISTORY = 1 @@ -205,7 +207,7 @@ gimp_palette_mru_add (GimpPaletteMru *mru, { GimpPaletteEntry *entry = list->data; - if (gimp_rgba_distance (&entry->color, color) < 0.0001) + if (gimp_rgba_distance (&entry->color, color) < RGBA_EPSILON) { found = entry; @@ -228,7 +230,7 @@ gimp_palette_mru_add (GimpPaletteMru *mru, GimpPaletteEntry *entry2 = list2->data; if (gimp_rgba_distance (&entry->color, - &entry2->color) < 0.0001) + &entry2->color) < RGBA_EPSILON) { found = entry2; diff --git a/app/core/gimppattern-header.h b/app/core/gimppattern-header.h index d9ae91f9cf..e11b3c9cbd 100644 --- a/app/core/gimppattern-header.h +++ b/app/core/gimppattern-header.h @@ -19,11 +19,10 @@ #define __GIMP_PATTERN_HEADER_H__ -#define GIMP_PATTERN_FILE_VERSION 1 -#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 */ +#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 */ @@ -40,9 +39,10 @@ struct _GimpPatternHeader 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... +/* 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__ */ +#endif /* __GIMP_PATTERN_HEADER_H__ */ diff --git a/app/core/gimppattern-save.c b/app/core/gimppattern-save.c new file mode 100644 index 0000000000..c745ab2063 --- /dev/null +++ b/app/core/gimppattern-save.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 "core-types.h" + +#include "gimppattern.h" +#include "gimppattern-header.h" +#include "gimppattern-save.h" +#include "gimptempbuf.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); + + 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 0000000000..d3c657c681 --- /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 index b9ec0bf5e9..31772111b7 100644 --- a/app/core/gimppattern.c +++ b/app/core/gimppattern.c @@ -30,6 +30,7 @@ #include "gimppattern.h" #include "gimppattern-load.h" +#include "gimppattern-save.h" #include "gimptagged.h" #include "gimptempbuf.h" @@ -83,6 +84,7 @@ gimp_pattern_class_init (GimpPatternClass *klass) 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; } @@ -193,8 +195,7 @@ gimp_pattern_copy (GimpData *data, GimpPattern *pattern = GIMP_PATTERN (data); GimpPattern *src_pattern = GIMP_PATTERN (src_data); - gimp_temp_buf_unref (pattern->mask); - + g_clear_pointer (&pattern->mask, gimp_temp_buf_unref); pattern->mask = gimp_temp_buf_copy (src_pattern->mask); gimp_data_dirty (data); diff --git a/app/core/gimppatternclipboard.c b/app/core/gimppatternclipboard.c index b503807253..9dd6a1f3ee 100644 --- a/app/core/gimppatternclipboard.c +++ b/app/core/gimppatternclipboard.c @@ -55,9 +55,7 @@ static void gimp_pattern_clipboard_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); -#if 0 static GimpData * gimp_pattern_clipboard_duplicate (GimpData *data); -#endif static void gimp_pattern_clipboard_changed (Gimp *gimp, GimpPattern *pattern); @@ -72,17 +70,13 @@ static void gimp_pattern_clipboard_class_init (GimpPatternClipboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); -#if 0 GimpDataClass *data_class = GIMP_DATA_CLASS (klass); -#endif 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; -#if 0 data_class->duplicate = gimp_pattern_clipboard_duplicate; -#endif g_object_class_install_property (object_class, PROP_GIMP, g_param_spec_object ("gimp", NULL, NULL, @@ -150,15 +144,15 @@ gimp_pattern_clipboard_get_property (GObject *object, } } -#if 0 static GimpData * gimp_pattern_clipboard_duplicate (GimpData *data) { - GimpPatternClipboard *pattern = GIMP_PATTERN_CLIPBOARD (data); + GimpData *new = g_object_new (GIMP_TYPE_PATTERN, NULL); - return gimp_pattern_clipboard_new (pattern->gimp); + gimp_data_copy (new, data); + + return new; } -#endif GimpData * gimp_pattern_clipboard_new (Gimp *gimp) diff --git a/app/core/gimppickable-contiguous-region.c b/app/core/gimppickable-contiguous-region.cc similarity index 56% rename from app/core/gimppickable-contiguous-region.c rename to app/core/gimppickable-contiguous-region.cc index b7033da546..d98186b794 100644 --- a/app/core/gimppickable-contiguous-region.c +++ b/app/core/gimppickable-contiguous-region.cc @@ -20,22 +20,41 @@ #include #include -#define GEGL_ITERATOR2_API #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, @@ -95,6 +114,11 @@ static void find_contiguous_region (GeglBuffer *src_buffer, gint y, const gfloat *col); +static void line_art_queue_pixel (GQueue *queue, + gint x, + gint y, + gint level); + /* public functions */ @@ -119,12 +143,10 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable, 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); @@ -179,17 +201,24 @@ gimp_pickable_contiguous_region_by_color (GimpPickable *pickable, * fuzzy_select. Modify the pickable's mask to reflect the * additional selection */ - GeglBufferIterator *iter; - GeglBuffer *src_buffer; - GeglBuffer *mask_buffer; - const Babl *format; - gint n_components; - gboolean has_alpha; - gfloat start_col[MAX_CHANNELS]; + 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); @@ -217,39 +246,368 @@ gimp_pickable_contiguous_region_by_color (GimpPickable *pickable, mask_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer), babl_format ("Y float")); - iter = gegl_buffer_iterator_new (src_buffer, - NULL, 0, format, - GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); - - gegl_buffer_iterator_add (iter, mask_buffer, - NULL, 0, babl_format ("Y float"), - GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); - - while (gegl_buffer_iterator_next (iter)) + gegl_parallel_distribute_area ( + gegl_buffer_get_extent (src_buffer), PIXELS_PER_THREAD, + [=] (const GeglRectangle *area) { - const gfloat *src = iter->items[0].data; - gfloat *dest = iter->items[1].data; - gint count = iter->length; + GeglBufferIterator *iter; - while (count--) + 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)) { - /* Find how closely the colors match */ - *dest = pixel_difference (start_col, src, - antialias, - threshold, - n_components, - has_alpha, - select_transparent, - select_criterion); + const gfloat *src = (const gfloat *) iter->items[0].data; + gfloat *dest = ( gfloat *) iter->items[1].data; + gint count = iter->length; - src += n_components; - dest += 1; + 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 */ @@ -362,8 +720,31 @@ pixel_difference (const gfloat *col1, break; case GIMP_SELECT_CRITERION_H: - max = fabs (col1[0] - col2[0]); - max = MIN (max, 1.0 - max); + 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: @@ -383,8 +764,31 @@ pixel_difference (const gfloat *col1, break; case GIMP_SELECT_CRITERION_LCH_H: - max = fabs (col1[2] - col2[2]) / 360.0; - max = MIN (max, 1.0 - max); + 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; } } @@ -506,7 +910,7 @@ find_contiguous_segment (const gfloat *col, row, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); s = row + initial_x * n_components; #else - s = g_alloca (n_components * sizeof (gfloat)); + s = (gfloat *) g_alloca (n_components * sizeof (gfloat)); gegl_sampler_get (src_sampler, initial_x, initial_y, NULL, s, GEGL_ABYSS_NONE); @@ -609,7 +1013,7 @@ find_contiguous_region (GeglBuffer *src_buffer, row = g_new (gfloat, gegl_buffer_get_width (src_buffer) * n_components); #endif - src_sampler = gegl_buffer_sampler_new (src_buffer, + src_sampler = gegl_buffer_sampler_new (src_buffer, format, GEGL_SAMPLER_NEAREST); segment_queue = g_queue_new (); @@ -696,3 +1100,20 @@ find_contiguous_region (GeglBuffer *src_buffer, 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 index 6db86248bb..26cbe7476a 100644 --- a/app/core/gimppickable-contiguous-region.h +++ b/app/core/gimppickable-contiguous-region.h @@ -19,21 +19,25 @@ #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_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_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/gimpprojection.c b/app/core/gimpprojection.c index ae73b2bee6..9f82833b57 100644 --- a/app/core/gimpprojection.c +++ b/app/core/gimpprojection.c @@ -35,6 +35,7 @@ #include "gimp.h" #include "gimp-memsize.h" +#include "gimpchunkiterator.h" #include "gimpimage.h" #include "gimpmarshal.h" #include "gimppickable.h" @@ -46,36 +47,9 @@ #include "gimp-priorities.h" -/* whether to use adaptive render-chunk size */ -static gboolean GIMP_PROJECTION_ADAPTIVE_CHUNK_SIZE = TRUE; - -/* chunk size for one iteration of the chunk renderer, when the use - * of adaptive chunk size is disabled - */ -static gint GIMP_PROJECTION_CHUNK_WIDTH = 256; -static gint GIMP_PROJECTION_CHUNK_HEIGHT = 128; - -/* the min/max adaptive chunk size */ -#define GIMP_PROJECTION_CHUNK_MIN_WIDTH 128 -#define GIMP_PROJECTION_CHUNK_MIN_HEIGHT 128 - -#define GIMP_PROJECTION_CHUNK_MAX_WIDTH 2048 -#define GIMP_PROJECTION_CHUNK_MAX_HEIGHT 2048 - -/* the minimal number of processed pixels on the current frame, - * above which we calculate a new target pixel count to render - * on the next frame - */ -#define GIMP_PROJECTION_MIN_PIXELS_PER_UPDATE 1024 - /* chunk size for area updates */ -static gint GIMP_PROJECTION_UPDATE_CHUNK_WIDTH = 32; -static gint GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT = 32; - -/* how much time, in seconds, do we allow chunk rendering to take, - * aiming for 15fps - */ -static gdouble GIMP_PROJECTION_CHUNK_TIME = 0.0666; +#define GIMP_PROJECTION_UPDATE_CHUNK_WIDTH 32 +#define GIMP_PROJECTION_UPDATE_CHUNK_HEIGHT 32 enum @@ -91,27 +65,6 @@ enum }; -typedef struct _GimpProjectionChunkRender GimpProjectionChunkRender; - -struct _GimpProjectionChunkRender -{ - guint idle_id; - - gint x; - gint y; - gint width; - gint height; - - gint work_x; - gint work_y; - gint work_height; - - gint n_pixels; - gint target_n_pixels; - - cairo_region_t *update_region; /* flushed update region */ -}; - struct _GimpProjectionPrivate { GimpProjectable *projectable; @@ -122,8 +75,9 @@ struct _GimpProjectionPrivate gint priority; cairo_region_t *update_region; - GimpProjectionChunkRender chunk_render; - cairo_rectangle_int_t priority_rect; + GeglRectangle priority_rect; + GimpChunkIterator *iter; + guint idle_id; gboolean invalidate_preview; }; @@ -179,15 +133,14 @@ static void gimp_projection_add_update_area (GimpProjection *proj, gint w, gint h); static void gimp_projection_flush_whenever (GimpProjection *proj, - gboolean now); + 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); -static gboolean gimp_projection_chunk_render_callback (gpointer data); -static void gimp_projection_chunk_render_init (GimpProjection *proj); -static void gimp_projection_chunk_render_reinit (GimpProjection *proj); -static gboolean gimp_projection_chunk_render_iteration(GimpProjection *proj, - gboolean chunk); -static gboolean gimp_projection_chunk_render_next_area(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, @@ -214,11 +167,6 @@ static void gimp_projection_projectable_bounds_changed (GimpProjectable *proje gint old_h, GimpProjection *proj); -static gint gimp_projection_round_chunk_size (gdouble size, - gboolean toward_zero); -static gint gimp_projection_round_chunk_width (gdouble width); -static gint gimp_projection_round_chunk_height (gdouble height); - G_DEFINE_TYPE_WITH_CODE (GimpProjection, gimp_projection, GIMP_TYPE_OBJECT, G_ADD_PRIVATE (GimpProjection) @@ -235,7 +183,6 @@ gimp_projection_class_init (GimpProjectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); - const gchar *env; projection_signals[UPDATE] = g_signal_new ("update", @@ -258,27 +205,6 @@ gimp_projection_class_init (GimpProjectionClass *klass) gimp_object_class->get_memsize = gimp_projection_get_memsize; g_object_class_override_property (object_class, PROP_BUFFER, "buffer"); - - if (g_getenv ("GIMP_NO_ADAPTIVE_CHUNK_SIZE")) - GIMP_PROJECTION_ADAPTIVE_CHUNK_SIZE = FALSE; - - 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_PROJECTION_CHUNK_WIDTH = width; - GIMP_PROJECTION_CHUNK_HEIGHT = height; - } - } } static void @@ -403,7 +329,7 @@ gimp_projection_pickable_flush (GimpPickable *pickable) gimp_projection_get_buffer (pickable); gimp_projection_finish_draw (proj); - gimp_projection_flush_now (proj); + gimp_projection_flush_now (proj, FALSE); if (proj->priv->invalidate_preview) { @@ -580,74 +506,19 @@ gimp_projection_set_priority_rect (GimpProjection *proj, gint w, gint h) { - cairo_rectangle_int_t rect; - gint off_x, off_y; - gint width, height; - g_return_if_fail (GIMP_IS_PROJECTION (proj)); - gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y); - gimp_projectable_get_size (proj->priv->projectable, &width, &height); + proj->priv->priority_rect = *GEGL_RECTANGLE (x, y, w, h); - /* 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; - - if (gimp_rectangle_intersect (x, y, w, h, - 0, 0, width, height, - &rect.x, &rect.y, &rect.width, &rect.height)) - { - proj->priv->priority_rect = rect; - - if (proj->priv->chunk_render.idle_id) - gimp_projection_chunk_render_reinit (proj); - } + gimp_projection_update_priority_rect (proj); } void gimp_projection_stop_rendering (GimpProjection *proj) { - GimpProjectionChunkRender *chunk_render; - cairo_rectangle_int_t rect; - g_return_if_fail (GIMP_IS_PROJECTION (proj)); - chunk_render = &proj->priv->chunk_render; - - if (! chunk_render->idle_id) - return; - - if (chunk_render->update_region) - { - if (proj->priv->update_region) - { - cairo_region_union (proj->priv->update_region, - chunk_render->update_region); - } - else - { - proj->priv->update_region = - cairo_region_copy (chunk_render->update_region); - } - - g_clear_pointer (&chunk_render->update_region, cairo_region_destroy); - } - - rect.x = chunk_render->x; - rect.y = chunk_render->work_y; - rect.width = chunk_render->width; - rect.height = chunk_render->height - (chunk_render->work_y - chunk_render->y); - - /* FIXME this is too much, the entire current row */ - if (proj->priv->update_region) - cairo_region_union_rectangle (proj->priv->update_region, &rect); - else - proj->priv->update_region = cairo_region_create_rectangle (&rect); - - gimp_projection_chunk_render_stop (proj); + gimp_projection_chunk_render_stop (proj, TRUE); } void @@ -656,16 +527,17 @@ gimp_projection_flush (GimpProjection *proj) g_return_if_fail (GIMP_IS_PROJECTION (proj)); /* Construct in chunks */ - gimp_projection_flush_whenever (proj, FALSE); + gimp_projection_flush_whenever (proj, FALSE, FALSE); } void -gimp_projection_flush_now (GimpProjection *proj) +gimp_projection_flush_now (GimpProjection *proj, + gboolean direct) { g_return_if_fail (GIMP_IS_PROJECTION (proj)); /* Construct NOW */ - gimp_projection_flush_whenever (proj, TRUE); + gimp_projection_flush_whenever (proj, TRUE, direct); } void @@ -673,15 +545,17 @@ gimp_projection_finish_draw (GimpProjection *proj) { g_return_if_fail (GIMP_IS_PROJECTION (proj)); - if (proj->priv->chunk_render.idle_id) + if (proj->priv->iter) { - gimp_projection_chunk_render_stop (proj); + gimp_chunk_iterator_set_priority_rect (proj->priv->iter, NULL); - gimp_projectable_begin_render (proj->priv->projectable); + gimp_tile_handler_validate_begin_validate (proj->priv->validate_handler); - while (gimp_projection_chunk_render_iteration (proj, FALSE)); + while (gimp_projection_chunk_render_iteration (proj)); - gimp_projectable_end_render (proj->priv->projectable); + gimp_tile_handler_validate_end_validate (proj->priv->validate_handler); + + gimp_projection_chunk_render_stop (proj, FALSE); } } @@ -717,24 +591,18 @@ gimp_projection_allocate_buffer (GimpProjection *proj) static void gimp_projection_free_buffer (GimpProjection *proj) { - if (proj->priv->chunk_render.idle_id) - gimp_projection_chunk_render_stop (proj); + gimp_projection_chunk_render_stop (proj, FALSE); g_clear_pointer (&proj->priv->update_region, cairo_region_destroy); - g_clear_pointer (&proj->priv->chunk_render.update_region, cairo_region_destroy); if (proj->priv->buffer) { - if (proj->priv->validate_handler) - { - gimp_tile_handler_validate_unassign (proj->priv->validate_handler, - 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_clear_object (&proj->priv->validate_handler); } static void @@ -773,10 +641,14 @@ gimp_projection_add_update_area (GimpProjection *proj, static void gimp_projection_flush_whenever (GimpProjection *proj, - gboolean now) + 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); @@ -790,20 +662,21 @@ gimp_projection_flush_whenever (GimpProjection *proj, i, &rect); gimp_projection_paint_area (proj, - FALSE, /* sic! */ + 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 */ { - gimp_projection_chunk_render_init (proj); + /* Consumes the update region */ + gimp_projection_chunk_render_start (proj); } - - /* Free the update region */ - g_clear_pointer (&proj->priv->update_region, cairo_region_destroy); } else if (! now && proj->priv->invalidate_preview) { @@ -816,308 +689,187 @@ gimp_projection_flush_whenever (GimpProjection *proj, } } +static void +gimp_projection_update_priority_rect (GimpProjection *proj) +{ + if (proj->priv->iter) + { + GeglRectangle rect; + gint off_x, off_y; + gint width, height; + + rect = proj->priv->priority_rect; + + gimp_projectable_get_offset (proj->priv->projectable, &off_x, &off_y); + gimp_projectable_get_size (proj->priv->projectable, &width, &height); + + /* 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, + GEGL_RECTANGLE (0, 0, width, height)); + + gimp_chunk_iterator_set_priority_rect (proj->priv->iter, &rect); + } +} + static void gimp_projection_chunk_render_start (GimpProjection *proj) { - g_return_if_fail (proj->priv->chunk_render.idle_id == 0); + cairo_region_t *region = proj->priv->update_region; + gboolean invalidate_preview = FALSE; - proj->priv->chunk_render.idle_id = - g_idle_add_full (GIMP_PRIORITY_PROJECTION_IDLE + proj->priv->priority, - gimp_projection_chunk_render_callback, proj, - NULL); -} - -static void -gimp_projection_chunk_render_stop (GimpProjection *proj) -{ - g_return_if_fail (proj->priv->chunk_render.idle_id != 0); - - g_source_remove (proj->priv->chunk_render.idle_id); - proj->priv->chunk_render.idle_id = 0; -} - -static gboolean -gimp_projection_chunk_render_callback (gpointer data) -{ - GimpProjection *proj = data; - GimpProjectionChunkRender *chunk_render = &proj->priv->chunk_render; - GTimer *timer = g_timer_new (); - gint chunks = 0; - gboolean retval = TRUE; - - /* reset the rendered pixel count, so that we count the number of pixels - * processed during this frame - */ - chunk_render->n_pixels = 0; - - gimp_projectable_begin_render (proj->priv->projectable); - - do + if (proj->priv->iter) { - if (! gimp_projection_chunk_render_iteration (proj, TRUE)) + 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) { - gimp_projection_chunk_render_stop (proj); + cairo_region_union (region, proj->priv->update_region); - retval = FALSE; - - break; - } - - chunks++; - } - while (g_timer_elapsed (timer, NULL) < GIMP_PROJECTION_CHUNK_TIME); - - gimp_projectable_end_render (proj->priv->projectable); - - /* adjust the target number of pixels to be processed on the next frame, - * according to the number of pixels processed during this frame and the - * elapsed time, in order to match the desired frame rate. - */ - if (chunk_render->n_pixels >= GIMP_PROJECTION_MIN_PIXELS_PER_UPDATE) - { - chunk_render->target_n_pixels = floor (chunk_render->n_pixels * - GIMP_PROJECTION_CHUNK_TIME / - g_timer_elapsed (timer, NULL)); - } - - GIMP_LOG (PROJECTION, "%d chunks in %f seconds\n", - chunks, g_timer_elapsed (timer, NULL)); - g_timer_destroy (timer); - - return retval; -} - -static void -gimp_projection_chunk_render_init (GimpProjection *proj) -{ - GimpProjectionChunkRender *chunk_render = &proj->priv->chunk_render; - - chunk_render->target_n_pixels = GIMP_PROJECTION_CHUNK_WIDTH * - GIMP_PROJECTION_CHUNK_HEIGHT; - - gimp_projection_chunk_render_reinit (proj); -} - -static void -gimp_projection_chunk_render_reinit (GimpProjection *proj) -{ - GimpProjectionChunkRender *chunk_render = &proj->priv->chunk_render; - - /* We need to merge the ChunkRender's and the GimpProjection's - * update_regions list to keep track of which of the updates have - * been flushed and hence need to be drawn. - */ - if (proj->priv->update_region) - { - if (chunk_render->update_region) - { - cairo_region_union (chunk_render->update_region, - proj->priv->update_region); - } - else - { - chunk_render->update_region = - cairo_region_copy (proj->priv->update_region); + cairo_region_destroy (proj->priv->update_region); } } - /* If a chunk renderer was already running, merge the remainder of - * its unrendered area with the update_areas list, and make it start - * work on the next unrendered area in the list. - */ - if (chunk_render->idle_id) + proj->priv->update_region = NULL; + + if (region && ! cairo_region_is_empty (region)) { - cairo_rectangle_int_t rect; - gint work_h = 0; + proj->priv->iter = gimp_chunk_iterator_new (region); - if (chunk_render->work_x != chunk_render->x) + gimp_projection_update_priority_rect (proj); + + if (! proj->priv->idle_id) { - work_h = MIN (chunk_render->work_height, - chunk_render->y + chunk_render->height - - chunk_render->work_y); - - rect.x = chunk_render->work_x; - rect.y = chunk_render->work_y; - rect.width = chunk_render->x + chunk_render->width - - chunk_render->work_x; - rect.height = work_h; - - if (chunk_render->update_region) - cairo_region_union_rectangle (chunk_render->update_region, &rect); - else - chunk_render->update_region = cairo_region_create_rectangle (&rect); - } - - rect.x = chunk_render->x; - rect.y = chunk_render->work_y + work_h; - rect.width = chunk_render->width; - rect.height = chunk_render->y + chunk_render->height - rect.y; - - if (chunk_render->update_region) - cairo_region_union_rectangle (chunk_render->update_region, &rect); - else - chunk_render->update_region = cairo_region_create_rectangle (&rect); - - gimp_projection_chunk_render_next_area (proj); - } - else - { - if (chunk_render->update_region == NULL) - { - g_warning ("%s: wanted to start chunk render with no update_region", - G_STRFUNC); - return; - } - - proj->priv->chunk_render.target_n_pixels = GIMP_PROJECTION_CHUNK_WIDTH * - GIMP_PROJECTION_CHUNK_HEIGHT; - - gimp_projection_chunk_render_next_area (proj); - - gimp_projection_chunk_render_start (proj); - } -} - -/* Unless specified otherwise, projection re-rendering is organised by - * ChunkRender, which amalgamates areas to be re-rendered and breaks - * them into bite-sized chunks which are chewed on in an idle - * function. This greatly improves responsiveness for many GIMP - * operations. -- Adam - */ -static gboolean -gimp_projection_chunk_render_iteration (GimpProjection *proj, - gboolean chunk) -{ - GimpProjectionChunkRender *chunk_render = &proj->priv->chunk_render; - gint work_x = chunk_render->work_x; - gint work_y = chunk_render->work_y; - gint work_w; - gint work_h; - - work_w = chunk_render->x + chunk_render->width - work_x; - work_h = chunk_render->y + chunk_render->height - work_y; - - if (chunk) - { - if (GIMP_PROJECTION_ADAPTIVE_CHUNK_SIZE) - { - gint chunk_w; - gint chunk_h; - - /* try to render in square chunks */ - chunk_h = gimp_projection_round_chunk_height ( - sqrt (chunk_render->target_n_pixels)); - - work_h = MIN (work_h, chunk_h); - - chunk_w = gimp_projection_round_chunk_width ( - chunk_render->target_n_pixels / work_h); - - work_w = MIN (work_w, chunk_w); - } - else - { - work_w = MIN (work_w, GIMP_PROJECTION_CHUNK_WIDTH); - work_h = MIN (work_h, GIMP_PROJECTION_CHUNK_HEIGHT); + 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 (work_x != chunk_render->x) - work_h = MIN (work_h, chunk_render->work_height); - } + if (region) + cairo_region_destroy (region); - if (work_h != chunk_render->work_height) - { - /* if the chunk height changed in the middle of a row, refetch the - * current area, so that we're back at the beginning of a row - */ - if (work_x != chunk_render->x) - gimp_projection_chunk_render_reinit (proj); - - chunk_render->work_height = work_h; - } - - gimp_projection_paint_area (proj, TRUE /* sic! */, - work_x, work_y, work_w, work_h); - - chunk_render->n_pixels += work_w * work_h; - - chunk_render->work_x += work_w; - - if (chunk_render->work_x >= chunk_render->x + chunk_render->width) - { - chunk_render->work_x = chunk_render->x; - - chunk_render->work_y += work_h; - - if (chunk_render->work_y >= chunk_render->y + chunk_render->height) + if (proj->priv->idle_id) { - if (! gimp_projection_chunk_render_next_area (proj)) + 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) { - if (proj->priv->invalidate_preview) - { - /* invalidate the preview here since it is constructed from - * the projection - */ - proj->priv->invalidate_preview = FALSE; + cairo_region_union (proj->priv->update_region, region); - gimp_projectable_invalidate_preview (proj->priv->projectable); - } - - /* FINISHED */ - return FALSE; + cairo_region_destroy (region); + } + else + { + proj->priv->update_region = region; } } - } + else + { + gimp_chunk_iterator_stop (proj->priv->iter, TRUE); + } - /* Still work to do. */ - return TRUE; + proj->priv->iter = NULL; + } } static gboolean -gimp_projection_chunk_render_next_area (GimpProjection *proj) +gimp_projection_chunk_render_callback (GimpProjection *proj) { - GimpProjectionChunkRender *chunk_render = &proj->priv->chunk_render; - cairo_region_t *next_region; - cairo_rectangle_int_t rect; - - if (! chunk_render->update_region) - return FALSE; - - if (cairo_region_is_empty (chunk_render->update_region)) + if (gimp_projection_chunk_render_iteration (proj)) { - g_clear_pointer (&chunk_render->update_region, cairo_region_destroy); + 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; } - - next_region = cairo_region_copy (chunk_render->update_region); - cairo_region_intersect_rectangle (next_region, &proj->priv->priority_rect); - - if (cairo_region_is_empty (next_region)) - cairo_region_get_rectangle (chunk_render->update_region, 0, &rect); - else - cairo_region_get_rectangle (next_region, 0, &rect); - - cairo_region_destroy (next_region); - - cairo_region_subtract_rectangle (chunk_render->update_region, &rect); - - if (cairo_region_is_empty (chunk_render->update_region)) - { - g_clear_pointer (&chunk_render->update_region, cairo_region_destroy); - } - - chunk_render->x = rect.x; - chunk_render->y = rect.y; - chunk_render->width = rect.width; - chunk_render->height = rect.height; - - chunk_render->work_x = chunk_render->x; - chunk_render->work_y = chunk_render->y; - - return TRUE; } static void @@ -1138,19 +890,19 @@ gimp_projection_paint_area (GimpProjection *proj, 0, 0, width, height, &x, &y, &w, &h)) { - if (proj->priv->validate_handler) - gimp_tile_handler_validate_invalidate (proj->priv->validate_handler, - GEGL_RECTANGLE (x, y, w, h)); if (now) { - GeglNode *graph = gimp_projectable_get_graph (proj->priv->projectable); - - if (proj->priv->validate_handler) - gimp_tile_handler_validate_undo_invalidate (proj->priv->validate_handler, - GEGL_RECTANGLE (x, y, w, h)); - - gegl_node_blit_buffer (graph, proj->priv->buffer, - GEGL_RECTANGLE (x, y, w, h), 0, GEGL_ABYSS_NONE); + gimp_tile_handler_validate_validate ( + proj->priv->validate_handler, + proj->priv->buffer, + GEGL_RECTANGLE (x, y, w, h), + FALSE); + } + else + { + gimp_tile_handler_validate_invalidate ( + proj->priv->validate_handler, + GEGL_RECTANGLE (x, y, w, h)); } /* add the projectable's offsets because the list of update areas @@ -1213,11 +965,6 @@ gimp_projection_projectable_structure_changed (GimpProjectable *projectable, gimp_projectable_get_size (projectable, &width, &height); gimp_projection_add_update_area (proj, 0, 0, width, height); - - proj->priv->priority_rect.x = 0; - proj->priv->priority_rect.y = 0; - proj->priv->priority_rect.width = width; - proj->priv->priority_rect.height = height; } static void @@ -1278,10 +1025,11 @@ gimp_projection_projectable_bounds_changed (GimpProjectable *projectable, #endif /* reallocate the buffer, and copy the old buffer to the corresponding - * region of the new buffer. additionally, shift and clip all outstanding - * update regions as necessary. + * region of the new buffer. */ + gimp_projection_chunk_render_stop (proj, TRUE); + old_validate_handler = proj->priv->validate_handler; proj->priv->buffer = NULL; @@ -1311,60 +1059,6 @@ gimp_projection_projectable_bounds_changed (GimpProjectable *projectable, cairo_region_intersect_rectangle (proj->priv->update_region, &bounds); } - if (proj->priv->chunk_render.idle_id) - { - const cairo_rectangle_int_t bounds = {0, 0, w, h}; - - proj->priv->chunk_render.x += dx; - proj->priv->chunk_render.y += dy; - - proj->priv->chunk_render.work_x += dx; - proj->priv->chunk_render.work_y += dx; - - if (proj->priv->chunk_render.update_region) - { - cairo_region_translate - (proj->priv->chunk_render.update_region, dx, dy); - cairo_region_intersect_rectangle - (proj->priv->chunk_render.update_region, &bounds); - } - - if (! gimp_rectangle_intersect (proj->priv->chunk_render.x, - proj->priv->chunk_render.y, - proj->priv->chunk_render.width, - proj->priv->chunk_render.height, - - 0, 0, w, h, - - &proj->priv->chunk_render.x, - &proj->priv->chunk_render.y, - &proj->priv->chunk_render.width, - &proj->priv->chunk_render.height)) - { - if (! gimp_projection_chunk_render_next_area (proj)) - gimp_projection_chunk_render_stop (proj); - } - } - - if (proj->priv->priority_rect.width > 0 && - proj->priv->priority_rect.height > 0) - { - proj->priv->priority_rect.x += dx; - proj->priv->priority_rect.y += dy; - - gimp_rectangle_intersect (proj->priv->priority_rect.x, - proj->priv->priority_rect.y, - proj->priv->priority_rect.width, - proj->priv->priority_rect.height, - - 0, 0, w, h, - - &proj->priv->priority_rect.x, - &proj->priv->priority_rect.y, - &proj->priv->priority_rect.width, - &proj->priv->priority_rect.height); - } - if (dx > 0) gimp_projection_add_update_area (proj, 0, 0, dx, h); if (dy > 0) @@ -1376,54 +1070,3 @@ gimp_projection_projectable_bounds_changed (GimpProjectable *projectable, proj->priv->invalidate_preview = TRUE; } - -static gint -gimp_projection_round_chunk_size (gdouble size, - gboolean toward_zero) -{ - /* round 'size' (up or down, depending on 'toward_zero') to the closest power - * of 2 - */ - - if (size < 0.0) - { - return -gimp_projection_round_chunk_size (-size, toward_zero); - } - else if (size == 0.0) - { - return 0; - } - else if (size < 1.0) - { - return toward_zero ? 0 : 1; - } - else - { - gdouble log2_size = log (size) / G_LN2; - - if (toward_zero) - log2_size = floor (log2_size); - else - log2_size = ceil (log2_size); - - return 1 << (gint) log2_size; - } -} - -static gint -gimp_projection_round_chunk_width (gdouble width) -{ - gint w = gimp_projection_round_chunk_size (width, FALSE); - - return CLAMP (w, GIMP_PROJECTION_CHUNK_MIN_WIDTH, - GIMP_PROJECTION_CHUNK_MAX_WIDTH); -} - -static gint -gimp_projection_round_chunk_height (gdouble height) -{ - gint h = gimp_projection_round_chunk_size (height, TRUE); - - return CLAMP (h, GIMP_PROJECTION_CHUNK_MIN_HEIGHT, - GIMP_PROJECTION_CHUNK_MAX_HEIGHT); -} diff --git a/app/core/gimpprojection.h b/app/core/gimpprojection.h index 2446e93420..8c9baf69e6 100644 --- a/app/core/gimpprojection.h +++ b/app/core/gimpprojection.h @@ -70,7 +70,8 @@ void gimp_projection_set_priority_rect (GimpProjection *proj, void gimp_projection_stop_rendering (GimpProjection *proj); void gimp_projection_flush (GimpProjection *proj); -void gimp_projection_flush_now (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, diff --git a/app/core/gimpscanconvert.c b/app/core/gimpscanconvert.c index 108c0e1a47..78b73461b1 100644 --- a/app/core/gimpscanconvert.c +++ b/app/core/gimpscanconvert.c @@ -20,7 +20,6 @@ #include #include -#define GEGL_ITERATOR2_API #include #include diff --git a/app/core/gimpsymmetry-mandala.c b/app/core/gimpsymmetry-mandala.c index f8578ab6f2..c8c4b0e2ea 100644 --- a/app/core/gimpsymmetry-mandala.c +++ b/app/core/gimpsymmetry-mandala.c @@ -81,10 +81,10 @@ static void gimp_mandala_guide_position_cb (GObject *object, static void gimp_mandala_update_strokes (GimpSymmetry *mandala, GimpDrawable *drawable, GimpCoords *origin); -static GeglNode * gimp_mandala_get_operation (GimpSymmetry *mandala, +static void gimp_mandala_get_transform (GimpSymmetry *mandala, gint stroke, - gint paint_width, - gint paint_height); + gdouble *angle, + gboolean *reflect); static void gimp_mandala_image_size_changed_cb (GimpImage *image, gint previous_origin_x, gint previous_origin_y, @@ -112,7 +112,7 @@ gimp_mandala_class_init (GimpMandalaClass *klass) symmetry_class->label = _("Mandala"); symmetry_class->update_strokes = gimp_mandala_update_strokes; - symmetry_class->get_operation = gimp_mandala_get_operation; + 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, @@ -179,20 +179,6 @@ gimp_mandala_finalize (GObject *object) g_clear_object (&mandala->horizontal_guide); g_clear_object (&mandala->vertical_guide); - if (mandala->ops) - { - GList *iter; - - for (iter = mandala->ops; iter; iter = g_list_next (iter)) - { - if (iter->data) - g_object_unref (G_OBJECT (iter->data)); - } - - g_list_free (mandala->ops); - mandala->ops = NULL; - } - G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -490,66 +476,18 @@ gimp_mandala_update_strokes (GimpSymmetry *sym, g_signal_emit_by_name (sym, "strokes-updated", sym->image); } -static GeglNode * -gimp_mandala_get_operation (GimpSymmetry *sym, +static void +gimp_mandala_get_transform (GimpSymmetry *sym, gint stroke, - gint paint_width, - gint paint_height) + gdouble *angle, + gboolean *reflect) { GimpMandala *mandala = GIMP_MANDALA (sym); - GeglNode *op = NULL; - gint i; - if (! mandala->disable_transformation && - stroke != 0 && - paint_width != 0 && - paint_height != 0) - { - if (mandala->size != mandala->cached_size || - mandala->cached_paint_width != paint_width || - mandala->cached_paint_height != paint_height) - { - GList *iter; + if (mandala->disable_transformation) + return; - if (mandala->ops) - { - for (iter = mandala->ops; iter; iter = g_list_next (iter)) - { - if (iter->data) - g_object_unref (G_OBJECT (iter->data)); - } - - g_list_free (mandala->ops); - mandala->ops = NULL; - } - - mandala->ops = g_list_prepend (mandala->ops, NULL); - - for (i = 1; i < mandala->size; i++) - { - op = gegl_node_new_child (NULL, - "operation", "gegl:rotate", - "origin-x", - (gdouble) paint_width / 2.0, - "origin-y", - (gdouble) paint_height / 2.0, - "degrees", - i * 360.0 / (gdouble) mandala->size, - NULL); - mandala->ops = g_list_prepend (mandala->ops, op); - } - - mandala->ops = g_list_reverse (mandala->ops); - - mandala->cached_size = mandala->size; - mandala->cached_paint_width = paint_width; - mandala->cached_paint_height = paint_height; - } - - op = g_list_nth_data (mandala->ops, stroke); - } - - return op; + *angle = 360.0 * stroke / mandala->size; } static void diff --git a/app/core/gimpsymmetry-mandala.h b/app/core/gimpsymmetry-mandala.h index 63e8c82130..d7486f5b68 100644 --- a/app/core/gimpsymmetry-mandala.h +++ b/app/core/gimpsymmetry-mandala.h @@ -46,11 +46,6 @@ struct _GimpMandala GimpGuide *horizontal_guide; GimpGuide *vertical_guide; - - GList *ops; - gint cached_size; - gint cached_paint_width; - gint cached_paint_height; }; struct _GimpMandalaClass diff --git a/app/core/gimpsymmetry-mirror.c b/app/core/gimpsymmetry-mirror.c index 66b960a338..7f92301292 100644 --- a/app/core/gimpsymmetry-mirror.c +++ b/app/core/gimpsymmetry-mirror.c @@ -71,13 +71,10 @@ static void gimp_mirror_get_property (GObject *obje static void gimp_mirror_update_strokes (GimpSymmetry *mirror, GimpDrawable *drawable, GimpCoords *origin); -static void gimp_mirror_prepare_operations (GimpMirror *mirror, - gint paint_width, - gint paint_height); -static GeglNode * gimp_mirror_get_operation (GimpSymmetry *mirror, +static void gimp_mirror_get_transform (GimpSymmetry *mirror, gint stroke, - gint paint_width, - gint paint_height); + gdouble *angle, + gboolean *reflect); static void gimp_mirror_reset (GimpMirror *mirror); static void gimp_mirror_add_guide (GimpMirror *mirror, GimpOrientationType orientation); @@ -122,7 +119,7 @@ gimp_mirror_class_init (GimpMirrorClass *klass) symmetry_class->label = _("Mirror"); symmetry_class->update_strokes = gimp_mirror_update_strokes; - symmetry_class->get_operation = gimp_mirror_get_operation; + 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, @@ -205,10 +202,6 @@ gimp_mirror_finalize (GObject *object) g_clear_object (&mirror->horizontal_guide); g_clear_object (&mirror->vertical_guide); - g_clear_object (&mirror->horizontal_op); - g_clear_object (&mirror->vertical_op); - g_clear_object (&mirror->central_op); - G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -365,78 +358,47 @@ gimp_mirror_update_strokes (GimpSymmetry *sym, } static void -gimp_mirror_prepare_operations (GimpMirror *mirror, - gint paint_width, - gint paint_height) -{ - if (paint_width == mirror->last_paint_width && - paint_height == mirror->last_paint_height) - return; - - mirror->last_paint_width = paint_width; - mirror->last_paint_height = paint_height; - - if (mirror->horizontal_op) - g_object_unref (mirror->horizontal_op); - - mirror->horizontal_op = gegl_node_new_child (NULL, - "operation", "gegl:reflect", - "origin-x", 0.0, - "origin-y", paint_height / 2.0, - "x", 1.0, - "y", 0.0, - NULL); - - if (mirror->vertical_op) - g_object_unref (mirror->vertical_op); - - mirror->vertical_op = gegl_node_new_child (NULL, - "operation", "gegl:reflect", - "origin-x", paint_width / 2.0, - "origin-y", 0.0, - "x", 0.0, - "y", 1.0, - NULL); - - if (mirror->central_op) - g_object_unref (mirror->central_op); - - mirror->central_op = gegl_node_new_child (NULL, - "operation", "gegl:rotate", - "origin-x", paint_width / 2.0, - "origin-y", paint_height / 2.0, - "degrees", 180.0, - NULL); -} - -static GeglNode * -gimp_mirror_get_operation (GimpSymmetry *sym, +gimp_mirror_get_transform (GimpSymmetry *sym, gint stroke, - gint paint_width, - gint paint_height) + gdouble *angle, + gboolean *reflect) { GimpMirror *mirror = GIMP_MIRROR (sym); - GeglNode *op; - g_return_val_if_fail (stroke >= 0 && - stroke < g_list_length (sym->strokes), NULL); + if (mirror->disable_transformation) + return; - gimp_mirror_prepare_operations (mirror, paint_width, paint_height); + if (! mirror->horizontal_mirror && stroke >= 1) + stroke++; - if (mirror->disable_transformation || stroke == 0 || - paint_width == 0 || paint_height == 0) - op = NULL; - else if (stroke == 1 && mirror->horizontal_mirror) - op = g_object_ref (mirror->horizontal_op); - else if ((stroke == 2 && mirror->horizontal_mirror && - mirror->vertical_mirror) || - (stroke == 1 && mirror->vertical_mirror && - ! mirror->horizontal_mirror)) - op = g_object_ref (mirror->vertical_op); - else - op = g_object_ref (mirror->central_op); + if (! mirror->vertical_mirror && stroke >= 2) + stroke++; - return op; + 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 diff --git a/app/core/gimpsymmetry-mirror.h b/app/core/gimpsymmetry-mirror.h index 3b15441d84..f9ac26ab46 100644 --- a/app/core/gimpsymmetry-mirror.h +++ b/app/core/gimpsymmetry-mirror.h @@ -37,24 +37,17 @@ typedef struct _GimpMirrorClass GimpMirrorClass; struct _GimpMirror { - GimpSymmetry parent_instance; + GimpSymmetry parent_instance; - gboolean horizontal_mirror; - gboolean vertical_mirror; - gboolean point_symmetry; - gboolean disable_transformation; + 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; - - /* Cached data */ - gint last_paint_width; - gint last_paint_height; - GeglNode *horizontal_op; - GeglNode *vertical_op; - GeglNode *central_op; + gdouble mirror_position_y; + gdouble mirror_position_x; + GimpGuide *horizontal_guide; + GimpGuide *vertical_guide; }; struct _GimpMirrorClass diff --git a/app/core/gimpsymmetry-tiling.c b/app/core/gimpsymmetry-tiling.c index 25480346e5..555f58480c 100644 --- a/app/core/gimpsymmetry-tiling.c +++ b/app/core/gimpsymmetry-tiling.c @@ -70,10 +70,6 @@ static void gimp_tiling_get_property (GObject *object, static void gimp_tiling_update_strokes (GimpSymmetry *tiling, GimpDrawable *drawable, GimpCoords *origin); -static GeglNode * gimp_tiling_get_operation (GimpSymmetry *tiling, - gint stroke, - gint paint_width, - gint paint_height); static void gimp_tiling_image_size_changed_cb (GimpImage *image, gint previous_origin_x, gint previous_origin_y, @@ -101,7 +97,6 @@ gimp_tiling_class_init (GimpTilingClass *klass) symmetry_class->label = _("Tiling"); symmetry_class->update_strokes = gimp_tiling_update_strokes; - symmetry_class->get_operation = gimp_tiling_get_operation; GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_INTERVAL_X, "interval-x", @@ -431,16 +426,6 @@ gimp_tiling_update_strokes (GimpSymmetry *sym, g_signal_emit_by_name (sym, "strokes-updated", sym->image); } -static GeglNode * -gimp_tiling_get_operation (GimpSymmetry *sym, - gint stroke, - gint paint_width, - gint paint_height) -{ - /* No buffer transformation happens for tiling. */ - return NULL; -} - static void gimp_tiling_image_size_changed_cb (GimpImage *image, gint previous_origin_x, diff --git a/app/core/gimpsymmetry.c b/app/core/gimpsymmetry.c index 04a2510a49..e0bc23bfd6 100644 --- a/app/core/gimpsymmetry.c +++ b/app/core/gimpsymmetry.c @@ -27,9 +27,12 @@ #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" @@ -71,10 +74,10 @@ static void gimp_symmetry_get_property (GObject *object, static void gimp_symmetry_real_update_strokes (GimpSymmetry *sym, GimpDrawable *drawable, GimpCoords *origin); -static GeglNode * gimp_symmetry_real_get_op (GimpSymmetry *sym, +static void gimp_symmetry_real_get_transform (GimpSymmetry *sym, gint stroke, - gint paint_width, - gint paint_height); + gdouble *angle, + gboolean *reflect); static gboolean gimp_symmetry_real_update_version (GimpSymmetry *sym); @@ -133,7 +136,7 @@ gimp_symmetry_class_init (GimpSymmetryClass *klass) klass->label = _("None"); klass->update_strokes = gimp_symmetry_real_update_strokes; - klass->get_operation = gimp_symmetry_real_get_op; + klass->get_transform = gimp_symmetry_real_get_transform; klass->active_changed = NULL; klass->update_version = gimp_symmetry_real_update_version; @@ -235,15 +238,14 @@ gimp_symmetry_real_update_strokes (GimpSymmetry *sym, g_memdup (origin, sizeof (GimpCoords))); } -static GeglNode * -gimp_symmetry_real_get_op (GimpSymmetry *sym, - gint stroke, - gint paint_width, - gint paint_height) +static void +gimp_symmetry_real_get_transform (GimpSymmetry *sym, + gint stroke, + gdouble *angle, + gboolean *reflect) { - /* The basic symmetry just returns NULL, since no transformation of the + /* The basic symmetry does nothing, since no transformation of the * brush painting happen. */ - return NULL; } static gboolean @@ -395,28 +397,89 @@ gimp_symmetry_get_coords (GimpSymmetry *sym, 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 - * @paint_width: the width of the painting area - * @paint_height: the height of the painting area * - * Returns: the operation to apply to the paint buffer for stroke number @stroke. - * NULL means to copy the original stroke as-is. + * 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, - gint paint_width, - gint paint_height) + gint stroke) { + GimpMatrix3 matrix; + g_return_val_if_fail (GIMP_IS_SYMMETRY (sym), NULL); - return GIMP_SYMMETRY_GET_CLASS (sym)->get_operation (sym, - stroke, - paint_width, - paint_height); + gimp_symmetry_get_matrix (sym, stroke, &matrix); + + if (gimp_matrix3_is_identity (&matrix)) + return NULL; + + return gimp_gegl_create_transform_node (&matrix); } /* diff --git a/app/core/gimpsymmetry.h b/app/core/gimpsymmetry.h index 20dbe02481..94746625b5 100644 --- a/app/core/gimpsymmetry.h +++ b/app/core/gimpsymmetry.h @@ -63,10 +63,10 @@ struct _GimpSymmetryClass void (* update_strokes) (GimpSymmetry *symmetry, GimpDrawable *drawable, GimpCoords *origin); - GeglNode * (* get_operation) (GimpSymmetry *symmetry, + void (* get_transform) (GimpSymmetry *symmetry, gint stroke, - gint paint_width, - gint paint_height); + gdouble *angle, + gboolean *reflect); void (* active_changed) (GimpSymmetry *symmetry); gboolean (* update_version) (GimpSymmetry *symmetry); @@ -86,10 +86,15 @@ GimpCoords * gimp_symmetry_get_origin (GimpSymmetry *symmetry); gint gimp_symmetry_get_size (GimpSymmetry *symmetry); GimpCoords * gimp_symmetry_get_coords (GimpSymmetry *symmetry, gint stroke); -GeglNode * gimp_symmetry_get_operation (GimpSymmetry *symmetry, +void gimp_symmetry_get_transform (GimpSymmetry *symmetry, gint stroke, - gint paint_width, - gint paint_height); + 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); diff --git a/app/core/gimptagcache.c b/app/core/gimptagcache.c index ab508c0243..a0e4534d8f 100644 --- a/app/core/gimptagcache.c +++ b/app/core/gimptagcache.c @@ -430,11 +430,22 @@ gimp_tag_cache_save (GimpTagCache *cache) g_printerr ("%s\n", error->message); } else if (! g_output_stream_write_all (output, buf->str, buf->len, - NULL, NULL, &error) || - ! g_output_stream_close (output, NULL, &error)) + 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) diff --git a/app/core/gimptempbuf.c b/app/core/gimptempbuf.c index 80d35ef3a0..2879be9241 100644 --- a/app/core/gimptempbuf.c +++ b/app/core/gimptempbuf.c @@ -32,6 +32,9 @@ #include "gimptempbuf.h" +#define LOCK_DATA_ALIGNMENT 16 + + struct _GimpTempBuf { gint ref_count; @@ -41,6 +44,21 @@ struct _GimpTempBuf 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, @@ -65,6 +83,9 @@ gimp_temp_buf_new (gint width, 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; } @@ -132,29 +153,31 @@ gimp_temp_buf_copy (const GimpTempBuf *src) } GimpTempBuf * -gimp_temp_buf_ref (GimpTempBuf *buf) +gimp_temp_buf_ref (const GimpTempBuf *buf) { g_return_val_if_fail (buf != NULL, NULL); - buf->ref_count++; + g_atomic_int_inc ((gint *) &buf->ref_count); - return buf; + return (GimpTempBuf *) buf; } void -gimp_temp_buf_unref (GimpTempBuf *buf) +gimp_temp_buf_unref (const GimpTempBuf *buf) { g_return_if_fail (buf != NULL); g_return_if_fail (buf->ref_count > 0); - buf->ref_count--; - - if (buf->ref_count < 1) + 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, buf); + g_slice_free (GimpTempBuf, (GimpTempBuf *) buf); } } @@ -262,6 +285,75 @@ gimp_temp_buf_data_clear (GimpTempBuf *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) { @@ -271,8 +363,8 @@ gimp_temp_buf_get_memsize (const GimpTempBuf *buf) return 0; } -GeglBuffer * -gimp_temp_buf_create_buffer (GimpTempBuf *temp_buf) +GeglBuffer * +gimp_temp_buf_create_buffer (const GimpTempBuf *temp_buf) { GeglBuffer *buffer; @@ -288,13 +380,14 @@ gimp_temp_buf_create_buffer (GimpTempBuf *temp_buf) (GDestroyNotify) gimp_temp_buf_unref, gimp_temp_buf_ref (temp_buf)); - g_object_set_data (G_OBJECT (buffer), "gimp-temp-buf", temp_buf); + g_object_set_data (G_OBJECT (buffer), + "gimp-temp-buf", (GimpTempBuf *) temp_buf); return buffer; } GdkPixbuf * -gimp_temp_buf_create_pixbuf (GimpTempBuf *temp_buf) +gimp_temp_buf_create_pixbuf (const GimpTempBuf *temp_buf) { GdkPixbuf *pixbuf; const Babl *format; @@ -346,3 +439,12 @@ gimp_gegl_buffer_get_temp_buf (GeglBuffer *buffer) 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 index 9ab13155a9..d5228f5e8b 100644 --- a/app/core/gimptempbuf.h +++ b/app/core/gimptempbuf.h @@ -19,39 +19,49 @@ #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_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 (GimpTempBuf *buf); -void gimp_temp_buf_unref (GimpTempBuf *buf); +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; +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); +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); +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_get_data (const GimpTempBuf *buf); +gsize gimp_temp_buf_get_data_size (const GimpTempBuf *buf); -guchar * gimp_temp_buf_data_clear (GimpTempBuf *buf); +guchar * gimp_temp_buf_data_clear (GimpTempBuf *buf); -gsize gimp_temp_buf_get_memsize (const 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); -GeglBuffer * gimp_temp_buf_create_buffer (GimpTempBuf *temp_buf) G_GNUC_WARN_UNUSED_RESULT; -GdkPixbuf * gimp_temp_buf_create_pixbuf (GimpTempBuf *temp_buf) G_GNUC_WARN_UNUSED_RESULT; +gsize gimp_temp_buf_get_memsize (const GimpTempBuf *buf); -GimpTempBuf * gimp_gegl_buffer_get_temp_buf (GeglBuffer *buffer); +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/gimptilehandlerprojectable.c b/app/core/gimptilehandlerprojectable.c index c60d09786d..4b9e86b5ee 100644 --- a/app/core/gimptilehandlerprojectable.c +++ b/app/core/gimptilehandlerprojectable.c @@ -27,11 +27,8 @@ #include "gimptilehandlerprojectable.h" -static void gimp_tile_handler_projectable_validate (GimpTileHandlerValidate *validate, - const GeglRectangle *rect, - const Babl *format, - gpointer dest_buf, - gint dest_stride); +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, @@ -47,7 +44,8 @@ gimp_tile_handler_projectable_class_init (GimpTileHandlerProjectableClass *klass validate_class = GIMP_TILE_HANDLER_VALIDATE_CLASS (klass); - validate_class->validate = gimp_tile_handler_projectable_validate; + validate_class->begin_validate = gimp_tile_handler_projectable_begin_validate; + validate_class->end_validate = gimp_tile_handler_projectable_end_validate; } static void @@ -56,24 +54,23 @@ gimp_tile_handler_projectable_init (GimpTileHandlerProjectable *projectable) } static void -gimp_tile_handler_projectable_validate (GimpTileHandlerValidate *validate, - const GeglRectangle *rect, - const Babl *format, - gpointer dest_buf, - gint dest_stride) +gimp_tile_handler_projectable_begin_validate (GimpTileHandlerValidate *validate) { GimpTileHandlerProjectable *handler = GIMP_TILE_HANDLER_PROJECTABLE (validate); - GeglNode *graph; - graph = gimp_projectable_get_graph (handler->projectable); + GIMP_TILE_HANDLER_VALIDATE_CLASS (parent_class)->begin_validate (validate); gimp_projectable_begin_render (handler->projectable); +} - gegl_node_blit (graph, 1.0, rect, format, - dest_buf, dest_stride, - GEGL_BLIT_DEFAULT); +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 * @@ -85,6 +82,9 @@ gimp_tile_handler_projectable_new (GimpProjectable *projectable) 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/gimptoolpreset.c b/app/core/gimptoolpreset.c index abd4bf7fcb..607f6b4cd3 100644 --- a/app/core/gimptoolpreset.c +++ b/app/core/gimptoolpreset.c @@ -174,7 +174,7 @@ gimp_tool_preset_class_init (GimpToolPresetClass *klass) "use-pattern", _("Apply stored pattern"), NULL, - TRUE, + DEFAULT_USE_PATTERN, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_USE_PALETTE, @@ -208,7 +208,6 @@ gimp_tool_preset_config_iface_init (GimpConfigInterface *iface) static void gimp_tool_preset_init (GimpToolPreset *tool_preset) { - tool_preset->tool_options = NULL; } static void diff --git a/app/core/gimpviewable.c b/app/core/gimpviewable.c index 0fbe0e7bf9..9092dcd2e1 100644 --- a/app/core/gimpviewable.c +++ b/app/core/gimpviewable.c @@ -68,6 +68,7 @@ struct _GimpViewablePrivate gchar *icon_name; GdkPixbuf *icon_pixbuf; gint freeze_count; + gboolean invalidate_pending; GimpViewable *parent; gint depth; @@ -206,6 +207,8 @@ gimp_viewable_class_init (GimpViewableClass *klass) 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; @@ -586,6 +589,8 @@ gimp_viewable_invalidate_preview (GimpViewable *viewable) if (private->freeze_count == 0) g_signal_emit (viewable, viewable_signals[INVALIDATE_PREVIEW], 0); + else + private->invalidate_pending = TRUE; } /** @@ -1251,8 +1256,7 @@ gimp_viewable_set_icon_name (GimpViewable *viewable, private = GET_PRIVATE (viewable); - g_free (private->icon_name); - private->icon_name = NULL; + g_clear_pointer (&private->icon_name, g_free); viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable); @@ -1280,7 +1284,12 @@ gimp_viewable_preview_freeze (GimpViewable *viewable) private->freeze_count++; if (private->freeze_count == 1) - g_object_notify (G_OBJECT (viewable), "frozen"); + { + if (GIMP_VIEWABLE_GET_CLASS (viewable)->preview_freeze) + GIMP_VIEWABLE_GET_CLASS (viewable)->preview_freeze (viewable); + + g_object_notify (G_OBJECT (viewable), "frozen"); + } } void @@ -1298,8 +1307,17 @@ gimp_viewable_preview_thaw (GimpViewable *viewable) if (private->freeze_count == 0) { - gimp_viewable_invalidate_preview (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); } } diff --git a/app/core/gimpviewable.h b/app/core/gimpviewable.h index b3c1fa84e4..249750ec17 100644 --- a/app/core/gimpviewable.h +++ b/app/core/gimpviewable.h @@ -97,6 +97,9 @@ struct _GimpViewableClass 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, diff --git a/app/dialogs/Makefile.am b/app/dialogs/Makefile.am index e2b399a9f9..a8d076886a 100644 --- a/app/dialogs/Makefile.am +++ b/app/dialogs/Makefile.am @@ -35,8 +35,6 @@ libappdialogs_a_sources = \ convert-precision-dialog.h \ data-delete-dialog.c \ data-delete-dialog.h \ - fade-dialog.c \ - fade-dialog.h \ file-open-dialog.c \ file-open-dialog.h \ file-open-location-dialog.c \ @@ -69,8 +67,6 @@ libappdialogs_a_sources = \ lebl-dialog.h \ module-dialog.c \ module-dialog.h \ - offset-dialog.c \ - offset-dialog.h \ palette-import-dialog.c \ palette-import-dialog.h \ preferences-dialog.c \ diff --git a/app/dialogs/Makefile.in b/app/dialogs/Makefile.in index 23592b2412..e8606a11a3 100644 --- a/app/dialogs/Makefile.in +++ b/app/dialogs/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -129,18 +128,16 @@ am__objects_2 = dialogs.$(OBJEXT) dialogs-constructors.$(OBJEXT) \ color-profile-import-dialog.$(OBJEXT) \ convert-indexed-dialog.$(OBJEXT) \ convert-precision-dialog.$(OBJEXT) \ - data-delete-dialog.$(OBJEXT) fade-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) \ + 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) \ - offset-dialog.$(OBJEXT) palette-import-dialog.$(OBJEXT) \ - preferences-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) \ @@ -175,7 +172,7 @@ am__depfiles_remade = ./$(DEPDIR)/about-dialog.Po \ ./$(DEPDIR)/convert-precision-dialog.Po \ ./$(DEPDIR)/data-delete-dialog.Po \ ./$(DEPDIR)/dialogs-constructors.Po ./$(DEPDIR)/dialogs.Po \ - ./$(DEPDIR)/fade-dialog.Po ./$(DEPDIR)/file-open-dialog.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 \ @@ -188,7 +185,7 @@ am__depfiles_remade = ./$(DEPDIR)/about-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)/offset-dialog.Po \ + ./$(DEPDIR)/module-dialog.Po \ ./$(DEPDIR)/palette-import-dialog.Po \ ./$(DEPDIR)/preferences-dialog-utils.Po \ ./$(DEPDIR)/preferences-dialog.Po \ @@ -275,8 +272,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -406,7 +401,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -545,8 +539,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ @@ -703,8 +695,6 @@ libappdialogs_a_sources = \ convert-precision-dialog.h \ data-delete-dialog.c \ data-delete-dialog.h \ - fade-dialog.c \ - fade-dialog.h \ file-open-dialog.c \ file-open-dialog.h \ file-open-location-dialog.c \ @@ -737,8 +727,6 @@ libappdialogs_a_sources = \ lebl-dialog.h \ module-dialog.c \ module-dialog.h \ - offset-dialog.c \ - offset-dialog.h \ palette-import-dialog.c \ palette-import-dialog.h \ preferences-dialog.c \ @@ -839,7 +827,6 @@ distclean-compile: @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)/fade-dialog.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 @@ -856,7 +843,6 @@ distclean-compile: @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)/offset-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 @@ -1042,7 +1028,6 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/data-delete-dialog.Po -rm -f ./$(DEPDIR)/dialogs-constructors.Po -rm -f ./$(DEPDIR)/dialogs.Po - -rm -f ./$(DEPDIR)/fade-dialog.Po -rm -f ./$(DEPDIR)/file-open-dialog.Po -rm -f ./$(DEPDIR)/file-open-location-dialog.Po -rm -f ./$(DEPDIR)/file-save-dialog.Po @@ -1059,7 +1044,6 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/layer-options-dialog.Po -rm -f ./$(DEPDIR)/lebl-dialog.Po -rm -f ./$(DEPDIR)/module-dialog.Po - -rm -f ./$(DEPDIR)/offset-dialog.Po -rm -f ./$(DEPDIR)/palette-import-dialog.Po -rm -f ./$(DEPDIR)/preferences-dialog-utils.Po -rm -f ./$(DEPDIR)/preferences-dialog.Po @@ -1131,7 +1115,6 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/data-delete-dialog.Po -rm -f ./$(DEPDIR)/dialogs-constructors.Po -rm -f ./$(DEPDIR)/dialogs.Po - -rm -f ./$(DEPDIR)/fade-dialog.Po -rm -f ./$(DEPDIR)/file-open-dialog.Po -rm -f ./$(DEPDIR)/file-open-location-dialog.Po -rm -f ./$(DEPDIR)/file-save-dialog.Po @@ -1148,7 +1131,6 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/layer-options-dialog.Po -rm -f ./$(DEPDIR)/lebl-dialog.Po -rm -f ./$(DEPDIR)/module-dialog.Po - -rm -f ./$(DEPDIR)/offset-dialog.Po -rm -f ./$(DEPDIR)/palette-import-dialog.Po -rm -f ./$(DEPDIR)/preferences-dialog-utils.Po -rm -f ./$(DEPDIR)/preferences-dialog.Po diff --git a/app/dialogs/authors.h b/app/dialogs/authors.h index b34a50d165..41168d8505 100644 --- a/app/dialogs/authors.h +++ b/app/dialogs/authors.h @@ -102,10 +102,10 @@ static const gchar * const authors[] = "David Odin", "Nelson A. de Oliveira", "Victor Oliveira", + "Yoshio Ono", "Nathan Osman", "Benjamin Otte", "Petr Ovtchenkov", - "Jehan Pagès", "Juan Palacios", "Ville Pätsi", "Akkana Peck", @@ -120,6 +120,7 @@ static const gchar * const authors[] = "Marco Rossini", "Karthikeyan S", "Daniel Sabo", + "Oleksii Samorukov", "Enrico Schröder", "Michael Schumacher", "shark0r", diff --git a/app/dialogs/convert-indexed-dialog.c b/app/dialogs/convert-indexed-dialog.c index c3685aeb83..3259190c5d 100644 --- a/app/dialogs/convert-indexed-dialog.c +++ b/app/dialogs/convert-indexed-dialog.c @@ -200,7 +200,7 @@ convert_indexed_dialog_new (GimpImage *image, adjustment = (GtkAdjustment *) gtk_adjustment_new (private->max_colors, 2, 256, 1, 8, 0); - spinbutton = gtk_spin_button_new (adjustment, 1.0, 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); diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c index f2f1ae5eae..64619df614 100644 --- a/app/dialogs/dialogs.c +++ b/app/dialogs/dialogs.c @@ -236,6 +236,7 @@ static const GimpDialogFactoryEntry entries[] = 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), diff --git a/app/dialogs/fade-dialog.c b/app/dialogs/fade-dialog.c deleted file mode 100644 index 1fcf91335d..0000000000 --- a/app/dialogs/fade-dialog.c +++ /dev/null @@ -1,214 +0,0 @@ -/* GIMP - The GNU Image Manipulation Program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You 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/gimpimage-fade.h" -#include "core/gimpimage-undo.h" -#include "core/gimpdrawable.h" -#include "core/gimpdrawableundo.h" -#include "core/gimpundostack.h" - -#include "widgets/gimplayermodebox.h" -#include "widgets/gimppropwidgets.h" -#include "widgets/gimphelp-ids.h" -#include "widgets/gimpviewabledialog.h" - -#include "fade-dialog.h" - -#include "gimp-intl.h" - - -typedef struct -{ - GimpImage *image; - GimpDrawable *drawable; - GimpContext *context; - - gboolean applied; - GimpLayerMode orig_paint_mode; - gdouble orig_opacity; -} FadeDialog; - - -/* local function prototypes */ - -static void fade_dialog_free (FadeDialog *private); -static void fade_dialog_response (GtkWidget *dialog, - gint response_id, - FadeDialog *private); -static void fade_dialog_context_changed (FadeDialog *private); - - -/* public functions */ - -GtkWidget * -fade_dialog_new (GimpImage *image, - GtkWidget *parent) -{ - FadeDialog *private; - GimpDrawableUndo *undo; - GimpDrawable *drawable; - GimpItem *item; - - GtkWidget *dialog; - GtkWidget *main_vbox; - GtkWidget *menu; - GtkWidget *scale; - gchar *title; - - g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); - g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); - - undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); - - if (! (undo && undo->applied_buffer)) - return NULL; - - item = GIMP_ITEM_UNDO (undo)->item; - drawable = GIMP_DRAWABLE (item); - - private = g_slice_new0 (FadeDialog); - - private->image = image; - private->drawable = drawable; - private->context = gimp_context_new (image->gimp, - "fade-dialog", NULL); - private->applied = FALSE; - private->orig_paint_mode = undo->paint_mode; - private->orig_opacity = undo->opacity; - - g_object_set (private->context, - "paint-mode", undo->paint_mode, - "opacity", undo->opacity, - NULL); - - title = g_strdup_printf (_("Fade %s"), gimp_object_get_name (undo)); - - dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (drawable), - private->context, - title, "gimp-edit-fade", - "edit-undo", title, - parent, - gimp_standard_help_func, - GIMP_HELP_EDIT_FADE, - - _("_Cancel"), GTK_RESPONSE_CANCEL, - _("_Fade"), GTK_RESPONSE_OK, - - NULL); - - g_free (title); - - gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), - GTK_RESPONSE_OK, - GTK_RESPONSE_CANCEL, - -1); - - gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - - g_object_weak_ref (G_OBJECT (dialog), - (GWeakNotify) fade_dialog_free, private); - - g_signal_connect (dialog, "response", - G_CALLBACK (fade_dialog_response), - private); - - main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4); - 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 paint mode menu */ - menu = gimp_prop_layer_mode_box_new (G_OBJECT (private->context), - "paint-mode", - GIMP_LAYER_MODE_CONTEXT_FADE); - gimp_layer_mode_box_set_label (GIMP_LAYER_MODE_BOX (menu), _("Mode")); - gtk_box_pack_start (GTK_BOX (main_vbox), menu, FALSE, FALSE, 0); - gtk_widget_show (menu); - - /* the opacity scale */ - scale = gimp_prop_spin_scale_new (G_OBJECT (private->context), - "opacity", - _("Opacity"), - 0.01, 0.1, 2); - gimp_prop_widget_set_factor (scale, 100, 1.0, 10.0, 1); - gtk_box_pack_start (GTK_BOX (main_vbox), scale, FALSE, FALSE, 0); - gtk_widget_show (scale); - - g_signal_connect_swapped (private->context, "paint-mode-changed", - G_CALLBACK (fade_dialog_context_changed), - private); - g_signal_connect_swapped (private->context, "opacity-changed", - G_CALLBACK (fade_dialog_context_changed), - private); - - return dialog; -} - - -/* private functions */ - -static void -fade_dialog_free (FadeDialog *private) -{ - g_slice_free (FadeDialog, private); -} - -static void -fade_dialog_response (GtkWidget *dialog, - gint response_id, - FadeDialog *private) -{ - g_signal_handlers_disconnect_by_func (private->context, - fade_dialog_context_changed, - private); - - if (response_id != GTK_RESPONSE_OK && private->applied) - { - g_object_set (private->context, - "paint-mode", private->orig_paint_mode, - "opacity", private->orig_opacity, - NULL); - - fade_dialog_context_changed (private); - } - - g_object_unref (private->context); - gtk_widget_destroy (dialog); -} - -static void -fade_dialog_context_changed (FadeDialog *private) -{ - if (gimp_image_fade (private->image, private->context)) - { - private->applied = TRUE; - gimp_image_flush (private->image); - } -} diff --git a/app/dialogs/file-open-dialog.c b/app/dialogs/file-open-dialog.c index 2113703e56..9b4c5e91ed 100644 --- a/app/dialogs/file-open-dialog.c +++ b/app/dialogs/file-open-dialog.c @@ -128,6 +128,9 @@ file_open_dialog_response (GtkWidget *dialog, 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; @@ -136,13 +139,19 @@ file_open_dialog_response (GtkWidget *dialog, { if (! file_dialog->image) { - file_dialog->image = file_open_dialog_open_image (dialog, - gimp, - file, - file_dialog->file_proc); + gimp_open_dialog_set_image ( + open_dialog, + file_open_dialog_open_image (dialog, + gimp, + file, + file_dialog->file_proc), + TRUE); if (file_dialog->image) - success = TRUE; + { + g_object_ref (file_dialog->image); + success = TRUE; + } } else if (file_open_dialog_open_layers (dialog, file_dialog->image, @@ -174,13 +183,21 @@ file_open_dialog_response (GtkWidget *dialog, if (success) { - if (open_dialog->open_as_layers && file_dialog->image) - gimp_image_flush (file_dialog->image); + 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); } diff --git a/app/dialogs/image-merge-layers-dialog.c b/app/dialogs/image-merge-layers-dialog.c index 1b1059c7cf..ce6ceac975 100644 --- a/app/dialogs/image-merge-layers-dialog.c +++ b/app/dialogs/image-merge-layers-dialog.c @@ -182,7 +182,8 @@ image_merge_layers_dialog_response (GtkWidget *dialog, private->context, private->merge_type, private->merge_active_group, - private->discard_invisible); + private->discard_invisible, + private->user_data); } else { diff --git a/app/dialogs/image-merge-layers-dialog.h b/app/dialogs/image-merge-layers-dialog.h index e763f91cc9..ee965150ee 100644 --- a/app/dialogs/image-merge-layers-dialog.h +++ b/app/dialogs/image-merge-layers-dialog.h @@ -24,7 +24,8 @@ typedef void (* GimpMergeLayersCallback) (GtkWidget *dialog, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible); + gboolean discard_invisible, + gpointer user_data); GtkWidget * diff --git a/app/dialogs/image-new-dialog.c b/app/dialogs/image-new-dialog.c index db709ef29b..3650329f95 100644 --- a/app/dialogs/image-new-dialog.c +++ b/app/dialogs/image-new-dialog.c @@ -347,7 +347,7 @@ image_new_confirm_dialog (ImageNewDialog *private) 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 Image Size\" in the Preferences " + "\"Maximum new image size\" in the Preferences " "dialog (currently %s)."), size); g_free (size); diff --git a/app/dialogs/input-devices-dialog.c b/app/dialogs/input-devices-dialog.c index 3cb4d17bbe..ace943139e 100644 --- a/app/dialogs/input-devices-dialog.c +++ b/app/dialogs/input-devices-dialog.c @@ -35,6 +35,9 @@ #include "gimp-intl.h" +#define RESPONSE_SAVE 1 + + /* local function prototypes */ static void input_devices_dialog_response (GtkWidget *dialog, @@ -59,16 +62,11 @@ input_devices_dialog_new (Gimp *gimp) gimp_standard_help_func, GIMP_HELP_INPUT_DEVICES, + _("_Save"), RESPONSE_SAVE, _("_Close"), GTK_RESPONSE_CLOSE, - _("_Save"), GTK_RESPONSE_OK, NULL); - gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), - GTK_RESPONSE_OK, - GTK_RESPONSE_CLOSE, - -1); - g_signal_connect (dialog, "response", G_CALLBACK (input_devices_dialog_response), gimp); @@ -93,7 +91,7 @@ input_devices_dialog_response (GtkWidget *dialog, { switch (response_id) { - case GTK_RESPONSE_OK: + case RESPONSE_SAVE: gimp_devices_save (gimp, TRUE); break; diff --git a/app/dialogs/keyboard-shortcuts-dialog.c b/app/dialogs/keyboard-shortcuts-dialog.c index 3e4fc74de0..9fd354b51c 100644 --- a/app/dialogs/keyboard-shortcuts-dialog.c +++ b/app/dialogs/keyboard-shortcuts-dialog.c @@ -30,11 +30,25 @@ #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) { @@ -52,13 +66,14 @@ keyboard_shortcuts_dialog_new (Gimp *gimp) gimp_standard_help_func, GIMP_HELP_KEYBOARD_SHORTCUTS, - _("_Close"), GTK_RESPONSE_OK, + _("_Save"), RESPONSE_SAVE, + _("_Close"), GTK_RESPONSE_CLOSE, NULL); g_signal_connect (dialog, "response", - G_CALLBACK (gtk_widget_destroy), - NULL); + G_CALLBACK (keyboard_shortcuts_dialog_response), + gimp); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); @@ -85,3 +100,23 @@ keyboard_shortcuts_dialog_new (Gimp *gimp) 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/layer-options-dialog.c b/app/dialogs/layer-options-dialog.c index 4c89967e5b..3c9b52b1ec 100644 --- a/app/dialogs/layer-options-dialog.c +++ b/app/dialogs/layer-options-dialog.c @@ -264,7 +264,7 @@ layer_options_dialog_new (GimpImage *image, /* The size sizeentry */ adjustment = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); - spinbutton = gtk_spin_button_new (adjustment, 1.0, 2); + 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); @@ -328,7 +328,7 @@ layer_options_dialog_new (GimpImage *image, /* The offset sizeentry */ adjustment = (GtkAdjustment *) gtk_adjustment_new (0, 1, 1, 1, 10, 0); - spinbutton = gtk_spin_button_new (adjustment, 1.0, 2); + 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); diff --git a/app/dialogs/offset-dialog.c b/app/dialogs/offset-dialog.c deleted file mode 100644 index 887daf7c7c..0000000000 --- a/app/dialogs/offset-dialog.c +++ /dev/null @@ -1,343 +0,0 @@ -/* GIMP - The GNU Image Manipulation Program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You 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 "dialogs-types.h" - -#include "core/gimp.h" -#include "core/gimpcontext.h" -#include "core/gimpdrawable.h" -#include "core/gimpitem.h" -#include "core/gimplayer.h" -#include "core/gimplayermask.h" -#include "core/gimpimage.h" - -#include "widgets/gimphelp-ids.h" -#include "widgets/gimpviewabledialog.h" - -#include "offset-dialog.h" - -#include "gimp-intl.h" - - -#define WRAP_AROUND (1 << 3) -#define FILL_MASK (GIMP_OFFSET_BACKGROUND | GIMP_OFFSET_TRANSPARENT) - - -typedef struct _OffsetDialog OffsetDialog; - -struct _OffsetDialog -{ - GimpDrawable *drawable; - GimpContext *context; - GimpOffsetType fill_type; - GimpOffsetCallback callback; - gpointer user_data; - - GtkWidget *off_se; -}; - - -/* local function prototypes */ - -static void offset_dialog_free (OffsetDialog *private); -static void offset_dialog_response (GtkWidget *dialog, - gint response_id, - OffsetDialog *private); -static void offset_dialog_half_xy_callback (GtkWidget *widget, - OffsetDialog *private); -static void offset_dialog_half_x_callback (GtkWidget *widget, - OffsetDialog *private); -static void offset_dialog_half_y_callback (GtkWidget *widget, - OffsetDialog *private); - - -/* public functions */ - -GtkWidget * -offset_dialog_new (GimpDrawable *drawable, - GimpContext *context, - GtkWidget *parent, - GimpOffsetCallback callback, - gpointer user_data) -{ - OffsetDialog *private; - GimpImage *image; - GimpItem *item; - GtkWidget *dialog; - GtkWidget *main_vbox; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *button; - GtkWidget *spinbutton; - GtkWidget *frame; - GtkWidget *radio_button; - GtkAdjustment *adjustment; - gdouble xres; - gdouble yres; - const gchar *title = 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 (GTK_IS_WIDGET (parent), NULL); - g_return_val_if_fail (callback != NULL, NULL); - - item = GIMP_ITEM (drawable); - image = gimp_item_get_image (item); - - private = g_slice_new0 (OffsetDialog); - - private->drawable = drawable; - private->context = context; - private->fill_type = gimp_drawable_has_alpha (drawable) | WRAP_AROUND; - private->callback = callback; - private->user_data = user_data; - - gimp_image_get_resolution (image, &xres, &yres); - - if (GIMP_IS_LAYER (drawable)) - title = _("Offset Layer"); - else if (GIMP_IS_LAYER_MASK (drawable)) - title = _("Offset Layer Mask"); - else if (GIMP_IS_CHANNEL (drawable)) - title = _("Offset Channel"); - else - g_warning ("%s: unexpected drawable type", G_STRFUNC); - - dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (drawable), context, - _("Offset"), "gimp-drawable-offset", - GIMP_ICON_TOOL_MOVE, - title, - parent, - gimp_standard_help_func, - GIMP_HELP_LAYER_OFFSET, - - _("_Cancel"), GTK_RESPONSE_CANCEL, - /* offset, used as a verb */ - _("_Offset"), 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) offset_dialog_free, private); - - g_signal_connect (dialog, "response", - G_CALLBACK (offset_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 offset frame */ - frame = gimp_frame_new (_("Offset")); - 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); - - adjustment = (GtkAdjustment *) - gtk_adjustment_new (1, 1, 1, 1, 10, 0); - spinbutton = gtk_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->off_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->off_se), 0, 4); - gtk_table_set_col_spacing (GTK_TABLE (private->off_se), 1, 4); - gtk_table_set_row_spacing (GTK_TABLE (private->off_se), 0, 2); - - gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->off_se), - GTK_SPIN_BUTTON (spinbutton), NULL); - gtk_table_attach_defaults (GTK_TABLE (private->off_se), spinbutton, - 1, 2, 0, 1); - gtk_widget_show (spinbutton); - - gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (private->off_se), - _("_X:"), 0, 0, 0.0); - gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (private->off_se), - _("_Y:"), 1, 0, 0.0); - - gtk_box_pack_start (GTK_BOX (vbox), private->off_se, FALSE, FALSE, 0); - gtk_widget_show (private->off_se); - - gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->off_se), GIMP_UNIT_PIXEL); - - gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->off_se), 0, - xres, FALSE); - gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->off_se), 1, - yres, FALSE); - - gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->off_se), 0, - - gimp_item_get_width (item), - gimp_item_get_width (item)); - gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->off_se), 1, - - gimp_item_get_height (item), - gimp_item_get_height (item)); - - gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->off_se), 0, - 0, gimp_item_get_width (item)); - gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->off_se), 1, - 0, gimp_item_get_height (item)); - - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->off_se), 0, 0); - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->off_se), 1, 0); - - button = gtk_button_new_with_mnemonic (_("By width/_2, height/2")); - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); - gtk_widget_show (button); - - g_signal_connect (button, "clicked", - G_CALLBACK (offset_dialog_half_xy_callback), - private); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - button = gtk_button_new_with_mnemonic ("By _width/2"); - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); - gtk_widget_show (button); - - g_signal_connect (button, "clicked", - G_CALLBACK (offset_dialog_half_x_callback), - private); - - button = gtk_button_new_with_mnemonic ("By _height/2"); - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); - gtk_widget_show (button); - - g_signal_connect (button, "clicked", - G_CALLBACK (offset_dialog_half_y_callback), - private); - - /* The edge behavior frame */ - frame = gimp_int_radio_group_new (TRUE, _("Edge Behavior"), - G_CALLBACK (gimp_radio_button_update), - &private->fill_type, private->fill_type, - - _("W_rap around"), - WRAP_AROUND, NULL, - - _("Fill with _background color"), - GIMP_OFFSET_BACKGROUND, NULL, - - _("Make _transparent"), - GIMP_OFFSET_TRANSPARENT, &radio_button, - NULL); - - if (! gimp_drawable_has_alpha (drawable)) - gtk_widget_set_sensitive (radio_button, FALSE); - - gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); - gtk_widget_show (frame); - - return dialog; -} - - -/* private functions */ - -static void -offset_dialog_free (OffsetDialog *private) -{ - g_slice_free (OffsetDialog, private); -} - -static void -offset_dialog_response (GtkWidget *dialog, - gint response_id, - OffsetDialog *private) -{ - if (response_id == GTK_RESPONSE_OK) - { - gint offset_x; - gint offset_y; - - offset_x = - RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->off_se), - 0)); - offset_y = - RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->off_se), - 1)); - - private->callback (dialog, - private->drawable, - private->context, - private->fill_type & WRAP_AROUND ? TRUE : FALSE, - private->fill_type & FILL_MASK, - offset_x, - offset_y); - } - else - { - gtk_widget_destroy (dialog); - } -} - -static void -offset_dialog_half_xy_callback (GtkWidget *widget, - OffsetDialog *private) -{ - GimpItem *item = GIMP_ITEM (private->drawable); - - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->off_se), - 0, gimp_item_get_width (item) / 2); - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->off_se), - 1, gimp_item_get_height (item) / 2); -} - -static void -offset_dialog_half_x_callback (GtkWidget *widget, - OffsetDialog *private) -{ - GimpItem *item = GIMP_ITEM (private->drawable); - - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->off_se), - 0, gimp_item_get_width (item) / 2); -} - -static void -offset_dialog_half_y_callback (GtkWidget *widget, - OffsetDialog *private) -{ - GimpItem *item = GIMP_ITEM (private->drawable); - - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->off_se), - 1, gimp_item_get_height (item) / 2); -} diff --git a/app/dialogs/offset-dialog.h b/app/dialogs/offset-dialog.h deleted file mode 100644 index b2f6d1c6a0..0000000000 --- a/app/dialogs/offset-dialog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* GIMP - The GNU Image Manipulation Program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __OFFSET_DIALOG_H__ -#define __OFFSET_DIALOG_H__ - - -typedef void (* GimpOffsetCallback) (GtkWidget *dialog, - GimpDrawable *drawable, - GimpContext *context, - gboolean wrap_around, - GimpOffsetType fill_type, - gint offset_x, - gint offset_y); - - -GtkWidget * offset_dialog_new (GimpDrawable *drawable, - GimpContext *context, - GtkWidget *parent, - GimpOffsetCallback callback, - gpointer user_data); - - -#endif /* __OFFSET_DIALOG_H__ */ diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c index ff0131d76a..6172e8edda 100644 --- a/app/dialogs/preferences-dialog.c +++ b/app/dialogs/preferences-dialog.c @@ -98,6 +98,10 @@ 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); @@ -485,6 +489,30 @@ prefs_dialog_defaults_reset (GtkWidget *widget, 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, @@ -1166,6 +1194,12 @@ prefs_dialog_new (Gimp *gimp, _("Use OpenCL"), GTK_BOX (vbox2)); + 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); + /* Image Thumbnails */ vbox2 = prefs_frame_new (_("Image Thumbnails"), GTK_CONTAINER (vbox), FALSE); @@ -1179,9 +1213,6 @@ prefs_dialog_new (Gimp *gimp, _("Maximum _filesize for thumbnailing:"), GTK_TABLE (table), 1, size_group); - g_object_unref (size_group); - size_group = NULL; - /* Document History */ vbox2 = prefs_frame_new (_("Document History"), GTK_CONTAINER (vbox), FALSE); @@ -1189,6 +1220,8 @@ prefs_dialog_new (Gimp *gimp, _("Keep record of used files in the Recent Documents list"), GTK_BOX (vbox2)); + g_clear_object (&size_group); + /***************/ /* Debugging */ @@ -1221,12 +1254,11 @@ prefs_dialog_new (Gimp *gimp, vbox2 = prefs_frame_new (_("Bug Reporting"), GTK_CONTAINER (vbox), FALSE); - size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); 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, size_group); + 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 @@ -1439,7 +1471,7 @@ prefs_dialog_new (Gimp *gimp, _("Show advanced color options"), GTK_BOX (vbox2)); - g_object_unref (size_group); + g_clear_object (&size_group); g_object_unref (store); } @@ -1456,6 +1488,10 @@ prefs_dialog_new (Gimp *gimp, 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); @@ -1481,7 +1517,7 @@ prefs_dialog_new (Gimp *gimp, 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, NULL); + GTK_TABLE (table), 0, size_group); /* Export Policies */ vbox2 = prefs_frame_new (_("Export Policies"), @@ -1491,20 +1527,26 @@ prefs_dialog_new (Gimp *gimp, _("Export the image'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. + /* 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. + /* 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. + /* 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)); @@ -1512,6 +1554,13 @@ prefs_dialog_new (Gimp *gimp, _("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 type:"), + GTK_TABLE (table), 0, size_group); /* Raw Image Importer */ vbox2 = prefs_frame_new (_("Raw Image Importer"), @@ -1540,6 +1589,9 @@ prefs_dialog_new (Gimp *gimp, config); } + g_clear_object (&size_group); + + /****************/ /* Playground */ /****************/ @@ -1622,6 +1674,7 @@ prefs_dialog_new (Gimp *gimp, GTK_TABLE (table), 0, size_group); g_object_unref (size_group); + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* Global Brush, Pattern, ... */ @@ -1650,8 +1703,7 @@ prefs_dialog_new (Gimp *gimp, GIMP_ICON_TOOL_MOVE, GTK_BOX (vbox2), size_group); - g_object_unref (size_group); - size_group = NULL; + g_clear_object (&size_group); /*******************/ @@ -1761,13 +1813,13 @@ prefs_dialog_new (Gimp *gimp, prefs_enum_combo_box_add (object, "layer-preview-size", 0, 0, _("_Default layer & channel preview size:"), - GTK_TABLE (table), 0, size_group); + GTK_TABLE (table), 0, NULL); prefs_enum_combo_box_add (object, "undo-preview-size", 0, 0, _("_Undo preview size:"), - GTK_TABLE (table), 1, size_group); + GTK_TABLE (table), 1, NULL); prefs_enum_combo_box_add (object, "navigation-preview-size", 0, 0, _("Na_vigation preview size:"), - GTK_TABLE (table), 2, size_group); + GTK_TABLE (table), 2, NULL); /* Keyboard Shortcuts */ vbox2 = prefs_frame_new (_("Keyboard Shortcuts"), @@ -2033,6 +2085,7 @@ prefs_dialog_new (Gimp *gimp, gtk_widget_show (icon_size_scale); } + /*************************/ /* Interface / Toolbox */ /*************************/ @@ -2067,8 +2120,7 @@ prefs_dialog_new (Gimp *gimp, GIMP_ICON_IMAGE, GTK_BOX (vbox2), size_group); - g_object_unref (size_group); - size_group = NULL; + g_clear_object (&size_group); /* Tool Editor */ vbox2 = prefs_frame_new (_("Tools Configuration"), @@ -2399,7 +2451,7 @@ prefs_dialog_new (Gimp *gimp, gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0); gtk_widget_show (table); - g_object_unref (size_group); + g_clear_object (&size_group); /*****************************/ @@ -2546,8 +2598,7 @@ prefs_dialog_new (Gimp *gimp, G_CALLBACK (prefs_search_clear_callback), gimp); - g_object_unref (size_group); - size_group = NULL; + g_clear_object (&size_group); /*************************/ @@ -2671,8 +2722,7 @@ prefs_dialog_new (Gimp *gimp, G_CALLBACK (prefs_resolution_calibrate_callback), entry); - g_object_unref (size_group); - size_group = NULL; + g_clear_object (&size_group); /***********************************/ @@ -2693,7 +2743,7 @@ prefs_dialog_new (Gimp *gimp, prefs_enum_combo_box_add (object, "dock-window-hint", 0, 0, _("Hint for _docks and toolbox:"), - GTK_TABLE (table), 1, size_group); + GTK_TABLE (table), 1, NULL); vbox2 = prefs_frame_new (_("Focus"), GTK_CONTAINER (vbox), FALSE); @@ -2805,8 +2855,7 @@ prefs_dialog_new (Gimp *gimp, _("Pointer _handedness:"), GTK_TABLE (table), 1, NULL); - g_object_unref (size_group); - size_group = NULL; + g_clear_object (&size_group); /********************************/ @@ -2954,6 +3003,7 @@ prefs_dialog_new (Gimp *gimp, } } + /******************************/ /* Image Windows / Snapping */ /******************************/ @@ -2981,7 +3031,7 @@ prefs_dialog_new (Gimp *gimp, prefs_spin_button_add (object, "snap-distance", 1.0, 5.0, 0, _("_Snapping distance:"), - GTK_TABLE (table), 0, size_group); + GTK_TABLE (table), 0, NULL); /*******************/ @@ -3059,6 +3109,13 @@ prefs_dialog_new (Gimp *gimp, 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 { @@ -3102,70 +3159,101 @@ prefs_dialog_new (Gimp *gimp, 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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", + { 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 } }; @@ -3185,6 +3273,17 @@ prefs_dialog_new (Gimp *gimp, &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, diff --git a/app/dialogs/print-size-dialog.c b/app/dialogs/print-size-dialog.c index 126e13bdc2..e3b9ba0c34 100644 --- a/app/dialogs/print-size-dialog.c +++ b/app/dialogs/print-size-dialog.c @@ -158,12 +158,12 @@ print_size_dialog_new (GimpImage *image, /* the print size entry */ adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); - width = gtk_spin_button_new (adj, 1.0, 2); + 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 = gtk_spin_button_new (adj, 1.0, 2); + 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); @@ -224,12 +224,12 @@ print_size_dialog_new (GimpImage *image, /* the resolution entry */ adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); - width = gtk_spin_button_new (adj, 1.0, 2); + 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 = gtk_spin_button_new (adj, 1.0, 2); + 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); diff --git a/app/dialogs/resize-dialog.c b/app/dialogs/resize-dialog.c index 7278fb7953..722f8a80b2 100644 --- a/app/dialogs/resize-dialog.c +++ b/app/dialogs/resize-dialog.c @@ -237,7 +237,7 @@ resize_dialog_new (GimpViewable *viewable, /* the offset sizeentry */ adjustment = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0); - spinbutton = gtk_spin_button_new (adjustment, 1.0, 2); + 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); diff --git a/app/display/Makefile.in b/app/display/Makefile.in index 61e57b722c..c129b994d1 100644 --- a/app/display/Makefile.in +++ b/app/display/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -345,8 +344,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -476,7 +473,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -615,8 +611,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/display/gimpcanvasboundary.c b/app/display/gimpcanvasboundary.c index b2e445fd2c..a6cab9d150 100644 --- a/app/display/gimpcanvasboundary.c +++ b/app/display/gimpcanvasboundary.c @@ -130,18 +130,10 @@ gimp_canvas_boundary_finalize (GObject *object) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); - if (private->segs) - { - g_free (private->segs); - private->segs = NULL; - private->n_segs = 0; - } + g_clear_pointer (&private->segs, g_free); + private->n_segs = 0; - if (private->transform) - { - g_free (private->transform); - private->transform = NULL; - } + g_clear_pointer (&private->transform, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/app/display/gimpcanvaspassepartout.c b/app/display/gimpcanvaspassepartout.c index 06b26e703e..cadf22cc82 100644 --- a/app/display/gimpcanvaspassepartout.c +++ b/app/display/gimpcanvaspassepartout.c @@ -170,6 +170,8 @@ gimp_canvas_passe_partout_get_extents (GimpCanvasItem *item) cairo_region_xor (outer, inner); + cairo_region_destroy (inner); + return outer; } diff --git a/app/display/gimpcanvaspolygon.c b/app/display/gimpcanvaspolygon.c index b4bf5f6183..9c670e78fe 100644 --- a/app/display/gimpcanvaspolygon.c +++ b/app/display/gimpcanvaspolygon.c @@ -121,18 +121,10 @@ gimp_canvas_polygon_finalize (GObject *object) { GimpCanvasPolygonPrivate *private = GET_PRIVATE (object); - if (private->points) - { - g_free (private->points); - private->points = NULL; - private->n_points = 0; - } + g_clear_pointer (&private->points, g_free); + private->n_points = 0; - if (private->transform) - { - g_free (private->transform); - private->transform = NULL; - } + g_clear_pointer (&private->transform, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -151,8 +143,7 @@ gimp_canvas_polygon_set_property (GObject *object, { GimpArray *array = g_value_get_boxed (value); - g_free (private->points); - private->points = NULL; + g_clear_pointer (&private->points, g_free); private->n_points = 0; if (array) diff --git a/app/display/gimpcanvasprogress.c b/app/display/gimpcanvasprogress.c index 8995d40e6c..4def1ac1ed 100644 --- a/app/display/gimpcanvasprogress.c +++ b/app/display/gimpcanvasprogress.c @@ -173,11 +173,7 @@ gimp_canvas_progress_finalize (GObject *object) { GimpCanvasProgressPrivate *private = GET_PRIVATE (object); - if (private->text) - { - g_free (private->text); - private->text = NULL; - } + g_clear_pointer (&private->text, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c index 7429c2da7f..04439043a0 100644 --- a/app/display/gimpdisplay.c +++ b/app/display/gimpdisplay.c @@ -725,6 +725,7 @@ void gimp_display_empty (GimpDisplay *display) { GimpDisplayPrivate *private; + GList *iter; g_return_if_fail (GIMP_IS_DISPLAY (display)); @@ -732,6 +733,14 @@ gimp_display_empty (GimpDisplay *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)); diff --git a/app/display/gimpdisplayshell-dnd.c b/app/display/gimpdisplayshell-dnd.c index 996cb75595..24ed1c238a 100644 --- a/app/display/gimpdisplayshell-dnd.c +++ b/app/display/gimpdisplayshell-dnd.c @@ -518,12 +518,11 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, GList *list; gboolean open_as_layers; - /* If the app is already being torn down, shell->display might be NULL here. - * Play it safe. */ + /* If the app is already being torn down, shell->display might be + * NULL here. Play it safe. + */ if (! shell->display) - { - return; - } + return; image = gimp_display_get_image (shell->display); context = gimp_get_user_context (shell->display->gimp); @@ -532,6 +531,9 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, 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); @@ -543,6 +545,7 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, { /* It seems as if GIMP is being torn down for quitting. Bail out. */ g_object_unref (file); + g_clear_object (&image); return; } @@ -558,11 +561,14 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, if (new_layers) { - gint x, y; - gint width, height; + gint x = 0; + gint y = 0; + gint width = gimp_image_get_width (image); + gint height = gimp_image_get_height (image); - gimp_display_shell_untransform_viewport (shell, &x, &y, - &width, &height); + if (gimp_display_get_image (shell->display)) + gimp_display_shell_untransform_viewport (shell, &x, &y, + &width, &height); gimp_image_add_layers (image, new_layers, GIMP_IMAGE_ACTIVE_PARENT, -1, @@ -601,13 +607,20 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, gimp_widget_get_monitor (widget), &status, &error); - if (! image && status != GIMP_PDB_CANCEL) - warn = TRUE; + 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. */ + * is being torn down for quitting. + */ if (warn && shell->display) { gimp_message (shell->display->gimp, G_OBJECT (shell->display), @@ -622,6 +635,8 @@ gimp_display_shell_drop_uri_list (GtkWidget *widget, if (image) gimp_display_shell_dnd_flush (shell, image); + + g_clear_object (&image); } static void diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 28456ae48d..e7dedba1d8 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -41,6 +41,9 @@ #include "gimpdisplayshell-transform.h" #include "gimpdisplayxfer.h" +#ifdef GDK_WINDOWING_QUARTZ +#import +#endif /* #define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 */ @@ -147,6 +150,17 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell, 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); diff --git a/app/display/gimpdisplayshell-handlers.c b/app/display/gimpdisplayshell-handlers.c index f208272f21..85539a5798 100644 --- a/app/display/gimpdisplayshell-handlers.c +++ b/app/display/gimpdisplayshell-handlers.c @@ -120,6 +120,8 @@ static void gimp_display_shell_sample_point_move_handler (GimpImage *i 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, @@ -262,15 +264,15 @@ gimp_display_shell_connect (GimpDisplayShell *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, "precision-changed", - G_CALLBACK (gimp_display_shell_precision_changed_handler), - shell); g_signal_connect (image, "saved", G_CALLBACK (gimp_display_shell_saved_handler), shell); @@ -482,6 +484,9 @@ gimp_display_shell_disconnect (GimpDisplayShell *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); @@ -845,6 +850,13 @@ gimp_display_shell_invalidate_preview_handler (GimpImage *image, 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) diff --git a/app/display/gimpdisplayshell-rotate-dialog.c b/app/display/gimpdisplayshell-rotate-dialog.c index e50acedde7..c8992356dc 100644 --- a/app/display/gimpdisplayshell-rotate-dialog.c +++ b/app/display/gimpdisplayshell-rotate-dialog.c @@ -157,7 +157,7 @@ gimp_display_shell_rotate_dialog (GimpDisplayShell *shell) data->rotate_adj = (GtkAdjustment *) gtk_adjustment_new (shell->rotate_angle, 0.0, 360.0, 1, 15, 0); - spin = gtk_spin_button_new (data->rotate_adj, 1.0, 2); + 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); diff --git a/app/display/gimpdisplayshell-scale-dialog.c b/app/display/gimpdisplayshell-scale-dialog.c index 26d7e8f43b..f3d2eabe3b 100644 --- a/app/display/gimpdisplayshell-scale-dialog.c +++ b/app/display/gimpdisplayshell-scale-dialog.c @@ -166,7 +166,7 @@ gimp_display_shell_scale_dialog (GimpDisplayShell *shell) data->num_adj = (GtkAdjustment *) gtk_adjustment_new (num, 1, 256, 1, 8, 0); - spin = gtk_spin_button_new (data->num_adj, 1.0, 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); @@ -178,7 +178,7 @@ gimp_display_shell_scale_dialog (GimpDisplayShell *shell) data->denom_adj = (GtkAdjustment *) gtk_adjustment_new (denom, 1, 256, 1, 8, 0); - spin = gtk_spin_button_new (data->denom_adj, 1.0, 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); @@ -193,7 +193,7 @@ gimp_display_shell_scale_dialog (GimpDisplayShell *shell) gtk_adjustment_new (fabs (shell->other_scale) * 100, 100.0 / 256.0, 25600.0, 10, 50, 0); - spin = gtk_spin_button_new (data->scale_adj, 1.0, 2); + 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); diff --git a/app/display/gimpdisplayshell-tool-events.c b/app/display/gimpdisplayshell-tool-events.c index 743b89c9a9..714f39f77f 100644 --- a/app/display/gimpdisplayshell-tool-events.c +++ b/app/display/gimpdisplayshell-tool-events.c @@ -32,6 +32,7 @@ #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" @@ -66,7 +67,9 @@ #include "gimpdisplayshell-transform.h" #include "gimpimagewindow.h" #include "gimpmotionbuffer.h" +#include "gimpstatusbar.h" +#include "gimp-intl.h" #include "gimp-log.h" @@ -1239,6 +1242,10 @@ gimp_display_shell_canvas_tool_events_internal (GtkWidget *canvas, 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: @@ -1320,6 +1327,17 @@ gimp_display_shell_canvas_tool_events_internal (GtkWidget *canvas, 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 && @@ -1537,13 +1555,57 @@ gimp_display_shell_start_scrolling (GimpDisplayShell *shell, 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); + { + 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); + { + 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); @@ -1565,6 +1627,7 @@ gimp_display_shell_stop_scrolling (GimpDisplayShell *shell, 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. @@ -1600,6 +1663,10 @@ gimp_display_shell_handle_scrolling (GimpDisplayShell *shell, 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, diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h index 5f7b9bae50..acaf6e8b8e 100644 --- a/app/display/gimpdisplayshell.h +++ b/app/display/gimpdisplayshell.h @@ -208,6 +208,8 @@ struct _GimpDisplayShell gdouble rotate_drag_angle; gboolean scaling; gpointer scroll_info; + gboolean layer_picking; + GimpLayer *picked_layer; GeglBuffer *mask; gint mask_offset_x; diff --git a/app/display/gimpimagewindow.c b/app/display/gimpimagewindow.c index 9defe164fc..6d659872d5 100644 --- a/app/display/gimpimagewindow.c +++ b/app/display/gimpimagewindow.c @@ -78,6 +78,8 @@ #include "gimpstatusbar.h" #include "gimp-log.h" +#include "gimp-priorities.h" + #include "gimp-intl.h" @@ -1924,8 +1926,10 @@ gimp_image_window_update_ui_manager (GimpImageWindow *window) if (! private->update_ui_manager_idle_id) { private->update_ui_manager_idle_id = - g_idle_add ((GSourceFunc) gimp_image_window_update_ui_manager_idle, - window); + g_idle_add_full (GIMP_PRIORITY_IMAGE_WINDOW_UPDATE_UI_MANAGER_IDLE, + (GSourceFunc) gimp_image_window_update_ui_manager_idle, + window, + NULL); } } diff --git a/app/display/gimpstatusbar.c b/app/display/gimpstatusbar.c index 8f51b5f63d..0c0ca14907 100644 --- a/app/display/gimpstatusbar.c +++ b/app/display/gimpstatusbar.c @@ -235,6 +235,9 @@ gimp_statusbar_init (GimpStatusbar *statusbar) 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); diff --git a/app/display/gimptoolcompass.c b/app/display/gimptoolcompass.c index 626195c148..472d57a6e4 100644 --- a/app/display/gimptoolcompass.c +++ b/app/display/gimptoolcompass.c @@ -77,7 +77,8 @@ enum PROP_X3, PROP_Y3, PROP_PIXEL_ANGLE, - PROP_UNIT_ANGLE + PROP_UNIT_ANGLE, + PROP_EFFECTIVE_ORIENTATION }; enum @@ -98,6 +99,7 @@ struct _GimpToolCompassPrivate gdouble display_angle; gdouble pixel_angle; gdouble unit_angle; + GimpCompassOrientation effective_orientation; CompassFunction function; gdouble mouse_x; @@ -274,6 +276,12 @@ gimp_tool_compass_class_init (GimpToolCompassClass *klass) 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 @@ -362,6 +370,9 @@ gimp_tool_compass_set_property (GObject *object, 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; @@ -383,9 +394,6 @@ gimp_tool_compass_set_property (GObject *object, case PROP_Y3: private->y[2] = g_value_get_int (value); break; - case PROP_ORIENTATION: - private->orientation = g_value_get_enum (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -404,6 +412,9 @@ gimp_tool_compass_get_property (GObject *object, 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; @@ -425,15 +436,15 @@ gimp_tool_compass_get_property (GObject *object, case PROP_Y3: g_value_set_int (value, private->y[2]); break; - case PROP_ORIENTATION: - g_value_set_enum (value, private->orientation); - 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); @@ -1086,6 +1097,8 @@ gimp_tool_compass_update_angle (GimpToolCompass *compass, 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]; } @@ -1144,30 +1157,43 @@ gimp_tool_compass_update_angle (GimpToolCompass *compass, return; } - else if (orientation == GIMP_COMPASS_ORIENTATION_AUTO && - fabs (private->display_angle) > G_PI / 4.0 + EPSILON) + else if (orientation == GIMP_COMPASS_ORIENTATION_AUTO) { - gimp_tool_compass_update_angle (compass, - GIMP_COMPASS_ORIENTATION_VERTICAL, - FALSE); + 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; + return; + } } } - if (pixel_angle != private->pixel_angle) + if (fabs (pixel_angle - private->pixel_angle) > EPSILON) { private->pixel_angle = pixel_angle; g_object_notify (G_OBJECT (compass), "pixel-angle"); } - if (unit_angle != private->unit_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"); + } } diff --git a/app/display/gimptoolgui.c b/app/display/gimptoolgui.c index 23bb00dafc..28c40bf0f7 100644 --- a/app/display/gimptoolgui.c +++ b/app/display/gimptoolgui.c @@ -84,28 +84,30 @@ struct _GimpToolGuiPrivate #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_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_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_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 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); +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) @@ -225,7 +227,6 @@ gimp_tool_gui_new (GimpToolInfo *tool_info, GimpToolGui *gui; GimpToolGuiPrivate *private; va_list args; - const gchar *button_text; g_return_val_if_fail (GIMP_IS_TOOL_INFO (tool_info), NULL); @@ -254,16 +255,7 @@ gimp_tool_gui_new (GimpToolInfo *tool_info, va_start (args, overlay); - for (button_text = va_arg (args, const gchar *); - button_text; - button_text = va_arg (args, const gchar *)) - { - gint response_id = va_arg (args, gint); - - private->response_entries = g_list_append (private->response_entries, - response_entry_new (response_id, - button_text)); - } + gimp_tool_gui_add_buttons_valist (gui, args); va_end (args); @@ -637,6 +629,45 @@ gimp_tool_gui_get_focus_on_map (GimpToolGui *gui) 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) @@ -678,7 +709,8 @@ gimp_tool_gui_set_response_sensitive (GimpToolGui *gui, entry = response_entry_find (private->response_entries, response_id); - g_return_if_fail (entry != NULL); + if (! entry) + return; entry->sensitive = sensitive; @@ -747,14 +779,7 @@ gimp_tool_gui_create_dialog (GimpToolGui *gui, { ResponseEntry *entry = list->data; - 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); + gimp_tool_gui_add_dialog_button (gui, entry); } if (private->default_response != -1) @@ -781,14 +806,7 @@ gimp_tool_gui_create_dialog (GimpToolGui *gui, { ResponseEntry *entry = list->data; - 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); + gimp_tool_gui_add_dialog_button (gui, entry); } if (private->default_response != -1) @@ -817,6 +835,38 @@ gimp_tool_gui_create_dialog (GimpToolGui *gui, 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) { diff --git a/app/display/gimptoolgui.h b/app/display/gimptoolgui.h index 1e86b4ea06..7b099a5b65 100644 --- a/app/display/gimptoolgui.h +++ b/app/display/gimptoolgui.h @@ -97,6 +97,12 @@ 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, diff --git a/app/display/gimptoolpath.c b/app/display/gimptoolpath.c index 027ef9486e..9e2cf543ee 100644 --- a/app/display/gimptoolpath.c +++ b/app/display/gimptoolpath.c @@ -37,6 +37,8 @@ #include "widgets/gimpwidgets-utils.h" +#include "tools/gimptools-utils.h" + #include "gimpcanvashandle.h" #include "gimpcanvasitem-utils.h" #include "gimpcanvasline.h" @@ -492,12 +494,18 @@ 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_set_status (GIMP_TOOL_WIDGET (path), - _("The active path is locked.")); + 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; diff --git a/app/display/gimptoolpolygon.c b/app/display/gimptoolpolygon.c index 7c8174eec8..4f2c20b518 100644 --- a/app/display/gimptoolpolygon.c +++ b/app/display/gimptoolpolygon.c @@ -37,6 +37,7 @@ #include "display-types.h" #include "core/gimp-utils.h" +#include "core/gimpmarshal.h" #include "widgets/gimpwidgets-utils.h" @@ -57,6 +58,13 @@ #define NO_CLICK_TIME_AVAILABLE 0 +enum +{ + CHANGE_COMPLETE, + LAST_SIGNAL +}; + + struct _GimpToolPolygonPrivate { /* Index of grabbed segment index. */ @@ -101,9 +109,6 @@ struct _GimpToolPolygonPrivate /* Is the polygon closed? */ gboolean polygon_closed; - /* The selection operation active when the tool was started */ - GimpChannelOps operation_at_start; - /* Whether or not to constrain the angle for newly created polygonal * segments. */ @@ -188,6 +193,8 @@ static gboolean gimp_tool_polygon_get_cursor (GimpToolWidget *widget 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); @@ -197,6 +204,8 @@ G_DEFINE_TYPE_WITH_PRIVATE (GimpToolPolygon, gimp_tool_polygon, #define parent_class gimp_tool_polygon_parent_class +static guint polygon_signals[LAST_SIGNAL] = { 0, }; + static const GimpVector2 vector2_zero = { 0.0, 0.0 }; @@ -222,6 +231,15 @@ gimp_tool_polygon_class_init (GimpToolPolygonClass *klass) 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 @@ -1134,6 +1152,8 @@ gimp_tool_polygon_button_release (GimpToolWidget *widget, gimp_tool_polygon_revert_to_saved_state (polygon); priv->polygon_closed = TRUE; + + gimp_tool_polygon_change_complete (polygon); } priv->last_click_time = time; @@ -1155,6 +1175,8 @@ gimp_tool_polygon_button_release (GimpToolWidget *widget, { priv->polygon_closed = TRUE; } + + gimp_tool_polygon_change_complete (polygon); break; case GIMP_BUTTON_RELEASE_CANCEL: @@ -1304,12 +1326,16 @@ static gboolean gimp_tool_polygon_key_press (GimpToolWidget *widget, GdkEventKey *kevent) { - GimpToolPolygon *polygon = GIMP_TOOL_POLYGON (widget); + 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: @@ -1385,6 +1411,12 @@ gimp_tool_polygon_get_cursor (GimpToolWidget *widget, 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) @@ -1435,15 +1467,29 @@ gimp_tool_polygon_new (GimpDisplayShell *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 = polygon->private; + GimpToolPolygonPrivate *private; - g_return_if_fail (points != NULL && n_points != NULL); + g_return_if_fail (GIMP_IS_TOOL_POLYGON (polygon)); - *points = private->points; - *n_points = private->n_points; + 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 index a01507a7e1..fa3560a17f 100644 --- a/app/display/gimptoolpolygon.h +++ b/app/display/gimptoolpolygon.h @@ -47,6 +47,9 @@ struct _GimpToolPolygon struct _GimpToolPolygonClass { GimpToolWidgetClass parent_class; + + /* signals */ + void (* change_complete) (GimpToolPolygon *polygon); }; @@ -54,6 +57,7 @@ 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); diff --git a/app/display/gimptoolrectangle.c b/app/display/gimptoolrectangle.c index 97c5d058f7..c0c3a3ef7e 100644 --- a/app/display/gimptoolrectangle.c +++ b/app/display/gimptoolrectangle.c @@ -222,12 +222,12 @@ struct _GimpToolRectanglePrivate /* Whether to draw round corners */ gboolean round_corners; - gboolean corner_radius; + gdouble corner_radius; /* The title for the statusbar coords */ gchar *status_title; - /* For saving in case of cancelation. */ + /* For saving in case of cancellation. */ gdouble saved_x1; gdouble saved_y1; gdouble saved_x2; @@ -556,7 +556,7 @@ gimp_tool_rectangle_class_init (GimpToolRectangleClass *klass) g_object_class_install_property (object_class, PROP_CORNER_RADIUS, g_param_spec_double ("corner-radius", NULL, NULL, - 0.0, 1000.0, 5.0, + 0.0, 10000.0, 10.0, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT)); @@ -819,11 +819,7 @@ gimp_tool_rectangle_finalize (GObject *object) GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (object); GimpToolRectanglePrivate *private = rectangle->private; - if (private->status_title) - { - g_free (private->status_title); - private->status_title = NULL; - } + g_clear_pointer (&private->status_title, g_free); G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/app/display/gimptooltransformgrid.c b/app/display/gimptooltransformgrid.c index 6c150b6130..eb649222c3 100644 --- a/app/display/gimptooltransformgrid.c +++ b/app/display/gimptooltransformgrid.c @@ -801,6 +801,17 @@ transform_is_convex (GimpVector2 *pos) 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) { @@ -876,11 +887,11 @@ calcangle (GimpVector2 a, length = norm (a) * norm (b); - angle = acos (dotprod (a, b)/length); + angle = acos (SAFE_CLAMP (dotprod (a, b) / length, -1.0, +1.0)); angle2 = b.y; b.y = -b.x; b.x = angle2; - angle2 = acos (dotprod (a, b)/length); + angle2 = acos (SAFE_CLAMP (dotprod (a, b) / length, -1.0, +1.0)); return ((angle2 > G_PI / 2.0) ? angle : 2.0 * G_PI - angle); } @@ -1309,16 +1320,29 @@ gimp_tool_transform_grid_motion (GimpToolWidget *widget, newpos[3].y = oldpos[3].y = private->prev_ty4; /* put center point in this array too */ - oldpos[4].x = (oldpos[0].x + oldpos[1].x + oldpos[2].x + oldpos[3].x) / 4.; - oldpos[4].y = (oldpos[0].y + oldpos[1].y + oldpos[2].y + oldpos[3].y) / 4.; + oldpos[4].x = private->prev_tcx; + oldpos[4].y = private->prev_tcy; d = vectorsubtract (cur, mouse); newpivot_x = &private->tpx; newpivot_y = &private->tpy; - pivot.x = private->prev_tpx; - pivot.y = private->prev_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) @@ -1726,12 +1750,6 @@ gimp_tool_transform_grid_motion (GimpToolWidget *widget, } } - for (i = 0; i < 4; i++) - { - *x[i] = newpos[i].x; - *y[i] = newpos[i].y; - } - /* this will have been set to TRUE if an operation used the pivot in * addition to being a user option */ @@ -1744,6 +1762,22 @@ gimp_tool_transform_grid_motion (GimpToolWidget *widget, 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 */ @@ -2307,14 +2341,24 @@ gimp_tool_transform_grid_update_box (GimpToolTransformGrid *grid) private->tpx = private->pivot_x; private->tpy = private->pivot_y; - private->tcx = (private->tx1 + - private->tx2 + - private->tx3 + - private->tx4) / 4.0; - private->tcy = (private->ty1 + - private->ty2 + - private->ty3 + - private->ty4) / 4.0; + 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 diff --git a/app/display/gimptooltransformgrid.h b/app/display/gimptooltransformgrid.h index 7037c5a8c4..e42b9ed4ed 100644 --- a/app/display/gimptooltransformgrid.h +++ b/app/display/gimptooltransformgrid.h @@ -22,9 +22,14 @@ #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, diff --git a/app/display/gimptoolwidget.c b/app/display/gimptoolwidget.c index d54c5dd2a3..d62cfe2505 100644 --- a/app/display/gimptoolwidget.c +++ b/app/display/gimptoolwidget.c @@ -20,6 +20,8 @@ #include "config.h" +#include + #include #include #include @@ -56,6 +58,7 @@ enum SNAP_OFFSETS, STATUS, STATUS_COORDS, + MESSAGE, FOCUS_CHANGED, LAST_SIGNAL }; @@ -71,6 +74,7 @@ struct _GimpToolWidgetPrivate gint snap_width; gint snap_height; + gboolean visible; gboolean focus; }; @@ -173,6 +177,16 @@ gimp_tool_widget_class_init (GimpToolWidgetClass *klass) 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), @@ -200,6 +214,8 @@ static void gimp_tool_widget_init (GimpToolWidget *widget) { widget->private = gimp_tool_widget_get_instance_private (widget); + + widget->private->visible = TRUE; } static void @@ -213,6 +229,8 @@ gimp_tool_widget_constructed (GObject *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); } static void @@ -338,6 +356,32 @@ gimp_tool_widget_get_item (GimpToolWidget *widget) 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) @@ -449,6 +493,39 @@ gimp_tool_widget_set_status_coords (GimpToolWidget *widget, 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) @@ -772,10 +849,14 @@ gimp_tool_widget_button_press (GimpToolWidget *widget, g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), 0); g_return_val_if_fail (coords != NULL, 0); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_press) - return GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_press (widget, - coords, time, state, - press_type); + 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; } @@ -790,10 +871,13 @@ gimp_tool_widget_button_release (GimpToolWidget *widget, g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); g_return_if_fail (coords != NULL); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_release) - GIMP_TOOL_WIDGET_GET_CLASS (widget)->button_release (widget, - coords, time, state, - release_type); + 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 @@ -805,9 +889,12 @@ gimp_tool_widget_motion (GimpToolWidget *widget, g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); g_return_if_fail (coords != NULL); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion) - GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion (widget, - coords, time, state); + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion (widget, + coords, time, state); + } } GimpHit @@ -819,9 +906,13 @@ gimp_tool_widget_hit (GimpToolWidget *widget, g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), GIMP_HIT_NONE); g_return_val_if_fail (coords != NULL, GIMP_HIT_NONE); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->hit) - return GIMP_TOOL_WIDGET_GET_CLASS (widget)->hit (widget, - coords, state, proximity); + 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; } @@ -835,9 +926,12 @@ gimp_tool_widget_hover (GimpToolWidget *widget, g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); g_return_if_fail (coords != NULL); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover) - GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover (widget, - coords, state, proximity); + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover (widget, + coords, state, proximity); + } } void @@ -845,8 +939,11 @@ gimp_tool_widget_leave_notify (GimpToolWidget *widget) { g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->leave_notify) - GIMP_TOOL_WIDGET_GET_CLASS (widget)->leave_notify (widget); + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->leave_notify) + { + GIMP_TOOL_WIDGET_GET_CLASS (widget)->leave_notify (widget); + } } gboolean @@ -856,8 +953,11 @@ gimp_tool_widget_key_press (GimpToolWidget *widget, g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); g_return_val_if_fail (kevent != NULL, FALSE); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_press) - return GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_press (widget, kevent); + 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; } @@ -869,8 +969,11 @@ gimp_tool_widget_key_release (GimpToolWidget *widget, g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); g_return_val_if_fail (kevent != NULL, FALSE); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_release) - return GIMP_TOOL_WIDGET_GET_CLASS (widget)->key_release (widget, kevent); + 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; } @@ -883,9 +986,12 @@ gimp_tool_widget_motion_modifier (GimpToolWidget *widget, { g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion_modifier) - GIMP_TOOL_WIDGET_GET_CLASS (widget)->motion_modifier (widget, - key, press, state); + 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 @@ -896,9 +1002,12 @@ gimp_tool_widget_hover_modifier (GimpToolWidget *widget, { g_return_if_fail (GIMP_IS_TOOL_WIDGET (widget)); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover_modifier) - GIMP_TOOL_WIDGET_GET_CLASS (widget)->hover_modifier (widget, - key, press, state); + 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 @@ -913,7 +1022,8 @@ gimp_tool_widget_get_cursor (GimpToolWidget *widget, g_return_val_if_fail (GIMP_IS_TOOL_WIDGET (widget), FALSE); g_return_val_if_fail (coords != NULL, FALSE); - if (GIMP_TOOL_WIDGET_GET_CLASS (widget)->get_cursor) + if (widget->private->visible && + GIMP_TOOL_WIDGET_GET_CLASS (widget)->get_cursor) { GimpCursorType my_cursor; GimpToolCursorType my_tool_cursor; diff --git a/app/display/gimptoolwidget.h b/app/display/gimptoolwidget.h index 68437ee64a..be78cbbe2f 100644 --- a/app/display/gimptoolwidget.h +++ b/app/display/gimptoolwidget.h @@ -69,6 +69,8 @@ struct _GimpToolWidgetClass const gchar *separator, gdouble y, const gchar *help); + void (* message) (GimpToolWidget *widget, + const gchar *message); void (* focus_changed) (GimpToolWidget *widget); /* virtual functions */ @@ -125,6 +127,10 @@ 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); @@ -156,6 +162,12 @@ void gimp_tool_widget_set_status_coords (GimpToolWidget *widget, 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, diff --git a/app/display/gimptoolwidgetgroup.c b/app/display/gimptoolwidgetgroup.c index 8fcb162d8c..d62f4c926b 100644 --- a/app/display/gimptoolwidgetgroup.c +++ b/app/display/gimptoolwidgetgroup.c @@ -123,6 +123,9 @@ static void gimp_tool_widget_group_child_status_coords (GimpToolWidg 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); @@ -201,6 +204,9 @@ gimp_tool_widget_group_init (GimpToolWidgetGroup *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); @@ -596,6 +602,18 @@ gimp_tool_widget_group_child_status_coords (GimpToolWidget *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) diff --git a/app/file-data/Makefile.am b/app/file-data/Makefile.am new file mode 100644 index 0000000000..ba0fd577f2 --- /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 0000000000..8bb2cc2ee2 --- /dev/null +++ b/app/file-data/Makefile.in @@ -0,0 +1,932 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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)/m4macros/gtk-doc.m4 \ + $(top_srcdir)/m4macros/intltool.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(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_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_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@ +GIMP_PLUGINS = @GIMP_PLUGINS@ +GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@ +GIMP_REAL_VERSION = @GIMP_REAL_VERSION@ +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@ +GLIB_WIN_BUG_CFLAGS = @GLIB_WIN_BUG_CFLAGS@ +GLIB_WIN_BUG_LIBS = @GLIB_WIN_BUG_LIBS@ +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@ +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@ +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 0000000000..63d4c0e401 --- /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 0000000000..fb3bf47ff3 --- /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 0000000000..240eb9dbca --- /dev/null +++ b/app/file-data/file-data-gih.c @@ -0,0 +1,346 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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); + + 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 0000000000..877e4a53eb --- /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 0000000000..21506fe30e --- /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 0000000000..1417139290 --- /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 0000000000..411d2cadba --- /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/dialogs/fade-dialog.h b/app/file-data/file-data.h similarity index 80% rename from app/dialogs/fade-dialog.h rename to app/file-data/file-data.h index 37e3848653..a36381d81a 100644 --- a/app/dialogs/fade-dialog.h +++ b/app/file-data/file-data.h @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -#ifndef __FADE_DIALOG_H__ -#define __FADE_DIALOG_H__ +#ifndef __FILE_DATA_H__ +#define __FILE_DATA_H__ -GtkWidget * fade_dialog_new (GimpImage *image, - GtkWidget *parent); +void file_data_init (Gimp *gimp); +void file_data_exit (Gimp *gimp); -#endif /* __FADE_DIALOG_H__ */ +#endif /* __FILE_DATA_H__ */ diff --git a/app/file/Makefile.in b/app/file/Makefile.in index f902e4887c..02f91f4836 100644 --- a/app/file/Makefile.in +++ b/app/file/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -215,8 +214,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -346,7 +343,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -485,8 +481,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/file/file-open.c b/app/file/file-open.c index c833a43909..2cddfbb4fe 100644 --- a/app/file/file-open.c +++ b/app/file/file-open.c @@ -265,7 +265,7 @@ file_open_image (Gimp *gimp, { if (error && ! *error) g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, - _("%s plug-In could not open image"), + _("%s plug-in could not open image"), gimp_procedure_get_label (GIMP_PROCEDURE (file_proc))); } @@ -636,7 +636,8 @@ file_open_layers (Gimp *gimp, layer = gimp_image_merge_visible_layers (new_image, context, GIMP_CLIP_TO_IMAGE, - FALSE, FALSE); + FALSE, FALSE, + NULL); layers = g_list_prepend (NULL, layer); } diff --git a/app/gegl/Makefile.am b/app/gegl/Makefile.am index f7fca48d67..e4020563c1 100644 --- a/app/gegl/Makefile.am +++ b/app/gegl/Makefile.am @@ -31,7 +31,7 @@ libappgegl_generic_a_sources = \ gimp-gegl-loops.h \ gimp-gegl-mask.c \ gimp-gegl-mask.h \ - gimp-gegl-mask-combine.c \ + gimp-gegl-mask-combine.cc \ gimp-gegl-mask-combine.h \ gimp-gegl-nodes.c \ gimp-gegl-nodes.h \ diff --git a/app/gegl/Makefile.in b/app/gegl/Makefile.in index 2d6d403585..1e1cef5b5d 100644 --- a/app/gegl/Makefile.in +++ b/app/gegl/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -258,8 +257,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -389,7 +386,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -528,8 +524,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ @@ -683,7 +677,7 @@ libappgegl_generic_a_sources = \ gimp-gegl-loops.h \ gimp-gegl-mask.c \ gimp-gegl-mask.h \ - gimp-gegl-mask-combine.c \ + gimp-gegl-mask-combine.cc \ gimp-gegl-mask-combine.h \ gimp-gegl-nodes.c \ gimp-gegl-nodes.h \ diff --git a/app/gegl/gimp-babl-compat.c b/app/gegl/gimp-babl-compat.c index f94b708638..b077f863e5 100644 --- a/app/gegl/gimp-babl-compat.c +++ b/app/gegl/gimp-babl-compat.c @@ -81,3 +81,13 @@ gimp_babl_compat_u8_format (const Babl *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 index a342141c09..778bf3ec90 100644 --- a/app/gegl/gimp-babl-compat.h +++ b/app/gegl/gimp-babl-compat.h @@ -25,6 +25,7 @@ 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 index c9b9b1b007..b2dc20a859 100644 --- a/app/gegl/gimp-babl.c +++ b/app/gegl/gimp-babl.c @@ -780,6 +780,25 @@ gimp_babl_is_valid (GimpImageBaseType base_type, 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, @@ -1266,6 +1285,32 @@ gimp_babl_component_format (GimpImageBaseType base_type, 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) diff --git a/app/gegl/gimp-babl.h b/app/gegl/gimp-babl.h index 12db267eaa..3062e02425 100644 --- a/app/gegl/gimp-babl.h +++ b/app/gegl/gimp-babl.h @@ -22,35 +22,41 @@ #define __GIMP_BABL_H__ -void gimp_babl_init (void); -void gimp_babl_init_fishes (GimpInitStatusFunc status_callback); +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); +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); +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); +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); +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 (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); -gchar ** gimp_babl_print_pixel (const Babl *format, - gpointer pixel); +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 index 90f3092dee..ad838cd41e 100644 --- a/app/gegl/gimp-gegl-apply-operation.c +++ b/app/gegl/gimp-gegl-apply-operation.c @@ -30,6 +30,7 @@ #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" @@ -75,17 +76,18 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, GeglBuffer *cache, const GeglRectangle *valid_rects, gint n_valid_rects, - gboolean cancellable) + gboolean cancelable) { - GeglNode *gegl; - GeglNode *effect; - GeglNode *dest_node; - GeglNode *operation_src_node = NULL; - GeglRectangle rect = { 0, }; - GeglProcessor *processor = NULL; - gboolean progress_started = FALSE; - gdouble value; - gboolean cancel = FALSE; + GeglNode *gegl; + GeglNode *effect; + GeglNode *dest_node; + GeglNode *operation_src_node = NULL; + GimpChunkIterator *iter; + cairo_region_t *region; + gboolean progress_started = FALSE; + gboolean cancel = FALSE; + gint all_pixels; + gint 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); @@ -95,15 +97,8 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, 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) - { - rect = *dest_rect; - } - else - { - rect = *GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (dest_buffer), - gegl_buffer_get_height (dest_buffer)); - } + if (! dest_rect) + dest_rect = gegl_buffer_get_extent (dest_buffer); gegl = gegl_node_new (); @@ -115,14 +110,25 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, if (src_buffer) { GeglNode *src_node; + GeglNode *underlying_operation; + + underlying_operation = + gimp_gegl_node_get_underlying_operation (operation); /* dup() because reading and writing the same buffer doesn't - * work with area ops when using a processor. See bug #701875. + * generally work with non-point ops when working in chunks. + * See bug #701875. */ - if (progress && (src_buffer == dest_buffer)) - src_buffer = gegl_buffer_dup (src_buffer); + if (src_buffer == dest_buffer && + ! (gimp_gegl_node_is_point_operation (underlying_operation) || + gimp_gegl_node_is_source_operation (underlying_operation))) + { + src_buffer = gegl_buffer_dup (src_buffer); + } else - g_object_ref (src_buffer); + { + g_object_ref (src_buffer); + } src_node = gegl_node_new_child (gegl, "operation", "gegl:buffer-source", @@ -137,10 +143,10 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, crop_node = gegl_node_new_child (gegl, "operation", "gegl:crop", - "x", (gdouble) rect.x, - "y", (gdouble) rect.y, - "width", (gdouble) rect.width, - "height", (gdouble) rect.height, + "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", @@ -175,21 +181,19 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, if (progress) { - processor = gegl_node_new_processor (dest_node, &rect); - if (gimp_progress_is_active (progress)) { if (undo_desc) gimp_progress_set_text_literal (progress, undo_desc); progress_started = FALSE; - cancellable = FALSE; + cancelable = FALSE; } else { - gimp_progress_start (progress, cancellable, "%s", undo_desc); + gimp_progress_start (progress, cancelable, "%s", undo_desc); - if (cancellable) + if (cancelable) g_signal_connect (progress, "cancel", G_CALLBACK (gimp_gegl_apply_operation_cancel), &cancel); @@ -197,102 +201,84 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, progress_started = TRUE; } } + else + { + cancelable = FALSE; + } + + gegl_buffer_freeze_changed (dest_buffer); + + all_pixels = dest_rect->width * dest_rect->height; + done_pixels = 0; + + region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) dest_rect); if (cache) { - cairo_region_t *region; - gint all_pixels; - gint done_pixels = 0; - gint n_rects; - gint i; - - region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect); - - all_pixels = rect.width * rect.height; + gint i; for (i = 0; i < n_valid_rects; i++) { - gimp_gegl_buffer_copy (cache, valid_rects + i, GEGL_ABYSS_NONE, - dest_buffer, valid_rects + i); + GeglRectangle valid_rect; + + if (! gegl_rectangle_intersect (&valid_rect, + &valid_rects[i], dest_rect)) + { + continue; + } + + gimp_gegl_buffer_copy (cache, &valid_rect, GEGL_ABYSS_NONE, + dest_buffer, &valid_rect); cairo_region_subtract_rectangle (region, (cairo_rectangle_int_t *) - valid_rects + i); + &valid_rect); - done_pixels += valid_rects[i].width * valid_rects[i].height; - - if (progress) - gimp_progress_set_value (progress, - (gdouble) done_pixels / - (gdouble) all_pixels); - } - - n_rects = cairo_region_num_rectangles (region); - - for (i = 0; ! cancel && (i < n_rects); i++) - { - cairo_rectangle_int_t render_rect; - - cairo_region_get_rectangle (region, i, &render_rect); + done_pixels += valid_rect.width * valid_rect.height; if (progress) { - gint rect_pixels = render_rect.width * render_rect.height; - -#ifdef REUSE_PROCESSOR - gegl_processor_set_rectangle (processor, - (GeglRectangle *) &render_rect); -#else - g_object_unref (processor); - processor = gegl_node_new_processor (dest_node, - (GeglRectangle *) &render_rect); -#endif - - while (! cancel && gegl_processor_work (processor, &value)) - { - gimp_progress_set_value (progress, - ((gdouble) done_pixels + - value * rect_pixels) / - (gdouble) all_pixels); - - if (cancellable) - while (! cancel && g_main_context_pending (NULL)) - g_main_context_iteration (NULL, FALSE); - } - - done_pixels += rect_pixels; - } - else - { - gegl_node_blit (dest_node, 1.0, (GeglRectangle *) &render_rect, - NULL, NULL, 0, GEGL_BLIT_DEFAULT); + gimp_progress_set_value (progress, + (gdouble) done_pixels / + (gdouble) all_pixels); } } - - cairo_region_destroy (region); } - else + + iter = gimp_chunk_iterator_new (region); + + 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)) + { + gint rect_pixels = render_rect.width * render_rect.height; + + gegl_node_blit (dest_node, 1.0, &render_rect, NULL, NULL, 0, + GEGL_BLIT_DEFAULT); + + done_pixels += rect_pixels; + } + if (progress) { - while (! cancel && gegl_processor_work (processor, &value)) - { - gimp_progress_set_value (progress, value); - - if (cancellable) - while (! cancel && g_main_context_pending (NULL)) - g_main_context_iteration (NULL, FALSE); - } - } - else - { - gegl_node_blit (dest_node, 1.0, &rect, - NULL, NULL, 0, GEGL_BLIT_DEFAULT); + gimp_progress_set_value (progress, + (gdouble) done_pixels / + (gdouble) all_pixels); } } - if (processor) - g_object_unref (processor); + gegl_buffer_thaw_changed (dest_buffer); g_object_unref (gegl); @@ -306,7 +292,7 @@ gimp_gegl_apply_cached_operation (GeglBuffer *src_buffer, { gimp_progress_end (progress); - if (cancellable) + if (cancelable) g_signal_handlers_disconnect_by_func (progress, gimp_gegl_apply_operation_cancel, &cancel); diff --git a/app/gegl/gimp-gegl-loops.cc b/app/gegl/gimp-gegl-loops.cc index d1edbf17cb..f093f7c02e 100644 --- a/app/gegl/gimp-gegl-loops.cc +++ b/app/gegl/gimp-gegl-loops.cc @@ -24,7 +24,6 @@ #include #include -#define GEGL_ITERATOR2_API #include extern "C" @@ -41,13 +40,12 @@ extern "C" #include "gimp-gegl-loops-sse2.h" #include "core/gimp-atomic.h" -#include "core/gimp-parallel.h" #include "core/gimp-utils.h" #include "core/gimpprogress.h" -#define MIN_PARALLEL_SUB_SIZE 64 -#define MIN_PARALLEL_SUB_AREA (MIN_PARALLEL_SUB_SIZE * MIN_PARALLEL_SUB_SIZE) +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) #define SHIFTED_AREA(dest, src) \ const GeglRectangle dest##_area_ = { \ @@ -82,8 +80,9 @@ gimp_gegl_buffer_copy (GeglBuffer *src_buffer, if (! dest_rect) dest_rect = src_rect; - gimp_parallel_distribute_area (src_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *src_area) + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) { SHIFTED_AREA (dest, src); @@ -93,6 +92,58 @@ gimp_gegl_buffer_copy (GeglBuffer *src_buffer, } } +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, @@ -161,8 +212,9 @@ gimp_gegl_convolve (GeglBuffer *src_buffer, offset = 0.0; } - gimp_parallel_distribute_area (dest_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *dest_area) + gegl_parallel_distribute_area ( + dest_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *dest_area) { const gint components = src_components; const gint a_component = components - 1; @@ -313,8 +365,9 @@ gimp_gegl_dodgeburn (GeglBuffer *src_buffer, if (! dest_rect) dest_rect = gegl_buffer_get_extent (dest_buffer); - gimp_parallel_distribute_area (src_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *src_area) + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) { GeglBufferIterator *iter; @@ -548,8 +601,9 @@ gimp_gegl_smudge_with_paint (GeglBuffer *accum_buffer, brush_a *= brush_color_ptr[3]; } - gimp_parallel_distribute_area (accum_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *accum_area) + gegl_parallel_distribute_area ( + accum_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *accum_area) { GeglBufferIterator *iter; @@ -616,8 +670,9 @@ gimp_gegl_apply_mask (GeglBuffer *mask_buffer, if (! dest_rect) dest_rect = gegl_buffer_get_extent (dest_buffer); - gimp_parallel_distribute_area (mask_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *mask_area) + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) { GeglBufferIterator *iter; @@ -661,8 +716,9 @@ gimp_gegl_combine_mask (GeglBuffer *mask_buffer, if (! dest_rect) dest_rect = gegl_buffer_get_extent (dest_buffer); - gimp_parallel_distribute_area (mask_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *mask_area) + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) { GeglBufferIterator *iter; @@ -707,8 +763,9 @@ gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer, if (! dest_rect) dest_rect = gegl_buffer_get_extent (dest_buffer); - gimp_parallel_distribute_area (mask_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *mask_area) + gegl_parallel_distribute_area ( + mask_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *mask_area) { GeglBufferIterator *iter; @@ -753,132 +810,6 @@ gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer, }); } -void -gimp_gegl_replace (GeglBuffer *top_buffer, - const GeglRectangle *top_rect, - GeglBuffer *bottom_buffer, - const GeglRectangle *bottom_rect, - GeglBuffer *mask_buffer, - const GeglRectangle *mask_rect, - GeglBuffer *dest_buffer, - const GeglRectangle *dest_rect, - gdouble opacity, - const gboolean *affect) -{ - if (! top_rect) - top_rect = gegl_buffer_get_extent (top_buffer); - - if (! bottom_rect) - bottom_rect = gegl_buffer_get_extent (bottom_buffer); - - if (! mask_rect) - mask_rect = gegl_buffer_get_extent (mask_buffer); - - if (! dest_rect) - dest_rect = gegl_buffer_get_extent (dest_buffer); - - gimp_parallel_distribute_area (top_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *top_area) - { - GeglBufferIterator *iter; - - SHIFTED_AREA (bottom, top); - SHIFTED_AREA (mask, top); - SHIFTED_AREA (dest, top); - - iter = gegl_buffer_iterator_new (top_buffer, top_area, 0, - babl_format ("RGBA float"), - GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 4); - - gegl_buffer_iterator_add (iter, bottom_buffer, bottom_area, 0, - babl_format ("RGBA float"), - GEGL_ACCESS_READ, GEGL_ABYSS_NONE); - - gegl_buffer_iterator_add (iter, mask_buffer, mask_area, 0, - babl_format ("Y float"), - GEGL_ACCESS_READ, GEGL_ABYSS_NONE); - - gegl_buffer_iterator_add (iter, dest_buffer, dest_area, 0, - babl_format ("RGBA float"), - GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); - - while (gegl_buffer_iterator_next (iter)) - { - const gfloat *top = (const gfloat *) iter->items[0].data; - const gfloat *bottom = (const gfloat *) iter->items[1].data; - const gfloat *mask = (const gfloat *) iter->items[2].data; - gfloat *dest = (gfloat *) iter->items[3].data; - gint count = iter->length; - - while (count--) - { - gint b; - gdouble mask_val = *mask * opacity; - - /* calculate new alpha first. */ - gfloat s1_a = bottom[3]; - gfloat s2_a = top[3]; - gdouble a_val = s1_a + mask_val * (s2_a - s1_a); - - if (a_val == 0.0) - { - /* In any case, write out versions of the blending - * function that result when combinations of s1_a, s2_a, - * and mask_val --> 0 (or mask_val -->1) - */ - - /* 1: s1_a, s2_a, AND mask_val all approach 0+: */ - /* 2: s1_a AND s2_a both approach 0+, regardless of mask_val: */ - if (s1_a + s2_a == 0.0) - { - for (b = 0; b < 3; b++) - { - gfloat new_val; - - new_val = bottom[b] + mask_val * (top[b] - bottom[b]); - - dest[b] = affect[b] ? new_val : bottom[b]; - } - } - - /* 3: mask_val AND s1_a both approach 0+, regardless of s2_a */ - else if (s1_a + mask_val == 0.0) - { - for (b = 0; b < 3; b++) - dest[b] = bottom[b]; - } - - /* 4: mask_val -->1 AND s2_a -->0, regardless of s1_a */ - else if (1.0 - mask_val + s2_a == 0.0) - { - for (b = 0; b < 3; b++) - dest[b] = affect[b] ? top[b] : bottom[b]; - } - } - else - { - gdouble a_recip = 1.0 / a_val; - - /* possible optimization: fold a_recip into s1_a and s2_a */ - for (b = 0; b < 3; b++) - { - gfloat new_val = a_recip * (bottom[b] * s1_a + mask_val * - (top[b] * s2_a - bottom[b] * s1_a)); - dest[b] = affect[b] ? new_val : bottom[b]; - } - } - - dest[3] = affect[3] ? a_val : s1_a; - - top += 4; - bottom += 4; - mask += 1; - dest += 4; - } - } - }); -} - void gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer, const GeglRectangle *indexed_rect, @@ -893,8 +824,9 @@ gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer, if (! mask_rect) mask_rect = gegl_buffer_get_extent (mask_buffer); - gimp_parallel_distribute_area (indexed_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *indexed_area) + gegl_parallel_distribute_area ( + indexed_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *indexed_area) { GeglBufferIterator *iter; @@ -983,8 +915,9 @@ gimp_gegl_convert_color_profile (GeglBuffer *src_buffer, GIMP_TIMER_START (); - gimp_parallel_distribute_area (src_rect, MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *src_area) + gegl_parallel_distribute_area ( + src_rect, PIXELS_PER_THREAD, + [=] (const GeglRectangle *src_area) { SHIFTED_AREA (dest, src); @@ -1042,8 +975,9 @@ gimp_gegl_average_color (GeglBuffer *buffer, else roi = *rect; - gimp_parallel_distribute_area (&roi, MIN_PARALLEL_SUB_AREA, - [&] (const GeglRectangle *area) + gegl_parallel_distribute_area ( + &roi, PIXELS_PER_THREAD, + [&] (const GeglRectangle *area) { Sum *sum; GeglBufferIterator *iter; diff --git a/app/gegl/gimp-gegl-loops.h b/app/gegl/gimp-gegl-loops.h index 566fe39f76..872c2b4660 100644 --- a/app/gegl/gimp-gegl-loops.h +++ b/app/gegl/gimp-gegl-loops.h @@ -28,6 +28,9 @@ void gimp_gegl_buffer_copy (GeglBuffer *src_buffer, 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 */ @@ -78,17 +81,6 @@ void gimp_gegl_combine_mask_weird (GeglBuffer *mask_buffer, gdouble opacity, gboolean stipple); -void gimp_gegl_replace (GeglBuffer *top_buffer, - const GeglRectangle *top_rect, - GeglBuffer *bottom_buffer, - const GeglRectangle *bottom_rect, - GeglBuffer *mask_buffer, - const GeglRectangle *mask_rect, - GeglBuffer *dest_buffer, - const GeglRectangle *dest_rect, - gdouble opacity, - const gboolean *affect); - void gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer, const GeglRectangle *indexed_rect, const Babl *indexed_format, diff --git a/app/gegl/gimp-gegl-mask-combine.c b/app/gegl/gimp-gegl-mask-combine.c deleted file mode 100644 index 11ad1b22f4..0000000000 --- a/app/gegl/gimp-gegl-mask-combine.c +++ /dev/null @@ -1,502 +0,0 @@ -/* GIMP - The GNU Image Manipulation Program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "config.h" - -#include - -#include -#define GEGL_ITERATOR2_API -#include - -#include "libgimpbase/gimpbase.h" -#include "libgimpmath/gimpmath.h" - -#include "gimp-gegl-types.h" - -#include "gimp-babl.h" -#include "gimp-gegl-mask-combine.h" - - -gboolean -gimp_gegl_mask_combine_rect (GeglBuffer *mask, - GimpChannelOps op, - gint x, - gint y, - gint w, - gint h) -{ - GeglColor *color; - - g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE); - - if (! gimp_rectangle_intersect (x, y, w, h, - 0, 0, - gegl_buffer_get_width (mask), - gegl_buffer_get_height (mask), - &x, &y, &w, &h)) - return FALSE; - - if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE) - color = gegl_color_new ("#fff"); - else - color = gegl_color_new ("#000"); - - gegl_buffer_set_color (mask, GEGL_RECTANGLE (x, y, w, h), color); - g_object_unref (color); - - return TRUE; -} - -/** - * gimp_gegl_mask_combine_ellipse: - * @mask: the channel with which to combine the ellipse - * @op: whether to replace, add to, or subtract from the current - * contents - * @x: x coordinate of upper left corner of ellipse - * @y: y coordinate of upper left corner of ellipse - * @w: width of ellipse bounding box - * @h: height of ellipse bounding box - * @antialias: if %TRUE, antialias the ellipse - * - * Mainly used for elliptical selections. If @op is - * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels - * within the ellipse to 255. If @op is %GIMP_CHANNEL_OP_SUBTRACT, - * sets pixels within to zero. If @antialias is %TRUE, pixels that - * impinge on the edge of the ellipse are set to intermediate values, - * depending on how much they overlap. - **/ -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); -} - -static void -gimp_gegl_mask_combine_span (gfloat *data, - GimpChannelOps op, - gint x1, - gint x2, - gfloat value) -{ - if (x2 <= x1) - return; - - switch (op) - { - case GIMP_CHANNEL_OP_ADD: - case GIMP_CHANNEL_OP_REPLACE: - if (value == 1.0) - { - while (x1 < x2) - data[x1++] = 1.0; - } - else - { - while (x1 < x2) - { - const gfloat val = data[x1] + value; - data[x1++] = val > 1.0 ? 1.0 : val; - } - } - break; - - case GIMP_CHANNEL_OP_SUBTRACT: - if (value == 1.0) - { - while (x1 < x2) - data[x1++] = 0.0; - } - else - { - while (x1 < x2) - { - const gfloat val = data[x1] - value; - data[x1++] = val > 0.0 ? val : 0.0; - } - } - break; - - case GIMP_CHANNEL_OP_INTERSECT: - /* Should not happen */ - break; - } -} - -/** - * gimp_gegl_mask_combine_ellipse_rect: - * @mask: the channel with which to combine the elliptic rect - * @op: whether to replace, add to, or subtract from the current - * contents - * @x: x coordinate of upper left corner of bounding rect - * @y: y coordinate of upper left corner of bounding rect - * @w: width of bounding rect - * @h: height of bounding rect - * @a: elliptic a-constant applied to corners - * @b: elliptic b-constant applied to corners - * @antialias: if %TRUE, antialias the elliptic corners - * - * Used for rounded cornered rectangles and ellipses. If @op is - * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels - * within the ellipse to 255. If @op is %GIMP_CHANNEL_OP_SUBTRACT, - * sets pixels within to zero. If @antialias is %TRUE, pixels that - * impinge on the edge of the ellipse are set to intermediate values, - * depending on how much they overlap. - **/ -gboolean -gimp_gegl_mask_combine_ellipse_rect (GeglBuffer *mask, - GimpChannelOps op, - gint x, - gint y, - gint w, - gint h, - gdouble a, - gdouble b, - gboolean antialias) -{ - GeglBufferIterator *iter; - GeglRectangle *roi; - gdouble a_sqr; - gdouble b_sqr; - gdouble ellipse_center_x; - gint x0, y0; - gint width, height; - - g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE); - g_return_val_if_fail (a >= 0.0 && b >= 0.0, FALSE); - g_return_val_if_fail (op != GIMP_CHANNEL_OP_INTERSECT, FALSE); - - /* Make sure the elliptic corners fit into the rect */ - a = MIN (a, w / 2.0); - b = MIN (b, h / 2.0); - - a_sqr = SQR (a); - b_sqr = SQR (b); - - if (! gimp_rectangle_intersect (x, y, w, h, - 0, 0, - gegl_buffer_get_width (mask), - gegl_buffer_get_height (mask), - &x0, &y0, &width, &height)) - return FALSE; - - ellipse_center_x = x + a; - - iter = gegl_buffer_iterator_new (mask, - GEGL_RECTANGLE (x0, y0, width, height), 0, - babl_format ("Y float"), - GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1); - roi = &iter->items[0].roi; - - while (gegl_buffer_iterator_next (iter)) - { - gfloat *data = iter->items[0].data; - gint py; - - for (py = roi->y; - py < roi->y + roi->height; - py++, data += roi->width) - { - const gint px = roi->x; - gdouble ellipse_center_y; - - if (py >= y + b && py < y + h - b) - { - /* we are on a row without rounded corners */ - gimp_gegl_mask_combine_span (data, op, 0, roi->width, 1.0); - continue; - } - - /* Match the ellipse center y with our current y */ - if (py < y + b) - { - ellipse_center_y = y + b; - } - else - { - ellipse_center_y = y + h - b; - } - - /* For a non-antialiased ellipse, use the normal equation - * for an ellipse with an arbitrary center - * (ellipse_center_x, ellipse_center_y). - */ - if (! antialias) - { - gdouble half_ellipse_width_at_y; - gint x_start; - gint x_end; - - half_ellipse_width_at_y = - sqrt (a_sqr - - a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr); - - x_start = ROUND (ellipse_center_x - half_ellipse_width_at_y); - x_end = ROUND (ellipse_center_x + w - 2 * a + - half_ellipse_width_at_y); - - gimp_gegl_mask_combine_span (data, op, - MAX (x_start - px, 0), - MIN (x_end - px, roi->width), 1.0); - } - else /* use antialiasing */ - { - /* algorithm changed 7-18-04, because the previous one - * did not work well for eccentric ellipses. The new - * algorithm measures the distance to the ellipse in the - * X and Y directions, and uses trigonometry to - * approximate the distance to the ellipse as the - * distance to the hypotenuse of a right triangle whose - * legs are the X and Y distances. (WES) - */ - const gfloat yi = ABS (py + 0.5 - ellipse_center_y); - gfloat last_val = -1; - gint x_start = px; - gint cur_x; - - for (cur_x = px; cur_x < (px + roi->width); cur_x++) - { - gfloat xj; - gfloat xdist; - gfloat ydist; - gfloat r; - gfloat dist; - gfloat val; - - if (cur_x < x + w / 2) - { - ellipse_center_x = x + a; - } - else - { - ellipse_center_x = x + w - a; - } - - xj = ABS (cur_x + 0.5 - ellipse_center_x); - - if (yi < b) - xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr); - else - xdist = 1000.0; /* anything large will work */ - - if (xj < a) - ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr); - else - ydist = 1000.0; /* anything large will work */ - - r = hypot (xdist, ydist); - - if (r < 0.001) - dist = 0.0; - else - dist = xdist * ydist / r; /* trig formula for distance to - * hypotenuse - */ - - if (xdist < 0.0) - dist *= -1; - - if (dist < -0.5) - val = 1.0; - else if (dist < 0.5) - val = (1.0 - (dist + 0.5)); - else - val = 0.0; - - if (last_val != val) - { - if (last_val != -1) - gimp_gegl_mask_combine_span (data, op, - MAX (x_start - px, 0), - MIN (cur_x - px, roi->width), - last_val); - - x_start = cur_x; - last_val = val; - } - - /* skip ahead if we are on the straight segment - * between rounded corners - */ - if (cur_x >= x + a && cur_x < x + w - a) - { - gimp_gegl_mask_combine_span (data, op, - MAX (x_start - px, 0), - MIN (cur_x - px, roi->width), - last_val); - - x_start = cur_x; - cur_x = x + w - a; - last_val = val = 1.0; - } - - /* Time to change center? */ - if (cur_x >= x + w / 2) - { - ellipse_center_x = x + w - a; - } - } - - gimp_gegl_mask_combine_span (data, op, - MAX (x_start - px, 0), - MIN (cur_x - px, roi->width), - last_val); - } - } - } - - return TRUE; -} - -gboolean -gimp_gegl_mask_combine_buffer (GeglBuffer *mask, - GeglBuffer *add_on, - GimpChannelOps op, - gint off_x, - gint off_y) -{ - GeglBufferIterator *iter; - GeglRectangle rect; - const Babl *mask_format; - const Babl *add_on_format; - gint x, y, w, h; - - g_return_val_if_fail (GEGL_IS_BUFFER (mask), FALSE); - g_return_val_if_fail (GEGL_IS_BUFFER (add_on), FALSE); - - if (! gimp_rectangle_intersect (off_x, off_y, - gegl_buffer_get_width (add_on), - gegl_buffer_get_height (add_on), - 0, 0, - gegl_buffer_get_width (mask), - gegl_buffer_get_height (mask), - &x, &y, &w, &h)) - return FALSE; - - rect.x = x; - rect.y = y; - rect.width = w; - rect.height = h; - - /* 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. - */ - if (gimp_babl_format_get_linear (gegl_buffer_get_format (mask))) - mask_format = babl_format ("Y float"); - else - mask_format = babl_format ("Y' float"); - - iter = gegl_buffer_iterator_new (mask, &rect, 0, - mask_format, - GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 2); - - rect.x -= off_x; - rect.y -= off_y; - - /* 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 - */ - if (gimp_babl_format_get_linear (gegl_buffer_get_format (add_on))) - add_on_format = babl_format ("Y float"); - else - add_on_format = babl_format ("Y' float"); - - gegl_buffer_iterator_add (iter, add_on, &rect, 0, - add_on_format, - GEGL_ACCESS_READ, GEGL_ABYSS_NONE); - - switch (op) - { - case GIMP_CHANNEL_OP_ADD: - case GIMP_CHANNEL_OP_REPLACE: - while (gegl_buffer_iterator_next (iter)) - { - gfloat *mask_data = iter->items[0].data; - const gfloat *add_on_data = iter->items[1].data; - gint count = iter->length; - - while (count--) - { - const gfloat val = *mask_data + *add_on_data; - - *mask_data = CLAMP (val, 0.0, 1.0); - - add_on_data++; - mask_data++; - } - } - break; - - case GIMP_CHANNEL_OP_SUBTRACT: - while (gegl_buffer_iterator_next (iter)) - { - gfloat *mask_data = iter->items[0].data; - const gfloat *add_on_data = iter->items[1].data; - gint count = iter->length; - - while (count--) - { - if (*add_on_data > *mask_data) - *mask_data = 0.0; - else - *mask_data -= *add_on_data; - - add_on_data++; - mask_data++; - } - } - break; - - case GIMP_CHANNEL_OP_INTERSECT: - while (gegl_buffer_iterator_next (iter)) - { - gfloat *mask_data = iter->items[0].data; - const gfloat *add_on_data = iter->items[1].data; - gint count = iter->length; - - while (count--) - { - *mask_data = MIN (*mask_data, *add_on_data); - - add_on_data++; - mask_data++; - } - } - break; - - default: - g_warning ("%s: unknown operation type", G_STRFUNC); - break; - } - - return TRUE; -} diff --git a/app/gegl/gimp-gegl-mask-combine.cc b/app/gegl/gimp-gegl-mask-combine.cc new file mode 100644 index 0000000000..223c743712 --- /dev/null +++ b/app/gegl/gimp-gegl-mask-combine.cc @@ -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 + +#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, off_y, + 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 index 0f9da50548..d8e0fa2590 100644 --- a/app/gegl/gimp-gegl-mask-combine.h +++ b/app/gegl/gimp-gegl-mask-combine.h @@ -38,8 +38,8 @@ gboolean gimp_gegl_mask_combine_ellipse_rect (GeglBuffer *mask, gint y, gint w, gint h, - gdouble a, - gdouble b, + gdouble rx, + gdouble ry, gboolean antialias); gboolean gimp_gegl_mask_combine_buffer (GeglBuffer *mask, GeglBuffer *add_on, diff --git a/app/gegl/gimp-gegl-mask.c b/app/gegl/gimp-gegl-mask.c index 4dad33315c..8c896b8eb5 100644 --- a/app/gegl/gimp-gegl-mask.c +++ b/app/gegl/gimp-gegl-mask.c @@ -17,7 +17,6 @@ #include "config.h" -#define GEGL_ITERATOR2_API #include #include "gimp-gegl-types.h" @@ -34,6 +33,8 @@ gimp_gegl_mask_bounds (GeglBuffer *buffer, { GeglBufferIterator *iter; GeglRectangle *roi; + const Babl *format; + gint bpp; gint tx1, tx2, ty1, ty2; g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); @@ -48,17 +49,18 @@ gimp_gegl_mask_bounds (GeglBuffer *buffer, tx2 = 0; ty2 = 0; - iter = gegl_buffer_iterator_new (buffer, NULL, 0, babl_format ("Y float"), + 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)) { - gfloat *data = iter->items[0].data; - gfloat *data1 = data; - gint ex = roi->x + roi->width; - gint ey = roi->y + roi->height; - gint x, y; + 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 @@ -69,7 +71,8 @@ gimp_gegl_mask_bounds (GeglBuffer *buffer, /* Check upper left and lower right corners to see if we can * avoid checking the rest of the pixels in this tile */ - if (data[0] && data[iter->length - 1]) + 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 @@ -84,27 +87,112 @@ gimp_gegl_mask_bounds (GeglBuffer *buffer, } else { - for (y = roi->y; y < ey; y++, data1 += roi->width) + #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; \ + } \ + } \ + \ + data += roi->width; \ + } \ + } \ + G_STMT_END + + switch (bpp) { - for (x = roi->x, data = data1; x < ex; x++, data++) - { - if (*data) - { - gint minx = x; - gint maxx = x; + case 1: + FIND_BOUNDS (1, guint8); + break; - for (; x < ex; x++, data++) - if (*data) - maxx = x; + case 2: + FIND_BOUNDS (2, guint16); + break; - if (minx < tx1) tx1 = minx; - if (maxx > tx2) tx2 = maxx; + case 4: + FIND_BOUNDS (4, guint32); + break; - if (y < ty1) ty1 = y; - if (y > ty2) ty2 = y; - } - } + 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 } } } @@ -135,25 +223,24 @@ 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); - iter = gegl_buffer_iterator_new (buffer, NULL, 0, babl_format ("Y float"), + 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)) { - gfloat *data = iter->items[0].data; - gint i; - - for (i = 0; i < iter->length; i++) + if (! gegl_memeq_zero (iter->items[0].data, bpp * iter->length)) { - if (data[i]) - { - gegl_buffer_iterator_stop (iter); + gegl_buffer_iterator_stop (iter); - return FALSE; - } + return FALSE; } } diff --git a/app/gegl/gimp-gegl-nodes.c b/app/gegl/gimp-gegl-nodes.c index 3236f3d5e9..5067da28f8 100644 --- a/app/gegl/gimp-gegl-nodes.c +++ b/app/gegl/gimp-gegl-nodes.c @@ -55,9 +55,17 @@ gimp_gegl_create_flatten_node (const GimpRGB *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); @@ -101,6 +109,8 @@ gimp_gegl_create_apply_opacity_node (GeglBuffer *mask, "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); @@ -115,6 +125,22 @@ gimp_gegl_create_apply_opacity_node (GeglBuffer *mask, 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, diff --git a/app/gegl/gimp-gegl-nodes.h b/app/gegl/gimp-gegl-nodes.h index 90f13fc2b2..0f627ff52a 100644 --- a/app/gegl/gimp-gegl-nodes.h +++ b/app/gegl/gimp-gegl-nodes.h @@ -28,6 +28,8 @@ 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, diff --git a/app/gegl/gimp-gegl-utils.c b/app/gegl/gimp-gegl-utils.c index 96d3936776..dc9171bbae 100644 --- a/app/gegl/gimp-gegl-utils.c +++ b/app/gegl/gimp-gegl-utils.c @@ -25,6 +25,8 @@ #include #include +#include "libgimpmath/gimpmath.h" + #include "gimp-gegl-types.h" #include "core/gimpprogress.h" @@ -114,6 +116,39 @@ gimp_gegl_progress_connect (GeglNode *node, (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); +} + const Babl * gimp_gegl_node_get_format (GeglNode *node, const gchar *pad_name) @@ -139,6 +174,33 @@ gimp_gegl_node_get_format (GeglNode *node, 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, @@ -151,3 +213,37 @@ gimp_gegl_param_spec_has_key (GParamSpec *pspec, return FALSE; } + +void +gimp_gegl_rectangle_align_to_tile_grid (GeglRectangle *dest, + const GeglRectangle *src, + GeglBuffer *buffer) +{ + gint shift_x; + gint shift_y; + gint tile_width; + gint tile_height; + GeglRectangle rect; + + g_return_if_fail (dest != NULL); + g_return_if_fail (src != NULL); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + + g_object_get (buffer, + "shift-x", &shift_x, + "shift-y", &shift_y, + "tile-width", &tile_width, + "tile-height", &tile_height, + NULL); + + rect.x = (gint) floor ((gdouble) (src->x + shift_x) / + tile_width) * tile_width; + rect.y = (gint) floor ((gdouble) (src->y + shift_y) / + tile_height) * tile_height; + rect.width = (gint) ceil ((gdouble) (src->x + src->width + shift_x) / + tile_width) * tile_width - rect.x; + rect.height = (gint) ceil ((gdouble) (src->y + src->height + shift_y) / + tile_height) * tile_height - rect.y; + + *dest = rect; +} diff --git a/app/gegl/gimp-gegl-utils.h b/app/gegl/gimp-gegl-utils.h index cc25338aa4..1a7d51a69e 100644 --- a/app/gegl/gimp-gegl-utils.h +++ b/app/gegl/gimp-gegl-utils.h @@ -22,21 +22,32 @@ #define __GIMP_GEGL_UTILS_H__ -GType gimp_gegl_get_op_enum_type (const gchar *operation, - const gchar *property); +GType gimp_gegl_get_op_enum_type (const gchar *operation, + const gchar *property); -GeglColor * gimp_gegl_color_new (const GimpRGB *rgb); +GeglColor * gimp_gegl_color_new (const GimpRGB *rgb); -void gimp_gegl_progress_connect (GeglNode *node, - GimpProgress *progress, - const gchar *text); +void gimp_gegl_progress_connect (GeglNode *node, + GimpProgress *progress, + const gchar *text); -const Babl * gimp_gegl_node_get_format (GeglNode *node, - const gchar *pad_name); +gboolean gimp_gegl_node_is_source_operation (GeglNode *node); +gboolean gimp_gegl_node_is_point_operation (GeglNode *node); -gboolean gimp_gegl_param_spec_has_key (GParamSpec *pspec, - const gchar *key, - const gchar *value); +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); + +void gimp_gegl_rectangle_align_to_tile_grid (GeglRectangle *dest, + const GeglRectangle *src, + GeglBuffer *buffer); #endif /* __GIMP_GEGL_UTILS_H__ */ diff --git a/app/gegl/gimp-gegl.c b/app/gegl/gimp-gegl.c index af0c28efca..6c7987b432 100644 --- a/app/gegl/gimp-gegl.c +++ b/app/gegl/gimp-gegl.c @@ -23,6 +23,8 @@ #include #include +#include "libgimpconfig/gimpconfig.h" + #include "gimp-gegl-types.h" #include "config/gimpgeglconfig.h" @@ -35,12 +37,17 @@ #include "gimp-babl.h" #include "gimp-gegl.h" +#include + +static void gimp_gegl_notify_swap_path (GimpGeglConfig *config); +static void gimp_gegl_notify_temp_path (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); -#include + +/* public functions */ void gimp_gegl_init (Gimp *gimp) @@ -51,6 +58,10 @@ gimp_gegl_init (Gimp *gimp) config = GIMP_GEGL_CONFIG (gimp->config); + /* make sure swap and temp directories exist */ + gimp_gegl_notify_swap_path (config); + gimp_gegl_notify_temp_path (config); + g_object_set (gegl_config (), "tile-cache-size", (guint64) config->tile_cache_size, "threads", config->num_processors, @@ -59,6 +70,15 @@ gimp_gegl_init (Gimp *gimp) gimp_parallel_init (gimp); + g_signal_connect (config, "notify::swap-path", + G_CALLBACK (gimp_gegl_notify_swap_path), + NULL); + g_signal_connect (config, "notify::temp-path", + G_CALLBACK (gimp_gegl_notify_temp_path), + 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); @@ -82,6 +102,37 @@ gimp_gegl_exit (Gimp *gimp) gimp_parallel_exit (gimp); } + +/* private functions */ + +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_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_tile_cache_size (GimpGeglConfig *config) { diff --git a/app/gegl/gimpapplicator.c b/app/gegl/gimpapplicator.c index acdc75aed6..5cfeb0ec79 100644 --- a/app/gegl/gimpapplicator.c +++ b/app/gegl/gimpapplicator.c @@ -104,9 +104,7 @@ gimp_applicator_get_property (GObject *object, } GimpApplicator * -gimp_applicator_new (GeglNode *parent, - gboolean use_split_preview, - gboolean use_result_cache) +gimp_applicator_new (GeglNode *parent) { GimpApplicator *applicator; @@ -148,40 +146,12 @@ gimp_applicator_new (GeglNode *parent, "operation", "gegl:translate", NULL); - applicator->dup_apply_buffer_node = - gegl_node_new_child (applicator->node, - "operation", "gegl:copy-buffer", - NULL); - gegl_node_link_many (applicator->aux_node, applicator->apply_offset_node, - applicator->dup_apply_buffer_node, NULL); - if (use_split_preview) - { - applicator->preview_cache_node = - gegl_node_new_child (applicator->node, - "operation", "gegl:cache", - NULL); - - applicator->preview_crop_node = - gegl_node_new_child (applicator->node, - "operation", "gegl:nop", - NULL); - - gegl_node_link_many (applicator->dup_apply_buffer_node, - applicator->preview_cache_node, - applicator->preview_crop_node, - NULL); - gegl_node_connect_to (applicator->preview_crop_node, "output", - applicator->mode_node, "aux"); - } - else - { - gegl_node_connect_to (applicator->dup_apply_buffer_node, "output", - applicator->mode_node, "aux"); - } + gegl_node_connect_to (applicator->apply_offset_node, "output", + applicator->mode_node, "aux"); applicator->mask_node = gegl_node_new_child (applicator->node, @@ -203,26 +173,28 @@ gimp_applicator_new (GeglNode *parent, "mask", applicator->affect, NULL); - if (use_result_cache) - { - applicator->output_cache_node = - gegl_node_new_child (applicator->node, - "operation", "gegl:cache", - NULL); + applicator->convert_format_node = + gegl_node_new_child (applicator->node, + "operation", "gegl:nop", + NULL); - gegl_node_link_many (applicator->input_node, - applicator->affect_node, - applicator->output_cache_node, - applicator->output_node, - NULL); - } - else - { - gegl_node_link_many (applicator->input_node, - applicator->affect_node, - applicator->output_node, - 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"); @@ -487,32 +459,132 @@ gimp_applicator_set_affect (GimpApplicator *applicator, } } +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_preview (GimpApplicator *applicator, - gboolean enable, - const GeglRectangle *rect) +gimp_applicator_set_crop (GimpApplicator *applicator, + const GeglRectangle *rect) { g_return_if_fail (GIMP_IS_APPLICATOR (applicator)); - g_return_if_fail (rect != NULL); - if (! applicator->preview_cache_node) - return; - - if (applicator->preview_enabled != enable || - applicator->preview_rect.x != rect->x || - applicator->preview_rect.y != rect->y || - applicator->preview_rect.width != rect->width || - applicator->preview_rect.height != rect->height) + if (applicator->crop_enabled != (rect != NULL) || + (rect && ! gegl_rectangle_equal (&applicator->crop_rect, rect))) { - if (enable) + if (rect) { - if (! applicator->preview_enabled) + if (! applicator->crop_enabled) { - gegl_node_set (applicator->preview_crop_node, + gegl_node_set (applicator->crop_node, "operation", "gimp:compose-crop", "x", rect->x, "y", rect->y, @@ -520,63 +592,45 @@ gimp_applicator_set_preview (GimpApplicator *applicator, "height", rect->height, NULL); - gegl_node_connect_to (applicator->input_node, "output", - applicator->preview_crop_node, "aux"); + gegl_node_connect_to (applicator->input_node, "output", + applicator->crop_node, "aux"); } else { - gegl_node_set (applicator->preview_crop_node, + gegl_node_set (applicator->crop_node, "x", rect->x, "y", rect->y, "width", rect->width, "height", rect->height, NULL); } - } - else if (applicator->preview_enabled) - { - GeglBuffer *cache; - gegl_node_disconnect (applicator->preview_crop_node, "aux"); - gegl_node_set (applicator->preview_crop_node, + 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); - /* when disabling the preview, preserve the cached result - * by processing it into the output cache, which only - * involves the mode and affect nodes. - */ - gegl_node_get (applicator->preview_cache_node, - "cache", &cache, - NULL); - - if (cache) - { - GeglRectangle *rectangles; - gint n_rectangles; - - if (gegl_buffer_list_valid_rectangles (cache, &rectangles, - &n_rectangles)) - { - gint i; - - for (i = 0; i < n_rectangles; i++) - gegl_node_blit (applicator->output_cache_node, 1.0, - &rectangles[i], - NULL, NULL, 0, GEGL_BLIT_DEFAULT); - - g_free (rectangles); - } - - g_object_unref (cache); - } + applicator->crop_enabled = FALSE; } - - applicator->preview_enabled = enable; - applicator->preview_rect = *rect; } } +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) @@ -586,60 +640,3 @@ gimp_applicator_blit (GimpApplicator *applicator, gegl_node_blit (applicator->dest_node, 1.0, rect, NULL, NULL, 0, GEGL_BLIT_DEFAULT); } - -GeglBuffer * -gimp_applicator_dup_apply_buffer (GimpApplicator *applicator, - const GeglRectangle *rect) -{ - GeglBuffer *buffer; - GeglBuffer *shifted; - - g_return_val_if_fail (GIMP_IS_APPLICATOR (applicator), NULL); - g_return_val_if_fail (rect != NULL, NULL); - - buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, rect->width, rect->height), - babl_format ("RGBA float")); - - shifted = g_object_new (GEGL_TYPE_BUFFER, - "source", buffer, - "shift-x", -rect->x, - "shift-y", -rect->y, - NULL); - - gegl_node_set (applicator->dup_apply_buffer_node, - "buffer", shifted, - NULL); - - g_object_unref (shifted); - - return buffer; -} - -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->output_cache_node) - { - GeglBuffer *cache; - - gegl_node_get (applicator->output_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; -} diff --git a/app/gegl/gimpapplicator.h b/app/gegl/gimpapplicator.h index 154fefa0d3..a981999c13 100644 --- a/app/gegl/gimpapplicator.h +++ b/app/gegl/gimpapplicator.h @@ -48,13 +48,6 @@ struct _GimpApplicator gint apply_offset_y; GeglNode *apply_offset_node; - GeglNode *dup_apply_buffer_node; - - gboolean preview_enabled; - GeglRectangle preview_rect; - GeglNode *preview_cache_node; - GeglNode *preview_crop_node; - gdouble opacity; GimpLayerMode paint_mode; GimpLayerColorSpace blend_space; @@ -65,7 +58,15 @@ struct _GimpApplicator GimpComponentMask affect; GeglNode *affect_node; - GeglNode *output_cache_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; @@ -87,51 +88,54 @@ struct _GimpApplicatorClass }; -GType gimp_applicator_get_type (void) G_GNUC_CONST; +GType gimp_applicator_get_type (void) G_GNUC_CONST; -GimpApplicator * gimp_applicator_new (GeglNode *parent, - gboolean use_split_preview, - gboolean use_result_cache); +GimpApplicator * gimp_applicator_new (GeglNode *parent); -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_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_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_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_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_preview (GimpApplicator *applicator, - gboolean enable, - const GeglRectangle *rect); +void gimp_applicator_set_output_format (GimpApplicator *applicator, + const Babl *format); +const Babl * gimp_applicator_get_output_format (GimpApplicator *applicator); -void gimp_applicator_blit (GimpApplicator *applicator, - const GeglRectangle *rect); +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); -GeglBuffer * gimp_applicator_dup_apply_buffer (GimpApplicator *applicator, - const GeglRectangle *rect); -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 index b10189397e..2e7e3b6657 100644 --- a/app/gegl/gimptilehandlervalidate.c +++ b/app/gegl/gimptilehandlervalidate.c @@ -22,6 +22,7 @@ #include "gimp-gegl-types.h" +#include "gimp-gegl-loops.h" #include "gimptilehandlervalidate.h" @@ -35,28 +36,33 @@ enum }; -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_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_validate (GimpTileHandlerValidate *validate, - const GeglRectangle *rect, - const Babl *format, - gpointer dest_buf, - gint dest_stride); +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); +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, @@ -74,7 +80,10 @@ gimp_tile_handler_validate_class_init (GimpTileHandlerValidateClass *klass) 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, @@ -178,6 +187,18 @@ gimp_tile_handler_validate_get_property (GObject *object, } } +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, @@ -198,19 +219,49 @@ gimp_tile_handler_validate_real_validate (GimpTileHandlerValidate *validate, 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 (GeglTileSource *source, - GeglTile *tile, - gint x, - gint y) +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 tile; + return gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); } tile_rect.x = x * validate->tile_width; @@ -218,95 +269,107 @@ gimp_tile_handler_validate_validate (GeglTileSource *source, tile_rect.width = validate->tile_width; tile_rect.height = validate->tile_height; - if (validate->whole_tile) + overlap = cairo_region_contains_rectangle (validate->dirty_region, + &tile_rect); + + if (overlap == CAIRO_REGION_OVERLAP_OUT) { - if (cairo_region_contains_rectangle (validate->dirty_region, &tile_rect) - != CAIRO_REGION_OVERLAP_OUT) - { - gint tile_bpp; - gint tile_stride; + return 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); + if (overlap == CAIRO_REGION_OVERLAP_IN || validate->whole_tile) + { + gint tile_bpp; + gint tile_stride; - cairo_region_subtract_rectangle (validate->dirty_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_bpp = babl_format_get_bytes_per_pixel (validate->format); + tile_stride = tile_bpp * validate->tile_width; - gegl_tile_lock (tile); + tile = gegl_tile_handler_get_source_tile (GEGL_TILE_HANDLER (source), + x, y, 0, FALSE); - 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); + gimp_tile_handler_validate_begin_validate (validate); - gegl_tile_unlock (tile); - } + 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 = cairo_region_copy (validate->dirty_region); + 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); - if (! cairo_region_is_empty (tile_region)) + 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) { - gint tile_bpp; - gint tile_stride; - gint n_rects; - gint i; + tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), + x, y, 0); - cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect); + memset (gegl_tile_get_data (tile), + 0, tile_stride * validate->tile_height); + } - tile_bpp = babl_format_get_bytes_per_pixel (validate->format); - tile_stride = tile_bpp * validate->tile_width; + gimp_tile_handler_validate_begin_validate (validate); - if (! tile) - { - tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), - x, y, 0); + gegl_tile_lock (tile); - memset (gegl_tile_get_data (tile), - 0, tile_stride * validate->tile_height); - } - - gegl_tile_lock (tile); - - n_rects = cairo_region_num_rectangles (tile_region); + n_rects = cairo_region_num_rectangles (tile_region); #if 0 - g_printerr ("%d chunks\n", n_rects); + g_printerr ("%d chunks\n", n_rects); #endif - for (i = 0; i < n_rects; i++) - { - cairo_rectangle_int_t blit_rect; + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t blit_rect; - cairo_region_get_rectangle (tile_region, i, &blit_rect); + cairo_region_get_rectangle (tile_region, i, &blit_rect); - 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) + - (blit_rect.y % validate->tile_height) * tile_stride + - (blit_rect.x % validate->tile_width) * tile_bpp, - tile_stride); - } - - gegl_tile_unlock (tile); + 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) + + (blit_rect.y % validate->tile_height) * tile_stride + + (blit_rect.x % validate->tile_width) * tile_bpp, + tile_stride); } + gegl_tile_unlock (tile); + + gimp_tile_handler_validate_end_validate (validate); + cairo_region_destroy (tile_region); } @@ -321,14 +384,10 @@ gimp_tile_handler_validate_command (GeglTileSource *source, gint z, gpointer data) { - gpointer retval; - - retval = gegl_tile_handler_source_command (source, command, x, y, z, data); - if (command == GEGL_TILE_GET && z == 0) - retval = gimp_tile_handler_validate_validate (source, retval, x, y); + return gimp_tile_handler_validate_validate_tile (source, x, y); - return retval; + return gegl_tile_handler_source_command (source, command, x, y, z, data); } @@ -415,6 +474,89 @@ gimp_tile_handler_validate_undo_invalidate (GimpTileHandlerValidate *validate, (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) +{ + GimpTileHandlerValidateClass *klass; + + 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 (intersect) + { + cairo_region_t *region = cairo_region_copy (validate->dirty_region); + + cairo_region_intersect_rectangle (region, + (const cairo_rectangle_int_t *) rect); + + if (! cairo_region_is_empty (region)) + { + gint n_rects; + gint i; + + gimp_tile_handler_validate_begin_validate (validate); + + 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); + } + + cairo_region_destroy (region); + } + 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); + } +} + void gimp_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer, const GeglRectangle *src_rect, @@ -454,16 +596,31 @@ gimp_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer, 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) - src_validate->suspend_validate++; + { + g_object_ref (src_validate); + + gimp_tile_handler_validate_unassign (src_validate, src_buffer); + } + dst_validate->suspend_validate++; - gegl_buffer_copy (src_buffer, &real_src_rect, GEGL_ABYSS_NONE, - dst_buffer, &real_dst_rect); + gimp_gegl_buffer_copy (src_buffer, &real_src_rect, GEGL_ABYSS_NONE, + dst_buffer, &real_dst_rect); + + dst_validate->suspend_validate--; if (src_validate) - src_validate->suspend_validate--; - dst_validate->suspend_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); diff --git a/app/gegl/gimptilehandlervalidate.h b/app/gegl/gimptilehandlervalidate.h index 4d636641fb..63a6e97a47 100644 --- a/app/gegl/gimptilehandlervalidate.h +++ b/app/gegl/gimptilehandlervalidate.h @@ -48,6 +48,7 @@ struct _GimpTileHandlerValidate gint tile_width; gint tile_height; gboolean whole_tile; + gint validating; gint suspend_validate; }; @@ -55,11 +56,16 @@ struct _GimpTileHandlerValidateClass { GeglTileHandlerClass parent_class; - void (* validate) (GimpTileHandlerValidate *validate, - const GeglRectangle *rect, - const Babl *format, - gpointer dest_buf, - gint dest_stride); + 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); }; @@ -78,6 +84,14 @@ void gimp_tile_handler_validate_invalidate (GimpTileHa 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); + void gimp_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer, const GeglRectangle *src_rect, GeglBuffer *dst_buffer, diff --git a/app/gimp-priorities.h b/app/gimp-priorities.h index 07e0148b18..198f1d3252 100644 --- a/app/gimp-priorities.h +++ b/app/gimp-priorities.h @@ -28,7 +28,8 @@ /* #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_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) diff --git a/app/gui/Makefile.in b/app/gui/Makefile.in index 8b7a8b06ec..e797986442 100644 --- a/app/gui/Makefile.in +++ b/app/gui/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -222,8 +221,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -353,7 +350,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -492,8 +488,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/gui/gimpdbusservice-generated.c b/app/gui/gimpdbusservice-generated.c index cedbfce9e9..744f237ac0 100644 --- a/app/gui/gimpdbusservice-generated.c +++ b/app/gui/gimpdbusservice-generated.c @@ -1,5 +1,5 @@ /* - * Generated by gdbus-codegen 2.59.0 from dbus-service.xml. DO NOT EDIT. + * Generated by gdbus-codegen 2.61.1 from dbus-service.xml. DO NOT EDIT. * * The license of this code is the same as for the D-Bus interface description * it was derived from. @@ -39,7 +39,8 @@ typedef struct { GDBusPropertyInfo parent_struct; const gchar *hyphen_name; - gboolean use_gvariant; + guint use_gvariant : 1; + guint emits_changed_signal : 1; } _ExtendedGDBusPropertyInfo; typedef struct @@ -608,7 +609,7 @@ gimp_dbus_service_ui_call_open ( /** * gimp_dbus_service_ui_call_open_finish: * @proxy: A #GimpDBusServiceUIProxy. - * @out_success: (out): Return location for return parameter or %NULL to ignore. + * @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. * @@ -639,7 +640,7 @@ _out: * gimp_dbus_service_ui_call_open_sync: * @proxy: A #GimpDBusServiceUIProxy. * @arg_uri: Argument to pass with the method invocation. - * @out_success: (out): Return location for return parameter or %NULL to ignore. + * @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. * @@ -712,7 +713,7 @@ gimp_dbus_service_ui_call_open_as_new ( /** * gimp_dbus_service_ui_call_open_as_new_finish: * @proxy: A #GimpDBusServiceUIProxy. - * @out_success: (out): Return location for return parameter or %NULL to ignore. + * @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. * @@ -743,7 +744,7 @@ _out: * gimp_dbus_service_ui_call_open_as_new_sync: * @proxy: A #GimpDBusServiceUIProxy. * @arg_uri: Argument to pass with the method invocation. - * @out_success: (out): Return location for return parameter or %NULL to ignore. + * @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. * @@ -819,7 +820,7 @@ gimp_dbus_service_ui_call_batch_run ( /** * gimp_dbus_service_ui_call_batch_run_finish: * @proxy: A #GimpDBusServiceUIProxy. - * @out_success: (out): Return location for return parameter or %NULL to ignore. + * @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. * @@ -851,7 +852,7 @@ _out: * @proxy: A #GimpDBusServiceUIProxy. * @arg_interpreter: Argument to pass with the method invocation. * @arg_command: Argument to pass with the method invocation. - * @out_success: (out): Return location for return parameter or %NULL to ignore. + * @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. * diff --git a/app/gui/gimpdbusservice-generated.h b/app/gui/gimpdbusservice-generated.h index 709acdcb9c..2801aa21ac 100644 --- a/app/gui/gimpdbusservice-generated.h +++ b/app/gui/gimpdbusservice-generated.h @@ -1,5 +1,5 @@ /* - * Generated by gdbus-codegen 2.59.0 from dbus-service.xml. DO NOT EDIT. + * Generated by gdbus-codegen 2.61.1 from dbus-service.xml. DO NOT EDIT. * * The license of this code is the same as for the D-Bus interface description * it was derived from. diff --git a/app/gui/gui.c b/app/gui/gui.c index ec35071b04..022d3b91e9 100644 --- a/app/gui/gui.c +++ b/app/gui/gui.c @@ -45,6 +45,8 @@ #include "display/gimpstatusbar.h" #include "tools/gimp-tools.h" +#include "tools/gimptool.h" +#include "tools/tool_manager.h" #include "widgets/gimpaction-history.h" #include "widgets/gimpclipboard.h" @@ -640,7 +642,10 @@ gui_restore_after_callback (Gimp *gimp, if (GTK_IS_MENU_ITEM (menu)) menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu)); - gtkosx_application_set_menu_bar (osx_app, GTK_MENU_SHELL (menu)); + /* do not activate OSX menu if tests are running */ + if (!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, @@ -732,7 +737,8 @@ static gboolean gui_exit_callback (Gimp *gimp, gboolean force) { - GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config); + GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config); + GimpTool *active_tool; if (gimp->be_verbose) g_print ("EXIT: %s\n", G_STRFUNC); @@ -755,6 +761,15 @@ gui_exit_callback (Gimp *gimp, 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); diff --git a/app/gui/themes.c b/app/gui/themes.c index 32aff091f1..d17d50eca8 100644 --- a/app/gui/themes.c +++ b/app/gui/themes.c @@ -296,60 +296,99 @@ themes_apply_theme (Gimp *gimp, } else { - GFile *theme_dir = themes_get_theme_dir (gimp, theme_name); - GFile *gtkrc_theme; - GFile *gtkrc_user; - gchar *esc_gtkrc_theme; - gchar *esc_gtkrc_user; - gchar *tmp; + GFile *theme_dir = themes_get_theme_dir (gimp, theme_name); + GFile *gtkrc_user; + GSList *gtkrc_files = NULL; + GSList *iter; if (theme_dir) { - gtkrc_theme = g_file_get_child (theme_dir, "gtkrc"); + gtkrc_files = g_slist_prepend ( + gtkrc_files, + g_file_get_child (theme_dir, "gtkrc")); } else { /* get the hardcoded default theme gtkrc */ - gtkrc_theme = g_file_new_for_path (gimp_gtkrc ()); + gtkrc_files = g_slist_prepend ( + gtkrc_files, + g_file_new_for_path (gimp_gtkrc ())); } - gtkrc_user = gimp_directory_file ("gtkrc", NULL); + gtkrc_files = g_slist_prepend ( + gtkrc_files, + gimp_sysconf_directory_file ("gtkrc", NULL)); - tmp = g_file_get_path (gtkrc_theme); - esc_gtkrc_theme = g_strescape (tmp, NULL); - g_free (tmp); + gtkrc_user = gimp_directory_file ("gtkrc", NULL); + gtkrc_files = g_slist_prepend ( + gtkrc_files, + gtkrc_user); - tmp = g_file_get_path (gtkrc_user); - esc_gtkrc_user = g_strescape (tmp, NULL); - g_free (tmp); + gtkrc_files = g_slist_reverse (gtkrc_files); - if (! 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" - "include \"%s\"\n" - "include \"%s\"\n" - "\n" - "# end of themerc\n", - gimp_file_get_utf8_name (gtkrc_user), - esc_gtkrc_theme, - esc_gtkrc_user) || - ! g_output_stream_close (output, NULL, &error)) + 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)); + + 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_free (esc_gtkrc_theme); - g_free (esc_gtkrc_user); - g_object_unref (gtkrc_theme); - g_object_unref (gtkrc_user); + g_slist_free_full (gtkrc_files, g_object_unref); g_object_unref (output); } diff --git a/app/main.c b/app/main.c index ac98d01313..d743999edb 100644 --- a/app/main.c +++ b/app/main.c @@ -53,6 +53,7 @@ #include "config/gimpconfig-dump.h" #include "core/gimp.h" +#include "core/gimpbacktrace.h" #include "pdb/gimppdb.h" #include "pdb/gimpprocedure.h" @@ -324,6 +325,12 @@ main (int 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); diff --git a/app/menus/Makefile.in b/app/menus/Makefile.in index b90547b12b..77f43e1aab 100644 --- a/app/menus/Makefile.in +++ b/app/menus/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -220,8 +219,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -351,7 +348,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -490,8 +486,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/menus/windows-menu.c b/app/menus/windows-menu.c index 80cbd99363..5fe057ff2a 100644 --- a/app/menus/windows-menu.c +++ b/app/menus/windows-menu.c @@ -415,6 +415,9 @@ windows_menu_display_query_tooltip (GtkWidget *widget, gint width; gint height; + if (! image) + return FALSE; + text = gtk_widget_get_tooltip_text (widget); gtk_tooltip_set_text (tooltip, text); g_free (text); diff --git a/app/operations/Makefile.am b/app/operations/Makefile.am index de9a042a76..dad1ec9ebd 100644 --- a/app/operations/Makefile.am +++ b/app/operations/Makefile.am @@ -52,6 +52,8 @@ libappoperations_a_sources = \ gimpoperationcomposecrop.h \ gimpoperationequalize.c \ gimpoperationequalize.h \ + gimpoperationfillsource.c \ + gimpoperationfillsource.h \ gimpoperationflood.c \ gimpoperationflood.h \ gimpoperationgradient.c \ @@ -60,8 +62,10 @@ libappoperations_a_sources = \ gimpoperationgrow.h \ gimpoperationhistogramsink.c \ gimpoperationhistogramsink.h \ - gimpoperationmaskcomponents.c \ + gimpoperationmaskcomponents.cc \ gimpoperationmaskcomponents.h \ + gimpoperationoffset.c \ + gimpoperationoffset.h \ gimpoperationprofiletransform.c \ gimpoperationprofiletransform.h \ gimpoperationscalarmultiply.c \ diff --git a/app/operations/Makefile.in b/app/operations/Makefile.in index fe919be7eb..0d95ef7bef 100644 --- a/app/operations/Makefile.in +++ b/app/operations/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -132,10 +131,12 @@ am__objects_2 = gimp-operations.$(OBJEXT) \ gimpoperationcagecoefcalc.$(OBJEXT) \ gimpoperationcagetransform.$(OBJEXT) \ gimpoperationcomposecrop.$(OBJEXT) \ - gimpoperationequalize.$(OBJEXT) gimpoperationflood.$(OBJEXT) \ + gimpoperationequalize.$(OBJEXT) \ + gimpoperationfillsource.$(OBJEXT) gimpoperationflood.$(OBJEXT) \ gimpoperationgradient.$(OBJEXT) gimpoperationgrow.$(OBJEXT) \ gimpoperationhistogramsink.$(OBJEXT) \ gimpoperationmaskcomponents.$(OBJEXT) \ + gimpoperationoffset.$(OBJEXT) \ gimpoperationprofiletransform.$(OBJEXT) \ gimpoperationscalarmultiply.$(OBJEXT) \ gimpoperationsemiflatten.$(OBJEXT) \ @@ -185,6 +186,7 @@ am__depfiles_remade = ./$(DEPDIR)/gimp-operation-config.Po \ ./$(DEPDIR)/gimpoperationcurves.Po \ ./$(DEPDIR)/gimpoperationdesaturate.Po \ ./$(DEPDIR)/gimpoperationequalize.Po \ + ./$(DEPDIR)/gimpoperationfillsource.Po \ ./$(DEPDIR)/gimpoperationflood.Po \ ./$(DEPDIR)/gimpoperationgradient.Po \ ./$(DEPDIR)/gimpoperationgrow.Po \ @@ -192,6 +194,7 @@ am__depfiles_remade = ./$(DEPDIR)/gimp-operation-config.Po \ ./$(DEPDIR)/gimpoperationhuesaturation.Po \ ./$(DEPDIR)/gimpoperationlevels.Po \ ./$(DEPDIR)/gimpoperationmaskcomponents.Po \ + ./$(DEPDIR)/gimpoperationoffset.Po \ ./$(DEPDIR)/gimpoperationpointfilter.Po \ ./$(DEPDIR)/gimpoperationposterize.Po \ ./$(DEPDIR)/gimpoperationprofiletransform.Po \ @@ -225,6 +228,24 @@ 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 \ @@ -317,8 +338,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -448,7 +467,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -587,8 +605,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ @@ -763,6 +779,8 @@ libappoperations_a_sources = \ gimpoperationcomposecrop.h \ gimpoperationequalize.c \ gimpoperationequalize.h \ + gimpoperationfillsource.c \ + gimpoperationfillsource.h \ gimpoperationflood.c \ gimpoperationflood.h \ gimpoperationgradient.c \ @@ -771,8 +789,10 @@ libappoperations_a_sources = \ gimpoperationgrow.h \ gimpoperationhistogramsink.c \ gimpoperationhistogramsink.h \ - gimpoperationmaskcomponents.c \ + gimpoperationmaskcomponents.cc \ gimpoperationmaskcomponents.h \ + gimpoperationoffset.c \ + gimpoperationoffset.h \ gimpoperationprofiletransform.c \ gimpoperationprofiletransform.h \ gimpoperationscalarmultiply.c \ @@ -822,7 +842,7 @@ CLEANFILES = $(gen_sources) all: all-recursive .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.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 \ @@ -886,6 +906,7 @@ distclean-compile: @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 @@ -893,6 +914,7 @@ distclean-compile: @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 @@ -931,6 +953,27 @@ am--depfiles: $(am__depfiles_remade) @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 @@ -1155,6 +1198,7 @@ distclean: distclean-recursive -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 @@ -1162,6 +1206,7 @@ distclean: distclean-recursive -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 @@ -1236,6 +1281,7 @@ maintainer-clean: maintainer-clean-recursive -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 @@ -1243,6 +1289,7 @@ maintainer-clean: maintainer-clean-recursive -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 diff --git a/app/operations/gimp-operations.c b/app/operations/gimp-operations.c index f21a02a8ba..7cb30fd2dd 100644 --- a/app/operations/gimp-operations.c +++ b/app/operations/gimp-operations.c @@ -36,11 +36,13 @@ #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" @@ -129,11 +131,13 @@ gimp_operations_init (Gimp *gimp) 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); diff --git a/app/operations/gimpcurvesconfig.c b/app/operations/gimpcurvesconfig.c index 4855ac8135..42426bcab9 100644 --- a/app/operations/gimpcurvesconfig.c +++ b/app/operations/gimpcurvesconfig.c @@ -402,13 +402,10 @@ gimp_curves_config_new_spline (gint32 channel, gimp_data_freeze (GIMP_DATA (curve)); gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH); - gimp_curve_set_n_points (curve, n_points); - - /* unset the last point */ - gimp_curve_set_point (curve, curve->n_points - 1, -1.0, -1.0); + gimp_curve_clear_points (curve); for (i = 0; i < n_points; i++) - gimp_curve_set_point (curve, i, + gimp_curve_add_point (curve, (gdouble) points[i * 2], (gdouble) points[i * 2 + 1]); @@ -598,18 +595,18 @@ gimp_curves_config_load_cruft (GimpCurvesConfig *config, gimp_data_freeze (GIMP_DATA (curve)); gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH); - gimp_curve_set_n_points (curve, GIMP_CURVE_N_CRUFT_POINTS); - - gimp_curve_reset (curve, FALSE); + gimp_curve_clear_points (curve); for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++) { - if (index[i][j] < 0 || value[i][j] < 0) - gimp_curve_set_point (curve, j, -1.0, -1.0); - else - gimp_curve_set_point (curve, j, - (gdouble) index[i][j] / 255.0, - (gdouble) value[i][j] / 255.0); + 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)); @@ -643,53 +640,39 @@ gimp_curves_config_save_cruft (GimpCurvesConfig *config, GimpCurve *curve = config->curve[i]; gint j; - if (curve->curve_type == GIMP_CURVE_FREE) + if (curve->curve_type == GIMP_CURVE_SMOOTH) { - gint n_points; + g_object_ref (curve); + } + else + { + curve = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (curve))); - for (j = 0; j < curve->n_points; j++) - { - curve->points[j].x = -1; - curve->points[j].y = -1; - } - - /* pick some points from the curve and make them control - * points - */ - n_points = CLAMP (9, curve->n_points / 2, curve->n_points); - - for (j = 0; j < n_points; j++) - { - gint sample = j * (curve->n_samples - 1) / (n_points - 1); - gint point = j * (curve->n_points - 1) / (n_points - 1); - - curve->points[point].x = ((gdouble) sample / - (gdouble) (curve->n_samples - 1)); - curve->points[point].y = curve->samples[sample]; - } + gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH); } - for (j = 0; j < curve->n_points; j++) + for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++) { - /* don't use gimp_curve_get_point() because that doesn't - * work when the curve type is GIMP_CURVE_FREE - */ - gdouble x = curve->points[j].x; - gdouble y = curve->points[j].y; + gint x = -1; + gint y = -1; - if (x < 0.0 || y < 0.0) + if (j < gimp_curve_get_n_points (curve)) { - g_string_append_printf (string, "%d %d ", -1, -1); - } - else - { - g_string_append_printf (string, "%d %d ", - (gint) (x * 255.999), - (gint) (y * 255.999)); + 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, diff --git a/app/operations/gimplevelsconfig.c b/app/operations/gimplevelsconfig.c index 27325a3083..5ea17e9f51 100644 --- a/app/operations/gimplevelsconfig.c +++ b/app/operations/gimplevelsconfig.c @@ -696,17 +696,14 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config) channel++) { GimpCurve *curve = curves->curve[channel]; - const gint n_points = gimp_curve_get_n_points (curve); static const gint n = 8; - gint point = -1; gdouble gamma = config->gamma[channel]; gdouble delta_in; gdouble delta_out; gdouble x, y; /* clear the points set by default */ - gimp_curve_set_point (curve, 0, -1, -1); - gimp_curve_set_point (curve, n_points - 1, -1, -1); + 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]; @@ -714,8 +711,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config) x = config->low_input[channel]; y = config->low_output[channel]; - point = CLAMP (n_points * x, point + 1, n_points - 1 - n); - gimp_curve_set_point (curve, point, x, y); + gimp_curve_add_point (curve, x, y); if (delta_out != 0 && gamma != 1.0) { @@ -755,8 +751,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config) x = config->low_input[channel] + dx; y = config->low_output[channel] + delta_out * gimp_operation_levels_map_input (config, channel, x); - point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i); - gimp_curve_set_point (curve, point, x, y); + gimp_curve_add_point (curve, x, y); } } else @@ -792,8 +787,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config) y = config->low_output[channel] + dy; x = config->low_input[channel] + delta_in * gimp_operation_levels_map_input (config_inv, channel, y); - point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i); - gimp_curve_set_point (curve, point, x, y); + gimp_curve_add_point (curve, x, y); } g_object_unref (config_inv); @@ -803,8 +797,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config) x = config->high_input[channel]; y = config->high_output[channel]; - point = CLAMP (n_points * x, point + 1, n_points - 1); - gimp_curve_set_point (curve, point, x, y); + gimp_curve_add_point (curve, x, y); } return curves; diff --git a/app/operations/gimpoperationbuffersourcevalidate.c b/app/operations/gimpoperationbuffersourcevalidate.c index 40b16871e4..e9e257bae3 100644 --- a/app/operations/gimpoperationbuffersourcevalidate.c +++ b/app/operations/gimpoperationbuffersourcevalidate.c @@ -18,19 +18,15 @@ * along with this program. If not, see . */ -#define GEGL_ITERATOR2_API #include "config.h" #include #include #include -#include "libgimpcolor/gimpcolor.h" -#include "libgimpconfig/gimpconfig.h" -#include "libgimpmath/gimpmath.h" - #include "operations-types.h" +#include "gegl/gimp-gegl-utils.h" #include "gegl/gimptilehandlervalidate.h" #include "gimpoperationbuffersourcevalidate.h" @@ -87,7 +83,7 @@ gimp_operation_buffer_source_validate_class_init (GimpOperationBufferSourceValid operation_class->process = gimp_operation_buffer_source_validate_process; operation_class->threaded = FALSE; - operation_class->no_cache = TRUE; + operation_class->cache_policy = GEGL_CACHE_POLICY_NEVER; gegl_operation_class_set_keys (operation_class, "name", "gimp:buffer-source-validate", @@ -240,89 +236,16 @@ gimp_operation_buffer_source_validate_process (GeglOperation *operation, if (validate_handler) { - gint n_threads; + GeglRectangle rect; - g_object_get (gegl_config (), - "threads", &n_threads, - NULL); + /* align the rectangle to the tile grid */ + gimp_gegl_rectangle_align_to_tile_grid ( + &rect, result, buffer_source_validate->buffer); - /* the main reason to validate the buffer during processing is to - * avoid threading issues. skip validation if not using - * multithreading. - */ - if (n_threads > 1) - { - gint shift_x; - gint shift_y; - gint tile_width; - gint tile_height; - cairo_rectangle_int_t rect; - cairo_region_overlap_t overlap; - - g_object_get (buffer_source_validate->buffer, - "shift-x", &shift_x, - "shift-y", &shift_y, - "tile-width", &tile_width, - "tile-height", &tile_height, - NULL); - - /* align the rectangle to the tile grid */ - rect.x = (gint) floor ((gdouble) (result->x + shift_x) / tile_width) * tile_width; - rect.y = (gint) floor ((gdouble) (result->y + shift_y) / tile_height) * tile_height; - rect.width = (gint) ceil ((gdouble) (result->x + result->width + shift_x) / tile_width) * tile_width - rect.x; - rect.height = (gint) ceil ((gdouble) (result->y + result->height + shift_y) / tile_height) * tile_height - rect.y; - - /* check if the rectangle interescts with the dirty region */ - overlap = cairo_region_contains_rectangle (validate_handler->dirty_region, - &rect); - - if (overlap != CAIRO_REGION_OVERLAP_OUT) - { - GeglBufferIterator *iter; - - /* if the rectangle is not entirely within the dirty - * region ... - */ - if (overlap == CAIRO_REGION_OVERLAP_PART) - { - cairo_region_t *region; - cairo_rectangle_int_t intersection; - - /* ... intersect it with region and use the result's - * bounds - */ - region = cairo_region_copy (validate_handler->dirty_region); - - cairo_region_intersect_rectangle (region, &rect); - cairo_region_get_extents (region, &intersection); - - cairo_region_destroy (region); - - /* realign the rectangle to the tile grid */ - rect.x = (gint) floor ((gdouble) (intersection.x ) / tile_width) * tile_width; - rect.y = (gint) floor ((gdouble) (intersection.y ) / tile_height) * tile_height; - rect.width = (gint) ceil ((gdouble) (intersection.x + intersection.width ) / tile_width) * tile_width - rect.x; - rect.height = (gint) ceil ((gdouble) (intersection.y + intersection.height) / tile_height) * tile_height - rect.y; - } - - rect.x -= shift_x; - rect.y -= shift_y; - - /* iterate over the rectangle -- this implicitly causes - * validation - */ - iter = gegl_buffer_iterator_new (buffer, - GEGL_RECTANGLE (rect.x, - rect.y, - rect.width, - rect.height), - level, NULL, - GEGL_BUFFER_READ, - GEGL_ABYSS_NONE, 1); - - while (gegl_buffer_iterator_next (iter)); - } - } + gimp_tile_handler_validate_validate (validate_handler, + buffer_source_validate->buffer, + &rect, + TRUE); } gegl_operation_context_set_object (context, "output", G_OBJECT (buffer)); diff --git a/app/operations/gimpoperationcagecoefcalc.c b/app/operations/gimpoperationcagecoefcalc.c index 66e7527341..ba8cd3f5e4 100644 --- a/app/operations/gimpoperationcagecoefcalc.c +++ b/app/operations/gimpoperationcagecoefcalc.c @@ -20,7 +20,6 @@ #include "config.h" #include -#define GEGL_ITERATOR2_API #include #include "libgimpmath/gimpmath.h" @@ -72,7 +71,7 @@ gimp_operation_cage_coef_calc_class_init (GimpOperationCageCoefCalcClass *klass) operation_class->prepare = gimp_operation_cage_coef_calc_prepare; operation_class->get_bounding_box = gimp_operation_cage_coef_calc_get_bounding_box; - operation_class->no_cache = FALSE; + operation_class->cache_policy = GEGL_CACHE_POLICY_ALWAYS; operation_class->get_cached_region = NULL; source_class->process = gimp_operation_cage_coef_calc_process; diff --git a/app/operations/gimpoperationcagetransform.c b/app/operations/gimpoperationcagetransform.c index d2267c8bfa..46bc4cb138 100644 --- a/app/operations/gimpoperationcagetransform.c +++ b/app/operations/gimpoperationcagetransform.c @@ -20,7 +20,6 @@ #include "config.h" #include -#define GEGL_ITERATOR2_API #include #include @@ -109,7 +108,6 @@ gimp_operation_cage_transform_class_init (GimpOperationCageTransformClass *klass 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->no_cache = FALSE; 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. diff --git a/app/operations/gimpoperationfillsource.c b/app/operations/gimpoperationfillsource.c new file mode 100644 index 0000000000..c2a798fd06 --- /dev/null +++ b/app/operations/gimpoperationfillsource.c @@ -0,0 +1,255 @@ +/* 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 "gegl/gimp-gegl-utils.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; + + gimp_gegl_rectangle_align_to_tile_grid ( + &rect, result, + gimp_drawable_get_buffer (fill_source->drawable)); + + 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 0000000000..6a9f468c93 --- /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/gimpoperationgradient.c b/app/operations/gimpoperationgradient.c index ec3a0164c7..a10900d115 100644 --- a/app/operations/gimpoperationgradient.c +++ b/app/operations/gimpoperationgradient.c @@ -23,7 +23,6 @@ #include "config.h" #include -#define GEGL_ITERATOR2_API #include #include @@ -81,11 +80,9 @@ typedef struct typedef struct { - GeglBuffer *buffer; - gfloat *row_data; - gint roi_x; - gint width; - GRand *dither_rand; + gfloat *data; + GeglRectangle roi; + GRand *dither_rand; } PutPixelData; @@ -983,8 +980,8 @@ gradient_put_pixel (gint x, gpointer put_pixel_data) { PutPixelData *ppd = put_pixel_data; - const gint index = x - ppd->roi_x; - gfloat *dest = ppd->row_data + 4 * index; + const gint index = (y - ppd->roi.y) * ppd->roi.width + (x - ppd->roi.x); + gfloat *dest = ppd->data + 4 * index; if (ppd->dither_rand) { @@ -1012,12 +1009,6 @@ gradient_put_pixel (gint x, *dest++ = color->b; *dest++ = color->a; } - - /* Paint whole row if we are on the rightmost pixel */ - if (index == (ppd->width - 1)) - gegl_buffer_set (ppd->buffer, GEGL_RECTANGLE (ppd->roi_x, y, ppd->width, 1), - 0, babl_format ("R'G'B'A float"), ppd->row_data, - GEGL_AUTO_ROWSTRIDE); } static gboolean @@ -1036,6 +1027,10 @@ gimp_operation_gradient_process (GeglOperation *operation, RenderBlendData rbd = { 0, }; + GeglBufferIterator *iter; + GeglRectangle *roi; + GRand *dither_rand = NULL; + if (! self->gradient) return TRUE; @@ -1098,45 +1093,38 @@ gimp_operation_gradient_process (GeglOperation *operation, /* 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 = { 0, }; + PutPixelData ppd; - ppd.buffer = output; - ppd.row_data = g_malloc (sizeof (float) * 4 * result->width); - ppd.roi_x = result->x; - ppd.width = result->width; - if (self->dither) - ppd.dither_rand = g_rand_new (); + ppd.dither_rand = dither_rand; - gimp_adaptive_supersample_area (result->x, result->y, - result->x + result->width - 1, - result->y + result->height - 1, - self->supersample_depth, - self->supersample_threshold, - gradient_render_pixel, &rbd, - gradient_put_pixel, &ppd, - NULL, - NULL); + while (gegl_buffer_iterator_next (iter)) + { + ppd.data = iter->items[0].data; + ppd.roi = *roi; - if (self->dither) - g_rand_free (ppd.dither_rand); - g_free (ppd.row_data); + 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 { - GeglBufferIterator *iter; - GeglRectangle *roi; - GRand *seed = NULL; - - 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) - seed = g_rand_new (); - while (gegl_buffer_iterator_next (iter)) { gfloat *dest = iter->items[0].data; @@ -1144,10 +1132,8 @@ gimp_operation_gradient_process (GeglOperation *operation, gint endy = roi->y + roi->height; gint x, y; - if (seed) + if (dither_rand) { - GRand *dither_rand = g_rand_new_with_seed (g_rand_int (seed)); - for (y = roi->y; y < endy; y++) for (x = roi->x; x < endx; x++) { @@ -1171,8 +1157,6 @@ gimp_operation_gradient_process (GeglOperation *operation, *dest++ = MAX (b, 0.0); *dest++ = MAX (a, 0.0); } - - g_rand_free (dither_rand); } else { @@ -1190,11 +1174,11 @@ gimp_operation_gradient_process (GeglOperation *operation, } } } - - if (self->dither) - g_rand_free (seed); } + if (self->dither) + g_rand_free (dither_rand); + g_clear_object (&rbd.dist_sampler); return TRUE; diff --git a/app/operations/gimpoperationlevels.c b/app/operations/gimpoperationlevels.c index ea91a48631..1bf0b0636b 100644 --- a/app/operations/gimpoperationlevels.c +++ b/app/operations/gimpoperationlevels.c @@ -201,7 +201,7 @@ gimp_operation_levels_map_input (GimpLevelsConfig *config, else value = (value - config->low_input[channel]); - if (config->gamma[channel] != 0.0) + if (config->gamma[channel] != 0.0 && value > 0.0) value = pow (value, 1.0 / config->gamma[channel]); return value; diff --git a/app/operations/gimpoperationmaskcomponents.c b/app/operations/gimpoperationmaskcomponents.c deleted file mode 100644 index 2d17b3df9b..0000000000 --- a/app/operations/gimpoperationmaskcomponents.c +++ /dev/null @@ -1,248 +0,0 @@ -/* 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 - -#include "operations-types.h" - -#include "gimpoperationmaskcomponents.h" - - -enum -{ - PROP_0, - PROP_MASK -}; - - -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 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->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, - 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; - - 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 = g_value_get_flags (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gimp_operation_mask_components_prepare (GeglOperation *operation) -{ - const Babl *format = gegl_operation_get_source_format (operation, "input"); - - if (format) - { - const Babl *model = babl_format_get_model (format); - - if (model == babl_model ("R'G'B'A")) - format = babl_format ("R'G'B'A float"); - else - format = babl_format ("RGBA float"); - } - else - { - format = babl_format ("RGBA float"); - } - - gegl_operation_set_format (operation, "input", format); - gegl_operation_set_format (operation, "aux", format); - gegl_operation_set_format (operation, "output", format); -} - -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 0 - 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"); - - gegl_operation_context_set_object (context, "output", aux); - - return TRUE; - } -#endif - 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) -{ - GimpOperationMaskComponents *self = GIMP_OPERATION_MASK_COMPONENTS (operation); - gfloat *src = in_buf; - gfloat *aux = aux_buf; - gfloat *dest = out_buf; - GimpComponentMask mask = self->mask; - static const gfloat nothing[] = { 0.0, 0.0, 0.0, 1.0 }; - -#if 0 - if (self->mask == 0) - { - if (in_buf != out_buf) - memcpy (out_buf, in_buf, sizeof (float) * 4 * samples); - } - else if (self->mask == GIMP_COMPONENT_MASK_ALL && aux_buf) - { - if (aux_buf != out_buf) - memcpy (out_buf, aux_buf, sizeof (float) * 4 * samples); - } - else -#endif - { - if (! aux) - aux = (gfloat *) nothing; - - while (samples--) - { - dest[RED] = (mask & GIMP_COMPONENT_MASK_RED) ? aux[RED] : src[RED]; - dest[GREEN] = (mask & GIMP_COMPONENT_MASK_GREEN) ? aux[GREEN] : src[GREEN]; - dest[BLUE] = (mask & GIMP_COMPONENT_MASK_BLUE) ? aux[BLUE] : src[BLUE]; - dest[ALPHA] = (mask & GIMP_COMPONENT_MASK_ALPHA) ? aux[ALPHA] : src[ALPHA]; - - src += 4; - - if (aux_buf) - aux += 4; - - dest += 4; - } - } - - return TRUE; -} diff --git a/app/operations/gimpoperationmaskcomponents.cc b/app/operations/gimpoperationmaskcomponents.cc new file mode 100644 index 0000000000..b28171b8ca --- /dev/null +++ b/app/operations/gimpoperationmaskcomponents.cc @@ -0,0 +1,552 @@ +/* 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 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->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 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 index d4999c8c74..0919a3876f 100644 --- a/app/operations/gimpoperationmaskcomponents.h +++ b/app/operations/gimpoperationmaskcomponents.h @@ -40,6 +40,11 @@ struct _GimpOperationMaskComponents GeglOperationPointComposer parent_instance; GimpComponentMask mask; + gdouble alpha; + + guint32 alpha_value; + gpointer process; + const Babl *format; }; struct _GimpOperationMaskComponentsClass @@ -48,7 +53,16 @@ struct _GimpOperationMaskComponentsClass }; -GType gimp_operation_mask_components_get_type (void) G_GNUC_CONST; +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 0000000000..ee269eddf9 --- /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 0000000000..dcd0e0b320 --- /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/gimpoperationsemiflatten.c b/app/operations/gimpoperationsemiflatten.c index ce0c630709..1352f02bb2 100644 --- a/app/operations/gimpoperationsemiflatten.c +++ b/app/operations/gimpoperationsemiflatten.c @@ -92,8 +92,8 @@ gimp_operation_semi_flatten_class_init (GimpOperationSemiFlattenClass *klass) g_object_class_install_property (object_class, PROP_COLOR, gimp_param_spec_rgb ("color", - "Color", - "The color", + _("Color"), + _("The color"), FALSE, &white, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); diff --git a/app/operations/gimpoperationthresholdalpha.c b/app/operations/gimpoperationthresholdalpha.c index 2a3edeffe6..94cbaf51a0 100644 --- a/app/operations/gimpoperationthresholdalpha.c +++ b/app/operations/gimpoperationthresholdalpha.c @@ -87,8 +87,8 @@ gimp_operation_threshold_alpha_class_init (GimpOperationThresholdAlphaClass *kla g_object_class_install_property (object_class, PROP_VALUE, g_param_spec_double ("value", - "Value", - "The alpha value", + _("Value"), + _("The alpha value"), 0.0, 1.0, 0.5, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); diff --git a/app/operations/layer-modes-legacy/Makefile.in b/app/operations/layer-modes-legacy/Makefile.in index b85b216fea..08cb89e089 100644 --- a/app/operations/layer-modes-legacy/Makefile.in +++ b/app/operations/layer-modes-legacy/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -248,8 +247,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -379,7 +376,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -518,8 +514,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/operations/layer-modes/Makefile.in b/app/operations/layer-modes/Makefile.in index d54fc510c3..3382d826f4 100644 --- a/app/operations/layer-modes/Makefile.in +++ b/app/operations/layer-modes/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -261,8 +260,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -392,7 +389,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -531,8 +527,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/operations/layer-modes/gimp-layer-modes.c b/app/operations/layer-modes/gimp-layer-modes.c index a8788753a6..2064ae539c 100644 --- a/app/operations/layer-modes/gimp-layer-modes.c +++ b/app/operations/layer-modes/gimp-layer-modes.c @@ -60,7 +60,8 @@ static const GimpLayerModeInfo layer_mode_infos[] = .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_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, @@ -70,8 +71,9 @@ static const GimpLayerModeInfo layer_mode_infos[] = { GIMP_LAYER_MODE_DISSOLVE, .op_name = "gimp:dissolve", - .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | - GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE, + .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 @@ -84,8 +86,7 @@ static const GimpLayerModeInfo layer_mode_infos[] = 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_FADE, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT, .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL @@ -366,8 +367,7 @@ static const GimpLayerModeInfo layer_mode_infos[] = 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_FADE, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT, .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, @@ -436,7 +436,8 @@ static const GimpLayerModeInfo layer_mode_infos[] = { GIMP_LAYER_MODE_NORMAL, .op_name = "gimp:normal", - .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .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, @@ -447,8 +448,7 @@ static const GimpLayerModeInfo layer_mode_infos[] = .op_name = "gimp:behind", .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, - .context = GIMP_LAYER_MODE_CONTEXT_PAINT | - GIMP_LAYER_MODE_CONTEXT_FADE, + .context = GIMP_LAYER_MODE_CONTEXT_PAINT, .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR @@ -775,7 +775,8 @@ static const GimpLayerModeInfo layer_mode_infos[] = .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_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, @@ -785,7 +786,8 @@ static const GimpLayerModeInfo layer_mode_infos[] = { GIMP_LAYER_MODE_MERGE, .op_name = "gimp:merge", - .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, + .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, @@ -798,7 +800,8 @@ static const GimpLayerModeInfo layer_mode_infos[] = .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_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 @@ -807,8 +810,9 @@ static const GimpLayerModeInfo layer_mode_infos[] = { 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, + .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 @@ -817,8 +821,8 @@ static const GimpLayerModeInfo layer_mode_infos[] = { GIMP_LAYER_MODE_REPLACE, .op_name = "gimp:replace", - .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE, - .context = GIMP_LAYER_MODE_CONTEXT_FADE, + .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | + GIMP_LAYER_MODE_FLAG_TRIVIAL, .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_space = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR @@ -830,7 +834,6 @@ static const GimpLayerModeInfo layer_mode_infos[] = .flags = GIMP_LAYER_MODE_FLAG_BLEND_SPACE_IMMUTABLE | GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE | GIMP_LAYER_MODE_FLAG_ALPHA_ONLY, - .context = GIMP_LAYER_MODE_CONTEXT_FADE, .paint_composite_mode = GIMP_LAYER_COMPOSITE_UNION, .composite_mode = GIMP_LAYER_COMPOSITE_UNION } @@ -1189,6 +1192,17 @@ gimp_layer_mode_is_alpha_only (GimpLayerMode mode) 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) { @@ -1409,11 +1423,14 @@ gimp_layer_mode_get_for_group (GimpLayerMode old_mode, } const Babl * -gimp_layer_mode_get_format (GimpLayerMode mode, - GimpLayerColorSpace composite_space, - GimpLayerColorSpace blend_space, - const Babl *preferred_format) +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; @@ -1421,6 +1438,32 @@ gimp_layer_mode_get_format (GimpLayerMode 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); + + 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: diff --git a/app/operations/layer-modes/gimp-layer-modes.h b/app/operations/layer-modes/gimp-layer-modes.h index 8005fd1ab4..30c5e152a4 100644 --- a/app/operations/layer-modes/gimp-layer-modes.h +++ b/app/operations/layer-modes/gimp-layer-modes.h @@ -33,6 +33,7 @@ gboolean gimp_layer_mode_is_composite_mode_mutable (GimpLayer 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); @@ -60,8 +61,9 @@ gboolean gimp_layer_mode_get_for_group (GimpLayer GimpLayerMode *new_mode); const Babl * gimp_layer_mode_get_format (GimpLayerMode mode, - GimpLayerColorSpace composite_space, GimpLayerColorSpace blend_space, + GimpLayerColorSpace composite_space, + GimpLayerCompositeMode composite_mode, const Babl *preferred_format); GimpLayerCompositeRegion gimp_layer_mode_get_included_region (GimpLayerMode mode, diff --git a/app/operations/layer-modes/gimpoperationlayermode-blend.c b/app/operations/layer-modes/gimpoperationlayermode-blend.c index 5ab6a15ff9..e107462d09 100644 --- a/app/operations/layer-modes/gimpoperationlayermode-blend.c +++ b/app/operations/layer-modes/gimpoperationlayermode-blend.c @@ -80,10 +80,11 @@ safe_div (gfloat a, void /* aka linear_dodge */ -gimp_operation_layer_mode_blend_addition (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_addition (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -104,10 +105,11 @@ gimp_operation_layer_mode_blend_addition (const gfloat *in, } void -gimp_operation_layer_mode_blend_burn (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_burn (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -128,10 +130,11 @@ gimp_operation_layer_mode_blend_burn (const gfloat *in, } void -gimp_operation_layer_mode_blend_darken_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_darken_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -152,10 +155,11 @@ gimp_operation_layer_mode_blend_darken_only (const gfloat *in, } void -gimp_operation_layer_mode_blend_difference (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_difference (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -176,10 +180,11 @@ gimp_operation_layer_mode_blend_difference (const gfloat *in, } void -gimp_operation_layer_mode_blend_divide (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_divide (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -200,10 +205,11 @@ gimp_operation_layer_mode_blend_divide (const gfloat *in, } void -gimp_operation_layer_mode_blend_dodge (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_dodge (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -224,10 +230,11 @@ gimp_operation_layer_mode_blend_dodge (const gfloat *in, } void -gimp_operation_layer_mode_blend_exclusion (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_exclusion (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -248,10 +255,11 @@ gimp_operation_layer_mode_blend_exclusion (const gfloat *in, } void -gimp_operation_layer_mode_blend_grain_extract (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_grain_extract (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -272,10 +280,11 @@ gimp_operation_layer_mode_blend_grain_extract (const gfloat *in, } void -gimp_operation_layer_mode_blend_grain_merge (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_grain_merge (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -296,10 +305,11 @@ gimp_operation_layer_mode_blend_grain_merge (const gfloat *in, } void -gimp_operation_layer_mode_blend_hard_mix (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_hard_mix (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -320,10 +330,11 @@ gimp_operation_layer_mode_blend_hard_mix (const gfloat *in, } void -gimp_operation_layer_mode_blend_hardlight (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_hardlight (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -358,10 +369,11 @@ gimp_operation_layer_mode_blend_hardlight (const gfloat *in, } void -gimp_operation_layer_mode_blend_hsl_color (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_hsl_color (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -422,10 +434,11 @@ gimp_operation_layer_mode_blend_hsl_color (const gfloat *in, } void -gimp_operation_layer_mode_blend_hsv_hue (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_hsv_hue (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -477,10 +490,11 @@ gimp_operation_layer_mode_blend_hsv_hue (const gfloat *in, } void -gimp_operation_layer_mode_blend_hsv_saturation (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_hsv_saturation (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -531,10 +545,11 @@ gimp_operation_layer_mode_blend_hsv_saturation (const gfloat *in, } void -gimp_operation_layer_mode_blend_hsv_value (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_hsv_value (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -574,10 +589,11 @@ gimp_operation_layer_mode_blend_hsv_value (const gfloat *in, } void -gimp_operation_layer_mode_blend_lch_chroma (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_lch_chroma (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -616,10 +632,11 @@ gimp_operation_layer_mode_blend_lch_chroma (const gfloat *in, } void -gimp_operation_layer_mode_blend_lch_color (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_lch_color (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -639,10 +656,11 @@ gimp_operation_layer_mode_blend_lch_color (const gfloat *in, } void -gimp_operation_layer_mode_blend_lch_hue (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_lch_hue (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -681,10 +699,11 @@ gimp_operation_layer_mode_blend_lch_hue (const gfloat *in, } void -gimp_operation_layer_mode_blend_lch_lightness (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_lch_lightness (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -704,10 +723,11 @@ gimp_operation_layer_mode_blend_lch_lightness (const gfloat *in, } void -gimp_operation_layer_mode_blend_lighten_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_lighten_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -728,10 +748,11 @@ gimp_operation_layer_mode_blend_lighten_only (const gfloat *in, } void -gimp_operation_layer_mode_blend_linear_burn (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_linear_burn (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -754,10 +775,11 @@ gimp_operation_layer_mode_blend_linear_burn (const gfloat *in, /* 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 (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_linear_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -787,10 +809,11 @@ gimp_operation_layer_mode_blend_linear_light (const gfloat *in, } void -gimp_operation_layer_mode_blend_luma_darken_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_luma_darken_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -824,10 +847,11 @@ gimp_operation_layer_mode_blend_luma_darken_only (const gfloat *in, } void -gimp_operation_layer_mode_blend_luma_lighten_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_luma_lighten_only (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -861,25 +885,33 @@ gimp_operation_layer_mode_blend_luma_lighten_only (const gfloat *in, } void -gimp_operation_layer_mode_blend_luminance (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_luminance (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { - gfloat layer_Y[samples], *layer_Y_p; - gfloat in_Y[samples], *in_Y_p; + static const Babl *fish; + gfloat *scratch; + gfloat *in_Y; + gfloat *layer_Y; - babl_process (babl_fish ("RGBA float", "Y float"), layer, layer_Y, samples); - babl_process (babl_fish ("RGBA float", "Y float"), in, in_Y, samples); + if (! fish) + fish = babl_fish ("RGBA float", "Y float"); - layer_Y_p = &layer_Y[0]; - in_Y_p = &in_Y[0]; + 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_p[0], in_Y_p[0]); + gfloat ratio = safe_div (layer_Y[0], in_Y[0]); gint c; for (c = 0; c < 3; c ++) @@ -888,19 +920,22 @@ gimp_operation_layer_mode_blend_luminance (const gfloat *in, comp[ALPHA] = layer[ALPHA]; - comp += 4; - in += 4; - layer += 4; - in_Y_p ++; - layer_Y_p ++; + comp += 4; + in += 4; + layer += 4; + in_Y ++; + layer_Y ++; } + + gegl_scratch_free (scratch); } void -gimp_operation_layer_mode_blend_multiply (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_multiply (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -921,10 +956,11 @@ gimp_operation_layer_mode_blend_multiply (const gfloat *in, } void -gimp_operation_layer_mode_blend_overlay (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_overlay (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -955,10 +991,11 @@ gimp_operation_layer_mode_blend_overlay (const gfloat *in, /* 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 (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_pin_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -988,10 +1025,11 @@ gimp_operation_layer_mode_blend_pin_light (const gfloat *in, } void -gimp_operation_layer_mode_blend_screen (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_screen (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -1012,10 +1050,11 @@ gimp_operation_layer_mode_blend_screen (const gfloat *in, } void -gimp_operation_layer_mode_blend_softlight (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_softlight (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -1041,10 +1080,11 @@ gimp_operation_layer_mode_blend_softlight (const gfloat *in, } void -gimp_operation_layer_mode_blend_subtract (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_subtract (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -1067,10 +1107,11 @@ gimp_operation_layer_mode_blend_subtract (const gfloat *in, /* added according to: http://www.simplefilter.de/en/basics/mixmods.html */ void -gimp_operation_layer_mode_blend_vivid_light (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_vivid_light (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { @@ -1115,10 +1156,11 @@ gimp_operation_layer_mode_blend_vivid_light (const gfloat *in, void -gimp_operation_layer_mode_blend_color_erase (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples) +gimp_operation_layer_mode_blend_color_erase (GeglOperation *operation, + const gfloat *in, + const gfloat *layer, + gfloat *comp, + gint samples) { while (samples--) { diff --git a/app/operations/layer-modes/gimpoperationlayermode-blend.h b/app/operations/layer-modes/gimpoperationlayermode-blend.h index 25290a4645..3a8f995070 100644 --- a/app/operations/layer-modes/gimpoperationlayermode-blend.h +++ b/app/operations/layer-modes/gimpoperationlayermode-blend.h @@ -26,142 +26,175 @@ /* nonsubtractive blend functions */ -void gimp_operation_layer_mode_blend_addition (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_burn (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_darken_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_difference (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_divide (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_dodge (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_exclusion (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_grain_extract (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_grain_merge (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_hard_mix (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_hardlight (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_hsl_color (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_hsv_hue (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_hsv_saturation (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_hsv_value (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_lch_chroma (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_lch_color (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_lch_hue (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_lch_lightness (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_lighten_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_linear_burn (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_linear_light (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_luma_darken_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_luma_lighten_only (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_luminance (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_multiply (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_overlay (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_pin_light (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_screen (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_softlight (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_subtract (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); -void gimp_operation_layer_mode_blend_vivid_light (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); +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 (const gfloat *in, - const gfloat *layer, - gfloat *comp, - gint samples); +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.c b/app/operations/layer-modes/gimpoperationlayermode.c index b2e679b792..a9a806f312 100644 --- a/app/operations/layer-modes/gimpoperationlayermode.c +++ b/app/operations/layer-modes/gimpoperationlayermode.c @@ -359,8 +359,9 @@ gimp_operation_layer_mode_prepare (GeglOperation *operation) } format = gimp_layer_mode_get_format (self->layer_mode, - self->composite_space, self->blend_space, + self->composite_space, + self->composite_mode, preferred_format); gegl_operation_set_format (operation, "input", format); @@ -677,7 +678,7 @@ gimp_operation_layer_mode_real_process (GeglOperation *operation, babl_process (composite_to_blend_fish, layer + first, blend_layer + first, count); - blend_function (blend_in + first, blend_layer + first, + blend_function (operation, blend_in + first, blend_layer + first, blend_out + first, count); babl_process (blend_to_composite_fish, @@ -702,7 +703,7 @@ gimp_operation_layer_mode_real_process (GeglOperation *operation, blend_out = g_alloca (sizeof (gfloat) * 4 * samples); } - blend_function (blend_in, blend_layer, blend_out, samples); + blend_function (operation, blend_in, blend_layer, blend_out, samples); } if (! gimp_layer_mode_is_subtractive (layer_mode->layer_mode)) diff --git a/app/operations/layer-modes/gimpoperationpassthrough.c b/app/operations/layer-modes/gimpoperationpassthrough.c index fab9dc7d74..bc7d328a8f 100644 --- a/app/operations/layer-modes/gimpoperationpassthrough.c +++ b/app/operations/layer-modes/gimpoperationpassthrough.c @@ -25,22 +25,12 @@ #include "../operations-types.h" -#include "gimp-layer-modes.h" #include "gimpoperationpassthrough.h" -static gboolean gimp_operation_pass_through_parent_process (GeglOperation *operation, - GeglOperationContext *context, - const gchar *output_prop, - const GeglRectangle *result, - gint level); - - G_DEFINE_TYPE (GimpOperationPassThrough, gimp_operation_pass_through, GIMP_TYPE_OPERATION_REPLACE) -#define parent_class gimp_operation_pass_through_parent_class - static void gimp_operation_pass_through_class_init (GimpOperationPassThroughClass *klass) @@ -53,8 +43,6 @@ gimp_operation_pass_through_class_init (GimpOperationPassThroughClass *klass) "description", "GIMP pass through mode operation", NULL); - operation_class->process = gimp_operation_pass_through_parent_process; - /* don't use REPLACE mode's specialized get_affected_region(); PASS_THROUGH * behaves like an ordinary layer mode here. */ @@ -65,42 +53,3 @@ static void gimp_operation_pass_through_init (GimpOperationPassThrough *self) { } - -static gboolean -gimp_operation_pass_through_parent_process (GeglOperation *operation, - GeglOperationContext *context, - const gchar *output_prop, - const GeglRectangle *result, - gint level) -{ - GimpOperationLayerMode *layer_mode = (gpointer) operation; - - /* if the layer's opacity is 100%, it has no mask, and its composite mode - * contains "aux" (the latter should always be the case for pass through - * mode,) we can just pass "aux" directly as output. note that the same - * optimization would more generally apply to REPLACE mode, save for the fact - * that when both the backdrop and the layer have a pixel with 0% alpha, we - * want to maintain the color value of the backdrop, not the layer; since, - * for pass through groups, the layer is already composited against the - * backdrop, such pixels will have the same color value for both the backdrop - * and the layer. - */ - if (layer_mode->opacity == 1.0 && - ! gegl_operation_context_get_object (context, "aux2") && - (gimp_layer_mode_get_included_region (layer_mode->layer_mode, - layer_mode->real_composite_mode) & - 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; - } - - return GEGL_OPERATION_CLASS (parent_class)->process (operation, context, - output_prop, result, - level); -} diff --git a/app/operations/layer-modes/gimpoperationreplace.c b/app/operations/layer-modes/gimpoperationreplace.c index d033be20b3..18111fd402 100644 --- a/app/operations/layer-modes/gimpoperationreplace.c +++ b/app/operations/layer-modes/gimpoperationreplace.c @@ -20,13 +20,22 @@ #include "config.h" +#include + #include #include "../operations-types.h" +#include "gimp-layer-modes.h" #include "gimpoperationreplace.h" +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, @@ -41,6 +50,8 @@ static GimpLayerCompositeRegion gimp_operation_replace_get_affected_region (Gi 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) @@ -53,6 +64,8 @@ gimp_operation_replace_class_init (GimpOperationReplaceClass *klass) "description", "GIMP replace mode operation", NULL); + operation_class->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; } @@ -62,6 +75,41 @@ gimp_operation_replace_init (GimpOperationReplace *self) { } +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; + + /* 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") && + (gimp_layer_mode_get_included_region (layer_mode->layer_mode, + layer_mode->real_composite_mode) & + 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. + */ + + return GEGL_OPERATION_CLASS (parent_class)->process (op, context, output_prop, + result, level); +} + static gboolean gimp_operation_replace_process (GeglOperation *op, void *in_p, @@ -88,6 +136,7 @@ gimp_operation_replace_process (GeglOperation *op, { gfloat opacity_value = opacity; gfloat new_alpha; + gfloat ratio; gint b; if (has_mask) @@ -95,18 +144,13 @@ gimp_operation_replace_process (GeglOperation *op, new_alpha = (layer[ALPHA] - in[ALPHA]) * opacity_value + in[ALPHA]; - if (new_alpha) - { - gfloat ratio = opacity_value * layer[ALPHA] / new_alpha; + ratio = opacity_value; - for (b = RED; b < ALPHA; b++) - out[b] = (layer[b] - in[b]) * ratio + in[b]; - } - else - { - for (b = RED; b < ALPHA; b++) - out[b] = in[b]; - } + 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; @@ -136,8 +180,8 @@ gimp_operation_replace_process (GeglOperation *op, out[ALPHA] = new_alpha; - in += 4; - out += 4; + in += 4; + out += 4; if (has_mask) mask++; @@ -156,16 +200,8 @@ gimp_operation_replace_process (GeglOperation *op, new_alpha = layer[ALPHA] * opacity_value; - if (new_alpha) - { - for (b = RED; b < ALPHA; b++) - out[b] = layer[b]; - } - else - { - for (b = RED; b < ALPHA; b++) - out[b] = in[b]; - } + for (b = RED; b < ALPHA; b++) + out[b] = layer[b]; out[ALPHA] = new_alpha; @@ -179,18 +215,7 @@ gimp_operation_replace_process (GeglOperation *op, 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; - } + memset (out, 0, 4 * samples * sizeof (gfloat)); break; } diff --git a/app/operations/operations-enums.c b/app/operations/operations-enums.c index a0d67b88e0..dabf1fca01 100644 --- a/app/operations/operations-enums.c +++ b/app/operations/operations-enums.c @@ -338,7 +338,6 @@ gimp_layer_mode_context_get_type (void) { 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_FADE, "GIMP_LAYER_MODE_CONTEXT_FADE", "fade" }, { GIMP_LAYER_MODE_CONTEXT_ALL, "GIMP_LAYER_MODE_CONTEXT_ALL", "all" }, { 0, NULL, NULL } }; @@ -348,7 +347,6 @@ gimp_layer_mode_context_get_type (void) { 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_FADE, "GIMP_LAYER_MODE_CONTEXT_FADE", NULL }, { GIMP_LAYER_MODE_CONTEXT_ALL, "GIMP_LAYER_MODE_CONTEXT_ALL", NULL }, { 0, NULL, NULL } }; diff --git a/app/operations/operations-enums.h b/app/operations/operations-enums.h index ad81bc170d..8bcfb06e00 100644 --- a/app/operations/operations-enums.h +++ b/app/operations/operations-enums.h @@ -153,12 +153,10 @@ 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_FADE = 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_FADE) + GIMP_LAYER_MODE_CONTEXT_PAINT) } GimpLayerModeContext; @@ -182,7 +180,8 @@ typedef enum /*< pdb-skip, skip >*/ 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_ALPHA_ONLY = 1 << 5, + GIMP_LAYER_MODE_FLAG_TRIVIAL = 1 << 6 } GimpLayerModeFlags; diff --git a/app/operations/operations-types.h b/app/operations/operations-types.h index 0f418a2332..f3b774b19b 100644 --- a/app/operations/operations-types.h +++ b/app/operations/operations-types.h @@ -64,7 +64,8 @@ typedef gboolean (* GimpLayerModeFunc) (GeglOperation *operation, const GeglRectangle *roi, gint level); -typedef void (* GimpLayerModeBlendFunc) (const gfloat *in, +typedef void (* GimpLayerModeBlendFunc) (GeglOperation *operation, + const gfloat *in, const gfloat *layer, gfloat *out, gint samples); diff --git a/app/operations/tests/Makefile.in b/app/operations/tests/Makefile.in index dad2cfd883..240c07aa91 100644 --- a/app/operations/tests/Makefile.in +++ b/app/operations/tests/Makefile.in @@ -105,7 +105,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -159,8 +158,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -290,7 +287,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -429,8 +425,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/paint/Makefile.in b/app/paint/Makefile.in index 74318e099b..132f7bd154 100644 --- a/app/paint/Makefile.in +++ b/app/paint/Makefile.in @@ -103,7 +103,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.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/binreloc.m4 \ $(top_srcdir)/m4macros/detectcflags.m4 \ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -273,8 +272,6 @@ AWK = @AWK@ BABL_CFLAGS = @BABL_CFLAGS@ BABL_LIBS = @BABL_LIBS@ BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@ -BINRELOC_CFLAGS = @BINRELOC_CFLAGS@ -BINRELOC_LIBS = @BINRELOC_LIBS@ BUG_REPORT_URL = @BUG_REPORT_URL@ BUILD_EXEEXT = @BUILD_EXEEXT@ BUILD_OBJEXT = @BUILD_OBJEXT@ @@ -404,7 +401,6 @@ 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@ -GTK_WIN32_RECOMMENDED_VERSION = @GTK_WIN32_RECOMMENDED_VERSION@ GUDEV_CFLAGS = @GUDEV_CFLAGS@ GUDEV_LIBS = @GUDEV_LIBS@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ @@ -543,8 +539,6 @@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ -RECOMMENDED_WIN32_GTK_CFLAGS = @RECOMMENDED_WIN32_GTK_CFLAGS@ -RECOMMENDED_WIN32_GTK_LIBS = @RECOMMENDED_WIN32_GTK_LIBS@ RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@ RT_LIBS = @RT_LIBS@ SCREENSHOT_LIBS = @SCREENSHOT_LIBS@ diff --git a/app/paint/gimpbrushcore-kernels.h b/app/paint/gimpbrushcore-kernels.h index a9ae0768d0..ea5eb4530c 100644 --- a/app/paint/gimpbrushcore-kernels.h +++ b/app/paint/gimpbrushcore-kernels.h @@ -7,50 +7,110 @@ #ifndef __GIMP_BRUSH_CORE_KERNELS_H__ #define __GIMP_BRUSH_CORE_KERNELS_H__ + #define KERNEL_WIDTH 3 #define KERNEL_HEIGHT 3 #define KERNEL_SUBSAMPLE 4 -#define KERNEL_SUM 256 -/* Brush pixel subsampling kernels */ -static const int subsample[5][5][9] = +#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) { - { 64, 64, 0, 64, 64, 0, 0, 0, 0, }, - { 25, 103, 0, 25, 103, 0, 0, 0, 0, }, - { 0, 128, 0, 0, 128, 0, 0, 0, 0, }, - { 0, 103, 25, 0, 103, 25, 0, 0, 0, }, - { 0, 64, 64, 0, 64, 64, 0, 0, 0, } - }, + return x; + } + + static constexpr value_type + round (accum_type x) { - { 25, 25, 0, 103, 103, 0, 0, 0, 0, }, - { 6, 44, 0, 44, 162, 0, 0, 0, 0, }, - { 0, 50, 0, 0, 206, 0, 0, 0, 0, }, - { 0, 44, 6, 0, 162, 44, 0, 0, 0, }, - { 0, 25, 25, 0, 103, 103, 0, 0, 0, } - }, - { - { 0, 0, 0, 128, 128, 0, 0, 0, 0, }, - { 0, 0, 0, 50, 206, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 256, 0, 0, 0, 0, }, - { 0, 0, 0, 0, 206, 50, 0, 0, 0, }, - { 0, 0, 0, 0, 128, 128, 0, 0, 0, } - }, - { - { 0, 0, 0, 103, 103, 0, 25, 25, 0, }, - { 0, 0, 0, 44, 162, 0, 6, 44, 0, }, - { 0, 0, 0, 0, 206, 0, 0, 50, 0, }, - { 0, 0, 0, 0, 162, 44, 0, 44, 6, }, - { 0, 0, 0, 0, 103, 103, 0, 25, 25, } - }, - { - { 0, 0, 0, 64, 64, 0, 64, 64, 0, }, - { 0, 0, 0, 25, 103, 0, 25, 103, 0, }, - { 0, 0, 0, 0, 128, 0, 0, 128, 0, }, - { 0, 0, 0, 0, 103, 25, 0, 103, 25, }, - { 0, 0, 0, 0, 64, 64, 0, 64, 64, } + 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 index 5a26663972..d517377f75 100644 --- a/app/paint/gimpbrushcore-loops.cc +++ b/app/paint/gimpbrushcore-loops.cc @@ -29,129 +29,126 @@ extern "C" #include "paint-types.h" -#include "core/gimp-parallel.h" #include "core/gimptempbuf.h" #include "gimpbrushcore.h" #include "gimpbrushcore-loops.h" + +} /* extern "C" */ + #include "gimpbrushcore-kernels.h" -#define MIN_PARALLEL_SUB_SIZE 64 -#define MIN_PARALLEL_SUB_AREA (MIN_PARALLEL_SUB_SIZE * MIN_PARALLEL_SUB_SIZE) +#define PIXELS_PER_THREAD \ + (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */) + +#define EPSILON 1e-6 -static inline void -rotate_pointers (gulong **p, - guint32 n) +static void +clear_edges (GimpTempBuf *buf, + gint top, + gint bottom, + gint left, + gint right) { - guint32 i; - gulong *tmp; + 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]; + for (i = 0; i < n - 1; i++) + p[i] = p[i + 1]; p[i] = tmp; } -const GimpTempBuf * -gimp_brush_core_subsample_mask (GimpBrushCore *core, - const GimpTempBuf *mask, - gdouble x, - gdouble y) +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) { - GimpTempBuf *dest; - gdouble left; - gint index1; - gint index2; - gint dest_offset_x = 0; - gint dest_offset_y = 0; - const gint *kernel; - gint mask_width = gimp_temp_buf_get_width (mask); - gint mask_height = gimp_temp_buf_get_height (mask); - gint dest_width; - gint dest_height; + using value_type = typename Subsample::value_type; + using kernel_type = typename Subsample::kernel_type; + using accum_type = typename Subsample::accum_type; - left = x - floor (x); - index1 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1)); + 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); - left = y - floor (y); - index2 = (gint) (left * (gdouble) (KERNEL_SUBSAMPLE + 1)); - - - if ((mask_width % 2) == 0) + gegl_parallel_distribute_range ( + mask_height, PIXELS_PER_THREAD / mask_width, + [=] (gint y, gint height) { - 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; - } - } - - kernel = subsample[index2][index1]; - - 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; - } - - dest = gimp_temp_buf_new (mask_width + 2, - mask_height + 2, - gimp_temp_buf_get_format (mask)); - gimp_temp_buf_data_clear (dest); - - dest_width = gimp_temp_buf_get_width (dest); - dest_height = gimp_temp_buf_get_height (dest); - - core->subsample_brushes[index2][index1] = dest; - - gimp_parallel_distribute_range (mask_height, MIN_PARALLEL_SUB_SIZE, - [=] (gint y, gint height) - { - const guchar *m; - guchar *d; - const gint *k; - gint y0; - gint i, j; - gint r, s; - gint offs; - gulong *accum[KERNEL_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] = g_new0 (gulong, dest_width + 1); + accum[i] = gegl_scratch_new0 (accum_type, dest_width + 1); y0 = MAX (y - (KERNEL_HEIGHT - 1), 0); - m = gimp_temp_buf_get_data (mask) + y0 * mask_width; + m = (const value_type *) gimp_temp_buf_get_data (mask) + + y0 * mask_width; for (i = y0; i < y; i++) { @@ -187,13 +184,15 @@ gimp_brush_core_subsample_mask (GimpBrushCore *core, } /* store the accum buffer into the destination mask */ - d = gimp_temp_buf_get_data (dest) + (i + dest_offset_y) * dest_width; + d = (value_type *) gimp_temp_buf_get_data (dest) + + (i + dest_offset_y) * dest_width; for (j = 0; j < dest_width; j++) - *d++ = (accum[0][j] + 127) / KERNEL_SUM; + *d++ = subsample.round (accum[0][j]); rotate_pointers (accum, KERNEL_HEIGHT); - memset (accum[KERNEL_HEIGHT - 1], 0, sizeof (gulong) * dest_width); + memset (accum[KERNEL_HEIGHT - 1], 0, + sizeof (accum_type) * dest_width); } if (y + height == mask_height) @@ -201,71 +200,170 @@ gimp_brush_core_subsample_mask (GimpBrushCore *core, /* store the rest of the accum buffer into the dest mask */ while (i + dest_offset_y < dest_height) { - d = gimp_temp_buf_get_data (dest) + (i + dest_offset_y) * dest_width; + d = (value_type *) gimp_temp_buf_get_data (dest) + + (i + dest_offset_y) * dest_width; for (j = 0; j < dest_width; j++) - *d++ = (accum[0][j] + (KERNEL_SUM / 2)) / KERNEL_SUM; + *d++ = subsample.round (accum[0][j]); rotate_pointers (accum, KERNEL_HEIGHT); i++; } } - for (i = 0; i < KERNEL_HEIGHT ; i++) - g_free (accum[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; } -/* #define FANCY_PRESSURE */ - -const GimpTempBuf * -gimp_brush_core_pressurize_mask (GimpBrushCore *core, - const GimpTempBuf *brush_mask, - gdouble x, - gdouble y, - gdouble pressure) +/* The simple pressure profile + * + * It is: I'(I) = MIN (2 * pressure * I, 1) + */ +class SimplePressure { - static guchar mapi[256]; - const GimpTempBuf *subsample_mask; - gint i; + gfloat scale; - /* Get the raw subsampled mask */ - subsample_mask = gimp_brush_core_subsample_mask (core, - brush_mask, - x, y); - - /* Special case pressure = 0.5 */ - if ((gint) (pressure * 100 + 0.5) == 50) - return subsample_mask; - - g_clear_pointer (&core->pressure_brush, gimp_temp_buf_unref); - - core->pressure_brush = - gimp_temp_buf_new (gimp_temp_buf_get_width (brush_mask) + 2, - gimp_temp_buf_get_height (brush_mask) + 2, - gimp_temp_buf_get_format (brush_mask)); - gimp_temp_buf_data_clear (core->pressure_brush); - -#ifdef FANCY_PRESSURE - - /* Create the 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 - * - * | / -- - * | / / - * / / | - * -- / | - */ +public: + SimplePressure (gdouble pressure) { - gdouble map[256]; - gdouble ds, s, c; + 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; @@ -281,7 +379,7 @@ gimp_brush_core_pressurize_mask (GimpBrushCore *core, } for (i = 0; i < 256; i++) - mapi[i] = (gint) (255 * map[i] / map[255]); + map[i] = map[i] / map[255]; } else { @@ -294,53 +392,184 @@ gimp_brush_core_pressurize_mask (GimpBrushCore *core, c += s * ds; } - for (i = 0; i < 256; i++) - mapi[i] = (gint) (255 * (1 - map[i] / map[0])); + for (i = 255; i >= 0; i--) + map[i] = 1.0f - map[i] / map[0]; } + + map[256] = map[255]; } -#else /* ! FANCY_PRESSURE */ - + guchar + operator () (guchar x) const { - gdouble j, k; - - j = pressure + pressure; - k = 0; - - for (i = 0; i < 256; i++) - { - if (k > 255) - mapi[i] = 255; - else - mapi[i] = (guchar) k; - - k += j; - } + return RINT (255.0f * map[x]); } -#endif /* FANCY_PRESSURE */ + gfloat + operator () (gfloat x) const + { + gint i; + gfloat f; - /* Now convert the brush */ + x *= 255.0f; - gimp_parallel_distribute_range (gimp_temp_buf_get_width (subsample_mask) * - gimp_temp_buf_get_height (subsample_mask), - MIN_PARALLEL_SUB_AREA, - [=] (gint offset, gint size) + 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 < 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 guchar *source; - guchar *dest; - gint i; + const T *m; + T *d; + gint i; - source = gimp_temp_buf_get_data (subsample_mask) + offset; - dest = gimp_temp_buf_get_data (core->pressure_brush) + offset; + 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++) - *dest++ = mapi[(*source++)]; + *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; + gint i; + + /* 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); + gint dest_height = gimp_temp_buf_get_height (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, @@ -348,6 +577,7 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core, 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); @@ -355,8 +585,8 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core, if ((brush_mask_width % 2) == 0) { - while (x < 0) - x += brush_mask_width; + if (x < 0.0) + x = fmod (x, brush_mask_width) + brush_mask_width; if ((x - floor (x)) >= 0.5) dest_offset_x++; @@ -364,8 +594,8 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core, if ((brush_mask_height % 2) == 0) { - while (y < 0) - y += brush_mask_height; + if (y < 0.0) + y = fmod (y, brush_mask_height) + brush_mask_height; if ((y - floor (y)) >= 0.5) dest_offset_y++; @@ -389,41 +619,31 @@ gimp_brush_core_solidify_mask (GimpBrushCore *core, 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")); - gimp_temp_buf_data_clear (dest); + 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; - gimp_parallel_distribute_area (GEGL_RECTANGLE (0, - 0, - brush_mask_width, - brush_mask_height), - MIN_PARALLEL_SUB_AREA, - [=] (const GeglRectangle *area) + if (brush_mask_format == babl_format ("Y u8")) { - const guchar *m; - gfloat *d; - gint i, j; - - m = gimp_temp_buf_get_data (brush_mask) + - area->y * brush_mask_width + area->x; - d = ((gfloat *) gimp_temp_buf_get_data (dest) + - ((dest_offset_y + 1 + area->y) * gimp_temp_buf_get_width (dest) + - (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 += brush_mask_width - area->width; - d += brush_mask_width + 2 - area->width; - } - }); + 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; } - -} /* extern "C" */ diff --git a/app/paint/gimpbrushcore.c b/app/paint/gimpbrushcore.c index ad1721d386..da4f26b9bd 100644 --- a/app/paint/gimpbrushcore.c +++ b/app/paint/gimpbrushcore.c @@ -20,7 +20,6 @@ #include #include -#define GEGL_ITERATOR2_API #include #include "libgimpmath/gimpmath.h" @@ -30,6 +29,7 @@ #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" @@ -39,6 +39,7 @@ #include "core/gimperror.h" #include "core/gimpimage.h" #include "core/gimpmarshal.h" +#include "core/gimpsymmetry.h" #include "core/gimptempbuf.h" #include "gimpbrushcore.h" @@ -99,27 +100,16 @@ static void gimp_brush_core_real_set_brush (GimpBrushCore *core, 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, - GeglNode *op); -static const GimpTempBuf * - gimp_brush_core_transform_pixmap (GimpBrushCore *core, - GimpBrush *brush, - GeglNode *op); + GimpBrush *brush); static void gimp_brush_core_invalidate_cache (GimpBrush *brush, GimpBrushCore *core); -/* brush pipe utility functions */ -static void gimp_brush_core_paint_line_pixmap_mask (const Babl *fish, - const GimpTempBuf *pixmap_mask, - const GimpTempBuf *brush_mask, - gfloat *d, - gint x, - gint y, - gint width); - G_DEFINE_TYPE (GimpBrushCore, gimp_brush_core, GIMP_TYPE_PAINT_CORE) @@ -180,10 +170,14 @@ gimp_brush_core_init (GimpBrushCore *core) core->dynamics = NULL; core->spacing = 1.0; core->scale = 1.0; - core->angle = 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; @@ -393,6 +387,8 @@ gimp_brush_core_start (GimpPaintCore *paint_core, drawable, paint_options, coords); + + gimp_brush_core_eval_transform_symmetry (core, NULL, 0); } core->spacing = paint_options->brush_spacing; @@ -806,7 +802,8 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core, gimp_brush_transform_size (core->brush, core->scale, core->aspect_ratio, - core->angle, core->reflect, + gimp_brush_core_get_angle (core), + gimp_brush_core_get_reflect (core), &brush_width, &brush_height); if (paint_width) @@ -829,12 +826,16 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core, /* configure the canvas buffer */ if ((x2 - x1) && (y2 - y1)) { - GimpTempBuf *temp_buf; - const Babl *format; + 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 && @@ -848,14 +849,14 @@ gimp_brush_core_get_paint_buffer (GimpPaintCore *paint_core, 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; - g_clear_object (&paint_core->paint_buffer); - paint_core->paint_buffer = gimp_temp_buf_create_buffer (temp_buf); gimp_temp_buf_unref (temp_buf); @@ -930,12 +931,11 @@ gimp_brush_core_paste_canvas (GimpBrushCore *core, GimpLayerMode paint_mode, GimpBrushApplicationMode brush_hardness, gdouble dynamic_force, - GimpPaintApplicationMode mode, - GeglNode *op) + GimpPaintApplicationMode mode) { const GimpTempBuf *brush_mask; - brush_mask = gimp_brush_core_get_brush_mask (core, coords, op, + brush_mask = gimp_brush_core_get_brush_mask (core, coords, brush_hardness, dynamic_force); @@ -975,12 +975,11 @@ gimp_brush_core_replace_canvas (GimpBrushCore *core, gdouble image_opacity, GimpBrushApplicationMode brush_hardness, gdouble dynamic_force, - GimpPaintApplicationMode mode, - GeglNode *op) + GimpPaintApplicationMode mode) { const GimpTempBuf *brush_mask; - brush_mask = gimp_brush_core_get_brush_mask (core, coords, op, + brush_mask = gimp_brush_core_get_brush_mask (core, coords, brush_hardness, dynamic_force); @@ -1027,10 +1026,33 @@ gimp_brush_core_invalidate_cache (GimpBrush *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, - GeglNode *op) + GimpBrush *brush) { const GimpTempBuf *mask; @@ -1038,11 +1060,10 @@ gimp_brush_core_transform_mask (GimpBrushCore *core, return NULL; mask = gimp_brush_transform_mask (brush, - op, core->scale, core->aspect_ratio, - core->angle, - core->reflect, + gimp_brush_core_get_angle (core), + gimp_brush_core_get_reflect (core), core->hardness); if (mask == core->transform_brush) @@ -1055,37 +1076,9 @@ gimp_brush_core_transform_mask (GimpBrushCore *core, return core->transform_brush; } -static const GimpTempBuf * -gimp_brush_core_transform_pixmap (GimpBrushCore *core, - GimpBrush *brush, - GeglNode *op) -{ - const GimpTempBuf *pixmap; - - if (core->scale <= 0.0) - return NULL; - - pixmap = gimp_brush_transform_pixmap (brush, - op, - core->scale, - core->aspect_ratio, - core->angle, - core->reflect, - core->hardness); - - if (pixmap == core->transform_pixmap) - return pixmap; - - core->transform_pixmap = pixmap; - core->subsample_cache_invalid = TRUE; - - return core->transform_pixmap; -} - const GimpTempBuf * gimp_brush_core_get_brush_mask (GimpBrushCore *core, const GimpCoords *coords, - GeglNode *op, GimpBrushApplicationMode brush_hardness, gdouble dynamic_force) { @@ -1094,7 +1087,7 @@ gimp_brush_core_get_brush_mask (GimpBrushCore *core, if (dynamic_force <= 0.0) return NULL; - mask = gimp_brush_core_transform_mask (core, core->brush, op); + mask = gimp_brush_core_transform_mask (core, core->brush); if (! mask) return NULL; @@ -1124,6 +1117,30 @@ gimp_brush_core_get_brush_mask (GimpBrushCore *core, 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, @@ -1222,196 +1239,106 @@ gimp_brush_core_eval_transform_dynamics (GimpBrushCore *core, } } +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)); -/**************************************************/ -/* Brush pipe utility functions */ -/**************************************************/ + 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, - GeglNode *op, - GeglBuffer *area, - gint area_x, - gint area_y, - GimpBrushApplicationMode mode) +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) { - GeglBufferIterator *iter; - GeglRectangle *roi; - gint ulx; - gint uly; - gint offsetx; - gint offsety; - const GimpTempBuf *pixmap_mask; - const GimpTempBuf *brush_mask; - const Babl *area_format; - const Babl *pixmap_format; - const Babl *fish = NULL; + 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 brushes */ - pixmap_mask = gimp_brush_core_transform_pixmap (core, core->brush, op); + /* scale the brush */ + pixmap = gimp_brush_core_get_brush_pixmap (core); - if (! pixmap_mask) + if (! pixmap) return; - if (mode != GIMP_BRUSH_HARD) - brush_mask = gimp_brush_core_transform_mask (core, core->brush, op); + if (apply_mask) + mask = gimp_brush_core_transform_mask (core, core->brush); else - brush_mask = NULL; + mask = NULL; /* Calculate upper left corner of brush as in * gimp_paint_core_get_paint_area. Ugly to have to do this here, too. */ - ulx = (gint) floor (coords->x) - (gimp_temp_buf_get_width (pixmap_mask) >> 1); - uly = (gint) floor (coords->y) - (gimp_temp_buf_get_height (pixmap_mask) >> 1); + 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_mask) % 2 == 0) - ulx += ROUND (coords->x) - floor (coords->x); - if (gimp_temp_buf_get_height (pixmap_mask) % 2 == 0) - uly += ROUND (coords->y) - floor (coords->y); + 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); - offsetx = area_x - ulx; - offsety = area_y - uly; + offset_x = area_x - ul_x; + offset_y = area_y - ul_y; - area_format = gegl_buffer_get_format (area); - pixmap_format = gimp_temp_buf_get_format (pixmap_mask); + area_width = gegl_buffer_get_width (area); + area_height = gegl_buffer_get_height (area); - iter = gegl_buffer_iterator_new (area, NULL, 0, area_format, - GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1); + pixmap_buffer = gimp_temp_buf_create_buffer (pixmap); - if (mode == GIMP_BRUSH_SOFT && brush_mask) + 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) { - GimpImageBaseType pixmap_base_type; - GimpPrecision pixmap_precision; + mask_buffer = gimp_temp_buf_create_buffer (mask); - pixmap_base_type = gimp_babl_format_get_base_type (pixmap_format); - pixmap_precision = gimp_babl_format_get_precision (pixmap_format); + 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); - fish = babl_fish (gimp_babl_format (pixmap_base_type, pixmap_precision, - TRUE), - area_format); - } - else - { - fish = babl_fish (pixmap_format, area_format); - - brush_mask = NULL; - } - - roi = &iter->items[0].roi; - - while (gegl_buffer_iterator_next (iter)) - { - gfloat *d = iter->items[0].data; - gint y; - - for (y = 0; y < roi->height; y++) - { - gimp_brush_core_paint_line_pixmap_mask (fish, - pixmap_mask, brush_mask, - d, offsetx, y + offsety, - roi->width); - d += roi->width * 4; - } - } -} - -static void -gimp_brush_core_paint_line_pixmap_mask (const Babl *fish, - const GimpTempBuf *pixmap_mask, - const GimpTempBuf *brush_mask, - gfloat *d, - gint x, - gint y, - gint width) -{ - const Babl *pixmap_format; - gint pixmap_bytes; - gint pixmap_width; - gint pixmap_height; - guchar *b; - - pixmap_width = gimp_temp_buf_get_width (pixmap_mask); - pixmap_height = gimp_temp_buf_get_height (pixmap_mask); - - /* Make sure x, y are positive */ - x %= pixmap_width; - if (x < 0) - x += pixmap_width; - - y %= pixmap_height; - if (y < 0) - y += pixmap_height; - - pixmap_format = gimp_temp_buf_get_format (pixmap_mask); - pixmap_bytes = babl_format_get_bytes_per_pixel (pixmap_format); - - /* Point to the appropriate scanline */ - b = (gimp_temp_buf_get_data (pixmap_mask) + - y * pixmap_width * pixmap_bytes); - - if (brush_mask) - { - const guchar *mask = (gimp_temp_buf_get_data (brush_mask) + - y * pixmap_width); - guchar *line_buf = g_alloca (width * (pixmap_bytes + 1)); - guchar *l = line_buf; - gint i; - - g_return_if_fail (gimp_temp_buf_get_width (brush_mask) == pixmap_width); - g_return_if_fail (gimp_temp_buf_get_height (brush_mask) == pixmap_height); - - /* put the source pixmap's pixels, plus the mask's alpha, into - * one line, so we can use one single call to babl_process() to - * convert the entire line - */ - - for (i = 0; i < width; i++) - { - gint p_bytes = pixmap_bytes; - guchar *p = b + x * p_bytes; - - while (p_bytes--) - *l++ = *p++; - - *l++ = mask[x++]; - - if (x == pixmap_width) - x = 0; - } - - babl_process (fish, line_buf, d, width); - } - else - { - guchar *line_buf = g_alloca (width * (pixmap_bytes)); - guchar *l = line_buf; - gint i; - - /* put the source pixmap's pixels into one line, so we can use - * one single call to babl_process() to convert the entire line - */ - for (i = 0; i < width; i++) - { - gint p_bytes = pixmap_bytes; - guchar *p = b + x * p_bytes; - - while (p_bytes--) - *l++ = *p++; - - x++; - - if (x == pixmap_width) - x = 0; - } - - babl_process (fish, line_buf, d, width); + g_object_unref (mask_buffer); } } diff --git a/app/paint/gimpbrushcore.h b/app/paint/gimpbrushcore.h index b733f0f50f..3ba4a41984 100644 --- a/app/paint/gimpbrushcore.h +++ b/app/paint/gimpbrushcore.h @@ -51,6 +51,9 @@ struct _GimpBrushCore gboolean reflect; gdouble hardness; + gdouble symmetry_angle; + gboolean symmetry_reflect; + /* brush buffers */ GimpTempBuf *pressure_brush; @@ -108,8 +111,7 @@ void gimp_brush_core_paste_canvas (GimpBrushCore *core, GimpLayerMode paint_mode, GimpBrushApplicationMode brush_hardness, gdouble dynamic_hardness, - GimpPaintApplicationMode mode, - GeglNode *op); + GimpPaintApplicationMode mode); void gimp_brush_core_replace_canvas (GimpBrushCore *core, GimpDrawable *drawable, const GimpCoords *coords, @@ -117,31 +119,34 @@ void gimp_brush_core_replace_canvas (GimpBrushCore *core, gdouble image_opacity, GimpBrushApplicationMode brush_hardness, gdouble dynamic_hardness, - GimpPaintApplicationMode mode, - GeglNode *op); + GimpPaintApplicationMode mode); void gimp_brush_core_color_area_with_pixmap (GimpBrushCore *core, GimpDrawable *drawable, const GimpCoords *coords, - GeglNode *op, GeglBuffer *area, gint area_x, gint area_y, - GimpBrushApplicationMode mode); + gboolean apply_mask); const GimpTempBuf * gimp_brush_core_get_brush_mask (GimpBrushCore *core, const GimpCoords *coords, - GeglNode *op, GimpBrushApplicationMode brush_hardness, gdouble dynamic_hardness); +const GimpTempBuf * gimp_brush_core_get_brush_pixmap + (GimpBrushCore *core); void gimp_brush_core_eval_transform_dynamics - (GimpBrushCore *paint_core, + (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 index bafb1845a1..9c807f43ff 100644 --- a/app/paint/gimpclone.c +++ b/app/paint/gimpclone.c @@ -27,6 +27,7 @@ #include "paint-types.h" +#include "gegl/gimp-gegl-apply-operation.h" #include "gegl/gimp-gegl-loops.h" #include "core/gimp.h" @@ -168,35 +169,28 @@ gimp_clone_motion (GimpSourceCore *source_core, if (gimp_source_core_use_source (source_core, source_options)) { - 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)); - if (op) + if (! op) { - GeglNode *graph, *source, *target; - - graph = gegl_node_new (); - source = gegl_node_new_child (graph, - "operation", "gegl:buffer-source", - "buffer", paint_buffer, - NULL); - gegl_node_add_child (graph, op); - target = gegl_node_new_child (graph, - "operation", "gegl:write-buffer", - "buffer", paint_buffer, - NULL); - - gegl_node_link_many (source, op, target, NULL); - gegl_node_process (target); - - g_object_unref (graph); + 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) @@ -251,8 +245,7 @@ gimp_clone_motion (GimpSourceCore *source_core, */ source_options->align_mode == GIMP_SOURCE_ALIGN_FIXED ? - GIMP_PAINT_INCREMENTAL : GIMP_PAINT_CONSTANT, - NULL); + GIMP_PAINT_INCREMENTAL : GIMP_PAINT_CONSTANT); } static gboolean diff --git a/app/paint/gimpconvolve.c b/app/paint/gimpconvolve.c index 1d1b8a80c1..f2eea47cb6 100644 --- a/app/paint/gimpconvolve.c +++ b/app/paint/gimpconvolve.c @@ -139,7 +139,6 @@ gimp_convolve_motion (GimpPaintCore *paint_core, gdouble opacity; gdouble rate; const GimpCoords *coords; - GeglNode *op; gint paint_width, paint_height; gint n_strokes; gint i; @@ -165,6 +164,8 @@ gimp_convolve_motion (GimpPaintCore *paint_core, { 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, @@ -176,10 +177,6 @@ gimp_convolve_motion (GimpPaintCore *paint_core, if (! paint_buffer) continue; - op = gimp_symmetry_get_operation (sym, i, - paint_width, - paint_height); - rate = (options->rate * gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_RATE, @@ -228,7 +225,7 @@ gimp_convolve_motion (GimpPaintCore *paint_core, gimp_context_get_opacity (context), gimp_paint_options_get_brush_mode (paint_options), 1.0, - GIMP_PAINT_INCREMENTAL, op); + GIMP_PAINT_INCREMENTAL); } } diff --git a/app/paint/gimpdodgeburn.c b/app/paint/gimpdodgeburn.c index 3376ceb9d7..cdfc176443 100644 --- a/app/paint/gimpdodgeburn.c +++ b/app/paint/gimpdodgeburn.c @@ -112,10 +112,12 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core, GimpPaintOptions *paint_options, GimpSymmetry *sym) { - 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)); + 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; @@ -123,7 +125,6 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core, gdouble opacity; gdouble force; const GimpCoords *coords; - GeglNode *op; gint paint_width, paint_height; gint n_strokes; gint i; @@ -140,7 +141,12 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core, if (opacity == 0.0) return; - gimp_brush_core_eval_transform_dynamics (GIMP_BRUSH_CORE (paint_core), + 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); @@ -149,6 +155,8 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core, { 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, @@ -160,12 +168,8 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core, if (! paint_buffer) continue; - op = gimp_symmetry_get_operation (sym, i, - paint_width, - paint_height); - /* DodgeBurn the region */ - gimp_gegl_dodgeburn (gimp_paint_core_get_orig_image (paint_core), + gimp_gegl_dodgeburn (src_buffer, GEGL_RECTANGLE (paint_buffer_x, paint_buffer_y, gegl_buffer_get_width (paint_buffer), @@ -192,6 +196,6 @@ gimp_dodge_burn_motion (GimpPaintCore *paint_core, gimp_context_get_opacity (context), gimp_paint_options_get_brush_mode (paint_options), force, - GIMP_PAINT_CONSTANT, op); + paint_options->application_mode); } } diff --git a/app/paint/gimperaser.c b/app/paint/gimperaser.c index 1ebb36ec4c..a8d72d1dae 100644 --- a/app/paint/gimperaser.c +++ b/app/paint/gimperaser.c @@ -38,19 +38,21 @@ #include "gimp-intl.h" -static void gimp_eraser_paint (GimpPaintCore *paint_core, - GimpDrawable *drawable, - GimpPaintOptions *paint_options, - GimpSymmetry *sym, - GimpPaintState paint_state, - guint32 time); -static void gimp_eraser_motion (GimpPaintCore *paint_core, - GimpDrawable *drawable, - GimpPaintOptions *paint_options, - GimpSymmetry *sym); +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, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color); -G_DEFINE_TYPE (GimpEraser, gimp_eraser, GIMP_TYPE_BRUSH_CORE) +G_DEFINE_TYPE (GimpEraser, gimp_eraser, GIMP_TYPE_PAINTBRUSH) void @@ -68,12 +70,10 @@ gimp_eraser_register (Gimp *gimp, static void gimp_eraser_class_init (GimpEraserClass *klass) { - GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass); - GimpBrushCoreClass *brush_core_class = GIMP_BRUSH_CORE_CLASS (klass); + GimpPaintbrushClass *paintbrush_class = GIMP_PAINTBRUSH_CLASS (klass); - paint_core_class->paint = gimp_eraser_paint; - - brush_core_class->handles_changing_brush = TRUE; + paintbrush_class->get_color_history_color = gimp_eraser_get_color_history_color; + paintbrush_class->get_paint_params = gimp_eraser_get_paint_params; } static void @@ -81,137 +81,48 @@ gimp_eraser_init (GimpEraser *eraser) { } -static void -gimp_eraser_paint (GimpPaintCore *paint_core, - GimpDrawable *drawable, - GimpPaintOptions *paint_options, - GimpSymmetry *sym, - GimpPaintState paint_state, - guint32 time) +static gboolean +gimp_eraser_get_color_history_color (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpRGB *color) { - switch (paint_state) + /* Erasing on a drawable without alpha is equivalent to + * drawing with background color. So let's save history. + */ + if (! gimp_drawable_has_alpha (drawable)) { - case GIMP_PAINT_STATE_INIT: - { - if (! gimp_drawable_has_alpha (drawable)) - { - /* Erasing on a drawable without alpha is equivalent to - * drawing with background color. So let's save history. - */ - GimpContext *context = GIMP_CONTEXT (paint_options); - GimpRGB background; + GimpContext *context = GIMP_CONTEXT (paint_options); - gimp_context_get_background (context, &background); - gimp_palettes_add_color_history (context->gimp, - &background); + gimp_context_get_background (context, color); - } - } - break; - case GIMP_PAINT_STATE_MOTION: - gimp_eraser_motion (paint_core, drawable, paint_options, sym); - break; - - default: - break; + return TRUE; } + + return FALSE; } static void -gimp_eraser_motion (GimpPaintCore *paint_core, - GimpDrawable *drawable, - GimpPaintOptions *paint_options, - GimpSymmetry *sym) +gimp_eraser_get_paint_params (GimpPaintbrush *paintbrush, + GimpDrawable *drawable, + GimpPaintOptions *paint_options, + GimpSymmetry *sym, + 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); - GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; - GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); - gdouble fade_point; - gdouble opacity; - GimpLayerMode paint_mode; - GeglBuffer *paint_buffer; - gint paint_buffer_x; - gint paint_buffer_y; - GimpRGB background; - GeglColor *color; - gdouble force; - const GimpCoords *coords; - GeglNode *op; - gint n_strokes; - gint paint_width, paint_height; - gint i; + GimpEraserOptions *options = GIMP_ERASER_OPTIONS (paint_options); + GimpContext *context = GIMP_CONTEXT (paint_options); - 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_context_get_background (context, &background); + gimp_context_get_background (context, paint_color); gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), - &background, &background); - color = gimp_gegl_color_new (&background); + paint_color, paint_color); if (options->anti_erase) - paint_mode = GIMP_LAYER_MODE_ANTI_ERASE; + *paint_mode = GIMP_LAYER_MODE_ANTI_ERASE; else if (gimp_drawable_has_alpha (drawable)) - paint_mode = GIMP_LAYER_MODE_ERASE; + *paint_mode = GIMP_LAYER_MODE_ERASE; else - paint_mode = GIMP_LAYER_MODE_NORMAL_LEGACY; - - 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); - - 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; - - - 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; - - op = gimp_symmetry_get_operation (sym, i, - paint_width, - paint_height); - - gegl_buffer_set_color (paint_buffer, NULL, color); - - gimp_brush_core_paste_canvas (GIMP_BRUSH_CORE (paint_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_options->application_mode, op); - } - - g_object_unref (color); + *paint_mode = GIMP_LAYER_MODE_NORMAL_LEGACY; } diff --git a/app/paint/gimperaser.h b/app/paint/gimperaser.h index 748cf253e4..5b04a3292f 100644 --- a/app/paint/gimperaser.h +++ b/app/paint/gimperaser.h @@ -19,7 +19,7 @@ #define __GIMP_ERASER_H__ -#include "gimpbrushcore.h" +#include "gimppaintbrush.h" #define GIMP_TYPE_ERASER (gimp_eraser_get_type ()) @@ -34,12 +34,12 @@ typedef struct _GimpEraserClass GimpEraserClass; struct _GimpEraser { - GimpBrushCore parent_instance; + GimpPaintbrush parent_instance; }; struct _GimpEraserClass { - GimpBrushCoreClass parent_class; + GimpPaintbrushClass parent_class; }; diff --git a/app/paint/gimpheal.c b/app/paint/gimpheal.c index 140baf0504..8f7451a3b2 100644 --- a/app/paint/gimpheal.c +++ b/app/paint/gimpheal.c @@ -25,7 +25,6 @@ #include #include -#define GEGL_ITERATOR2_API #include #include "libgimpbase/gimpbase.h" @@ -33,6 +32,7 @@ #include "paint-types.h" +#include "gegl/gimp-gegl-apply-operation.h" #include "gegl/gimp-gegl-loops.h" #include "core/gimpbrush.h" @@ -512,17 +512,21 @@ gimp_heal_motion (GimpSourceCore *source_core, gint paint_area_width, gint paint_area_height) { - GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); - GimpContext *context = GIMP_CONTEXT (paint_options); - GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; - GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + 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); @@ -537,7 +541,7 @@ gimp_heal_motion (GimpSourceCore *source_core, force = paint_options->brush_force; mask_buf = gimp_brush_core_get_brush_mask (GIMP_BRUSH_CORE (source_core), - coords, op, + coords, GIMP_BRUSH_HARD, force); @@ -556,19 +560,43 @@ gimp_heal_motion (GimpSourceCore *source_core, } /* heal should work in perceptual space, use R'G'B' instead of RGB */ - src_copy = gegl_buffer_new (GEGL_RECTANGLE (0, 0, + 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")); - gimp_gegl_buffer_copy (src_buffer, src_rect, GEGL_ABYSS_NONE, - src_copy, - GEGL_RECTANGLE (0, 0, - src_rect->width, - src_rect->height)); + 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); + } - gimp_gegl_buffer_copy (gimp_drawable_get_buffer (drawable), - GEGL_RECTANGLE (paint_buffer_x, paint_buffer_y, + 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, @@ -589,31 +617,7 @@ gimp_heal_motion (GimpSourceCore *source_core, mask_off_y = (y < 0) ? -y : 0; } - if (op) - { - GeglNode *graph, *source, *target; - - graph = gegl_node_new (); - source = gegl_node_new_child (graph, - "operation", "gegl:buffer-source", - "buffer", src_copy, - NULL); - gegl_node_add_child (graph, op); - target = gegl_node_new_child (graph, - "operation", "gegl:write-buffer", - "buffer", src_copy, - NULL); - - gegl_node_link_many (source, op, target, NULL); - gegl_node_process (target); - - g_object_unref (graph); - } - - gimp_heal (src_copy, - GEGL_RECTANGLE (0, 0, - gegl_buffer_get_width (src_copy), - gegl_buffer_get_height (src_copy)), + gimp_heal (src_copy, gegl_buffer_get_extent (src_copy), paint_buffer, GEGL_RECTANGLE (paint_area_offset_x, paint_area_offset_y, @@ -634,5 +638,5 @@ gimp_heal_motion (GimpSourceCore *source_core, gimp_context_get_opacity (context), gimp_paint_options_get_brush_mode (paint_options), force, - GIMP_PAINT_INCREMENTAL, NULL); + GIMP_PAINT_INCREMENTAL); } diff --git a/app/paint/gimpink.c b/app/paint/gimpink.c index dfeed67b30..50f72b83b3 100644 --- a/app/paint/gimpink.c +++ b/app/paint/gimpink.c @@ -20,7 +20,6 @@ #include #include -#define GEGL_ITERATOR2_API #include #include "libgimpmath/gimpmath.h" @@ -52,44 +51,45 @@ /* local function prototypes */ -static void gimp_ink_finalize (GObject *object); +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_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 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); +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); +static void render_blob (GeglBuffer *buffer, + GeglRectangle *rect, + GimpBlob *blob); G_DEFINE_TYPE (GimpInk, gimp_ink, GIMP_TYPE_PAINT_CORE) @@ -259,12 +259,16 @@ gimp_ink_get_paint_buffer (GimpPaintCore *paint_core, /* configure the canvas buffer */ if ((x2 - x1) && (y2 - y1)) { - GimpTempBuf *temp_buf; - const Babl *format; + 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), @@ -340,15 +344,20 @@ gimp_ink_motion (GimpPaintCore *paint_core, 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); + 100, + &transform); ink->last_blobs = g_list_prepend (ink->last_blobs, last_blob); @@ -364,17 +373,22 @@ gimp_ink_motion (GimpPaintCore *paint_core, { for (i = 0; i < n_strokes; i++) { - GimpBlob *blob; - GimpBlob *blob_union = NULL; + 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); + coords->velocity * 100, + &transform); last_blob = g_list_nth_data (ink->last_blobs, i); blob_union = gimp_blob_convex_union (last_blob, blob); @@ -443,13 +457,14 @@ gimp_ink_motion (GimpPaintCore *paint_core, } static GimpBlob * -ink_pen_ellipse (GimpInkOptions *options, - gdouble x_center, - gdouble y_center, - gdouble pressure, - gdouble xtilt, - gdouble ytilt, - gdouble velocity) +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; @@ -525,10 +540,14 @@ ink_pen_ellipse (GimpInkOptions *options, } else { - tsin = sin (options->blob_angle); 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); diff --git a/app/paint/gimpmybrushsurface.c b/app/paint/gimpmybrushsurface.c index dece18ae1d..9b4283cb61 100644 --- a/app/paint/gimpmybrushsurface.c +++ b/app/paint/gimpmybrushsurface.c @@ -16,7 +16,6 @@ */ #include "config.h" -#define GEGL_ITERATOR2_API #include #include diff --git a/app/paint/gimppaintbrush.c b/app/paint/gimppaintbrush.c index 0766058380..a4bbf387b4 100644 --- a/app/paint/gimppaintbrush.c +++ b/app/paint/gimppaintbrush.c @@ -46,12 +46,25 @@ #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 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, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color); G_DEFINE_TYPE (GimpPaintbrush, gimp_paintbrush, GIMP_TYPE_BRUSH_CORE) @@ -78,6 +91,9 @@ gimp_paintbrush_class_init (GimpPaintbrushClass *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 @@ -93,24 +109,21 @@ gimp_paintbrush_paint (GimpPaintCore *paint_core, GimpPaintState paint_state, guint32 time) { + GimpPaintbrush *paintbrush = GIMP_PAINTBRUSH (paint_core); + switch (paint_state) { case GIMP_PAINT_STATE_INIT: { - GimpContext *context = GIMP_CONTEXT (paint_options); - GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core); - GimpDynamics *dynamics = gimp_context_get_dynamics (context); + GimpRGB color; - if (! gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_COLOR) && - (! brush_core->brush || ! gimp_brush_get_pixmap (brush_core->brush))) + if (GIMP_PAINTBRUSH_GET_CLASS (paintbrush)->get_color_history_color && + GIMP_PAINTBRUSH_GET_CLASS (paintbrush)->get_color_history_color ( + paintbrush, drawable, paint_options, &color)) { - /* We don't save gradient color history and pixmap brushes - * have no color to save. - */ - GimpRGB foreground; + GimpContext *context = GIMP_CONTEXT (paint_options); - gimp_context_get_foreground (context, &foreground); - gimp_palettes_add_color_history (context->gimp, &foreground); + gimp_palettes_add_color_history (context->gimp, &color); } } break; @@ -120,11 +133,107 @@ gimp_paintbrush_paint (GimpPaintCore *paint_core, sym, GIMP_OPACITY_OPAQUE); break; - default: + 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, + 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); + GimpDynamics *dynamics = brush_core->dynamics; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + const GimpCoords *coords = gimp_symmetry_get_origin (sym); + gdouble fade_point; + gdouble grad_point; + + fade_point = gimp_paint_options_get_fade (paint_options, image, + paint_core->pixel_dist); + + grad_point = gimp_dynamics_get_linear_value (dynamics, + GIMP_DYNAMICS_OUTPUT_COLOR, + coords, + paint_options, + fade_point); + + *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, @@ -132,25 +241,16 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core, GimpSymmetry *sym, gdouble opacity) { - GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core); - GimpContext *context = GIMP_CONTEXT (paint_options); - GimpDynamics *dynamics = brush_core->dynamics; - GimpImage *image; - GimpLayerMode paint_mode; - GimpRGB gradient_color; - GeglBuffer *paint_buffer; - gint paint_buffer_x; - gint paint_buffer_y; - GimpPaintApplicationMode paint_appl_mode; - gdouble fade_point; - gdouble grad_point; - gdouble force; - const GimpCoords *coords; - GeglNode *op; - gint n_strokes; - gint i; - - image = gimp_item_get_image (GIMP_ITEM (drawable)); + 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 force; + const GimpCoords *coords; + gint n_strokes; + gint i; fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); @@ -165,15 +265,6 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core, if (opacity == 0.0) return; - paint_appl_mode = paint_options->application_mode; - - grad_point = gimp_dynamics_get_linear_value (dynamics, - GIMP_DYNAMICS_OUTPUT_COLOR, - coords, - paint_options, - fade_point); - - if (GIMP_BRUSH_CORE_GET_CLASS (brush_core)->handles_transforming_brush) { gimp_brush_core_eval_transform_dynamics (brush_core, @@ -182,15 +273,34 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core, coords); } - paint_mode = gimp_context_get_paint_mode (context); - n_strokes = gimp_symmetry_get_size (sym); for (i = 0; i < n_strokes; i++) { - gint paint_width, paint_height; + 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, + &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, @@ -202,56 +312,68 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core, if (! paint_buffer) continue; - op = gimp_symmetry_get_operation (sym, i, - paint_width, - paint_height); - if (gimp_paint_options_get_gradient_color (paint_options, image, - grad_point, - paint_core->pixel_dist, - &gradient_color)) + if (! paint_pixmap) { - /* optionally take the color from the current gradient */ - - GeglColor *color; - - opacity *= gradient_color.a; - gimp_rgb_set_alpha (&gradient_color, GIMP_OPACITY_OPAQUE); - - color = gimp_gegl_color_new (&gradient_color); - - gegl_buffer_set_color (paint_buffer, NULL, color); - g_object_unref (color); - - paint_appl_mode = GIMP_PAINT_INCREMENTAL; + opacity *= paint_color.a; + gimp_rgb_set_alpha (&paint_color, GIMP_OPACITY_OPAQUE); } - else if (brush_core->brush && gimp_brush_get_pixmap (brush_core->brush)) + + /* 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)))) { - /* otherwise check if the brush has a pixmap and use that to - * color the area - */ - gimp_brush_core_color_area_with_pixmap (brush_core, drawable, - coords, op, - paint_buffer, - paint_buffer_x, - paint_buffer_y, - gimp_paint_options_get_brush_mode (paint_options)); + if (paint_buffer != paintbrush->paint_buffer) + { + if (paintbrush->paint_buffer) + { + g_object_remove_weak_pointer ( + G_OBJECT (paintbrush->paint_buffer), + (gpointer) &paintbrush->paint_buffer); + } - paint_appl_mode = GIMP_PAINT_INCREMENTAL; - } - else - { - /* otherwise fill the area with the foreground color */ + paintbrush->paint_buffer = paint_buffer; - GimpRGB foreground; - GeglColor *color; + g_object_add_weak_pointer ( + G_OBJECT (paintbrush->paint_buffer), + (gpointer) &paintbrush->paint_buffer); + } - gimp_context_get_foreground (context, &foreground); - gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), - &foreground, &foreground); - color = gimp_gegl_color_new (&foreground); + if (paint_pixmap != paintbrush->paint_pixmap) + { + g_clear_pointer (&paintbrush->paint_pixmap, gimp_temp_buf_unref); - gegl_buffer_set_color (paint_buffer, NULL, color); - g_object_unref (color); + 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)) @@ -271,6 +393,6 @@ _gimp_paintbrush_motion (GimpPaintCore *paint_core, paint_mode, gimp_paint_options_get_brush_mode (paint_options), force, - paint_appl_mode, op); + paint_appl_mode); } } diff --git a/app/paint/gimppaintbrush.h b/app/paint/gimppaintbrush.h index 2a3097dabb..75ca7adf48 100644 --- a/app/paint/gimppaintbrush.h +++ b/app/paint/gimppaintbrush.h @@ -34,12 +34,30 @@ typedef struct _GimpPaintbrushClass GimpPaintbrushClass; struct _GimpPaintbrush { - GimpBrushCore parent_instance; + 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, + GimpLayerMode *paint_mode, + GimpPaintApplicationMode *paint_appl_mode, + const GimpTempBuf **paint_pixmap, + GimpRGB *paint_color); }; diff --git a/app/paint/gimppaintcore-loops.cc b/app/paint/gimppaintcore-loops.cc index 78089c55e8..2180c547be 100644 --- a/app/paint/gimppaintcore-loops.cc +++ b/app/paint/gimppaintcore-loops.cc @@ -16,7 +16,6 @@ */ #include "config.h" -#define GEGL_ITERATOR2_API #include #include @@ -27,9 +26,10 @@ extern "C" #include "operations/layer-modes/gimp-layer-modes.h" -#include "core/gimp-parallel.h" #include "core/gimptempbuf.h" +#include "operations/gimpoperationmaskcomponents.h" + #include "operations/layer-modes/gimpoperationlayermode.h" #include "gimppaintcore-loops.h" @@ -37,8 +37,8 @@ extern "C" } /* extern "C" */ -#define MIN_PARALLEL_SUB_SIZE 64 -#define MIN_PARALLEL_SUB_AREA (MIN_PARALLEL_SUB_SIZE * MIN_PARALLEL_SUB_SIZE) +#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 @@ -97,9 +97,15 @@ extern "C" enum { - ALGORITHM_PAINT_BUF = 1u << 31, - ALGORITHM_PAINT_MASK = 1u << 30, - ALGORITHM_STIPPLE = 1u << 29 + 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 }; @@ -232,18 +238,13 @@ struct AlgorithmBase * Algorithms that redefine 'filter' should bitwise-OR their filter with that * of their base class. */ - static constexpr guint filter = 0; + static constexpr guint filter = 0; - /* See CanvasBufferIterator. */ - static constexpr gint canvas_buffer_iterator = -1; - static constexpr GeglAccessMode canvas_buffer_access = {}; - - /* The current number of iterators used by the hierarchy. Algorithms should - * use the 'n_iterators' value of their base class as the base-index for - * their iterators, and redefine 'n_iterators' by adding the number of + /* 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 n_iterators = 0; + static constexpr gint max_n_iterators = 0; /* Non-static data members should be initialized in the constructor, and * should not be further modified. @@ -334,6 +335,41 @@ struct AlgorithmBase 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 + { + } }; @@ -342,16 +378,46 @@ struct AlgorithmBase * 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), and 'Mask' is the dispatch function mask, as described in - * 'dispatch()'. + * 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