Just some fun with gimp logo and gimp chat dot com text.
Then I realized I have to save file as png if I want to have transparency on the map content.
#!/usr/bin/env python
from gimpfu import *
import os
import math
from array import array #fast pixel operations need this
import time
import tempfile
def get_temp_file_name_with_extension(extension=".png"):
# Get the temporary directory path
temp_dir = tempfile.gettempdir()
# Generate a temporary file name with extension
temp_file_name = next(tempfile._get_candidate_names()) + extension
# Create the full path by joining the temporary directory and file name
temp_file_path = os.path.join(temp_dir, temp_file_name)
# Return the temporary file path
return temp_file_path
def get_curve_vertical(image):
pdb_gimp_vectors_stroke_get_point_at_dist = pdb.gimp_vectors_stroke_get_point_at_dist
vectors = pdb.gimp_image_get_vectors_by_name(image,'vertical')
rightvectors = vectors
leftvectors = vectors
num_strokes,stroke_ids = pdb.gimp_vectors_get_strokes(vectors)
stroke1 = stroke_ids[0] #we don't know which is upper and which is lower strokes here
stroke2 = stroke_ids[1]
xpoint1,ypoint1,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(vectors,stroke1,0,0.1)
xpoint2,ypoint2,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(vectors,stroke2,0,0.1)
if xpoint1 < xpoint2:
rightstroke = stroke2
leftstroke = stroke1
else:
rightstroke = stroke1
leftstroke = stroke2
right_length = pdb.gimp_vectors_stroke_get_length(rightvectors,rightstroke,0.01) #first stroke
left_length = pdb.gimp_vectors_stroke_get_length(leftvectors,leftstroke,0.01) #second stroke
longpath = int(max(right_length,left_length))
rightdiv = float(right_length) / longpath
leftdiv = float(left_length) / longpath
rightdist = 0.0
leftdist = 0.0
#base on direction of each stroke we'll make the div positive or negative and set dists variables
xpoint1,ypoint1,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(rightvectors,rightstroke,0,0.1)
xpoint2,ypoint2,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(rightvectors,rightstroke,right_length,0.1)
if ypoint1 > ypoint2:
rightdist = right_length
rightdiv = -rightdiv
xpoint1,ypoint1,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(leftvectors,leftstroke,0,0.1)
xpoint2,ypoint2,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(leftvectors,leftstroke,left_length,0.1)
if ypoint1 > ypoint2:
leftdist = left_length
leftdiv = -leftdiv
rightcurvex = []
leftcurvex = []
y_values = []
y_values2 = []
maxdiffx = 0
i = 0
y_values_append = y_values.append
y_values2_append = y_values2.append
rightcurvex_append = rightcurvex.append
leftcurvex_append = leftcurvex.append
while i < longpath:
#while rightdist >= 0 and leftdist >=0 and rightdist <= right_length and leftdist <= left_length:
rightxpoint,rightypoint,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(rightvectors,rightstroke,rightdist,1)
leftxpoint,leftypoint,slope,valid2 = pdb_gimp_vectors_stroke_get_point_at_dist(leftvectors,leftstroke,leftdist,1)
# if valid1 == TRUE and valid2 == TRUE:
y_values_append(rightypoint)
y_values2_append(leftypoint)
rightcurvex_append(rightxpoint)
leftcurvex_append(leftxpoint)
maxdiffx = max(rightxpoint-leftxpoint,maxdiffx)
rightdist += rightdiv
leftdist += leftdiv
i+=1
return [y_values,y_values2,rightcurvex,leftcurvex,maxdiffx]
def get_curve_horizontal(image,maxdiffx):
pdb_gimp_vectors_stroke_get_point_at_dist = pdb.gimp_vectors_stroke_get_point_at_dist
#vectors = pdb.gimp_image_get_active_vectors(image)
#vectors = image.vectors[0] #just read the first one, the only one path
vectors = pdb.gimp_image_get_vectors_by_name(image,'horizontal')
uppervectors = vectors
lowervectors = vectors
num_strokes,stroke_ids = pdb.gimp_vectors_get_strokes(vectors)
stroke1 = stroke_ids[0] #we don't know which is upper and which is lower strokes here
stroke2 = stroke_ids[1]
xpoint1,ypoint1,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(vectors,stroke1,0,0.1)
xpoint2,ypoint2,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(vectors,stroke2,0,0.1)
if ypoint1 < ypoint2:
upperstroke = stroke1
lowerstroke = stroke2
else:
upperstroke = stroke2
lowerstroke = stroke1
upper_length = pdb.gimp_vectors_stroke_get_length(uppervectors,upperstroke,0.01) #first stroke
lower_length = pdb.gimp_vectors_stroke_get_length(lowervectors,lowerstroke,0.01) #second stroke
longpath = max(upper_length,lower_length,maxdiffx)
upperdiv = float(upper_length) / longpath
lowerdiv = float(lower_length) / longpath
upperdist = 0.0
lowerdist = 0.0
#base on direction of each stroke we'll make the div positive or negative and set dists variables
xpoint1,ypoint1,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(uppervectors,upperstroke,0,0.1)
xpoint2,ypoint2,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(uppervectors,upperstroke,upper_length,0.1)
if xpoint1 > xpoint2:
upperdist = upper_length
upperdiv = -upperdiv
xpoint1,ypoint1,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(lowervectors,lowerstroke,0,0.1)
xpoint2,ypoint2,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(lowervectors,lowerstroke,lower_length,0.1)
if xpoint1 > xpoint2:
lowerdist = lower_length
lowerdiv = -lowerdiv
uppercurvey = []
lowercurvey = []
lowesty = 0
x_values = []
x_values2 = []
#while upperdist >= 0 and lowerdist >=0 and upperdist <= upper_length and lowerdist <= lower_length:
i=0;
x_values_append = x_values.append
x_values2_append = x_values2.append
uppercurvey_append = uppercurvey.append
lowercurvey_append = lowercurvey.append
while i < longpath:
upperxpoint,upperypoint,slope,valid1 = pdb_gimp_vectors_stroke_get_point_at_dist(uppervectors,upperstroke,upperdist,1)
lowerxpoint,lowerypoint,slope,valid2 = pdb_gimp_vectors_stroke_get_point_at_dist(lowervectors,lowerstroke,lowerdist,1)
# if valid1 == TRUE and valid2 == TRUE:
x_values_append(upperxpoint)
x_values2_append(lowerxpoint)
uppercurvey_append(upperypoint)
lowercurvey_append(lowerypoint)
upperdist += upperdiv
lowerdist += lowerdiv
i+=1
return [x_values,x_values2,uppercurvey,lowercurvey]
def python_my_manual_curve_bend_v10(image,layer):
starttime = time.time()
#saves the png to load it up later not sure why but it's faster
maplayer = layer
#png to keep transparency on layer content
png_full_path = get_temp_file_name_with_extension(".png") #this gets a temporary file name with .png
pdb.file_png_save_defaults(image,maplayer,png_full_path,png_full_path)
#exporting to jpg should be faster
# png_full_path = get_temp_file_name_with_extension(".jpg") #this gets a temporary file name with .png
# pdb.file_jpeg_save(image,maplayer,png_full_path,png_full_path,1.0,0,1,1,"jpg for double curve bend mapping",2,1,0,0)
pdb.gimp_image_remove_layer(image,maplayer)
y_values,y_values2,right_x,left_x,maxdiffx = get_curve_vertical(image)
x_values,x_values2,upper_y,lower_y = get_curve_horizontal(image,maxdiffx*1.2) #make it 20 percent more mapping to get rid of stray pixels
new_layer = pdb.gimp_file_load_layer(image, png_full_path) #it's odd that this makes it fast
image.add_layer(new_layer, 0)
#new_layer = layer
if pdb.gimp_drawable_has_alpha(new_layer) == FALSE:
new_layer.add_alpha()
map_layer = pdb.gimp_layer_new(image,image.width,image.height,RGBA_IMAGE,"Double Curve Bend",100,LAYER_MODE_NORMAL)
pdb.gimp_image_insert_layer(image,map_layer,None,0)
w = len(x_values)*1.0
h = len(y_values)*1.0 # This gives us height that's molded around the vertical curves.
# #always scale since if it's smaller it'll be fast
# h = float(w*2)/new_layer.width * new_layer.height #make our image has double pixels to pick from
# pdb.gimp_layer_scale(new_layer,w*2,h,FALSE)
offset_x,offset_y = pdb.gimp_drawable_offsets(new_layer) #save offset
#[ ... setting up ... ] # initialize the regions and get their contents into arrays: START =============
layer = new_layer
dest = map_layer
srcWidth = layer.width+0
srcHeight = layer.height+0
srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,False, False)
src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
newWidth = dest.width+0
newHeight = dest.height+0
dstRgn = dest.get_pixel_rgn(0, 0, newWidth, newHeight,True, True)
p_size = len(srcRgn[0,0])
dest_pixels = array("B", "\x00" * (newWidth * newHeight * p_size))
#[ ... setting up ... ] # initialize the regions and get their contents into arrays: DONE ===============
#pdb.gimp_progress_set_text("Pixel Mapping (Not Curve Bend)...")
oxinc = 1.0*new_layer.width/w
ox = 0
looplen = len(x_values)-1
looplen = looplen
mathsqrt = math.sqrt
for xi in range(0,looplen):
#pdb.gimp_progress_update(float(xi)/len(x_values)-1) #user friendliness
ox1 = int(ox); ox+=oxinc #(new_layer.width * float(x_values[xi]-minx)/width)
#ox2 = int((xi+1)/w*new_layer.width) #(new_layer.width * float(x_values[xi+1]-minx)/width)+1
oy1 = 0
oy2 = new_layer.height
nx1 = x_values[xi]; ny1 = upper_y[xi]
nx2 = x_values2[xi]; ny2 = lower_y[xi]
#NOW MAP FROM (ox1,oy1) to (ox1,oy1) => (nx1,ny1) to (nx2,ny2)
distance = mathsqrt((nx2 - nx1)**2 + (ny2 - ny1)**2)
one_over_distance = 1.0/distance #to reduce division
xstep = 1.0*(nx2-nx1)*one_over_distance
ystep = 1.0*(ny2-ny1)*one_over_distance
srcxstep = 0
srcystep = 1.0*(oy2-oy1)*one_over_distance
x = int(offset_x+ox1)
# p_sizesrcWidthyinc = p_size*1*srcystepd
xstepinc = 0 #set these so we can do addition instead of multiplication
ystepinc = 0
srcystepinc = offset_y+oy1
intdist = int(distance)
loopintdist = intdist
xiw = xi/w
one_over_indist_h = 1.0/intdist*h
for d in range(0,loopintdist): #d represent steps in pixels dm is main d
y = int(srcystepinc);srcystepinc+=srcystep
#[ ... then inside the loop over x and y ... ]
#this is like get pixel I am guessing CONFIRMED: It is getpixel(x,y) where newval is pixel value
src_pos = (x + srcWidth * y) << 2 #shift left 2 bit instead of multiplying by 4
#newval = src_pixels[src_pos: src_pos + p_size]
# h indicates how many horizontals we have for ### y_values,y_values2,right_x,left_x = get_curve_vertical(image)
# 1.0*xi/w indicates horizontal percentage ### this could be multiplied by new_layer.width to get source pixel
# d/intdist indicates at what h.
# newx = int(nx1+xstep*d)
# newy = int(ny1+ystep*d)
h_index = int(d*one_over_indist_h)
leftx = left_x[h_index]
rightx = right_x[h_index]
newx = int(leftx + xiw*(rightx-leftx))
newy = int(ny1+ystep*d)
#this is like putpixel I am guessing CONFIRMED: It is putpixel(newx,newy) where newval is pixel value
dest_pos = (newx + newWidth * newy) << 2 #shift left 2 bit instead of multiplying by 4
#dest_pixels[dest_pos : dest_pos + p_size] = newval
dest_pixels[dest_pos : dest_pos + p_size] = src_pixels[src_pos: src_pos + p_size]
#[ ... when the loop is all finished ... ] ==================== FAST UPDATE PIXEL RANGE
# Copy the whole array back to the pixel region:
dstRgn[0:newWidth, 0:newHeight] = dest_pixels.tostring()
#need this to update layer
dest.flush()
dest.merge_shadow(True)
dest.update(0, 0, newWidth,newHeight) #================= FAST UPDATE PIXEL RANGE COMPLETE
pdb.gimp_item_set_visible(new_layer,FALSE) #hides our temp layer
endtime = time.time()
pdb.gimp_message("Execution time: " + str(endtime-starttime))
pdb.gimp_displays_flush()
register(
"python_fu_double_curve_bend",
"Double Curve Bend using paths",
"Double Curve Bend using paths",
"TT",
"TT",
"2023.1.2",
"A Double Curve Bend",
"RGB*", # Alternately use RGB, RGB*, GRAY*, INDEXED etc.
[
(PF_IMAGE, "img", "Image", None),
(PF_DRAWABLE, "drawable", "Drawble", None),
],
[],
python_my_manual_curve_bend_v10,
menu="<Image>/Filters/Distort")
main()
# Below is all the example input types for INPUTS for the plug-in which can be cut and pasted into #INPUT BEGINS section and edited to taste
# (PF_INT, "p0", "_INT:", 0), # PF_INT8, PF_INT16, PF_INT32 similar but no difference in Python.
# (PF_FLOAT, "p02", "_FLOAT:", 3.141),
# (PF_STRING, "p03", "_STRING:", "foo"), # alias PF_VALUE
# (PF_TEXT, "p04", "TEXT:", "bar"),
# # PF_VALUE
# # Pick one from set of choices
# (PF_OPTION,"p1", "OPTION:", 0, ["0th","1st","2nd"]), # initially 0th is choice
# (PF_RADIO, "p16", "RADIO:", 0, (("0th", 1),("1st",0))), # note bool indicates initial setting of buttons
# # PF_RADIO is usually called a radio button group.
# # SLIDER, ADJUSTMENT types require the extra parameter of the form (min, max, step).
# (PF_TOGGLE, "p2", "TOGGLE:", 1), # initially True, checked. Alias PF_BOOL
# # PF_TOGGLE is usually called a checkbox.
# (PF_SLIDER, "p3", "SLIDER:", 0, (0, 100, 10)),
# (PF_SPINNER, "p4", "SPINNER:", 21, (1, 1000, 50)), # alias PF_ADJUSTMENT
# # Pickers ie combo boxes ie choosers from lists of existing Gimp objects
# (PF_COLOR, "p14", "_COLOR:", (100, 21, 40) ), # extra param is RGB triple
# # PF_COLOUR is an alias by aussie PyGimp author lol
# (PF_IMAGE, "p15", "IMAGE:", None), # should be type gimp.image, but None works
# (PF_FONT, "p17", "FONT:", 0),
# (PF_FILE, "p18", "FILE:", 0),
# (PF_BRUSH, "p19", "BRUSH:", 0),
# (PF_PATTERN, "p20", "PATTERN:", 0),
# (PF_GRADIENT, "p21", "GRADIENT:", 0),
# (PF_PALETTE, "p22", "PALETTE:", 0),
# (PF_LAYER, "p23", "LAYER:", None),
# (PF_CHANNEL, "p24", "CHANNEL:", None), # ??? Usually empty, I don't know why.
# (PF_DRAWABLE, "p25", "DRAWABLE:", None),
# # Mostly undocumented, but work
# (PF_VECTORS, "p26", "VECTORS:", None),
# (PF_FILENAME, "p27", "FILENAME:", 0),
# (PF_DIRNAME, "p28", "DIRNAME:", 0)
# # PF_REGION might work but probably of little use. See gimpfu.py.