View Single Post
Old 04-28-2012, 05:20 AM   #1
geekmaster
Carpe diem, c'est la vie.
geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.geekmaster ought to be getting tired of karma fortunes by now.
 
geekmaster's Avatar
 
Posts: 6,433
Karma: 10773668
Join Date: Nov 2011
Location: Multiverse 6627A
Device: K1 to PW3
Arrow 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,};

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 (
cbcb--;
          for (
y=0y<=MY/2y++) for (x=0x<=MX/2x++) {
            
dx=MX/2-xdy=MY/2-yc=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+=vx1py1+=vy1;
        if (
px1>MX-40 || px1<40) { vx1=-vx1; }
        if (
py1<40) { py1=40vy1=-vy1; }
        if (
py1>MY-40) { py1=MY-40vy1=-vy1wd+=3nt=1; }
        if (
0==cu%cl) { vy1++; }
        if (
1==nt && cb) {
            
nt=0gmlib(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/2setpx(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/2setpx(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)>xsetpx(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 += bbvxbby += bbvywbx += wbvxwby += wbvy;
        if (
bbx>MX-xoff-120) { bbx=MX-xoff-120bbvx=-bbvxbs-=(bs/50+1); }
        if (
bbx<xoff+20) { bbx=xoff+20bbvx=-bbvxbs-=(bs/50+1); }
        if (
bby>MY-yoff-120) { bby=MY-yoff-120bbvy=-bbvy; }
        if (
bby<yoff+20) { bby=yoff+20bbvy=-bbvy; }
        if (
wbx>MX-xoff-60) { wbx=MX-xoff-60wbvx=-wbvx; }
        if (
wbx<xoff+20) { wbx=xoff+20wbvx=-wbvx; }
        if (
wby>MY-yoff-60) { wby=MY-yoff-60wbvy=-wbvy; }
        if (
wby<yoff+20) { wby=yoff+20wbvy=-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=0y<MYy++) for (x=0x<MXx++) { dx=(x-MX*2/5); dy=(y-MY/2);
            
c=((dx*dx+dy*dy-(x*y-4*x)/4)*256/cu)%256setpx(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=0y<=MY/2y++) for (x=0x<=MX/2x++) {
            
dx=MX/2-xdy=MY/2-yc=(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/6000y=((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=vpx=xpy=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=MXua.y2=MY;
        
ur.update_region.width=MXur.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_UPDATEeupdata=&urur.update_mode=0; }
        else { 
eupcode=FBIO_EINK_UPDATE_DISPLAY_AREAeupdata=&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-1po=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+7ys=(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+=yy++; e+=y;
        if (
e>0) { e-=xx-=1e-=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-x0sx=1; } else { dx=x0-x1sx=-1; }
    if (
y1>y0) { ny=y0-y1sy=1; } else { ny=y1-y0sy=-1; }
    
e=dx+ny;
  for (;;) { 
circle(x0,y0,c);
        
e2=e+e;
        if (
x0==x1 && y0==y1) { break; }
        if (
e2>ny) { e+=nyx0+=sx; }
        if (
e2<dx) { e+=dxy0+=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.]
Attached Files
File Type: gz newtrix.tar.gz (14.2 KB, 558 views)

Last edited by geekmaster; 04-28-2012 at 05:56 AM.
geekmaster is offline   Reply With Quote