Carpe diem, c'est la vie.
Posts: 6,433
Karma: 10773668
Join Date: Nov 2011
Location: Multiverse 6627A
Device: K1 to PW3
|
newtrix - geekmaster's new tricks
Eink controller documents were studied. Code was written to take advantage of Samsung Broadsheet eink controller for K3 and older kindles (using einkfb device driver) and Freescale SoC-integrated eink controller (using mxcfb device driver), plus the funky hybrid K4 booted from main, with its mxcfb hardware emulation layer using the K3 einkfb device driver. K4 diags is also supported (and I STRONGLY recommend you watch this demo from there to see better performance, but main is not too shabby either). The new code contains a multi-stage demonstration showing what can be done using new interface methods.
All eink updates use ioctl() calls for all kindle models in all modes. New dithering code was written that is MUCH simpler and MUCH faster, and supports full 256-color 8-bit mode (which requires dithering despite the fact that the Freescale hardware has unused dithering support).
The demo shows WHAT you can do. The source code shows you HOW to do it.
The second stage of the demo is very hardware dependent on speed and what effects you see. It is rather slow on the DX, but it gets interesting near the end, and it is followed by MUCH more interesting demo stages.
Be sure to watch this multi-stage demo all the way to the end. There is a special bonus that *I* think is worth the wait.
We are just barely scratching the surface on capabilities here. There is some VERY interesting eink controller hardware here, mostly going unused. Look for GREAT things in the future.
Now, time for the show!
Teh Codez: Spoiler:
PHP Code:
//==================================================== // newtrix 2.0 - "new tricks" animation demo // Copyright (C) 2012 by geekmaster, with MIT license: // http://www.opensource.org/licenses/mit-license.php //---------------------------------------------------- // 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 typedef unsigned char u8; #include <linux/einkfb.h> #include <linux/mxcfb.h>
//#define FBIO_EINK_UPDATE_DISPLAY_AREA 0x46dd enum GMLIB_op { GMLIB_INIT,GMLIB_CLOSE,GMLIB_UPDATE }; //typedef unsigned int u32;
// function prototypes void circle(int,int,int); void line(int,int,int,int,int); int gmlib(int); inline void setpx(int,int,int); void dblit(void); int getmsec(void);
// gmlib global vars static int sv[424]={ 2330,1048,17,776,2309,2828,2580,3092,5134,6663,7168,5889,5385, 5396,5664,5673,4910,4135,5407,7198,8484,9263,8761,7485,7221,7469, 9514,11052,11575,11584,10316,8275,8275,10056,12094,13370,12867, 12618,12115,12888,14164,14413,13127,13380,15173,16456,16720,15963, 16479,18008,18265,17762,19040,20315,19555,19051,20327,21860,23143, 22384,22649,24187,24188,23161,22897,23657,25191,25456,25464,26224, 27507,27259,26755,27274,29065,30083,29820,28281,29819,30848,31370, 30868,29853,29852,30866,32139,33673,34704,35226,34722,33441,33688, 35219,36501,37022,37031,38567,39840,41378,41386,40880,0,29332, 30621,31654,0,0,0 };
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 *psave=NULL; // screensave pointer __u8 *fb0=NULL; // framebuffer pointer __u8 *wb0=NULL; // workbuffer pointer __u32 mpu=100; // msec/update int fdFB=0; // fb0 file descriptor int fdWB=0; // wb0 file descriptor __u32 fs=0; // fb0 stride __u32 fb=0; // fb0 bytes __u32 MX=0; // xres (visible) __u32 MY=0; // yres (visible) __u8 ppb=0; // pixels per byte int bs=340; // border size __u8 gs=0; // 8-bit grayscale __u32 teu=0; // eink update time __u32 tpu=0; // physics update time
//=============================================== // hoser - eink demo showing all-new dither design // This works on all kindle eink models. Enjoy! //----------------------------------------------- void hoser(void) { __u32 i,x,y,c,px1,py1,vx1,vy1,dx,dy,cc,cu,cl,cb,wd,nt; c=0,px1=100,py1=MY-40,vx1=1,vy1=-9,cc=31,cl=16,cb=11,nt=0,wd=3; for (cu=0;cu<60000;cu++) { if (0==cu%3000) { // periodic background display gmlib(GMLIB_UPDATE); teu+=400+gs*400; if (cb) cb--; for (y=0; y<=MY/2; y++) for (x=0; x<=MX/2; x++) { dx=MX/2-x; dy=MY/2-y; c=255-(dx*dx+dy*dy)*255/(MX*220); setpx(x,y,c); setpx(MX-x,y,c); setpx(x,MY-y,c); setpx(MX-x,MY-y,c); } } for (i=12;i<33;i++) { circle(px1,py1,i); } px1+=vx1; py1+=vy1; if (px1>MX-40 || px1<40) { vx1=-vx1; } if (py1<40) { py1=40; vy1=-vy1; } if (py1>MY-40) { py1=MY-40; vy1=-vy1; wd+=3; nt=1; } if (0==cu%cl) { vy1++; } if (1==nt && cb) { nt=0; gmlib(GMLIB_UPDATE); teu+=360+gs*240; } else if (0==cu%wd && wd<100) { gmlib(GMLIB_UPDATE); teu+=120; } } }
//========================================== // sparkle - eink spatiotemporal dither demo //------------------------------------------ void sparkle(void) { int 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;
int yoff=320,xoff=240,bbx=MX/2,bby=MY/6,bbvx=21; int bbvy=3,wbx=MX/2,wby=MY/2,wbvx=-18,wbvy=-12;
for (y=0;y<MY;y++) for (x=0;x<MX;x++) setpx(x,y,0); for (;;) { yoff=340-bs,xoff=yoff*3/4;
//### fill screen with grays for (y=0;y<(MY-yoff*2)/3;y++) for (x=0;x<MX-xoff*2;x++) { if (rand()%(MX-xoff*2)<x/2) setpx(x+xoff,y+yoff,0); else setpx(x+xoff,y+yoff,255); } for (y=(MY-yoff*2)/3;y<(MY-yoff*2)*2/3;y++) for (x=0;x<MX-xoff*2;x++) { if (rand()%(MX-xoff*2)<x/2) setpx(x+xoff,y+yoff,255); else setpx(x+xoff,y+yoff,0); } for (y=(MY-yoff*2)*2/3;y<MY-yoff*2;y++) for (x=0;x<MX-xoff*2;x++) { if (rand()%(MX-xoff*2)>x) setpx(x+xoff,y+yoff,0); else setpx(x+xoff,y+yoff,255); }
//### big ring for (y=25;y<51;y++) circle(bbx+50,bby+50,y);
//### small ball for (y=1;y<26;y++) circle(wbx+25,wby+25,y);
//### animate bbx += bbvx; bby += bbvy; wbx += wbvx; wby += wbvy; if (bbx>MX-xoff-120) { bbx=MX-xoff-120; bbvx=-bbvx; bs-=(bs/50+1); } if (bbx<xoff+20) { bbx=xoff+20; bbvx=-bbvx; bs-=(bs/50+1); } if (bby>MY-yoff-120) { bby=MY-yoff-120; bbvy=-bbvy; } if (bby<yoff+20) { bby=yoff+20; bbvy=-bbvy; } if (wbx>MX-xoff-60) { wbx=MX-xoff-60; wbvx=-wbvx; } if (wbx<xoff+20) { wbx=xoff+20; wbvx=-wbvx; } if (wby>MY-yoff-60) { wby=MY-yoff-60; wbvy=-wbvy; } if (wby<yoff+20) { wby=yoff+20; wbvy=-wbvy; } if (bs<0) { break; } gmlib(GMLIB_UPDATE); teu+=80; } }
//=============================================== // cosmegg - kindle cosmic egg demo // This works on all kindle eink models. Enjoy! //----------------------------------------------- void cosmegg(void) { int x,y,c,dx,dy,cu; for (cu=100000;cu>100;cu-=(cu/32)) { for (y=0; y<MY; y++) for (x=0; x<MX; x++) { dx=(x-MX*2/5); dy=(y-MY/2); c=((dx*dx+dy*dy-(x*y-4*x)/4)*256/cu)%256; setpx(x,y,c); } gmlib(GMLIB_UPDATE); teu+=300; } } //=============================================== // goodbye - goodbye demo //----------------------------------------------- void goodbye(void) { int i,j,c,v,pv,px=0,py=0,x,y,dx,dy,k; for (i=0;i<15000;i+=17) { for (y=0; y<=MY/2; y++) for (x=0; x<=MX/2; x++) { dx=MX/2-x; dy=MY/2-y; c=(dx*dy/4-i)&255; setpx(x,y,c); setpx(MX-x,y,c); setpx(x,MY-y,c); setpx(MX-x,MY-y,c); } if (i>1000 && i<40000) { for (k=0;i<30000;k++) { v=sv[k]; x=((v&255)-95)*i/6000; y=((v/256)-70)*i/6000; if (0==v+pv) { break; } else if (0==v) { pv=0; } else if (pv) { line(px+MX/2,py+MY/2,x+MX/2,y+MY/2,3*i/6000); } pv=v; px=x; py=y; } } gmlib(GMLIB_UPDATE); teu+=80; } }
//==================================== // gmlib - geekmaster function library // op (init, update, close) //------------------------------------ int gmlib(int op) { static struct update_area_t ua={0,0,600,800,fx_invert,NULL}; static struct mxcfb_update_data ur={ {0,0,600,800},WAVEFORM_MODE_AUTO,0,1,TEMP_USE_AMBIENT,0}; static int eupcode; static void *eupdata=NULL; struct fb_var_screeninfo screeninfo; if (GMLIB_INIT==op) { teu=getmsec(); // save original screen fdFB=open("/dev/fb0",O_RDWR); // open eink framebuffer fb0 ioctl(fdFB,FBIOGET_VSCREENINFO,&screeninfo); ppb=8/screeninfo.bits_per_pixel; // pixels per byte fs=screeninfo.xres_virtual/ppb; // fb0 stride fb=screeninfo.yres_virtual/ppb; // fb0 bytes MX=screeninfo.xres; // max X+1 MY=screeninfo.yres; // max Y+1 ua.x2=MX; ua.y2=MY; ur.update_region.width=MX; ur.update_region.height=MY; fdWB=open("/tmp/wb0",O_RDWR|O_CREAT); // open work framebuffer wb0 lseek(fdWB,MY*MX-1,SEEK_SET); write(fdWB,"",1); // create work buffer file fb0=(__u8 *)mmap(0,MY*fs,PROT_READ|PROT_WRITE,MAP_SHARED,fdFB,0); // map fb0 wb0=(__u8 *)mmap(0,MY*MX,PROT_READ|PROT_WRITE,MAP_SHARED,fdWB,0); // map wb0 if (fb>MY) { eupcode=MXCFB_SEND_UPDATE; eupdata=&ur; ur.update_mode=0; } else { eupcode=FBIO_EINK_UPDATE_DISPLAY_AREA; eupdata=&ua; } psave=malloc(fs*MY); memcpy(psave,fb0,fs*MY); memset(wb0,0,MY*MX); gmlib(GMLIB_UPDATE); teu+=300; memset(wb0,255,MY*MX); gmlib(GMLIB_UPDATE); teu+=80; } else if (GMLIB_UPDATE==op) { while (teu>getmsec()); // async update needs time dblit(); ioctl(fdFB,eupcode,eupdata); } else if (GMLIB_CLOSE==op) { gmlib(GMLIB_UPDATE); sleep(3); memcpy(fb0,psave,fs*MY); // restore display free(psave); gmlib(GMLIB_UPDATE); // update display munmap(wb0,MY*MX); // unmap wb0 close(fdWB); // close wb0 remove("/tmp/wb0"); // remove wb0 munmap(fb0,MY*fs); // unmap fb0 close(fdFB); // close fb0 } else { return -1; } return 0; }
//======================================== // 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) { wb0[y*MX+x]=c; }
//========================== // dblit - dither wb0 to fb0 //-------------------------- void dblit(void) { int x,y,ys; __u8 p,d,*pi,*po,*pd; pi=wb0-1; po=fb0-1; if (gs) { // 16-color dither if (ppb/2) { // 2 px/byte for (y=0;y<MY;y++) { pd=dt+(y&7)*8+7; for (x=0;x<MX;x+=8) { pd-=8; p=*pi++; d=*pd++; *po++=((p&240)+(d-(*pi&15)*255/15>>4&16)-16)&240| ((*pi++&240)+(*pd++-(*pi&15)*255/15>>4&16)-16)/16&15; p=*pi++; d=*pd++; *po++=((p&240)+(d-(*pi&15)*255/15>>4&16)-16)&240| ((*pi++&240)+(*pd++-(*pi&15)*255/15>>4&16)-16)/16&15; p=*pi++; d=*pd++; *po++=((p&240)+(d-(*pi&15)*255/15>>4&16)-16)&240| ((*pi++&240)+(*pd++-(*pi&15)*255/15>>4&16)-16)/16&15; p=*pi++; d=*pd++; *po++=((p&240)+(d-(*pi&15)*255/15>>4&16)-16)&240| ((*pi++&240)+(*pd++-(*pi&15)*255/15>>4&16)-16)/16&15; } } } else { // 1 px/byte for (y=0;y<MY;y++) { pd=dt+(y&7)*8+7; for (x=0;x<MX;x+=8) { pd-=8; // *po++=(*pi++&240)+(*pd++-(*pi&15)*255/15>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; *po++=(*pi++&240)+(((*pi&15)*4080>>8)+*pd++>>4&16)-16; } po+=(fs-MX); } } } else { // 2-color dither if (ppb/2) { // 2 px/byte for (y=0;y<MY;y++) { pd=dt+(y&7)*8+7; ys=(y&7)*8; for (x=0;x<MX;x+=8) { pd-=8; p=*pi++; d=*pd++; *po++=d-p>>4&240|*pd++-*pi++>>8&15; p=*pi++; d=*pd++; *po++=d-p>>4&240|*pd++-*pi++>>8&15; p=*pi++; d=*pd++; *po++=d-p>>4&240|*pd++-*pi++>>8&15; p=*pi++; d=*pd++; *po++=d-p>>4&240|*pd++-*pi++>>8&15; } }
} else { // 1 px/byte for (y=0;y<MY;y++) { pd=dt+(y&7)*8+7; for (x=0;x<MX;x+=8) { pd-=8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; *po++=*pd++-*pi++>>8; } po+=(fs-MX); } } } }
//============================================== // 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; } } }
//================================== // line - Bresenham's line algorithm //---------------------------------- void line(int x0,int y0,int x1,int y1,int c) { int dx,ny,sx,sy,e,e2; if (x1>x0) { dx=x1-x0; sx=1; } else { dx=x0-x1; sx=-1; } if (y1>y0) { ny=y0-y1; sy=1; } else { ny=y1-y0; sy=-1; } e=dx+ny; for (;;) { circle(x0,y0,c); e2=e+e; if (x0==x1 && y0==y1) { break; } if (e2>ny) { e+=ny; x0+=sx; } if (e2<dx) { e+=dx; y0+=sy; } } }
//==================================== // getmsec - get msec since first call // (tick counter wraps every 12 days) //------------------------------------ int getmsec(void) { int tc; static int ts=0; struct timeval tv; gettimeofday(&tv,NULL); tc=tv.tv_usec/1000+1000*(0xFFFFF&tv.tv_sec); if (0==tc) { ts=tc; } return tc-ts; }
//================== // main - start here //------------------ int main(void) {
gmlib(GMLIB_INIT);
hoser(); // hoser demo :D sparkle(); // sparkle demo :D gs=1; // grayscale dither hoser(); // hoser demo cosmegg(); // cosmegg demo gs=0; // binary dither goodbye(); // goodbye demo
sleep(5);
gmlib(GMLIB_CLOSE); // close geekmaster functions
return 0; }
NOTE: To compile the source code, you must copy the eink header files from the gpl source code to your compiler include/linux folder (as seen in the source code #include statements). If using TCC, then copy the header files into /mnt/us/tcc/include/linux/. I plan to include these header files in the next release of the tcc compiler package.
And by the way, I hand-optimized the dither routines to work better when compiled with TCC. The demos are no longer 5 times faster when compiled with a cross-tool. But I had to deal with a gcc compiler optimization bug that required that I use a couple of extra temporary variables to prevent bad code generation. NOT fun chasing that one down!
I hope to see interesting things from you people, using this code that required significant study and experimentation to perfect. Good luck, and let's not see my efforts wasted. Do good stuff with this.
UPDATE: I just noticed an obsolete comment in the dither code. Ignore the "broken code" warning. That was fixed by adding temporary vars (the compiler optimization bug). Weird stuff happens when you OR complex logic terms together that NO amount of parantheses can fix.) All better now. [I removed the comment from the code above, but it is still in the download file.]
Last edited by geekmaster; 04-28-2012 at 05:56 AM.
|