commit aa2146acd32801b24efc99bb492ea3c8e721c6c3 Author: Matthew Allum Date: Tue Mar 22 14:53:51 2005 +0000 Initial Import 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