Commit | Line | Data |
---|---|---|
df4f9147 PG |
1 | /* |
2 | * drawtext.c: print text over the screen | |
3 | ****************************************************************** | |
4 | * Options: | |
5 | * -f <filename> font filename | |
6 | * -s <pixel_size> font size in pixels | |
7 | * -b print background | |
8 | * -o outline glyphs (use the bg color) | |
9 | * -x <pos> x position ( > 0) | |
10 | * -y <pos> y position ( > 0) | |
11 | * -t <text> text to print (will be passed to strftime()) | |
12 | * -c <#RRGGBB> foreground color ('internet' way) | |
13 | * -C <#RRGGBB> background color ('internet' way) | |
14 | * | |
15 | ****************************************************************** | |
16 | * | |
17 | * Author: Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br> | |
18 | * | |
19 | * This library is free software; you can redistribute it and/or | |
20 | * modify it under the terms of the GNU Lesser General Public | |
21 | * License as published by the Free Software Foundation; either | |
22 | * version 2 of the License, or (at your option) any later version. | |
23 | * | |
24 | * This library is distributed in the hope that it will be useful, | |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 | * Lesser General Public License for more details. | |
28 | * | |
29 | * You should have received a copy of the GNU Lesser General Public | |
30 | * License along with this library; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
32 | */ | |
33 | #include <stdio.h> | |
34 | #include <stdlib.h> | |
35 | #include <fcntl.h> | |
36 | #include <stdarg.h> | |
37 | #include <string.h> | |
38 | #include <unistd.h> | |
39 | #include <sys/time.h> | |
40 | #include <time.h> | |
41 | ||
42 | #include "framehook.h" | |
43 | ||
44 | #include <ft2build.h> | |
45 | #include FT_FREETYPE_H | |
46 | ||
47 | #define RGB_TO_YUV(rgb_color, yuv_color) { \ | |
48 | yuv_color[0] = ( 0.257 * rgb_color[0]) + (0.504 * rgb_color[1]) + (0.098 * rgb_color[2]) + 16; \ | |
49 | yuv_color[2] = ( 0.439 * rgb_color[0]) - (0.368 * rgb_color[1]) - (0.071 * rgb_color[2]) + 128; \ | |
50 | yuv_color[1] = (-0.148 * rgb_color[0]) - (0.291 * rgb_color[1]) + (0.439 * rgb_color[2]) + 128; \ | |
51 | } | |
52 | ||
53 | #define COPY_3(dst,src) { \ | |
54 | dst[0]=src[0]; \ | |
55 | dst[1]=src[1]; \ | |
56 | dst[2]=src[2]; \ | |
57 | } | |
58 | ||
59 | ||
60 | ||
61 | #define SET_PIXEL(picture, yuv_color, x, y) { \ | |
62 | picture->data[0][ (x) + (y)*picture->linesize[0] ] = yuv_color[0]; \ | |
63 | picture->data[1][ ((x/2) + (y/2)*picture->linesize[1]) ] = yuv_color[1]; \ | |
64 | picture->data[2][ ((x/2) + (y/2)*picture->linesize[2]) ] = yuv_color[2]; \ | |
65 | } | |
66 | ||
67 | #define GET_PIXEL(picture, yuv_color, x, y) { \ | |
68 | yuv_color[0] = picture->data[0][ (x) + (y)*picture->linesize[0] ]; \ | |
69 | yuv_color[1] = picture->data[1][ (x/2) + (y/2)*picture->linesize[1] ]; \ | |
70 | yuv_color[2] = picture->data[2][ (x/2) + (y/2)*picture->linesize[2] ]; \ | |
71 | } | |
72 | ||
73 | ||
74 | typedef struct { | |
75 | char *text; | |
76 | unsigned int x; | |
77 | unsigned int y; | |
78 | int bg; | |
79 | int outline; | |
80 | unsigned char bgcolor[3]; /* YUV */ | |
81 | unsigned char fgcolor[3]; /* YUV */ | |
82 | FT_Library ft_lib; | |
83 | FT_Face ft_face; | |
84 | } ContextInfo; | |
85 | ||
86 | ||
87 | void Release(void *ctx) | |
88 | { | |
89 | if (ctx) | |
90 | av_free(ctx); | |
91 | } | |
92 | ||
93 | ||
94 | int ParseColor(char *text, unsigned char yuv_color[3]) | |
95 | { | |
96 | char tmp[3]; | |
97 | unsigned char rgb_color[3]; | |
98 | int i; | |
99 | ||
100 | tmp[2] = '\0'; | |
101 | ||
102 | if ((!text) || (strlen(text) != 7) || (text[0] != '#') ) | |
103 | return -1; | |
104 | ||
105 | for (i=0; i < 3; i++) | |
106 | { | |
107 | tmp[0] = text[i*2+1]; | |
108 | tmp[1] = text[i*2+2]; | |
109 | ||
110 | rgb_color[i] = strtol(tmp, NULL, 16); | |
111 | } | |
112 | ||
113 | RGB_TO_YUV(rgb_color, yuv_color); | |
114 | ||
115 | printf("RGB=%d,%d,%d YUV=%d,%d,%d\n",rgb_color[0],rgb_color[1],rgb_color[2], | |
116 | yuv_color[0],yuv_color[1],yuv_color[2]); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | int Configure(void **ctxp, int argc, char *argv[]) | |
121 | { | |
122 | int c; | |
123 | int error; | |
124 | ContextInfo *ci=NULL; | |
125 | char *font=NULL; | |
126 | unsigned int size=16; | |
127 | ||
128 | *ctxp = av_mallocz(sizeof(ContextInfo)); | |
129 | ci = (ContextInfo *) *ctxp; | |
130 | ||
131 | /* configure Context Info */ | |
132 | ci->text = NULL; | |
133 | ci->x = ci->y = 0; | |
134 | ci->fgcolor[0]=255; | |
135 | ci->fgcolor[1]=128; | |
136 | ci->fgcolor[2]=128; | |
137 | ci->bgcolor[0]=0; | |
138 | ci->fgcolor[1]=128; | |
139 | ci->fgcolor[2]=128; | |
140 | ci->bg = 0; | |
141 | ci->outline = 0; | |
142 | ||
143 | optind = 0; | |
144 | while ((c = getopt(argc, argv, "f:t:x:y:s:c:C:bo")) > 0) { | |
145 | switch (c) { | |
146 | case 'f': | |
147 | font = optarg; | |
148 | break; | |
149 | case 't': | |
150 | ci->text = av_strdup(optarg); | |
151 | break; | |
152 | case 'x': | |
153 | ci->x = (unsigned int) atoi(optarg); | |
154 | break; | |
155 | case 'y': | |
156 | ci->y = (unsigned int) atoi(optarg); | |
157 | break; | |
158 | case 's': | |
159 | size = (unsigned int) atoi(optarg); | |
160 | break; | |
161 | case 'c': | |
162 | if (ParseColor(optarg, ci->fgcolor) == -1) | |
163 | { | |
164 | fprintf(stderr, "ERROR: Invalid foreground color: '%s'. You must specify the color in the internet way(packaged hex): #RRGGBB, ie: -c #ffffff (for white foreground)\n",optarg); | |
165 | return -1; | |
166 | } | |
167 | break; | |
168 | case 'C': | |
169 | if (ParseColor(optarg, ci->bgcolor) == -1) | |
170 | { | |
171 | fprintf(stderr, "ERROR: Invalid foreground color: '%s'. You must specify the color in the internet way(packaged hex): #RRGGBB, ie: -c #ffffff (for white foreground)\n",optarg); | |
172 | return -1; | |
173 | } | |
174 | break; | |
175 | case 'b': | |
176 | ci->bg=1; | |
177 | break; | |
178 | case 'o': | |
179 | ci->outline=1; | |
180 | break; | |
181 | case '?': | |
182 | fprintf(stderr, "ERROR: Unrecognized argument '%s'\n", argv[optind]); | |
183 | return -1; | |
184 | } | |
185 | } | |
186 | ||
187 | if (!ci->text) | |
188 | { | |
189 | fprintf(stderr,"ERROR: No text provided (-t text)\n"); | |
190 | return -1; | |
191 | } | |
192 | ||
193 | if (!font) | |
194 | { | |
195 | fprintf(stderr,"ERROR: No font file provided! (-f filename)\n"); | |
196 | return -1; | |
197 | } | |
198 | ||
199 | if ((error = FT_Init_FreeType(&(ci->ft_lib))) != 0) | |
200 | { | |
201 | fprintf(stderr,"ERROR: Could not load FreeType (error# %d)\n",error); | |
202 | return -1; | |
203 | } | |
204 | ||
205 | if ((error = FT_New_Face( ci->ft_lib, font, 0, &(ci->ft_face) )) != 0) | |
206 | { | |
207 | fprintf(stderr,"ERROR: Could not load face: %s (error# %d)\n",font, error); | |
208 | return -1; | |
209 | } | |
210 | ||
211 | if ((error = FT_Set_Pixel_Sizes( ci->ft_face, 0, size)) != 0) | |
212 | { | |
213 | fprintf(stderr,"ERROR: Could not set font size to %d pixels (error# %d)\n",size, error); | |
214 | return -1; | |
215 | } | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | ||
221 | inline void draw_glyph(AVPicture *picture, FT_Bitmap *bitmap, unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned char yuv_fgcolor[3], unsigned char yuv_bgcolor[3], int outline) | |
222 | { | |
223 | int r, c; | |
224 | int spixel, dpixel[3], in_glyph=0; | |
225 | ||
226 | if (bitmap->pixel_mode == ft_pixel_mode_mono) | |
227 | { | |
228 | in_glyph = 0; | |
229 | for (r=0; (r < bitmap->rows) && (r+y < height); r++) | |
230 | { | |
231 | for (c=0; (c < bitmap->width) && (c+x < width); c++) | |
232 | { | |
233 | /* pixel in the picture (destination) */ | |
234 | GET_PIXEL(picture, dpixel, (c+x), (y+r)); | |
235 | ||
236 | /* pixel in the glyph bitmap (source) */ | |
237 | spixel = bitmap->buffer[r*bitmap->pitch +c/8] & (0x80>>(c%8)); | |
238 | ||
239 | if (spixel) | |
240 | COPY_3(dpixel, yuv_fgcolor); | |
241 | ||
242 | if (outline) | |
243 | { | |
244 | /* border detection: */ | |
245 | if ( (!in_glyph) && (spixel) ) | |
246 | /* left border detected */ | |
247 | { | |
248 | in_glyph = 1; | |
249 | /* draw left pixel border */ | |
250 | if (c-1 >= 0) | |
251 | SET_PIXEL(picture, yuv_bgcolor, (c+x-1), (y+r)); | |
252 | } | |
253 | else if ( (in_glyph) && (!spixel) ) | |
254 | /* right border detected */ | |
255 | { | |
256 | in_glyph = 0; | |
257 | /* 'draw' right pixel border */ | |
258 | COPY_3(dpixel, yuv_bgcolor); | |
259 | } | |
260 | else if (in_glyph) | |
261 | /* see if we have a top/bottom border */ | |
262 | { | |
263 | /* top */ | |
264 | if ( (r-1 >= 0) && (! bitmap->buffer[(r-1)*bitmap->pitch +c/8] & (0x80>>(c%8))) ) | |
265 | /* we have a top border */ | |
266 | SET_PIXEL(picture, yuv_bgcolor, (c+x), (y+r-1)); | |
267 | ||
268 | /* bottom */ | |
269 | if ( (r+1 < height) && (! bitmap->buffer[(r+1)*bitmap->pitch +c/8] & (0x80>>(c%8))) ) | |
270 | /* we have a bottom border */ | |
271 | SET_PIXEL(picture, yuv_bgcolor, (c+x), (y+r+1)); | |
272 | ||
273 | } | |
274 | } | |
275 | ||
276 | SET_PIXEL(picture, dpixel, (c+x), (y+r)); | |
277 | } | |
278 | } | |
279 | } | |
280 | } | |
281 | ||
282 | ||
283 | void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, INT64 pts) | |
284 | { | |
285 | ContextInfo *ci = (ContextInfo *) ctx; | |
286 | FT_Face face = ci->ft_face; | |
287 | FT_GlyphSlot slot = face->glyph; | |
288 | char *text = ci->text; | |
289 | int x = 0, y = 0, i=0, j=0, size=0, error; | |
290 | int str_w, str_h; | |
291 | char buff[1000]; | |
292 | time_t now = time(0); | |
293 | ||
294 | strftime(buff, sizeof(buff), text, localtime(&now)); | |
295 | ||
296 | text = buff; | |
297 | ||
298 | size = strlen(text); | |
299 | ||
300 | x = ci->x; | |
301 | y = ci->y; | |
302 | ||
303 | ||
304 | /* measure string size */ | |
305 | str_w = str_h = 0; | |
306 | for (i=0; i < size; i++) | |
307 | { | |
308 | /* load glyph image into the slot (erase previous one) */ | |
309 | error = FT_Load_Char( face, text[i], FT_LOAD_RENDER | FT_LOAD_MONOCHROME ); | |
310 | if (error) continue; /* ignore errors */ | |
311 | ||
312 | str_w += slot->advance.x >> 6; | |
313 | ||
314 | if (slot->bitmap_top > str_h) | |
315 | str_h = slot->bitmap_top; | |
316 | ||
317 | } | |
318 | ||
319 | ||
320 | if (ci->bg) | |
321 | /* draw background */ | |
322 | for (j = 0; (j < str_h) && (j+y < height); j++) | |
323 | for (i = 0; (i < str_w) && (i+x < width); i++) | |
324 | { | |
325 | SET_PIXEL(picture, ci->bgcolor, (i+x), (y+j)); | |
326 | } | |
327 | ||
328 | /* Draw Glyphs */ | |
329 | for (i=0; i < size; i++) | |
330 | { | |
331 | /* load glyph image into the slot (erase previous one) */ | |
332 | error = FT_Load_Char( face, text[i], FT_LOAD_RENDER | FT_LOAD_MONOCHROME ); | |
333 | if (error) continue; /* ignore errors */ | |
334 | ||
335 | if (text[i] != '_') /* skip '_' (consider as space) */ | |
336 | /* now, draw to our target surface */ | |
337 | ||
338 | draw_glyph( picture, &slot->bitmap, | |
339 | x + slot->bitmap_left, | |
340 | y - slot->bitmap_top + str_h, | |
341 | width, height, | |
342 | ci->fgcolor, ci->bgcolor, | |
343 | ci->outline); | |
344 | ||
345 | /* increment pen position */ | |
346 | x += slot->advance.x >> 6; | |
347 | } | |
348 | ||
349 | ||
350 | } | |
351 |