Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
plug in bump map: Works manually, but not using a Python procedure
#1
Hello fellow Gimp users,

In order to format names that will appear on a game board, I've created a Python script in which for each group associated to a city name, one layer is a bump map and another one is a layer on which I'd like to apply the bump map.

I've been trying to use the procedure "plug-in-bump-map", but I cannot figure out why it's not applied when I use the script instruction (although no error is returned in the Python console), while it's ok when I manually use the gimp-filter-bump-map menu.
Code:
# remove any current selection
g.gimp_selection_none(img)

# set the parameters to be applied for embossing
azimuth = 135 ; elevation = 22 ; depth = 23

# apply the bump layer to the embossed_letters_layer
# (drawable,bumpmap,azimuth,elevation,depth,xofs,yofs,waterlevel,ambient,compensate,invert,type)
g.plug_in_bump_map(img, embossed_letters_layer, bump_layer, azimuth, elevation, depth, 0, 0, 0, 0, 1, 0, 0)

   
   

Is there something wrong in the parameter settings? I saw a discrepancy between the parameters mentioned in the procedure description (14 including "run-mode") and the expected parameters in the console (13 not including "run-mode"). Then, I didn't include any parameter for "run-mode".

Or is there something else, I'm not aware of... Frankly speaking, I'm quite newbie when it comes to Python programming for Gimp :-)

Thanks in advance for your ideas,

Cheers,

Linett.

--------
Here is my full code so far:
Code:
"""
Script to format city names from a list

There are 2 initial layers / group of layers:
- formatted_names: Group of layers on top to store the resulting formatted texts
- target_layer: This is a text layer with a text in the correct font-family, font-size and margins

For each item in the list:
1. Create a dedicated group of layers within the "formatted_names" group of gimp_layer_set_name,
copy the target layer and replace the text in the copied layer by the name of the current item
2. Create 3 different layers by duplicating the alpha selection from the copied layer:
a. glow behind the name,
b. bump layer, and
c. embossed letters on which the bump map is applied
3. Apply the bump layer to the embossed letters layers
4. Merge all the layers of the group
NB:
1. Make sure that the font setting are ok before launching the Script: foreground colour is #4d4020
2. Make sure that the font-family ("ShangriLaNFSmallCaps") and size are ok (60)
# format text box
# font-family: ShangriLaNFSmallCaps
# font-size: 60
# fill-colour: #4d4020
# bump map: 135, 22, 23
"""


# shortcut to avoid writing gimp.pdb all the time
g = gimp.pdb

# access to list of opened images
images = gimp.image_list()
# identify image opened last
img = images[0]
# get layers of images[0] (last opened image)
# the group of layers should be on top, with at least one textbox layer on top
layers = images[0].layers
print layers

# identify the group of layers ("formatted_names")
group_layer = layers[0]

# identify the layer to duplicate
target_layer = layers[1]
print target_layer

# set the height and width of the layers to be created
drawable_height = 161 #g.gimp_drawable_height(target_layer)
drawable_width = 702 #g.gimp_drawable_width(target_layer)

# provide the list of the city names to be formatted
i = 0
lst = ['Mont Saint-Michel']
print lst[i]

# create a list of group of layers where one group will be used for one city name
name_groups = []

# set lock alpha
g.gimp_layer_set_lock_alpha(target_layer, TRUE)

#####################################################################################
# 1. create a new layer group within the "formatted names" group of layers
name_groups.append(g.gimp_layer_group_new(img))
g.gimp_image_insert_layer(img, name_groups[i], group_layer, 0)  # insert this group of layers into the "formatted names" group of layers
g.gimp_item_set_name(name_groups[i], "g_"+lst[i]) # rename the group of layers as per the city name

# duplicate the target layer in the group of layers "formatted names" and set the text as per the list item
copied_layer = g.gimp_layer_copy(target_layer, TRUE)
g.gimp_image_insert_layer(img, copied_layer, name_groups[i], 0)
g.gimp_text_layer_set_text(copied_layer, lst[i]) #change the text in the textbox

