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
{
44 static const unsigned int maxcolor
= 255;
45 static const unsigned base
= 256;
46 static const double blur_radius
= 1.5;
48 static int generate_tables(ass_synth_priv_t
* priv
, double radius
)
50 double A
= log(1.0/base
)/(radius
*radius
*2);
52 double volume_diff
, volume_factor
= 0;
55 priv
->g_r
= ceil(radius
);
56 priv
->g_w
= 2*priv
->g_r
+1;
59 priv
->g
= malloc(priv
->g_w
* sizeof(unsigned));
60 priv
->gt2
= malloc(256 * priv
->g_w
* sizeof(unsigned));
61 if (priv
->g
==NULL
|| priv
->gt2
==NULL
) {
67 // gaussian curve with volume = 256
68 for (volume_diff
=10000000; volume_diff
>0.0000001; volume_diff
*=0.5){
69 volume_factor
+= volume_diff
;
71 for (i
= 0; i
<priv
->g_w
; ++i
) {
72 priv
->g
[i
] = (unsigned)(exp(A
* (i
-priv
->g_r
)*(i
-priv
->g_r
)) * volume_factor
+ .5);
75 if(volume
>256) volume_factor
-= volume_diff
;
78 for (i
= 0; i
<priv
->g_w
; ++i
) {
79 priv
->g
[i
] = (unsigned)(exp(A
* (i
-priv
->g_r
)*(i
-priv
->g_r
)) * volume_factor
+ .5);
84 for(mx
=0;mx
<priv
->g_w
;mx
++){
86 priv
->gt2
[mx
+i
*priv
->g_w
] = i
*priv
->g
[mx
];
94 static void resize_tmp(ass_synth_priv_t
* priv
, int w
, int h
)
96 if (priv
->tmp_w
>= w
&& priv
->tmp_h
>= h
)
100 if (priv
->tmp_h
== 0)
102 while (priv
->tmp_w
< w
) priv
->tmp_w
*= 2;
103 while (priv
->tmp_h
< h
) priv
->tmp_h
*= 2;
106 priv
->tmp
= malloc((priv
->tmp_w
+ 1) * priv
->tmp_h
* sizeof(short));
109 ass_synth_priv_t
* ass_synth_init(void)
111 ass_synth_priv_t
* priv
= calloc(1, sizeof(ass_synth_priv_t
));
112 generate_tables(priv
, blur_radius
);
116 void ass_synth_done(ass_synth_priv_t
* priv
)
127 static bitmap_t
* alloc_bitmap(int w
, int h
)
130 bm
= calloc(1, sizeof(bitmap_t
));
131 bm
->buffer
= malloc(w
*h
);
134 bm
->left
= bm
->top
= 0;
138 void ass_free_bitmap(bitmap_t
* bm
)
141 if (bm
->buffer
) free(bm
->buffer
);
146 static bitmap_t
* copy_bitmap(const bitmap_t
* src
)
148 bitmap_t
* dst
= alloc_bitmap(src
->w
, src
->h
);
149 dst
->left
= src
->left
;
151 memcpy(dst
->buffer
, src
->buffer
, src
->w
* src
->h
);
155 static int check_glyph_area(FT_Glyph glyph
)
159 FT_Glyph_Get_CBox(glyph
, FT_GLYPH_BBOX_TRUNCATE
, &bbox
);
160 dx
= bbox
.xMax
- bbox
.xMin
;
161 dy
= bbox
.yMax
- bbox
.yMin
;
162 if (dx
* dy
> 8000000) {
163 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_GlyphBBoxTooLarge
, (int)dx
, (int)dy
);
169 static bitmap_t
* glyph_to_bitmap_internal(FT_Glyph glyph
, int bord
)
180 if (check_glyph_area(glyph
))
182 error
= FT_Glyph_To_Bitmap(&glyph
, FT_RENDER_MODE_NORMAL
, 0, 0);
184 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_FT_Glyph_To_BitmapError
, error
);
188 bg
= (FT_BitmapGlyph
)glyph
;
190 if (bit
->pixel_mode
!= FT_PIXEL_MODE_GRAY
) {
191 mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_LIBASS_UnsupportedPixelMode
, (int)(bit
->pixel_mode
));
192 FT_Done_Glyph(glyph
);
198 bm
= alloc_bitmap(w
+ 2*bord
, h
+ 2*bord
);
199 memset(bm
->buffer
, 0, bm
->w
* bm
->h
);
200 bm
->left
= bg
->left
- bord
;
201 bm
->top
= - bg
->top
- bord
;
204 dst
= bm
->buffer
+ bord
+ bm
->w
* bord
;
205 for (i
= 0; i
< h
; ++i
) {
215 * \brief fix outline bitmap and generate shadow bitmap
216 * Two things are done here:
217 * 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases.
218 * 2. Shadow bitmap is created as a sum of glyph and outline bitmaps.
220 static bitmap_t
* fix_outline_and_shadow(bitmap_t
* bm_g
, bitmap_t
* 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
;
225 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
;
226 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
;
228 bitmap_t
* bm_s
= copy_bitmap(bm_o
);
230 unsigned char* g
= bm_g
->buffer
+ (t
- bm_g
->top
) * bm_g
->w
+ (l
- bm_g
->left
);
231 unsigned char* o
= bm_o
->buffer
+ (t
- bm_o
->top
) * bm_o
->w
+ (l
- bm_o
->left
);
232 unsigned char* s
= bm_s
->buffer
+ (t
- bm_s
->top
) * bm_s
->w
+ (l
- bm_s
->left
);
234 for (y
= 0; y
< b
- t
; ++y
) {
235 for (x
= 0; x
< r
- l
; ++x
) {
236 unsigned char c_g
, c_o
;
239 o
[x
] = (c_o
> c_g
) ? c_o
: 0;
240 s
[x
] = (c_o
< 0xFF - c_g
) ? c_o
+ c_g
: 0xFF;
251 int glyph_to_bitmap(ass_synth_priv_t
* priv
, FT_Glyph glyph
, FT_Glyph outline_glyph
,
252 bitmap_t
** bm_g
, bitmap_t
** bm_o
, bitmap_t
** bm_s
, int be
)
254 const int bord
= be
? ceil(blur_radius
) : 0;
256 assert(bm_g
&& bm_o
&& bm_s
);
258 *bm_g
= *bm_o
= *bm_s
= 0;
261 *bm_g
= glyph_to_bitmap_internal(glyph
, bord
);
266 *bm_o
= glyph_to_bitmap_internal(outline_glyph
, bord
);
268 ass_free_bitmap(*bm_g
);
273 resize_tmp(priv
, (*bm_o
)->w
, (*bm_o
)->h
);
274 resize_tmp(priv
, (*bm_g
)->w
, (*bm_g
)->h
);
278 blur((*bm_o
)->buffer
, priv
->tmp
, (*bm_o
)->w
, (*bm_o
)->h
, (*bm_o
)->w
, (int*)priv
->gt2
, priv
->g_r
, priv
->g_w
);
280 blur((*bm_g
)->buffer
, priv
->tmp
, (*bm_g
)->w
, (*bm_g
)->h
, (*bm_g
)->w
, (int*)priv
->gt2
, priv
->g_r
, priv
->g_w
);
284 *bm_s
= fix_outline_and_shadow(*bm_g
, *bm_o
);
286 *bm_s
= copy_bitmap(*bm_g
);