/* test-freetype.c -- Measure fullscreen framebuffer write speed. Version 0.5 Copyright (C) 2003 Matthew Allum, Openedhand Ltd. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Matthew Allum mallum@openedhand.com === Compile with gcc `freetype-config --libs --cflags` test-freetype.c -o test-freetype */ #include #include #include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include "freetype/ftsynth.h" #define VERSION "0.1" #define DEFAULT_TEXT_STR "abcdefghijklmnopqrstuvwxyz" #define DEFAULT_FONT "/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf" #define DEFAULT_SIZE 20 #define DEFAULT_N_LINES 20 #define DEFAULT_N_CYCLES 100 static char *TextStr = DEFAULT_TEXT_STR; static char *TextFont = DEFAULT_FONT; static int TextSize = DEFAULT_SIZE; static int TextNLines = DEFAULT_N_LINES; static int con_fd = -1, fb_fd, last_vt = -1; static struct fb_fix_screeninfo fix; static struct fb_var_screeninfo vinfo; static struct fb_cmap cmap; static char *fbuffer; static int fb_fd=0; static int xres, yres; static int Verbose = 0; static int TotalCycles = 100; static char *defaultfbdevice = "/dev/fb0"; static char *defaultconsoledevice = "none"; static FT_Library FreetypeLibrary; static int WantMultiBlit = 0; static int framebuffer_open (char *fbdevice, char *consoledevice); static void framebuffer_blit(void); static void framebuffer_close(void); static unsigned long long GetTimeInMillis(void) { struct timeval tp; gettimeofday(&tp, 0); return (unsigned long long)(tp.tv_sec * 1000) + (tp.tv_usec / 1000); } static int framebuffer_open (char *fbdevice, char *consoledevice) { struct vt_stat vts; char vtname[128]; int fd, nr; unsigned short col[2]; int error; if (fbdevice == NULL) fbdevice = defaultfbdevice; if (consoledevice == NULL) consoledevice = defaultconsoledevice; if (strcmp(consoledevice,"none") != 0) { sprintf(vtname,"%s%d", consoledevice, 1); fd = open(vtname, O_WRONLY); if (fd < 0) { perror("open consoledevice"); return -1; } /* Now get next available terminal */ if (ioctl(fd, VT_OPENQRY, &nr) < 0) { perror("ioctl VT_OPENQRY"); return -1; } close(fd); /* Close initial */ /* Now open next available */ sprintf(vtname, "%s%d", consoledevice, nr); con_fd = open(vtname, O_RDWR | O_NDELAY); if (con_fd < 0) { perror("open tty"); return -1; } /* Get info about it */ if (ioctl(con_fd, VT_GETSTATE, &vts) == 0) last_vt = vts.v_active; /* Activate it */ if (ioctl(con_fd, VT_ACTIVATE, nr) < 0) { perror("VT_ACTIVATE"); close(con_fd); return -1; } /* Wait for it to activate */ if (ioctl(con_fd, VT_WAITACTIVE, nr) < 0) { perror("VT_WAITACTIVE"); close(con_fd); return -1; } /* Set the graphics mode */ if (ioctl(con_fd, KDSETMODE, KD_GRAPHICS) < 0) { perror("KDSETMODE"); close(con_fd); return -1; } } /* Now open the framebuffer */ fb_fd = open(fbdevice, O_RDWR); if (fb_fd == -1) { perror("open fbdevice"); return -1; } /* Get the framebuffer info */ if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fix) < 0) { perror("ioctl FBIOGET_FSCREENINFO"); close(fb_fd); return -1; } if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) { perror("ioctl FBIOGET_VSCREENINFO"); close(fb_fd); return -1; } xres = vinfo.xres; yres = vinfo.yres; cmap.start = 0; cmap.len = 2; cmap.red = col; cmap.green = col; cmap.blue = col; cmap.transp = NULL; col[0] = 0; col[1] = 0xffff; /* if(var.bits_per_pixel==8) { if (ioctl(fb_fd, FBIOPUTCMAP, &cmap) < 0) { perror("ioctl FBIOPUTCMAP"); close(fb_fd); return -1; } } */ if(vinfo.bits_per_pixel != 16) { perror("Framebuffer is not 16bpp."); close(fb_fd); return -1; } fbuffer = mmap(NULL, fix.smem_len, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fb_fd, 0); if (fbuffer == (char *)-1) { perror("mmap framebuffer"); close(fb_fd); return -1; } memset(fbuffer,0,fix.smem_len); /* freetype */ error = FT_Init_FreeType( &FreetypeLibrary ); if ( error ) { printf("Error in init\n"); exit(2); } return 0; } static void framebuffer_close(void) { munmap(fbuffer, fix.smem_len); close(fb_fd); if(con_fd != -1) { if (ioctl(con_fd, KDSETMODE, KD_TEXT) < 0) perror("KDSETMODE"); if (last_vt >= 0) if (ioctl(con_fd, VT_ACTIVATE, last_vt)) perror("VT_ACTIVATE"); close(con_fd); } } static void framebuffer_plot_pixel(int x, int y) { int r = 0xff, g = 0xff, b =0xff; int location = (x + vinfo.xoffset) * (vinfo.bits_per_pixel/8) + ((y + vinfo.yoffset) * fix.line_length); *((unsigned short int*)(fbuffer + location)) = 0xff; // (r<<11 | g << 5 | b); } static void framebuffer_blit(void) { int x = 0, y = 200, i, j, xx, yy, nchars; char *data = NULL, *c; int size = vinfo.yres * vinfo.xres * (vinfo.bits_per_pixel/8); unsigned long long start_clock,finish_clock,diff_clock; int cycles = 0, error; FT_Face face; data = malloc( size ); start_clock = GetTimeInMillis(); error = FT_New_Face( FreetypeLibrary, TextFont, 0, &face ); if ( error == FT_Err_Unknown_File_Format ) { printf("Unknown font format\n"); exit(-1); } if ( error == FT_Err_Cannot_Open_Stream ) { printf("Could not find/open font resource" ); exit(-1); } if (error) { printf("Error %d occured, exiting\n", error); } // FT_Set_Pixel_Sizes(face, 0, TextSize); /* Below assumes 72 dpi */ error = FT_Set_Char_Size( face, 0, 16*64, 72, 72); finish_clock = GetTimeInMillis(); if (Verbose) { printf("test-freetype: Font loaded, drawable + color created in %lli ms\n", finish_clock - start_clock); } start_clock = GetTimeInMillis(); for (j=0; jglyph->format != FT_GLYPH_FORMAT_BITMAP) { error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) { printf("WARNING: Glyph rendering error!\n"); } } for (yy=0; yy < face->glyph->bitmap.rows; yy++) for (xx=0; xx < face->glyph->bitmap.width; xx++) { if (face->glyph->bitmap.buffer[yy*face->glyph->bitmap.width + xx] > 0) { framebuffer_plot_pixel(x + xx, y + yy); } } c++; x += face->glyph->bitmap.width + 2; } y += face->glyph->bitmap.rows + 2; } } finish_clock = GetTimeInMillis(); diff_clock = finish_clock - start_clock; nchars = strlen(TextStr) * TextNLines * TotalCycles; printf("test-freetype: Total time %lli ms, %i glyphs rendered = approx %lli glyphs per second\n", diff_clock, nchars, ( 1000 * nchars ) / diff_clock); free(data); } static void usage(void) { fprintf(stderr, "test-freetype " VERSION "\n" "usage: test-freetype [options..]\n" "Options are;\n" "-display \n" "--verbose\n" "--text-str text to render ( defaults to alphabet )\n" "--font ttf font to use ( defaults to " DEFAULT_FONT ")\n" "--font-size font size\n" "--nlines Number of lines to draw per cycle\n" "--cycles number of times to runs the test ( default 100)\n" ); exit(1); } int main (int argc, char **argv) { int i; for (i = 1; i < argc; i++) { if (!strcmp ("--verbose", argv[i]) || !strcmp ("-v", argv[i])) { Verbose = 1; continue; } if (!strcmp ("--text-str", argv[i]) ) { if (++i>=argc) usage (); TextStr = argv[i]; continue; } if (!strcmp ("--font", argv[i]) ) { if (++i>=argc) usage (); TextFont = argv[i]; continue; } if (!strcmp ("--font-size", argv[i]) ) { if (++i>=argc) usage (); TextSize = atoi(argv[i]); continue; } if (!strcmp ("--nlines", argv[i]) ) { if (++i>=argc) usage (); TextNLines = atoi(argv[i]); if (TextNLines < 1) usage(); continue; } if (!strcmp ("--cycles", argv[i])) { if (++i>=argc) usage (); TotalCycles = atoi(argv[i]); if (TotalCycles < 1) usage(); continue; } usage(); } if (framebuffer_open(NULL, NULL) == -1) { perror("framebuffer_open failed."); exit(0); } framebuffer_blit(); framebuffer_close(); return 0; }