/* dither.c -- convert 16- or 24-bit bmp to 8 or fewer bits (Floyd-Steinberg) */
/* see end-of-file for (c) */
/* This was written for Microsoft 32-bit-integer compilers--it
* should compile with
*    cl gif2bmp.c
* to produce the executable gif2bmp.exe
*
* The Microsoft oddities are:
*  The funny include files -- they may be omitted on compilers
*  that can not find them (stdio.h better be there, however)
* #pragma pack(1) -- This forces the BITMAPFILEHEADER structure
*  to be the same length in memory as in files (otherwise, the structure
*  is padded out to be a multiple of 8? bytes long
* Note that Microsoft uses all-caps for typedefs.  Ugh.
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <math.h>
typedef unsigned char byte;
typedef unsigned short WORD;
typedef unsigned long DWORD;#define SIZE_BITMAPFILEHEADER 14
#define SIZE_BITMAPINFOHEADER 40
#define BI_BITFIELDS 3double gamma= 1.85;
int outmode= 0;
#define OUT16    1    // 16-color output
#define OUT2    2    // 2-color output
int reversing= 1;
int (*nearrtn)(int,int,int);
int bpp= 8;
int threshhold;
int startnoisy= 1;unsigned char winmap[]= {
  0,  0,  0,0,  0,  0,128,0,  0,128,  0,0,  0,128,128,0,
128,  0,  0,0, 128,  0,128,0, 128,128,  0,0, 192,192,192,0,
128,128,128,0,  0,  0,255,0,  0,255,  0,0,  0,255,255,0,
255,  0,  0,0, 255,  0,255,0, 255,255,  0,0, 255,255,255,0};
unsigned char monomap[]= {0,0,0,0, 255,255,255,0};#pragma pack(1)
typedef struct tagRGBQUAD {
    byte    rgbBlue;
    byte    rgbGreen;
    byte    rgbRed;
    byte    rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPFILEHEADER {
    WORD    bfType;
    DWORD    bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
    DWORD    bfOffBits;
} BITMAPFILEHEADER;
typedef BITMAPFILEHEADER *PBITMAPFILEHEADER;typedef struct tagBITMAPINFOHEADER{
      DWORD      biSize;
      DWORD      biWidth;
      DWORD      biHeight;
      WORD      biPlanes;
      WORD      biBitCount;    DWORD      biCompression;
    DWORD      biSizeImage;
    DWORD      biXPelsPerMeter;
    DWORD      biYPelsPerMeter;
    DWORD      biClrUsed;
    DWORD      biClrImportant;
} BITMAPINFOHEADER;BITMAPFILEHEADER hdr;
BITMAPINFOHEADER bi;    // Source image header
BITMAPINFOHEADER bo;    // Output image headerlong wib;
long dwib;RGBQUAD srccmap[256];
unsigned char cmap[256*4];    /* Bytes of blue, green, red, reserved */
unsigned char *scanline;
unsigned char used[256];FILE *fpin;    /* Input file fp */static int dib_wib(int bitcount, int wi){
    switch (bitcount){
    case 1: wi= (wi+31) >> 3; break;
    case 4:    wi= (wi+7)  >> 1; break;
    case 8:    wi=  wi+3; break;
    case 16:wi= (wi*2)+3; break;
        case 24:wi= (wi*3)+3; break;
    case 32:return wi*4;
    }
    return wi & ~3;
}
void err(char *msg){
    fprintf(stderr, "%s\n", msg);
    exit(5);
}
/* These are used for 16- and 32-bit DIBS */        
static DWORD rgbmask[3]= {0x7c00,0x03e0,0x001f};
static int rgbshft[3]= {7, 2, -3};
void gulp_header(char *dibname){
    int v, y;    if((fpin= fopen (dibname, "rb"))==0){
        perror(dibname);
        exit(0);
    }
    fread(&hdr,1,sizeof(hdr),fpin);
    if(hdr.bfType!= 0x4d42)
        err("Image not a .bmp");    fread(&bi, 1, sizeof(bi), fpin);
    wib= dib_wib((int)bi.biBitCount, (int)bi.biWidth);
    bi.biSizeImage= bi.biHeight * wib;
    if(bi.biBitCount<=8){
        v= (int)((hdr.bfOffBits - bi.biSize - sizeof(BITMAPFILEHEADER))/sizeof(RGBQUAD));
        bi.biClrUsed= v;
        if(v>0)
            fread(srccmap, sizeof(RGBQUAD), v, fpin);
    }else if((bi.biBitCount==16 || bi.biBitCount==32)
    && bi.biCompression==BI_BITFIELDS){
        /* Read 3 masks */
        fread(rgbmask, sizeof(DWORD), 3, fpin);
        for(y= 0; y<3; y++){
            for(v= sizeof(DWORD); --v>=0 && 0==(rgbmask[y]&(1<<v)); v++);
            rgbshft[y]= v-8;
        }
    }
    v= (int)wib;
    if(v<(int)(3*bi.biWidth))
        v= (int)3*bi.biWidth;
    scanline= malloc(v);
    if(scanline==0)
        err("Not enough room for the scanline");
}
void readnextscanline(){
    int k= 0;
    int x;
    DWORD *dwp;
    WORD *wp;
    fread(scanline, 1, (int)wib, fpin);
    switch((int)bi.biBitCount){
    case 32:
        dwp= (DWORD *)scanline;
        for(x= 0; x<(int)bi.biWidth; x++){
            if(bi.biCompression==BI_BITFIELDS){
                scanline[k++]= (byte)((*dwp&rgbmask[2]) >> rgbshft[2]);
                scanline[k++]= (byte)((*dwp&rgbmask[1]) >> rgbshft[1]);
                scanline[k++]= (byte)((*dwp&rgbmask[0]) >> rgbshft[0]);
                ++dwp;
            }else{
                scanline[k++]= scanline[4*x];
                scanline[k++]= scanline[4*x+1];
                scanline[k++]= scanline[4*x+2];
            }
        } break;
    case 24:
        break;
    case 16:
        k= (int)(3*bi.biWidth);
        wp= (WORD *)(&scanline[wib]);
        for(x= (int)bi.biWidth; --x>=0; ){
            --wp;
            scanline[--k]= (byte)((*wp&rgbmask[0]) >> rgbshft[0]);
            scanline[--k]= (byte)((*wp&rgbmask[1]) >> rgbshft[1]);
            scanline[--k]= (byte)((*wp&rgbmask[2]) >> rgbshft[2]);
        } break;
    case 8: case 4: case 1:
        k= (int)(3*bi.biWidth);
        for(x= (int)bi.biWidth; --x>=0; ){
            int pv;
            if(bi.biBitCount==8)
                pv= scanline[x];
            else if(bi.biBitCount==4)
                pv= (x&1? scanline[x>>1] : scanline[x>>1] >> 4) & 15;
            else
                pv= scanline[x>>3] & (1 << (x&7)) ? 255 : 0;
            scanline[--k]= srccmap[pv].rgbRed;
            scanline[--k]= srccmap[pv].rgbGreen;
            scanline[--k]= srccmap[pv].rgbBlue;
        } break;
    }
}
/* Output the image */
int spew_header(FILE *fp, unsigned char *cmap){
    int n= 0;    dwib= dib_wib((int)bo.biBitCount, (int)bo.biWidth);
    if(bo.biBitCount==1)n= 2;
    if(bo.biBitCount==4)n= 16;
    if(bo.biBitCount==8)n= 256;
    bo.biSize= SIZE_BITMAPINFOHEADER;
    bo.biPlanes= 1;
    bo.biCompression= 0;
    bo.biSizeImage= bo.biHeight * dwib;
    bo.biClrUsed= n;
    bo.biClrImportant= n;
    bo.biXPelsPerMeter= 0;
    bo.biYPelsPerMeter= 0;    hdr.bfType= 0x4d42;    /* BM */
    hdr.bfReserved1= 0;
    hdr.bfReserved2= 0;
    hdr.bfOffBits = SIZE_BITMAPFILEHEADER + bo.biSize + n*sizeof(RGBQUAD);
    hdr.bfSize= hdr.bfOffBits + bo.biSizeImage;    fwrite(&hdr,1,sizeof(hdr),fp);
    fwrite(&bo, 1, sizeof(bo), fp);
    if(n>0)
        fwrite(cmap, sizeof(RGBQUAD), n, fp);
    return 1;
}
char *note[]= {
    "Usage: [Options] source.bmp dest.bmp",
    "Dither a 24-bit .bmp file to 8 bits (or 4 bits Windows colors)",
    "Options:",
    "-n### Dither to # red, # green, # blue values [default is -n666]",
    "-w    Dither to the 16 standard windows colors (4 bits/pixel output)",
    "-m    Dither to black and white (1 bit/pixel output)",
    "-g #  Set image gamma [default 1.85]",
    "-r    Send errors to the right [default alternates right/left]",
    "-i    Start with no noise (when background color is an output color)",
    0};int tab7[1024], tab5[1024], tab3[1024], tab1[1024];
