summaryrefslogtreecommitdiffstats
path: root/plug-ins/pygimp/plug-ins/whirlpinch.py
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/pygimp/plug-ins/whirlpinch.py')
-rwxr-xr-xplug-ins/pygimp/plug-ins/whirlpinch.py227
1 files changed, 227 insertions, 0 deletions
diff --git a/plug-ins/pygimp/plug-ins/whirlpinch.py b/plug-ins/pygimp/plug-ins/whirlpinch.py
new file mode 100755
index 0000000..6c50e89
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/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 == 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()