GIMP Plugin for automating the boilerplate involved in colorizing manga pages my way
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
"""Simple GIMP helper to automate the boierplate of my approach to colorizing | |
manga pages. | |
Usage: | |
1. Run this plugin from "Image > Start Colorizing..." | |
2. Select the regions to be colored using whatever approach you find works | |
best. This script will preserve the original image as the bottom layer | |
(hidden below a solid white layer) so it can easily be used as input for | |
selection methods which don't play nicely with transparency. | |
3. Switch to the "Colors" layer and bucket-fill your selections. | |
4. Use a similar process with pure white, targeting the "Fluids" layer, | |
to fill in translucent droplets like water or sweat. | |
5. If any characters have blush lines, unlock the "Lines" and "Blush Lines" | |
layers and cut-and-paste the lines from one to the other. | |
6. If the style calls for a "glow" to the blush, select the blush lines | |
feather the selection by 10 or 20 pixels, and then bucket fill the color | |
from the "Blush Lines Color" layer into the selection on the | |
"Blush Glow" layer. | |
Reference materials used: | |
- https://www.ibm.com/developerworks/library/os-autogimp/ | |
- https://www.gimp.org/docs/python/ | |
- http://zwell.net/content/pygimp.html | |
- https://www.gimp-forum.net/Thread-Debugging-python-fu-scripts-in-Windows | |
- https://stackoverflow.com/q/10221926/435253 | |
""" | |
__author__ = "Stephan Sokolow (deitarion/SSokolow)" | |
__license__ = "GNU GPL 2.0 or later" | |
from contextlib import contextmanager | |
# pylint: disable=import-error | |
from gimpfu import gimp, main, pdb, register | |
from gimpfu import NORMAL_MODE, RGBA_IMAGE, SCREEN_MODE | |
from gimpfu import FOREGROUND_FILL, WHITE_FILL | |
@contextmanager | |
def undo_group(image): | |
"""Simple context manager to reliably undo-group a set of operations.""" | |
pdb.gimp_image_undo_group_start(image) | |
try: | |
yield | |
finally: | |
pdb.gimp_image_undo_group_end(image) | |
class Image(object): # pylint: disable=too-few-public-methods | |
"""Transient helper to perform a bunch of operations on the same image""" | |
def __init__(self, image): | |
self.image = image | |
self.w = pdb.gimp_image_width(image) | |
self.h = pdb.gimp_image_height(image) | |
# pylint: disable=too-many-arguments | |
def add_layer(self, name, fill=None, opacity=100, mode=NORMAL_MODE, | |
parent=None, copy_from=None): | |
"""Deduplicating wrapper for adding layers""" | |
if copy_from is not None: | |
layer = pdb.gimp_layer_copy(copy_from, 0) | |
pdb.gimp_item_set_name(layer, name) | |
else: | |
layer = pdb.gimp_layer_new(self.image, | |
self.image.width, self.image.height, | |
RGBA_IMAGE, name, opacity, mode) | |
if fill is not None: | |
if isinstance(fill, int): | |
pdb.gimp_drawable_fill(layer, fill) | |
else: | |
orig_fg = pdb.gimp_context_get_foreground() | |
try: | |
pdb.gimp_context_set_foreground((255, 0, 0)) | |
pdb.gimp_drawable_fill(layer, FOREGROUND_FILL) | |
finally: | |
pdb.gimp_context_set_foreground(orig_fg) | |
pdb.gimp_item_set_lock_content(layer, True) | |
pdb.gimp_image_insert_layer(self.image, layer, parent, 0) | |
return layer | |
def plugin_main(timg, tdrawable): | |
"""Expand a single layer into the colorization template I use""" | |
# Ensure that the image is in RGB mode to save a manual step | |
if not pdb.gimp_drawable_is_rgb(tdrawable): | |
pdb.gimp_image_convert_rgb(timg) | |
img = Image(timg) | |
# Get the bottom layer's ID | |
lyr_orig_image = gimp.Item.from_id(pdb.gimp_image_get_layers(timg)[-1][-1]) | |
pdb.gimp_item_set_lock_content(lyr_orig_image, True) | |
# Insert a white layer above it | |
img.add_layer("White Background", fill=WHITE_FILL) | |
# Insert a layer for colors above that | |
lyr_colors = img.add_layer("Colors") | |
pdb.gimp_item_set_lock_content(lyr_colors, False) | |
# Insert a half-opacity for fluids above that | |
img.add_layer("Fluids", opacity=50) | |
# Insert a Color-to-Alpha'd copy of the line art above that | |
lyr_lines = img.add_layer("Lines", copy_from=lyr_orig_image) | |
pdb.gimp_item_set_lock_content(lyr_lines, False) | |
pdb.plug_in_colortoalpha(timg, lyr_lines, (255, 255, 255)) | |
pdb.gimp_item_set_lock_content(lyr_lines, True) | |
# Insert layer group for drawing blushes | |
lyr_grp_blush = pdb.gimp_layer_group_new(timg) | |
pdb.gimp_item_set_name(lyr_grp_blush, "Blush") | |
pdb.gimp_image_insert_layer(timg, lyr_grp_blush, None, 0) | |
img.add_layer("Blush Lines", parent=lyr_grp_blush) | |
img.add_layer("Blush Lines Color", parent=lyr_grp_blush, | |
mode=SCREEN_MODE, fill=(255, 0, 0)) | |
img.add_layer("Blush Glow", parent=lyr_grp_blush, opacity=50) | |
# NOTE: All layers except "colors" start locked to avoid accidents but | |
# "Fluids", "Blush Lines", and "Blush Glow" are intended to be | |
# added to. | |
# | |
# Likewise, it's important to not modify "Lines" under normal | |
# circumstances, but it's necessary when moving portions of its | |
# content to the "Blush Lines" layer. | |
# Focus the original image's layer, because I like to start by using | |
# the magic wand select tool. | |
pdb.gimp_image_set_active_layer(timg, lyr_orig_image) | |
desc = ("Helper to automate the boierplate of my approach to colorizing manga" | |
" pages") | |
author = "Stephan Sokolow" | |
register( | |
"manga_coloring_helper", | |
desc, desc, | |
author, author, "2019", | |
"<Image>/Image/Start Colorizing...", | |
"RGB*, GRAY*, INDEXED*", | |
[], [], plugin_main | |
) | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment