View Single Post
Old 04-24-2012, 12:01 AM   #36
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: 10773670
Join Date: Nov 2011
Location: Multiverse 6627A
Device: K1 to PW3
Arrow 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=fb0psave=malloc(wbytes*MY); memcpy(psave,fb0,wbytes*MY);
    
start=pbuff+wbytes*40half=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/2setpx(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/2setpx(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>xsetpx(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 += bbvxbby += bbvywbx += wbvxwby += wbvy;
        if (
bbx>xoff+380) { bbx=xoff+380bbvx=-bbvx; }
        if (
bbx<xoff) { bbx=xoffbbvx=-bbvx; }
        if (
bby>140+yoff) { bby=140+yoffbbvy=-bbvy; }
        if (
bby<40) { bby=40bbvy=-bbvy; }
        if (
wbx>xoff+405) { wbx=xoff+405wbvx=-wbvx; }
        if (
wbx<xoff) { wbx=xoffwbvx=-wbvx; }
        if (
wby>215+yoff) { wby=215+yoffwbvy=-wbvy; }
        if (
wby<40) { wby=40wbvy=-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,fxu8 *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=0ua.y1=0ua.x2=MXua.y2=MYua.fx=0ua.buffer=NULL;
            
ioctl(fdFBFBIO_EINK_UPDATE_DISPLAY_AREA, &ua); // fx_update_partial
        
}
        else if (
blk) { d8w();
            
ua.x1=0ua.y1=0ua.x2=MXua.y2=MYua.fx=0ua.buffer=NULL;
            
ioctl(fdFBFBIO_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,*point x,y;
    
pi=wbpo=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,*point x,y;
    
pi=wbpo=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,*point x,y,ys;
    
pi=wbpo=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+=yy++; e+=y;
        if (
e>0) { e-=xx-=1e-=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.xresMY=screeninfo.yreswbytes=MX/2wb2=wbytes/2;
    
fb0=(__u8*)mmap(0,MY*wbytes,PROT_READ|PROT_WRITE,MAP_SHARED,fdFB,0); // map fb0

    // save original screen
    
pbuff=fb0psave=malloc(wbytes*MY); memcpy(psave,fb0,wbytes*MY);
    
start=pbuff+wbytes*40half=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=0y<240y++) for (x=0x<480x++) pxwht(x+xoff,y+yoff);


    for (
i=0i<800i++) {

//### fill screen with grays
        
for (y=0y<80y++) for (x=0x<480x++) {
            if (
rand()%480<x/2pxblk(x+xoff,y+yoff) else pxwht(x+xoff,y+yoff);
        }
        for (
y=80y<160y++) for (x=0x<480x++) {
            if (
rand()%480<x/2pxwht(x+xoff,y+yoff) else pxblk(x+xoff,y+yoff);
        }
        for (
y=160y<240y++) for (x=0x<480x++) {
            if (
rand()%480>xpxblk(x+xoff,y+yoff) else pxwht(x+xoff,y+yoff);
        }
        if (!
once) { once=1;
            
ua.x1=xoffua.y1=yoffua.x2=xoff+481ua.y2=yoff+241;
            
ua.which_fx=0// fx_update_partial
            
ioctl(fdFB,FBIO_EINK_UPDATE_DISPLAY_AREA,&ua);
        }

//### black box
        
for (y=bbyy<bby+33y++) for (x=bbxx<bbx+100x++) pxblk(x,y);
        for (
y=bby+33y<bby+66y++) {
            for (
x=bbxx<bbx+33x++) pxblk(x,y);
            for (
x=bbx+66x<bbx+100x++) pxblk(x,y);
        } 
        for (
y=bby+66y<bby+100y++) for (x=bbxx<bbx+100x++) pxblk(x,y);
        
//### white box
        
for (y=wbyy<wby+25y++) for (x=wbxx<wbx+25x++) pxwht(x,y);

//### animate
        
bbx += bbvxbby += bbvywbx += wbvxwby += wbvy;
        if (
bbx>xoff+380) { bbx=xoff+380bbvx=-bbvx; }
        if (
bbx<xoff) { bbx=xoffbbvx=-bbvx; }
        if (
bby>140+yoff) { bby=140+yoffbbvy=-bbvy; }
        if (
bby<40) { bby=40bbvy=-bbvy; }
        if (
wbx>xoff+405) { wbx=xoff+405wbvx=-wbvx; }
        if (
wbx<xoff) { wbx=xoffwbvx=-wbvx; }
        if (
wby>215+yoff) { wby=215+yoffwbvy=-wbvy; }
        if (
wby<40) { wby=40wbvy=-wbvy; }

        
ua.x1=xoffua.y1=yoffua.x2=xoff+481ua.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=0ua.y1=0ua.x2=600ua.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!

Attached Files
File Type: gz sparkle-1.0.tar.gz (5.9 KB, 337 views)
File Type: gz sparkle-2.0.tar.gz (9.4 KB, 326 views)

Last edited by geekmaster; 04-24-2012 at 02:07 AM.
geekmaster is offline   Reply With Quote