From aa2146acd32801b24efc99bb492ea3c8e721c6c3 Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Tue, 22 Mar 2005 14:53:51 +0000 Subject: [PATCH] Initial Import --- Makefile | 15 ++ cltr-list.c | 349 +++++++++++++++++++++++++++++ cltr.c | 469 +++++++++++++++++++++++++++++++++++++++ cltr.h | 45 ++++ fonts.c | 173 +++++++++++++++ fonts.h | 31 +++ pixbuf.c | 614 ++++++++++++++++++++++++++++++++++++++++++++++++++++ pixbuf.h | 67 ++++++ util.c | 26 +++ util.h | 13 ++ 10 files changed, 1802 insertions(+) create mode 100644 Makefile create mode 100644 cltr-list.c create mode 100644 cltr.c create mode 100644 cltr.h create mode 100644 fonts.c create mode 100644 fonts.h create mode 100644 pixbuf.c create mode 100644 pixbuf.h create mode 100644 util.c create mode 100644 util.h diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..8f304990a --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +LIBS=-lpng -lGL -ljpeg -L/usr/X11R6/lib -lX11 `pkg-config --libs pangoft2 pango glib-2.0` +CFLAGS=`pkg-config --cflags pangoft2 pango glib-2.0` + +.c.o: + $(CC) -g -Wall $(CFLAGS) $(INCS) -c $*.c + +OBJS=cltr.o pixbuf.o util.o fonts.o + +clutter: $(OBJS) + $(CC) -g -Wall -o $@ $(OBJS) $(LIBS) + +$(OBJS): pixbuf.h util.h fonts.h cltr.h + +clean: + rm -fr *.o clutter test diff --git a/cltr-list.c b/cltr-list.c new file mode 100644 index 000000000..e2189cf81 --- /dev/null +++ b/cltr-list.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "pixbuf.h" +#include "fonts.h" + +#define TEX_W 1024 /* must be > than frame_w & power of 2 */ +#define TEX_H 1024 +/* + * + */ + +#define WINW 640 +#define WINH 480 + +#define NBOXITEMS 10 + +#define NUMRECTS 4 +#define MAXSCALE 2 +#define MAXDIST (WINH) +#define MAXH (WINH/NUMRECTS) +#define MAXW (WINW - 20) + +#ifndef ABS +#define ABS(a) ((a > 0) ? (a) : -1 * (a)) +#endif + +typedef struct TableWidget TableWidget; + +typedef struct TableWidgetCell TableWidgetCell; + +int ScrollDir = 1; + +struct TableWidget +{ + int x,y,width,height; + + TableWidgetCell *cells, *active_cell; + + int active_cell_y; + + +}; + +struct TableWidgetCell +{ + XRectangle rect; + TableWidgetCell *next, *prev; + +}; + + +Display *xdpy; +Window xwin; +XEvent xevent; +GC xgc; +Pixbuf *pix, *pix_orig; + +float +distfunc(TableWidget *table, int d) +{ +/* printf("returning %f\n", (exp((float)d/MAXDIST)/exp(1.0))); */ + int maxdist = table->height; + + d = (maxdist-ABS(d)) ; + return ( exp( (float)d/maxdist * 2.0 ) / exp(2.0) ); +} + +TableWidgetCell* +table_cell_new() +{ + TableWidgetCell *cell = NULL; + + cell = malloc(sizeof(TableWidgetCell)); + memset(cell, 0, sizeof(TableWidgetCell)); + + return cell; +} + +TableWidget* +table_new(int n_items) +{ + TableWidget *table = NULL; + TableWidgetCell *last = NULL, *cell = NULL; + int i; + + table = malloc(sizeof(TableWidget)); + memset(table, 0, sizeof(TableWidget)); + + table->width = WINW; + table->height = WINH; + + table->active_cell_y = 100; + + for (i=0; inext = cell; + cell->prev = last; + } + else + table->cells = table->active_cell = cell; + + last = cell; + } + + table->cells->rect.y = table->active_cell_y; + + return table; +} + +void +table_redraw(TableWidget *table) +{ + TableWidgetCell *cur = table->cells; + int last = table->cells->rect.y; + + glClearColor( 0.0, 0.0, 0.0, 1.0); + glClear (GL_COLOR_BUFFER_BIT); + + while (cur) + { + cur->rect.y = last; + + if (cur->rect.y+cur->rect.height >= 0) + { + cur->rect.width = MAXW * distfunc(table, cur->rect.y - table->active_cell_y); + cur->rect.height = MAXH * distfunc(table, cur->rect.y - table->active_cell_y); + + cur->rect.x = (WINW - cur->rect.width)/6; + } + + last = cur->rect.y + cur->rect.height; + + if (last > 0 && cur->rect.y < WINH) /* crappy clip */ + { + float tx = 1.0, ty = 1.0, sx, sy; + int x1 = cur->rect.x, x2 = cur->rect.x + cur->rect.width; + int y1 = cur->rect.y, y2 = cur->rect.y + cur->rect.height; + + tx = (float) pix->width / TEX_W; + ty = (float) pix->height / TEX_H; + + + glBegin (GL_QUADS); + glTexCoord2f (tx, ty); glVertex2i (x2, y2); + glTexCoord2f (0, ty); glVertex2i (x1, y2); + glTexCoord2f (0, 0); glVertex2i (x1, y1); + glTexCoord2f (tx, 0); glVertex2i (x2, y1); + glEnd (); + + /* draw with X primitives + XDrawRectangle(xdpy, xwin, xgc, + cur->rect.x, cur->rect.y, + cur->rect.width, cur->rect.height); + + XDrawLine(xdpy, xwin, xgc, + cur->rect.x, cur->rect.y, + cur->rect.x + cur->rect.width, + cur->rect.y + cur->rect.height); + */ + } + + cur = cur->next; + } + + glXSwapBuffers(xdpy, xwin); + +} + + +void +table_scroll_down(TableWidget *table) +{ + TableWidgetCell *next_active = table->active_cell->next; + + if (!next_active) + { + ScrollDir = 0; + return; + } + + while (next_active->rect.y > table->active_cell_y) + { + table->cells->rect.y--; + table_redraw(table); + } + + table->active_cell = next_active; +} + +void +table_scroll_up(TableWidget *table) +{ + TableWidgetCell *next_active = table->active_cell->prev; + + if (!next_active) + return; + + while (next_active->rect.y < table->active_cell_y) + { + table->cells->rect.y++; + table_redraw(table); + } + + table->active_cell = next_active; +} + + +int +main(int argc, char **argv) +{ + TableWidget *table; + int i, j, last, offset=0; + XGCValues gcvals; + ClutterFont *font = NULL; + + /* GL */ + GLXContext context; /* OpenGL context */ + GLubyte *texture_data = NULL; + XVisualInfo *vinfo; + static int attributes[] = + { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + 0 + }; + + + + if ((xdpy = XOpenDisplay(getenv("DISPLAY"))) == NULL) + { + fprintf(stderr, "%s: Cant open display\n", argv[0]); + exit(-1); + } + + + if ((vinfo = glXChooseVisual(xdpy, DefaultScreen(xdpy), attributes)) == NULL) + { + fprintf(stderr, "Unable to find visual\n"); + exit(-1); + } + + xwin = XCreateSimpleWindow(xdpy, + RootWindow(xdpy, DefaultScreen(xdpy)), + 0, 0, + WINW, WINH, + 0, 0, WhitePixel(xdpy, DefaultScreen(xdpy))); + + gcvals.foreground = BlackPixel(xdpy, DefaultScreen(xdpy)); + gcvals.background = WhitePixel(xdpy, DefaultScreen(xdpy)); + gcvals.line_width = 1; + + xgc = XCreateGC(xdpy, RootWindow(xdpy, DefaultScreen(xdpy)), + GCForeground|GCBackground|GCLineWidth, + &gcvals); + + context = glXCreateContext(xdpy, vinfo, 0, True); + glXMakeCurrent(xdpy, xwin, context); + + glViewport (0, 0, WINW, WINH); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glClearColor (0, 0, 0, 0); + glClearDepth (1.0f); + + glDisable (GL_DEPTH_TEST); + glDepthMask (GL_FALSE); + glDisable (GL_CULL_FACE); + glShadeModel (GL_FLAT); + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + // glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, WINW, WINH, 0, -1, 1); + + glEnable (GL_TEXTURE_2D); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + texture_data = malloc (TEX_W * TEX_H * 4); + + for (i=0; i < (TEX_W * TEX_H * 4); i++) + texture_data[i] = rand()%255; + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + TEX_W, TEX_H, + 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data); + + pix_orig = pixbuf_new_from_file(argv[1]); + + if (!pix_orig) + { + fprintf(stderr, "image load failed\n"); + exit(-1); + } + + pix = pixbuf_scale_down(pix_orig, 100, 100); + + /* + font = font_new("Sans Bold 48"); + + font_draw(font, pix, "Hello World\nlove matmoo", 0, 0); + */ + + glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, + (GLsizei)pix->width, + (GLsizei)pix->height, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, + pix->data); + + table = table_new(NBOXITEMS); + + table_redraw(table); + + XMapWindow(xdpy, xwin); + + for (;;) + { + XEvent ev; + // XNextEvent(xdpy, &ev); + // XClearWindow(xdpy, xwin); + table_redraw(table); + ScrollDir ? table_scroll_down(table) : table_scroll_up(table); + + // scroll_to_next(); + XFlush(xdpy); + sleep(1); + } + +} diff --git a/cltr.c b/cltr.c new file mode 100644 index 000000000..b698ecb9f --- /dev/null +++ b/cltr.c @@ -0,0 +1,469 @@ +#include "cltr.h" + +float Zoom = 0.1; + +typedef struct ClutterMainContext ClutterMainContext; + +struct ClutterMainContext +{ + Display *xdpy; + Window xwin_root; + int xscreen; + GC xgc; + GLXContext gl_context; +}; + +typedef struct ClutterWindow ClutterWindow; + +struct ClutterWindow +{ + Window xwin; + int width; + int height; +}; + +ClutterMainContext CltrCntx; + +int +cltr_init(int *argc, char ***argv) +{ + int gl_attributes[] = + { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + 0 + }; + + XVisualInfo *vinfo; + + if ((CltrCntx.xdpy = XOpenDisplay(getenv("DISPLAY"))) == NULL) + { + return 0; + } + + CltrCntx.xscreen = DefaultScreen(CltrCntx.xdpy); + CltrCntx.xwin_root = RootWindow(CltrCntx.xdpy, CltrCntx.xscreen); + + if ((vinfo = glXChooseVisual(CltrCntx.xdpy, + CltrCntx.xscreen, + gl_attributes)) == NULL) + { + fprintf(stderr, "Unable to find visual\n"); + return 0; + } + + CltrCntx.gl_context = glXCreateContext(CltrCntx.xdpy, vinfo, 0, True); + + return 1; +} + + +ClutterWindow* +cltr_window_new(int width, int height) +{ + ClutterWindow *win; + + win = util_malloc0(sizeof(ClutterWindow)); + + win->width = width; + win->height = height; + + win->xwin = XCreateSimpleWindow(CltrCntx.xdpy, + CltrCntx.xwin_root, + 0, 0, + width, height, + 0, 0, WhitePixel(CltrCntx.xdpy, + CltrCntx.xscreen)); + + glXMakeCurrent(CltrCntx.xdpy, win->xwin, CltrCntx.gl_context); + + glViewport (0, 0, width, height); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glClearColor (0xff, 0xff, 0xff, 0xff); + glClearDepth (1.0f); + + glDisable (GL_DEPTH_TEST); + glDepthMask (GL_FALSE); + glDisable (GL_CULL_FACE); + glShadeModel (GL_FLAT); + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + + glLoadIdentity (); + glOrtho (0, width, height, 0, -1, 1); + + glMatrixMode (GL_PROJECTION); + + /* likely better somewhere elese */ + + glEnable (GL_TEXTURE_2D); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + return win; +} + +void +cltr_main_loop() +{ + XEvent xev; + + for (;;) + { + XNextEvent(CltrCntx.xdpy, &xev); + } +} + +/* xxxxxxxxxxxxx */ + +typedef struct ClutterPhotoGrid ClutterPhotoGrid; + +typedef struct ClutterPhotoGridCell ClutterPhotoGridCell; + +typedef enum ClutterPhotoGridState +{ + CLTR_PHOTO_GRID_STATE_BROWSE, + CLTR_PHOTO_GRID_STATE_ZOOM_IN, + CLTR_PHOTO_GRID_STATE_ZOOMED, + CLTR_PHOTO_GRID_STATE_ZOOM_OUT, +} +ClutterPhotoGridState; + +struct ClutterPhotoGrid +{ + /* XXX should be base widget stuff */ + int x,y; + int width; + int height; + ClutterWindow *parent; + + /* ****** */ + + int n_rows; + int n_cols; + + int cell_width; + int cell_height; + + GList *cells_tail; + + int tex_w; + int tex_h; + int *tex_data; + GLuint *texs; + + ClutterPhotoGridState state; +}; + +void +cltr_photo_grid_append_cell(ClutterPhotoGrid *grid, + Pixbuf *pixb, + const gchar *filename) +{ + int neww = 0, newh = 0; + + int maxw = grid->width, maxh = grid->height; + + Pixbuf *pixb_scaled = NULL; + + if (pixb->width > pixb->height) /* landscape */ + { + if (pixb->width > maxw) + { + neww = maxw; + newh = (neww * pixb->height) / pixb->width; + } + } + else /* portrait */ + { + if (pixb->height > maxh) + { + newh = maxh; + neww = (newh * pixb->width) / pixb->height; + } + } + + if (neww || newh) + { + pixb_scaled = pixbuf_scale_down(pixb, neww, newh); + pixbuf_unref(pixb); + } + else pixb_scaled = pixb; + + grid->cells_tail = g_list_append(grid->cells_tail, pixb_scaled); + + CLTR_DBG ("loaded %s at %ix%i", filename, neww, newh); +} + +void +cltr_photo_grid_populate(ClutterPhotoGrid *grid, + const char *imgs_path) +{ + GDir *dir; + GError *error; + const gchar *entry = NULL; + gchar *fullpath = NULL; + int n_pixb = 0, i =0; + GList *cell; + + if ((dir = g_dir_open (imgs_path, 0, &error)) == NULL) + { + /* handle this much better */ + fprintf(stderr, "failed to open '%s'\n", imgs_path); + return; + } + + while((entry = g_dir_read_name (dir)) != NULL) + { + Pixbuf *pixb = NULL; + fullpath = g_strconcat(imgs_path, "/", entry, NULL); + + pixb = pixbuf_new_from_file(fullpath); + + if (pixb) + { + cltr_photo_grid_append_cell(grid, pixb, entry); + n_pixb++; + } + + g_free(fullpath); + } + + g_dir_close (dir); + + /* Set up textures */ + + grid->texs = util_malloc0(sizeof(GLuint)*n_pixb); + glGenTextures(n_pixb, grid->texs); + + cell = g_list_first(grid->cells_tail); + + do + { + Pixbuf *tpixb = (Pixbuf *)cell->data; + + glBindTexture(GL_TEXTURE_2D, grid->texs[i]); + + CLTR_DBG("loading texture %i", i); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + grid->tex_w, + grid->tex_h, + 0, GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, + grid->tex_data); + + glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, + (GLsizei)tpixb->width, + (GLsizei)tpixb->height, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, + tpixb->data); + + i++; + } + while ( (cell = g_list_next(cell)) != NULL ); + + glEnable (GL_TEXTURE_2D); + +} + +void +cltr_photo_grid_redraw(ClutterPhotoGrid *grid) +{ + int x = 0, y = 0, rows = grid->n_rows, cols = 0, i =0; + GList *cell; + + glClearColor( 0.0, 0.0, 0.0, 1.0); + glClear (GL_COLOR_BUFFER_BIT); + + /* + + glTranslatef( 0.0, 0.0, 0.1); + */ + + glLoadIdentity (); + glScalef( Zoom, Zoom, Zoom); + + Zoom += 0.01; + + cell = g_list_first(grid->cells_tail); + + while (rows--) + { + cols = grid->n_cols; + x = 0; + while (cols--) + { + Pixbuf *pixb = NULL; + float tx, ty; + int x1, x2, y1, y2, thumb_w, thumb_h; + int ns_border, ew_border; + + pixb = (Pixbuf *)cell->data; + + thumb_w = (pixb->width / grid->n_cols); + thumb_h = (pixb->height / grid->n_rows); + + ew_border = thumb_w/4; + ns_border = thumb_h/4; + + thumb_w -= (2 * ew_border); + thumb_h -= (2 * ns_border); + + x1 = x + ew_border; + y1 = y + ns_border; + + x2 = x1 + thumb_w; + y2 = y1 + thumb_h; + + glBindTexture(GL_TEXTURE_2D, grid->texs[i]); + + /* + glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, + (GLsizei)pixb->width, + (GLsizei)pixb->height, + GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, + pixb->data); + */ + tx = (float) pixb->width / grid->tex_w; + ty = (float) pixb->height / grid->tex_h; + + glBegin (GL_QUADS); + glTexCoord2f (tx, ty); glVertex2i (x2, y2); + glTexCoord2f (0, ty); glVertex2i (x1, y2); + glTexCoord2f (0, 0); glVertex2i (x1, y1); + glTexCoord2f (tx, 0); glVertex2i (x2, y1); + glEnd (); + + cell = g_list_next(cell); + + if (!cell) + goto finish; + + x += grid->cell_width; + i++; + } + y += grid->cell_height; + } + + finish: + glXSwapBuffers(CltrCntx.xdpy, grid->parent->xwin); + +} + +ClutterPhotoGrid* +cltr_photo_grid_new(ClutterWindow *win, + int n_cols, + int n_rows, + const char *imgs_path) +{ + ClutterPhotoGrid *grid = NULL; + + grid = util_malloc0(sizeof(ClutterPhotoGrid)); + + grid->width = win->width; + grid->height = win->height; + grid->n_cols = n_cols; + grid->n_rows = n_rows; + grid->parent = win; + + grid->cell_width = grid->width / n_cols; + grid->cell_height = grid->height / n_rows; + + /* Below needs to go else where */ + + grid->tex_w = 1024; + grid->tex_h = 512; + + grid->tex_data = malloc (grid->tex_w * grid->tex_h * 4); + + /* + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + grid->tex_w, grid->tex_h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, grid->tex_data); + */ + + /* Load */ + cltr_photo_grid_populate(grid, imgs_path); + + cltr_photo_grid_redraw(grid); + + return grid; +} + +static Bool +get_xevent_timed(Display *dpy, + XEvent *event_return, + struct timeval *tv) +{ + if (tv->tv_usec == 0 && tv->tv_sec == 0) + { + XNextEvent(dpy, event_return); + return True; + } + + XFlush(dpy); + + if (XPending(dpy) == 0) + { + int fd = ConnectionNumber(dpy); + fd_set readset; + FD_ZERO(&readset); + FD_SET(fd, &readset); + + if (select(fd+1, &readset, NULL, NULL, tv) == 0) + return False; + else { + XNextEvent(dpy, event_return); + return True; + } + + } else { + XNextEvent(dpy, event_return); + return True; + } +} + + +int +main(int argc, char **argv) +{ + ClutterPhotoGrid *grid = NULL; + ClutterWindow *win = NULL; + + cltr_init(&argc, &argv); + + win = cltr_window_new(640, 480); + + grid = cltr_photo_grid_new(win, 3, 3, argv[1]); + + cltr_photo_grid_redraw(grid); + + XFlush(CltrCntx.xdpy); + + XMapWindow(CltrCntx.xdpy, grid->parent->xwin); + + { + for (;;) + { + cltr_photo_grid_redraw(grid); + XFlush(CltrCntx.xdpy); + } + } + + return 0; +} diff --git a/cltr.h b/cltr.h new file mode 100644 index 000000000..4b4c23570 --- /dev/null +++ b/cltr.h @@ -0,0 +1,45 @@ +#ifndef _HAVE_CLTR_H +#define _HAVE_CLTR_H + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pixbuf.h" +#include "fonts.h" + +#define CLTR_WANT_DEBUG 1 + +#if (CLTR_WANT_DEBUG) + +#define CLTR_DBG(x, a...) \ + g_printerr ( __FILE__ ":%d,%s() " x "\n", __LINE__, __func__, ##a) + +#define CLTR_GLERR() \ + { \ + GLenum err = glGetError (); /* Roundtrip */ \ + if (err != GL_NO_ERROR) \ + { \ + const GLubyte *message = gluErrorString (err); \ + g_printerr (__FILE__ ": GL Error: %s [at %s:%d]\n", \ + __func__, __LINE__); \ + } \ + } + +#else + +#define CLTR_DBG(x, a...) do {} while (0) +#define CLTR_GLERR() do {} while (0) + +#endif + +#endif diff --git a/fonts.c b/fonts.c new file mode 100644 index 000000000..1457df8f1 --- /dev/null +++ b/fonts.c @@ -0,0 +1,173 @@ +#include "fonts.h" + +ClutterFont* +font_new (const char *face) +{ + ClutterFont *font; + PangoFontDescription *desc; + + font = util_malloc0(sizeof(ClutterFont)); + + font->font_map = pango_ft2_font_map_new (); + + pango_ft2_font_map_set_resolution (PANGO_FT2_FONT_MAP (font->font_map), + 96., 96.); + + desc = pango_font_description_from_string (face); + + font->context + = pango_ft2_font_map_create_context (PANGO_FT2_FONT_MAP (font->font_map)); + + pango_context_set_font_description (font->context, desc); + + pango_font_description_free (desc); + + return font; +} + +#if 0 +static int +decoration_get_title_width (LmcDecoration *decoration) +{ + int title_space; + int width; + + title_space = (decoration->window_width + + lmc_theme->border_info.left + + lmc_theme->border_info.right + - lmc_theme->border_info.left_unscaled + - lmc_theme->border_info.right_unscaled); + title_space = MAX (title_space, 0); + + pango_layout_get_pixel_size (decoration->layout, &width, NULL); + + return MIN (width + TITLE_RIGHT_PAD, title_space); +} +#endif + +static void +get_layout_bitmap (PangoLayout *layout, + FT_Bitmap *bitmap, + PangoRectangle *ink) +{ + PangoRectangle ink_rect; + + pango_layout_get_extents (layout, &ink_rect, NULL); + + /* XXX why the >> 10 */ + ink->x = ink_rect.x >> 10; + ink->width = ((ink_rect.x + ink_rect.width + 1023) >> 10) - ink->x; + ink->y = ink_rect.y >> 10; + ink->height = ((ink_rect.y + ink_rect.height + 1023) >> 10) - ink->y; + + if (ink->width == 0) + ink->width = 1; + if (ink->height == 0) + ink->height = 1; + + bitmap->width = ink->width; + bitmap->pitch = (bitmap->width + 3) & ~3; + bitmap->rows = ink->height; + bitmap->buffer = malloc (bitmap->pitch * bitmap->rows); + bitmap->num_grays = 256; + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; + + printf("FT_Bitmap is %ix%i\n", bitmap->width, bitmap->rows); + + memset (bitmap->buffer, 0, bitmap->pitch * bitmap->rows); + + pango_ft2_render_layout (bitmap, layout, - ink->x, - ink->y); +} + +static void +draw_layout_on_pixbuf (PangoLayout *layout, + Pixbuf *pixb, + const PixbufPixel *color, + int x, + int y, + int clip_x, + int clip_y, + int clip_width, + int clip_height) +{ + PangoRectangle ink; + FT_Bitmap bitmap; + int i, j; + unsigned char *layout_bits; + + get_layout_bitmap (layout, &bitmap, &ink); + + layout_bits = bitmap.buffer; + + for (j = y + ink.y; j < y + ink.y + ink.height; j++) + { + if (j >= clip_y && j < clip_y + clip_height) + { + int start_x, end_x; + + start_x = MAX (x + ink.x, clip_x); + end_x = MIN (x + ink.x + ink.width, clip_x + clip_width); + + if (start_x < end_x) + { + unsigned char *b = layout_bits + (start_x - (x + ink.x)); + + for (i = start_x ; i < end_x; i++) + { + PixbufPixel pixel; + int tr1, tg1, tb1, tr2, tg2, tb2; + int a = (*b * color->a + 0x80) >> 8; + + pixbuf_get_pixel (pixb, i, j, &pixel); + + tr1 = (255 - a) * pixel.r + 0x80; + tr2 = a * color->r + 0x80; + pixel.r = ((tr1 + (tr1 >> 8)) >> 8) + ((tr2 + (tr2 >> 8)) >> 8); + tg1 = (255 - a) * pixel.g + 0x80; + tg2 = a * color->g + 0x80; + pixel.g = ((tg1 + (tg1 >> 8)) >> 8) + ((tg2 + (tg2 >> 8)) >> 8); + tb1 = (255 - a) * pixel.b + 0x80; + tb2 = a * color->b + 0x80; + pixel.b = ((tb1 + (tb1 >> 8)) >> 8) + ((tb2 + (tb2 >> 8)) >> 8); + + pixbuf_set_pixel (pixb, i, j, &pixel); + b++; + } + } + } + + layout_bits += bitmap.pitch; + } + + free (bitmap.buffer); +} + + +void +font_draw(ClutterFont *font, + Pixbuf *pixb, + const char *text, + int x, + int y) +{ + int layout_width, layout_height; + PangoLayout *layout; + PixbufPixel p = { 0xff,0,0,0x80 }; + + layout = pango_layout_new (font->context); + + pango_layout_set_text (layout, text, -1); + + pango_layout_get_pixel_size (layout, + &layout_width, &layout_height); + + printf("%s() %ix%i\n", __func__, layout_width, layout_height); + + /* cant rely on just clip - need to set layout width too */ + + draw_layout_on_pixbuf (layout, pixb, &p, x, y, + x, + y, + pixb->width - x, + pixb->height - y); +} diff --git a/fonts.h b/fonts.h new file mode 100644 index 000000000..ffbfaf303 --- /dev/null +++ b/fonts.h @@ -0,0 +1,31 @@ +#ifndef _HAVE_FONTS_H +#define _HAVE_FONTS_H + +#include + +#include "pixbuf.h" +#include "util.h" + +/* Code based on stuff found in luminocity */ + +typedef struct ClutterFont ClutterFont; + +struct ClutterFont +{ + PangoFontMap *font_map; + PangoContext *context; +}; + +ClutterFont* +font_new (const char *face); + + +void +font_draw(ClutterFont *font, + Pixbuf *pixb, + const char *text, + int x, + int y); + + +#endif diff --git a/pixbuf.c b/pixbuf.c new file mode 100644 index 000000000..3977df7ea --- /dev/null +++ b/pixbuf.c @@ -0,0 +1,614 @@ +#include +#include + +#include /* For memset() */ +#include /* For read() */ + + +#include +#include +#include + +#include /* For mmap()/munmap() */ +#include + + +#include +#include + +#include "pixbuf.h" +#include "util.h" + +static int* +load_png_file( const char *file, + int *width, + int *height) +{ + FILE *fd; + /* GLubyte *data; */ + int *data; + unsigned char header[8]; + int bit_depth, color_type; + png_uint_32 png_width, png_height, i, rowbytes; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers; + + if ((fd = fopen( file, "rb" )) == NULL) return NULL; + + /* check header etc */ + + fread(header, 1, 8, fd); + + if (!png_check_sig(header, 8)) + { + fclose(fd); + return NULL; + } + + png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + { + fclose(fd); + return NULL; + } + + info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) + { + png_destroy_read_struct( &png_ptr, (png_infopp)NULL, (png_infopp)NULL); + fclose(fd); + return NULL; + } + + if (setjmp( png_ptr->jmpbuf ) ) + { + png_destroy_read_struct( &png_ptr, &info_ptr, NULL); + fclose(fd); + return NULL; + } + + png_init_io( png_ptr, fd ); + + png_set_sig_bytes( png_ptr, 8); + png_read_info( png_ptr, info_ptr); + + png_get_IHDR( png_ptr, info_ptr, + &png_width, &png_height, &bit_depth, + &color_type, NULL, NULL, NULL); + + *width = (int) png_width; + *height = (int) png_height; + + /* Tranform to req 8888 */ + + if (bit_depth == 16 ) png_set_strip_16(png_ptr); /* 16 -> 8 */ + + if (bit_depth < 8) png_set_packing(png_ptr); /* 1,2,4 -> 8 */ + + if (( color_type == PNG_COLOR_TYPE_GRAY ) || + ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )) + png_set_gray_to_rgb(png_ptr); + + if (( color_type == PNG_COLOR_TYPE_GRAY ) || + ( color_type == PNG_COLOR_TYPE_RGB )) + png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); /* req 1.2.7 */ + + if (( color_type == PNG_COLOR_TYPE_PALETTE )|| + ( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ))) + png_set_expand(png_ptr); + + png_read_update_info( png_ptr, info_ptr); + + /* Now load the actual data */ + + rowbytes = png_get_rowbytes( png_ptr, info_ptr); + + data = (int *) malloc( (rowbytes*(*height + 1))); + + row_pointers = (png_bytep *) malloc( (*height)*sizeof(png_bytep)); + + if (( data == NULL ) || ( row_pointers == NULL )) + { + png_destroy_read_struct( &png_ptr, &info_ptr, NULL); + if (data) free(data); + if (row_pointers) free(row_pointers); + return NULL; + } + + for ( i = 0; i < *height; i++ ) + row_pointers[i] = (png_bytep) data + i*rowbytes; + + png_read_image( png_ptr, row_pointers ); + png_read_end( png_ptr, NULL); + + free(row_pointers); + png_destroy_read_struct( &png_ptr, &info_ptr, NULL); + fclose(fd); + + return data; +} + +struct local_error_mgr +{ + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct local_error_mgr * local_error_ptr; + +static void +_jpeg_error_exit (j_common_ptr cinfo) +{ + local_error_ptr err = (local_error_ptr) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(err->setjmp_buffer, 1); +} + +static int* +load_jpg_file( const char *file, + int *width, + int *height) +{ + struct jpeg_decompress_struct cinfo; + struct local_error_mgr jerr; + FILE *infile; /* source file */ + JSAMPLE *buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + + int *data = NULL, *d = NULL; + + if ((infile = fopen(file, "rb")) == NULL) + return NULL; + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = _jpeg_error_exit; + + if (setjmp(jerr.setjmp_buffer)) { + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return NULL; + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.out_color_space = JCS_RGB; + cinfo.scale_num = 1; + + jpeg_start_decompress(&cinfo); + + if( cinfo.output_components != 3 ) + { + /* + fprintf( stderr, "mbpixbuf: jpegs with %d channles not supported\n", + cinfo.output_components ); + */ + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + + *width = cinfo.output_width; + *height = cinfo.output_height; + + d = data = malloc(*width * *height * 4 ); + + row_stride = cinfo.output_width * cinfo.output_components; + buffer = malloc( sizeof(JSAMPLE)*row_stride ); + + while (cinfo.output_scanline < cinfo.output_height) + { + int off = 0; + + jpeg_read_scanlines(&cinfo, &buffer, 1); + + while (off < row_stride) + { + /* XXX Endianess */ + *d++ = + (buffer[off] << 24) | /* RGBA */ + (buffer[off+1] << 16) | + (buffer[off+2] << 8) | + (0xff << 0); + off += 3; + } + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + + if (buffer) free(buffer); + + return data; +} + +/* X pcx code, based on usplash code by paul coden */ +/* http://courses.ece.uiuc.edu/ece390/books/labmanual/graphics-pcx.html */ + +typedef struct +{ + unsigned char manufacturer; + unsigned char version; + unsigned char encoding; + unsigned char bits_per_pixel; + unsigned short xmin; + unsigned short ymin; + unsigned short xmax; + unsigned short ymax; + unsigned short xdpi; + unsigned short ydpi; + unsigned char colourmap[48]; + unsigned char reserved; + unsigned char planes; + unsigned short scanline_length; + unsigned short palette_info; + unsigned short xsize; + unsigned short ysize; + unsigned char fill[54]; + unsigned char data[0]; +} pcx; + +enum +{ + PCX_ZSOFT = 10, + PCX_RLE = 1, + PCX_WITH_PALETTE = 2, + PCX_COLOUR_MAP_LENGTH = 769 +}; + + +/* +** Reads the first 128 bytes of a PCX headers, from an file +** descriptor, into memory. +** RETURN zero on success. +*/ +int +pcx_read_header(pcx *header, int fd) +{ + if(!lseek(fd, 0, SEEK_SET)) + if(read(fd, header, sizeof(pcx)) == sizeof(pcx)) + return 0; + return -1; +} + +/* +** Does the file descriptor point to a PCX file, which is of a +** suitable colour-depth (8-bit) for us to use? +** RETURN zero on success. +*/ +static int +pcx_is_suitable(int fd) +{ + pcx header; + if(!pcx_read_header(&header, fd)) + if(header.manufacturer == PCX_ZSOFT + /* && header.version >= PCX_WITH_PALETTE && */ + && header.encoding == PCX_RLE + && header.planes == 3 /* 24bpp */ + && header.bits_per_pixel == 8 ) /* why not 24 from gimp */ + return 0; + + return -1; +} + +/* +** Takes a raw PCX RLE stream and decompresses it into the destination +** buffer, which must be big enough! +** RETURN zero on success +** +** PCX images are RLE (Run Length Encoded as follows: +** if(top two bits are set) // >= 0xc0 +** use bottom six bit (& 0x3f) as RLE count for next byte; +** else // < 0xc0 +** copy one byte normally; +*/ +static void +pcx_raw_decode24(int *dest, + unsigned char *src, + int width, + int height) +{ + int x, y, i, count; + int *d = dest; + unsigned char *p; + + memset(dest, 0xff, height * width * 4); + + for(y = 0; y < height; y++) + { + d = dest + (y * width); + /* RGB */ + for(x = 0; x < width;) + if(*src < 0xc0) + { + x++; + p = (unsigned char *)d++; + *p = *src++; + } + else + { + count = *src++ & 0x3f; + for (i=0; ixmax - header->xmin + 1; + *height = header->ymax - header->ymin + 1; + + /* Allocate enough room for the data and colourmap*/ + data = malloc(*width * *height * 4); + + if (!data) + { + munmap(header, file_length); + return NULL; + } + + /* Decode the data */ + pcx_raw_decode24(data, header->data, *width, *height); + + /* Clean up */ + munmap(header, file_length); + + close(fd); + + return data; +} + + +/* -------------------------------------------------------------------- */ + +Pixbuf* +pixbuf_new(int width, int height) +{ + Pixbuf *pixb; + + pixb = util_malloc0(sizeof(Pixbuf)); + + pixb->width = width; + pixb->height = height; + pixb->bytes_per_pixel = 4; + pixb->channels = 4; + pixb->bytes_per_line = pixb->bytes_per_pixel * pixb->width; + pixb->data = malloc(pixb->bytes_per_line * pixb->height); + + /* memset ? */ + + return pixb; +} + +void +pixbuf_unref(Pixbuf *pixb) +{ + pixb->refcnt--; + + if (pixb->refcnt < 0) + { + free(pixb->data); + free(pixb); + } +} + +Pixbuf* +pixbuf_new_from_file(const char *filename) +{ + Pixbuf *pixb; + + pixb = util_malloc0(sizeof(Pixbuf)); + + if (!strcasecmp(&filename[strlen(filename)-4], ".png")) + pixb->data =load_png_file(filename, &pixb->width, &pixb->height); + else if (!strcasecmp(&filename[strlen(filename)-4], ".jpg") + || !strcasecmp(&filename[strlen(filename)-5], ".jpeg")) + pixb->data = load_jpg_file( filename, &pixb->width, &pixb->height); + else if (!strcasecmp(&filename[strlen(filename)-4], ".pcx")) + pixb->data = load_pcx_file( filename, &pixb->width, &pixb->height); + + if (pixb->data == NULL) + { + free (pixb); + return NULL; + } + + pixb->bytes_per_pixel = 4; + pixb->channels = 4; + pixb->bytes_per_line = pixb->bytes_per_pixel * pixb->width; + + return pixb; +} + +void +pixbuf_set_pixel(Pixbuf *pixb, int x, int y, PixbufPixel *p) +{ + int *offset = pixb->data + ( y * pixb->width) + x; + + /* ARGB_32 MSB */ + + *offset = (p->r << 0) | (p->g << 8) | (p->b << 16) | (p->a << 24); +} + +void +pixbuf_get_pixel(Pixbuf *pixb, int x, int y, PixbufPixel *p) +{ + int *offset = pixb->data + ( y * pixb->width) + x; + + /* ARGB_32 MSB */ + + p->r = *offset & 0xff; + p->g = (*offset >> 8) & 0xff; + p->b = (*offset >> 16) & 0xff; + p->a = (*offset >> 24) & 0xff; +} + +void /* XXX could be DEFINE */ +pixel_set_vals(PixbufPixel *p, + const unsigned char r, + const unsigned char g, + const unsigned char b, + const unsigned char a) +{ + p->r = r; p->g = g; p->b = b; p->a = a; +} + +Pixbuf * +pixbuf_scale_down(Pixbuf *pixb, + int new_width, + int new_height) +{ + Pixbuf *pixb_scaled; + int *xsample, *ysample, *dest, *src, *srcy; + int i, x, y, r, g, b, a, nb_samples, xrange, yrange, rx, ry; + + if ( new_width > pixb->width || new_height > pixb->height) + return NULL; + + pixb_scaled = pixbuf_new(new_width, new_height); + + xsample = malloc( (new_width+1) * sizeof(int)); + ysample = malloc( (new_height+1) * sizeof(int)); + + for ( i = 0; i <= new_width; i++ ) + xsample[i] = i * pixb->width / new_width; + + for ( i = 0; i <= new_height; i++ ) + ysample[i] = i * pixb->height / new_height * pixb->width; + + dest = pixb_scaled->data; + + /* scan output image */ + for ( y = 0; y < new_height; y++ ) + { + yrange = ( ysample[y+1] - ysample[y] ) / pixb->width; + for ( x = 0; x < new_width; x++) + { + xrange = xsample[x+1] - xsample[x]; + srcy = pixb->data + ( ysample[y] + xsample[x] ); + + /* average R,G,B,A values on sub-rectangle of source image */ + nb_samples = xrange * yrange; + + if ( nb_samples > 1 ) + { + r = 0; g = 0; b = 0; a = 0; + for ( ry = 0; ry < yrange; ry++ ) + { + src = srcy; + for ( rx = 0; rx < xrange; rx++ ) + { + /* average R,G,B,A values */ + r += *src & 0xff; + g += ((*src) >> 8) & 0xff; + b += ((*src) >> 16) & 0xff; + a += ((*src) >> 24) & 0xff; + + src++; + } + + srcy += pixb->width; + } + + *dest++ = + ((unsigned char)(r/nb_samples) << 0) | + ((unsigned char)(g/nb_samples) << 8) | + ((unsigned char)(b/nb_samples) << 16) | + ((unsigned char)(a/nb_samples) << 24); + } + else + { + *dest++ = *srcy++; + } + } + } + + /* cleanup */ + free( xsample ); + free( ysample ); + + return pixb_scaled; +} diff --git a/pixbuf.h b/pixbuf.h new file mode 100644 index 000000000..80608b9ba --- /dev/null +++ b/pixbuf.h @@ -0,0 +1,67 @@ +#ifndef _HAVE_PIXBUF_H +#define _HAVE_PIXBUF_H + +typedef struct Pixbuf Pixbuf; +typedef struct PixbufPixel PixbufPixel; + +typedef enum PixbufFormat +{ + PB_FMT_RGBA +} +PixbufFormat; + +struct PixbufPixel +{ + unsigned char r,g,b,a; +}; + +struct Pixbuf +{ + int *data; + int bytes_per_pixel; /* bits per pixel = bpp << 3 */ + int channels; /* 4 with alpha */ + int width, height; + int bytes_per_line; /* ( width * bpp ) */ + + int refcnt; /* starts at 0 */ + + void *meta; /* for jpeg meta text ? to a hash ? */ + + /* Possibles */ + + int rmask, gmask, bmask, amask; /* Masks - good for packed formats > */ + int has_alpha; /* Rather than channels ? */ + + + /* PixbufFormat format; like GL format */ + +}; + +Pixbuf* +pixbuf_new_from_file(const char *filename); + +Pixbuf* +pixbuf_new(int width, int height); + +void +pixbuf_unref(Pixbuf *pixb); + +void +pixbuf_set_pixel(Pixbuf *pixb, int x, int y, PixbufPixel *p); + +void +pixbuf_get_pixel(Pixbuf *pixbuf, int x, int y, PixbufPixel *p); + +void +pixel_set_vals(PixbufPixel *p, + const unsigned char r, + const unsigned char g, + const unsigned char b, + const unsigned char a); + +Pixbuf* +pixbuf_scale_down(Pixbuf *pixb, + int new_width, + int new_height); + +#endif diff --git a/util.c b/util.c new file mode 100644 index 000000000..67cc5eb65 --- /dev/null +++ b/util.c @@ -0,0 +1,26 @@ +#include "util.h" + +/* misc utility code */ + + +void* +util_malloc0(int size) +{ + void *p; + + p = malloc(size); + memset(p, 0, size); + + return p; +} + +int +util_next_p2 ( int a ) +{ + int rval=1; + while(rval < a) + rval <<= 1; + + return rval; +} + diff --git a/util.h b/util.h new file mode 100644 index 000000000..67593acd4 --- /dev/null +++ b/util.h @@ -0,0 +1,13 @@ +#ifndef _HAVE_UTIL_H +#define _HAVE_UTIL_H + +#include +#include + +void* +util_malloc0(int size); + +int +util_next_p2 ( int a ); + +#endif