2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
4 * This file is part of libass.
6 * libass is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * libass is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with libass; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "ass_utils.h"
29 #include "ass_bitmap.h"
31 struct ass_synth_priv
{
44 static const unsigned int maxcolor
= 255;
45 static const unsigned base
= 256;
47 static int generate_tables(ASS_SynthPriv
*priv
, double radius
)
49 double A
= log(1.0 / base
) / (radius
* radius
* 2);
51 double volume_diff
, volume_factor
= 0;
54 if (priv
->radius
== radius
)
57 priv
->radius
= radius
;
59 priv
->g_r
= ceil(radius
);
60 priv
->g_w
= 2 * priv
->g_r
+ 1;
63 priv
->g
= realloc(priv
->g
, priv
->g_w
* sizeof(unsigned));
64 priv
->gt2
= realloc(priv
->gt2
, 256 * priv
->g_w
* sizeof(unsigned));
65 if (priv
->g
== NULL
|| priv
->gt2
== NULL
) {
71 // gaussian curve with volume = 256
72 for (volume_diff
= 10000000; volume_diff
> 0.0000001;
74 volume_factor
+= volume_diff
;
76 for (i
= 0; i
< priv
->g_w
; ++i
) {
78 (unsigned) (exp(A
* (i
- priv
->g_r
) * (i
- priv
->g_r
)) *
83 volume_factor
-= volume_diff
;
86 for (i
= 0; i
< priv
->g_w
; ++i
) {
88 (unsigned) (exp(A
* (i
- priv
->g_r
) * (i
- priv
->g_r
)) *
94 for (mx
= 0; mx
< priv
->g_w
; mx
++) {
95 for (i
= 0; i
< 256; i
++) {
96 priv
->gt2
[mx
+ i
* priv
->g_w
] = i
* priv
->g
[mx
];
104 static void resize_tmp(ASS_SynthPriv
*priv
, int w
, int h
)
106 if (priv
->tmp_w
>= w
&& priv
->tmp_h
>= h
)
108 if (priv
->tmp_w
== 0)
110 if (priv
->tmp_h
== 0)
112 while (priv
->tmp_w
< w
)
114 while (priv
->tmp_h
< h
)
118 priv
->tmp
= malloc((priv
->tmp_w
+ 1) * priv
->tmp_h
* sizeof(short));
121 ASS_SynthPriv
*ass_synth_init(double radius
)
123 ASS_SynthPriv
*priv
= calloc(1, sizeof(ASS_SynthPriv
));
124 generate_tables(priv
, radius
);
128 void ass_synth_done(ASS_SynthPriv
*priv
)
139 static Bitmap
*alloc_bitmap(int w
, int h
)
142 bm
= calloc(1, sizeof(Bitmap
));
143 bm
->buffer
= malloc(w
* h
);
146 bm
->left
= bm
->top
= 0;
150 void ass_free_bitmap(Bitmap
*bm
)
159 static Bitmap
*copy_bitmap(const Bitmap
*src
)
161 Bitmap
*dst
= alloc_bitmap(src
->w
, src
->h
);
162 dst
->left
= src
->left
;
164 memcpy(dst
->buffer
, src
->buffer
, src
->w
* src
->h
);
168 static int check_glyph_area(ASS_Library
*library
, FT_Glyph glyph
)
172 FT_Glyph_Get_CBox(glyph
, FT_GLYPH_BBOX_TRUNCATE
, &bbox
);
173 dx
= bbox
.xMax
- bbox
.xMin
;
174 dy
= bbox
.yMax
- bbox
.yMin
;
175 if (dx
* dy
> 8000000) {
176 ass_msg(library
, MSGL_WARN
, "Glyph bounding box too large: %dx%dpx",
183 static Bitmap
*glyph_to_bitmap_internal(ASS_Library
*library
,
184 FT_Glyph glyph
, int bord
)
195 if (check_glyph_area(library
, glyph
))
197 error
= FT_Glyph_To_Bitmap(&glyph
, FT_RENDER_MODE_NORMAL
, 0, 0);
199 ass_msg(library
, MSGL_WARN
, "FT_Glyph_To_Bitmap error %d",
204 bg
= (FT_BitmapGlyph
) glyph
;
206 if (bit
->pixel_mode
!= FT_PIXEL_MODE_GRAY
) {
207 ass_msg(library
, MSGL_WARN
, "Unsupported pixel mode: %d",
208 (int) (bit
->pixel_mode
));
209 FT_Done_Glyph(glyph
);
215 bm
= alloc_bitmap(w
+ 2 * bord
, h
+ 2 * bord
);
216 memset(bm
->buffer
, 0, bm
->w
* bm
->h
);
217 bm
->left
= bg
->left
- bord
;
218 bm
->top
= -bg
->top
- bord
;
221 dst
= bm
->buffer
+ bord
+ bm
->w
* bord
;
222 for (i
= 0; i
< h
; ++i
) {
228 FT_Done_Glyph(glyph
);
233 * \brief fix outline bitmap and generate shadow bitmap
234 * Two things are done here:
235 * 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases.
236 * 2. Shadow bitmap is created as a sum of glyph and outline bitmaps.
238 static Bitmap
*fix_outline_and_shadow(Bitmap
*bm_g
, Bitmap
*bm_o
)
241 const int l
= bm_o
->left
> bm_g
->left
? bm_o
->left
: bm_g
->left
;
242 const int t
= bm_o
->top
> bm_g
->top
? bm_o
->top
: bm_g
->top
;
244 bm_o
->left
+ bm_o
->w
<
245 bm_g
->left
+ bm_g
->w
? bm_o
->left
+ bm_o
->w
: bm_g
->left
+ bm_g
->w
;
247 bm_o
->top
+ bm_o
->h
<
248 bm_g
->top
+ bm_g
->h
? bm_o
->top
+ bm_o
->h
: bm_g
->top
+ bm_g
->h
;
250 Bitmap
*bm_s
= copy_bitmap(bm_o
);
253 bm_g
->buffer
+ (t
- bm_g
->top
) * bm_g
->w
+ (l
- bm_g
->left
);
255 bm_o
->buffer
+ (t
- bm_o
->top
) * bm_o
->w
+ (l
- bm_o
->left
);
257 bm_s
->buffer
+ (t
- bm_s
->top
) * bm_s
->w
+ (l
- bm_s
->left
);
259 for (y
= 0; y
< b
- t
; ++y
) {
260 for (x
= 0; x
< r
- l
; ++x
) {
261 unsigned char c_g
, c_o
;
264 o
[x
] = (c_o
> c_g
) ? c_o
- (c_g
/ 2) : 0;
265 s
[x
] = (c_o
< 0xFF - c_g
) ? c_o
+ c_g
: 0xFF;
277 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
278 * expressed in 26.6 fixed point
280 static void shift_bitmap(unsigned char *buf
, int w
, int h
, int shift_x
,
285 // Shift in x direction
287 for (y
= 0; y
< h
; y
++) {
288 for (x
= w
- 1; x
> 0; x
--) {
289 b
= (buf
[x
+ y
* w
- 1] * shift_x
) >> 6;
290 buf
[x
+ y
* w
- 1] -= b
;
294 } else if (shift_x
< 0) {
296 for (y
= 0; y
< h
; y
++) {
297 for (x
= 0; x
< w
- 1; x
++) {
298 b
= (buf
[x
+ y
* w
+ 1] * shift_x
) >> 6;
299 buf
[x
+ y
* w
+ 1] -= b
;
305 // Shift in y direction
307 for (x
= 0; x
< w
; x
++) {
308 for (y
= h
- 1; y
> 0; y
--) {
309 b
= (buf
[x
+ (y
- 1) * w
] * shift_y
) >> 6;
310 buf
[x
+ (y
- 1) * w
] -= b
;
314 } else if (shift_y
< 0) {
316 for (x
= 0; x
< w
; x
++) {
317 for (y
= 0; y
< h
- 1; y
++) {
318 b
= (buf
[x
+ (y
+ 1) * w
] * shift_y
) >> 6;
319 buf
[x
+ (y
+ 1) * w
] -= b
;
327 * Gaussian blur. An fast pure C implementation from MPlayer.
329 static void ass_gauss_blur(unsigned char *buffer
, unsigned short *tmp2
,
330 int width
, int height
, int stride
, int *m2
,
336 unsigned char *s
= buffer
;
337 unsigned short *t
= tmp2
+ 1;
338 for (y
= 0; y
< height
; y
++) {
339 memset(t
- 1, 0, (width
+ 1) * sizeof(short));
341 for (x
= 0; x
< r
; x
++) {
342 const int src
= s
[x
];
344 register unsigned short *dstp
= t
+ x
- r
;
346 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
347 for (mx
= r
- x
; mx
< mwidth
; mx
++) {
353 for (; x
< width
- r
; x
++) {
354 const int src
= s
[x
];
356 register unsigned short *dstp
= t
+ x
- r
;
358 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
359 for (mx
= 0; mx
< mwidth
; mx
++) {
365 for (; x
< width
; x
++) {
366 const int src
= s
[x
];
368 register unsigned short *dstp
= t
+ x
- r
;
370 const int x2
= r
+ width
- x
;
371 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
372 for (mx
= 0; mx
< x2
; mx
++) {
383 for (x
= 0; x
< width
; x
++) {
384 for (y
= 0; y
< r
; y
++) {
385 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
388 register unsigned short *dstp
= srcp
- 1 + width
+ 1;
389 const int src2
= (src
+ 128) >> 8;
390 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
394 for (mx
= r
- 1; mx
< mwidth
; mx
++) {
400 for (; y
< height
- r
; y
++) {
401 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
404 register unsigned short *dstp
= srcp
- 1 - r
* (width
+ 1);
405 const int src2
= (src
+ 128) >> 8;
406 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
410 for (mx
= 0; mx
< mwidth
; mx
++) {
416 for (; y
< height
; y
++) {
417 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
420 const int y2
= r
+ height
- y
;
421 register unsigned short *dstp
= srcp
- 1 - r
* (width
+ 1);
422 const int src2
= (src
+ 128) >> 8;
423 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
427 for (mx
= 0; mx
< y2
; mx
++) {
438 for (y
= 0; y
< height
; y
++) {
439 for (x
= 0; x
< width
; x
++) {
448 * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
449 * This blur is the same as the one employed by vsfilter.
451 static void be_blur(unsigned char *buf
, int w
, int h
)
454 unsigned int old_sum
, new_sum
;
456 for (y
= 0; y
< h
; y
++) {
457 old_sum
= 2 * buf
[y
* w
];
458 for (x
= 0; x
< w
- 1; x
++) {
459 new_sum
= buf
[y
* w
+ x
] + buf
[y
* w
+ x
+ 1];
460 buf
[y
* w
+ x
] = (old_sum
+ new_sum
) >> 2;
465 for (x
= 0; x
< w
; x
++) {
466 old_sum
= 2 * buf
[x
];
467 for (y
= 0; y
< h
- 1; y
++) {
468 new_sum
= buf
[y
* w
+ x
] + buf
[(y
+ 1) * w
+ x
];
469 buf
[y
* w
+ x
] = (old_sum
+ new_sum
) >> 2;
475 int glyph_to_bitmap(ASS_Library
*library
, ASS_SynthPriv
*priv_blur
,
476 FT_Glyph glyph
, FT_Glyph outline_glyph
,
477 Bitmap
**bm_g
, Bitmap
**bm_o
, Bitmap
**bm_s
,
478 int be
, double blur_radius
, FT_Vector shadow_offset
,
482 int bbord
= be
> 0 ? sqrt(2 * be
) : 0;
483 int gbord
= blur_radius
> 0.0 ? blur_radius
+ 1 : 0;
484 int bord
= FFMAX(bbord
, gbord
);
485 if (bord
== 0 && (shadow_offset
.x
|| shadow_offset
.y
))
488 assert(bm_g
&& bm_o
&& bm_s
);
490 *bm_g
= *bm_o
= *bm_s
= 0;
493 *bm_g
= glyph_to_bitmap_internal(library
, glyph
, bord
);
498 *bm_o
= glyph_to_bitmap_internal(library
, outline_glyph
, bord
);
504 // Apply box blur (multiple passes, if requested)
507 be_blur((*bm_o
)->buffer
, (*bm_o
)->w
, (*bm_o
)->h
);
509 be_blur((*bm_g
)->buffer
, (*bm_g
)->w
, (*bm_g
)->h
);
512 // Apply gaussian blur
513 if (blur_radius
> 0.0) {
515 resize_tmp(priv_blur
, (*bm_o
)->w
, (*bm_o
)->h
);
517 resize_tmp(priv_blur
, (*bm_g
)->w
, (*bm_g
)->h
);
518 generate_tables(priv_blur
, blur_radius
);
520 ass_gauss_blur((*bm_o
)->buffer
, priv_blur
->tmp
,
521 (*bm_o
)->w
, (*bm_o
)->h
, (*bm_o
)->w
,
522 (int *) priv_blur
->gt2
, priv_blur
->g_r
,
525 ass_gauss_blur((*bm_g
)->buffer
, priv_blur
->tmp
,
526 (*bm_g
)->w
, (*bm_g
)->h
, (*bm_g
)->w
,
527 (int *) priv_blur
->gt2
, priv_blur
->g_r
,
531 if (*bm_o
&& border_style
== 3)
532 *bm_s
= copy_bitmap(*bm_o
);
534 *bm_s
= fix_outline_and_shadow(*bm_g
, *bm_o
);
536 *bm_s
= copy_bitmap(*bm_g
);
538 shift_bitmap((*bm_s
)->buffer
, (*bm_s
)->w
,(*bm_s
)->h
,
539 shadow_offset
.x
, shadow_offset
.y
);