モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

GIMPでアルファ継承(擬似クリッピングレイヤー)

スクリプトで実装という。

つまるところ単純にグループレイヤーの最下層のアルファ値でマスクを作り、その他の兄弟レイヤにマスクとして適用するというモノです。

最下層のレイヤを描き変え終わったら、このスクリプトを走らせてマスクを同期させる、という原始的なものでございます。

マスクをかけるレイヤを制限するには、必要なレイヤにだけマスクを作るか、リンク指定するなどして、スクリプト実行時にソレに合わせた適切なオプションを選択して実行してください。

なお、トップレベルレイヤでは大抵の場合、最下層が全不透明の背景であることが多いため、このsync-layergroup-maskは単純に機能しないように作ってあります。

その他、真下のレイヤのアルファ値と同期とかのスクリプトも一緒に混ぜてあります。

バグがあるとは思うんですが適当に修正してください(^^;)

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#[license] GPLv3
#[plugin]
#[name] sync-layer-mask
#[desc] 
# レイヤのマスクをワンタッチで同期させるスクリプト
# 指定された正規表現にマッチしたレイヤを適合させたり
# 兄弟レイヤのマスクを統一したり
# アクティブレイヤをベースレイヤ(マスクが存在しない場合は不透明部分)と同じマスクにすることができる。
# 兄弟レイヤやベースレイヤの関係性は、基本的にレイヤの名前から決定される.
#
# なお、sync-layergroup-maskについては若干挙動が異なり、「アルファ相続」「クリッピングレイヤー」
# 的な挙動を行うためのものになっている。
# 所属するレイヤグループの最下層のレイヤのアルファ値と、その他の兄弟レイヤのマスクを同期するというものだ。
# 子グループについては「再帰して処理を行う」
# トップレベルレイヤについてはこのsync-layergroup-maskは機能しない(常にではないが大抵の場合、
# 最下層が完全に不完全なキャンバスな事が多く、まず機能せず混乱を招くだけという考え)
#
#[version]
#0.1 初期リリース
#0.2 グループの最下層レイヤーのアルファ値に同期させる(クリッピングレイヤー|アルファ相続)関数を追加
#[end]

#  このプログラムはGPLライセンスver3で公開します。
# 
#   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 may have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.           
#
#   Copyright (C) 2015 dothiko(http://dothiko.blog.fc2.com/) 


from gimpfu import *
import re

SIBLING_PATTERN='^%s(-\S+)?$'

def python_fu_sync_layer_mask(a_img,a_drawable,src_layer,regexp_pattern):
    if src_layer==None:
        src_layer=a_img.active_layer

    sync_layer_mask(a_img,src_layer,re.compile(regexp_pattern),None)

def python_fu_sync_layer_mask_of_sibling(a_img,a_drawable):
    names=a_img.active_layer.name.split('-')
    head=names[0]
    sync_layer_mask(a_img,a_img.active_layer,re.compile(SIBLING_PATTERN % head),None)

def python_fu_sync_layer_mask_of_base(a_img,a_drawable):

    names=a_img.active_layer.name.split('-')
    head=names[0]

    # searching '-base' layer
    regexp=re.compile('^%s-base$' % head)
    for bl in a_img.layers:

        mo=regexp.search(bl.name)
        if mo:
            if bl==a_img.active_layer:
                gimp.message("アクティブレイヤがベースレイヤです")
            else:
                sync_layer_mask(a_img,re.compile(SIBLING_PATTERN % head),bl,targets=(a_img.active_layer,))

            return
    
    gimp.message("ベースレイヤ(%s-base)は発見できませんでした" % head)

def python_fu_sync_layergroup_mask(a_img,a_drawable,limit_to_linked,limit_to_has_mask,apply_prev_mask):

    cl=a_img.active_layer
    if len(cl.children)>0:
        # this is the layer group
        target_layers=cl.children
    elif cl.parent==None:
        gimp.message("グループに所属するレイヤにのみ有効です")
        return
    else:
        target_layers=cl.parent.children

    cl=target_layers[-1] # the last child used as unified mask source.
    sync_layer_mask(a_img,None,cl,targets=target_layers,
            limit_to_linked=limit_to_linked,
            limit_to_has_mask=limit_to_has_mask,
            apply_prev_mask=apply_prev_mask,
            force_other_mask=True)


def python_fu_sync_layer_mask_to_below(a_img,a_drawable,apply_prev_mask):
    al=a_img.active_layer
    if al.parent:
        plst=al.parent.children
    else:
        plst=a_img.layers

    curidx=pdb.gimp_image_get_item_position(a_img,al)
    
    if curidx < len(plst):
        sync_layer_mask(a_img,None,plst[curidx+1],(a_img.active_layer,),apply_prev_mask=apply_prev_mask)
    else:
        gimp.message("これより下にレイヤがありませんので、同期できません")

