AbePralle-FGB/Tools/GBConv2/Source/wingk.cpp

1547 lines
36 KiB
C++

#include "stdafx.h"
#include "wingk.h"
#include <stdlib.h>
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; i<numEntries; i++) palette[i] = gkRGB(0,0,0);
//1 entry from every this many indices
double scaleFactor = 52.0 / numEntries;
int curEntry = 0;
while(curEntry < numEntries){
//Get first & last array indices for this section
int first = (int) (scaleFactor * (curEntry));
int nextEntry = (int) (scaleFactor * (curEntry+1));
int last = nextEntry - 1;
if(last < first) last = first;
//Count total # of colors in this section
int count = 0;
for(i=first; i<=last; i++){
if(colorCube[i]) count += colorCube[i]->GetCount();
}
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; i<numToGrab; i++){
palette[curEntry++] = colors[i]->GetColor();
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<rowWidth; i++){
*(dest++) = color;
}
destStart += width;
}
}
void gkWinShape::RectFillAlpha(int x, int y, int w, int h, gkRGB color,
int alpha){
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<rowWidth; i++){
dest->Combine(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<rowWidth; i++){
*dest = (*dest & destMask) | (srcColor);
dest++;
}
destStart += width;
}
}
struct gkFillItem{
short int x, y;
gkRGB *pos;
inline gkFillItem(){}
inline gkFillItem(int _x, int _y, gkRGB *_pos){
x = _x;
y = _y;
pos = _pos;
}
};
void gkWinShape::FloodFill(int x, int y, gkRGB color){
gkFillItem queue[16384];
int qHead=0, qTail=0;
//not the color we're filling WITH, the color we're filling ON
gkRGB fillColor = Point(x,y);
if(color==fillColor) return;
//enqueue the starting location
queue[qTail++] = gkFillItem(x,y,(gkRGB *)(data + ((y*width + x) * 4)));
//keep looping, filling current location & adding adjacent locations
//until queue is empty
while(qHead != qTail){
//dequeue an item
gkFillItem cur = queue[qHead++];
qHead &= 16383;
//out of bounds check
if(cur.x<0 || cur.y<0 || cur.x>=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; j<height; j++){
for(i=0; i<width; i++){
if(oldColor == (*src)){
*src = newColor;
}
src++;
}
}
}
int gkWinShape::GetPalette(gkRGB *palette, int maxColors){
gkPaletteGenerator palGen;
int i = width * height;
gkRGB *src = (gkRGB*) data;
while(i--){
palGen.AddColor(*(src++));
}
int num = palGen.GetNumColors();
palGen.CreatePalette(palette, maxColors);
return num;
}
void gkWinShape::RemapToPalette(gkRGB *palette, int numColors){
//add palette colors to generator for matching purposes
gkPaletteGenerator generator;
int i = numColors;
gkRGB *src = palette;
while(i--){
generator.AddColor(*(src++));
}
//find color in palette that best matches each pixel
i = width * height;
src = (gkRGB*) data;
while(i--){
*src = generator.MatchColor(*src);
src++;
}
}
void gkWinShape::ReduceColors(int numColors){
gkPaletteGenerator generator;
int i = width * height;
gkRGB *src = (gkRGB*) data;
while(i--){
generator.AddColor(*(src++));
}
//gkRGB palette[256];
gkRGB *palette = new gkRGB[numColors];
generator.CreatePalette(palette, numColors);
RemapToPalette(palette, numColors);
delete palette;
}
void gkWinShape::ExchangeColors(gkRGB c1, gkRGB c2){
if(!data) return;
gkRGB *src = (gkRGB*) data;
int i, j;
for(j=0; j<height; j++){
for(i=0; i<width; i++){
if(c1 == (*src)){
*src = c2;
}else if(c2 == (*src)){
*src = c1;
}
src++;
}
}
}
void gkWinShape::SetAlpha(int alpha){
if(!data) return;
gkRGB *src = (gkRGB*) data;
int i = (width * height);
while(i--){
(src++)->SetA(alpha);
}
}
void gkWinShape::SetColorAlpha(gkRGB color, int alpha){
if(!data) return;
gkRGB *src = (gkRGB*) data;
int i, j;
for(j=0; j<height; j++){
for(i=0; i<width; i++){
if(color == (*src)){
src->SetA(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; j<height; j++){
src = (gkLONG*) srcStart;
for(i=0; i<width; i++){
gkIO::WriteLong(out, *(src++));
}
srcStart += pitch;
}
}
return 1;
}
int gkWinShape::LoadShape(char *filename){
ifstream infile(filename,ios::in | ios::binary | ios::nocreate);
if(!infile) return 0;
int result = this->LoadShape(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; y<height; y++){
dest = (gkLONG*) destStart;
for(x=0; x<width; x++){
*(dest++) = gkIO::ReadLong(infile);
}
destStart += pitch;
}
return 1;
}
int gkWinShape::LoadBMP(char *filename){
ifstream infile(filename,ios::in | ios::binary | ios::nocreate);
if(!infile) return 0;
int result = this->LoadBMP(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; i<width; i++){
dest->SetARGB(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;
}
}