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
235 * The glyph bitmap is subtracted from outline bitmap. This way looks much
236 * better in some cases.
238 static void fix_outline(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
;
251 bm_g
->buffer
+ (t
- bm_g
->top
) * bm_g
->w
+ (l
- bm_g
->left
);
253 bm_o
->buffer
+ (t
- bm_o
->top
) * bm_o
->w
+ (l
- bm_o
->left
);
255 for (y
= 0; y
< b
- t
; ++y
) {
256 for (x
= 0; x
< r
- l
; ++x
) {
257 unsigned char c_g
, c_o
;
260 o
[x
] = (c_o
> c_g
) ? c_o
- (c_g
/ 2) : 0;
268 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
269 * expressed in 26.6 fixed point
271 static void shift_bitmap(unsigned char *buf
, int w
, int h
, int shift_x
,
276 // Shift in x direction
278 for (y
= 0; y
< h
; y
++) {
279 for (x
= w
- 1; x
> 0; x
--) {
280 b
= (buf
[x
+ y
* w
- 1] * shift_x
) >> 6;
281 buf
[x
+ y
* w
- 1] -= b
;
285 } else if (shift_x
< 0) {
287 for (y
= 0; y
< h
; y
++) {
288 for (x
= 0; x
< w
- 1; x
++) {
289 b
= (buf
[x
+ y
* w
+ 1] * shift_x
) >> 6;
290 buf
[x
+ y
* w
+ 1] -= b
;
296 // Shift in y direction
298 for (x
= 0; x
< w
; x
++) {
299 for (y
= h
- 1; y
> 0; y
--) {
300 b
= (buf
[x
+ (y
- 1) * w
] * shift_y
) >> 6;
301 buf
[x
+ (y
- 1) * w
] -= b
;
305 } else if (shift_y
< 0) {
307 for (x
= 0; x
< w
; x
++) {
308 for (y
= 0; y
< h
- 1; y
++) {
309 b
= (buf
[x
+ (y
+ 1) * w
] * shift_y
) >> 6;
310 buf
[x
+ (y
+ 1) * w
] -= b
;
318 * Gaussian blur. An fast pure C implementation from MPlayer.
320 static void ass_gauss_blur(unsigned char *buffer
, unsigned short *tmp2
,
321 int width
, int height
, int stride
, int *m2
,
327 unsigned char *s
= buffer
;
328 unsigned short *t
= tmp2
+ 1;
329 for (y
= 0; y
< height
; y
++) {
330 memset(t
- 1, 0, (width
+ 1) * sizeof(short));
332 for (x
= 0; x
< r
; x
++) {
333 const int src
= s
[x
];
335 register unsigned short *dstp
= t
+ x
- r
;
337 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
338 for (mx
= r
- x
; mx
< mwidth
; mx
++) {
344 for (; x
< width
- r
; x
++) {
345 const int src
= s
[x
];
347 register unsigned short *dstp
= t
+ x
- r
;
349 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
350 for (mx
= 0; mx
< mwidth
; mx
++) {
356 for (; x
< width
; x
++) {
357 const int src
= s
[x
];
359 register unsigned short *dstp
= t
+ x
- r
;
361 const int x2
= r
+ width
- x
;
362 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
363 for (mx
= 0; mx
< x2
; mx
++) {
374 for (x
= 0; x
< width
; x
++) {
375 for (y
= 0; y
< r
; y
++) {
376 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
379 register unsigned short *dstp
= srcp
- 1 + width
+ 1;
380 const int src2
= (src
+ 128) >> 8;
381 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
385 for (mx
= r
- 1; mx
< mwidth
; mx
++) {
391 for (; y
< height
- r
; y
++) {
392 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
395 register unsigned short *dstp
= srcp
- 1 - r
* (width
+ 1);
396 const int src2
= (src
+ 128) >> 8;
397 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
401 for (mx
= 0; mx
< mwidth
; mx
++) {
407 for (; y
< height
; y
++) {
408 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
411 const int y2
= r
+ height
- y
;
412 register unsigned short *dstp
= srcp
- 1 - r
* (width
+ 1);
413 const int src2
= (src
+ 128) >> 8;
414 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
418 for (mx
= 0; mx
< y2
; mx
++) {
429 for (y
= 0; y
< height
; y
++) {
430 for (x
= 0; x
< width
; x
++) {
439 * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
440 * This blur is the same as the one employed by vsfilter.
442 static void be_blur(unsigned char *buf
, int w
, int h
)
445 unsigned int old_sum
, new_sum
;
447 for (y
= 0; y
< h
; y
++) {
448 old_sum
= 2 * buf
[y
* w
];
449 for (x
= 0; x
< w
- 1; x
++) {
450 new_sum
= buf
[y
* w
+ x
] + buf
[y
* w
+ x
+ 1];
451 buf
[y
* w
+ x
] = (old_sum
+ new_sum
) >> 2;
456 for (x
= 0; x
< w
; x
++) {
457 old_sum
= 2 * buf
[x
];
458 for (y
= 0; y
< h
- 1; y
++) {
459 new_sum
= buf
[y
* w
+ x
] + buf
[(y
+ 1) * w
+ x
];
460 buf
[y
* w
+ x
] = (old_sum
+ new_sum
) >> 2;
466 int glyph_to_bitmap(ASS_Library
*library
, ASS_SynthPriv
*priv_blur
,
467 FT_Glyph glyph
, FT_Glyph outline_glyph
,
468 Bitmap
**bm_g
, Bitmap
**bm_o
, Bitmap
**bm_s
,
469 int be
, double blur_radius
, FT_Vector shadow_offset
,
473 int bbord
= be
> 0 ? sqrt(2 * be
) : 0;
474 int gbord
= blur_radius
> 0.0 ? blur_radius
+ 1 : 0;
475 int bord
= FFMAX(bbord
, gbord
);
476 if (bord
== 0 && (shadow_offset
.x
|| shadow_offset
.y
))
479 assert(bm_g
&& bm_o
&& bm_s
);
481 *bm_g
= *bm_o
= *bm_s
= 0;
484 *bm_g
= glyph_to_bitmap_internal(library
, glyph
, bord
);
489 *bm_o
= glyph_to_bitmap_internal(library
, outline_glyph
, bord
);
495 // Apply box blur (multiple passes, if requested)
498 be_blur((*bm_o
)->buffer
, (*bm_o
)->w
, (*bm_o
)->h
);
500 be_blur((*bm_g
)->buffer
, (*bm_g
)->w
, (*bm_g
)->h
);
503 // Apply gaussian blur
504 if (blur_radius
> 0.0) {
506 resize_tmp(priv_blur
, (*bm_o
)->w
, (*bm_o
)->h
);
508 resize_tmp(priv_blur
, (*bm_g
)->w
, (*bm_g
)->h
);
509 generate_tables(priv_blur
, blur_radius
);
511 ass_gauss_blur((*bm_o
)->buffer
, priv_blur
->tmp
,
512 (*bm_o
)->w
, (*bm_o
)->h
, (*bm_o
)->w
,
513 (int *) priv_blur
->gt2
, priv_blur
->g_r
,
516 ass_gauss_blur((*bm_g
)->buffer
, priv_blur
->tmp
,
517 (*bm_g
)->w
, (*bm_g
)->h
, (*bm_g
)->w
,
518 (int *) priv_blur
->gt2
, priv_blur
->g_r
,
522 // Create shadow and fix outline as needed
523 if (*bm_o
&& border_style
!= 3) {
524 *bm_s
= copy_bitmap(*bm_o
);
525 fix_outline(*bm_g
, *bm_o
);
527 *bm_s
= copy_bitmap(*bm_o
);
529 *bm_s
= copy_bitmap(*bm_g
);
533 shift_bitmap((*bm_s
)->buffer
, (*bm_s
)->w
,(*bm_s
)->h
,
534 shadow_offset
.x
, shadow_offset
.y
);