[gimp-data-extras] plug-ins: move py-slice.py from GIMP repository to gimp-data-extras.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp-data-extras] plug-ins: move py-slice.py from GIMP repository to gimp-data-extras.
- Date: Tue, 15 Dec 2020 00:45:40 +0000 (UTC)
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]