forked from akkana/gimp-plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharclayer.py
executable file
·140 lines (117 loc) · 5.72 KB
/
arclayer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/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()