mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-11-05 16:04:31 +00:00
442 lines
11 KiB
C
442 lines
11 KiB
C
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
|
|
|
|
test-ft-gtk.c: Testbed for freetype/libart integration.
|
|
|
|
Copyright (C) 2000 Eazel, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public
|
|
License along with this program; if not, write to the
|
|
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA.
|
|
|
|
Author: Raph Levien <raph@artofcode.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <png.h>
|
|
#include <popt.h>
|
|
#include <math.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include <freetype/freetype.h>
|
|
|
|
#include <libart_lgpl/art_misc.h>
|
|
#include <libart_lgpl/art_rect.h>
|
|
#include <libart_lgpl/art_alphagamma.h>
|
|
#include <libart_lgpl/art_affine.h>
|
|
#include "art_render.h"
|
|
#include "art_render_mask.h"
|
|
|
|
#include "rsvg.h"
|
|
#include "rsvg-ft.h"
|
|
|
|
typedef struct _TestCtx TestCtx;
|
|
|
|
struct _TestCtx {
|
|
RsvgFTCtx *ctx;
|
|
RsvgFTFontHandle fh;
|
|
int n_lines;
|
|
char **lines;
|
|
int y_sp;
|
|
int y_scroll;
|
|
GtkWidget *drawingarea;
|
|
GtkWidget *status;
|
|
double start_time;
|
|
gboolean do_drawing;
|
|
gboolean do_scrolling;
|
|
gboolean do_invert;
|
|
};
|
|
|
|
static double
|
|
timing_get_time (void)
|
|
{
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
|
|
gettimeofday (&tv, &tz);
|
|
|
|
return tv.tv_sec + 1e-6 * tv.tv_usec;
|
|
}
|
|
|
|
static void invert_glyph (guchar *buf, int rowstride, int width, int height)
|
|
{
|
|
int x, y;
|
|
int first;
|
|
int n_words;
|
|
int last;
|
|
guint32 *middle;
|
|
|
|
if (width >= 8 && ((rowstride & 3) == 0)) {
|
|
first = (-(int)buf) & 3;
|
|
n_words = (width - first) >> 2;
|
|
last = first + (n_words << 2);
|
|
|
|
for (y = 0; y < height; y++) {
|
|
middle = (guint32 *)(buf + first);
|
|
for (x = 0; x < first; x++)
|
|
buf[x] = ~buf[x];
|
|
for (x = 0; x < n_words; x++)
|
|
middle[x] = ~middle[x];
|
|
for (x = last; x < width; x++)
|
|
buf[x] = ~buf[x];
|
|
buf += rowstride;
|
|
}
|
|
} else {
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width; x++)
|
|
buf[x] = ~buf[x];
|
|
buf += rowstride;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void draw_line (TestCtx *ctx, int line_num, ArtIRect *rect)
|
|
{
|
|
GtkWidget *drawingarea = ctx->drawingarea;
|
|
int y0;
|
|
RsvgFTGlyph *glyph;
|
|
const double affine[6] = { 1, 0, 0, 1, 5, 12 };
|
|
int glyph_xy[2];
|
|
ArtIRect line_rect, clear_rect, glyph_rect, draw_rect;
|
|
int width;
|
|
|
|
width = drawingarea->allocation.width;
|
|
|
|
y0 = line_num * ctx->y_sp - ctx->y_scroll;
|
|
if (line_num < 0 || line_num >= ctx->n_lines) {
|
|
if (ctx->do_drawing) {
|
|
gdk_draw_rectangle (drawingarea->window,
|
|
drawingarea->style->white_gc,
|
|
TRUE,
|
|
0, y0, width, ctx->y_sp);
|
|
}
|
|
} else {
|
|
guchar *buf;
|
|
int rowstride;
|
|
|
|
glyph = rsvg_ft_render_string (ctx->ctx, ctx->fh,
|
|
ctx->lines[line_num],
|
|
14, 14,
|
|
affine,
|
|
glyph_xy);
|
|
rowstride = glyph->rowstride;
|
|
|
|
glyph_rect.x0 = glyph_xy[0];
|
|
glyph_rect.y0 = y0 + glyph_xy[1];
|
|
glyph_rect.x1 = glyph_rect.x0 + glyph->width;
|
|
glyph_rect.y1 = glyph_rect.y0 + glyph->height;
|
|
line_rect.x0 = 0;
|
|
line_rect.y0 = y0;
|
|
line_rect.x1 = width;
|
|
line_rect.y1 = y0 + ctx->y_sp;
|
|
art_irect_intersect (&clear_rect, rect, &line_rect);
|
|
|
|
if (ctx->do_drawing)
|
|
gdk_draw_rectangle (drawingarea->window,
|
|
drawingarea->style->white_gc,
|
|
TRUE,
|
|
clear_rect.x0, clear_rect.y0,
|
|
clear_rect.x1 - clear_rect.x0,
|
|
clear_rect.y1 - clear_rect.y0);
|
|
|
|
art_irect_intersect (&draw_rect, rect, &glyph_rect);
|
|
if (!art_irect_empty (&draw_rect) && ctx->do_drawing) {
|
|
buf = glyph->buf +
|
|
draw_rect.x0 - glyph_rect.x0 +
|
|
rowstride * (draw_rect.y0 - glyph_rect.y0);
|
|
if (ctx->do_invert) {
|
|
invert_glyph (buf, rowstride,
|
|
draw_rect.x1 - draw_rect.x0,
|
|
draw_rect.y1 - draw_rect.y0);
|
|
}
|
|
gdk_draw_gray_image (drawingarea->window,
|
|
drawingarea->style->white_gc,
|
|
draw_rect.x0, draw_rect.y0,
|
|
draw_rect.x1 - draw_rect.x0,
|
|
draw_rect.y1 - draw_rect.y0,
|
|
GDK_RGB_DITHER_NONE,
|
|
buf,
|
|
rowstride);
|
|
}
|
|
rsvg_ft_glyph_unref (glyph);
|
|
}
|
|
}
|
|
|
|
static gint
|
|
test_expose (GtkWidget *widget, GdkEventExpose *event, TestCtx *ctx)
|
|
{
|
|
int line0, line1;
|
|
int line;
|
|
ArtIRect rect;
|
|
|
|
rect.x0 = event->area.x;
|
|
rect.y0 = event->area.y;
|
|
rect.x1 = rect.x0 + event->area.width;
|
|
rect.y1 = rect.y0 + event->area.height;
|
|
line0 = (rect.y0 + ctx->y_scroll) / ctx->y_sp;
|
|
line1 = (rect.y1 + ctx->y_scroll + ctx->y_sp - 1) / ctx->y_sp;
|
|
for (line = line0; line < line1; line++) {
|
|
#ifdef VERBOSE
|
|
g_print ("drawing line %d of [%d..%d]\n", line, line0, line1 - 1);
|
|
#endif
|
|
draw_line (ctx, line, &rect);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
scroll_to (TestCtx *ctx, int new_y)
|
|
{
|
|
GtkWidget *drawingarea = ctx->drawingarea;
|
|
int scroll_amt = new_y - ctx->y_scroll;
|
|
int width = drawingarea->allocation.width;
|
|
int height = drawingarea->allocation.height;
|
|
int y0, y1;
|
|
GdkEventExpose expose;
|
|
|
|
if (scroll_amt == 0)
|
|
return;
|
|
|
|
#ifdef VERBOSE
|
|
g_print ("scrolling to %d\n", new_y);
|
|
#endif
|
|
if (scroll_amt > 0 && scroll_amt < height) {
|
|
y0 = height - scroll_amt;
|
|
y1 = height;
|
|
if (ctx->do_scrolling) {
|
|
gdk_draw_pixmap (drawingarea->window,
|
|
drawingarea->style->white_gc,
|
|
drawingarea->window,
|
|
0, scroll_amt,
|
|
0, 0,
|
|
width, y0);
|
|
}
|
|
} else if (scroll_amt < 0 && -scroll_amt < height) {
|
|
y0 = 0;
|
|
y1 = -scroll_amt;
|
|
if (ctx->do_scrolling) {
|
|
gdk_draw_pixmap (drawingarea->window,
|
|
drawingarea->style->white_gc,
|
|
drawingarea->window,
|
|
0, 0,
|
|
0, y1,
|
|
width, height - y1);
|
|
}
|
|
} else {
|
|
y0 = 0;
|
|
y1 = height;
|
|
}
|
|
ctx->y_scroll = new_y;
|
|
expose.area.x = 0;
|
|
expose.area.width = width;
|
|
expose.area.y = y0;
|
|
expose.area.height = y1 - y0;
|
|
test_expose (drawingarea, &expose, ctx);
|
|
}
|
|
|
|
static gboolean scroll_idler (gpointer data)
|
|
{
|
|
TestCtx *ctx = (TestCtx *)data;
|
|
GtkWidget *drawingarea = ctx->drawingarea;
|
|
int width = drawingarea->allocation.width;
|
|
int height = drawingarea->allocation.height;
|
|
|
|
if ((ctx->y_scroll + height) < ctx->n_lines * ctx->y_sp) {
|
|
scroll_to (ctx, ctx->y_scroll + 100);
|
|
return TRUE;
|
|
} else {
|
|
double elapsed;
|
|
char str[128];
|
|
|
|
scroll_to (ctx, 0);
|
|
elapsed = timing_get_time () - ctx->start_time;
|
|
sprintf (str, "%g seconds to scroll, %g Mpix/s",
|
|
elapsed,
|
|
width * ctx->y_sp * ctx->n_lines * 1e-6 / elapsed);
|
|
gtk_label_set_text (GTK_LABEL (ctx->status), str);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_toggle (GtkWidget *button, int state, gboolean *bool) {
|
|
*bool = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
|
|
}
|
|
|
|
static GtkWidget *
|
|
check_button (const char *label, gboolean *bool) {
|
|
GtkWidget *result;
|
|
|
|
result = gtk_check_button_new_with_label (label);
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (result), *bool);
|
|
gtk_signal_connect (GTK_OBJECT (result), "state_changed",
|
|
(GtkSignalFunc) check_toggle, bool);
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
start_scrolling (GtkWidget *widget, TestCtx *ctx)
|
|
{
|
|
scroll_to (ctx, 0);
|
|
gtk_idle_add (scroll_idler, ctx);
|
|
ctx->start_time = timing_get_time ();
|
|
gtk_label_set_text (GTK_LABEL (ctx->status), "Scrolling...");
|
|
}
|
|
|
|
static TestCtx *new_test_window (const char *fn, int width, int height)
|
|
{
|
|
GtkWidget *topwin;
|
|
GtkWidget *vbox;
|
|
GtkWidget *buttonbar;
|
|
GtkWidget *button;
|
|
GtkWidget *drawingarea;
|
|
TestCtx *ctx;
|
|
|
|
ctx = g_new (TestCtx, 1);
|
|
|
|
topwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_signal_connect (GTK_OBJECT (topwin), "destroy",
|
|
(GtkSignalFunc) gtk_main_quit, NULL);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (topwin), vbox);
|
|
|
|
drawingarea = gtk_drawing_area_new ();
|
|
gtk_drawing_area_size (GTK_DRAWING_AREA (drawingarea), width, height);
|
|
gtk_container_add (GTK_CONTAINER (vbox), drawingarea);
|
|
|
|
ctx->ctx = rsvg_ft_ctx_new ();
|
|
ctx->fh = rsvg_ft_intern (ctx->ctx, fn);
|
|
ctx->n_lines = 0;
|
|
ctx->lines = NULL;
|
|
ctx->y_sp = 16;
|
|
ctx->y_scroll = 0;
|
|
ctx->drawingarea = drawingarea;
|
|
|
|
ctx->do_drawing = TRUE;
|
|
ctx->do_scrolling = TRUE;
|
|
ctx->do_invert = TRUE;
|
|
|
|
gtk_signal_connect (GTK_OBJECT (drawingarea), "expose_event",
|
|
(GtkSignalFunc) test_expose, ctx);
|
|
|
|
buttonbar = gtk_hbox_new (FALSE, 5);
|
|
gtk_container_add (GTK_CONTAINER (vbox), buttonbar);
|
|
|
|
button = check_button ("Do drawing", &ctx->do_drawing);
|
|
gtk_container_add (GTK_CONTAINER (buttonbar), button);
|
|
|
|
button = check_button ("Do scrolling", &ctx->do_scrolling);
|
|
gtk_container_add (GTK_CONTAINER (buttonbar), button);
|
|
|
|
button = check_button ("Do invert", &ctx->do_invert);
|
|
gtk_container_add (GTK_CONTAINER (buttonbar), button);
|
|
|
|
button = gtk_button_new_with_label ("Start scroll test");
|
|
gtk_container_add (GTK_CONTAINER (buttonbar), button);
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
(GtkSignalFunc) start_scrolling, ctx);
|
|
|
|
ctx->status = gtk_label_new ("");
|
|
gtk_container_add (GTK_CONTAINER (vbox), ctx->status);
|
|
|
|
gtk_widget_show_all (topwin);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static void set_text (TestCtx *ctx, const char *fn) {
|
|
FILE *f;
|
|
char line[256];
|
|
int n_lines;
|
|
char **lines;
|
|
|
|
f = fopen (fn, "r");
|
|
if (f == NULL) {
|
|
g_warning ("Error opening file %s\n", fn);
|
|
return;
|
|
}
|
|
n_lines = 0;
|
|
for (;;) {
|
|
int len;
|
|
|
|
if (fgets (line, sizeof(line), f) == NULL)
|
|
break;
|
|
if (n_lines == 0)
|
|
lines = g_new (char *, 1);
|
|
else if (!(n_lines & (n_lines - 1))) {
|
|
lines = g_renew (char *, lines, n_lines << 1);
|
|
}
|
|
len = strlen (line);
|
|
if (len > 0 && line[len - 1] == '\n')
|
|
line[--len] = 0;
|
|
while (len > 0 && line[len - 1] == '\r')
|
|
line[--len] = 0;
|
|
lines[n_lines++] = g_strdup (line);
|
|
}
|
|
fclose (f);
|
|
ctx->n_lines = n_lines;
|
|
ctx->lines = lines;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *zoom_str = "1.0";
|
|
|
|
gint font_width = 36;
|
|
gint font_height = 36;
|
|
char *font_file_name = "/usr/share/fonts/default/Type1/n021003l.pfb";
|
|
char *text_file_name = "rsvg-ft.c";
|
|
|
|
poptContext optCtx;
|
|
struct poptOption optionsTable[] =
|
|
{
|
|
{"zoom", 'z', POPT_ARG_STRING, &zoom_str, 0, NULL, "zoom factor"},
|
|
{"font-width", 'w', POPT_ARG_INT, &font_width, 0, NULL, "Font Width"},
|
|
{"font-height", 'h', POPT_ARG_INT, &font_height, 0, NULL, "Font Height"},
|
|
{"font-file-name", 'f', POPT_ARG_STRING, &font_file_name, 0, NULL, "Font File Name"},
|
|
{"text-file-name", 't', POPT_ARG_STRING, &text_file_name, 0, NULL, "Text"},
|
|
POPT_AUTOHELP {NULL, 0, 0, NULL, 0}
|
|
};
|
|
char c;
|
|
const char *const *args;
|
|
TestCtx *ctx;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
gdk_rgb_init ();
|
|
|
|
gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
|
|
gtk_widget_set_default_visual (gdk_rgb_get_visual ());
|
|
|
|
optCtx =
|
|
poptGetContext("test-ft", argc, (const char **) argv,
|
|
optionsTable, 0);
|
|
|
|
c = poptGetNextOpt(optCtx);
|
|
args = poptGetArgs(optCtx);
|
|
|
|
ctx = new_test_window (font_file_name, 640, 480);
|
|
|
|
set_text (ctx, text_file_name);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|