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>
6 * This file is part of libass.
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.
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.
22 #include "ass_compat.h"
33 #include "ass_utils.h"
34 #include "ass_outline.h"
35 #include "ass_bitmap.h"
36 #include "ass_render.h"
39 #define ALIGN C_ALIGN_ORDER
40 #define DECORATE(func) ass_##func##_c
41 #include "ass_func_template.h"
45 #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
48 #define DECORATE(func) ass_##func##_sse2
49 #include "ass_func_template.h"
54 #define DECORATE(func) ass_##func##_avx2
55 #include "ass_func_template.h"
62 void ass_synth_blur(const BitmapEngine *engine, Bitmap *bm,
63 int be, double blur_radius)
68 // Apply gaussian blur
69 double r2 = blur_radius * blur_radius / log(256);
71 ass_gaussian_blur(engine, bm, r2);
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);
84 ptrdiff_t stride = bm->stride;
85 uint8_t *buf = bm->buffer;
87 be_blur_pre(buf, w, h, stride);
89 memset(tmp, 0, stride * 2);
90 engine->be_blur(buf, w, h, stride, tmp);
92 be_blur_post(buf, w, h, stride);
94 memset(tmp, 0, stride * 2);
95 engine->be_blur(buf, w, h, stride, tmp);
96 ass_aligned_free(tmp);
99 bool alloc_bitmap(const BitmapEngine *engine, Bitmap *bm,
100 int32_t w, int32_t h, bool zero)
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))
107 uint8_t *buf = ass_aligned_alloc(align, s * h + 32, zero);
117 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int32_t w, int32_t h)
119 uint8_t *old = bm->buffer;
120 if (!alloc_bitmap(engine, bm, w, h, false))
122 ass_aligned_free(old);
126 void ass_free_bitmap(Bitmap *bm)
128 ass_aligned_free(bm->buffer);
131 bool copy_bitmap(const BitmapEngine *engine, Bitmap *dst, const Bitmap *src)
134 memset(dst, 0, sizeof(*dst));
137 if (!alloc_bitmap(engine, dst, src->w, src->h, false))
139 dst->left = src->left;
141 memcpy(dst->buffer, src->buffer, src->stride * src->h);
145 bool outline_to_bitmap(ASS_Renderer *render_priv, Bitmap *bm,
146 ASS_Outline *outline1, ASS_Outline *outline2)
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");
153 if (outline2 && !rasterizer_set_outline(rst, outline2, !!outline1)) {
154 ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
157 if (rst->bbox.x_min > rst->bbox.x_max || rst->bbox.y_min > rst->bbox.y_max)
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;
168 int mask = (1 << render_priv->engine->tile_order) - 1;
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);
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))
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");
195 * \brief fix outline bitmap
197 * The glyph bitmap is subtracted from outline bitmap. This way looks much
198 * better in some cases.
200 void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
202 if (!bm_g->buffer || !bm_o->buffer)
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);
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);
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;
222 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
223 * expressed in 26.6 fixed point
225 void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
227 assert((shift_x & ~63) == 0 && (shift_y & ~63) == 0);
232 int32_t w = bm->w, h = bm->h;
233 ptrdiff_t s = bm->stride;
234 uint8_t *buf = bm->buffer;
236 // Shift in x direction
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;
246 // Shift in y direction
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;
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.
262 void ass_be_blur_c(uint8_t *buf, intptr_t w, intptr_t h,
263 intptr_t stride, uint16_t *tmp)
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;
269 memset(tmp, 0, sizeof(uint16_t) * w * 2);
278 for ( ; x < w; x++) {
280 temp2 = old_pix + temp1;
282 temp1 = old_sum + temp2;
284 col_pix_buf[x-1] = temp1;
285 col_sum_buf[x-1] = temp1;
287 temp1 = old_sum + old_pix;
288 col_pix_buf[x-1] = temp1;
289 col_sum_buf[x-1] = temp1;
292 for (y++; y < h; y++) {
294 dst=buf+(y-1)*stride;
299 for ( ; x < w; x++) {
301 temp2 = old_pix + temp1;
303 temp1 = old_sum + temp2;
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;
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;
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;
325 void be_blur_pre(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
327 for (int y = 0; y < h; ++y)
329 for (int x = 0; x < w; ++x)
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;
340 void be_blur_post(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
342 for (int y = 0; y < h; ++y)
344 for (int x = 0; x < w; ++x)
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);
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!
372 int be_padding(int be)
380 return FFMAX(128 - be, 0);
384 * \brief Add two bitmaps together at a given position
385 * Uses additive blending, clipped to [0,255]. Pure C implementation.
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)
392 uint8_t* end = dst + dst_stride * height;
394 for (unsigned j = 0; j < width; ++j) {
395 out = dst[j] + src[j];
396 dst[j] = FFMIN(out, 255);
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)
408 uint8_t* end = dst + dst_stride * height;
410 for (unsigned j = 0; j < width; ++j) {
411 out = dst[j] - src[j];
412 dst[j] = FFMAX(out, 0);
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)
424 uint8_t* end = src1 + src1_stride * h;
426 for (unsigned x = 0; x < w; ++x) {
427 dst[x] = (src1[x] * src2[x] + 255) >> 8;