]> granicus.if.org Git - libass/blob - libass/ass_bitmap.c
renderer: eliminate use of bitmap pointers as mode flags
[libass] / libass / ass_bitmap.c
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 #define ALIGN           C_ALIGN_ORDER
40 #define DECORATE(func)  ass_##func##_c
41 #include "ass_func_template.h"
42 #undef ALIGN
43 #undef DECORATE
44
45 #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
46
47 #define ALIGN           4
48 #define DECORATE(func)  ass_##func##_sse2
49 #include "ass_func_template.h"
50 #undef ALIGN
51 #undef DECORATE
52
53 #define ALIGN           5
54 #define DECORATE(func)  ass_##func##_avx2
55 #include "ass_func_template.h"
56 #undef ALIGN
57 #undef DECORATE
58
59 #endif
60
61
62 void ass_synth_blur(const BitmapEngine *engine, Bitmap *bm,
63                     int be, double blur_radius)
64 {
65     if (!bm->buffer)
66         return;
67
68     // Apply gaussian blur
69     double r2 = blur_radius * blur_radius / log(256);
70     if (r2 > 0.001)
71         ass_gaussian_blur(engine, bm, r2);
72
73     if (!be)
74         return;
75
76     // Apply box blur (multiple passes, if requested)
77     size_t size = sizeof(uint16_t) * bm->stride * 2;
78     uint16_t *tmp = ass_aligned_alloc(32, size, false);
79     if (!tmp)
80         return;
81
82     int32_t w = bm->w;
83     int32_t h = bm->h;
84     ptrdiff_t stride = bm->stride;
85     uint8_t *buf = bm->buffer;
86     if (--be) {
87         be_blur_pre(buf, w, h, stride);
88         do {
89             memset(tmp, 0, stride * 2);
90             engine->be_blur(buf, w, h, stride, tmp);
91         } while (--be);
92         be_blur_post(buf, w, h, stride);
93     }
94     memset(tmp, 0, stride * 2);
95     engine->be_blur(buf, w, h, stride, tmp);
96     ass_aligned_free(tmp);
97 }
98
99 bool alloc_bitmap(const BitmapEngine *engine, Bitmap *bm,
100                   int32_t w, int32_t h, bool zero)
101 {
102     unsigned align = 1 << engine->align_order;
103     size_t s = ass_align(align, w);
104     // Too often we use ints as offset for bitmaps => use INT_MAX.
105     if (s > (INT_MAX - 32) / FFMAX(h, 1))
106         return false;
107     uint8_t *buf = ass_aligned_alloc(align, s * h + 32, zero);
108     if (!buf)
109         return false;
110     bm->w = w;
111     bm->h = h;
112     bm->stride = s;
113     bm->buffer = buf;
114     return true;
115 }
116
117 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int32_t w, int32_t h)
118 {
119     uint8_t *old = bm->buffer;
120     if (!alloc_bitmap(engine, bm, w, h, false))
121         return false;
122     ass_aligned_free(old);
123     return true;
124 }
125
126 void ass_free_bitmap(Bitmap *bm)
127 {
128     ass_aligned_free(bm->buffer);
129 }
130
131 bool copy_bitmap(const BitmapEngine *engine, Bitmap *dst, const Bitmap *src)
132 {
133     if (!src->buffer) {
134         memset(dst, 0, sizeof(*dst));
135         return true;
136     }
137     if (!alloc_bitmap(engine, dst, src->w, src->h, false))
138         return false;
139     dst->left = src->left;
140     dst->top  = src->top;
141     memcpy(dst->buffer, src->buffer, src->stride * src->h);
142     return true;
143 }
144
145 bool outline_to_bitmap(ASS_Renderer *render_priv, Bitmap *bm,
146                        ASS_Outline *outline1, ASS_Outline *outline2)
147 {
148     RasterizerData *rst = &render_priv->rasterizer;
149     if (outline1 && !rasterizer_set_outline(rst, outline1, false)) {
150         ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
151         return false;
152     }
153     if (outline2 && !rasterizer_set_outline(rst, outline2, !!outline1)) {
154         ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
155         return false;
156     }
157     if (rst->bbox.x_min > rst->bbox.x_max || rst->bbox.y_min > rst->bbox.y_max)
158         return false;
159
160     // enlarge by 1/64th of pixel to bypass slow rasterizer path, add 1 pixel for shift_bitmap
161     int32_t x_min = (rst->bbox.x_min -   1) >> 6;
162     int32_t y_min = (rst->bbox.y_min -   1) >> 6;
163     int32_t x_max = (rst->bbox.x_max + 127) >> 6;
164     int32_t y_max = (rst->bbox.y_max + 127) >> 6;
165     int32_t w = x_max - x_min;
166     int32_t h = y_max - y_min;
167
168     int mask = (1 << render_priv->engine->tile_order) - 1;
169
170     // XXX: is that possible to trigger at all?
171     if (w < 0 || h < 0 || w > INT_MAX - mask || h > INT_MAX - mask) {
172         ass_msg(render_priv->library, MSGL_WARN,
173                 "Glyph bounding box too large: %dx%dpx", w, h);
174         return false;
175     }
176
177     int32_t tile_w = (w + mask) & ~mask;
178     int32_t tile_h = (h + mask) & ~mask;
179     if (!alloc_bitmap(render_priv->engine, bm, tile_w, tile_h, false))
180         return false;
181     bm->left = x_min;
182     bm->top  = y_min;
183
184     if (!rasterizer_fill(render_priv->engine, rst, bm->buffer,
185                          x_min, y_min, bm->stride, tile_h, bm->stride)) {
186         ass_msg(render_priv->library, MSGL_WARN, "Failed to rasterize glyph!\n");
187         ass_free_bitmap(bm);
188         return false;
189     }
190
191     return true;
192 }
193
194 /**
195  * \brief fix outline bitmap
196  *
197  * The glyph bitmap is subtracted from outline bitmap. This way looks much
198  * better in some cases.
199  */
200 void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
201 {
202     if (!bm_g->buffer || !bm_o->buffer)
203         return;
204
205     int32_t l = FFMAX(bm_o->left, bm_g->left);
206     int32_t t = FFMAX(bm_o->top,  bm_g->top);
207     int32_t r = FFMIN(bm_o->left + bm_o->stride, bm_g->left + bm_g->stride);
208     int32_t b = FFMIN(bm_o->top  + bm_o->h,      bm_g->top  + bm_g->h);
209
210     uint8_t *g = bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left);
211     uint8_t *o = bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left);
212
213     for (int32_t y = 0; y < b - t; y++) {
214         for (int32_t x = 0; x < r - l; x++)
215             o[x] = (o[x] > g[x]) ? o[x] - (g[x] / 2) : 0;
216         g += bm_g->stride;
217         o += bm_o->stride;
218     }
219 }
220
221 /**
222  * \brief Shift a bitmap by the fraction of a pixel in x and y direction
223  * expressed in 26.6 fixed point
224  */
225 void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
226 {
227     assert((shift_x & ~63) == 0 && (shift_y & ~63) == 0);
228
229     if (!bm->buffer)
230         return;
231
232     int32_t w = bm->w, h = bm->h;
233     ptrdiff_t s = bm->stride;
234     uint8_t *buf = bm->buffer;
235
236     // Shift in x direction
237     if (shift_x)
238         for (int32_t y = 0; y < h; y++) {
239             for (int32_t x = w - 1; x > 0; x--) {
240                 uint8_t b = buf[x + y * s - 1] * shift_x >> 6;
241                 buf[x + y * s - 1] -= b;
242                 buf[x + y * s] += b;
243             }
244         }
245
246     // Shift in y direction
247     if (shift_y)
248         for (int32_t x = 0; x < w; x++) {
249             for (int32_t y = h - 1; y > 0; y--) {
250                 uint8_t b = buf[x + y * s - s] * shift_y >> 6;
251                 buf[x + y * s - s] -= b;
252                 buf[x + y * s] += b;
253             }
254         }
255 }
256
257 /**
258  * \brief Blur with [[1,2,1], [2,4,2], [1,2,1]] kernel
259  * This blur is the same as the one employed by vsfilter.
260  * Pure C implementation.
261  */
262 void ass_be_blur_c(uint8_t *buf, intptr_t w, intptr_t h,
263                    intptr_t stride, uint16_t *tmp)
264 {
265     uint16_t *col_pix_buf = tmp;
266     uint16_t *col_sum_buf = tmp + w;
267     unsigned x, y, old_pix, old_sum, temp1, temp2;
268     uint8_t *src, *dst;
269     memset(tmp, 0, sizeof(uint16_t) * w * 2);
270     y = 0;
271
272     {
273         src=buf+y*stride;
274
275         x = 1;
276         old_pix = src[x-1];
277         old_sum = old_pix;
278         for ( ; x < w; x++) {
279             temp1 = src[x];
280             temp2 = old_pix + temp1;
281             old_pix = temp1;
282             temp1 = old_sum + temp2;
283             old_sum = temp2;
284             col_pix_buf[x-1] = temp1;
285             col_sum_buf[x-1] = temp1;
286         }
287         temp1 = old_sum + old_pix;
288         col_pix_buf[x-1] = temp1;
289         col_sum_buf[x-1] = temp1;
290     }
291
292     for (y++; y < h; y++) {
293         src=buf+y*stride;
294         dst=buf+(y-1)*stride;
295
296         x = 1;
297         old_pix = src[x-1];
298         old_sum = old_pix;
299         for ( ; x < w; x++) {
300             temp1 = src[x];
301             temp2 = old_pix + temp1;
302             old_pix = temp1;
303             temp1 = old_sum + temp2;
304             old_sum = temp2;
305
306             temp2 = col_pix_buf[x-1] + temp1;
307             col_pix_buf[x-1] = temp1;
308             dst[x-1] = (col_sum_buf[x-1] + temp2) >> 4;
309             col_sum_buf[x-1] = temp2;
310         }
311         temp1 = old_sum + old_pix;
312         temp2 = col_pix_buf[x-1] + temp1;
313         col_pix_buf[x-1] = temp1;
314         dst[x-1] = (col_sum_buf[x-1] + temp2) >> 4;
315         col_sum_buf[x-1] = temp2;
316     }
317
318     {
319         dst=buf+(y-1)*stride;
320         for (x = 0; x < w; x++)
321             dst[x] = (col_sum_buf[x] + col_pix_buf[x]) >> 4;
322     }
323 }
324
325 void be_blur_pre(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
326 {
327     for (int y = 0; y < h; ++y)
328     {
329         for (int x = 0; x < w; ++x)
330         {
331             // This is equivalent to (value * 64 + 127) / 255 for all
332             // values from 0 to 256 inclusive. Assist vectorizing
333             // compilers by noting that all temporaries fit in 8 bits.
334             buf[y * stride + x] =
335                 (uint8_t) ((buf[y * stride + x] >> 1) + 1) >> 1;
336         }
337     }
338 }
339
340 void be_blur_post(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
341 {
342     for (int y = 0; y < h; ++y)
343     {
344         for (int x = 0; x < w; ++x)
345         {
346             // This is equivalent to (value * 255 + 32) / 64 for all values
347             // from 0 to 96 inclusive, and we only care about 0 to 64.
348             uint8_t value = buf[y * stride + x];
349             buf[y * stride + x] = (value << 2) - (value > 32);
350         }
351     }
352 }
353
354 /*
355  * To find these values, simulate blur on the border between two
356  * half-planes, one zero-filled (background) and the other filled
357  * with the maximum supported value (foreground). Keep incrementing
358  * the \be argument. The necessary padding is the distance by which
359  * the blurred foreground image extends beyond the original border
360  * and into the background. Initially it increases along with \be,
361  * but very soon it grinds to a halt. At some point, the blurred
362  * image actually reaches a stationary point and stays unchanged
363  * forever after, simply _shifting_ by one pixel for each \be
364  * step--moving in the direction of the non-zero half-plane and
365  * thus decreasing the necessary padding (although the large
366  * padding is still needed for intermediate results). In practice,
367  * images are finite rather than infinite like half-planes, but
368  * this can only decrease the required padding. Half-planes filled
369  * with extreme values are the theoretical limit of the worst case.
370  * Make sure to use the right pixel value range in the simulation!
371  */
372 int be_padding(int be)
373 {
374     if (be <= 3)
375         return be;
376     if (be <= 7)
377         return 4;
378     if (be <= 123)
379         return 5;
380     return FFMAX(128 - be, 0);
381 }
382
383 /**
384  * \brief Add two bitmaps together at a given position
385  * Uses additive blending, clipped to [0,255]. Pure C implementation.
386  */
387 void ass_add_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
388                        uint8_t *src, intptr_t src_stride,
389                        intptr_t height, intptr_t width)
390 {
391     unsigned out;
392     uint8_t* end = dst + dst_stride * height;
393     while (dst < end) {
394         for (unsigned j = 0; j < width; ++j) {
395             out = dst[j] + src[j];
396             dst[j] = FFMIN(out, 255);
397         }
398         dst += dst_stride;
399         src += src_stride;
400     }
401 }
402
403 void ass_sub_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
404                        uint8_t *src, intptr_t src_stride,
405                        intptr_t height, intptr_t width)
406 {
407     short out;
408     uint8_t* end = dst + dst_stride * height;
409     while (dst < end) {
410         for (unsigned j = 0; j < width; ++j) {
411             out = dst[j] - src[j];
412             dst[j] = FFMAX(out, 0);
413         }
414         dst += dst_stride;
415         src += src_stride;
416     }
417 }
418
419 void ass_mul_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
420                        uint8_t *src1, intptr_t src1_stride,
421                        uint8_t *src2, intptr_t src2_stride,
422                        intptr_t w, intptr_t h)
423 {
424     uint8_t* end = src1 + src1_stride * h;
425     while (src1 < end) {
426         for (unsigned x = 0; x < w; ++x) {
427             dst[x] = (src1[x] * src2[x] + 255) >> 8;
428         }
429         dst  += dst_stride;
430         src1 += src1_stride;
431         src2 += src2_stride;
432     }
433 }