Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: cf598b9a1f
Fetching contributors…

Cannot retrieve contributors at this time

executable file 141 lines (117 sloc) 5.891 kb
#!/usr/bin/env python
# arclayer.py: Gimp plug-in to warp one layer along an arc of a given radius.
# Copyright 2002,2008 by Akkana Peck, http://www.shallowsky.com/software/
# You may use and distribute this plug-in under the terms of the GPL.
#
# Thanks to Joao Bueno for some excellent tips on speeding up
# pixel ops in gimp-python!
import math
import time
from gimpfu import *
from array import array
def python_arc_layer(img, layer, radius, ontop) :
gimp.progress_init("Arcing layer" + layer.name + "...")
# Spinner passes an integer, which will upset later calculations
radius = float(radius)
pdb.gimp_image_undo_group_start(img)
layername = "arc " + layer.name
# Calculate the size for the new layer
theta2 = layer.width / radius / 2
newWidth = int(2 * radius * math.sin(theta2))
if theta2 <= 1.570795 : # PI/2: if we're doing less than a semicircle
newHeight = int(layer.height * math.cos(theta2)
+ .5 * radius * math.sin(theta2) *
math.tan(theta2))
else :
newHeight = layer.height + int(radius * (1 - math.cos(theta2)))
#print "Old size: ",layer.width,"x",layer.height
#print "New size: ",newWidth,"x",newHeight
#print "r =", radius, ", theta/2 =", theta2
# Create the new layer:
destDrawable = gimp.Layer(img, layername, newWidth, newHeight,
layer.type, layer.opacity, layer.mode)
img.add_layer(destDrawable, 0)
xoff, yoff = layer.offsets
destDrawable.translate(xoff - (newWidth - layer.width)/2,
yoff - (newHeight - layer.height)/2)
# No need to clear the layer here --
# we'll initialize it to zero when we create the pixel array.
srcWidth, srcHeight = layer.width, layer.height
srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,
False, False)
src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
dstRgn = destDrawable.get_pixel_rgn(0, 0, newWidth, newHeight,
True, True)
p_size = len(srcRgn[0,0])
dest_pixels = array("B", "\x00" * (newWidth * newHeight * p_size))
# Finally, loop over the region:
for x in xrange(0, srcWidth - 1) :
for y in xrange(0, srcHeight) :
# Calculate new coordinates
phi = theta2 - x/radius
if ontop :
r = radius - y
newy = int(radius - r * math.cos(phi))
if (newy < 0) :
continue
else :
r = radius - layer.height + y
newy = newHeight \
+ int(r * math.cos(phi) - radius)
if (newy > newHeight) :
continue
newx = int(newWidth/2 - r * math.sin(phi))
src_pos = (x + srcWidth * y) * p_size
dest_pos = (newx + newWidth * newy) * p_size
newval = src_pixels[src_pos: src_pos + p_size]
dest_pixels[dest_pos : dest_pos + p_size] = newval
# A fast (but not great) cheat to fill in holes:
# Write to two pixel above the new location
# and one pixel to the right of it.
# If that's a valid location, then later
# another set of coordinates will map to it,
# and overwrite this value; if not, then it
# would have been a hole, and this fills it.
if (newy < newHeight-1) :
next_row = dest_pos + p_size * newWidth
dest_pixels [next_row : next_row + p_size] \
= newval
if (newx < newWidth-1) :
next_col = dest_pos + p_size
dest_pixels [next_col : next_col + p_size] \
= newval
#print "Progress:", 100.0 * x / layer.width
# Docs say progress_update takes a percent,
# but it's really a fraction.
# GIMP whines "arclayer.py is updating the progress too often"
# but, well, tough. It's only ten times total.
progress = float(x)/layer.width
if (int(progress * 100) % 20 == 0) :
gimp.progress_update(progress)
# Copy the whole array back to the pixel region:
dstRgn[0:newWidth, 0:newHeight] = dest_pixels.tostring()
destDrawable.flush()
destDrawable.merge_shadow(True)
destDrawable.update(0, 0, newWidth,newHeight)
# Remove the old layer
#img.remove_layer(layer)
layer.visible = False
pdb.gimp_selection_none(img)
pdb.gimp_image_undo_group_end(img)
register(
"python_fu_arc_layer",
"Bend a layer in an arc",
"Bend a layer in an arc",
"Akkana Peck",
"Akkana Peck",
"2002,2008",
"<Image>/Filters/Distorts/ArcLayer(py)...",
"*",
[
(PF_SPINNER, "Radius", "Arc Radius (pixels)",
400, (0, 2000, 50)),
(PF_TOGGLE, "Top", "Top of circle?", 1),
],
[],
python_arc_layer)
main()
Jump to Line
Something went wrong with that request. Please try again.