Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/xps/xpsopacity.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* XPS interpreter - transparency support */
18
19
#include "ghostxps.h"
20
21
void
22
xps_bounds_in_user_space(xps_context_t *ctx, gs_rect *ubox)
23
0
{
24
0
    gx_clip_path *clip_path;
25
0
    gs_rect dbox;
26
0
    int code;
27
28
0
    code = gx_effective_clip_path(ctx->pgs, &clip_path);
29
0
    if (code < 0)
30
0
        gs_warn("gx_effective_clip_path failed");
31
32
0
    dbox.p.x = fixed2float(clip_path->outer_box.p.x);
33
0
    dbox.p.y = fixed2float(clip_path->outer_box.p.y);
34
0
    dbox.q.x = fixed2float(clip_path->outer_box.q.x);
35
0
    dbox.q.y = fixed2float(clip_path->outer_box.q.y);
36
0
    code = gs_bbox_transform_inverse(&dbox, &ctm_only(ctx->pgs), ubox);
37
0
    if (code < 0)
38
0
        gs_warn("gs_bbox_transform_inverse failed");
39
0
}
40
41
/* This will get the proper bounds based upon the current path, clip path
42
   and stroke width */
43
int xps_bounds_in_user_space_path_clip(xps_context_t *ctx, gs_rect *ubox,
44
                                       bool use_path, bool is_stroke)
45
0
{
46
0
    int code;
47
0
    gs_rect bbox;
48
49
0
    if (!use_path)
50
0
        code = gx_curr_bbox(ctx->pgs, &bbox, NO_PATH);
51
0
    else {
52
0
        if (is_stroke)
53
0
            code = gx_curr_bbox(ctx->pgs, &bbox, PATH_STROKE);
54
0
        else
55
0
            code = gx_curr_bbox(ctx->pgs, &bbox, PATH_FILL);
56
0
    }
57
0
    if (code < 0)
58
0
        return code;
59
0
    return gs_bbox_transform_inverse(&bbox, &ctm_only(ctx->pgs), ubox);
60
0
}
61
62
int
63
xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict,
64
        char *opacity_att, xps_item_t *opacity_mask_tag, bool use_path,
65
        bool is_stroke)
66
2.87k
{
67
2.87k
    gs_transparency_group_params_t tgp;
68
2.87k
    gs_transparency_mask_params_t tmp;
69
2.87k
    gs_rect bbox;
70
2.87k
    float opacity;
71
2.87k
    int save;
72
2.87k
    int code;
73
74
2.87k
    if (!opacity_att && !opacity_mask_tag)
75
2.87k
        return 0;
76
77
0
    opacity = 1.0;
78
0
    if (opacity_att)
79
0
        opacity = atof(opacity_att);
80
0
    gs_setblendmode(ctx->pgs, BLEND_MODE_Normal);
81
0
    gs_setfillconstantalpha(ctx->pgs, 1.0);
82
0
    gs_setstrokeconstantalpha(ctx->pgs, 1.0);
83
84
0
    code = xps_bounds_in_user_space_path_clip(ctx, &bbox, use_path, is_stroke);
85
0
    if (code < 0)
86
0
        return code;
87
88
0
    if (opacity_mask_tag)
89
0
    {
90
0
        gs_trans_mask_params_init(&tmp, TRANSPARENCY_MASK_Luminosity);
91
0
        gs_gsave(ctx->pgs);
92
0
        gs_setcolorspace(ctx->pgs, ctx->gray_lin);
93
0
        tmp.ColorSpace = ctx->gray_lin;
94
0
        gs_begin_transparency_mask(ctx->pgs, &tmp, &bbox, 0);
95
96
        /* Create a path if we dont have one that defines the opacity.
97
           For example if we had a canvas opacity then we need to make
98
           the path for that opacity.  Otherwise we use the opacity path
99
           that was defined and its intesection with the clipping path. */
100
0
        if (!use_path)
101
0
        {
102
0
            gs_moveto(ctx->pgs, bbox.p.x, bbox.p.y);
103
0
            gs_lineto(ctx->pgs, bbox.p.x, bbox.q.y);
104
0
            gs_lineto(ctx->pgs, bbox.q.x, bbox.q.y);
105
0
            gs_lineto(ctx->pgs, bbox.q.x, bbox.p.y);
106
0
            gs_closepath(ctx->pgs);
107
0
        }
108
109
        /* opacity-only mode: use alpha value as gray color to create luminosity mask */
110
0
        save = ctx->opacity_only;
111
0
        ctx->opacity_only = 1;
112
113
0
        code = xps_parse_brush(ctx, base_uri, dict, opacity_mask_tag);
114
0
        if (code)
115
0
        {
116
            /* gs_grestore(ctx->pgs); */
117
0
            gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity);
118
0
            ctx->opacity_only = save;
119
0
            return gs_rethrow(code, "cannot parse opacity mask brush");
120
0
        }
121
122
0
        gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity);
123
0
        gs_grestore(ctx->pgs);
124
0
        gs_push_transparency_state(ctx->pgs);
125
0
        ctx->opacity_only = save;
126
0
    }
127
128
0
    gs_setfillconstantalpha(ctx->pgs, opacity);
129
0
    gs_setstrokeconstantalpha(ctx->pgs, opacity);
130
0
    gs_trans_group_params_init(&tgp, opacity);
131
0
    gs_begin_transparency_group(ctx->pgs, &tgp, &bbox, PDF14_BEGIN_TRANS_GROUP);
132
0
    gs_setfillconstantalpha(ctx->pgs, 1.0);
133
0
    gs_setstrokeconstantalpha(ctx->pgs, 1.0);
134
135
0
    return 0;
136
0
}
137
138
void
139
xps_end_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict,
140
        char *opacity_att, xps_item_t *opacity_mask_tag)
141
2.87k
{
142
2.87k
    if (!opacity_att && !opacity_mask_tag)
143
2.87k
        return;
144
0
    gs_end_transparency_group(ctx->pgs);
145
    /* Need to remove the soft mask from the graphic state.  Otherwise
146
       we may end up using it in subsequent drawings.  Note that there
147
       is not a push of the state made since there is already a soft
148
       mask present from gs_end_transparency_mask.  In this case,
149
       we are removing the mask with this forced pop. */
150
0
    if (opacity_mask_tag != NULL)
151
0
        gs_pop_transparency_state(ctx->pgs, true);
152
0
}