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_bitmap.h"
35 #include "ass_render.h"
38 #define ALIGN C_ALIGN_ORDER
39 #define DECORATE(func) ass_##func##_c
40 #include "ass_func_template.h"
44 #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
47 #define DECORATE(func) ass_##func##_sse2
48 #include "ass_func_template.h"
53 #define DECORATE(func) ass_##func##_avx2
54 #include "ass_func_template.h"
61 void ass_synth_blur(const BitmapEngine *engine, int opaque_box, int be,
62 double blur_radius, Bitmap *bm_g, Bitmap *bm_o)
64 bool blur_g = !bm_o || opaque_box;
68 // Apply gaussian blur
69 double r2 = blur_radius * blur_radius / log(256);
72 ass_gaussian_blur(engine, bm_o, r2);
74 ass_gaussian_blur(engine, bm_g, r2);
77 // Apply box blur (multiple passes, if requested)
79 size_t size_o = 0, size_g = 0;
81 size_o = sizeof(uint16_t) * bm_o->stride * 2;
83 size_g = sizeof(uint16_t) * bm_g->stride * 2;
84 size_t size = FFMAX(size_o, size_g);
85 uint16_t *tmp = size ? ass_aligned_alloc(32, size, false) : NULL;
92 unsigned stride = bm_o->stride;
93 unsigned char *buf = bm_o->buffer;
96 be_blur_pre(buf, w, h, stride);
98 memset(tmp, 0, stride * 2);
99 engine->be_blur(buf, w, h, stride, tmp);
101 be_blur_post(buf, w, h, stride);
103 memset(tmp, 0, stride * 2);
104 engine->be_blur(buf, w, h, stride, tmp);
108 unsigned passes = be;
109 unsigned w = bm_g->w;
110 unsigned h = bm_g->h;
111 unsigned stride = bm_g->stride;
112 unsigned char *buf = bm_g->buffer;
115 be_blur_pre(buf, w, h, stride);
117 memset(tmp, 0, stride * 2);
118 engine->be_blur(buf, w, h, stride, tmp);
120 be_blur_post(buf, w, h, stride);
122 memset(tmp, 0, stride * 2);
123 engine->be_blur(buf, w, h, stride, tmp);
126 ass_aligned_free(tmp);
130 static bool alloc_bitmap_buffer(const BitmapEngine *engine, Bitmap *bm, int w, int h,
133 unsigned align = 1 << engine->align_order;
134 size_t s = ass_align(align, w);
135 // Too often we use ints as offset for bitmaps => use INT_MAX.
136 if (s > (INT_MAX - 32) / FFMAX(h, 1))
138 uint8_t *buf = ass_aligned_alloc(align, s * h + 32, zero);
148 Bitmap *alloc_bitmap(const BitmapEngine *engine, int w, int h, bool zero)
150 Bitmap *bm = malloc(sizeof(Bitmap));
153 if (!alloc_bitmap_buffer(engine, bm, w, h, zero)) {
157 bm->left = bm->top = 0;
161 bool realloc_bitmap(const BitmapEngine *engine, Bitmap *bm, int w, int h)
163 uint8_t *old = bm->buffer;
164 if (!alloc_bitmap_buffer(engine, bm, w, h, false))
166 ass_aligned_free(old);
170 void ass_free_bitmap(Bitmap *bm)
173 ass_aligned_free(bm->buffer);
177 Bitmap *copy_bitmap(const BitmapEngine *engine, const Bitmap *src)
179 Bitmap *dst = alloc_bitmap(engine, src->w, src->h, false);
182 dst->left = src->left;
184 memcpy(dst->buffer, src->buffer, src->stride * src->h);
188 #if CONFIG_RASTERIZER
190 Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
191 ASS_Outline *outline, int bord)
193 RasterizerData *rst = &render_priv->rasterizer;
194 if (!rasterizer_set_outline(rst, outline)) {
195 ass_msg(render_priv->library, MSGL_WARN, "Failed to process glyph outline!\n");
199 if (bord < 0 || bord > INT_MAX / 2)
202 if (rst->x_min >= rst->x_max || rst->y_min >= rst->y_max) {
203 Bitmap *bm = alloc_bitmap(render_priv->engine, 2 * bord, 2 * bord, true);
206 bm->left = bm->top = -bord;
210 if (rst->x_max > INT_MAX - 63 || rst->y_max > INT_MAX - 63)
213 int x_min = rst->x_min >> 6;
214 int y_min = rst->y_min >> 6;
215 int x_max = (rst->x_max + 63) >> 6;
216 int y_max = (rst->y_max + 63) >> 6;
217 int w = x_max - x_min;
218 int h = y_max - y_min;
220 int mask = (1 << render_priv->engine->tile_order) - 1;
222 if (w < 0 || h < 0 ||
223 w > INT_MAX - (2 * bord + mask) || h > INT_MAX - (2 * bord + mask)) {
224 ass_msg(render_priv->library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx",
229 int tile_w = (w + 2 * bord + mask) & ~mask;
230 int tile_h = (h + 2 * bord + mask) & ~mask;
231 Bitmap *bm = alloc_bitmap(render_priv->engine, tile_w, tile_h, false);
234 bm->left = x_min - bord;
235 bm->top = y_min - bord;
237 if (!rasterizer_fill(render_priv->engine, rst, bm->buffer,
238 x_min - bord, y_min - bord,
239 bm->stride, tile_h, bm->stride)) {
240 ass_msg(render_priv->library, MSGL_WARN, "Failed to rasterize glyph!\n");
250 static Bitmap *outline_to_bitmap_ft(ASS_Renderer *render_priv,
251 FT_Outline *outline, int bord)
259 FT_Outline_Get_CBox(outline, &bbox);
260 if (bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
261 bm = alloc_bitmap(render_priv->engine, 2 * bord, 2 * bord, true);
264 bm->left = bm->top = -bord;
268 // move glyph to origin (0, 0)
271 FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);
272 if (bbox.xMax > INT_MAX - 63 || bbox.yMax > INT_MAX - 63)
275 bbox.xMax = (bbox.xMax + 63) & ~63;
276 bbox.yMax = (bbox.yMax + 63) & ~63;
277 w = (bbox.xMax - bbox.xMin) >> 6;
278 h = (bbox.yMax - bbox.yMin) >> 6;
283 if (w < 0 || h < 0 ||
284 w > INT_MAX - 2 * bord || h > INT_MAX - 2 * bord) {
285 ass_msg(render_priv->library, MSGL_WARN, "Glyph bounding box too large: %dx%dpx",
290 // allocate and set up bitmap
291 bm = alloc_bitmap(render_priv->engine, w + 2 * bord, h + 2 * bord, true);
294 bm->left = bbox.xMin - bord;
295 bm->top = -bbox.yMax - bord;
298 bitmap.pitch = bm->stride;
299 bitmap.buffer = bm->buffer + bord + bm->stride * bord;
300 bitmap.num_grays = 256;
301 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
303 // render into target bitmap
304 if ((error = FT_Outline_Get_Bitmap(render_priv->ftlibrary, outline, &bitmap))) {
305 ass_msg(render_priv->library, MSGL_WARN, "Failed to rasterize glyph: %d\n", error);
313 Bitmap *outline_to_bitmap(ASS_Renderer *render_priv,
314 ASS_Outline *outline, int bord)
316 size_t n_points = outline->n_points;
317 if (n_points > SHRT_MAX) {
318 ass_msg(render_priv->library, MSGL_WARN, "Too many outline points: %d",
323 size_t n_contours = FFMIN(outline->n_contours, SHRT_MAX);
324 short contours_small[EFFICIENT_CONTOUR_COUNT];
325 short *contours = contours_small;
326 short *contours_large = NULL;
327 if (n_contours > EFFICIENT_CONTOUR_COUNT) {
328 contours_large = malloc(n_contours * sizeof(short));
331 contours = contours_large;
333 for (size_t i = 0; i < n_contours; ++i)
334 contours[i] = FFMIN(outline->contours[i], n_points - 1);
337 ftol.n_points = n_points;
338 ftol.n_contours = n_contours;
339 ftol.points = outline->points;
340 ftol.tags = outline->tags;
341 ftol.contours = contours;
344 Bitmap *bm = outline_to_bitmap_ft(render_priv, &ftol, bord);
345 free(contours_large);
352 * \brief fix outline bitmap
354 * The glyph bitmap is subtracted from outline bitmap. This way looks much
355 * better in some cases.
357 void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
360 const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left;
361 const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top;
363 bm_o->left + bm_o->stride <
364 bm_g->left + bm_g->stride ? bm_o->left + bm_o->stride : bm_g->left + bm_g->stride;
366 bm_o->top + bm_o->h <
367 bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h;
370 bm_g->buffer + (t - bm_g->top) * bm_g->stride + (l - bm_g->left);
372 bm_o->buffer + (t - bm_o->top) * bm_o->stride + (l - bm_o->left);
374 for (y = 0; y < b - t; ++y) {
375 for (x = 0; x < r - l; ++x) {
376 unsigned char c_g, c_o;
379 o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0;
387 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
388 * expressed in 26.6 fixed point
390 void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
396 unsigned char *buf = bm->buffer;
398 assert((shift_x & ~63) == 0 && (shift_y & ~63) == 0);
400 // Shift in x direction
401 for (y = 0; y < h; y++) {
402 for (x = w - 1; x > 0; x--) {
403 b = (buf[x + y * s - 1] * shift_x) >> 6;
404 buf[x + y * s - 1] -= b;
409 // Shift in y direction
410 for (x = 0; x < w; x++) {
411 for (y = h - 1; y > 0; y--) {
412 b = (buf[x + (y - 1) * s] * shift_y) >> 6;
413 buf[x + (y - 1) * s] -= b;
420 * \brief Blur with [[1,2,1], [2,4,2], [1,2,1]] kernel
421 * This blur is the same as the one employed by vsfilter.
422 * Pure C implementation.
424 void ass_be_blur_c(uint8_t *buf, intptr_t w, intptr_t h,
425 intptr_t stride, uint16_t *tmp)
427 uint16_t *col_pix_buf = tmp;
428 uint16_t *col_sum_buf = tmp + w;
429 unsigned x, y, old_pix, old_sum, temp1, temp2;
431 memset(tmp, 0, sizeof(uint16_t) * w * 2);
440 for ( ; x < w; x++) {
442 temp2 = old_pix + temp1;
444 temp1 = old_sum + temp2;
446 col_pix_buf[x-1] = temp1;
447 col_sum_buf[x-1] = temp1;
449 temp1 = old_sum + old_pix;
450 col_pix_buf[x-1] = temp1;
451 col_sum_buf[x-1] = temp1;
454 for (y++; y < h; y++) {
456 dst=buf+(y-1)*stride;
461 for ( ; x < w; x++) {
463 temp2 = old_pix + temp1;
465 temp1 = old_sum + temp2;
468 temp2 = col_pix_buf[x-1] + temp1;
469 col_pix_buf[x-1] = temp1;
470 dst[x-1] = (col_sum_buf[x-1] + temp2) >> 4;
471 col_sum_buf[x-1] = temp2;
473 temp1 = old_sum + old_pix;
474 temp2 = col_pix_buf[x-1] + temp1;
475 col_pix_buf[x-1] = temp1;
476 dst[x-1] = (col_sum_buf[x-1] + temp2) >> 4;
477 col_sum_buf[x-1] = temp2;
481 dst=buf+(y-1)*stride;
482 for (x = 0; x < w; x++)
483 dst[x] = (col_sum_buf[x] + col_pix_buf[x]) >> 4;
487 void be_blur_pre(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
489 for (int y = 0; y < h; ++y)
491 for (int x = 0; x < w; ++x)
493 // This is equivalent to (value * 64 + 127) / 255 for all
494 // values from 0 to 256 inclusive. Assist vectorizing
495 // compilers by noting that all temporaries fit in 8 bits.
496 buf[y * stride + x] =
497 (uint8_t) ((buf[y * stride + x] >> 1) + 1) >> 1;
502 void be_blur_post(uint8_t *buf, intptr_t w, intptr_t h, intptr_t stride)
504 for (int y = 0; y < h; ++y)
506 for (int x = 0; x < w; ++x)
508 // This is equivalent to (value * 255 + 32) / 64 for all values
509 // from 0 to 96 inclusive, and we only care about 0 to 64.
510 uint8_t value = buf[y * stride + x];
511 buf[y * stride + x] = (value << 2) - (value > 32);
517 * To find these values, simulate blur on the border between two
518 * half-planes, one zero-filled (background) and the other filled
519 * with the maximum supported value (foreground). Keep incrementing
520 * the \be argument. The necessary padding is the distance by which
521 * the blurred foreground image extends beyond the original border
522 * and into the background. Initially it increases along with \be,
523 * but very soon it grinds to a halt. At some point, the blurred
524 * image actually reaches a stationary point and stays unchanged
525 * forever after, simply _shifting_ by one pixel for each \be
526 * step--moving in the direction of the non-zero half-plane and
527 * thus decreasing the necessary padding (although the large
528 * padding is still needed for intermediate results). In practice,
529 * images are finite rather than infinite like half-planes, but
530 * this can only decrease the required padding. Half-planes filled
531 * with extreme values are the theoretical limit of the worst case.
532 * Make sure to use the right pixel value range in the simulation!
534 int be_padding(int be)
542 return FFMAX(128 - be, 0);
545 int outline_to_bitmap2(ASS_Renderer *render_priv,
546 ASS_Outline *outline, ASS_Outline *border,
547 Bitmap **bm_g, Bitmap **bm_o)
549 assert(bm_g && bm_o);
551 *bm_g = *bm_o = NULL;
554 *bm_g = outline_to_bitmap(render_priv, outline, 1);
559 *bm_o = outline_to_bitmap(render_priv, border, 1);
569 * \brief Add two bitmaps together at a given position
570 * Uses additive blending, clipped to [0,255]. Pure C implementation.
572 void ass_add_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
573 uint8_t *src, intptr_t src_stride,
574 intptr_t height, intptr_t width)
577 uint8_t* end = dst + dst_stride * height;
579 for (unsigned j = 0; j < width; ++j) {
580 out = dst[j] + src[j];
581 dst[j] = FFMIN(out, 255);
588 void ass_sub_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
589 uint8_t *src, intptr_t src_stride,
590 intptr_t height, intptr_t width)
593 uint8_t* end = dst + dst_stride * height;
595 for (unsigned j = 0; j < width; ++j) {
596 out = dst[j] - src[j];
597 dst[j] = FFMAX(out, 0);
604 void ass_mul_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
605 uint8_t *src1, intptr_t src1_stride,
606 uint8_t *src2, intptr_t src2_stride,
607 intptr_t w, intptr_t h)
609 uint8_t* end = src1 + src1_stride * h;
611 for (unsigned x = 0; x < w; ++x) {
612 dst[x] = (src1[x] * src2[x] + 255) >> 8;