[gimp-data-extras] plug-ins: add Python 2/GIMP 2 API plug-ins not ported to new API.



commit 3d11ab9ada9b7c986726bd27c0e1cf6a649a7c1f
Author: Jehan <jehan girinstud io>
Date:   Mon Sep 21 14:58:38 2020 +0200

    plug-ins: add Python 2/GIMP 2 API plug-ins not ported to new API.
    
    After review by Elad Shahar, then by myself, we decided these 4 plug-ins
    are probably not worth the effort to port them to the new GIMP 3 API
    (and Python 3).
    Either they are duplicate of existing script-fu script or of GEGL
    operations or they are in unfinished state. All of them were not even
    installed on stable builds.
    See isssue gimp#4368.
    
    Nevertheless as Akkana Peck (akk) points out on IRC, some of them are
    interesting to study, in order to know how some things can be done in
    GIMP plug-ins (because even with API changes, most of base code logics
    is reusable) and also maybe these could be improved in the future to do
    something actually useful or better than existing plug-ins. So I move
    them to gimp-data-extras. I don't add them in the build system though,
    because they probably should not be considered stable even in this
    repository. But at least code is there for whoever wants to build good
    plug-ins from there.

 plug-ins/python2/clothify.py     |  76 +++++++++++++
 plug-ins/python2/shadow_bevel.py |  82 ++++++++++++++
 plug-ins/python2/sphere.py       | 111 +++++++++++++++++++
 plug-ins/python2/whirlpinch.py   | 227 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 496 insertions(+)
