mirror of https://github.com/AbePralle/FGB.git
1547 lines
36 KiB
C++
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;
|
|
}
|
|
}
|
|
|