#####################################################################################
# 2.a. create a glow behind the city name
# create a new layer called "glow"
glow = g.gimp_layer_new(img, drawable_width, drawable_height, RGBA_IMAGE, "glow", 90, NORMAL_MODE)
# set lock alpha
g.gimp_layer_set_lock_alpha(copied_layer, TRUE)
# copy the alpha channel to the selection
g.gimp_selection_layer_alpha(copied_layer)
# enlarge the selection by 6 pixels
g.gimp_selection_grow(img, 6)
# feather the selection by 5 pixels
g.gimp_selection_feather(img, 5)
# insert the glow layer behind the other layers of the group
g.gimp_image_insert_layer(img, glow, name_groups[i], 1)
# fill the selection with white
g.gimp_edit_fill(glow, WHITE_FILL)

#####################################################################################
# 2.b. create a bump map to emboss the letters of the city name
# create a new layer called "bump_layer"
bump_layer = g.gimp_layer_new(img, drawable_width, drawable_height, RGBA_IMAGE, "bump_layer", 100, NORMAL_MODE)
# copy the alpha channel to the selection
g.gimp_selection_layer_alpha(copied_layer)
# feather the selection by 5 pixels
g.gimp_selection_feather(img, 5)
# insert the bump layer on top of the other layers of the group
g.gimp_image_insert_layer(img, bump_layer, name_groups[i], 0) # 0 to add the layer on top or 1 to add the layer in the bottom
# fill the selection with white
g.gimp_edit_fill(bump_layer, WHITE_FILL)

#####################################################################################
# 2.c. create a layer of letters that will then be embossed
# create a new layer called "embossed_letters_layer"
embossed_letters_layer = g.gimp_layer_new(img, drawable_width, drawable_height, RGBA_IMAGE, "embossed_letters_layer", 100, NORMAL_MODE)
# copy the alpha channel to the selection
g.gimp_selection_layer_alpha(copied_layer)
# create a new layer called "embossed_letters_layer"
g.gimp_image_insert_layer(img, embossed_letters_layer, name_groups[i], 0) # 0 to add the layer on top or 1 to add the layer in the bottom
# fill the selection with foreground color
g.gimp_edit_fill(embossed_letters_layer, 0) # 0 is for FILL-FOREGROUND

#####################################################################################
# 3. Apply the bump layer to the embossed letters layer
# hide some layers
g.gimp_item_set_visible(copied_layer, FALSE)
g.gimp_item_set_visible(bump_layer, FALSE)
# set the embossed letters layer as active layer
g.gimp_image_set_active_layer(img, embossed_letters_layer)

# remove any current selection
g.gimp_selection_none(img)

# set the parameters to be applied for embossing
azimuth = 135 ; elevation = 22 ; depth = 23

# apply the bump layer to the embossed_letters_layer
# (drawable,bumpmap,azimuth,elevation,depth,xofs,yofs,waterlevel,ambient,compensate,invert,type)
g.plug_in_bump_map(img, embossed_letters_layer, bump_layer, azimuth, elevation, depth, 0, 0, 0, 0, 1, 0, 0)

#####################################################################################
#4. Merge all the layers of the group
Reply
#2
Unfortunately I can only confirm the problem. The Python API is OK on Gimp 2.8.

In the case at hand, you will notice the big "G" icon in the menu entry. This means that the function is implemented with GEGL, which is the new "core" in Gimp. The plugin is a backward-compatibility facade that calls the GEGL code but in this case it doesn't work (it works for others, for instance the Gaussian blur).

However since this is GEGL, you can call the GEGL function directly, but this is somewhat more complicated.
Reply
#3
Thank you Ofnuts for this feedback. I suspected something like a « missing link » somewhere, as it works ok from the Filter menu. Wasn’t aware of how Gimp 2.10 was built and its new core…

I initially wrote the script in Scheme, but the bump map part wasn’t working either, then I guess it was the same issue.

If I understand well, I need to rewrite the bump map instruction in a way that it points to the implemented function with GEGL, get the result running GEGL (it seems using the function « gegl.gegl_render_op »), and passing it as a result to my Python function. 

Yes, it’s somewhat more complicated than for other « already linked » functions. This makes us realize the great work done so far on the backward-compatibility!