---
diff --git a/plug-ins/python2/clothify.py b/plug-ins/python2/clothify.py
new file mode 100755
index 0000000..258329c
--- /dev/null
+++ b/plug-ins/python2/clothify.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python2
+
+#   Gimp-Python - allows the writing of Gimp plugins in Python.
+#   Copyright (C) 1997  James Henstridge <james daa com au>
+#
+#   This program is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import math
+from gimpfu import *
+
+def clothify(timg, tdrawable, bx=9, by=9, azimuth=135, elevation=45, depth=3):
+    width = tdrawable.width
+    height = tdrawable.height
+
+    img = gimp.Image(width, height, RGB)
+    img.disable_undo()
+
+    layer_one = gimp.Layer(img, "X Dots", width, height, RGB_IMAGE,
+                           100, NORMAL_MODE)
+    img.insert_layer(layer_one)
+    pdb.gimp_edit_fill(layer_one, BACKGROUND_FILL)
+
+    pdb.plug_in_noisify(img, layer_one, 0, 0.7, 0.7, 0.7, 0.7)
+
+    layer_two = layer_one.copy()
+    layer_two.mode = MULTIPLY_MODE
+    layer_two.name = "Y Dots"
+    img.insert_layer(layer_two)
+
+    pdb.plug_in_gauss_rle(img, layer_one, bx, 1, 0)
+    pdb.plug_in_gauss_rle(img, layer_two, by, 0, 1)
+
+    img.flatten()
+
+    bump_layer = img.active_layer
+
+    pdb.plug_in_c_astretch(img, bump_layer)
+    pdb.plug_in_noisify(img, bump_layer, 0, 0.2, 0.2, 0.2, 0.2)
+    pdb.plug_in_bump_map(img, tdrawable, bump_layer, azimuth,
+                         elevation, depth, 0, 0, 0, 0, True, False, 0)
+
+    gimp.delete(img)
+
+register(
+        "python-fu-clothify",
+        "Make the image look like it is printed on cloth",
+        "Make the specified layer look like it is printed on cloth",
+        "James Henstridge",
+        "James Henstridge",
+        "1997-1999",
+        "_Clothify...",
+        "RGB*, GRAY*",
+        [
+            (PF_IMAGE, "image", "Input image", None),
+            (PF_DRAWABLE, "drawable", "Input drawable", None),
+            (PF_INT, "x-blur", "X blur", 9),
+            (PF_INT, "y-blur", "Y blur", 9),
+            (PF_INT, "azimuth", "Azimuth", 135),
+            (PF_INT, "elevation", "Elevation", 45),
+            (PF_INT, "depth", "Depth", 3)
+        ],
+        [],
+        clothify, menu="<Image>/Filters/Artistic")
+
+main()
diff --git a/plug-ins/python2/shadow_bevel.py b/plug-ins/python2/shadow_bevel.py
new file mode 100755
index 0000000..29a9246
--- /dev/null
+++ b/plug-ins/python2/shadow_bevel.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python2
+
+#   Gimp-Python - allows the writing of Gimp plugins in Python.
+#   Copyright (C) 1997  James Henstridge <james daa com au>
+#
+#   This program is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def shadow_bevel(img, drawable, blur, bevel, do_shadow, drop_x, drop_y):
+    # disable undo for the image
+    img.undo_group_start()
+
+    # copy the layer
+    shadow = drawable.copy(True)
+    img.insert_layer(shadow, position=img.layers.index(drawable) + 1)
+    shadow.name = drawable.name + " shadow"
+    shadow.lock_alpha = False
+
+    # threshold the shadow layer to all white
+    pdb.gimp_threshold(shadow, 0, 255)
+
+    # blur the shadow layer
+    pdb.plug_in_gauss_iir(img, shadow, blur, True, True)
+
+    # do the bevel thing ...
+    if bevel:
+        pdb.plug_in_bump_map(img, drawable, shadow, 135, 45, 3,
+                             0, 0, 0, 0, True, False, 0)
+
+    # make the shadow layer black now ...
+    pdb.gimp_drawable_invert(shadow, False)
+
+    # translate the drop shadow
+    shadow.translate(drop_x, drop_y)
+
+    if not do_shadow:
+        # delete shadow ...
+        gimp.delete(shadow)
+
+    # enable undo again
+    img.undo_group_end()
+
+
+register(
+    "python-fu-shadow-bevel",
+    N_("Add a drop shadow to a layer, and optionally bevel it"),
+    "Add a drop shadow to a layer, and optionally bevel it",
+    "James Henstridge",
+    "James Henstridge",
+    "1999",
+    N_("_Drop Shadow and Bevel..."),
+    "RGBA, GRAYA",
+    [
+        (PF_IMAGE, "image", "Input image", None),
+        (PF_DRAWABLE, "drawable", "Input drawable", None),
+        (PF_SLIDER, "blur",   _("_Shadow blur"), 6, (1, 30, 1)),
+        (PF_BOOL,   "bevel",  _("_Bevel"),       True),
+        (PF_BOOL,   "shadow", _("_Drop shadow"), True),
+        (PF_INT,    "drop-x", _("Drop shadow _X displacement"), 3),
+        (PF_INT,    "drop-y", _("Drop shadow _Y displacement"), 6)
+    ],
+    [],
+    shadow_bevel,
+    menu="<Image>/Filters/Light and Shadow/Shadow",
+    domain=("gimp20-python", gimp.locale_directory)
+    )
+
+main()
diff --git a/plug-ins/python2/sphere.py b/plug-ins/python2/sphere.py
new file mode 100755
index 0000000..889c3f7
--- /dev/null
+++ b/plug-ins/python2/sphere.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python2
+
+#   Gimp-Python - allows the writing of Gimp plugins in Python.
+#   Copyright (C) 1997  James Henstridge <james daa com au>
+#
+#   This program is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import math
+from gimpfu import *
+
+def sphere(radius, light, shadow, foo, bg_colour, sphere_colour):
+    if radius < 1:
+        radius = 1
+
+    width = int(radius * 3.75)
+    height = int(radius * 2.5)
+
+    gimp.context_push()
+
+    img = gimp.Image(width, height, RGB)
+
+    drawable = gimp.Layer(img, "Sphere Layer", width, height,
+                          RGB_IMAGE, 100, NORMAL_MODE)
+
+    radians = light * math.pi / 180
+
+    cx = width / 2
+    cy = height / 2
+
+    light_x = cx + radius * 0.6 * math.cos(radians)
+    light_y = cy - radius * 0.6 * math.sin(radians)
+
+    light_end_x = cx + radius * math.cos(math.pi + radians)
+    light_end_y = cy - radius * math.sin(math.pi + radians)
+
+    offset = radius * 0.1
+
+    img.disable_undo()
+    img.insert_layer(drawable)
+
+    gimp.set_foreground(sphere_colour)
+
+    gimp.set_background(bg_colour)
+    pdb.gimp_edit_fill(drawable, BACKGROUND_FILL)
+
+    gimp.set_background(20, 20, 20)
+
+    if (light >= 45 and light <= 75 or light <= 135 and
+        light >= 105) and shadow:
+        shadow_w = radius * 2.5 * math.cos(math.pi + radians)
+        shadow_h = radius * 0.5
+        shadow_x = cx
+        shadow_y = cy + radius * 0.65
+
+        if shadow_w < 0:
+            shadow_x = cx + shadow_w
+            shadow_w = -shadow_w
+
+        pdb.gimp_ellipse_select(img, shadow_x, shadow_y, shadow_w, shadow_h,
+                                CHANNEL_OP_REPLACE, True, True, 7.5)
+        pdb.gimp_edit_bucket_fill(drawable, BG_BUCKET_FILL,
+                                  MULTIPLY_MODE, 100, 0, False, 0, 0)
+
+    pdb.gimp_ellipse_select(img, cx - radius, cy - radius, 2 * radius,
+                            2 * radius, CHANNEL_OP_REPLACE, True, False, 0)
+    pdb.gimp_edit_blend(drawable, FG_BG_RGB_MODE, NORMAL_MODE, GRADIENT_RADIAL,
+                        100, offset, REPEAT_NONE, False, False, 0, 0, True,
+                        light_x, light_y, light_end_x, light_end_y)
+
+    pdb.gimp_selection_none(img)
+
+    img.enable_undo()
+
+    disp = gimp.Display(img)
+
+    gimp.context_pop()
+
+
+register(
+    "python-fu-sphere",
+    "Simple sphere with drop shadow",
+    "Simple sphere with drop shadow",
+    "James Henstridge",
+    "James Henstridge",
+    "1997-1999, 2007",
+    "_Sphere",
+    "",
+    [
+        (PF_INT, "radius", "Radius for sphere", 100),
+        (PF_SLIDER, "light", "Light angle", 45, (0,360,1)),
+        (PF_TOGGLE, "shadow", "Shadow?", 1),
+        (PF_RADIO, "foo", "Test", "foo", (("Foo", "foo"), ("Bar", "bar"))),
+        (PF_COLOR, "bg-color", "Background", (1.0, 1.0, 1.0)),
+        (PF_COLOR, "sphere-color", "Sphere", "orange")
+    ],
+    [],
+    sphere,
+    menu="<Image>/Filters/Languages/Python-Fu/Test")
+
+main()
diff --git a/plug-ins/python2/whirlpinch.py b/plug-ins/python2/whirlpinch.py
new file mode 100755
index 0000000..3b166de
--- /dev/null
+++ b/plug-ins/python2/whirlpinch.py
@@ -0,0 +1,227 @@
+#!/usr/bin/env python2
+
+#   Gimp-Python - allows the writing of Gimp plugins in Python.
+#   Copyright (C) 1997  James Henstridge <james daa com au>
+#
+#   This program is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# Algorithms stolen from the whirl and pinch plugin distributed with Gimp,
+# by Federico Mena Quintero and Scott Goehring
+#
+# This version does the same thing, except there is no preview, and it is
+# written in python and is slower.
+
+import math, struct
+from gimpfu import *
+
+class pixel_fetcher:
+        def __init__(self, drawable):
+                self.col = -1
+                self.row = -1
+                self.img_width = drawable.width
+                self.img_height = drawable.height
+                self.img_bpp = drawable.bpp
+                self.img_has_alpha = drawable.has_alpha
+                self.tile_width = gimp.tile_width()
+                self.tile_height = gimp.tile_height()
+                self.bg_colour = '\0\0\0\0'
+                self.bounds = drawable.mask_bounds
+                self.drawable = drawable
+                self.tile = None
+        def set_bg_colour(self, r, g, b, a):
+                self.bg_colour = struct.pack('BBB', r,g,b)
+                if self.img_has_alpha:
+                        self.bg_colour = self.bg_colour + chr(a)
+        def get_pixel(self, x, y):
+                sel_x1, sel_y1, sel_x2, sel_y2 = self.bounds
+                if x < sel_x1 or x >= sel_x2 or y < sel_y1 or y >= sel_y2:
+                        return self.bg_colour
+                col = x / self.tile_width
+                coloff = x % self.tile_width
+                row = y / self.tile_height
+                rowoff = y % self.tile_height
+
+                if col != self.col or row != self.row or self.tile is None:
+                        self.tile = self.drawable.get_tile(False, row, col)
+                        self.col = col
+                        self.row = row
+                return self.tile[coloff, rowoff]
+
+class Dummy:
+        pass
+
+def whirl_pinch(image, drawable, whirl, pinch, radius):
+        self = Dummy()
+        self.width = drawable.width
+        self.height = drawable.height
+        self.bpp = drawable.bpp
+        self.has_alpha = drawable.has_alpha
+        self.bounds = drawable.mask_bounds
+        self.sel_x1, self.sel_y1, self.sel_x2, self.sel_y2 = \
+                     drawable.mask_bounds
+        self.sel_w = self.sel_x2 - self.sel_x1
+        self.sel_h = self.sel_y2 - self.sel_y1
+        self.cen_x = (self.sel_x1 + self.sel_x2 - 1) / 2.0
+        self.cen_y = (self.sel_y1 + self.sel_y2 - 1) / 2.0
+        xhsiz = (self.sel_w - 1) / 2.0
+        yhsiz = (self.sel_h - 1) / 2.0
+
+        if xhsiz < yhsiz:
+                self.scale_x = yhsiz / xhsiz
+                self.scale_y = 1.0
+        elif xhsiz > yhsiz:
+                self.scale_x = 1.0
+                self.scale_y = xhsiz / yhsiz
+        else:
+                self.scale_x = 1.0
+                self.scale_y = 1.0
+
+        self.radius = max(xhsiz, yhsiz);
+
+        if not drawable.is_rgb and not drawable.is_grey:
+                return
+
+        gimp.tile_cache_ntiles(2 * (1 + self.width / gimp.tile_width()))
+
+        whirl = whirl * math.pi / 180
+        dest_rgn = drawable.get_pixel_rgn(self.sel_x1, self.sel_y1,
+                                          self.sel_w, self.sel_h, True, True)
+        pft = pixel_fetcher(drawable)
+        pfb = pixel_fetcher(drawable)
+
+        bg_colour = gimp.get_background()
+
+        pft.set_bg_colour(bg_colour[0], bg_colour[1], bg_colour[2], 0)
+        pfb.set_bg_colour(bg_colour[0], bg_colour[1], bg_colour[2], 0)
+
+        progress = 0
+        max_progress = self.sel_w * self.sel_h
+
+        gimp.progress_init("Whirling and pinching")
+
+        self.radius2 = self.radius * self.radius * radius
+        pixel = ['', '', '', '']
+        values = [0,0,0,0]
+
+        for row in range(self.sel_y1, (self.sel_y1+self.sel_y2)/2+1):
+                top_p = ''
+                bot_p = ''
+                for col in range(self.sel_x1, self.sel_x2):
+                        q, cx, cy = calc_undistorted_coords(self, col,
+                                                            row, whirl, pinch,
+                                                            radius)
+                        if q:
+                                if cx >= 0: ix = int(cx)
+                                else:       ix = -(int(-cx) + 1)
+                                if cy >= 0: iy = int(cy)
+                                else:       iy = -(int(-cy) + 1)
+                                pixel[0] = pft.get_pixel(ix, iy)
+                                pixel[1] = pft.get_pixel(ix+1, iy)
+                                pixel[2] = pft.get_pixel(ix, iy+1)
+                                pixel[3] = pft.get_pixel(ix+1, iy+1)
+                                for i in range(self.bpp):
+                                        values[0] = ord(pixel[0][i])
+                                        values[1] = ord(pixel[1][i])
+                                        values[2] = ord(pixel[2][i])
+                                        values[3] = ord(pixel[3][i])
+                                        top_p = top_p + bilinear(cx,cy, values)
+                                cx = self.cen_x + (self.cen_x - cx)
+                                cy = self.cen_y + (self.cen_y - cy)
+                                if cx >= 0: ix = int(cx)
+                                else:       ix = -(int(-cx) + 1)
+                                if cy >= 0: iy = int(cy)
+                                else:       iy = -(int(-cy) + 1)
+                                pixel[0] = pfb.get_pixel(ix, iy)
+                                pixel[1] = pfb.get_pixel(ix+1, iy)
+                                pixel[2] = pfb.get_pixel(ix, iy+1)
+                                pixel[3] = pfb.get_pixel(ix+1, iy+1)
+                                tmp = ''
+                                for i in range(self.bpp):
+                                        values[0] = ord(pixel[0][i])
+                                        values[1] = ord(pixel[1][i])
+                                        values[2] = ord(pixel[2][i])
+                                        values[3] = ord(pixel[3][i])
+                                        tmp = tmp + bilinear(cx,cy, values)
+                                bot_p = tmp + bot_p
+                        else:
+                                top_p = top_p + pft.get_pixel(col, row)
+                                bot_p = pfb.get_pixel((self.sel_x2 - 1) -
+                                        (col - self.sel_x1), (self.sel_y2-1) -
+                                        (row - self.sel_y1)) + bot_p
+
+                dest_rgn[self.sel_x1:self.sel_x2, row] = top_p
+                dest_rgn[self.sel_x1:self.sel_x2, (self.sel_y2 - 1)
+                         - (row - self.sel_y1)] = bot_p
+
+                progress = progress + self.sel_w * 2
+                gimp.progress_update(float(progress) / max_progress)
+
+        drawable.flush()
+        drawable.merge_shadow(True)
+        drawable.update(self.sel_x1,self.sel_y1,self.sel_w,self.sel_h)
+
+def calc_undistorted_coords(self, wx, wy, whirl, pinch, radius):
+        dx = (wx - self.cen_x) * self.scale_x
+        dy = (wy - self.cen_y) * self.scale_y
+        d = dx * dx + dy * dy
+        inside = d < self.radius2
+
+        if inside:
+                dist = math.sqrt(d / radius) / self.radius
+                if (d == 0.0):
+                        factor = 1.0
+                else:
+                        factor = math.pow(math.sin(math.pi / 2 * dist),
+                                          -pinch)
+                dx = dx * factor
+                dy = dy * factor
+                factor = 1 - dist
+                ang = whirl * factor * factor
+                sina = math.sin(ang)
+                cosa = math.cos(ang)
+                x = (cosa * dx - sina * dy) / self.scale_x + self.cen_x
+                y = (sina * dx + cosa * dy) / self.scale_y + self.cen_y
+        else:
+                x = wx
+                y = wy
+        return inside, float(x), float(y)
+
+def bilinear(x, y, values):
+        x = x % 1.0
+        y = y % 1.0
+        m0 = values[0] + x * (values[1] - values[0])
+        m1 = values[2] + x * (values[3] - values[2])
+        return chr(int(m0 + y * (m1 - m0)))
+
+
+register(
+        "python-fu-whirl-pinch",
+        "Distorts an image by whirling and pinching",
+        "Distorts an image by whirling and pinching",
+        "James Henstridge (translated from C plugin)",
+        "James Henstridge",
+        "1997-1999",
+        "_Whirl and Pinch...",
+        "RGB*, GRAY*",
+        [
+            (PF_IMAGE, "image", "Input image", None),
+            (PF_DRAWABLE, "drawable", "Input drawable", None),
+            (PF_SLIDER, "whirl", "Whirl angle", 90, (-360, 360, 1)),
+            (PF_FLOAT, "pinch", "Pinch amount", 0),
+            (PF_FLOAT, "radius", "radius", 1)
+        ],
+        [],
+        whirl_pinch, menu="<Image>/Filters/Distorts")
+
+main()


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