4 * Copyright (C) 1998 - 2001 Rasca, Berlin
6 * This program 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 * This program 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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #if defined __GLIBC__ && __GLIBC__ >= 2
30 #include <libgen.h> /* basename */
32 #include <sys/types.h>
34 #include <sys/ioctl.h>
38 #include <linux/types.h>
39 #include <linux/videodev.h>
57 * some default values, change these to fit your needs
58 * most of these could be changed at runtime with config file
60 #define FMT_DEFAULT FMT_JPEG /* FMT_PPM, FMT_JPEG, FMT_PNG */
61 #define QUALITY_DEFAULT 65 /* JPEG default quality */
63 #define WIDTH_DEFAULT 240 /* default width and height of the image */
64 #define HEIGHT_DEFAULT 180
66 #define MODE_DEFAULT MODE_PLAIN /* MODE_GUI or MODE_PLAIN */
67 #define USEC_DEFAULT 20000 /* wait microseconds before capturing */
68 #define REFRESH_DEFAULT OFF /* don't use refreshing */
69 #define MIN_REFRESH 0.0 /* min refresh time, compile time option */
70 #define FREQLIST_DEFAULT "878;9076;9844;9460" /* default frequenzies */
71 #define MAX_TRY_OPEN 20 /* may be the device is locked, so try max*/
72 /* end of default values
73 * *********************
80 usage (char *pname, int width, int height, int color, int quality, int usec)
82 cgi_response (http_bad_request, "text/html");
84 "<title>w3cam - help</title><pre>W3Cam, Version %s\n\n"
85 "Usage: %s<?parameters>\n"
86 "CGI parameters (GET or POST):\n"
87 " help show this page\n"
88 " size=#x# geometry of picture "
90 " color={0|1} color or grey mode "
92 " input={tv|composite|composite2|s-video} define input source\n"
93 " quality={1-100} jpeg quality "
95 " format={ppm|png|jpeg} output format\n"
96 " freq=# define frequenzy for TV\n"
97 " usleep=# sleep # micro secs before cap. "
99 " mode=[gui|html] build a page with panel or plain html\n"
100 " refresh=#.# time in sec to refresh gui\n"
101 " norm={pal|ntsc|secam} tv norm\n"
102 " bgr={1|0} swap RGB to BGR (default: no)\n",
103 VERSION, basename(pname), width, height, color, quality, usec);
105 "\nCompiled in features:\n");
107 printf (" PNG file format\n");
110 printf (" JPEG file format\n");
113 printf ( " TTF/TimeStamp\n");
116 printf ( " SYSLOG support\n");
127 syslog (LOG_USER, "%s\n", info);
129 fprintf (stderr, "%s\n", info);
136 log2 (char *s1, char *s2)
139 syslog (LOG_USER, "%s %s\n", s1, s2);
141 fprintf (stderr, "%s %s\n", s1, s2);
146 * parse comma seperated frequency list
149 parse_list (char *freqs)
152 char *p = freqs, *end = NULL;
157 while ((p = strchr(p, ';')) != NULL) {
162 flist = malloc ((num + 1) * sizeof (char *));
165 for (i = 0; i < num; i++) {
170 end = strchr(p, ';');
173 flist[i] = malloc (len+1);
174 strncpy (flist[i], p, len);
181 * read rgb image from v4l device
182 * return: new allocated buffer
185 get_image (int dev, int width, int height, int input, int usec,
186 unsigned long freq, int palette)
188 struct video_mbuf vid_buf;
189 struct video_mmap vid_mmap;
195 if (input == IN_TV) {
197 if (ioctl (dev, VIDIOCSFREQ, &freq) == -1)
198 log2 ("ioctl (VIDIOCSREQ):", strerror(errno));
202 /* it seems some cards need a little bit time to come in
203 sync with the new settings */
207 if (palette != VIDEO_PALETTE_GREY) {
209 size = width * height * 3;
212 size = width * height * 1;
215 vid_mmap.format = palette;
217 if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) {
218 /* do a normal read()
220 struct video_window vid_win;
222 if (ioctl (dev, VIDIOCGWIN, &vid_win) != -1) {
223 vid_win.width = width;
224 vid_win.height = height;
225 if (ioctl (dev, VIDIOCSWIN, &vid_win) == -1) {
226 log2 ("ioctl(VIDIOCSWIN):", strerror(errno));
234 len = read (dev, map, size);
239 if (palette == VIDEO_PALETTE_YUV420P) {
241 convmap = malloc ( width * height * bpp );
242 v4l_yuv420p2rgb (convmap, map, width, height, bpp * 8);
243 memcpy (map, convmap, (size_t) width * height * bpp);
245 } else if (palette == VIDEO_PALETTE_YUV422P) {
247 convmap = malloc ( width * height * bpp );
248 v4l_yuv422p2rgb (convmap, map, width, height, bpp * 8);
249 memcpy (map, convmap, (size_t) width * height * bpp);
255 map = mmap (0, vid_buf.size, PROT_READ|PROT_WRITE,MAP_SHARED,dev,0);
256 if ((unsigned char *)-1 == (unsigned char *)map) {
257 log2 ("mmap():", strerror(errno));
261 vid_mmap.width = width;
262 vid_mmap.height =height;
263 if (ioctl (dev, VIDIOCMCAPTURE, &vid_mmap) == -1) {
264 log2 ("ioctl(VIDIOCMCAPTURE):", strerror(errno));
265 munmap (map, vid_buf.size);
268 if (ioctl (dev, VIDIOCSYNC, &vid_mmap.frame) == -1) {
269 log2 ("ioctl(VIDIOCSYNC):", strerror(errno));
270 munmap (map, vid_buf.size);
273 buff = (unsigned char *) malloc (size);
275 if (palette == VIDEO_PALETTE_YUV420P) {
276 v4l_yuv420p2rgb (buff, map, width, height, 24);
277 } else if (palette == VIDEO_PALETTE_YUV422P) {
278 v4l_yuv422p2rgb (buff, map, width, height, 24);
280 for (i = 0; i < size; i++)
286 munmap (map, vid_buf.size);
293 put_image_jpeg (char *image, int width, int height, int quality, int color)
296 register int x, y, line_width;
298 struct jpeg_compress_struct cjpeg;
299 struct jpeg_error_mgr jerr;
303 line_width = width * 3;
304 line = malloc (line_width);
310 cjpeg.err = jpeg_std_error(&jerr);
311 jpeg_create_compress (&cjpeg);
312 cjpeg.image_width = width;
313 cjpeg.image_height= height;
315 cjpeg.input_components = 3;
316 cjpeg.in_color_space = JCS_RGB;
318 cjpeg.input_components = 1;
319 cjpeg.in_color_space = JCS_GRAYSCALE;
321 jpeg_set_defaults (&cjpeg);
323 jpeg_simple_progression (&cjpeg);
324 jpeg_set_quality (&cjpeg, quality, TRUE);
325 cjpeg.dct_method = JDCT_FASTEST;
326 jpeg_stdio_dest (&cjpeg, stdout);
328 jpeg_start_compress (&cjpeg, TRUE);
332 for ( y = 0; y < height; y++) {
333 for (x = 0; x < line_width; x += 3) {
334 line[x] = image[x+2];
335 line[x+1] = image[x+1];
336 line[x+2] = image[x];
339 jpeg_write_scanlines (&cjpeg, row_ptr, 1);
343 for ( y = 0; y < height; y++) {
345 jpeg_write_scanlines (&cjpeg, row_ptr, 1);
349 jpeg_finish_compress (&cjpeg);
350 jpeg_destroy_compress (&cjpeg);
355 * write png image to stdout
358 put_image_png (char *image, int width, int height, int color)
364 png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
368 info_ptr = png_create_info_struct (png_ptr);
372 png_init_io (png_ptr, stdout);
374 png_set_IHDR (png_ptr, info_ptr, width, height,
375 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
376 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
377 png_set_bgr (png_ptr);
379 png_set_IHDR (png_ptr, info_ptr, width, height,
380 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
381 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
383 png_write_info (png_ptr, info_ptr);
387 for (y = 0; y < height; y++) {
388 png_write_row (png_ptr, p);
392 for (y = 0; y < height; y++) {
393 png_write_row (png_ptr, p);
397 png_write_end (png_ptr, info_ptr);
398 png_destroy_write_struct (&png_ptr, &info_ptr);
403 * write ppm image to stdout
406 put_image_ppm (char *image, int width, int height)
409 unsigned char *p = (unsigned char *)image;
410 printf ("P3\n%d %d\n%d\n", width, height, 255);
411 for (x = 0; x < width; x++) {
412 for (y = 0; y < height; y++) {
413 printf ("%03d %03d %03d ", p[2], p[1], p[0]);
426 palette2string (int palette) {
427 if (palette == VIDEO_PALETTE_RGB24)
429 if (palette == VIDEO_PALETTE_YUV420P)
431 if (palette == VIDEO_PALETTE_YUV422P)
433 if (palette == VIDEO_PALETTE_GREY)
439 * create a plain html page
442 make_html (int width, int height, int color, int input, int fmt, int quality,
443 float refresh, int us, int norm, int freq, char **freqs, int pal,
446 cgi_response (http_ok, "text/html");
447 /* cgi_refresh (refresh, NULL); */
448 cgi_html_start ("W3Cam");
449 printf ("<DIV class=image><IMG width=%d height=%d src=\"%s?"
450 "size=%dx%d&color=%s&id=%d&refresh=%1.2f&usleep=%d&freq=%d"
454 width, height, palette2string(pal),(int)time(NULL), refresh, us, freq);
455 if (input != INPUT_DEFAULT)
456 printf ("&input=%s", input == IN_TV? "tv" :
457 input == IN_COMP1 ? "composite" :
458 input == IN_COMP2? "composite2" : "s-video");
460 printf ("&norm=%s", norm == NORM_PAL ? "pal":
461 norm == NORM_NTSC ? "ntsc" : "secam");
462 if (fmt != FMT_DEFAULT)
463 printf ("&format=%s", fmt == FMT_PNG? "png":
464 fmt == FMT_JPEG? "jpeg": "ppm");
466 printf ("&quality=%d", quality);
469 printf ("\"></DIV>\n");
474 * create a html page with panel
477 make_gui (int width, int height, int color, int input, int fmt, int quality,
478 float refresh, int us, int norm, int freq, char **freqs, int pal,
481 make_html (width, height, color, input, fmt, quality,
482 refresh, us, norm, freq, freqs, pal, swapRGB);
484 printf ("<P><DIV class=panel><FORM>\n");
485 printf ("<INPUT type=hidden name=width value=%d>", width);
486 printf ("<INPUT type=hidden name=height value=%d>\n", height);
487 printf ("<INPUT type=hidden name=mode value=gui>");
488 printf ("<INPUT type=hidden name=quality value=%d>\n", quality);
489 printf ("<INPUT type=hidden name=usleep value=%d>\n", us);
491 printf ("Input:<SELECT name=input>\n");
492 printf ("<option%s value='-1'>Default</option>",
493 input == INPUT_DEFAULT? " selected":"");
494 printf ("<option%s>TV</option>",
495 input == IN_TV? " selected":"");
496 printf ("<option%s>Composite</option>",
497 input == IN_COMP1 ? " selected":"");
498 printf ("<option%s>Composite2</option>",
499 input == IN_COMP2? " selected":"");
500 printf ("<option%s>S-Video</option></SELECT>\n",
501 input == IN_SVIDEO? " selected":"");
503 if ((norm != OFF) && (input == IN_TV)) {
504 printf ("Norm:<SELECT name=norm>\n");
505 printf ("<option%s>PAL</option>", norm == NORM_PAL ? " selected":"");
506 printf ("<option%s>NTSC</option>", norm == NORM_NTSC? " selected":"");
507 printf ("<option%s>SECAM</option>", norm == NORM_SECAM?" selected":"");
508 printf ("<option>off</option>"); /* hide gui entry */
509 printf ("</SELECT>\n");
511 if (freqs && (input == IN_TV)) {
513 printf ("Freq:<SELECT name=freq>\n");
514 printf ("<option value=0>default</option>\n");
517 printf ("<option%s>%d</option>", freq == f ? " selected": "", f);
520 printf ("</SELECT>\n");
523 printf ("Format:<SELECT name=format>\n");
524 printf ("<option%s>PPM", fmt == FMT_PPM? " selected":"");
525 printf ("<option%s>PNG", fmt == FMT_PNG? " selected":"");
526 printf ("<option%s>JPEG</SELECT>\n", fmt == FMT_JPEG? " selected":"");
528 printf ("Size:<SELECT name=size>\n");
529 printf ("<option%s>80x60\n", width == 80 ? " selected": "");
530 printf ("<option%s>160x120", width == 160 ? " selected": "");
531 printf ("<option%s>240x180", width == 240 ? " selected": "");
532 printf ("<option%s>320x240", width == 320 ? " selected": "");
533 printf ("<option%s>400x300", width == 400 ? " selected": "");
534 printf ("<option%s>480x360", width == 480 ? " selected": "");
535 printf ("<option%s>640x480", width == 640 ? " selected": "");
536 printf ("<option%s>720x540", width == 720 ? " selected": "");
537 printf ("<option%s>768x576</SELECT>\n", width == 768 ? " selected": "");
539 printf ("Refresh (sec.):<SELECT name=refresh>\n");
540 printf ("<OPTION value=\"-1\">off\n");
541 printf ("<OPTION>0.0<OPTION>0.1<OPTION>0.5<OPTION>1.0<OPTION>2.0\n");
542 printf ("<OPTION>3.0<OPTION>4.0<OPTION>5.0\n");
543 printf ("<OPTION>10<OPTION>20<OPTION>40<OPTION>80\n");
545 printf ("<option selected>%1.2f</SELECT>\n", refresh);
547 printf ("</SELECT>\n");
549 printf ("<P><input type=submit value=Update></FORM></DIV><P>\n");
550 cgi_html_end ("<HR><DIV class=footer>w3cam, © rasca</DIV>");
556 on_signal (int signum)
570 main (int argc, char *argv[])
572 int width = WIDTH_DEFAULT, height = HEIGHT_DEFAULT, dev = -1;
573 char *val = NULL, **form = NULL, *image;
574 char *boundary = "--w3cam-ns-boundary--may-not-work-with-ie--";
575 char *freqlist = FREQLIST_DEFAULT;
577 char *device = VIDEO_DEV;
578 int max_try = MAX_TRY_OPEN; /* we try 20 times (5 sec) to open the device */
579 int quality = QUALITY_DEFAULT; /* default jpeg quality setting */
580 int input = INPUT_DEFAULT;
581 int norm = NORM_DEFAULT;
582 int mode = MODE_DEFAULT;
585 int palette = VIDEO_PALETTE_RGB24;
586 float refresh = REFRESH_DEFAULT;
587 float min_refresh = MIN_REFRESH;
588 int format = FMT_DEFAULT;
589 int usec = USEC_DEFAULT;
595 char *timestamp = NULL;
598 /* timestamp string */
599 char ts_buff[TS_MAX+1];
608 TT_Face_Properties properties;
609 TT_Instance instance;
610 TT_Glyph *glyphs = NULL;
616 openlog (argv[0], LOG_PID, LOG_USER);
619 if (signal (SIGTERM, on_signal) == SIG_ERR) {
620 log ("couldn't register handler for SIGTERM");
622 if (signal (SIGPIPE, on_signal) == SIG_ERR) {
623 log ("couldn't register handler for SIGPIPE");
625 /* check some values from the config file
627 val = cgi_cfg_value ("width");
628 if (val) width = atoi (val);
629 val = cgi_cfg_value ("height");
630 if (val) height = atoi (val);
631 val = cgi_cfg_value ("color");
633 if (strcasecmp (val, "yuv420p") == 0) {
635 palette = VIDEO_PALETTE_YUV420P;
636 } else if (strcasecmp (val, "yuv422p") == 0) {
638 palette = VIDEO_PALETTE_YUV422P;
639 } else if (*val == '0' || *val == 'g') {
641 palette = VIDEO_PALETTE_GREY;
646 val = cgi_cfg_value ("refresh");
647 if (val) refresh = atof (val);
648 val = cgi_cfg_value ("norm");
649 if (val) norm = atoi (val);
650 val = cgi_cfg_value ("input");
651 if (val) input = atoi (val);
652 val = cgi_cfg_value ("format");
653 if (val) format = atoi (val);
654 val = cgi_cfg_value ("quality");
655 if (val) quality = atoi (val);
656 val = cgi_cfg_value ("mode");
657 if (val) mode = atoi (val);
658 val = cgi_cfg_value ("usleep");
659 if (val) usec = atoi (val);
660 val = cgi_cfg_value ("freq");
661 if (val) freq = atoi (val);
662 val = cgi_cfg_value ("freqlist");
663 if (val) freqlist = val;
664 val = cgi_cfg_value ("protected");
665 if (val) protected = atoi (val);
666 val = cgi_cfg_value ("device");
667 if (val) device = val;
668 val = cgi_cfg_value ("bgr");
669 if (val) swapRGB = atoi (val);
671 val = cgi_cfg_value ("font");
673 val = cgi_cfg_value ("font_size");
674 if (val) font_size = atoi (val);
675 val = cgi_cfg_value ("timestamp");
676 if (val) timestamp = val;
677 val = cgi_cfg_value ("timestamp_border");
678 if (val) border = atoi (val);
679 val = cgi_cfg_value ("timestamp_blend");
680 if (val) blend = atoi (val);
681 val = cgi_cfg_value ("timestamp_align");
682 if (val) align = atoi (val);
685 /* parse the form, if there is any
688 form = cgi_parse_form ();
690 if (form && !protected) {
691 val = cgi_form_value ("help");
693 usage (argv[0], width, height, color, quality, usec);
695 val = cgi_form_value ("size");
697 sscanf (val, "%dx%d", &width, &height);
699 val = cgi_form_value ("color");
701 if (strcasecmp (val, "yuv420p") == 0) {
703 palette = VIDEO_PALETTE_YUV420P;
704 } else if (strcasecmp (val, "yuv422p") == 0) {
706 palette = VIDEO_PALETTE_YUV422P;
707 } else if (*val == '0' || *val == 'g') {
709 palette = VIDEO_PALETTE_GREY;
712 palette = VIDEO_PALETTE_RGB24;
715 val = cgi_form_value ("format");
717 if ((strcasecmp ("ppm", val) == 0) && color) {
719 } else if (strcasecmp ("png", val) == 0) {
721 } else if (strcasecmp ("jpeg", val) == 0) {
725 val = cgi_form_value ("refresh");
726 if (val) refresh = atof (val);
727 val = cgi_form_value ("quality");
728 if (val) quality = atoi (val);
729 val = cgi_form_value ("usleep");
730 if (val) usec = atoi (val);
731 val = cgi_form_value ("freq");
732 if (val) freq = atoi (val);
734 val = cgi_form_value ("mode");
736 if (strcmp ("gui", val) == 0)
738 else if (strcmp ("html", val) == 0)
743 val = cgi_form_value ("input");
745 if (strcasecmp ("tv", val) == 0) {
747 } else if (strcasecmp ("composite", val) == 0) {
749 } else if (strcasecmp ("composite2", val) ==0) {
751 } else if (strcasecmp ("s-video", val) == 0) {
754 input = INPUT_DEFAULT;
757 val = cgi_form_value ("norm");
759 if (strcasecmp ("pal", val) == 0) {
761 } else if (strcasecmp ("ntsc", val) == 0) {
763 } else if (strcasecmp ("secam", val) == 0) {
769 val = cgi_form_value ("bgr");
771 /* check for yes,true,1 */
772 if ((*val == '1') || (*val == 't') || (*val == 'y')) {
780 if ((refresh > OFF) && (refresh < min_refresh))
781 refresh = min_refresh;
785 if (mode == MODE_GUI) {
786 freqs = parse_list (freqlist);
787 make_gui (width, height, color, input, format, quality, refresh, usec,
788 norm,freq, freqs, palette, swapRGB);
791 if (mode == MODE_HTML) {
792 freqs = parse_list (freqlist);
793 make_html (width, height, color, input, format, quality, refresh, usec,
794 norm,freq, freqs, palette, swapRGB);
808 log ("unknown image format..!?");
812 if (font && timestamp) {
813 if (TT_Init_FreeType (&engine)) {
817 if (Face_Open (font, engine, &face, &properties, &instance, font_size)){
818 TT_Done_FreeType (engine);
826 palette = VIDEO_PALETTE_GREY;
828 /* open the video4linux device */
831 dev = open (device, O_RDWR);
833 log2 (device, strerror(errno));
835 cgi_response (http_ok, "text/plain");
836 printf ("Can't open device %s: %s\n",device,strerror(errno));
839 /* sleep 1/4 second */
842 max_try = MAX_TRY_OPEN; /* we may need it in a loop later .. */
846 if (v4l_mute_sound (dev) == -1) {
847 perror ("mute sound");
849 if (v4l_set_input (dev, input, norm) == -1) {
852 if (v4l_check_size (dev, &width, &height) == -1) {
855 /* if (v4l_check_palette (dev, &palette) == -1) {
859 image = get_image (dev, width, height, input, usec,freq, palette);
861 if (swapRGB && (palette == VIDEO_PALETTE_RGB24)) {
863 unsigned char *p, pt;
865 for (y = 0; y < height; y++) {
866 for (x = 0; x < width; x++) {
874 if (refresh != 0.0) {
877 if (refresh != OFF) {
878 cgi_multipart (boundary);
879 printf ("Content-Type: %s\n\n", mime);
881 cgi_response (http_ok, mime);
884 if (font && timestamp) {
887 ts_buff[TS_MAX] = '\0';
888 strftime (ts_buff, TS_MAX, timestamp, tm);
889 ts_len = strlen (ts_buff);
891 glyphs = Glyphs_Load (face, &properties, instance, ts_buff, ts_len);
892 Raster_Init(face, &properties,instance,ts_buff,ts_len, border, glyphs, &bit);
893 Raster_Small_Init (&sbit, &instance);
894 Render_String (glyphs, ts_buff, ts_len, &bit, &sbit, border);
896 int x, y, psize, i, x_off, y_off;
906 x_off = (width - bit.width) * psize;
911 y_off = height - bit.rows;
914 x_off = (width - bit.width) * psize;
915 y_off = height - bit.rows;
922 for (y = 0; y < bit.rows; y++) {
923 p = image + (y + y_off) * (width * psize) + x_off;
924 for (x = 0; x < bit.width; x++) {
925 switch (((unsigned char *)bit.bitmap)
926 [((bit.rows-y-1)*bit.cols)+x]) {
928 for (i = 0; i < psize; i++) {
929 *p = (255 * blend + *p * (100 - blend))/100;
934 for (i = 0; i < psize; i++) {
935 *p = (220 * blend + *p * (100 - blend))/100;
940 for (i = 0; i < psize; i++) {
941 *p = (162 * blend + *p * (100 - blend))/100;
946 for (i = 0; i < psize; i++) {
947 *p = (64 * blend + *p * (100 - blend))/100;
952 for (i = 0; i < psize; i++) {
953 *p = (0 * blend + *p * (100 - blend))/100;
963 Glyphs_Done (glyphs);
969 put_image_ppm (image, width, height);
970 printf ("\n%s\n", boundary);
973 put_image_png (image, width, height, color);
974 printf ("\n%s\n", boundary);
977 put_image_jpeg (image, width, height, quality, color);
978 printf ("\n%s\n", boundary);
981 /* should never be reached */
982 printf ("Unknown format (%d)\n", format);
983 printf ("\n%s\n", boundary);
987 if (refresh == 0.0) {
990 goto again_without_open;
992 if (refresh != OFF) {
994 usleep ((int)(refresh * 1000000));
998 cgi_response (http_ok, "text/plain");
999 printf ("Error: Can't get image\n");
1003 if (font && timestamp) {
1004 Face_Done (instance, face);
1005 TT_Done_FreeType (engine);