It is currently Sun Jun 30, 2024 1:36 am


All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Double Curve Bend using paths/vectors
PostPosted: Tue Jan 02, 2024 10:12 pm  (#1) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
Double? or Quadruple? Well I called it double because the curve bend tool in GIMP does 2 curves upper and lower.

I did some work and like manually pixel mapping to reproduce curve bend but better with upper and lower of different x-ranges.
This is handy for mapping a straight mugs (coffee mugs). But then I thought what if the mug was like slightly rounded.

That's when I though of double curve bend. By using 2 paths/vectors (1 for upper and lower curves for bottom and top of mug named "horizontal", and 1 for right and left curves of mug named "vertical"). It's important that these paths are named as such. And also when you run plug-in make sure you make the layer/content you want to map is active

Some images and hopefully you get what I what this plug-in does.
1. Have two paths/vectors, each with 2 strokes either top and bottom or left and right named 'horizontal' and 'vertical'
Image
2. Have some art/design on a layer that you want to map using the above paths.
Image
3. Run plug-in
Image
4. Plug-in result showing this
Image
5. Copy background layer, put it on very top and set mode to DARKEN-ONLY and then change opacity to taste.
Image

Here's the plug-in code:
#!/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):
    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 = 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 = []
    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,0.01)
        leftxpoint,leftypoint,slope,valid2 = pdb.gimp_vectors_stroke_get_point_at_dist(leftvectors,leftstroke,leftdist,0.01)
        if valid1 == TRUE and valid2 == TRUE:
            y_values.append(rightypoint)
            y_values2.append(leftypoint)
            rightcurvex.append(rightxpoint)
            leftcurvex.append(leftxpoint)
        rightdist += rightdiv
        leftdist += leftdiv
    return [y_values,y_values2,rightcurvex,leftcurvex]
def get_curve_horizontal(image):
        #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)
    upperdiv = float(upper_length) / longpath/2.0
    lowerdiv = float(lower_length) / longpath/2.0
    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:
        upperxpoint,upperypoint,slope,valid1 = pdb.gimp_vectors_stroke_get_point_at_dist(uppervectors,upperstroke,upperdist,0.01)
        lowerxpoint,lowerypoint,slope,valid2 = pdb.gimp_vectors_stroke_get_point_at_dist(lowervectors,lowerstroke,lowerdist,0.01)
        if valid1 == TRUE and valid2 == TRUE:
            x_values.append(upperxpoint)
            x_values2.append(lowerxpoint)
            uppercurvey.append(upperypoint)
            lowercurvey.append(lowerypoint)
        upperdist += upperdiv
        lowerdist += lowerdiv
    return [x_values,x_values2,uppercurvey,lowercurvey]
