#include <stdio.h>
#include <stdlib.h>
#include <cstring>
using namespace std;
#include "Draw.h"
///////////////
//OGL Texture//
///////////////
OGL_Texture::OGL_Texture(){
w=h=0;
texture_generated = false;
Texture_ID = 0;
}
OGL_Texture::~OGL_Texture(){
if (Texture_ID){glDeleteTextures(1,&Texture_ID);}//0 is not a valid texture id
}
void OGL_Texture::gen_texture(uint16_t width, uint16_t height, uint8_t *PixData){
if (width==0 || height==0){printf("ERROR! WIDTH OR HEIGHT OF TEXTURE == 0"); exit(1);}
//delete old texture if existing
if (texture_generated && Texture_ID){
glDeleteTextures(1,&Texture_ID);
}
w = width;
h = height;
//generate texture
glGenTextures(1,&Texture_ID);
if (Texture_ID==0){printf("ERROR IN TEXTURE GENERATION! NO DEVICE CONTEXT?"); exit(1);}
glEnable(GL_TEXTURE_RECTANGLE_NV);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, Texture_ID);
//2d texture, level of detail, number of components (3=rgb), w,h, border, byte order, data format, data
glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, PixData);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glDisable(GL_TEXTURE_RECTANGLE_NV);
texture_generated = true;
}
///////////////
//OGL Surface//
///////////////
OGL_Surface::OGL_Surface(){
texture_generated = false;
bpp=4;
w=h=0;
PixData = nullptr;
}
OGL_Surface::~OGL_Surface(){
if (PixData != nullptr) {delete [] PixData;}
}
void OGL_Surface::Create_Surface(uint16_t set_w,uint16_t set_h, char set_bpp){
if (set_bpp==24){bpp=3;}
if (set_bpp==32){bpp=4;}
// if surface already exists and width or height change delete old texture
if (texture_generated && (w != set_w || h != set_h)){
glDeleteTextures(1,&Texture_ID);
texture_generated = false;
}
// if surface already exists delete old data
if (PixData != nullptr) {delete [] PixData;}
w = set_w;
h = set_h;
bpp = set_bpp;
PixData = new uint8_t[w*h*bpp];
memset(PixData,255, w*h*bpp);
}
void OGL_Surface::Clear_Surface(uint8_t r, uint8_t g, uint8_t b, uint8_t a){
if (r == g && g == b && bpp==3){
memset(PixData, r, w*h*bpp);
}
else if (r == g && g == b && b == a && bpp==4){
memset(PixData, r, w*h*bpp);
}
else{
unsigned int pos = 0;
while (pos<w*h*bpp){
PixData[pos++] = r;
PixData[pos++] = g;
PixData[pos++] = b;
if(bpp==4){PixData[pos++] = a;}
}
}
}
void OGL_Surface::Set_Surface_AlphaKey(uint8_t r, uint8_t g, uint8_t b)
{
if(bpp!=4){printf("trying to set alphakey on bpp that does not support alpha\n");return;}
int pos = 0;
for (unsigned int y = 0; y<h; y++){
for (unsigned int x = 0; x<w; x++){
if ((PixData[pos]==r) && (PixData[pos+1]==b) && (PixData[pos+2] == b)){
PixData[pos+3]= 0;//if colour = alphakey set alpha channel to 0
}
pos+=4;
}
}
}
void OGL_Surface::gen_texture(){
if (w==0 || h==0){printf("ERROR! WIDTH OR HEIGHT OF SURFACE == 0"); exit(1);}
//Power of two texture support
uint32_t width = w;
uint32_t height= h;
//only generate a texture once
if (!texture_generated){
glGenTextures(1,&Texture_ID);
if (Texture_ID==0){printf("ERROR IN TEXTURE GENERATION! NO DEVICE CONTEXT?"); exit(1);}
}
glEnable(GL_TEXTURE_RECTANGLE_NV);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, Texture_ID);
//glTexImage2D respecifies the entire texture, changing its size, deleting the previous data etc.
//only do this if texture doesn't exist or if w/h change
if (!texture_generated){
switch (bpp){
//2d texture, level of detail, number of components (3=rgb), w,h, border, byte order, data format, data
case 3: glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA8, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, PixData); break;
case 4: glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA8, width, height, 0, GL_BGRA,GL_UNSIGNED_BYTE, PixData); break;
}
}
//glTexSubImage2D only modifies pixel data within the texture.
//do this when pixels need to be updated
else{
switch (bpp){
//2d texture, level of detail, xoffset,yoffset, w,h, byte order, data format, data
case 3: glTexSubImage2D(GL_TEXTURE_RECTANGLE_NV, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, PixData); break;
case 4: glTexSubImage2D(GL_TEXTURE_RECTANGLE_NV, 0, 0, 0, width, height, GL_BGRA,GL_UNSIGNED_BYTE, PixData); break;
}
}
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glDisable(GL_TEXTURE_RECTANGLE_NV);
texture_generated = true;
}
OGL_Texture *Texture_Cast(OGL_Surface *Surface)
{
OGL_Texture *Texture = static_cast<OGL_Texture*>(Surface);
Texture->w = Surface->w;
Texture->h = Surface->h;
return Texture;
}
////////
//BLIT//
////////
//glBlendFunc(source_blend, dst_blend)
//GL_ZERO colour is ignored
//GL_ONE full colour is taken into addition
//GL_(DST/SRC)_COLOR colour is multiplied by (DST/SRC) color
//GL_ONE_MINUS_(DST/SRC)_COLOR colour is multiplied by inverted (DST/SRC) color
//GL_(DST/SRC)_ALPHA colour is multiplied by (DST/SRC) alpha value
//GL_ONE_MINUS_(DST/SRC)_ALPHA colour is multiplied by inverted (DST/SRC) alpha value
//overall colour (GL_SRC_ALPHA, GL_ONE) -addative- = (r1*a1,g1*a1,b1*a1)+(r2,g2,b2)
void DrawImage(OGL_Texture *Texture, int dx, int dy, float rot,float scale_x,float scale_y,float alpha,int options){
if (options & IMG_HFLIP){dx+=Texture->w; scale_x=-1;}
if (options & IMG_VFLIP){dy+=Texture->h; scale_y=-1;}
glLoadIdentity();
glTranslatef(dx, dy, 0.0);
if (scale_x!=1 && scale_y!=1){glScaled(scale_x, scale_y, 0);}
if (rot!=0){
int tmpw = Texture->w/2;
int tmph = Texture->h/2;
glTranslatef(tmpw, tmph, 0.0);
glRotatef(rot, 0, 0, 1.0);
glTranslatef(-tmpw, -tmph, 0.0);
}
glEnable(GL_TEXTURE_RECTANGLE_NV);
glEnable(GL_BLEND);
//additive blending - gray + gray = white
if (options &= IMG_ADD_BLEND){glBlendFunc(GL_SRC_ALPHA, GL_ONE);}
//regular alpha blending - gray + gray = gray
else{glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);}
//blit
glColor4f(1.0f, 1.0f, 1.0f, alpha);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, Texture->Texture_ID);
glBegin(GL_QUADS);
glTexCoord2i(0,0);
glVertex2i(0, 0);
glTexCoord2i(Texture->w,0);
glVertex2i(Texture->w, 0);
glTexCoord2i(Texture->w,Texture->h);
glVertex2i(Texture->w, Texture->h);
glTexCoord2i(0,Texture->h);
glVertex2i(0, Texture->h);
glEnd();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_RECTANGLE_NV);
glLoadIdentity();
}
void DrawSubImage(OGL_Texture *Texture, int sx, int sy, int sw, int sh, int dx, int dy, float rot,float scale_x,float scale_y,float alpha,int options){
if (options & IMG_HFLIP){dx+=Texture->w; scale_x=-1;}
if (options & IMG_VFLIP){dy+=Texture->h; scale_y=-1;}
glLoadIdentity();
glTranslatef(dx, dy, 0.0);
if (scale_x!=1 && scale_y!=1){glScaled(scale_x, scale_y, 0);}
if (rot!=0){
int tmpw = Texture->w/2;
int tmph = Texture->h/2;
glTranslatef(tmpw, tmph, 0.0);
glRotatef(rot, 0, 0, 1.0);
glTranslatef(-tmpw, -tmph, 0.0);
}
glEnable(GL_TEXTURE_RECTANGLE_NV);
glEnable(GL_BLEND);
//additive blending - gray + gray = white
if (options &= IMG_ADD_BLEND){glBlendFunc(GL_SRC_ALPHA, GL_ONE);}
//regular alpha blending - gray + gray = gray
else{glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);}
//blit
glColor4f(1.0,1.0,1.0,alpha);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, Texture->Texture_ID);
glBegin(GL_QUADS);
glTexCoord2i(sx,sy);
glVertex2i(0, 0);
glTexCoord2i(sx+sw,sy);
glVertex2i(sw, 0);
glTexCoord2i(sx+sw,sy+sh);
glVertex2i(sw, sh);
glTexCoord2i(sx,sy+sh);
glVertex2i(0, sh);
glEnd();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_RECTANGLE_NV);
glLoadIdentity();
}
//////////
//COLOUR//
//////////
uint32_t make_col(uint8_t r, uint8_t g, uint8_t b, uint8_t a){
uint32_t col = 0;
col |= r;
col |= uint32_t(g)<<8;
col |= uint32_t(b)<<16;
col |= uint32_t(a)<<24;
//printf("%#X, %d,%d,%d\n", col,r,g,b);
return col;
}
uint32_t make_col(uint8_t r, uint8_t g, uint8_t b){
return make_col(r,g,b,255);
}
///////////////////
//DRAW PRIMITIVES//
///////////////////
//pos
void putpixel(OGL_Surface *Surface, int pos, uint32_t col)
{
Surface->PixData[pos+2] = (col&0x000000FF);//r
Surface->PixData[pos+1] = (col&0x0000FF00)>>8;//g
Surface->PixData[pos] = (col&0x00FF0000)>>16;//b
if (Surface->bpp != 4){return;}
Surface->PixData[pos+3] = (col&0xFF000000)>>24;//a
}
void getpixel(OGL_Surface *Surface, int pos, uint32_t &col)
{
col = 0;
col |= uint32_t(Surface->PixData[pos+2]);//r
col |= uint32_t(Surface->PixData[pos+1])<<8;//g
col |= uint32_t(Surface->PixData[pos] )<<16;//b
if (Surface->bpp != 4){return;}
col |= uint32_t(Surface->PixData[pos+3])<<24;//a
}
//x,y
bool putpixel(OGL_Surface *Surface, int x, int y, uint32_t col)
{
if(x < 0 || y < 0 || x >= (signed)Surface->w || y >= (signed)Surface->h) {return 0;}
int pos = (x+(y*Surface->w))*Surface->bpp;
putpixel(Surface, pos, col);
return 1;
}
bool getpixel(OGL_Surface *Surface, int x, int y, uint32_t &col)
{
if(x < 0 || y < 0 || x >= (signed)Surface->w || y >= (signed)Surface->h) {return 0;}
int pos = (x+(y*Surface->w))*Surface->bpp;
getpixel(Surface, pos, col);
return 1;
}
//primitives
void hline(OGL_Surface *Surface, int x, int y, int w, uint32_t col)
{
if (w<0){w=abs(w);x-=w;}
for (int i=0;i<w;i++){putpixel(Surface, x+i, y, col);}
}
void vline(OGL_Surface *Surface, int x, int y, int h, uint32_t col)
{
if (h<0){h=abs(h);y-=h;}
for (int i=0;i<h;i++){putpixel(Surface, x, y+i, col);}
}
void line(OGL_Surface *Surface, int x1, int y1, int x2, int y2, uint32_t col)
{
if (x1==x2 && y1==y2){putpixel(Surface,x1,y1, col);return;}
if (x1==x2){vline(Surface,x1,y1,y2-y1, col);return;}
if (y1==y2){hline(Surface,x1,y1,x2-x1, col);return;}
int x = x1;
int y = y1;
int maxpos = abs(x2 - x1);//width
int numadder= abs(y2 - y1);//height
int num;
int xstep1, ystep1, xstep2, ystep2;//step direction
if(x2 >= x1){xstep1 = 1; xstep2 = 0;} else{xstep1 =-1; xstep2 = 0;}
if(y2 >= y1){ystep1 = 1; ystep2 = 0;} else{ystep1 =-1; ystep2 = 0;}
//check step direction, true = x axis, false = y axis
if (maxpos >= numadder){//if width >= height
ystep2 = ystep1; ystep1 =0;
}
else{
num = maxpos; maxpos = numadder; numadder = num;
xstep2 = xstep1; xstep1 =0;
}
num = maxpos >>1;
for (int pos = 0; pos <= maxpos; pos++){
putpixel(Surface, x, y, col);
num += numadder;
if (num >= maxpos){
num -= maxpos;
x += xstep2;
y += ystep2;
}
x += xstep1;
y += ystep1;
}
}
void box(OGL_Surface *Surface, int x, int y, int w, int h, uint32_t col)
{
hline(Surface,x,y,w, col);
hline(Surface,x,y+h,w, col);
vline(Surface,x,y,h, col);
vline(Surface,x+w,y,h, col);
}
void rect(OGL_Surface *Surface, int x, int y, int w, int h, uint32_t col)
{
if (w<0){w=abs(w);x-=w;}
if (h<0){h=abs(h);y-=h;}
for (int i = 0; i<h; i++){
for (int i2=0; i2<w; i2++){
putpixel(Surface, x+i2, y+i, col);
}
}
}
void polyline(OGL_Surface *Surface, unsigned char n, int points[], uint32_t col)
{
for (int i = 0; i<n+n-3; i+=2){
line(Surface, points[i], points[i+1], points[i+2], points[i+3], col);
}
}
void polygon(OGL_Surface *Surface, unsigned char n, int points[], uint32_t col)
{
polyline(Surface, n, points, col);
line(Surface, points[n+n-1], points[n+n], points[0], points[1], col);
}
void circle(OGL_Surface *Surface, int x0, int y0, int radius, uint32_t col)
{
if(radius == 0 || x0 + radius < 0 || x0 - radius >= (signed)Surface->w || y0 + radius < 0 || y0 - radius >= (signed)Surface->h) {return;}
int x = 0;
int y = radius;
//8 points can be plotted at once due to symmetry
int x1,x2,x3,x4;
int y1,y2,y3,y4;
int pos = 0-radius;
while (x <= y){
x1 = x0 + x; x2 = x0 - x; x3 = x0 + y; x4 = x0 - y;//xstart +x,-x,+y,-y
y1 = y0 + y; y2 = y0 - y; y3 = y0 + x; y4 = y0 - x;
putpixel(Surface, x1, y1, col);
putpixel(Surface, x2, y2, col);
putpixel(Surface, x3, y3, col);
putpixel(Surface, x4, y3, col);
if(x > 0){
putpixel(Surface, x1, y2, col);
putpixel(Surface, x2, y1, col);
putpixel(Surface, x3, y4, col);
putpixel(Surface, x4, y4, col);
}
//increment
if ((pos += x++<<1)>= 0) {
pos-= y--<<1;
}
}
}
void circle_fill(OGL_Surface *Surface, int x0, int y0, int radius, uint32_t col)
{
if(radius == 0 || x0 + radius < 0 || x0 - radius >= (signed)Surface->w || y0 + radius < 0 || y0 - radius >= (signed)Surface->h) {return;}
int x = 0;
int y = radius;
int pos = 0-radius;
while (x <= y){
hline(Surface,x0-x, y0 + y, x+x, col);
hline(Surface,x0-x, y0 - y, x+x, col);
hline(Surface,x0-y, y0 + x, y+y, col);
hline(Surface,x0-y, y0 - x, y+y, col);
//increment
if ((pos += x++<<1)>= 0) {
pos-= y--<<1;
}
}
}