From 372114fefff757839acdb9d9ebb19ed27696b371 Mon Sep 17 00:00:00 2001 From: askmeaboutloom Date: Thu, 24 Aug 2023 21:09:55 +0200 Subject: [PATCH] Store alpha preserve of a layer in ORA files For compatibility with Krita and maybe other applications. In Drawpile, alpha preserve is implicit depending on the blend mode, but e.g. in Krita, it has to be enabled separately. This adds an `alpha-preserve="true"` attribute to layers with affected blend mdoes so that Krita can restore them properly, once the attribute is implemented on their side. Drawpile doesn't read the attribute, since it can't do anything with the information, except for the special case of the Normal blend mode, which becomes Recolor when alpha preserve is enabled. Normally this is saved as a separate blend mode, but since it's a valid way to represent it, we handle it too. --- ChangeLog | 1 + src/drawdance/libengine/dpengine/load.c | 11 ++++++ src/drawdance/libengine/dpengine/save.c | 9 +++++ src/drawdance/libmsg/dpmsg/blend_mode.c | 46 ++++++++++++++----------- src/drawdance/libmsg/dpmsg/blend_mode.h | 2 ++ 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index 94a890ca9b..1e9a5bbc50 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,7 @@ Unreleased Version 2.2.0-pre * Fix: Make the flipbook remember your last crop, frame range and playback speed for the current window. Thanks Ben for finding. * Fix: Don't mark guests as registered. Thanks to xxxx for reporting. * Fix: Allow assigning a shortcut to open the Layouts dialog (F9 by default) and to the entries in the Help menu (nothing by default.) + * Feature: Store alpha preserve state of layers in ORA files for better Krita compatibility. 2023-07-31 Version 2.2.0-beta.6 * Fix: Don't forget account password when entering a wrong session password. diff --git a/src/drawdance/libengine/dpengine/load.c b/src/drawdance/libengine/dpengine/load.c index 64e4fec957..0e4ffd056d 100644 --- a/src/drawdance/libengine/dpengine/load.c +++ b/src/drawdance/libengine/dpengine/load.c @@ -423,6 +423,17 @@ static DP_TransientLayerProps *ora_make_layer_props(DP_XmlElement *element, DP_BlendMode blend_mode = DP_blend_mode_by_svg_name( DP_xml_element_attribute(element, NULL, "composite-op"), DP_BLEND_MODE_NORMAL); + + // Normal with alpha preserve is Recolor. Drawpile doesn't save it this way, + // but it's a valid way to represent it, so we handle it. + if (blend_mode == DP_BLEND_MODE_NORMAL) { + const char *alpha_preserve = + DP_xml_element_attribute(element, NULL, "alpha-preserve"); + if (DP_str_equal_lowercase(alpha_preserve, "true")) { + blend_mode = DP_BLEND_MODE_RECOLOR; + } + } + DP_transient_layer_props_blend_mode_set(tlp, (int)blend_mode); const char *censored = diff --git a/src/drawdance/libengine/dpengine/save.c b/src/drawdance/libengine/dpengine/save.c index d8e1534e47..4c8e063c65 100644 --- a/src/drawdance/libengine/dpengine/save.c +++ b/src/drawdance/libengine/dpengine/save.c @@ -388,6 +388,15 @@ static void ora_write_layer_props_xml(DP_SaveOraContext *c, DP_Output *output, DP_blend_mode_svg_name(blend_mode)); } + // Drawpile doesn't itself need the alpha preserve property, since its alpha + // preserve behavior depends on the blend mode, but other software cares. + // The Recolor blend mode is saved as src-atop, which is alpha-preserving + // per its definition, so it doesn't get the extra attribute. + if (DP_blend_mode_preserves_alpha(blend_mode) + && blend_mode != DP_BLEND_MODE_RECOLOR) { + DP_OUTPUT_PRINT_LITERAL(output, " alpha-preserve=\"true\""); + } + if (DP_layer_props_censored(lp)) { DP_OUTPUT_PRINT_LITERAL(output, " drawpile:censored=\"true\""); } diff --git a/src/drawdance/libmsg/dpmsg/blend_mode.c b/src/drawdance/libmsg/dpmsg/blend_mode.c index ee5fa238ce..34054d0b8b 100644 --- a/src/drawdance/libmsg/dpmsg/blend_mode.c +++ b/src/drawdance/libmsg/dpmsg/blend_mode.c @@ -28,6 +28,7 @@ #define DECREASE_OPACITY (1 << 2) #define INCREASE_OPACITY (1 << 3) #define BLEND_BLANK (1 << 4) +#define PRESERVES_ALPHA (1 << 5) // The Krita name for the linear light blend mode contains a space, which isn't // supported in draw dabs messages, since they already use the curly brace body @@ -65,63 +66,63 @@ static const DP_BlendModeAttributes mode_attributes[DP_BLEND_MODE_COUNT] = { }, [DP_BLEND_MODE_MULTIPLY] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_MULTIPLY", "svg:multiply", "Multiply", }, [DP_BLEND_MODE_DIVIDE] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_DIVIDE", "-dp-divide", "Divide", }, [DP_BLEND_MODE_BURN] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_BURN", "svg:color-burn", "Burn", }, [DP_BLEND_MODE_DODGE] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_DODGE", "svg:color-dodge", "Dodge", }, [DP_BLEND_MODE_DARKEN] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_DARKEN", "svg:darken", "Darken", }, [DP_BLEND_MODE_LIGHTEN] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_LIGHTEN", "svg:lighten", "Lighten", }, [DP_BLEND_MODE_SUBTRACT] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_SUBTRACT", "-dp-minus", "Subtract", }, [DP_BLEND_MODE_ADD] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_ADD", "svg:plus", "Add", }, [DP_BLEND_MODE_RECOLOR] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_RECOLOR", "svg:src-atop", "Recolor", @@ -142,7 +143,7 @@ static const DP_BlendModeAttributes mode_attributes[DP_BLEND_MODE_COUNT] = { }, [DP_BLEND_MODE_SCREEN] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_SCREEN", "svg:screen", "Screen", @@ -156,70 +157,70 @@ static const DP_BlendModeAttributes mode_attributes[DP_BLEND_MODE_COUNT] = { }, [DP_BLEND_MODE_LUMINOSITY_SHINE_SAI] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_LUMINOSITY_SHINE_SAI", "krita:luminosity_sai", "Luminosity/Shine (SAI)", }, [DP_BLEND_MODE_OVERLAY] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_OVERLAY", "svg:overlay", "Overlay", }, [DP_BLEND_MODE_HARD_LIGHT] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_HARD_LIGHT", "svg:hard-light", "Hard Light", }, [DP_BLEND_MODE_SOFT_LIGHT] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_SOFT_LIGHT", "svg:soft-light", "Soft Light", }, [DP_BLEND_MODE_LINEAR_BURN] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_LINEAR_BURN", "krita:linear_burn", "Linear Burn", }, [DP_BLEND_MODE_LINEAR_LIGHT] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_LINEAR_LIGHT", "krita:linear light", "Linear Light", }, [DP_BLEND_MODE_HUE] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_HUE", "svg:hue", "Hue", }, [DP_BLEND_MODE_SATURATION] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_SATURATION", "svg:saturation", "Saturation", }, [DP_BLEND_MODE_LUMINOSITY] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_LUMINOSITY", "svg:luminosity", "Luminosity", }, [DP_BLEND_MODE_COLOR] = { - LAYER | BRUSH, + LAYER | BRUSH | PRESERVES_ALPHA, "DP_BLEND_MODE_COLOR", "svg:color", "Color", @@ -310,6 +311,11 @@ bool DP_blend_mode_blend_blank(int blend_mode) return get_attributes(blend_mode)->flags & BLEND_BLANK; } +bool DP_blend_mode_preserves_alpha(int blend_mode) +{ + return get_attributes(blend_mode)->flags & PRESERVES_ALPHA; +} + DP_BlendMode DP_blend_mode_by_svg_name(const char *svg_name, DP_BlendMode not_found_value) { diff --git a/src/drawdance/libmsg/dpmsg/blend_mode.h b/src/drawdance/libmsg/dpmsg/blend_mode.h index cc1170fd7b..760de45315 100644 --- a/src/drawdance/libmsg/dpmsg/blend_mode.h +++ b/src/drawdance/libmsg/dpmsg/blend_mode.h @@ -79,6 +79,8 @@ bool DP_blend_mode_can_decrease_opacity(int blend_mode); bool DP_blend_mode_blend_blank(int blend_mode); +bool DP_blend_mode_preserves_alpha(int blend_mode); + DP_BlendMode DP_blend_mode_by_svg_name(const char *svg_name, DP_BlendMode not_found_value);