1 // -*- c-basic-offset: 8; indent-tabs-mode: t -*-
2 // vim:ts=8:sw=8:noet:ai:
4 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
6 * This file is part of libass.
8 * libass is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * libass is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with libass; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include "ass_bitmap.h"
33 struct ass_synth_priv_s
{
46 static const unsigned int maxcolor
= 255;
47 static const unsigned base
= 256;
49 static int generate_tables(ass_synth_priv_t
* priv
, double radius
)
51 double A
= log(1.0/base
)/(radius
*radius
*2);
53 double volume_diff
, volume_factor
= 0;
56 if (priv
->radius
== radius
)
59 priv
->radius
= radius
;
61 priv
->g_r
= ceil(radius
);
62 priv
->g_w
= 2*priv
->g_r
+1;
65 priv
->g
= realloc(priv
->g
, priv
->g_w
* sizeof(unsigned));
66 priv
->gt2
= realloc(priv
->gt2
, 256 * priv
->g_w
* sizeof(unsigned));
67 if (priv
->g
==NULL
|| priv
->gt2
==NULL
) {
73 // gaussian curve with volume = 256
74 for (volume_diff
=10000000; volume_diff
>0.0000001; volume_diff
*=0.5){
75 volume_factor
+= volume_diff
;
77 for (i
= 0; i
<priv
->g_w
; ++i
) {
78 priv
->g
[i
] = (unsigned)(exp(A
* (i
-priv
->g_r
)*(i
-priv
->g_r
)) * volume_factor
+ .5);
81 if(volume
>256) volume_factor
-= volume_diff
;
84 for (i
= 0; i
<priv
->g_w
; ++i
) {
85 priv
->g
[i
] = (unsigned)(exp(A
* (i
-priv
->g_r
)*(i
-priv
->g_r
)) * volume_factor
+ .5);
90 for(mx
=0;mx
<priv
->g_w
;mx
++){
92 priv
->gt2
[mx
+i
*priv
->g_w
] = i
*priv
->g
[mx
];
100 static void resize_tmp(ass_synth_priv_t
* priv
, int w
, int h
)
102 if (priv
->tmp_w
>= w
&& priv
->tmp_h
>= h
)
104 if (priv
->tmp_w
== 0)
106 if (priv
->tmp_h
== 0)
108 while (priv
->tmp_w
< w
) priv
->tmp_w
*= 2;
109 while (priv
->tmp_h
< h
) priv
->tmp_h
*= 2;
112 priv
->tmp
= malloc((priv
->tmp_w
+ 1) * priv
->tmp_h
* sizeof(short));
115 ass_synth_priv_t
* ass_synth_init(double radius
)
117 ass_synth_priv_t
* priv
= calloc(1, sizeof(ass_synth_priv_t
));
118 generate_tables(priv
, radius
);
122 void ass_synth_done(ass_synth_priv_t
* priv
)
133 static bitmap_t
* alloc_bitmap(int w
, int h
)
136 bm
= calloc(1, sizeof(bitmap_t
));
137 bm
->buffer
= malloc(w
*h
);
140 bm
->left
= bm
->top
= 0;
144 void ass_free_bitmap(bitmap_t
* bm
)
147 if (bm
->buffer
) free(bm
->buffer
);
152 static bitmap_t
* copy_bitmap(const bitmap_t
* src
)
154 bitmap_t
* dst
= alloc_bitmap(src
->w
, src
->h
);
155 dst
->left
= src
->left
;
157 memcpy(dst
->buffer
, src
->buffer
, src
->w
* src
->h
);
161 static int check_glyph_area(FT_Glyph glyph
)
165 FT_Glyph_Get_CBox(glyph
, FT_GLYPH_BBOX_TRUNCATE
, &bbox
);
166 dx
= bbox
.xMax
- bbox
.xMin
;
167 dy
= bbox
.yMax
- bbox
.yMin
;
168 if (dx
* dy
> 8000000) {
169 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_GlyphBBoxTooLarge
, (int)dx
, (int)dy
);
175 static bitmap_t
* glyph_to_bitmap_internal(FT_Glyph glyph
, int bord
)
186 if (check_glyph_area(glyph
))
188 error
= FT_Glyph_To_Bitmap(&glyph
, FT_RENDER_MODE_NORMAL
, 0, 0);
190 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FT_Glyph_To_BitmapError
, error
);
194 bg
= (FT_BitmapGlyph
)glyph
;
196 if (bit
->pixel_mode
!= FT_PIXEL_MODE_GRAY
) {
197 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UnsupportedPixelMode
, (int)(bit
->pixel_mode
));
198 FT_Done_Glyph(glyph
);
204 bm
= alloc_bitmap(w
+ 2*bord
, h
+ 2*bord
);
205 memset(bm
->buffer
, 0, bm
->w
* bm
->h
);
206 bm
->left
= bg
->left
- bord
;
207 bm
->top
= - bg
->top
- bord
;
210 dst
= bm
->buffer
+ bord
+ bm
->w
* bord
;
211 for (i
= 0; i
< h
; ++i
) {
217 FT_Done_Glyph(glyph
);
222 * \brief fix outline bitmap and generate shadow bitmap
223 * Two things are done here:
224 * 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases.
225 * 2. Shadow bitmap is created as a sum of glyph and outline bitmaps.
227 static bitmap_t
* fix_outline_and_shadow(bitmap_t
* bm_g
, bitmap_t
* bm_o
)
230 const int l
= bm_o
->left
> bm_g
->left
? bm_o
->left
: bm_g
->left
;
231 const int t
= bm_o
->top
> bm_g
->top
? bm_o
->top
: bm_g
->top
;
232 const int r
= bm_o
->left
+ bm_o
->w
< bm_g
->left
+ bm_g
->w
? bm_o
->left
+ bm_o
->w
: bm_g
->left
+ bm_g
->w
;
233 const int b
= bm_o
->top
+ bm_o
->h
< bm_g
->top
+ bm_g
->h
? bm_o
->top
+ bm_o
->h
: bm_g
->top
+ bm_g
->h
;
235 bitmap_t
* bm_s
= copy_bitmap(bm_o
);
237 unsigned char* g
= bm_g
->buffer
+ (t
- bm_g
->top
) * bm_g
->w
+ (l
- bm_g
->left
);
238 unsigned char* o
= bm_o
->buffer
+ (t
- bm_o
->top
) * bm_o
->w
+ (l
- bm_o
->left
);
239 unsigned char* s
= bm_s
->buffer
+ (t
- bm_s
->top
) * bm_s
->w
+ (l
- bm_s
->left
);
241 for (y
= 0; y
< b
- t
; ++y
) {
242 for (x
= 0; x
< r
- l
; ++x
) {
243 unsigned char c_g
, c_o
;
246 o
[x
] = (c_o
> c_g
) ? c_o
- (c_g
/2) : 0;
247 s
[x
] = (c_o
< 0xFF - c_g
) ? c_o
+ c_g
: 0xFF;
259 * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
260 * This blur is the same as the one employed by vsfilter.
262 static void be_blur(unsigned char *buf
, int w
, int h
) {
264 unsigned int old_sum
, new_sum
;
266 for (y
=0; y
<h
; y
++) {
267 old_sum
= 2 * buf
[y
*w
];
268 for (x
=0; x
<w
-1; x
++) {
269 new_sum
= buf
[y
*w
+x
] + buf
[y
*w
+x
+1];
270 buf
[y
*w
+x
] = (old_sum
+ new_sum
) >> 2;
275 for (x
=0; x
<w
; x
++) {
276 old_sum
= 2 * buf
[x
];
277 for (y
=0; y
<h
-1; y
++) {
278 new_sum
= buf
[y
*w
+x
] + buf
[(y
+1)*w
+x
];
279 buf
[y
*w
+x
] = (old_sum
+ new_sum
) >> 2;
285 int glyph_to_bitmap(ass_synth_priv_t
* priv_blur
,
286 FT_Glyph glyph
, FT_Glyph outline_glyph
, bitmap_t
** bm_g
,
287 bitmap_t
** bm_o
, bitmap_t
** bm_s
, int be
, double blur_radius
)
289 int bord
= be
? (be
/4+1) : 0;
291 bord
= (blur_radius
> 0.0) ? blur_radius
: bord
;
293 assert(bm_g
&& bm_o
&& bm_s
);
295 *bm_g
= *bm_o
= *bm_s
= 0;
298 *bm_g
= glyph_to_bitmap_internal(glyph
, bord
);
303 *bm_o
= glyph_to_bitmap_internal(outline_glyph
, bord
);
305 ass_free_bitmap(*bm_g
);
310 resize_tmp(priv_blur
, (*bm_o
)->w
, (*bm_o
)->h
);
311 resize_tmp(priv_blur
, (*bm_g
)->w
, (*bm_g
)->h
);
316 be_blur((*bm_o
)->buffer
, (*bm_o
)->w
, (*bm_o
)->h
);
318 be_blur((*bm_g
)->buffer
, (*bm_g
)->w
, (*bm_g
)->h
);
321 if (blur_radius
> 0.0) {
322 generate_tables(priv_blur
, blur_radius
);
324 blur((*bm_o
)->buffer
, priv_blur
->tmp
, (*bm_o
)->w
, (*bm_o
)->h
, (*bm_o
)->w
, (int*)priv_blur
->gt2
, priv_blur
->g_r
, priv_blur
->g_w
);
326 blur((*bm_g
)->buffer
, priv_blur
->tmp
, (*bm_g
)->w
, (*bm_g
)->h
, (*bm_g
)->w
, (int*)priv_blur
->gt2
, priv_blur
->g_r
, priv_blur
->g_w
);
330 *bm_s
= fix_outline_and_shadow(*bm_g
, *bm_o
);
332 *bm_s
= copy_bitmap(*bm_g
);