Will try to adapt the code from http://gimpchat.com/viewtopic.php?f=9&t=...10#p255005, after following the thread you referred me too. If successful, will post the corresponding code.

Thanks a lot!
Reply
#4
OK, I've tried my best given my current knowledge (actually lack of knowledge when it comes to pygtk, gtk C...) to replicate the procedure provided by Kevin:
- Updated the "load_library" function to include macOS
- Added a reference to the bump_layer in the main function.
- Reviewed the required parameters from the bump_map function based on the description at the GEGL project.

My understanding is that the bump_layer has to be passed as a GeglBuffer to be used in the GeglOperation: Could someone help me doing this part? The examples I was working on only had an "input" and an "output", while here, there is an "auxiliary" (bump_layer) as well.
The function where I'm stuck is:
Code:
def linettridge_gegl_graph(image, drawable, bump_layer, gegl_graph_string):
   class GeglBuffer(Structure):
       pass
   drawable_id = drawable.ID
   bump_layer_id = bump_layer.ID
   #
   gegl = load_library ('libgegl-0.4')
   sucess = gegl.gegl_init (None, None, None) # related to GeglBuffer: input, aux, output?
   #
   gimp.gimp_drawable_get_shadow_buffer.restype = POINTER (GeglBuffer)
   gimp.gimp_drawable_get_buffer.restype        = POINTER (GeglBuffer)
   #
   x = c_int (); y = c_int (); w = c_int (); h = c_int ()
   non_empty,x,y,w,h = pdb.gimp_drawable_mask_intersect (drawable)
   args = [b"string", c_char_p (gegl_graph_string), c_void_p ()]
   #
   if non_empty:
       source = gimp.gimp_drawable_get_buffer (drawable_id)
       auxiliary = gimp.gimp_drawable_get_buffer (bump_layer_id)
       target = gimp.gimp_drawable_get_shadow_buffer (drawable_id)
       sucess = gegl.gegl_render_op (source, auxiliary, target, "gegl:gegl", *args)
       gegl.gegl_buffer_flush (target)
       gimp.gimp_drawable_merge_shadow (drawable_id, PushUndo = True)
       gimp.gimp_drawable_update (drawable_id, x, y, w, h)
       gimp.gimp_displays_flush ()

Thanks in advance for any advice.
------------