def python_my_manual_curve_bend_v10(image,layer):
    pdb.gimp_message("running")
    starttime = time.time()
    #saves the png to load it up later not sure why but it's faster
    png_full_path = get_temp_file_name_with_extension(".png") #this gets a temporary file name with .png
    maplayer = layer
    pdb.file_png_save_defaults(image,maplayer,png_full_path,png_full_path)
    pdb.gimp_image_remove_layer(image,maplayer)

   
    x_values,x_values2,upper_y,lower_y = get_curve_horizontal(image) #move it here so we only read it once per template
    y_values,y_values2,right_x,left_x = get_curve_vertical(image)
   
    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)
    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
    srcHeight = layer.height
    srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,False, False)
    src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
    newWidth = dest.width
    newHeight = dest.height
    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
    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 = math.sqrt((nx2 - nx1)**2 + (ny2 - ny1)**2)
        xstep = 1.0*(nx2-nx1)/distance
        ystep = 1.0*(ny2-ny1)/distance
        srcxstep = 0
        srcystep = 1.0*(oy2-oy1)/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 = 0
        intdist = int(distance)
        loopintdist = intdist
        for d in range(0,loopintdist): #d represent steps in pixels dm is main d
            y = int(offset_y+oy1+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) * p_size
            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*1.0/intdist*h)
            leftx = left_x[h_index]
            rightx = right_x[h_index]
            newx = int(leftx + xi/w*(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) * p_size
            dest_pixels[dest_pos : dest_pos + p_size] = newval
    #[ ... 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.


What's really weird when I was testing was that I found that if I apply the work using fast pixel operations reading from the design layer itself it's super slow compared to when it's loaded using python syntax as a new layer. So in order to make it faster I had to export the design layer as a temporary png file then reload it using python syntax then the fast pixel operation is fast again. really weird I don't know how to explain that.
But this works and does what I want.

Attached is the .xcf I used.

Reference (cup image) used: https://unsplash.com/photos/white-and-b ... dDJdOlA3CY


Attachments:
File comment: mycup.xcf sample file to test plug-in with
mycup.xcf [2.5 MiB]
Downloaded 62 times

_________________
TinT


Last edited by trandoductin on Wed Jan 03, 2024 8:18 am, edited 1 time in total.
Share on Facebook Share on Twitter Share on Orkut Share on Digg Share on MySpace Share on Delicious Share on Technorati
Top
 Post subject: Re: Double Curve Bend using paths/vectors
PostPosted: Tue Jan 02, 2024 10:28 pm  (#2) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
Ok just having with above plug-in and I thought I should try to bend the reflection of the cup as well.
here it is
Image

_________________
TinT


Top
 Post subject: Re: Double Curve Bend using paths/vectors
PostPosted: Tue Jan 02, 2024 11:16 pm  (#3) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
This version saves in .jpg then loads it and add alpha instead of png (so it's a little faster)
And change the get_curve_horizontal function to take a subdivide parameter which specifies a factor for example 1.1 will give 110 values for curve length of 100 so that all pixels are covered when we map using vertical paths. If the path is really variant and produces holes, we can increase this factor.
Here's the code:
#!/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):
    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 = 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 = []
    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,0.01)
        leftxpoint,leftypoint,slope,valid2 = pdb.gimp_vectors_stroke_get_point_at_dist(leftvectors,leftstroke,leftdist,0.01)
        if valid1 == TRUE and valid2 == TRUE:
            y_values.append(rightypoint)
            y_values2.append(leftypoint)
            rightcurvex.append(rightxpoint)
            leftcurvex.append(leftxpoint)
        rightdist += rightdiv
        leftdist += leftdiv
    return [y_values,y_values2,rightcurvex,leftcurvex]
def get_curve_horizontal(image,subdivide):
        #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)
    upperdiv = float(upper_length) / longpath/subdivide
    lowerdiv = float(lower_length) / longpath/subdivide
    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:
        upperxpoint,upperypoint,slope,valid1 = pdb.gimp_vectors_stroke_get_point_at_dist(uppervectors,upperstroke,upperdist,0.01)
        lowerxpoint,lowerypoint,slope,valid2 = pdb.gimp_vectors_stroke_get_point_at_dist(lowervectors,lowerstroke,lowerdist,0.01)
        if valid1 == TRUE and valid2 == TRUE:
            x_values.append(upperxpoint)
            x_values2.append(lowerxpoint)
            uppercurvey.append(upperypoint)
            lowercurvey.append(lowerypoint)
        upperdist += upperdiv
        lowerdist += lowerdiv
    return [x_values,x_values2,uppercurvey,lowercurvey]
def python_my_manual_curve_bend_v10(image,layer):
    pdb.gimp_message("running")
    starttime = time.time()
    #saves the png to load it up later not sure why but it's faster
    maplayer = layer
    #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)

   
    x_values,x_values2,upper_y,lower_y = get_curve_horizontal(image,1.1) #1.1 subdivide is 110 to 100 should be plenty to cover all pixels
                                                                         #unless the vertical strips are really variant then we can increase subdivide
    y_values,y_values2,right_x,left_x = get_curve_vertical(image)
   
    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.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
    srcHeight = layer.height
    srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,False, False)
    src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
    newWidth = dest.width
    newHeight = dest.height
    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
    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 = math.sqrt((nx2 - nx1)**2 + (ny2 - ny1)**2)
        xstep = 1.0*(nx2-nx1)/distance
        ystep = 1.0*(ny2-ny1)/distance
        srcxstep = 0
        srcystep = 1.0*(oy2-oy1)/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 = 0
        intdist = int(distance)
        loopintdist = intdist
        for d in range(0,loopintdist): #d represent steps in pixels dm is main d
            y = int(offset_y+oy1+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) * p_size
            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*1.0/intdist*h)
            leftx = left_x[h_index]
            rightx = right_x[h_index]
            newx = int(leftx + xi/w*(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) * p_size
            dest_pixels[dest_pos : dest_pos + p_size] = newval
    #[ ... 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.

_________________
TinT


Top
 Post subject: Re: Double Curve Bend using paths/vectors
PostPosted: Wed Jan 03, 2024 6:44 am  (#4) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
Ok I found out it was the saving of file and loading it that made it fast but because my layer didn't have alpha channel that slowed it down maybe because it's different p_size from destination. anyways this code doesn't need to save file and reopen it.

Even faster version using local variable to point to pdb function names that are called inside while loops instead of call pdb. functions directly.
Large image runs in about 1.5 seconds.
#!/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_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
    srcHeight = layer.height
    srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,False, False)
    src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
    newWidth = dest.width
    newHeight = dest.height
    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
    #[ ... 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.

_________________
TinT


Top
 Post subject: Re: Double Curve Bend using paths/vectors
PostPosted: Wed Jan 03, 2024 8:50 am  (#5) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
Yep it's weird I tried another image it's slow again and it was fast again once I added in the saving and reopening the map layer so
I am gonna keep it as this version of code now
#!/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_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.

Image
Attached is scaled .xcf with paths to this image: https://unsplash.com/photos/three-stack ... qDJiEcvQ1w


Attachments:
File comment: small .xcf sample (because of size limit on forum)
mycup2-small.xcf [2.63 MiB]
Downloaded 67 times

_________________
TinT
Top
 Post subject: Re: Double Curve Bend using paths/vectors
PostPosted: Wed Jan 03, 2024 10:15 am  (#6) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
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.
Image

#!/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.


Attachments:
File comment: gimp-logo with double-curve roughly drawn
gimpchatdotcom.xcf [2.24 MiB]
Downloaded 57 times

_________________
TinT
Top
 Post subject: Re: Double Curve Bend using paths/vectors
PostPosted: Wed Jan 03, 2024 10:50 am  (#7) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 3999
Location: Canada
from the last example there's missing rendered pixels near the bottom of the "COM" wording.
So this slow version subdivides to get no missing pixels.
Image
#!/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)) * 3
    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,0.1)
        leftxpoint,leftypoint,slope,valid2 = pdb_gimp_vectors_stroke_get_point_at_dist(leftvectors,leftstroke,leftdist,0.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) * 3
    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,0.1)
        lowerxpoint,lowerypoint,slope,valid2 = pdb_gimp_vectors_stroke_get_point_at_dist(lowervectors,lowerstroke,lowerdist,0.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) #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 xl in range(0,looplen*3):
        xi = int(xl/3.0)
        #pdb.gimp_progress_update(float(xi)/len(x_values)-1) #user friendliness
        ox1 = int(ox); ox+=oxinc/3 #(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 dl in range(0,loopintdist*3): #d represent steps in pixels dm is main d
            d = dl/3.0
            y = int(srcystepinc);srcystepinc+=srcystep/3
            #[ ... 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.

_________________
TinT


Top
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


   Similar Topics   Replies 
No new posts Script-fu Curve Bend

4

No new posts Attachment(s) Curve Bend (warping text)

6

No new posts Plugin or Script to bend/curve text

17

No new posts Curve Bend tool called from python giving error not very consistent.

0

No new posts Attachment(s) Bend Text-Logo as helper Ofn-bend-path

75



* Login  



Powered by phpBB3 © phpBB Group