unsigned char neartable[16*16*16];
int *rtab, *gtab, *btab;
int gammatab[256];
unsigned char degamma[4*512];
int gammacmap[4*256];/* The 'get the pvalue of a color near this color' routines: */int pvnear(int r, int g, int b){    /* reds x greens x blues colormap */
    return rtab[degamma[512+r]] + gtab[degamma[512+g]] + btab[degamma[512+b]];
}
int pvtab_near(int r, int g, int b){    /* disordered colormap */
    r >>= 5; g >>= 5; b >>= 5;
    if((r|g|b)&-16){    // Color off cube, then force to a cube corner!
        r= r>7? 15:0;
        g= g>7? 15:0;
        b= b>7? 15:0;
    }
    return neartable[b+16*(g+16*r)];
}
int mono_near(int r, int g, int b){    /* one-bit output */
    return b + 2*(r+2*g) < threshhold? 0 : 1;
}void neartable_make(int n){    /* Set up the disordered table from gammacmap */
    int r,g,b;
    int j= 0;
    for(r= 0; r<512; r+= 34)
    for(g= 0; g<512; g+= 34)
    for(b= 0; b<512; b+= 34){
        int pv= 0;
        int k;
        long dmin= 0x7fffffff;
        for(k= 0; k<n; k++){
            int k4= 4*k;
            long dr= r-gammacmap[k4+2];
            long dg= g-gammacmap[k4+1];
            long db= b-gammacmap[k4+0];
            long d= dr*dr+db*db+dg*dg;
            if(d<dmin){
                dmin= d;
                pv= k;
            }
        }
        neartable[j++]= pv;
    }
}
void createmap(unsigned char *map, int nr, int ng, int nb){
    int r,g,b,k;
    if(nr*ng*nb>256)
        return;    for(k= 0; k<512; k++){
        tab7[512+k]= (7*k+8)/16;
        tab7[512-k]= -tab7[512+k];
        tab5[512+k]= (5*k+8)/16;
        tab5[512-k]= -tab5[512+k];
        tab3[512+k]= (3*k+8)/16;
        tab3[512-k]= -tab3[512+k];
        tab1[512+k]= (1*k+8)/16;
        tab1[512-k]= -tab1[512+k];
    }
    for(k= 0; k<256; k++)
        gammatab[k]= (int)(511.0*pow(k/255.0, gamma) + 0.5);
    for(k= 0; k<512; k++){
        degamma[k]= 0;
        degamma[k+512]= (int)(255.0*pow(k/511.0, 1/gamma) + 0.5);
        degamma[k+1024]= 255;
        degamma[k+1536]= 255;
    }
    if(outmode==OUT2){            // One-bit colormap
        threshhold= (int)(7*511*pow(0.5, 1/gamma) + 0.5);
        for(k= 0; k<sizeof(monomap); k++)
            map[k]= monomap[k];
        for(k= 0; k<4*2; k++)
            gammacmap[k]= map[k]? 511 : 0;
    }else if(outmode==OUT16){        // Windows colormap
        for(k= 0; k<sizeof(winmap); k++)
            map[k]= winmap[k];
        for(k= 0; k<4*16; k++)
            gammacmap[k]= gammatab[map[k]];
        neartable_make(16);
    }else{                    // Ordered colormap
        for(k= r= 0; r<nr; r++){
            for(g= 0; g<ng; g++){
                for(b= 0; b<nb; b++){
                    map[k++]= b*255/(nb-1);
                    map[k++]= g*255/(ng-1);
                    map[k++]= r*255/(nr-1);
                    k++;
                }
            }
        }
        rtab= (int *)(&neartable[0]);
        gtab= rtab+256;
        btab= gtab+256;
        for(k= 0; k<256; k++){
            rtab[k]= ng * nb * ( k * nr/256 );
            gtab[k]= nb * ( k * ng/256 );
            btab[k]= k * nb/256;
        }
        for(k= 0; k<4*256; k++)
            gammacmap[k]= gammatab[map[k]];
    }
}
void fsdither(FILE *fo){
    int y, r, g, b, pv;
    int ru, gu, bu;
    unsigned char *outbuf= (char *)malloc((int)bo.biWidth);
    int x= 3*bi.biWidth+6;
    int *scanerr= (int *)malloc(sizeof(int)*x);    // Scanline of errors    if(startnoisy){
        int mask;
        switch(bo.biBitCount){
        case 1:    mask= 511; break;
        case 4: mask= 255; break;
        case 8: mask= 128; break;
        }
        srand(12345);    // Always use the same noise
        while(--x>=0)scanerr[x]= 2*(rand()&mask)-mask;
    }else
        while(--x>=0)scanerr[x]= 0;    
    ru= 0; gu= 0; bu= 0;
    for(y= 0; y<(int)bi.biHeight; y++){
        unsigned char *pc;
        int *pe;
        readnextscanline();
        if(y&reversing){
            while(--x>=0){
                pc -= 3;
                pe -= 3;
                r= gammatab[pc[2]] + tab7[ru] + pe[2];
                g= gammatab[pc[1]] + tab7[gu] + pe[1];
                b= gammatab[pc[0]] + tab7[bu] + pe[0];
                pv= (*nearrtn)(r,g,b);
                outbuf[x]= pv;
                pv *= 4;
                r -= gammacmap[pv+2]-512; if(r>1023)r= 1023;
                g -= gammacmap[pv+1]-512; if(g>1023)g= 1023;
                b -= gammacmap[pv+0]-512; if(b>1023)b= 1023;
                pe[5] += tab3[r];
                pe[4] += tab3[g];
                pe[3] += tab3[b];
                pe[2]= tab5[r] + tab1[ru];
                pe[1]= tab5[g] + tab1[gu];
                pe[0]= tab5[b] + tab1[bu];
                ru= r; gu= g; bu= b;
            }
        }else{
            pc= scanline;
            pe= scanerr+3;
            for(x= 0; x<(int)bi.biWidth; x++){
                r= gammatab[pc[2]] + tab7[ru] + pe[2];
                g= gammatab[pc[1]] + tab7[gu] + pe[1];
                b= gammatab[pc[0]] + tab7[bu] + pe[0];
                pv= (*nearrtn)(r,g,b);
                outbuf[x]= pv;
                pv *= 4;
                r -= gammacmap[pv+2]-512; if(r>1023)r= 1023;
                g -= gammacmap[pv+1]-512; if(g>1023)g= 1023;
                b -= gammacmap[pv+0]-512; if(b>1023)b= 1023;
                pe[-1] += tab3[r];
                pe[-2] += tab3[g];
                pe[-3] += tab3[b];
                pe[2]= tab5[r] + tab1[ru];
                pe[1]= tab5[g] + tab1[gu];
                pe[0]= tab5[b] + tab1[bu];
                ru= r; gu= g; bu= b;
                pc += 3;
                pe += 3;
            }
        }
        if(bpp==1){
            b= 64;
            r= 0;
            if(outbuf[0])
                outbuf[0]= 128;
            for(x= 1; x<(int)bi.biWidth; x++){
                if(outbuf[x])
                    outbuf[r] |= b;
                if(0==(b >>= 1)){
                    b= 128;
                    outbuf[++r]= 0;
                }
            }
        }else if(bpp==4){
            for(r= x= 0; x<(int)bi.biWidth; x+= 2)
                outbuf[r++]= (unsigned char)((outbuf[x]<<4) | outbuf[x+1]);
        }
        fwrite(outbuf, 1, (int)dwib, fo);
    }
}
void main(int ac, char **av){
    FILE *fp;
    char *cp;
    char *srcname= 0;
    char *dstname= 0;
    int nr= 6, ng= 6, nb= 6;    bpp= 8;
    nearrtn= pvnear;    while(cp= *++av)if(*cp++=='-')switch(*cp++){
    case 'i':    startnoisy= 0; break;
    case 'r':    reversing= 0; break;
    case 'w':    outmode= OUT16; nearrtn= pvtab_near; bpp= 4; break;
    case 'm':    outmode= OUT2;  nearrtn= mono_near; bpp= 1; break;
    case 'g':    gamma= atof(*++av); break;
    case 'n':    if(*cp==0)cp= *++av;
            nr= *cp++ - '0'; ng= *cp++ - '0'; nb= *cp++ - '0';
            break;
    default:
    erra:        for(av= note; *av; ++av)
                fprintf(stderr, "%s\n", *av);
            exit(1);
    }else if(srcname==0)
        srcname= *av;
    else if(dstname==0)
        dstname= *av;
    else
        goto erra;
    if(dstname==0)
        goto erra;    /* Open the source bmp */
    gulp_header(srcname);    /* Create the gif file */
    fp= fopen(dstname, "wb");
    if(fp==0){
        perror(dstname);
        exit(1);
    }
    bo= bi;    // The output image resembles the source image
    bo.biBitCount= bpp;    // With fewer bits per pixel
    createmap(cmap, nr, ng, nb);
    spew_header(fp, cmap);
    fsdither(fp);
}
/*(C)1996 Ephraim Cohen--
** permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notices appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.

*/
这个可以学习一下