[gimp-data-extras] plug-ins: move py-slice.py from GIMP repository to gimp-data-extras.



commit beffc6bbca18ea7c256669cc427d416792167a44
Author: Jehan <jehan girinstud io>
Date:   Tue Dec 15 01:27:54 2020 +0100

    plug-ins: move py-slice.py from GIMP repository to gimp-data-extras.
    
    Per polls on Twitter, Reddit, Patreon and Tipeee, most people were
    willing to drop this plug-ins as not being relevant anymore (247 for
    dropping and 63 for keeping it, so nearly 80% out of 310 participants).
    
    So let's move it to gimp-data-extras allowing it to still have a
    possible life, and who knows, maybe to even evolve.
    
    While doing so, adding a --enable-gimp3 flag to the configure step. With
    this option, the repository will look for a gimp-3.0 pkg-config file
    instead of gimp-2.0 and will install to the relevant data and plug-in
    folders.
    
    Note that data (brushes, patterns, etc.) are installed both with
    --enable-gimp3 and without, but the Python 3 plug-in is only installed
    with the option enabled, since it is not GIMP 2.x compatible.

 Makefile.am                  |   2 +-
 configure.ac                 |  27 ++-
 plug-ins/Makefile.am         |   9 +
 plug-ins/python3/Makefile.am |  11 +
 plug-ins/python3/py-slice.py | 527 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 572 insertions(+), 4 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 704810a..1ec62e6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = brushes patterns scripts
+SUBDIRS = brushes patterns scripts plug-ins
diff --git a/configure.ac b/configure.ac
index e9586ea..01eb998 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,10 +17,29 @@ AC_CONFIG_SRCDIR([brushes/Splatters/flower.gbr])
 AM_INIT_AUTOMAKE(no-define dist-bzip2)
 AM_MAINTAINER_MODE
 
-PKG_CHECK_MODULES(gimp, gimp-2.0)
-GIMP_DATA_DIR=`$PKG_CONFIG gimp-2.0 --variable=gimpdatadir`
+AC_ARG_ENABLE(gimp3,
+  [  --enable-gimp3          Install GIMP 2.99/3 data and plug-ins],,
+  enable_gimp3=no)
 
