Coverage Report

Created: 2025-08-24 06:26

/src/libass/libass/ass_bitmap.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
3
 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
4
 * Copyright (c) 2011-2014, Yu Zhuohuang <yuzhuohuang@qq.com>
5
 *
6
 * This file is part of libass.
7
 *
8
 * Permission to use, copy, modify, and distribute this software for any
9
 * purpose with or without fee is hereby granted, provided that the above
10
 * copyright notice and this permission notice appear in all copies.
11
 *
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
20
21
#include "config.h"
22
#include "ass_compat.h"
23
24
#include <stdlib.h>
25
#include <string.h>
26
#include <math.h>
27
#include <stdbool.h>
28
#include <assert.h>
29
#include <ft2build.h>
30
#include FT_GLYPH_H
31
#include FT_OUTLINE_H
32
33
#include "ass_utils.h"
34
#include "ass_outline.h"
35
#include "ass_bitmap.h"
36
#include "ass_render.h"
37
38
39
static void be_blur_pre(uint8_t *buf, intptr_t stride, intptr_t width, intptr_t height)
40
5
{
41
141
    for (int y = 0; y < height; ++y)
42
136
    {
43
4.99k
        for (int x = 0; x < width; ++x)
44
4.85k
        {
45
            // This is equivalent to (value * 64 + 127) / 255 for all
46
            // values from 0 to 256 inclusive. Assist vectorizing
47
            // compilers by noting that all temporaries fit in 8 bits.
48
4.85k
            buf[y * stride + x] =
49
4.85k
                (uint8_t) ((buf[y * stride + x] >> 1) + 1) >> 1;
50
4.85k
        }
51
136
    }
52
5
}
53
54
static void be_blur_post(uint8_t *buf, intptr_t stride, intptr_t width, intptr_t height)
55
5
{
56
141
    for (int y = 0; y < height; ++y)
57
136
    {
58
4.99k
        for (int x = 0; x < width; ++x)
59
4.85k
        {
60
            // This is equivalent to (value * 255 + 32) / 64 for all values
61
            // from 0 to 96 inclusive, and we only care about 0 to 64.
62
4.85k
            uint8_t value = buf[y * stride + x];
63
4.85k
            buf[y * stride + x] = (value << 2) - (value > 32);
64
4.85k
        }
65
136
    }
66
5
}
67
68
void ass_synth_blur(const BitmapEngine *engine, Bitmap *bm,
69
                    int be, double blur_r2x, double blur_r2y)
70
3.37k
{
71
3.37k
    if (!bm->buffer)
72
26
        return;
73
74
    // Apply gaussian blur
75
3.34k
    if (blur_r2x > 0.001 || blur_r2y > 0.001)
76
10
        ass_gaussian_blur(engine, bm, blur_r2x, blur_r2y);
77
78
3.34k
    if (!be)
79
3.34k
        return;
80
81
    // Apply box blur (multiple passes, if requested)
82
5
    unsigned align = 1 << engine->align_order;
83
5
    size_t size = sizeof(uint16_t) * bm->stride * 2;
84
5
    uint16_t *tmp = ass_aligned_alloc(align, size, false);
85
5
    if (!tmp)
86
0
        return;
87
88
5
    int32_t w = bm->w;
89
5
    int32_t h = bm->h;
90
5
    ptrdiff_t stride = bm->stride;
91
5
    uint8_t *buf = bm->buffer;
92
5
    if (--be) {
93
5
        be_blur_pre(buf, stride, w, h);
94
630
        do {
95
630
            engine->be_blur(buf, stride, w, h, tmp);
96
630
        } while (--be);
97
5
        be_blur_post(buf, stride, w, h);
98
5
    }
99
5
    engine->be_blur(buf, stride, w, h, tmp);
100
5
    ass_aligned_free(tmp);
101
5
}
102
103
bool ass_alloc_bitmap(const BitmapEngine *engine, Bitmap *bm,
104
                      int32_t w, int32_t h, bool zero)
105
40.7k
{
106
40.7k
    unsigned align = 1 << engine->align_order;
107
40.7k
    size_t s = ass_align(align, w);
108
    // Too often we use ints as offset for bitmaps => use INT_MAX.
109
40.7k
    if (s > (INT_MAX - align) / FFMAX(h, 1))
110
0
        return false;
111
40.7k
    uint8_t *buf = ass_aligned_alloc(align, s * h + align, zero);
112
40.7k
    if (!buf)
113
0
        return false;
114
40.7k
    bm->w = w;
115
40.7k
    bm->h = h;
116
40.7k
    bm->stride = s;
117
40.7k
    bm->buffer = buf;
118
40.7k
    return true;
119
40.7k
}
120
121
bool ass_realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int32_t w, int32_t h)
122
10
{
123
10
    uint8_t *old = bm->buffer;
124
10
    if (!ass_alloc_bitmap(engine, bm, w, h, false))
125
0
        return false;
126
10
    ass_aligned_free(old);
127
10
    return true;
128
10
}
129
130
void ass_free_bitmap(Bitmap *bm)
131
41.5k
{
132
41.5k
    ass_aligned_free(bm->buffer);
133
41.5k
}
134
135
bool ass_copy_bitmap(const BitmapEngine *engine, Bitmap *dst, const Bitmap *src)
136
5.34k
{
137
5.34k
    if (!src->buffer) {
138
0
        memset(dst, 0, sizeof(*dst));
139
0
        return true;
140
0
    }
141
5.34k
    if (!ass_alloc_bitmap(engine, dst, src->w, src->h, false))
142
0
        return false;
143
5.34k
    dst->left = src->left;
144
5.34k
    dst->top  = src->top;
145
5.34k
    memcpy(dst->buffer, src->buffer, src->stride * src->h);
146
5.34k
    return true;
147
5.34k
}
148
149
bool ass_outline_to_bitmap(RenderContext *state, Bitmap *bm,
150
                           ASS_Outline *outline1, ASS_Outline *outline2)
151
31.4k
{
152
31.4k
    ASS_Renderer *render_priv = state->renderer;
153
31.4k
    RasterizerData *rst = &state->rasterizer;
154
31.4k
    if (outline1 && !ass_rasterizer_set_outline(rst, outline1, false)) {
155
0
        ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
156
0
        return false;
157
0
    }
158
31.4k
    if (outline2 && !ass_rasterizer_set_outline(rst, outline2, !!outline1)) {
159
0
        ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
160
0
        return false;
161
0
    }
162
31.4k
    if (rst->bbox.x_min > rst->bbox.x_max || rst->bbox.y_min > rst->bbox.y_max)
163
788
        return false;
164
165
    // enlarge by 1/64th of pixel to bypass slow rasterizer path, add 1 pixel for shift_bitmap
166
30.7k
    int32_t x_min = (rst->bbox.x_min -   1) >> 6;
167
30.7k
    int32_t y_min = (rst->bbox.y_min -   1) >> 6;
168
30.7k
    int32_t x_max = (rst->bbox.x_max + 127) >> 6;
169
30.7k
    int32_t y_max = (rst->bbox.y_max + 127) >> 6;
170
30.7k
    int32_t w = x_max - x_min;
171
30.7k
    int32_t h = y_max - y_min;
172
173
30.7k
    int mask = (1 << render_priv->engine.tile_order) - 1;
174
175
    // XXX: is that possible to trigger at all?
176
30.7k
    if (w < 0 || h < 0 || w > INT_MAX - mask || h > INT_MAX - mask) {
177
0
        ass_msg(render_priv->library, MSGL_WARN,
178
0
                "Glyph bounding box too large: %dx%dpx", w, h);
179
0
        return false;
180
0
    }
181
182
30.7k
    int32_t tile_w = (w + mask) & ~mask;
183
30.7k
    int32_t tile_h = (h + mask) & ~mask;
184
30.7k
    if (!ass_alloc_bitmap(&render_priv->engine, bm, tile_w, tile_h, false))
185
0
        return false;
186
30.7k
    bm->left = x_min;
187
30.7k
    bm->top  = y_min;
188
189
30.7k
    if (!ass_rasterizer_fill(&render_priv->engine, rst, bm->buffer,
190
30.7k
                             x_min, y_min, bm->stride, tile_h, bm->stride)) {
191
0
        ass_msg(render_priv->library, MSGL_WARN, "Failed to rasterize glyph!\n");
192
0
        ass_free_bitmap(bm);
193
0
        return false;
194
0
    }
195
196
30.7k
    return true;
197
30.7k
}
198
199
/**
200
 * \brief fix outline bitmap
201
 *
202
 * The glyph bitmap is subtracted from outline bitmap. This way looks much
203
 * better in some cases.
204
 */
205
void ass_fix_outline(Bitmap *bm_g, Bitmap *bm_o)
206
71
{
207
71
    if (!bm_g->buffer || !bm_o->buffer)
208
26
        return;
209
210
45
    int32_t l = FFMAX(bm_o->left, bm_g->left);
211
45
    int32_t t = FFMAX(bm_o->top,  bm_g->top);
212
45
    int32_t r = FFMIN(bm_o->left + bm_o->stride, bm_g->left + bm_g->stride);
213
45
    int32_t b = FFMIN(bm_o->top  + bm_o->h,      bm_g->top  + bm_g->h);
214
215
45
    uint8_t *g = bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left);
216
45
    uint8_t *o = bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left);
217
218
722
    for (int32_t y = 0; y < b - t; y++) {
219
23.3k
        for (int32_t x = 0; x < r - l; x++)
220
22.6k
            o[x] = (o[x] > g[x]) ? o[x] - (g[x] / 2) : 0;
221
677
        g += bm_g->stride;
222
677
        o += bm_o->stride;
223
677
    }
224
45
}
225
226
/**
227
 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
228
 * expressed in 26.6 fixed point
229
 */
230
void ass_shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
231
3.33k
{
232
3.33k
    assert((shift_x & ~63) == 0 && (shift_y & ~63) == 0);
233
234
3.33k
    if (!bm->buffer)
235
0
        return;
236
237
3.33k
    int32_t w = bm->w, h = bm->h;
238
3.33k
    ptrdiff_t s = bm->stride;
239
3.33k
    uint8_t *buf = bm->buffer;
240
241
    // Shift in x direction
242
3.33k
    if (shift_x)
243
227
        for (int32_t y = 0; y < h; y++) {
244
19.7k
            for (int32_t x = w - 1; x > 0; x--) {
245
19.5k
                uint8_t b = buf[x + y * s - 1] * shift_x >> 6;
246
19.5k
                buf[x + y * s - 1] -= b;
247
19.5k
                buf[x + y * s] += b;
248
19.5k
            }
249
216
        }
250
251
    // Shift in y direction
252
3.33k
    if (shift_y)
253
8.20k
        for (int32_t x = 0; x < w; x++) {
254
131k
            for (int32_t y = h - 1; y > 0; y--) {
255
123k
                uint8_t b = buf[x + y * s - s] * shift_y >> 6;
256
123k
                buf[x + y * s - s] -= b;
257
123k
                buf[x + y * s] += b;
258
123k
            }
259
8.20k
        }
260
3.33k
}