2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
3 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
5 * This file is part of libass.
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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
)
117 priv
->tmp
= malloc((priv
->tmp_w
+ 1) * priv
->tmp_h
* sizeof(short));
120 ASS_SynthPriv
*ass_synth_init(double radius
)
122 ASS_SynthPriv
*priv
= calloc(1, sizeof(ASS_SynthPriv
));
123 generate_tables(priv
, radius
);
127 void ass_synth_done(ASS_SynthPriv
*priv
)
135 static Bitmap
*alloc_bitmap(int w
, int h
)
138 unsigned s
= w
; // XXX: alignment
139 bm
= malloc(sizeof(Bitmap
));
140 bm
->buffer
= calloc(s
, h
);
144 bm
->left
= bm
->top
= 0;
148 void ass_free_bitmap(Bitmap
*bm
)
155 static Bitmap
*copy_bitmap(const Bitmap
*src
)
157 Bitmap
*dst
= alloc_bitmap(src
->w
, src
->h
);
158 dst
->left
= src
->left
;
160 memcpy(dst
->buffer
, src
->buffer
, src
->stride
* src
->h
);
164 Bitmap
*outline_to_bitmap(ASS_Library
*library
, FT_Library ftlib
,
165 FT_Outline
*outline
, int bord
)
173 FT_Outline_Get_CBox(outline
, &bbox
);
174 // move glyph to origin (0, 0)
177 FT_Outline_Translate(outline
, -bbox
.xMin
, -bbox
.yMin
);
179 bbox
.xMax
= (bbox
.xMax
+ 63) & ~63;
180 bbox
.yMax
= (bbox
.yMax
+ 63) & ~63;
181 w
= (bbox
.xMax
- bbox
.xMin
) >> 6;
182 h
= (bbox
.yMax
- bbox
.yMin
) >> 6;
187 if (w
* h
> 8000000) {
188 ass_msg(library
, MSGL_WARN
, "Glyph bounding box too large: %dx%dpx",
193 // allocate and set up bitmap
194 bm
= alloc_bitmap(w
+ 2 * bord
, h
+ 2 * bord
);
195 bm
->left
= bbox
.xMin
- bord
;
196 bm
->top
= -bbox
.yMax
- bord
;
199 bitmap
.pitch
= bm
->stride
;
200 bitmap
.buffer
= bm
->buffer
+ bord
+ bm
->stride
* bord
;
201 bitmap
.num_grays
= 256;
202 bitmap
.pixel_mode
= FT_PIXEL_MODE_GRAY
;
204 // render into target bitmap
205 if ((error
= FT_Outline_Get_Bitmap(ftlib
, outline
, &bitmap
))) {
206 ass_msg(library
, MSGL_WARN
, "Failed to rasterize glyph: %d\n", error
);
215 * \brief fix outline bitmap
217 * The glyph bitmap is subtracted from outline bitmap. This way looks much
218 * better in some cases.
220 static void fix_outline(Bitmap
*bm_g
, Bitmap
*bm_o
)
223 const int l
= bm_o
->left
> bm_g
->left
? bm_o
->left
: bm_g
->left
;
224 const int t
= bm_o
->top
> bm_g
->top
? bm_o
->top
: bm_g
->top
;
226 bm_o
->left
+ bm_o
->stride
<
227 bm_g
->left
+ bm_g
->stride
? bm_o
->left
+ bm_o
->stride
: bm_g
->left
+ bm_g
->stride
;
229 bm_o
->top
+ bm_o
->h
<
230 bm_g
->top
+ bm_g
->h
? bm_o
->top
+ bm_o
->h
: bm_g
->top
+ bm_g
->h
;
233 bm_g
->buffer
+ (t
- bm_g
->top
) * bm_g
->stride
+ (l
- bm_g
->left
);
235 bm_o
->buffer
+ (t
- bm_o
->top
) * bm_o
->stride
+ (l
- bm_o
->left
);
237 for (y
= 0; y
< b
- t
; ++y
) {
238 for (x
= 0; x
< r
- l
; ++x
) {
239 unsigned char c_g
, c_o
;
242 o
[x
] = (c_o
> c_g
) ? c_o
- (c_g
/ 2) : 0;
250 * \brief Shift a bitmap by the fraction of a pixel in x and y direction
251 * expressed in 26.6 fixed point
253 static void shift_bitmap(Bitmap
*bm
, int shift_x
, int shift_y
)
259 unsigned char *buf
= bm
->buffer
;
261 // Shift in x direction
263 for (y
= 0; y
< h
; y
++) {
264 for (x
= w
- 1; x
> 0; x
--) {
265 b
= (buf
[x
+ y
* s
- 1] * shift_x
) >> 6;
266 buf
[x
+ y
* s
- 1] -= b
;
270 } else if (shift_x
< 0) {
272 for (y
= 0; y
< h
; y
++) {
273 for (x
= 0; x
< w
- 1; x
++) {
274 b
= (buf
[x
+ y
* s
+ 1] * shift_x
) >> 6;
275 buf
[x
+ y
* s
+ 1] -= b
;
281 // Shift in y direction
283 for (x
= 0; x
< w
; x
++) {
284 for (y
= h
- 1; y
> 0; y
--) {
285 b
= (buf
[x
+ (y
- 1) * s
] * shift_y
) >> 6;
286 buf
[x
+ (y
- 1) * s
] -= b
;
290 } else if (shift_y
< 0) {
292 for (x
= 0; x
< w
; x
++) {
293 for (y
= 0; y
< h
- 1; y
++) {
294 b
= (buf
[x
+ (y
+ 1) * s
] * shift_y
) >> 6;
295 buf
[x
+ (y
+ 1) * s
] -= b
;
303 * Gaussian blur. An fast pure C implementation from MPlayer.
305 static void ass_gauss_blur(unsigned char *buffer
, unsigned short *tmp2
,
306 int width
, int height
, int stride
, int *m2
,
312 unsigned char *s
= buffer
;
313 unsigned short *t
= tmp2
+ 1;
314 for (y
= 0; y
< height
; y
++) {
315 memset(t
- 1, 0, (width
+ 1) * sizeof(short));
317 for (x
= 0; x
< r
; x
++) {
318 const int src
= s
[x
];
320 register unsigned short *dstp
= t
+ x
- r
;
322 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
323 for (mx
= r
- x
; mx
< mwidth
; mx
++) {
329 for (; x
< width
- r
; x
++) {
330 const int src
= s
[x
];
332 register unsigned short *dstp
= t
+ x
- r
;
334 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
335 for (mx
= 0; mx
< mwidth
; mx
++) {
341 for (; x
< width
; x
++) {
342 const int src
= s
[x
];
344 register unsigned short *dstp
= t
+ x
- r
;
346 const int x2
= r
+ width
- x
;
347 unsigned *m3
= (unsigned *) (m2
+ src
* mwidth
);
348 for (mx
= 0; mx
< x2
; mx
++) {
359 for (x
= 0; x
< width
; x
++) {
360 for (y
= 0; y
< r
; y
++) {
361 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
364 register unsigned short *dstp
= srcp
- 1 + width
+ 1;
365 const int src2
= (src
+ 128) >> 8;
366 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
370 for (mx
= r
- 1; mx
< mwidth
; mx
++) {
376 for (; y
< height
- r
; y
++) {
377 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
380 register unsigned short *dstp
= srcp
- 1 - r
* (width
+ 1);
381 const int src2
= (src
+ 128) >> 8;
382 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
386 for (mx
= 0; mx
< mwidth
; mx
++) {
392 for (; y
< height
; y
++) {
393 unsigned short *srcp
= t
+ y
* (width
+ 1) + 1;
396 const int y2
= r
+ height
- y
;
397 register unsigned short *dstp
= srcp
- 1 - r
* (width
+ 1);
398 const int src2
= (src
+ 128) >> 8;
399 unsigned *m3
= (unsigned *) (m2
+ src2
* mwidth
);
403 for (mx
= 0; mx
< y2
; mx
++) {
414 for (y
= 0; y
< height
; y
++) {
415 for (x
= 0; x
< width
; x
++) {
424 * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
425 * This blur is the same as the one employed by vsfilter.
427 static void be_blur(Bitmap
*bm
)
432 unsigned char *buf
= bm
->buffer
;
434 unsigned int old_sum
, new_sum
;
436 for (y
= 0; y
< h
; y
++) {
437 old_sum
= 2 * buf
[y
* s
];
438 for (x
= 0; x
< w
- 1; x
++) {
439 new_sum
= buf
[y
* s
+ x
] + buf
[y
* s
+ x
+ 1];
440 buf
[y
* s
+ x
] = (old_sum
+ new_sum
) >> 2;
445 for (x
= 0; x
< w
; x
++) {
446 old_sum
= 2 * buf
[x
];
447 for (y
= 0; y
< h
- 1; y
++) {
448 new_sum
= buf
[y
* s
+ x
] + buf
[(y
+ 1) * s
+ x
];
449 buf
[y
* s
+ x
] = (old_sum
+ new_sum
) >> 2;
455 int outline_to_bitmap3(ASS_Library
*library
, ASS_SynthPriv
*priv_blur
,
456 FT_Library ftlib
, FT_Outline
*outline
, FT_Outline
*border
,
457 Bitmap
**bm_g
, Bitmap
**bm_o
, Bitmap
**bm_s
,
458 int be
, double blur_radius
, FT_Vector shadow_offset
,
462 int bbord
= be
> 0 ? sqrt(2 * be
) : 0;
463 int gbord
= blur_radius
> 0.0 ? blur_radius
+ 1 : 0;
464 int bord
= FFMAX(bbord
, gbord
);
465 if (bord
== 0 && (shadow_offset
.x
|| shadow_offset
.y
))
468 assert(bm_g
&& bm_o
&& bm_s
);
470 *bm_g
= *bm_o
= *bm_s
= 0;
473 *bm_g
= outline_to_bitmap(library
, ftlib
, outline
, bord
);
478 *bm_o
= outline_to_bitmap(library
, ftlib
, border
, bord
);
484 // Apply box blur (multiple passes, if requested)
492 // Apply gaussian blur
493 if (blur_radius
> 0.0) {
495 resize_tmp(priv_blur
, (*bm_o
)->w
, (*bm_o
)->h
);
497 resize_tmp(priv_blur
, (*bm_g
)->w
, (*bm_g
)->h
);
498 generate_tables(priv_blur
, blur_radius
);
500 ass_gauss_blur((*bm_o
)->buffer
, priv_blur
->tmp
,
501 (*bm_o
)->w
, (*bm_o
)->h
, (*bm_o
)->stride
,
502 (int *) priv_blur
->gt2
, priv_blur
->g_r
,
505 ass_gauss_blur((*bm_g
)->buffer
, priv_blur
->tmp
,
506 (*bm_g
)->w
, (*bm_g
)->h
, (*bm_g
)->stride
,
507 (int *) priv_blur
->gt2
, priv_blur
->g_r
,
511 // Create shadow and fix outline as needed
512 if (*bm_o
&& border_style
!= 3) {
513 *bm_s
= copy_bitmap(*bm_o
);
514 fix_outline(*bm_g
, *bm_o
);
516 *bm_s
= copy_bitmap(*bm_o
);
518 *bm_s
= copy_bitmap(*bm_g
);
522 shift_bitmap(*bm_s
, shadow_offset
.x
, shadow_offset
.y
);