#!/usr/bin/env python from gimpfu import * import random import gtk class ModeDialog(gtk.Window): def __init__ (self): self.w, self.h = 0, 0 ret = gtk.Window.__init__(self) vbox = gtk.VBox(False, 0) self.add(vbox) btn = gtk.Button("You are supposed to create all the layers for the animation before. Only one layer found. How many frames you want?") btn.connect("clicked", self.disappear) vbox.pack_start(btn, False, False, 0) btn.show() adjustment = gtk.Adjustment(50, 0, 10000, 1, 5, 0) self.spinbutton = gtk.SpinButton(adjustment) vbox.pack_start(self.spinbutton, False, False, 0) self.spinbutton.show() closeButton = gtk.Button("close", gtk.STOCK_CLOSE) closeButton.connect("clicked", self.disappear) vbox.pack_start(closeButton, False, False, 0) closeButton.show() vbox.show() self.show() return ret def disappear(self, widget) : gtk.main_quit() return False def snowflake(image, drawable, flake_number, brush_name, color, opacity, flake_size_1, flake_size_2, flake_fall_1, flake_fall_2, relation, wind_speed_1, wind_speed_2, wind_change_1, wind_change_2, angle_1, angle_2, position_1, position_2): # Get the list of layers in the image layers = image.layers if len(layers) == 1: r = ModeDialog() gtk.main() # Get the top-level layer top_layer = image.layers[0] # Duplicate the layer x times pdb.gimp_item_set_visible(top_layer, True) for i in range(r.spinbutton.get_value_as_int() - 1): duplicate = pdb.gimp_layer_new_from_drawable(top_layer, image) pdb.gimp_image_insert_layer(image, duplicate, None, (i + 1) * 2) layers = image.layers pdb.gimp_progress_init("Start adding snow...", None) pdb.gimp_context_push() # Translate parameters flake_size_min = min(flake_size_1, flake_size_2) flake_size_max = max(flake_size_1, flake_size_2) flake_fall_min = min(flake_fall_1, flake_fall_2) flake_fall_max = max(flake_fall_1, flake_fall_2) wind_speed_min = min(wind_speed_1, wind_speed_2) wind_speed_max = max(wind_speed_1, wind_speed_2) wind_change_min = min(wind_change_1, wind_change_2) wind_change_max = max(wind_change_1, wind_change_2) angle_min = min(angle_1, angle_2) angle_min = max(angle_min, -180) angle_max = max(angle_1, angle_2) angle_max = min(angle_max, 180) position_1 = min(position_1, image.width) position_1 = max(position_1, -image.width) position_1 = position_1 % image.width position_2 = min(position_2, image.width) position_2 = max(position_2, -image.width) position_2 = position_2 % image.width position_min = min(position_1, position_2) position_max = max(position_1, position_2) if (position_min == 0) and (position_max == 0): position_min = 0 position_max = image.width # Group actions as a whole pdb.gimp_image_undo_group_start(image) # Select the paint tool pdb.gimp_context_set_brush(str(brush_name)) pdb.gimp_context_set_foreground(color) pdb.gimp_context_set_opacity(opacity) # Loop through each layer and paint the point for i in range(flake_number): pdb.gimp_progress_update((i * 1.0) / flake_number) pdb.gimp_progress_set_text("Rendering flake #" + str(i + 1) + " over " + str(flake_number) + "...") flake_size = max(random.uniform(flake_size_min, flake_size_max), 1) flake_fall = random.uniform(flake_fall_min, flake_fall_max) # Relate to flake size flake_fall = ((flake_fall - flake_fall_min) * (1 - relation) + ((flake_size - flake_size_min) * relation * (flake_fall_max - flake_fall_min) / (flake_size_max - flake_size_min))) + flake_fall_min if flake_fall == 0: flake_fall = 1 wind_speed = random.uniform(wind_speed_min, wind_speed_max) wind_change = -1 angle = random.uniform(angle_min, angle_max) # A flake can fall from any frame j = random.randint(0, len(layers) - 1) # Flake coodinates x = position_min + (flake_size / 2 + (((i + 0.5) * ((position_max - position_min) + flake_size)) / flake_number)) if 0 < flake_fall: y = 0 - flake_size / 2 else: y = image.height + flake_size / 2 # Set the brush size pdb.gimp_context_set_brush_size(flake_size) pdb.gimp_context_set_brush_angle(angle) # We paint a flake till the flake is out of scope while ((0 < flake_fall) and (y < image.height + flake_size / 2)) or ((flake_fall <= 0) and (0 - flake_size / 2 < y)): if wind_change < 0: wind_change = random.randint(wind_change_min, wind_change_max) new_wind_speed = random.uniform(wind_speed_min, wind_speed_max) # The wind should not change too quickly new_wind_speed = min(new_wind_speed, wind_speed + 1) new_wind_speed = max(new_wind_speed, wind_speed - 1) wind_speed = new_wind_speed else: wind_change -= 1 layer = layers[j] # Make the layer active pdb.gimp_image_set_active_layer(image, layer) # Paint the point pdb.gimp_paintbrush_default(layer, 2, [x, y]) new_angle = pdb.gimp_context_get_brush_angle() + angle # More than 360 degrees is useless (and forbidden) new_angle = ((new_angle + 180) % 360) - 180 pdb.gimp_context_set_brush_angle(new_angle) # Flake new coodinates x += wind_speed if x < 0 - flake_size / 2: x = image.width + flake_size / 2 if image.width + flake_size / 2 < x: x = 0 - flake_size / 2 y += flake_fall j -= 1 if j < 0: j = len(layers) - 1 pdb.gimp_context_pop() pdb.gimp_image_undo_group_end(image) # Update the image display gimp.displays_flush() pdb.gimp_progress_end() pdb.plug_in_animationplay(image, drawable) # Register the GIMP Python-fu command register( "python_fu_snowflake", "Adds a snowflake fall effect on an existing animation", "Adds a snowflake fall effect on an existing animation", "Fabrice TIERCELIN", "Fabrice TIERCELIN", "2023", "/Filters/Animation/Snowflake fall on animation", "*", [ (PF_INT, "flake_number", "_Number of flakes that appear during the animation", 200), (PF_BRUSH, "brush_name", "Flake _brush", None), (PF_COLOR, "color", "Flake _color", (255, 255, 255)), (PF_SLIDER, "opacity", "Flake _opacity (0=transparent; 100=opaque)", 65, (1, 100, 1)), (PF_FLOAT, "flake_size_1", "Flake from this _size (in pixels)", 1.1), (PF_FLOAT, "flake_size_2", "...to this si_ze (in pixels)", 10.5), (PF_FLOAT, "fall_per_frame_1", "_Fall from this speed (pixels per frame)", 0.5), (PF_FLOAT, "fall_per_frame_2", "..._to this speed (pixels per frame)", 10.5), (PF_SLIDER, "relation", "_Relation between size and fall (0=not related; 1=analogous)", 0.85, (0, 1, 0.01)), (PF_FLOAT, "wind_speed_1", "_Wind from this force (in pixels, negative for left, positive for right)", -2.5), (PF_FLOAT, "wind_speed_2", "...to t_his force (in pixels, negative for left, positive for right)", 2.5), (PF_INT, "wind_change_1", "Win_d change from this time (frame number)", 1), (PF_INT, "wind_change_2", "...to th_is time (frame number)", 10), (PF_FLOAT, "angle_1", "Rotation from this _angle (in degrees)", -20.5), (PF_FLOAT, "angle_2", "...to this an_gle (in degrees)", 20.5), (PF_FLOAT, "position_1", "Falling from this _position (in pixels)", 0), (PF_FLOAT, "position_2", "...to this position (in pixels)", 0), ], [], snowflake) main()