#include "stdafx.h" #include "wingk.h" #include gkTransparencyTable gkWinShape::tranTable; struct BMP_header{ gkLONG fSize; //54 byte header + 4*numColors (1-8bit) + (w*h*bpp)/8 gkWORD zero1, zero2; //0,0 gkLONG offsetBytes; //should be header (54) plus Palette Size gkLONG headerSize; //size of remaining header (40) gkLONG width, height; //w,h in pixels gkWORD planes, bpp; //plane=1, bpp=1,2,4, or most commonly 8 gkLONG compression, imageSize; //compression to zero, size is w*h(8bit) gkLONG xpels, ypels, zero3, zero4; //set to 0,0,0,0 }; int gkIO::ReadWord(istream &in){ int retval = in.get() << 8; return retval | (in.get() & 0xff); } int gkIO::ReadLong(istream &in){ int retval = ReadWord(in) << 16; return retval | (ReadWord(in) & 0xffff); } char *gkIO::ReadString(istream &in){ static char st[80]; int len = ReadWord(in); if(!len) return 0; in.read(st,len); st[len] = 0; return st; } char *gkIO::ReadNewString(istream &in){ int len = ReadWord(in); if(!len) return 0; char *st = new char[len+1]; in.read(st,len); st[len] = 0; return st; } void gkIO::WriteLong(ostream &out, int n){ WriteWord(out, n>>16); WriteWord(out, n); } void gkIO::WriteWord(ostream &out, int n){ out << (char) ((n>>8)&0xff); out << (char) (n&0xff); } void gkIO::WriteString(ostream &out, char *st){ WriteWord(out,strlen(st)); out.write(st,strlen(st)); } int gkRGB::Equals(int _r, int _g, int _b){ return _r==color.bytes.r && _g==color.bytes.g && _b==color.bytes.b; } void gkRGB::Combine(gkRGB c2, int alpha){ //mix alpha color.bytes.r += gkWinShape::tranTable.LookupTransparencyOffset(c2.GetR()-GetR(), alpha); color.bytes.g += gkWinShape::tranTable.LookupTransparencyOffset(c2.GetG()-GetG(), alpha); color.bytes.b += gkWinShape::tranTable.LookupTransparencyOffset(c2.GetB()-GetB(), alpha); } gkRGB::operator COLORREF(){ return (color.bytes.b<<16) | (color.bytes.g<<8) | (color.bytes.r); } gkPalGenItem::gkPalGenItem(gkRGB _color){ color = _color; occurrences = 1; nextItem = 0; } gkPalGenItem::~gkPalGenItem(){ } gkRGB gkPalGenItem::GetColor(){ return color; } void gkPalGenItem::AddOccurrence(){ occurrences++; } int gkPalGenItem::GetOccurrences(){ return occurrences; } void gkPalGenItem::SetOccurrences(int n){ occurrences = n; } void gkPalGenItem::SetNextItem(gkPalGenItem *item){ nextItem = item; } gkPalGenItem *gkPalGenItem::GetNextItem(){ return nextItem; } int gkPalGenItem::GetCount(){ gkPalGenItem *cur; int count = 0; for(cur=this; cur; cur=cur->nextItem){ if(cur->occurrences) count++; } return count; } int gkPalGenItem::SortCallback(const void *e1, const void *e2){ gkPalGenItem *i1 = *((gkPalGenItem**) e1); gkPalGenItem *i2 = *((gkPalGenItem**) e2); if(i1->occurrences > i2->occurrences) return -1; if(i1->occurrences == i2->occurrences) return 0; return 1; } gkPaletteGenerator::gkPaletteGenerator(){ int i; for(i=0; i<52; i++){ colorCube[i] = 0; } } gkPaletteGenerator::~gkPaletteGenerator(){ Reset(); } void gkPaletteGenerator::Reset(){ int i; for(i=0; i<52; i++){ if(colorCube[i]){ gkPalGenItem *cur, *next; for(cur=colorCube[i]; cur; cur=next){ next = cur->GetNextItem(); delete cur; } colorCube[i] = 0; } } } void gkPaletteGenerator::AddColor(gkRGB color){ int i = GetHash(color); if(!colorCube[i]){ colorCube[i] = new gkPalGenItem(color); }else{ gkPalGenItem *cur, *prev; for(cur=colorCube[i]; cur; cur=cur->GetNextItem()){ if((cur->GetColor()) == color){ cur->AddOccurrence(); //Color already in list break; } prev = cur; } if(!cur){ //color not in list prev->SetNextItem(new gkPalGenItem(color)); } } } void gkPaletteGenerator::CreatePalette(gkRGB *palette, int numEntries){ if(numEntries<=0) return; //Set all entries to black int i; for(i=0; iGetCount(); } while(!count){ //if no colors yet expand area of inclusion if(first==0 && last==51) return; //no colors anywhere! if(first>0){ first--; if(colorCube[first]) count += colorCube[first]->GetCount(); } if(last<51){ last++; if(colorCube[last]) count += colorCube[last]->GetCount(); } } //Create an array to hold all the colors for sorting purposes gkPalGenItem **colors = new gkPalGenItem*[count]; gkPalGenItem *cur; i = 0; int j; for(j=first; j<=last; j++){ if(colorCube[j]){ for(cur=colorCube[j]; cur; cur=cur->GetNextItem()){ if(cur->GetOccurrences()) colors[i++] = cur; } } } //figure out how many colors will come from this section of the cube int numToGrab = 1; int tempCurEntry = curEntry; while(nextEntry==first && tempCurEntry<(52-1)){ tempCurEntry++; nextEntry = (int) (scaleFactor * (tempCurEntry+1)); numToGrab++; } if(numToGrab > count) numToGrab = count; //sort colors into descending order and pick "num" most frequent qsort(colors, count, sizeof(gkPalGenItem*), gkPalGenItem::SortCallback); for(i=0; iGetColor(); colors[i]->SetOccurrences(0); } //delete sorting table delete colors; } } int gkPaletteGenerator::GetNumColors(){ int num = 0, i; for(i=0; i<52; i++) if(colorCube[i]) num += colorCube[i]->GetCount(); return num; } int gkPaletteGenerator::GetHash(gkRGB color){ int r = color.GetR() >> 6; //rough categories 0-3 int g = color.GetG() >> 6; int b = color.GetB() >> 6; int highest = r; if(g > highest) highest = g; if(b > highest) highest = b; int hash; // r > (g < b) // r > (g = b) // r > (g > b) // g > (r < b) // g > (r = b) // g > (r > b) // b > (r < g) // b > (r = g) // b > (r > g) // (r = b) > g // (r = g) > b // (g = b) > r // (r = g) = b if(r > g && r > b){ //red high if(g < b) hash = 0; // r > (g < b) else if(g==b) hash = 1; // r > (g = b) else hash = 2; // r > (g > b) }else if(g > r && g > b){ //green high if(r < b) hash = 3; // g > (r < b) else if(r==b) hash = 4; // g > (r = b) else hash = 5; // g > (r > b) }else if(b > r && b > g){ //blue high if(r < g) hash = 6; // b > (r < g) else if(r==g) hash = 7; // b > (r = g) else hash = 8; // b > (r > g) }else if(r==b && b==g){ //r = g = b hash = 9; }else if(r==b){ //(r = b) > g hash = 10; }else if(r==g){ //(r = g) > b hash = 11; }else{ //(g = b) > r hash = 12; } //make room in each category for four levels of intensity (0-3) hash = hash*4 + highest; return hash; } int gkPaletteGenerator::ColorExists(gkRGB color){ int hash = GetHash(color); if(!colorCube[hash]) return 0; gkPalGenItem *cur; for(cur=colorCube[hash]; cur; cur=cur->GetNextItem()){ if(cur->GetColor()==color) return 1; //found exact color } return 0; } gkRGB gkPaletteGenerator::MatchColor(gkRGB color){ int hash = GetHash(color); int r = color.GetR(); int g = color.GetG(); int b = color.GetB(); if(colorCube[hash]){ //near colors; search just this section gkPalGenItem *cur, *bestMatch; int bestDiff; bestMatch = colorCube[hash]; int r2, g2, b2; r2 = abs(r - bestMatch->GetColor().GetR()); g2 = abs(g - bestMatch->GetColor().GetG()); b2 = abs(b - bestMatch->GetColor().GetB()); bestDiff = r2 + g2 + b2; for(cur=bestMatch->GetNextItem(); cur; cur=cur->GetNextItem()){ r2 = abs(r - cur->GetColor().GetR()); g2 = abs(g - cur->GetColor().GetG()); b2 = abs(b - cur->GetColor().GetB()); int curDiff = r2 + g2 + b2; if(curDiff < bestDiff){ bestDiff = curDiff; bestMatch = cur; if(!curDiff) return bestMatch->GetColor(); } } return bestMatch->GetColor(); }else{ //no colors nearby; expand search //Get it from ~greyscale if possible int first, last; first = last = 36 + (hash % 4); if(!colorCube[first]){ first = 0; //nothing there either; search everything last = 51; /* first = 36; last = 39; //first = hash - (hash%4); //different intensities, same color //last = first + 3; if(!colorCube[first] && !colorCube[first+1] && !colorCube[first+2] && !colorCube[last]){ first = 0; //nothing there either; search everything last = 51; } */ } gkPalGenItem *cur, *bestMatch; int bestDiff = 0x7fffffff; bestMatch = 0; int i; for(i=first; i<=last; i++){ for(cur=colorCube[i]; cur; cur=cur->GetNextItem()){ int r2 = abs(r - cur->GetColor().GetR()); int g2 = abs(g - cur->GetColor().GetG()); int b2 = abs(b - cur->GetColor().GetB()); int curDiff = r2 + g2 + b2; if(curDiff < bestDiff){ bestDiff = curDiff; bestMatch = cur; if(!curDiff) return bestMatch->GetColor(); } } } if(!bestMatch) return gkRGB(0,0,0); return bestMatch->GetColor(); } } gkTransparencyTable::gkTransparencyTable(){ lookup = new gkBYTE[256*256]; int baseOffset, alpha, i; i = 0; for(baseOffset=0; baseOffset<256; baseOffset++){ for(alpha=0; alpha<256; alpha++){ lookup[i++] = (baseOffset * alpha) / 255; } } } gkTransparencyTable::~gkTransparencyTable(){ if(lookup) delete lookup; lookup = 0; } int gkTransparencyTable::LookupTransparencyOffset(int baseOffset, int alpha){ return (baseOffset>=0) ? lookup[(baseOffset<<8)+alpha] : (-lookup[((-baseOffset)<<8)+alpha]); } gkWinShape::gkWinShape(){ data = 0; width = height = bpp = 0; } gkWinShape::~gkWinShape(){ FreeData(); } void gkWinShape::FreeData(){ if(data){ delete data; data = 0; } width = height = 0; } void gkWinShape::Create(int _width, int _height){ if(_width <= 0 || _height <= 0) return; FreeData(); width = _width; height = _height; //round up width to ensure multiple of 4 pixels width = (width + 3) & (~3); data = new gkBYTE[(width*height)<<2]; bpp = 32; } void gkWinShape::Cls(){ if(!this->data) return; gkLONG *dest = (gkLONG*) this->data; int i = this->width * this->height; while(i--){ *(dest++) = 0xff000000; } } void gkWinShape::Cls(gkRGB color){ if(!this->data) return; gkLONG *dest = (gkLONG*) this->data; int i = this->width * this->height; while(i--){ *(dest++) = color.GetARGB(); } } void gkWinShape::Plot(int x, int y, gkRGB color, int testBoundaries){ if(!data) return; if(testBoundaries){ if(x<0 || x>=width || y<0 || y>=height) return; } gkRGB *dest = (gkRGB*) (data + ((y*width + x) << 2)); *dest = color; } gkRGB gkWinShape::Point(int x, int y, int testBoundaries){ if(!data) return gkRGB(0,0,0); if(testBoundaries){ if(x<0 || x>=width || y<0 || y>=height) return gkRGB(0,0,0); } gkRGB *color = (gkRGB*) (data + ((y*width + x) << 2)); return *color; } void gkWinShape::Line(int x1, int y1, int x2, int y2, gkRGB color){ int temp; if(y1==y2){ //straight horizontal line if(x2 < x1) RectFill(x2,y1,(x1-x2)+1,1,color); else RectFill(x1,y1,(x2-x1)+1,1,color); return; }else if(x1==x2){ //straight vertical line if(y2 < y1) RectFill(x1,y2,1,(y1-y2)+1,color); else RectFill(x1,y1,1,(y2-y1)+1,color); return; } //clip line to screen if(y2 < y1){ //orient line to be drawn from top to temp = x1; x1 = x2; x2 = temp; //bottom for initial clipping tests temp = y1; y1 = y2; y2 = temp; } if(y2 < 0 || y1 >= height) return; double xdiff = x2-x1, ydiff = y2-y1; double slopexy = xdiff / ydiff; int diff; //perform vertical clipping diff = 0 - y1; if(diff > 0){ //y1 is above top boundary x1 += (int) (slopexy * diff); y1 = 0; } diff = (y2 - height) + 1; if(diff > 0){ //y2 is below bottom boundary x2 -= (int) (slopexy * diff); y2 = height - 1; } //reorient line to be drawn from left to right for horizontal clipping tests if(x2 < x1){ temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; xdiff = x2-x1; ydiff = y2-y1; } double slopeyx = ydiff / xdiff; if(x2 < 0 || x1 >= width) return; diff = 0 - x1; if(diff > 0){ //x1 is to left of left boundary y1 += (int) (slopeyx * diff); x1 = 0; } diff = (x2 - width) + 1; if(diff > 0){ //x2 is to right of right boundary y2 -= (int) (slopeyx * diff); x2 = width - 1; } //draw the line using Bresenham's //coordinates are now such that x increment is always positive int xdist = x2-x1; int ydist = y2-y1; int pitch = width; gkRGB *dest = (gkRGB*) (data + (((y1 * width) + x1) <<2)); if(ydist < 0){ ydist = -ydist; pitch = -pitch; } int err, i; if(xdist >= ydist){ //loop on x, change y every so often err = 0; for(i=xdist; i>=0; i--){ *(dest++) = color; err += ydist; if(err >= xdist){ err -= xdist; dest += pitch; } } }else{ //loop on y, change x every so often err = 0; for(i=ydist; i>=0; i--){ *dest = color; dest += pitch; err += xdist; if(err >= ydist){ err -= ydist; dest++; } } } } void gkWinShape::LineAlpha(int x1, int y1, int x2, int y2, gkRGB color, int alpha){ int temp; if(y1==y2){ //straight horizontal line if(x2 < x1) RectFill(x2,y1,(x1-x2)+1,1,color); else RectFill(x1,y1,(x2-x1)+1,1,color); return; }else if(x1==x2){ //straight vertical line if(y2 < y1) RectFill(x1,y2,1,(y1-y2)+1,color); else RectFill(x1,y1,1,(y2-y1)+1,color); return; } //clip line to screen if(y2 < y1){ //orient line to be drawn from top to temp = x1; x1 = x2; x2 = temp; //bottom for initial clipping tests temp = y1; y1 = y2; y2 = temp; } if(y2 < 0 || y1 >= height) return; double xdiff = x2-x1, ydiff = y2-y1; double slopexy = xdiff / ydiff; int diff; //perform vertical clipping diff = 0 - y1; if(diff > 0){ //y1 is above top boundary x1 += (int) (slopexy * diff); y1 = 0; } diff = (y2 - height) + 1; if(diff > 0){ //y2 is below bottom boundary x2 -= (int) (slopexy * diff); y2 = height - 1; } //reorient line to be drawn from left to right for horizontal clipping tests if(x2 < x1){ temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; xdiff = x2-x1; ydiff = y2-y1; } double slopeyx = ydiff / xdiff; if(x2 < 0 || x1 >= width) return; diff = 0 - x1; if(diff > 0){ //x1 is to left of left boundary y1 += (int) (slopeyx * diff); x1 = 0; } diff = (x2 - width) + 1; if(diff > 0){ //x2 is to right of right boundary y2 -= (int) (slopeyx * diff); x2 = width - 1; } //draw the line using Bresenham's //coordinates are now such that x increment is always positive int xdist = x2-x1; int ydist = y2-y1; int pitch = width; gkRGB *dest = (gkRGB*) (data + (((y1 * width) + x1) <<2)); if(ydist < 0){ ydist = -ydist; pitch = -pitch; } int err, i; if(xdist >= ydist){ //loop on x, change y every so often err = 0; for(i=xdist; i>=0; i--){ dest->Combine(color,alpha); dest++; err += ydist; if(err >= xdist){ err -= xdist; dest += pitch; } } }else{ //loop on y, change x every so often err = 0; for(i=ydist; i>=0; i--){ dest->Combine(color,alpha); dest += pitch; err += xdist; if(err >= ydist){ err -= ydist; dest++; } } } } void gkWinShape::RectFrame(int x, int y, int w, int h, gkRGB color){ int x2 = x + w - 1; int y2 = y + h - 1; RectFill(x,y,w,1,color); RectFill(x,y2,w,1,color); RectFill(x,y,1,h,color); RectFill(x2,y,1,h,color); } void gkWinShape::RectFill(int x, int y, int w, int h, gkRGB color){ int x2 = (x + w) - 1; int y2 = (y + h) - 1; //Clip rectangle if(x < 0) x = 0; if(y < 0) y = 0; if(x2 >= width) x2 = width - 1; if(y2 >= height) y2 = height - 1; if(x2 < x || y2 < y) return; //Set pointers and offsets gkRGB *destStart = (gkRGB*) (data + (((y * width) + x) <<2)); gkRGB *dest; int numRows = (y2 - y) + 1; int rowWidth = (x2 - x) + 1; //do it while(numRows--){ dest = destStart; int i; for(i=0; i= width) x2 = width - 1; if(y2 >= height) y2 = height - 1; if(x2 < x || y2 < y) return; //Set pointers and offsets gkRGB *destStart = (gkRGB*) (data + (((y * width) + x) <<2)); gkRGB *dest; int numRows = (y2 - y) + 1; int rowWidth = (x2 - x) + 1; //do it while(numRows--){ dest = destStart; int i; for(i=0; iCombine(color,alpha); dest++; } destStart += width; } } void gkWinShape::RectFillChannel(int x, int y, int w, int h, gkRGB color, int mask){ int x2 = (x + w) - 1; int y2 = (y + h) - 1; //Clip rectangle if(x < 0) x = 0; if(y < 0) y = 0; if(x2 >= width) x2 = width - 1; if(y2 >= height) y2 = height - 1; if(x2 < x || y2 < y) return; //Set pointers and offsets gkLONG *destStart = (gkLONG*) (data + (((y * width) + x) <<2)); gkLONG *dest; int numRows = (y2 - y) + 1; int rowWidth = (x2 - x) + 1; gkLONG srcColor = color.GetARGB() & mask; gkLONG destMask = ~mask; //do it while(numRows--){ dest = destStart; int i; for(i=0; i=width || cur.y>=height) continue; //filling correct color check if(!(*(cur.pos)==fillColor)) continue; //fill color & add adjacent *(cur.pos) = color; queue[qTail++] = gkFillItem(cur.x+1,cur.y,cur.pos+1); qTail &= 16383; queue[qTail++] = gkFillItem(cur.x-1,cur.y,cur.pos-1); qTail &= 16383; queue[qTail++] = gkFillItem(cur.x,cur.y+1,cur.pos+width); qTail &= 16383; queue[qTail++] = gkFillItem(cur.x,cur.y-1,cur.pos-width); qTail &= 16383; } } int gkWinShape::GetNumColors(){ gkPaletteGenerator palGen; int i = width * height; gkRGB *src = (gkRGB*) data; while(i--){ palGen.AddColor(*(src++)); } return palGen.GetNumColors(); } void gkWinShape::RemapColor(gkRGB oldColor, gkRGB newColor){ if(!data) return; gkRGB *src = (gkRGB*) data; int i, j; for(j=0; jSetA(alpha); } } void gkWinShape::SetColorAlpha(gkRGB color, int alpha){ if(!data) return; gkRGB *src = (gkRGB*) data; int i, j; for(j=0; jSetA(alpha); } src++; } } } int gkWinShape::GetShape(gkWinShape *srcShape, int x, int y, int w, int h){ if(!srcShape || !srcShape->data) return 0; //adjust src rectangle until it fits within source data if(x<0){ w += x; x = 0; } if(y<0){ h += y; y = 0; } if(x + w > srcShape->width){ w = srcShape->width - x; } if(y + h > srcShape->height){ h = srcShape->height - y; } if(w<=0 || h<=0) return 0; FreeData(); Create(w, h); gkBYTE *src = srcShape->data + ((y*srcShape->width + x)<<2); gkBYTE *dest = this->data; int srcSkip = srcShape->width << 2; //4 bytes per pixel w <<= 2; //4 bytes per pixel while(h--){ memcpy(dest, src, w); dest += w; src += srcSkip; } return 1; } int gkWinShape::GetShape(gkWinShape *srcShape){ if(this->width != srcShape->width || this->height != srcShape->height || (!this->data) || (!srcShape->data)){ //mem needs to be reallocated FreeData(); memcpy(this, srcShape, sizeof(gkWinShape)); data = 0; if(srcShape->data){ data = new gkBYTE[(width * height) << 2]; memcpy(data, srcShape->data, (width * height) << 2 ); } }else{ //already got right size mem, just copy data over memcpy(data, srcShape->data, (width * height) << 2); fontSpacing = srcShape->fontSpacing; fontKerning = srcShape->fontKerning; x_handle = srcShape->x_handle; y_handle = srcShape->y_handle; } return data!=0; } int gkWinShape::GetShape(HDC hdc){ //get a handle to the bitmap currently in the HDC HBITMAP bitmap = (HBITMAP) GetCurrentObject(hdc,OBJ_BITMAP); //get info about that bitmap BITMAP bm; GetObject(bitmap,sizeof(BITMAP),&bm); int width = bm.bmWidth; int height = bm.bmHeight; static BITMAPINFO bmInfo={{40, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0}}; static int setup=0; if(!setup){ setup = 1; *((int*)(&bmInfo.bmiColors[0])) = 0xff0000; //red mask *((int*)(&bmInfo.bmiColors[4])) = 0x00ff00; //green mask *((int*)(&bmInfo.bmiColors[8])) = 0x0000ff; //blue mask } //specify height as negative to get a top-to-bottom bitmap bmInfo.bmiHeader.biWidth = width; bmInfo.bmiHeader.biHeight = -height; Create(width,height); GetDIBits(hdc,bitmap,0,height,data,&bmInfo,DIB_RGB_COLORS); return 1; } int gkWinShape::GetShape(HBITMAP bitmap){ //Create a DC compatible with the screen HDC hdc = CreateDC("DISPLAY",0,0,0); //get info about that bitmap BITMAP bm; GetObject(bitmap,sizeof(BITMAP),&bm); int width = bm.bmWidth; int height = bm.bmHeight; static BITMAPINFO bmInfo={{40, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0}}; static int setup=0; if(!setup){ setup = 1; *((int*)(&bmInfo.bmiColors[0])) = 0xff0000; //red mask *((int*)(&bmInfo.bmiColors[4])) = 0x00ff00; //green mask *((int*)(&bmInfo.bmiColors[8])) = 0x0000ff; //blue mask } //specify height as negative to get a top-to-bottom bitmap bmInfo.bmiHeader.biWidth = width; bmInfo.bmiHeader.biHeight = -height; Create(width,height); GetDIBits(hdc,bitmap,0,height,data,&bmInfo,DIB_RGB_COLORS); DeleteDC(hdc); return 1; } int gkWinShape::SaveShape(char *filename){ ofstream outfile(filename, ios::out | ios::binary); if(!outfile) return 0; int result = SaveShape(outfile); outfile.close(); return result; } int gkWinShape::SaveShape(ostream &out){ int totalSize = ((width * height) << 2); out << "SHPE"; int skipSize = totalSize + 20; gkIO::WriteLong(out, skipSize); //Write shape header gkIO::WriteWord(out, 3); //type 3 gkIO::WriteWord(out, width); gkIO::WriteWord(out, height); gkIO::WriteWord(out, (bpp==8)?8:32); out << (char) fontSpacing << (char) fontKerning; gkIO::WriteWord(out, x_handle); gkIO::WriteWord(out, y_handle); gkIO::WriteWord(out, 0); //6 bytes of reserved space gkIO::WriteLong(out, 0); //Write data if(data){ int pitch = width << 2; gkLONG *src; gkBYTE *srcStart = data; int i,j; for(j=0; jLoadShape(infile); infile.close(); return result; } int gkWinShape::LoadShape(istream &infile){ FreeData(); if(infile.get() != 'S') return 0; if(infile.get() != 'H') return 0; if(infile.get() != 'P') return 0; if(infile.get() != 'E') return 0; gkIO::ReadLong(infile); //discard skipsize //Read shape header if(gkIO::ReadWord(infile) != 2) return 0; width = gkIO::ReadWord(infile); height = gkIO::ReadWord(infile); int filebpp = gkIO::ReadWord(infile); if(!bpp) bpp = filebpp; if(width && height){ Create(width,height); } fontSpacing = infile.get(); fontKerning = infile.get(); x_handle = gkIO::ReadWord(infile); y_handle = gkIO::ReadWord(infile); gkIO::ReadWord(infile); gkIO::ReadLong(infile); //discard reserved space if(!width || !height) return 1; //nothing to load, null shape int pitch = width << 2; gkBYTE *destStart = data; gkLONG *dest; int x,y; for(y=0; yLoadBMP(infile); infile.close(); return result; } int gkWinShape::LoadBMP(istream &infile){ BMP_header header; if(gkIO::ReadWord(infile)!=0x424d){ //check for "BM" return 0; } infile.read((char*)&header, sizeof(BMP_header)); if(header.bpp != 24){ cout << "LoadBMP can only handle 24-bit files" << endl; return 0; } FreeData(); width = header.width; height = header.height; bpp = (char) header.bpp; Create(width,height); // load graphics, coverting every three (B,R,G) bytes to one ARGB value. // lines padded to even multiple of 4 bytes int srcPitch = ((header.width * 3) + 3) & (~3); gkBYTE *buffer = new gkBYTE[srcPitch * height]; gkBYTE *nextBuffPtr = buffer + ((height-1) * srcPitch); gkBYTE *buffPtr; infile.read(buffer, srcPitch * height); gkBYTE *nextDest = data; gkRGB *dest; int destPitch = (width << 2); int i, j; j = height; while(j--){ buffPtr = nextBuffPtr; nextBuffPtr -= srcPitch; dest = (gkRGB*) nextDest; nextDest += destPitch; i = header.width; while(i--){ dest->SetB(*(buffPtr++)); dest->SetG(*(buffPtr++)); dest->SetR(*(buffPtr++)); dest->SetA(0xff); dest++; } for(i=header.width; iSetARGB(0); dest++; } } delete buffer; return 1; } void gkWinShape::Blit(gkWinShape *destShape, int x, int y, int flags){ if(!data || !destShape || !destShape->data) return; gkBYTE *src = 0; int srcWidth = this->width; int srcSkip = this->width; int lines = this->height; //clip left side if(x < 0){ src += -x; //times 4 later srcWidth -= -x; x = 0; } //clip right side int diff = (x + srcWidth) - destShape->width; if(diff > 0){ srcWidth -= diff; } if(srcWidth <= 0) return; //clip top if(y<0){ src += (-y * this->width); //times 4 later lines += y; //lines -= (-y) y = 0; } //clip bottom diff = (y + lines) - destShape->height; if(diff > 0){ lines -= diff; } if(lines <= 0) return; int destSkip = destShape->width; gkBYTE *dest = destShape->data + (((y * destShape->width) + x) << 2); src = this->data + ((int)src << 2); srcWidth <<= 2; srcSkip <<= 2; destSkip <<= 2; if(flags & GKBLT_TRANSPARENT){ //blit using alpha 0 as fully transparent while(lines--){ MemCpyTrans(dest, src, srcWidth); src += srcSkip; dest += destSkip; } }else{ //blit without a transparent color while(lines--){ memcpy(dest, src, srcWidth); src += srcSkip; dest += destSkip; } } } ////////////////////////////////////////////////////////////////////// // Function: BlitScale // Arguments: destShape // x // y // flags // scale - number of source pixels for every one // dest pixel, stored in 24:8 fixed point. // $100=100%, $200=200% (mag x2), $80=50% ////////////////////////////////////////////////////////////////////// void gkWinShape::BlitScale(gkWinShape *destShape, int x, int y, int scale, int flags){ if(!data || !destShape || !destShape->data || !scale) return; gkBYTE *src = 0; int srcSkip = this->width; int srcWidth = (this->width * scale) >> 8; int lines = (this->height * scale) >> 8; //clip left side if(x < 0){ src += (-x << 8) / scale; srcWidth -= -x; x = 0; } //clip right side int diff = (x + srcWidth) - destShape->width; if(diff > 0){ srcWidth -= diff; } if(srcWidth <= 0) return; //clip top if(y<0){ src += ((-y * this->width) << 8) / scale; lines += y; //lines -= (-y) y = 0; } //clip bottom diff = (y + lines) - destShape->height; if(diff > 0){ lines -= diff; } if(lines <= 0) return; int destSkip = destShape->width; gkBYTE *dest = destShape->data + (((y * destShape->width) + x) << 2); src = this->data + ((int)src << 2); srcSkip <<= 2; destSkip <<= 2; int ratio = (int) ((1.0f / (((float) scale) / 256.0f)) * 256.0f); int lineError = 0; while(lines--){ BlitLineScale(dest, src, srcWidth, ratio); lineError += ratio; src += srcSkip * (lineError >> 8); lineError &= 0xff; dest += destSkip; } /* if(flags & GKBLT_TRANSPARENT){ //blit using alpha 0 as fully transparent while(lines--){ MemCpyTrans(dest, src, srcWidth); src += srcSkip; dest += destSkip; } }else{ //blit without a transparent color while(lines--){ memcpy(dest, src, srcWidth); src += srcSkip; dest += destSkip; } } */ } void gkWinShape::BlitScale(gkWinShape *destShape, int x, int y, float scale, int flags){ BlitScale(destShape,x,y,flags,(int) (256.0f * scale)); } void gkWinShape::BlitChannel(gkWinShape *destShape, int x, int y, int mask){ if(!data || !destShape || !destShape->data) return; gkBYTE *src = 0; int srcWidth = this->width; int srcSkip = this->width; int lines = this->height; //clip left side if(x < 0){ src += -x; srcWidth -= -x; x = 0; } //clip right side int diff = (x + srcWidth) - destShape->width; if(diff > 0){ srcWidth -= diff; } if(srcWidth <= 0) return; //clip top if(y<0){ src += (-y * this->width); lines += y; //lines -= (-y) y = 0; } //clip bottom diff = (y + lines) - destShape->height; if(diff > 0){ lines -= diff; } if(lines <= 0) return; int destSkip = destShape->width; gkBYTE *dest = destShape->data + (((y * destShape->width) + x) << 2); src = this->data + ((int)src << 2); srcSkip <<= 2; destSkip <<= 2; int inverseMask = ~mask; while(lines--){ gkLONG *curSrc = (gkLONG*) src; gkLONG *curDest = (gkLONG*) dest; int pixels = srcWidth; while(pixels--){ *curDest = (*curSrc & mask) | (*curDest & inverseMask); curSrc++; curDest++; } src += srcSkip; dest += destSkip; } } HBITMAP gkWinShape::GetDIB(HDC hdc){ //note you must DeleteObject() on the dib that's returned once done static BITMAPINFO bmInfo={{40, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0}}; static int setup=0; if(!setup){ setup = 1; *((int*)(&bmInfo.bmiColors[0])) = 0xff0000; //red mask *((int*)(&bmInfo.bmiColors[4])) = 0x00ff00; //green mask *((int*)(&bmInfo.bmiColors[8])) = 0x0000ff; //blue mask } bmInfo.bmiHeader.biWidth = width; bmInfo.bmiHeader.biHeight = -height; HBITMAP dib = CreateDIBitmap(hdc, &bmInfo.bmiHeader, CBM_INIT, data, &bmInfo, DIB_RGB_COLORS); return dib; } void gkWinShape::BlitToHDC(HDC hDC, int x, int y){ static HDC srcHDC; static int setup=0; if(!setup){ setup = 1; srcHDC = CreateCompatibleDC(0); } HBITMAP dib = GetDIB(hDC); HBITMAP oldBitmap = (HBITMAP) SelectObject(srcHDC,dib); BitBlt(hDC, x, y, width, height, srcHDC, 0, 0, SRCCOPY); SelectObject(srcHDC,oldBitmap); DeleteObject(dib); } void gkWinShape::MemCpyTrans(gkBYTE *dest, gkBYTE *src, int numBytes){ //copies colors using the Alpha for transparency gkRGB *destColor = (gkRGB*) dest; gkRGB *srcColor = (gkRGB*) src; int numColors = numBytes >> 2; int c1; while(numColors--){ int alpha; switch(alpha = (srcColor->GetA())){ case 0: break; case 255: //Straight copy *destColor = *srcColor; break; default: //mix alpha c1 = destColor->GetR(); c1 += tranTable.LookupTransparencyOffset( srcColor->GetR()-c1, alpha); destColor->SetR(c1); c1 = destColor->GetG(); c1 += tranTable.LookupTransparencyOffset( srcColor->GetG()-c1, alpha); destColor->SetG(c1); c1 = destColor->GetB(); c1 += tranTable.LookupTransparencyOffset( srcColor->GetB()-c1, alpha); destColor->SetB(c1); break; } srcColor++; destColor++; } } void gkWinShape::BlitLineScale(gkBYTE *dest,gkBYTE *src,int pixels,int ratio){ gkRGB *srcRGB = (gkRGB*) src; gkRGB *destRGB = (gkRGB*) dest; int error = 0; while(pixels--){ *(destRGB++) = *srcRGB; error += ratio; srcRGB += error >> 8; error &= 0xff; } }