/* Tilemap BG scroll. */ #include #include #include #include #include #include #include /* include xpm binary */ #include "./scifi_bg_chip.xpm" #define USE_XPM_NAME scifi_bg_chip_xpm #define WIN_X 0 #define WIN_Y 0 #define BORDER 0 #define OUT_W 640 #define OUT_H 360 #ifndef M_PI #define M_PI 3.14159265358979 #endif #define deg2rad(x) (x * M_PI / 180.0) typedef struct { Pixmap pix; Pixmap mask; } Pixmaps; typedef struct { Display *dpy; Window root; Window win; int screen; int disp_w, disp_h; unsigned long black, white; XWindowAttributes xgwa; GC gc, mask_gc; Pixmaps pix; Pixmaps *pixs; int pixs_size; Pixmap buf; Pixmap outbuf; int xpm_w, xpm_h; float bg_x[3]; float bg_y[3]; int count; } State; #define BG_COUNT 3 #define BG_W 16 #define BG_H 6 #define BG_CHIP_W 64 #define BG_CHIP_H 64 static int bg_layer[][16 * 6] = { {43, 44, 29, 30, 43, 44, 27, 28, 43, 44, 29, 30, 43, 44, 27, 28, 51, 52, 37, 38, 51, 52, 35, 36, 51, 52, 37, 38, 51, 52, 35, 36, 27, 28, 43, 44, 27, 28, 29, 31, 51, 52, 43, 44, 27, 28, 29, 30, 35, 36, 51, 52, 35, 36, 37, 37, 37, 38, 51, 52, 35, 36, 37, 38, 29, 30, 27, 28, 43, 44, 27, 28, 43, 44, 27, 28, 29, 30, 27, 28, 37, 38, 35, 36, 51, 52, 35, 36, 51, 52, 35, 36, 37, 38, 35, 36}, {0, 24, 41, 48, 48, 26, 0, 24, 48, 48, 26, 40, 48, 48, 48, 26, 0, 49, 0, 0, 0, 40, 48, 41, 26, 0, 32, 48, 48, 26, 0, 49, 48, 41, 48, 25, 48, 25, 48, 48, 41, 48, 33, 48, 48, 33, 48, 41, 0, 0, 0, 32, 48, 33, 48, 48, 26, 0, 40, 48, 26, 40, 26, 0, 48, 48, 25, 41, 48, 41, 48, 25, 41, 48, 25, 25, 41, 48, 41, 48, 0, 0, 49, 0, 0, 0, 0, 40, 48, 48, 42, 49, 0, 0, 0, 0}, {1, 2, 3, 14, 15, 0, 6, 1, 1, 2, 3, 1, 1, 7, 0, 6, 12, 13, 7, 0, 0, 0, 0, 12, 13, 3, 2, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 0, 0, 0, 8, 9, 3, 2, 10, 11, 0, 0, 4, 1, 1, 1, 2, 3, 2, 3, 2, 3, 1, 3, 1, 3, 2, 3, 2, 3}}; /* get a color value from a color name */ static unsigned long get_color(Display *dpy, char *colorname) { Colormap cmap; XColor near_color, true_color; cmap = DefaultColormap(dpy, DefaultScreen(dpy)); XAllocNamedColor(dpy, cmap, colorname, &near_color, &true_color); return near_color.pixel; } static int load_images(State *st) { XpmAttributes attr; attr.valuemask = 0; if (XpmCreatePixmapFromData(st->dpy, st->win, USE_XPM_NAME, &st->pix.pix, &st->pix.mask, &attr)) { fprintf(stderr, "Error not load xpm."); return 1; } /* get xpm width and height */ st->xpm_w = attr.width; st->xpm_h = attr.height; /* free xpm attributes */ XpmFreeAttributes(&attr); return 0; } static Pixmaps * split_pixmaps(State *st, Pixmaps src, int src_w, int src_h, int div_x, int div_y) { Pixmaps *pixs, *p; int w, h; GC pix_gc, mask_gc; st->pixs_size = div_x * div_y; pixs = (Pixmaps *)malloc(sizeof(Pixmaps) * st->pixs_size); pix_gc = XCreateGC(st->dpy, src.pix, 0, 0); XSetGraphicsExposures(st->dpy, pix_gc, False); mask_gc = XCreateGC(st->dpy, src.mask, 0, 0); XSetGraphicsExposures(st->dpy, mask_gc, False); w = src_w / div_x; h = src_h / div_y; int ix, iy; p = pixs; for (iy = 0; iy < div_y; iy++) { int y = h * iy; for (ix = 0; ix < div_x; ix++) { int x; x = w * ix; p->pix = XCreatePixmap(st->dpy, st->win, w, h, st->xgwa.depth); XCopyArea(st->dpy, src.pix, p->pix, pix_gc, x, y, w, h, 0, 0); p->mask = XCreatePixmap(st->dpy, st->win, w, h, 1); XCopyArea(st->dpy, src.mask, p->mask, mask_gc, x, y, w, h, 0, 0); p++; } } XFreeGC(st->dpy, pix_gc); XFreeGC(st->dpy, mask_gc); return pixs; } static void init_pixmaps(State *st) { /* create Pixmaps */ st->buf = XCreatePixmap(st->dpy, st->win, st->disp_w, st->disp_h, st->xgwa.depth); st->outbuf = XCreatePixmap(st->dpy, st->win, OUT_W, OUT_H, st->xgwa.depth); XSetForeground(st->dpy, st->gc, get_color(st->dpy, "green")); XFillRectangle(st->dpy, st->buf, st->gc, 0, 0, st->disp_w, st->disp_h); XSetForeground(st->dpy, st->gc, get_color(st->dpy, "black")); XFillRectangle(st->dpy, st->outbuf, st->gc, 0, 0, OUT_W, OUT_H); } static void free_pixmaps(State *st) { /* free split pixmaps */ { int i; for (i = 0; i < st->pixs_size; i++) { XFreePixmap(st->dpy, st->pixs[i].pix); XFreePixmap(st->dpy, st->pixs[i].mask); } free(st->pixs); } XFreePixmap(st->dpy, st->pix.pix); XFreePixmap(st->dpy, st->pix.mask); XFreePixmap(st->dpy, st->buf); XFreePixmap(st->dpy, st->outbuf); } static void init_gc(State *st) { st->gc = XCreateGC(st->dpy, st->pix.pix, 0, 0); st->mask_gc = XCreateGC(st->dpy, st->pix.mask, 0, 0); XSetGraphicsExposures(st->dpy, st->gc, False); XSetGraphicsExposures(st->dpy, st->mask_gc, False); } static void free_gc(State *st) { XFreeGC(st->dpy, st->gc); XFreeGC(st->dpy, st->mask_gc); } /* scale Pixmap */ static void scale_pixmap(Display *dpy, Drawable win, Pixmap *src, Pixmap *dst, int src_w, int src_h, int dst_w, int dst_h) { GC gc; float xscale, yscale; int x, y; xscale = (float)src_w / dst_w; yscale = (float)src_h / dst_h; gc = XCreateGC(dpy, win, 0, 0); XSetGraphicsExposures(dpy, gc, False); for (x = dst_w - 1; x >= 0; x--) XCopyArea(dpy, *src, *dst, gc, (int)(x * xscale), 0, 1, src_h, x, 0); if (src_h <= dst_h) { for (y = dst_h - 1; y >= 0; y--) XCopyArea(dpy, *dst, *dst, gc, 0, (int)(y * yscale), dst_w, 1, 0, y); } else { for (y = 0; y < dst_h; y++) XCopyArea(dpy, *dst, *dst, gc, 0, (int)(y * yscale), dst_w, 1, 0, y); } XFreeGC(dpy, gc); } static void get_target_size(int src_w, int src_h, int scrw, int scrh, int *tgt_w, int *tgt_h) { double xscale, yscale, scale; xscale = (double)scrw / src_w; yscale = (double)scrh / src_h; scale = (xscale <= yscale) ? xscale : yscale; *tgt_w = (int)(src_w * scale); *tgt_h = (int)(src_h * scale); } static void draw_tilemap(State *st) { int src_w, src_h; int bgw, bgh; int xcnt, ycnt; int n; src_w = OUT_W; src_h = OUT_H; bgw = BG_CHIP_W * BG_W; bgh = BG_CHIP_H * BG_H; xcnt = (OUT_W / BG_CHIP_W) + 1 + ((OUT_W % BG_CHIP_W == 0) ? 0 : 1); ycnt = (OUT_H / BG_CHIP_H) + 1 + ((OUT_H % BG_CHIP_H == 0) ? 0 : 1); XSetGraphicsExposures(st->dpy, st->gc, False); XSetGraphicsExposures(st->dpy, st->mask_gc, False); for (n = 0; n < BG_COUNT; n++) { int bx, by, dx, dy, xc, yc; bx = (int)st->bg_x[n]; by = (int)st->bg_y[n]; while (bx < 0) bx += bgw; while (by < 0) by += bgh; bx = bx % bgw; by = by % bgh; dx = bx % BG_CHIP_W; dy = by % BG_CHIP_H; for (yc = 0; yc < ycnt; yc++) { int iy = ((yc * BG_CHIP_H + by) % bgh) / BG_CHIP_H; for (xc = 0; xc < xcnt; xc++) { int ix, id, x, y; ix = ((xc * BG_CHIP_W + bx) % bgw) / BG_CHIP_W; id = bg_layer[n][(iy % BG_H) * BG_W + (ix % BG_W)]; if (id == 0) continue; x = xc * BG_CHIP_W - dx; y = yc * BG_CHIP_H - dy; XSetClipMask(st->dpy, st->gc, st->pixs[id].mask); XSetClipOrigin(st->dpy, st->gc, x, y); XCopyArea(st->dpy, st->pixs[id].pix, st->outbuf, st->gc, 0, 0, BG_CHIP_W, BG_CHIP_H, x, y); } } } XSetClipMask(st->dpy, st->gc, None); } /* update and draw */ static void update(State *st) { int i; int scrw, scrh; int src_w, src_h; int aw, ah; scrw = st->xgwa.width; /* window width */ scrh = st->xgwa.height; /* window height */ src_w = OUT_W; src_h = OUT_H; get_target_size(src_w, src_h, scrw, scrh, &aw, &ah); /* draw tilemap */ draw_tilemap(st); /* scaling to screen resolution */ scale_pixmap(st->dpy, st->win, &st->outbuf, &st->buf, src_w, src_h, aw, ah); /* draw image (Pixmap) */ { int x = (scrw - aw) / 2; int y = (scrh - ah) / 2; /* clear window */ XClearWindow(st->dpy, st->win); XSetGraphicsExposures(st->dpy, st->gc, False); XCopyArea(st->dpy, st->buf, st->win, st->gc, 0, 0, aw, ah, x, y); } /* change bg position */ st->bg_x[0] += 0.5; st->bg_x[1] += 1.0; st->bg_x[2] += 3.0; st->bg_y[0] = st->bg_x[0] / 2.0; st->bg_y[1] = st->bg_x[1] / 2.0; st->bg_y[2] = st->bg_x[2] / 2.0; st->count++; } int main(void) { State st; /* open display */ st.dpy = XOpenDisplay(NULL); st.root = DefaultRootWindow(st.dpy); st.screen = DefaultScreen(st.dpy); /* get display size */ st.disp_w = DisplayWidth(st.dpy, st.screen); st.disp_h = DisplayHeight(st.dpy, st.screen); printf("Screen: %d x %d\n", st.disp_w, st.disp_h); /* get color black and white */ st.white = WhitePixel(st.dpy, st.screen); st.black = BlackPixel(st.dpy, st.screen); /* create window */ st.win = XCreateSimpleWindow(st.dpy, st.root, WIN_X, WIN_Y, st.disp_w, st.disp_h, BORDER, st.white, st.black); XSelectInput(st.dpy, st.win, KeyPressMask | ExposureMask); XMapWindow(st.dpy, st.win); /* wait display window */ { XEvent evt; do { XNextEvent(st.dpy, &evt); } while (evt.type != Expose); } XMoveWindow(st.dpy, st.win, WIN_X, WIN_Y); /* get window attributes */ XGetWindowAttributes(st.dpy, st.win, &st.xgwa); /* load image */ if (load_images(&st) == 1) return 1; /* create graphics context */ init_gc(&st); /* init Pixmaps */ init_pixmaps(&st); /* split pixmap */ st.pixs = split_pixmaps(&st, st.pix, st.xpm_w, st.xpm_h, 8, 8); /* init bg position */ { int i; for (i = 0; i < 3; i++) { st.bg_x[i] = 0.0; st.bg_y[i] = 0.0; } } st.count = 0; /* main loop */ { int busy_loop; XEvent evt; busy_loop = 1; while (busy_loop) { struct timespec start, end; double diff; timespec_get(&start, TIME_UTC); /* event */ while (XPending(st.dpy)) { XNextEvent(st.dpy, &evt); switch (evt.type) { /* case Expose: if (evt.xexpose.count == 0) { } break; */ case KeyPress: busy_loop = 0; break; default: break; } } update(&st); XFlush(st.dpy); timespec_get(&end, TIME_UTC); diff = (end.tv_sec + (double)end.tv_nsec / 1000000000.0) - (start.tv_sec + (double)start.tv_nsec / 1000000000.0); printf("%lf msec\n", diff * 1000); /* wait */ { int waitusec = (int)(16666 - (diff * 1000 * 1000)); /* printf("wait %d usec\n", waitusec); */ if (waitusec > 0) usleep(waitusec); } } } free_pixmaps(&st); free_gc(&st); XDestroyWindow(st.dpy, st.win); XCloseDisplay(st.dpy); return 0; }