The full code is here (NB: from the time being, I'm running it from the Python console directly)
Code:
########
g = gimp.pdb
# access to list of opened images
images = gimp.image_list()
# identify image opened last
img = images[0]
# get layers
layers = images[0].layers
#
target_layer = layers[0]
print target_layer
bump_layer = layers[1]
print bump_layer
#
########
from gimpfu import *
import pygtk
import gtk
from sys import argv, platform
from ctypes import *
#
def load_library(library_name):
   if platform == "linux" or platform == "linux2":
       library_name = library_name + '.so.0'
   elif platform == "win32":
       from ctypes.util import find_library
       library_name = find_library(library_name + "-0")
   elif platform == "darwin":
       from ctypes.util import find_library
       library_name = find_library(library_name + ".0.dylib")
   else:
       raise BaseException("TODO")
   return CDLL(library_name)

gimp = load_library('libgimp-2.0')

def linettridge_gegl_graph(image, drawable, bump_layer, gegl_graph_string):
   class GeglBuffer(Structure):
       pass
   drawable_id = drawable.ID
   bump_layer_id = bump_layer.ID
   #
   gegl = load_library ('libgegl-0.4')
   sucess = gegl.gegl_init (None, None, None) # related to GeglBuffer: input, aux, output?
   #
   gimp.gimp_drawable_get_shadow_buffer.restype = POINTER (GeglBuffer)
   gimp.gimp_drawable_get_buffer.restype        = POINTER (GeglBuffer)
   #
   x = c_int (); y = c_int (); w = c_int (); h = c_int ()
   non_empty,x,y,w,h = pdb.gimp_drawable_mask_intersect (drawable)
   args = [b"string", c_char_p (gegl_graph_string), c_void_p ()]
   #
   if non_empty:
       source = gimp.gimp_drawable_get_buffer (drawable_id)
       auxiliary = gimp.gimp_drawable_get_buffer (bump_layer_id)
       target = gimp.gimp_drawable_get_shadow_buffer (drawable_id)
       sucess = gegl.gegl_render_op (source, auxiliary, target, "gegl:gegl", *args)
       gegl.gegl_buffer_flush (target)
       gimp.gimp_drawable_merge_shadow (drawable_id, PushUndo = True)
       gimp.gimp_drawable_update (drawable_id, x, y, w, h)
       gimp.gimp_displays_flush ()

########@
#define the parameters of the GEGL function: cf. GEGL_PROPERTIES
type="linear"
compensate=TRUE
invert=FALSE
tiled=FALSE
azimuth=135.0
elevation=22.0
depth=23
offset_x=0
offset_y=0
waterlevel=0.0
ambient=0.0
gegl_graph_string="gegl:bump_map type=%s compensate=%d invert=%d tiled=%d azimuth=%f elevation=%f depth=%d offset_x=%d offset_y=%d waterlevel=%f ambient=%f" % (type, compensate, invert, tiled, azimuth, elevation, depth, offset_x, offset_y, waterlevel, ambient)
linettridge_gegl_graph(img, target_layer, bump_layer, gegl_graph_string)
Reply
#5
Here's a curious thing. The following script-fu works for me using GIMP 2.10.20 on Windows 10
Code:
(define this_menu "<Image>/Quick")

(define    (script-fu_kp24_bump_map image drawable bump_map)
  ;; Start undo group.
  (gimp-image-undo-group-start image)

  (let* (
          (selection (car (gimp-image-get-selection image)))
          (vectors 0)
        )

    (plug-in-bump-map RUN-NONINTERACTIVE image drawable bump_map 135 45 3 0 0 0.0 0 0 0 0)
    
    ;; Flush display.
    (gimp-displays-flush)

    ;; End undo group.
    (gimp-image-undo-group-end image)
  )
)

(script-fu-register "script-fu_kp24_bump_map"
                    _"Bump Map..."
                    "Bump Map"
                    "Me"
                    "Me"
                    "24.3.2015"
                    "*"
                    SF-IMAGE        "Image"        0
                    SF-DRAWABLE         "Drawable"      0
                    SF-DRAWABLE         "Bump-Map"      0
                    )
(script-fu-menu-register "script-fu_kp24_bump_map" this_menu)

It's got some unnecessary code as I took an existing script as a template for speed of prototyping.
Reply
#6
Since LinettRidge is using MacOS.
Mac version Gimp from gimp.org is 2.10 14 and backward compatibility was broken for bump-map. Using Gimp 2.10.20 and backward compatibility is very much improved. Previously using Windows or linux the 'fix' was 'borrow' the old Gimp 2.8 bumpmap plugin or go back to Gimp 2.10.12 so the script / plugin would work. Probably not possible with MacOS but Partha http://www.partha.com does have a 2.10.20 Mac version.
Reply
#7
(09-08-2020, 05:17 PM)rich2005 Wrote: Since LinettRidge is using MacOS.
Mac version Gimp  from gimp.org  is 2.10 14 and backward compatibility was broken for bump-map.  Using Gimp 2.10.20 and backward compatibility is very much improved. Previously using Windows or linux the 'fix' was 'borrow' the old Gimp 2.8 bumpmap plugin or go back to Gimp 2.10.12 so the script / plugin would work. Probably not possible with MacOS but Partha http://www.partha.com does have a 2.10.20 Mac version.

Thank you Kevin, thank you rich2005 for your feedback.

I need to get going on my project (almost complete and all in Gimp 2.10...). It seems that the Partha version involves installing X11, and I've no idea if this could be a "sandboxed" installation...
The simplest for me is to use an old machine where I re-install Gimp 2.8 to perform this specific embossing operations, get the results as PNG files and load them as new layers in my master file.

Just wanted to give a try at building a script for Gimp 2.10 to call the GEGL function directly, as maybe it could have help others using this bump-map plug-in or also using GEGL functions requiring auxiliary layers.
Reply


Forum Jump: