/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 | } |