def sync_layer_mask(a_img,regexp,src_layer,targets,
    limit_to_linked=False,limit_to_has_mask=False,
    apply_prev_mask=False,force_other_mask=False):
    """
    core function.

    Arguments:
    regexp -- the regular expression object.if this is None,all name should be match.
    targets -- a sequence of target layers.if None,a_img.layers used.

    Returns:
    None
    """


    if targets==None:
        targets=a_img.layers

    if src_layer.mask!=None:
        src_mask=src_layer.mask
    elif src_layer.has_alpha:
        src_mask=pdb.gimp_layer_create_mask(src_layer,2) # 2==ADD ALPHA-COMPONENT MASK
    else:
        gimp.message("この作業には、レイヤ %s にマスクかアルファチャネルが必要です" % src_layer.name)
        return


    # start of groping undoable operations
    pdb.gimp_image_undo_group_start(a_img)

    try:
        ax,ay=src_layer.offsets

        def do_sync(targets):
        # Use this local function ,to recursive call does not needs copy flags.
            for cl in targets:
                if cl!=src_layer:
                    
                    if len(cl.children)>0:
                        # this is layer group.so, walk into layer group
                        do_sync(cl.children) # RECURSIVE CALL
                    else:

                        if regexp:
                            mo=regexp.search(cl.name)
                        else:
                            flag_limit_linked=True
                            if limit_to_linked and not cl.linked:
                                flag_limit_linked=False

                            flag_limit_mask=True
                            if limit_to_has_mask and cl.mask==None:
                                flag_limit_mask=False

                            mo=(flag_limit_linked and flag_limit_mask)

                        if mo:
                           #pdb.gimp_message("processing %s" % cl.name)
                            if cl.mask==None:
                                if force_other_mask:
                                    new_mask=pdb.gimp_layer_create_mask(cl,1) # 1==Completely transparent mask,to composite it.
                                else:
                                    new_mask=pdb.gimp_layer_create_mask(cl,2) # 2==ADD ALPHA-COMPONENT MASK
                            elif apply_prev_mask:
                                pdb.gimp_layer_remove_mask(cl,0) # 0 means 'apply mask and delete it'
                                new_mask=pdb.gimp_layer_create_mask(cl,1) # completely transparent mask,to replace 
                            else:
                                pdb.gimp_layer_remove_mask(cl,1) # 1 means 'simply delete it without changing pixel.'
                                new_mask=pdb.gimp_layer_create_mask(cl,1) # completely transparent mask,to replace 

                            pdb.gimp_layer_add_mask(cl,new_mask)

                            cx,cy=cl.offsets
                            pdb.gimp_channel_combine_masks(cl.mask, src_mask, 0, -cx+ax , -cy+ay) # mask composited.

                            pdb.gimp_layer_set_edit_mask(cl,0)

        # Start processing here.
        do_sync(targets)

    finally:
        # end of grouping undoable operations
        pdb.gimp_image_undo_group_end(a_img)

        if src_layer.mask!=src_mask:
            gimp.delete(src_mask)

register(
        "python_fu_sync_layer_mask",
        "sync-layer-mask",
        "指定された正規表現パターンに適合する名前のレイヤ間で、マスクを同期させる",
        "dothiko",
        "dothiko",
        "apr 2015",
        "<Image>/Python-Fu/mask/sync-layer-mask",
        "RGB*,GRAY*",
        [
            (PF_LAYER,"src_layer","元レイヤ",None),
            (PF_STRING,"regexp_pattern","正規表現パターン(pythonのreモジュールの書式で)","\S+"),
        ],
        [],
        python_fu_sync_layer_mask)

register(
        "python_fu_sync_layer_mask_of_sibling",
        "sync-layer-mask-of-sibling",
        "アクティブレイヤの兄弟レイヤ間で、マスクを同期させる",
        "dothiko",
        "dothiko",
        "apr 2015",
        "<Image>/Python-Fu/mask/sync-layer-mask-of-sibling",
        "RGB*,GRAY*",
        [
        ],
        [],
        python_fu_sync_layer_mask_of_sibling)

register(
        "python_fu_sync_layer_mask_of_base",
        "sync-layer-mask-of-base",
        "アクティブレイヤのマスクを、ベースレイヤのマスクで同期する",
        "dothiko",
        "dothiko",
        "apr 2015",
        "<Image>/Python-Fu/mask/sync-layer-mask-of-base",
        "RGB*,GRAY*",
        [
        ],
        [],
        python_fu_sync_layer_mask_of_base)

register(
        "python_fu_sync_layergroup_mask",
        "alpha-inherit-mask",
        "アルファ相続的な動作で、グループの最後尾のアルファと各レイヤーのマスクを同期させる",
        "dothiko",
        "dothiko",
        "apr 2015",
        "<Image>/Python-Fu/mask/sync-layergroup-mask",
        "RGB*,GRAY*",
        [
            (PF_BOOL,"limit_to_linked","リンクされたレイヤに限定",False),
            (PF_BOOL,"limit_to_has_mask","現時点でマスクを持つレイヤに限定",True),
            (PF_BOOL,"apply_prev_mask","対象レイヤが既にマスクを持つ場合、同期前にマスクを適用する",False),
        ],
        [],
        python_fu_sync_layergroup_mask)

register(
        "python_fu_sync_layer_mask_to_below",
        "sync-layer-mask-to-below",
        "真下のレイヤの不透明部分をマスクとして同期させる",
        "dothiko",
        "dothiko",
        "apr 2015",
        "<Image>/Python-Fu/mask/sync-layer-mask-to-below",
        "RGB*,GRAY*",
        [
          (PF_BOOL,"apply_prev_mask","対象レイヤが既にマスクを持つ場合、同期前にマスクを適用する",False),
        ],
        [],
        python_fu_sync_layer_mask_to_below)
main()






if __name__ == '__main__':

    pass