#!/usr/bin/env python # -*- coding: utf-8 -*- # Screenshot Tearoff Effect # Copyright (c) 2008 Edgar D'Souza # Contact: edgar.b.dsouza@gmail.com # --------------------------------------------------------------------- # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . #---------------------------------------------------------------------- #Version: 0.1 - initial release. # Known issues: # - hasn't been tested on anything except the development platform - my laptop :) # # GIMP Python info taken from: http://www.gimp.org/docs/python/index.html # # This script was developed and tested under the following environment: # - Ubuntu Linux 7.10 # - Python 2.5.1 # - GIMP 2.4.2 # - gimp-python 2.4.2-0ubuntu0.7.10.1 # - libgimp2.0 2.4.2-0ubuntu0.7.10.1 # # Before invoking the plugin: # 1. Capture a new screenshot, or open an existing image file. # 2. Select the part to keep, making the selection a little larger than what # you want to keep. GIMP does appear to distress the selection "outward", # but verify that you're not losing image data that you want to keep. Since # a few more pixels than needed are probably not going to be fatal :) I'm # suggesting selecting a little more than you need to keep. from gimpfu import * #Helper function - writes debug messages to a log file. def log(msg, init_file=False): """Opens a log file, writes the string in the msg argument, closes the file. The optional init_file argument, if True, (re)initializes the file. """ filename = '/tmp/gimp_plugin_tearoff.log' #Uncomment and customize the next line if you're trying to get it running on Windows... #filename = r"C:\gimp_plugin_tearoff.log" dumpstr = "tearoff: " + msg + '\n' if init_file: write_mode = 'w' else: write_mode = 'a' tmpfile = open(filename, write_mode) tmpfile.write(dumpstr) tmpfile.close() #Observation: the log file contains only one startup and invocation block, no matter #how many times I invoke the plugin on a single image window. This implies that it's #being torn down and re-initialized after every invocation. A bit expensive, perhaps, #but I suppose it enables better garbage collection...just a guess. #The main plugin function that manipulates the image. def tearoff(the_img, the_drawable, distort_threshold, distort_spread, distort_granularity, distort_smooth_level, add_alpha, add_dropshadow, ds_offset_x, ds_offset_y, ds_blur_radius, ds_color, ds_opacity, ds_allow_resizing, crop ): """Applies the tearoff effect to the image. Parameters expected are those defined in the plugin_params_list below, plus: the_img: the open, currently active image (auto-passed by gimp-fu) the_drawable: drawable layer of the image (-do-) """ # 1A. Check that there is a valid selection. try: sel_empty = pdb.gimp_selection_is_empty(the_img) #See "name weirdness" note below. log('pdb.gimp_selection_is_empty returned:' + str(sel_empty)) except BaseException, error: log('Calling pdb.gimp_selection_is_empty: error: ' + str(error)) if sel_empty: log("Error: called without a selection for the image!") gimp.message('This plugin needs a selection in the image; please select an area of the image and re-run the plugin.\n\nPlugin will now exit.') return False # 1B. Start an undo group on the image, so that all steps done in the plugin can be undone in # one user undo step (Ctrl-Z) try: pdb.gimp_image_undo_group_start(the_img) except BaseException, error: log('Calling pdb.gimp_image_undo_group_start: error: ' + str(error)) # 2. Distress the selection, passing parameters obtained from UI. GIMP is smart # - it only distresses the edge of the selection that has image area beyond it # (i.e. the selection edge is NOT up against the edge of the image). try: pdb.script_fu_distress_selection(the_img, the_drawable,distort_threshold, distort_spread, distort_granularity, distort_smooth_level, True, True) # The True values are to force smoothing horiz and vert; looks awful otherwise. except BaseException, error: log('Calling script_fu_distress_selection: error: ' + str(error)) # 3A. Found that re-inverting the selection in code after cutting out # image content doesn't work (whole layer is selected) so am saving # the active selection here and will load it later. try: saved_selection_channel = pdb.gimp_selection_save(the_img) except BaseException, error: log('Calling gimp_selection_save: error: ' + str(error)) # 3B. Invert the selection, so what is selected can be deleted. try: pdb.gimp_selection_invert(the_img) except BaseException, error: log('Calling gimp_selection_invert: error: ' + str(error)) # 4. Check if image has alpha channel (transparency) or add it (GUI: Layer > # Transparency > Add Alpha Channel) if not add_alpha: log('Parameter passed from UI said not to add alpha channel') else: try: if the_drawable.has_alpha > 0: pass else: pdb.gimp_layer_add_alpha(the_drawable) except BaseException, error: log('Checking/adding alpha channel: error: ' + str(error)) # 5. Delete selection (leaving behind transparent/background-filled area). try: del_buf_name = pdb.gimp_edit_named_cut(the_drawable, "del_buf") pdb.gimp_buffer_delete(del_buf_name) #Couldn't find a 'delete selection' method... but this works? #Cutting to a named buffer, and then deleting it, avoids leaving the cut #image data on the clipboard, which ugly outcome you face when using just #plain gimp-edit-cut(). except BaseException, error: log('Trying to delete (inverted) selected area: error: ' + str(error)) # 6. Invert the selection again, selecting the part we want to keep. # 6A. Found that re-inverting the selection in code after cutting out # image content doesn't work (whole layer is selected) so I load # a selection that I saved earlier in step 3A. try: pdb.gimp_selection_none(the_img) pdb.gimp_selection_load(saved_selection_channel) except BaseException, error: log('Loading saved selection: error: ' + str(error)) # 7. Call Filters > Light and Shadow > Drop Shadow if not add_dropshadow: log('Parameter passed from UI said not to add drop shadow') else: try: pdb.script_fu_drop_shadow(the_img, the_drawable, ds_offset_x, ds_offset_y, ds_blur_radius, ds_color, ds_opacity, ds_allow_resizing) except BaseException, error: log('Calling script_fu_drop_shadow: error: ' + str(error)) # 8. Extend selection by extra area added by drop shadow plugin. log('Trying to grow selection to include drop shadow...') try: #Should grow it uniformly to the largest non-negative offset of #drop-shadow. grow_size = max(ds_offset_x, ds_offset_y) if grow_size > 0: pdb.gimp_selection_grow(the_img, grow_size) except BaseException, error: log('Calling gimp_selection_grow: error: ' + str(error)) # 9. Manually crop image if user has chosen to do so. # (Doing an auto-crop seems to eat up too much into the shadow...) if not crop: log('User-supplied UI param said not to crop image.') else: log('Trying to crop image...') try: #Due to previous operations, we're sure we have a selection... has_sel, x1, y1, x2, y2 = pdb.gimp_selection_bounds(the_img) pdb.gimp_image_crop(the_img, x2-x1, y2-y1, x1, y1) except BaseException, error: log('Cropping image: error: ' + str(error)) # 9B. End the undo group we started in step 1B. try: pdb.gimp_image_undo_group_end(the_img) except BaseException, error: log('Calling pdb.gimp_image_undo_group_end: error: ' + str(error)) #Register the plugin with GIMP. #For help/more info on params, read http://www.gimp.org/docs/python/index.html #Construct our list of params for register() in a verbose manner... params_list = [] #Params that we will pass to the selection-distort procedure: params_list.append( (PF_SPINNER, "distort_threshold", "(Selection) Distort - Threshold", 111, (1,255,1)) ) params_list.append( (PF_SPINNER, "distort_spread", "Distort - Spread", 8, (0,1000,1)) ) params_list.append( (PF_SPINNER, "distort_granularity", "Distort - Granularity (1 is low)", 4, (1,25,1)) ) params_list.append( (PF_SPINNER, "distort_smooth_level", "Distort - Smoothing Level", 5, (1,150,1)) ) #Parameters specific to this plugin: params_list.append( (PF_BOOL, "add_alpha", "Delete to transparency (add an alpha channel to image)?", True) ) params_list.append( (PF_BOOL, "add_dropshadow", "Add drop shadow?", True) ) #Parameters for the drop shadow (if user chooses to apply it): params_list.append( (PF_SPINNER, "ds_offset_x", "Drop Shadow - X axis offset (pixels)", 8, (-25,25,1)) ) params_list.append( (PF_SPINNER, "ds_offset_y", "Drop Shadow - Y axis offset (pixels)", 8, (-25,25,1)) ) params_list.append( (PF_SPINNER, "ds_blur_radius", "Drop Shadow - Blur Radius", 15, (0,1024,1)) ) params_list.append( (PF_COLOR, "ds_color", "Drop Shadow - Shadow Color", (0,0,0)) ) params_list.append( (PF_SLIDER, "ds_opacity", "Drop Shadow - Shadow Opacity", 80, (0,100,1)) ) params_list.append( (PF_TOGGLE, "ds_allow_resizing", "Drop Shadow - Allow resizing?", True) ) #Another parameter specific to this plugin: params_list.append( (PF_TOGGLE, "crop", "Crop image down to (selection plus drop shadow, if applied)?", True) ) log("About to register", init_file=True) #First invocation to log() in this script's run; empty out the log file. register( "screenshot_tearoff", "Applies an irregular 'tear-off' effect to the image at any selection edge that is NOT at the boundary of the image; deletes the remainder of the image, optionally applies a drop shadow to the 'to-keep' area, and optionally crops the image.", "Select the part of the image that you want to keep, before invoking this plugin. You can adjust the selection distort/distress parameters, and choose whether to apply a drop shadow and whether to crop the image after that.", "Edgar D'Souza (edgar.b.dsouza@gmail.com)", "(c) 2008 Edgar D'Souza, licensed under GPL v3 or later", "2008-10-26", "/Image/Te_ar-off", "RGB*, GRAY*", params_list, [], tearoff ) log("Registration finished") log("Calling main() to start the plugin running.") main()