Carpe diem, c'est la vie.
Posts: 6,433
Karma: 10773670
Join Date: Nov 2011
Location: Multiverse 6627A
Device: K1 to PW3
|
sparkle 2.0 - geekmaster's FIRST kindle program! (updated)
UPDATE: The demo kernel from the original sparkle-1.0 was added to the "dithermation" framework used by other demos in this thread. Now it works on all the eink kindles! On the K4(touch) the trail of ghosts is as long as on the old DX (but moving much faster).
Here we have a little piece of geekmaster history, dusted off and polished up a bit (a bunch of commented-out code removed). It is relevant now because it broke the bounds of what many still believe is even possible on a Kindle 3. Check out the frame rate.
This program was created in 2011 November, when geekmaster got his first kindle. It was his first kindle program (after the traditional "Hello World!" program to test the build tools). It formed the basis of his eink opinions expressed in mobileread forums since the beginning.
This demo uses spatiotemporal random dithering. The "temporal" aspect appears as "sparkle" on the eink display.
This method was abandoned for displaying live video because of significant display artifacts, which are very interesting from an "eye candy" point of view, so it is being presented for public viewing here and now.
This predates the newer kindles with 8-bit framebuffers (K4 and K5), but it works great on the DX, DXG, and K3. UPDATE: the "new and improved" version 2.0 now supports ALL eink kindles!
The interesting effect here is that on newer Pearl eink displays, the moving objects have a "snake-like" trail of alternating light and dark ghosts following them, taking two seconds to fade out. On the DX with the older eink technology, the trail of alternating fading ghosts gets so long it occupies much of the display, making a rather dazzling (but unintended) visual effect.
sparkle-2.0 source code:
Spoiler:
PHP Code:
//==================================================== // sparkle 2.0 - animation demo for all eink kindles // Copyright (C) 2012 by geekmaster, with MIT license: // http://www.opensource.org/licenses/mit-license.php //---------------------------------------------------- // This demo contains the code kernel of sparkle 1.0 // This was tested on DX,DXG,K3,K4(Mini),K5(Touch). //----------------------------------------------------
#include <stdio.h> // printf #include <stdlib.h> // malloc, free #include <string.h> // memset, memcpy #include <unistd.h> // usleep #include <fcntl.h> // open, close, write #include <time.h> // time #include <sys/mman.h> // mmap, munmap #include <sys/ioctl.h> // ioctl #include <linux/fb.h> // screeninfo
#define FBIO_EINK_UPDATE_DISPLAY_AREA 0x46dd enum GMLIB_op { GMLIB_INIT,GMLIB_CLOSE,GMLIB_UPDATE }; typedef unsigned char u8; typedef unsigned int u32;
// function prototypes void circle(int,int,int); int gmlib(int); inline void setpx(int,int,int); void d4w(void); void d8w(void); void d8b(void);
// gmlib global vars static u8 dt[64] = { 3,129,34,160,10,136,42,168,192,66,223,97,200,73,231,105,50, 176,18,144,58,184,26,152,239,113,207,81,247,121,215,89,14, 140,46,180,7,133,38,164,203,77,235,109,196,70,227,101,62,188, 30,156,54,180,22,148,251,125,219,93,243,117,211,85 }; // 0-255 dither table u8 *pbuff=NULL; // screensave pointer u8 *fb0=NULL; // framebuffer pointer u8 *wb=NULL; // workbuffer pointer u32 mpu=100; // msec/update int fdFB=0; // fb0 file descriptor int fdWB=0; // wb file descriptor u32 fs=0; // fb0 stride u32 MX=0; // xres (visible) u32 MY=0; // yres (visible) u8 blk=0; // black u8 msk=0; // black mask u8 ppb=0; // pixels per byte
//=============================================== // hoser - eink demo showing all-new dither design // This works on all kindle eink models. Enjoy! //----------------------------------------------- void hoser(void) { u32 x,y,c,px1,py1,vx1,vy1,dx,dy,cc,cu,cl; int i,j,n,d,wbytes,wb2,fdin,wdin,rc,once=0; __u8 *start,*end,*half,*pbyte,*psave,*pln;
gmlib(GMLIB_INIT); // init geekmaster functions
// save original screen pbuff=fb0; psave=malloc(wbytes*MY); memcpy(psave,fb0,wbytes*MY); start=pbuff+wbytes*40; half=pbuff+wbytes*(MY/2); end=pbuff+wbytes*(MY-33);
int yoff=40,xoff=60,bbx=xoff+10,bby=50,bbvx=7,bbvy=3; int wbx=xoff+480-80,wby=200,wbvx=-6,wbvy=-4;
for (y=0;y<240;y++) for (x=0;x<480;x++) setpx(x+xoff,y+yoff,255);
for (i=0;i<800;i++) {
//### fill screen with grays for (y=0;y<80;y++) for (x=0;x<480;x++) { if (rand()%480<x/2) setpx(x+xoff,y+yoff,0); else setpx(x+xoff,y+yoff,255); } for (y=80;y<160;y++) for (x=0;x<480;x++) { if (rand()%480<x/2) setpx(x+xoff,y+yoff,0); else setpx(x+xoff,y+yoff,255); } for (y=160;y<240;y++) for (x=0;x<480;x++) { if (rand()%480>x) setpx(x+xoff,y+yoff,0); else setpx(x+xoff,y+yoff,255); } if (!once) { once=1; gmlib(GMLIB_UPDATE); // update display }
//### black box for (y=bby;y<bby+33;y++) for (x=bbx;x<bbx+100;x++) setpx(x,y,0); for (y=bby+33;y<bby+66;y++) { for (x=bbx;x<bbx+33;x++) setpx(x,y,0); for (x=bbx+66;x<bbx+100;x++) setpx(x,y,0); } for (y=bby+66;y<bby+100;y++) for (x=bbx;x<bbx+100;x++) setpx(x,y,0);
//### white box for (y=wby;y<wby+25;y++) for (x=wbx;x<wbx+25;x++) setpx(x,y,255);
//### animate bbx += bbvx; bby += bbvy; wbx += wbvx; wby += wbvy; if (bbx>xoff+380) { bbx=xoff+380; bbvx=-bbvx; } if (bbx<xoff) { bbx=xoff; bbvx=-bbvx; } if (bby>140+yoff) { bby=140+yoff; bbvy=-bbvy; } if (bby<40) { bby=40; bbvy=-bbvy; } if (wbx>xoff+405) { wbx=xoff+405; wbvx=-wbvx; } if (wbx<xoff) { wbx=xoff; wbvx=-wbvx; } if (wby>215+yoff) { wby=215+yoff; wbvy=-wbvy; } if (wby<40) { wby=40; wbvy=-wbvy; } gmlib(GMLIB_UPDATE); // update display }
// cleanup - close and free resources memcpy(fb0,psave,wbytes*MY); // restore display gmlib(GMLIB_CLOSE); // close geekmaster functions free (psave); }
//==================================== // gmlib - geekmaster function library // op (init, update, close) //------------------------------------ int gmlib(int op) { struct update_area_t { int x1,y1,x2,y2,fx; u8 *buffer; } ua; struct fb_var_screeninfo screeninfo; static int fdUpdate=-1; if (GMLIB_INIT==op) { fdWB=open("/tmp/wb0",O_RDWR|O_CREAT); // work framebuffer fdFB=open("/dev/fb0",O_RDWR); // eink framebuffer ioctl(fdFB,FBIOGET_VSCREENINFO,&screeninfo); ppb=8/screeninfo.bits_per_pixel; // pixels per byte fs=screeninfo.xres_virtual/ppb; // fb0 stride blk=screeninfo.rotate-1; // black MX=screeninfo.xres; // max X+1 MY=screeninfo.yres; // max Y+1 msk=1/ppb-1; // black mask (4-bit=255,8-bit=0) fb0=(u8 *)mmap(0,MY*fs,PROT_READ|PROT_WRITE,MAP_SHARED,fdFB,0); // map fb0 lseek(fdWB,MY*MX-1,SEEK_SET); write(fdWB,"",1); // create work buffer file wb=(u8 *)mmap(0,MY*MX,PROT_READ|PROT_WRITE,MAP_SHARED,fdWB,0); // map wb fdUpdate=open("/proc/eink_fb/update_display",O_WRONLY); } else if (GMLIB_CLOSE==op) { gmlib(GMLIB_UPDATE); // update display munmap(fb0,MY*fs); // unmap fb0 munmap(wb,MY*MX); // unmap wb close(fdUpdate); // close update proc close(fdFB); // close fb0 close(fdWB); // close wb } else if (GMLIB_UPDATE==op) { if (ppb/2) { d4w(); ua.x1=0; ua.y1=0; ua.x2=MX; ua.y2=MY; ua.fx=0; ua.buffer=NULL; ioctl(fdFB, FBIO_EINK_UPDATE_DISPLAY_AREA, &ua); // fx_update_partial } else if (blk) { d8w(); ua.x1=0; ua.y1=0; ua.x2=MX; ua.y2=MY; ua.fx=0; ua.buffer=NULL; ioctl(fdFB, FBIO_EINK_UPDATE_DISPLAY_AREA, &ua); // fx_update_partial } else { d8b(); system("eips ''"); } } else { return -1; } return fdUpdate; }
//======================================== // setpx - draw pixel to 8-bit work buffer // x,y:screen coordinates, c:color(0-255). //---------------------------------------- inline void setpx(int x,int y,int c) { wb[y*MX+x]=c; }
//=========================== // d8b - dither 8-bit black 0 //--------------------------- void d8b(void) { u8 *pi,*po; int x,y; pi=wb; po=fb0; for (y=0;y<MY;y++) { for (x=0;x<MX;x++) { *po++=dt[(y&7)*8|x&7]-*pi++>>8; } po+=(fs-MX); } }
//=========================== // d8w - dither 8-bit white 0 //--------------------------- void d8w(void) { u8 *pi,*po; int x,y; pi=wb; po=fb0; for (y=0;y<MY;y++) { for (x=0;x<MX;x++) { *po++=~(dt[(y&7)*8|x&7]-*pi++>>8); } po+=(fs-MX); } }
//=========================== // d4w - dither 4-bit white 0 //--------------------------- void d4w(void) { u8 *pi,*po; int x,y,ys; pi=wb; po=fb0; for (y=0;y<MY;y++) { ys=(y&7)*8; for (x=0;x<MX;x+=8) { *po++=(~(dt[ys]-*pi++>>8)|15)&(~(dt[ys+1]-*pi++>>8)|240); *po++=(~(dt[ys+2]-*pi++>>8)|15)&(~(dt[ys+3]-*pi++>>8)|240); *po++=(~(dt[ys+4]-*pi++>>8)|15)&(~(dt[ys+5]-*pi++>>8)|240); *po++=(~(dt[ys+6]-*pi++>>8)|15)&(~(dt[ys+7]-*pi++>>8)|240); } } }
//============================================== // circle - optimized midpoint circle algorithm //---------------------------------------------- void circle(int cx,int cy,int r) { int e=-r,x=r,y=0; while (x>y) { setpx(cx+y,cy-x,255); setpx(cx+x,cy-y,159); setpx(cx+x,cy+y,95); setpx(cx+y,cy+x,31); setpx(cx-y,cy+x,0); setpx(cx-x,cy+y,63); setpx(cx-x,cy-y,127); setpx(cx-y,cy-x,191); e+=y; y++; e+=y; if (e>0) { e-=x; x-=1; e-=x; } } }
//================== // main - start here //------------------ int main(int argc,char **argv) { if (argc>1) { mpu=atoi(argv[1]); } hoser(); // do the hoser demo :D return 0; }
sparkle-1.0 source code:
Spoiler:
PHP Code:
//==================================================== // sparkle 1.0 - eink animation demo for DX/DXG/K3 // Copyright (C) 2012 by geekmaster, with MIT license: // http://www.opensource.org/licenses/mit-license.php // Copyright (C) 2011 by geekmaster //---------------------------------------------------- // This program was created in 2011 November, when // geekmaster got his first kindle. It was his first // kindle program (after traditional "Hello World!"). // It formed the basis of his eink opinions expressed // in mobileread forums since the beginning. Enjoy! //----------------------------------------------------
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/fb.h> #include <unistd.h> #include <string.h> #include <sys/mman.h> #include <sys/ioctl.h>
#define FBIO_MAGIC_NUMBER 'F' #define FBIO_EINK_UPDATE_DISPLAY_AREA _IO(FBIO_MAGIC_NUMBER,0xdd) // 0x46dd (update_area_t *)
#define pxwht(x,y) if ((x)&1) { fb0[(y)*MX+(x)>>1]&=0xF0; } else { fb0[(y)*MX+(x)>>1]&=0x0F; } #define pxblk(x,y) if ((x)&1) { fb0[(y)*MX+(x)>>1]|=0x0F; } else { fb0[(y)*MX+(x)>>1]|=0xF0; }
struct update_area_t { int x1,y1,x2,y2,which_fx; __u8 *buffer; // NULL=framebuffer }; typedef struct update_area_t update_area_t;
int MX=0,MY=0; __u8 *fb0=NULL,*pbuff=NULL; update_area_t ua;
int main(int argc,char** argv) { int i,j,n,x,y,d,wbytes,wb2,fdin,wdin,rc,once=0; __u8 *start,*end,*half,*pbyte,*psave,*pln; struct fb_var_screeninfo screeninfo;
int fdFB=open("/dev/fb0",O_RDWR); ioctl(fdFB,FBIOGET_VSCREENINFO,&screeninfo); MX=screeninfo.xres; MY=screeninfo.yres; wbytes=MX/2; wb2=wbytes/2; fb0=(__u8*)mmap(0,MY*wbytes,PROT_READ|PROT_WRITE,MAP_SHARED,fdFB,0); // map fb0
// save original screen pbuff=fb0; psave=malloc(wbytes*MY); memcpy(psave,fb0,wbytes*MY); start=pbuff+wbytes*40; half=pbuff+wbytes*(MY/2); end=pbuff+wbytes*(MY-33);
int yoff=40,xoff=60,bbx=xoff+10,bby=50,bbvx=7,bbvy=3; int wbx=xoff+480-80,wby=200,wbvx=-6,wbvy=-4;
ua.buffer=NULL; for (y=0; y<240; y++) for (x=0; x<480; x++) pxwht(x+xoff,y+yoff);
for (i=0; i<800; i++) {
//### fill screen with grays for (y=0; y<80; y++) for (x=0; x<480; x++) { if (rand()%480<x/2) pxblk(x+xoff,y+yoff) else pxwht(x+xoff,y+yoff); } for (y=80; y<160; y++) for (x=0; x<480; x++) { if (rand()%480<x/2) pxwht(x+xoff,y+yoff) else pxblk(x+xoff,y+yoff); } for (y=160; y<240; y++) for (x=0; x<480; x++) { if (rand()%480>x) pxblk(x+xoff,y+yoff) else pxwht(x+xoff,y+yoff); } if (!once) { once=1; ua.x1=xoff; ua.y1=yoff; ua.x2=xoff+481; ua.y2=yoff+241; ua.which_fx=0; // fx_update_partial ioctl(fdFB,FBIO_EINK_UPDATE_DISPLAY_AREA,&ua); }
//### black box for (y=bby; y<bby+33; y++) for (x=bbx; x<bbx+100; x++) pxblk(x,y); for (y=bby+33; y<bby+66; y++) { for (x=bbx; x<bbx+33; x++) pxblk(x,y); for (x=bbx+66; x<bbx+100; x++) pxblk(x,y); } for (y=bby+66; y<bby+100; y++) for (x=bbx; x<bbx+100; x++) pxblk(x,y); //### white box for (y=wby; y<wby+25; y++) for (x=wbx; x<wbx+25; x++) pxwht(x,y);
//### animate bbx += bbvx; bby += bbvy; wbx += wbvx; wby += wbvy; if (bbx>xoff+380) { bbx=xoff+380; bbvx=-bbvx; } if (bbx<xoff) { bbx=xoff; bbvx=-bbvx; } if (bby>140+yoff) { bby=140+yoff; bbvy=-bbvy; } if (bby<40) { bby=40; bbvy=-bbvy; } if (wbx>xoff+405) { wbx=xoff+405; wbvx=-wbvx; } if (wbx<xoff) { wbx=xoff; wbvx=-wbvx; } if (wby>215+yoff) { wby=215+yoff; wbvy=-wbvy; } if (wby<40) { wby=40; wbvy=-wbvy; }
ua.x1=xoff; ua.y1=yoff; ua.x2=xoff+481; ua.y2=yoff+241; ua.which_fx=0; // fx_update_partial ioctl(fdFB,FBIO_EINK_UPDATE_DISPLAY_AREA,&ua); }
memcpy(fb0,psave,wbytes*MY); // restore display ua.x1=0; ua.y1=0; ua.x2=600; ua.y2=800; ua.which_fx=2; // fx_update ioctl(fdFB,FBIO_EINK_UPDATE_DISPLAY_AREA,&ua);
free (psave); munmap(fb0,MX*MY/2); close(fdFB); }
Enjoy and learn!
Last edited by geekmaster; 04-24-2012 at 02:07 AM.
|