-AC_SUBST(GIMP_DATA_DIR)
+gimp_pkg_config_name="gimp-2.0"
+if test "x$enable_gimp3" = xyes; then
+  gimp_pkg_config_name="gimp-3.0"
+fi
+
+PKG_CHECK_MODULES(gimp, $gimp_pkg_config_name)
+
+if test "x$enable_gimp3" = xyes; then
+  GIMP_DATA_DIR=`$PKG_CONFIG gimp-3.0 --variable=gimpdatadir`
+  AC_SUBST(GIMP_DATA_DIR)
+
+  GIMP3_PLUG_INS_DIR=`$PKG_CONFIG gimp-3.0 --variable=gimplibdir`/plug-ins
+  AC_SUBST(GIMP3_PLUG_INS_DIR)
+else
+  GIMP_DATA_DIR=`$PKG_CONFIG gimp-2.0 --variable=gimpdatadir`
+  AC_SUBST(GIMP_DATA_DIR)
+fi
+
+AM_CONDITIONAL(ENABLE_GIMP3, test "x$enable_gimp3" = xyes)
 
 dnl Output the Makefiles
 AC_CONFIG_FILES([
@@ -32,6 +51,8 @@ brushes/Splatters/Makefile
 brushes/Texture/Makefile
 patterns/Makefile
 patterns/Legacy/Makefile
+plug-ins/Makefile
+plug-ins/python3/Makefile
 scripts/Makefile
 ])
 
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
new file mode 100644
index 0000000..5fd4dcb
--- /dev/null
+++ b/plug-ins/Makefile.am
@@ -0,0 +1,9 @@
+## Makefile.am for gimp-data-extras/plug-ins
+
+# Note: we don't add python2/ on purpose because these are only kept
+# here in case someone wants to port them to newer GIMP 3 + Python 3
+# API. As Python2/GIMP2, they still exist in `gimp-2-10` official branch
+# in GIMP repository.
+
+SUBDIRS = \
+       python3
diff --git a/plug-ins/python3/Makefile.am b/plug-ins/python3/Makefile.am
new file mode 100644
index 0000000..408d48d
--- /dev/null
+++ b/plug-ins/python3/Makefile.am
@@ -0,0 +1,11 @@
+## Makefile.am for gimp-data-extras/plug-ins/python3
+
+if ENABLE_GIMP3
+
+py_slicedir = $(GIMP3_PLUG_INS_DIR)/py-slice
+py_slice_SCRIPTS = py-slice.py
+
+EXTRA_DIST = \
+        py-slice.py
+
+endif
diff --git a/plug-ins/python3/py-slice.py b/plug-ins/python3/py-slice.py
new file mode 100755
index 0000000..7bc5dc6
--- /dev/null
+++ b/plug-ins/python3/py-slice.py
@@ -0,0 +1,527 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#Copyright (c) Manish Singh
+#javascript animation support by Joao S. O. Bueno Calligaris (2004)
+
+#   Gimp-Python - allows the writing of Gimp plugins in Python.
+#   Copyright (C) 2003, 2005  Manish Singh <yosh gimp org>
+#
+#   This program is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# (c) 2003 Manish Singh.
+#"Guillotine implemented ala python, with html output
+# (based on perlotine by Seth Burgess)",
+# Modified by João S. O. Bueno Calligaris to allow  dhtml animations (2005)
+
+import gi
+gi.require_version('Gimp', '3.0')
+from gi.repository import Gimp
+from gi.repository import GObject
+from gi.repository import GLib
+from gi.repository import Gio
+
+import gettext
+_ = gettext.gettext
+def N_(message): return message
+
+import os
+import os.path
+import sys
+
+def pyslice(procedure, run_mode, image, drawable, args, data):
+    save_path       = args.index(0)
+    html_filename   = args.index(1)
+    image_basename  = args.index(2)
+    image_extension = args.index(3)
+    separate        = args.index(4)
+    image_path      = args.index(5)
+    cellspacing     = args.index(6)
+    animate         = args.index(7)
+    skip_caps       = args.index(8)
+
+    cellspacing = int (cellspacing)
+
+    if animate:
+        count = 0
+        drw = []
+        #image.layers is a reversed list of the layers on the image
+        #so, count indexes from number of layers to 0.
+        for i in xrange (len (image.layers) -1, -1, -1):
+            if image.layers[i].visible:
+                drw.append(image.layers[i])
+                count += 1
+                if count == 3:
+                    break
+
+
+    vert, horz = get_guides(image)
+
+    if len(vert) == 0 and len(horz) == 0:
+        return
+
+    Gimp.progress_init(_("Slice"))
+    progress_increment = 1 / ((len(horz) + 1) * (len(vert) + 1))
+    progress = 0.0
+
+    def check_path(path):
+        path = os.path.abspath(path)
+
+        if not os.path.exists(path):
+            os.mkdir(path)
+
+        return path
+
+    save_path = check_path(save_path)
+
+    if not os.path.isdir(save_path):
+        save_path = os.path.dirname(save_path)
+
+    if separate:
+        image_relative_path = image_path
+        if not image_relative_path.endswith("/"):
+            image_relative_path += "/"
+        image_path = check_path(os.path.join(save_path, image_path))
+    else:
+        image_relative_path = ''
+        image_path = save_path
+
+    tw = TableWriter(os.path.join(save_path, html_filename),
+                     cellspacing=cellspacing, animate=animate)
+
+    top = 0
+
+    for i in range(0, len(horz) + 1):
+        if i == len(horz):
+            bottom = image.height()
+        else:
+            bottom = image.get_guide_position(horz[i])
+
+        tw.row_start()
+
+        left = 0
+
+        for j in range(0, len(vert) + 1):
+            if j == len(vert):
+                right = image.width()
+            else:
+                right = image.get_guide_position(vert[j])
+            if (skip_caps   and
+                 (
+                   (len(horz) >= 2 and (i == 0 or i == len(horz) )) or
+                   (len(vert) >= 2 and (j == 0 or j == len(vert) ))
+                 )
+               ):
+                skip_stub = True
+            else:
+                skip_stub = False
+
+            if (not animate or skip_stub):
+                src = (image_relative_path +
+                       slice (image, None, image_path,
+                              image_basename, image_extension,
+                              left, right, top, bottom, i, j, ""))
+            else:
+                src = []
+                for layer, postfix in zip (drw, ("", "hover", "clicked")):
+                    src.append (image_relative_path +
+                                slice(image, layer, image_path,
+                                      image_basename, image_extension,
+                                      left, right, top, bottom, i, j, postfix))
+
+            tw.cell(src, right - left, bottom - top, i, j, skip_stub)
+
+            left = right + cellspacing
+
+            progress += progress_increment
+            Gimp.progress_update(progress)
+
+        tw.row_end()
+
+        top = bottom + cellspacing
+
+    tw.close()
+    return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())
+
+def slice(image, drawable, image_path, image_basename, image_extension,
+          left, right, top, bottom, i, j, postfix):
+    if postfix:
+        postfix = "_" + postfix
+    src = "%s_%d_%d%s.%s" % (image_basename, i, j, postfix, image_extension)
+    filename = os.path.join(image_path, src)
+
+    if not drawable:
+        temp_image = image.duplicate()
+        temp_drawable = temp_image.get_active_layer()
+    else:
+        if image.base_type() == Gimp.ImageBaseType.INDEXED:
+            #gimp_layer_new_from_drawable doesn't work for indexed images.
+            #(no colormap on new images)
+            original_active = image.get_active_layer()
+            image.active_layer = drawable
+            temp_image = image.duplicate()
+            temp_drawable = temp_image.get_active_layer()
+            image.active_layer = original_active
+            temp_image.undo_disable()
+            #remove all layers but the intended one
+            while len (temp_image.layers) > 1:
+                if temp_image.layers[0] != temp_drawable:
+                    temp_image.remove_layer (temp_image.layers[0])
+                else:
+                    temp_image.remove_layer (temp_image.layers[1])
+        else:
+            temp_image = Gimp.image_new (drawable.width(),
+                                         drawable.height(),
+                                         image.base_type())
+            temp_drawable = Gimp.layer_new_from_drawable (drawable, temp_image)
+            temp_image.insert_layer (temp_drawable)
+
+    temp_image.undo_disable()
+    temp_image.crop(right - left, bottom - top, left, top)
+    if image_extension == "gif" and image.base_type() == Gimp.ImageBaseType.RGB:
+        temp_image.convert_indexed (Gimp.ConvertDitherType.NONE,
+                                    Gimp.ConvertPaletteType.GENERATE, 255,
+                                    True, False, "")
+    if image_extension == "jpg" and image.base_type() == Gimp.ImageBaseType.INDEXED:
+        temp_image.convert_rgb ()
+
+    Gimp.file_save(Gimp.RunMode.NONINTERACTIVE, temp_image, [temp_drawable],
+                   Gio.file_new_for_path (filename))
+
+    temp_image.delete()
+    return src
+
+class GuideIter:
+    def __init__(self, image):
+        self.image = image
+        self.guide = 0
+
+    def __iter__(self):
+        return iter(self.next_guide, 0)
+
+    def next_guide(self):
+        self.guide = self.image.find_next_guide(self.guide)
+        return self.guide
+
+def get_guides(image):
+    vguides = []
+    hguides = []
+
+    for guide in GuideIter(image):
+        orientation = image.get_guide_orientation(guide)
+
+        guide_position = image.get_guide_position(guide)
+
+        if guide_position > 0:
+            if orientation == Gimp.OrientationType.VERTICAL:
+                if guide_position < image.width():
+                    vguides.append((guide_position, guide))
+            elif orientation == Gimp.OrientationType.HORIZONTAL:
+                if guide_position < image.height():
+                    hguides.append((guide_position, guide))
+
+    def position_sort_key(x):
+        return x[0]
+
+    vguides.sort(key = position_sort_key)
+    hguides.sort(key = position_sort_key)
+
+    vguides = [g[1] for g in vguides]
+    hguides = [g[1] for g in hguides]
+
+    return vguides, hguides
+
+class TableWriter:
+    def __init__(self, filename, cellpadding=0, cellspacing=0, border=0,
+                 animate=False):
+
+        self.filename = filename
+        self.table_attrs = {}
+
+        #Hellraisen IE 6 doesn't support CSS for table control.
+        self.table_attrs['cellpadding'] = cellpadding
+        self.table_attrs['cellspacing'] = cellspacing
+        self.table_attrs['border'] = border
+
+        self.image_prefix = os.path.basename (filename)
+        self.image_prefix = self.image_prefix.split(".")[0]
+        self.image_prefix = self.image_prefix.replace ("-", "_")
+        self.image_prefix = self.image_prefix.replace (" ", "_")
+
+
+        if animate:
+            self.animate = True
+            self.images = []
+        else:
+            self.animate = False
+
+        if os.path.exists (filename):
+            #The plug-in is running to overwrite a previous
+            #version of the file. This will parse the href targets already
+            #in the file to preserve them.
+            self.urls = self.parse_urls ()
+        else:
+            self.urls = []
+
+        self.url_index = 0
+
+        self.html = open(filename, 'wt')
+        self.open()
+
+    def next_url (self):
+        if self.url_index < len (self.urls):
+            self.url_index += 1
+            return self.urls [self.url_index - 1]
+        else:
+            #Default url to use in the anchor tags:
+            return ("#")
+
+    def write(self, s, vals=None):
+        if vals:
+            s = s % vals
+
+        self.html.write(s + '\n')
+
+    def open(self):
+        out = '''<!--HTML SNIPPET GENERATED BY GIMP
+
+WARNING!! This is NOT a fully valid HTML document, it is rather a piece of
+HTML generated by GIMP's py-slice plugin that should be embedded in an HTML
+or XHTML document to be valid.
+
+Replace the href targets in the anchor (<a >) for your URLS to have it working
+as a menu.
+ -->\n'''
+        out += '<table'
+
+        for attr, value in self.table_attrs.items():
+            out += ' %s="%s"' % (attr, value)
+
+        out += '>'
+
+        self.write(out)
+
+    def close(self):
+        self.write('</table>\n')
+        prefix = self.image_prefix
+        if self.animate:
+            out = """
+<script language="javascript" type="text/javascript">
+/* Made with GIMP */
+
+/* Preload images: */
+    images_%s = new Array();
+                   \n"""    % prefix
+            for image in self.images:
+                for type_ in ("plain", "hover", "clicked"):
+                    if image.has_key(type_):
+                        image_index = ("%d_%d_%s" %
+                                       (image["index"][0],
+                                        image["index"][1], type_))
+                        out += ("    images_%s[\"%s\"] = new  Image();\n" %
+                                (prefix, image_index))
+                        out += ("    images_%s[\"%s\"].src = \"%s\";\n" %
+                            (prefix, image_index, image[type_]))
+
+            out+= """
+function exchange (image, images_array_name, event)
+  {
+    name = image.name;
+    images = eval (images_array_name);
+
+    switch (event)
+      {
+        case 0:
+          image.src = images[name + "_plain"].src;
+          break;
+        case 1:
+          image.src = images[name + "_hover"].src;
+          break;
+        case 2:
+          image.src = images[name + "_clicked"].src;
+          break;
+        case 3:
+          image.src = images[name + "_hover"].src;
+          break;
+      }
+
+  }
+</script>
+<!--
+End of the part generated by GIMP
+-->
+"""
+            self.write (out)
+
+
+    def row_start(self):
+        self.write('  <tr>')
+
+    def row_end(self):
+        self.write('</tr>\n')
+
+    def cell(self, src, width, height, row=0, col=0, skip_stub = False):
+        if isinstance (src, list):
+            prefix = "images_%s" % self.image_prefix
+            self.images.append ({"index" : (row, col), "plain" : src[0]})
+
+            out = ('    <td><a href="%s"><img alt="" src="%s" ' +
+                  'style="width: %dpx; height: %dpx; border-width: 0px" \n') %\
+                  (self.next_url(), src[0], width, height)
+            out += 'name="%d_%d" \n' % (row, col)
+            if len(src) >= 2:
+                self.images[-1]["hover"] = src [1]
+                out += """      onmouseout="exchange(this, '%s', 0);"\n""" % \
+                       prefix
+                out += """      onmouseover="exchange(this, '%s', 1);"\n""" % \
+                       prefix
+            if len(src) >= 3:
+                self.images[-1]["clicked"] = src [2]
+                out += """      onmousedown="exchange(this, '%s', 2);"\n""" % \
+                       prefix
+                out += """      onmouseup="exchange(this, '%s', 3);"\n""" % \
+                       prefix
+
+
+
+            out += "/></a></td>\n"
+
+        else:
+            if skip_stub:
+                out =  ('    <td><img alt=" " src="%s" style="width: %dpx; ' +
+                        ' height: %dpx; border-width: 0px;"></td>') % \
+                        (src, width, height)
+            else:
+                out = ('    <td><a href="#"><img alt=" " src="%s" ' +
+                      ' style="width: %dpx; height: %dpx; border-width: 0px;">' +
+                      '</a></td>') %  (src, width, height)
+        self.write(out)
+    def parse_urls (self):
+        """
+           This will parse any url targets in the href="XX" fields
+           of the given file and return then as a list
+        """
+        import re
+        url_list = []
+        try:
+            html_file = open (self.filename)
+
+            # Regular expression to pick everything up to the next
+            # doublequote character after finding the sequence 'href="'.
+            # The found sequences will be returned as a list by the
+            # "findall" method.
+            expr = re.compile (r"""href\=\"([^\"]*?)\"""")
+            url_list = expr.findall (html_file.read (2 ** 18))
+            html_file.close()
+
+        except:
+            # silently ignore any errors parsing this. The file being
+            # overwritten may not be a file created by py-slice.
+            pass
+
+        return url_list
+
+class PySlice (Gimp.PlugIn):
+    ## Parameters ##
+    __gproperties__ = {
+        "save-path": (str,
+                      _("Path for HTML export"),
+                      _("Path for HTML export"),
+                      os.getcwd(),
+                      GObject.ParamFlags.READWRITE),
+        "html-filename": (str,
+                          _("Filename for export"),
+                          _("Filename for export"),
+                          "slice.html",
+                          GObject.ParamFlags.READWRITE),
+        "image-basename": (str,
+                           _("Image name prefix"),
+                           _("Image name prefix"),
+                           "slice",
+                           GObject.ParamFlags.READWRITE),
+        "image-extension": (str,
+                             _("Image format (gif, jpg, png)"),
+                             _("Image format (gif, jpg, png)"),
+                             "gif",
+                           GObject.ParamFlags.READWRITE),
+        "separate-image-dir": (bool,
+                               _("Separate image folder"),
+                               _("Separate image folder"),
+                               False,
+                               GObject.ParamFlags.READWRITE),
+        "relative-image-path": (str,
+                                _("Folder for image export"),
+                                _("Folder for image export"),
+                                "images",
+                                GObject.ParamFlags.READWRITE),
+        "cellspacing": (int,
+                        _("Space between table elements"),
+                        _("Space between table elements"),
+                        0, 15, 0,
+                        GObject.ParamFlags.READWRITE),
+        "animate": (bool,
+                    _("Javascript for onmouseover and clicked"),
+                    _("Javascript for onmouseover and clicked"),
+                    False,
+                    GObject.ParamFlags.READWRITE),
+        # table caps are table cells on the edge of the table
+        "skip-caps": (bool,
+                      _("Skip animation for table caps"),
+                      _("Skip animation for table caps"),
+                      True,
+                    GObject.ParamFlags.READWRITE),
+    }
+
+    ## GimpPlugIn virtual methods ##
+    def do_query_procedures(self):
+        self.set_translation_domain("gimp30-python",
+                                    Gio.file_new_for_path(Gimp.locale_directory()))
+
+        return [ 'python-fu-slice' ]
+
+    def do_create_procedure(self, name):
+        procedure = None
+        if name == 'python-fu-slice':
+            procedure = Gimp.ImageProcedure.new(self, name,
+                                                Gimp.PDBProcType.PLUGIN,
+                                                pyslice, None)
+            procedure.set_image_types("*");
+            # table snippet means a small piece of HTML code here
+            procedure.set_documentation (N_("Cuts an image along its guides, creates images and a HTML table 
snippet"),
+                                         """Add guides to an image. Then run this. It will cut along the 
guides,
+                                         and give you the html to reassemble the resulting images. If you
+                                         choose to generate javascript for onmouseover and clicked events, it
+                                         will use the lower three visible layers on the image for normal,
+                                         onmouseover and clicked states, in that order. If skip caps is
+                                         enabled, table cells on the edge of the table won't become animated,
+                                         and its images will be taken from the active layer.""",
+                                         name)
+            procedure.set_menu_label(_("_Slice..."))
+            procedure.set_attribution("Manish Singh",
+                                      "Manish Singh",
+                                      "2003")
+            procedure.add_menu_path ("<Image>/Filters/Web")
+
+            procedure.add_argument_from_property(self, "save-path")
+            procedure.add_argument_from_property(self, "html-filename")
+            procedure.add_argument_from_property(self, "image-basename")
+            procedure.add_argument_from_property(self, "image-extension")
+            procedure.add_argument_from_property(self, "separate-image-dir")
+            procedure.add_argument_from_property(self, "relative-image-path")
+            procedure.add_argument_from_property(self, "cellspacing")
+            procedure.add_argument_from_property(self, "animate")
+            procedure.add_argument_from_property(self, "skip-caps")
+        return procedure
+
+Gimp.main(PySlice.__gtype__, sys.argv)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]