#include "ET++.ph" #ifdef __GNUG__ #pragma implementation #endif #include "DevBitmap.h" #include "WindowPort.h" #include "WindowColorMap.h" #include "Error.h" #include "StreamBuf.h" #include "ColorMapper.h" #include "Class.h" #define CACHESIZE 50 struct BitmapCache { int clock, wcmid; DevBitmap *org, *dbm; } Cache[CACHESIZE]; static int cacheclock; static int lastix; inline int PaddedBytesPerLine(int w, int depth, u_short padbytes) { int padbits= padbytes*8; return (((w*depth)+(padbits-1))/(padbits))*padbytes; } BitmapInfo::BitmapInfo(const Point &e, int bpp, int numcomp, int pad) : extent(e) { bitsperpixel= bpp; numcomponents= numcomp; rowpad= pad; rowlen= PaddedBytesPerLine(extent.x, bitsperpixel*numcomponents, rowpad); } BitmapInfo::BitmapInfo(const Point &e, int depth, int pad) : extent(e) { bitsperpixel= depth; rowpad= pad; rowlen= PaddedBytesPerLine(extent.x, bitsperpixel, rowpad); } //---- DevBitmap --------------------------------------------------------------- NewMetaImpl(DevBitmap,RefCounted, (T(size), T(depth), TB(changed))); DevBitmap::DevBitmap(const Point &sz, u_short dep) : size(sz) { depth= dep; changed= FALSE; } DevBitmap::DevBitmap(DevBitmap *dbm) : size(dbm->size) { depth= dbm->depth; changed= FALSE; } DevBitmap::~DevBitmap() { register BitmapCache *bmp= Cache; for (int i= 0; i < CACHESIZE; i++, bmp++) { if (bmp->org == this || bmp->dbm == this) { DevBitmap *o1= bmp->org, *o2= bmp->dbm; bmp->org= bmp->dbm= 0; SafeUnref(o1); SafeUnref(o2); bmp->org= 0; bmp->wcmid= 0; bmp->clock= 0; } } } DevBitmap *DevBitmap::MakeCopy() { DevBitmap *dbm= DevAllocBitmap(size, depth); dbm->DevBitBlt(0, 0, size.x, size.y, eBitCopy, this, 0, 0); return dbm; } int DevBitmap::BytesPerLine() { return PaddedBytesPerLine(size.x, depth, 1); } void DevBitmap::ReadData(BitmapInfo *bi, IStream &is) { int bpl= ((size.x*bi->bitsperpixel-1)/8+1); byte *bp= new byte[bpl]; int y; for (y= 0; y < size.y; y++) { is.read(bp, bpl); SetRow(bi, y, bp); } delete bp; } void DevBitmap::SetData(BitmapInfo *bi, void *data) { byte *b= (byte*) data; register int y; for (y= 0; y < bi->extent.y; y++) { SetRow(bi, y, &b[y*bi->rowlen]); //data+= bi->rowlen; } } void DevBitmap::SetRow(BitmapInfo *bi, int y, byte *rowdata) { register u_long pixel, m; register int x, i= 0, np, j; register byte *bp= rowdata; switch (bi->bitsperpixel) { case 1: for (x= 0; x < bi->rowlen; x++) { pixel= *bp++; m= 0x80; for (j= 0; j < 8; j++) { SetPixel(i++, y, (pixel & m) ? 1 : 0); m >>= 1; } } break; case 2: for (x= 0; x < bi->rowlen; x++) { pixel= *bp++; SetPixel(i++, y, (pixel & 0xc0) >> 6); SetPixel(i++, y, (pixel & 0x30) >> 4); SetPixel(i++, y, (pixel & 0x0c) >> 2); SetPixel(i++, y, (pixel & 0x03)); } break; case 4: for (x= 0; x < bi->rowlen; x++) { pixel= *bp++; SetPixel(i++, y, (pixel & 0xf0) >> 4); SetPixel(i++, y, (pixel & 0x0f)); } break; case 8: for (x= 0; x < bi->extent.x; x++) SetPixel(x, y, *bp++); break; case 16: { u_short *sp= (u_short*) bp; for (x= 0; x < bi->extent.x; x++) SetPixel(x, y, *sp++); } break; case 24: np= bi->bitsperpixel/8; for (x= 0; x < bi->extent.x; x++) { pixel= 0; for (j= 0; j < np; j++) pixel= (pixel << 8) + *bp++; SetPixel(i++, y, pixel); } break; case 32: { u_long *lp= (u_long*) bp; for (x= 0; x < bi->extent.x; x++) SetPixel(x, y, *lp++); } break; } } void DevBitmap::GetRow(BitmapInfo *bi, int row, byte *rowdata) { register u_long pixel; register int x, y= row, sh; register byte *bp= rowdata; switch (bi->bitsperpixel) { case 1: case 2: case 4: *bp= 0; sh= 8; for (x= 0; x < size.x; x++) { pixel= GetPixel(x, y); sh-= bi->bitsperpixel; *bp |= (byte)(pixel << sh); if (sh == 0) { *++bp= 0; sh= 8; } } if (sh != 8) bp++; break; case 8: for (x= 0; x < size.x; x++) { pixel= GetPixel(x, y); *bp++= (byte)(pixel & 0x000000ff); } break; case 16: for (x= 0; x < size.x; x++) { pixel= GetPixel(x, y); *bp++= (byte)((pixel & 0x0000ff00) >> 8); *bp++= (byte)(pixel & 0x000000ff); } break; case 24: for (x= 0; x < size.x; x++) { pixel= GetPixel(x, y); *bp++= (byte)((pixel & 0x00ff0000) >> 16); *bp++= (byte)((pixel & 0x0000ff00) >> 8); *bp++= (byte)(pixel & 0x000000ff); } break; case 32: for (x= 0; x < size.x; x++) { pixel= GetPixel(x, y); *bp++= (byte)((pixel & 0xff000000) >> 24); *bp++= (byte)((pixel & 0x00ff0000) >> 16); *bp++= (byte)((pixel & 0x0000ff00) >> 8); *bp++= (byte)(pixel & 0x000000ff); } break; } } void DevBitmap::WriteData(BitmapInfo *bi, OStream &os) { int bpl= ((size.x*bi->bitsperpixel-1)/8+1); byte *bp= new byte[bpl+100]; register int y; for (y= 0; y < size.y; y++) { GetRow(bi, y, bp); os.write(bp, bpl); } delete bp; } void DevBitmap::DevBitBlt(int x0, int y0, int w, int h, BitBltOp op, DevBitmap *s, int sx, int sy, u_long val) { register int x, y; for (y= 0; y < h; y++) { for (x= 0; x < w; x++) { switch (op) { case eBitCopy: SetPixel(x0+x, y0+y, s->GetPixel(sx+x, sy+y)); break; case eBitOr: SetPixel(x0+x, y0+y, GetPixel(x0+x, y0+y) | s->GetPixel(sx+x, sy+y)); break; case eBitSet: SetPixel(x0+x, y0+y, val); break; } } } } DevBitmap *DevBitmap::DevScaleBitmap(const Point &ss) { int x= size.x, y= size.y, sx= ss.x, sy= ss.y; DevBitmap *pr1, *pr2; register int b, src, dst, d, a1, a2; float scale; if (sx == x) // same width pr1= this; else if (sx < x) { // x-shrink pr1= DevAllocBitmap(Point(sx, y), depth); if (depth > 1) { scale= (float)x/(float)sx; for (dst= 0; dst < sx; dst++) if ((b= (int)(dst*scale+0.5)) < x) pr1->DevBitBlt(dst, 0, 1, y, eBitCopy, this, b, 0); } else { scale= (float)sx/(float)x; for (src= 0; src < x; src++) if ((b= (int)(src*scale+0.5)) < sx) pr1->DevBitBlt(b, 0, 1, y, eBitOr, this, src, 0); } } else { // x-magnify pr1= DevAllocBitmap(Point(sx, y), depth); d= sx/x; scale= (float)sx/(float)x; for (src= 0; src < x; src++) // slice if ((b= (int)(src*scale+0.5)) < sx) pr1->DevBitBlt(b, 0, 1, y, eBitCopy, this, src, 0); for (src= 0; src < d-1; src++) // smear pr1->DevBitBlt(src+1, 0, sx-d+1, y, eBitOr, pr1, src, 0); for (a1= src= 0; src < x; src++) { // fill remaining slots a2= (int)((src+1)*scale+0.5); if (a1+d != a2) pr1->DevBitBlt(a2-1, 0, 1, y, eBitCopy, this, src, 0); a1= a2; } } if (sy == y) // same height pr2= pr1; else if (sy < y) { // y-shrink pr2= DevAllocBitmap(Point(sx, sy), depth); if (depth > 1) { scale= (float)y/(float)sy; for (dst= 0; dst < sy; dst++) if ((b= (int)(dst*scale+0.5)) < y) pr2->DevBitBlt(0, dst, sx, 1, eBitCopy, pr1, 0, b); } else { scale= (float)sy/(float)y; for (src= 0; src < y; src++) if ((b= (int)(src*scale+0.5)) < sy) pr2->DevBitBlt(0, b, sx, 1, eBitOr, pr1, 0, src); } if (pr1 != this) delete pr1; } else if (sy > y) { // y-magnify pr2= DevAllocBitmap(Point(sx, sy), depth); d= sy/y; scale= (float)sy/(float)y; for (src= 0; src < y; src++) // slice if ((b= (int)(src*scale+0.5)) < sy) pr2->DevBitBlt(0, b, sx, 1, eBitCopy, pr1, 0, src); for (src= 0; src < d-1; src++) // smear pr2->DevBitBlt(0, src+1, sx, sy-d+1, eBitOr, pr2, 0, src); for (a1= src= 0; src < y; src++) { // fill remaining slots a2= (int)((src+1)*scale+0.5); if (a1+d != a2) pr2->DevBitBlt(0, a2-1, sx, 1, eBitCopy, pr1, 0, src); a1= a2; } if (pr1 != this) delete pr1; } return pr2; } void DevBitmap::DevHalftone(WindowColorMap *wcm, DevBitmap *spr, ColorMapper *cmapper) { register int *next, v, error; int s= (int)cmapper->MaxPixel()+1, *level, *errors, i, x, y, incr, lasterror, *nextbuf; u_long *out; RGB result; level= new int[s]; for (i= 0; i < s; i++) { cmapper->Pixel2RGB(i, &result); level[i]= result.AsGreyLevel(); } out= new u_long[256]; errors= new int[256]; for (i= 0; i < 256; i++) { RGB rgb((short)i); out[i]= wcm->RGB2Index(&rgb, &result); errors[i]= i - result.AsGreyLevel(); } nextbuf= new int[size.x + 4]; next= &nextbuf[2]; for (y= 0; y < size.y; y++) { if (y & 1) { // scan left incr= 1; i= size.x-1; } else { // scan right incr= -1; i= 0; } for (lasterror= x= 0; x < size.x; x++, i-= incr) { v= lasterror + level[spr->GetPixel(i, y)]; if (v < 0) v= 0; else if (v > 255) v= 255; SetPixel(i, y, out[v]); error= errors[v]; lasterror= next[i-incr] + (error*7) / 16; next[i-incr] = error / 16; next[i] += (error*5) / 16; next[i+incr]+= (error*3) / 16; } } delete nextbuf; delete out; delete errors; delete level; } void DevBitmap::DevFloydSteinberg(WindowColorMap *wcm, DevBitmap *spr, ColorMapper *cmapper) { RGB lasterror, error, *nextbuf, *next, rgb, result, v; register int i, x, y, incr; nextbuf= new RGB[size.x + 4]; next= &nextbuf[2]; for (y= 0; y < size.y; y++) { if (y & 1) { // scan left incr= 1; i= size.x-1; } else { // scan right incr= -1; i= 0; } for (x= 0; x < size.x; x++, i-= incr) { cmapper->Pixel2RGB(spr->GetPixel(i, y), &rgb); lasterror+= rgb; v= lasterror; v.Clamp(); SetPixel(i, y, wcm->RGB2Index(&v, &result)); error= rgb-result; lasterror= next[i-incr] + (error*7) / 16; next[i-incr] = error / 16; next[i] += (error*5) / 16; next[i+incr]+= (error*3) / 16; } } delete nextbuf; } void DevBitmap::DevMapColors(DevBitmap *src, u_long *map) { register int x, y; for (x= 0; x < size.x; x++) for (y= 0; y < size.y; y++) SetPixel(x, y, map[src->GetPixel(x, y)]); } void DevBitmap::DevMapDirect(WindowColorMap *wcm, DevBitmap *dbm, ColorMapper *cmapper) { RGB rgb; register int x, y; for (x= 0; x < size.x; x++) for (y= 0; y < size.y; y++) { cmapper->Pixel2RGB(dbm->GetPixel(x, y), &rgb); SetPixel(x, y, wcm->RGB2Index(&rgb)); } } DevBitmap *DevBitmap::FindInCache(WindowColorMap *wcm, const Point &scale) { register int i; register BitmapCache *bmc; int wcmid= 0; if (wcm) wcmid= wcm->GetId(); cacheclock++; for (i= 0; i < CACHESIZE; i++) { bmc= &Cache[(lastix + i) % CACHESIZE]; if (bmc->org == this && scale == bmc->dbm->Size() && wcmid == bmc->wcmid) { bmc->clock= cacheclock; return bmc->dbm; } } return 0; } void DevBitmap::AddToCache(DevBitmap *dbm, WindowColorMap *wcm) { register int i, old= cacheclock; register BitmapCache *bmc; for (lastix= i= 0; i < CACHESIZE; i++) { bmc= &Cache[i]; if (bmc->clock < old) { old= bmc->clock; lastix= i; } } bmc= &Cache[lastix]; DevBitmap *o1= bmc->org, *o2= bmc->dbm; bmc->dbm= bmc->org= 0; if (o1) o1->Unref(); if (o2) o2->Unref(); bmc->dbm= dbm; bmc->dbm->Ref(); bmc->org= this; bmc->org->Ref(); bmc->wcmid= wcm ? wcm->GetId() : 0; bmc->clock= cacheclock; } DevBitmap *DevBitmap::GetMask(const Point &e, ColorMapper *cmapper) { DevBitmap *mask; if (!cmapper->HasMask()) return 0; // try to find mask with correct size mask= FindInCache(0, e); if (mask) return mask; // try to find mask with original size mask= FindInCache(0, Size()); if (mask == 0) { // create mask from devbitmap mask= gWindowSystem->MakeDevBitmap(Size(), 0, 1); mask->Ref(); int w= Size().x, h= Size().y; register int x, y; RGB c; for (y= 0; y < h; y++) { for (x= 0; x < w; x++) { cmapper->Pixel2RGB(GetPixel(x, y), &c); mask->SetPixel(x, y, c.alpha); } } AddToCache(mask, 0); } if (mask->Size() != e) { mask= mask->DevScaleBitmap(e); AddToCache(mask, 0); } return mask; } DevBitmap *DevBitmap::PrepareBitmap(WindowPort *port, const Point &e, ColorMapper *cmapper) { register DevBitmap *bm; WindowColorMap *wcm= port->ColorMap(); // try to find devbitmap with correct size and prepared for this port if (!changed && (bm= FindInCache(wcm, e))) return bm; // try to find devbitmap prepared for this port (but wrong size) bm= FindInCache(wcm, Size()); if (bm == 0 || changed) { bm= cmapper->MapColors(wcm, this, port->GetDepth()); if (bm) AddToCache(bm, wcm); else bm= this; } // now bm has same depth as port // but has it correct size ? if (bm->Size() != e) { bm= bm->DevScaleBitmap(e); AddToCache(bm, wcm); } changed= FALSE; return bm; } //---- abstract methods -------------------------------------------------------- DevBitmap *DevBitmap::DevAllocBitmap(const Point&, u_short) { AbstractMethod("DevBitmap::DevAllocBitmap"); return 0; } void DevBitmap::SetPixel(u_int, u_int, u_long) { AbstractMethod("DevBitmap::SetPixel"); } u_long DevBitmap::GetPixel(u_int, u_int) { AbstractMethod("DevBitmap::GetPixel"); return 0; }