arsd/screen.d

1451 lines
32 KiB
D

// This code is D 1.0
/**
Part of my old D 1.0 game helper code that used SDL. I keep it compiling on new D compilers too, but it is not meant to be used in new projects.
*/
module arsd.screen;
import sdl.SDL;
import sdl.SDL_image;
import sdl.SDL_ttf;
import std.string;
import std.stdio;
import std.format;
import arsd.engine;
version(D_Version2)
static import stdcstring = core.stdc.string;
else
static import stdcstring = std.c.string;
version(none)
char[] fmt(...){
char[] o;
void putc(dchar c)
{
o ~= c;
}
std.format.doFormat(&putc, _arguments, _argptr);
return o;
}
extern(C){
void glGetIntegerv(int, void*);
void glMatrixMode(int);
void glPushMatrix();
void glLoadIdentity();
void glOrtho(double, double, double, double, double, double);
void glPopMatrix();
void glEnable(int);
void glDisable(int);
void glClear(int);
void glBegin(int);
void glVertex2f(float, float);
void glEnd();
void glColor3b(ubyte, ubyte, ubyte);
void glColor3i(int, int, int);
void glColor3f(float, float, float);
void glColor4f(float, float, float, float);
void glTranslatef(float, float, float);
void glRotatef(float, float, float, float);
uint glGetError();
void glDeleteTextures(int, uint*);
char* gluErrorString(uint);
void glRasterPos2i(int, int);
void glDrawPixels(int, int, uint, uint, void*);
void glClearColor(float, float, float, float);
void glGenTextures(uint, uint*);
void glBindTexture(int, int);
void glTexParameteri(uint, uint, int);
void glTexImage2D(int, int, int, int, int, int, int, int, void*);
void glTexCoord2f(float, float);
void glVertex2i(int, int);
void glBlendFunc (int, int);
void glViewport(int, int, int, int);
void glReadBuffer(uint);
void glReadPixels(int, int, int, int, int, int, void*);
const uint GL_FRONT = 0x0404;
const uint GL_BLEND = 0x0be2;
const uint GL_SRC_ALPHA = 0x0302;
const uint GL_ONE_MINUS_SRC_ALPHA = 0x0303;
const uint GL_UNSIGNED_BYTE = 0x1401;
const uint GL_RGB = 0x1907;
const uint GL_BGRA = 0x80e1;
const uint GL_RGBA = 0x1908;
const uint GL_TEXTURE_2D = 0x0DE1;
const uint GL_TEXTURE_MIN_FILTER = 0x2801;
const uint GL_NEAREST = 0x2600;
const uint GL_LINEAR = 0x2601;
const uint GL_TEXTURE_MAG_FILTER = 0x2800;
const uint GL_NO_ERROR = 0;
const int GL_VIEWPORT = 0x0BA2;
const int GL_MODELVIEW = 0x1700;
const int GL_TEXTURE = 0x1702;
const int GL_PROJECTION = 0x1701;
const int GL_DEPTH_TEST = 0x0B71;
const int GL_COLOR_BUFFER_BIT = 0x00004000;
const int GL_ACCUM_BUFFER_BIT = 0x00000200;
const int GL_DEPTH_BUFFER_BIT = 0x00000100;
const int GL_POINTS = 0x0000;
const int GL_LINES = 0x0001;
const int GL_LINE_LOOP = 0x0002;
const int GL_LINE_STRIP = 0x0003;
const int GL_TRIANGLES = 0x0004;
const int GL_TRIANGLE_STRIP = 5;
const int GL_TRIANGLE_FAN = 6;
const int GL_QUADS = 7;
const int GL_QUAD_STRIP = 8;
const int GL_POLYGON = 9;
}
public struct Point{
int x;
int y;
Point opAddAssign(Point p){
x += p.x;
y += p.y;
version(D_Version2)
return this;
else
return *this;
}
Point opAdd(Point p){
Point a;
a.x = x + p.x;
a.y = y + p.y;
return a;
}
Point opSub(Point p){
Point a;
a.x = x - p.x;
a.y = y - p.y;
return a;
}
}
Point XY(int x, int y){
Point p;
p.x = x;
p.y = y;
return p;
}
Point XY(float x, float y){
Point p;
p.x = cast(int)x;
p.y = cast(int)y;
return p;
}
public struct Color{
int r;
int g;
int b;
int a;
uint toInt(){
return r << 24 | g << 16 | b << 8 | a;
}
void fromInt(uint i){
r = i >> 24;
g = (i >> 16) & 0x0ff;
b = (i >> 8) & 0x0ff;
a = i & 0x0ff;
}
}
Color white = {255, 255, 255, 255};
Color black = {0, 0, 0, 255};
Color RGB(int r, int g, int b){
Color c;
c.r = r;
c.g = g;
c.b = b;
c.a = 255;
return c;
}
Color RGBA(int r, int g, int b, int a){
Color c;
c.r = r;
c.g = g;
c.b = b;
c.a = a;
return c;
}
Color XRGB(Color c, int alpha = -1){
Color a;
a.r = c.r ^ 255;
a.g = c.g ^ 255;
a.b = c.b ^ 255;
if(alpha == -1)
a.a = c.a;// ^ 255;
else
a.a = alpha;
return a;
}
class FontEngine{
public:
static FontEngine instance;
~this(){
foreach(a; fonts)
if(a != null)
TTF_CloseFont(a);
TTF_Quit();
}
void loadFont(in char[] font, int size = 12, int index = 0){
if(fonts[index] != null)
freeFont(index);
TTF_Font* temp;
temp = TTF_OpenFont(std.string.toStringz(font), size);
if(temp == null)
throw new Exception("load font");
fonts[index] = temp;
}
void freeFont(int index = 0){
if(fonts[index] != null){
TTF_CloseFont(fonts[index]);
fonts[index] = null;
}
}
Image renderText(in char[] text, Color foreground = RGB(255,255,255), int font = 0){
Image* a = immutableString(text) in cache[font];
if(a !is null)
return *a;
SDL_Color f;
f.r = cast(ubyte) foreground.r;
f.g = cast(ubyte) foreground.g;
f.b = cast(ubyte) foreground.b;
f.unused = cast(ubyte) foreground.a;
SDL_Surface* s = TTF_RenderText_Blended(fonts[font], std.string.toStringz(text), f);
Image i = new Image(s);
cache[font][text]/*[font]*/ = i;
return i;
}
int textHeight(in char[] text=" ",int font = 0){
int w, h;
TTF_SizeText(FontEngine.instance.fonts[font], std.string.toStringz(text), &w, &h);
return h;
}
void textSize(in char[] text, out int w, out int h, int font = 0){
TTF_SizeText(fonts[font], std.string.toStringz(text), &w, &h);
}
private:
static this() {
instance = new FontEngine;
}
this(){
if(TTF_Init() == -1)
throw new Exception("TTF_Init");
}
TTF_Font*[8] fonts;
Image[char[]][8] cache;
}
interface Drawable{
public:
void flip();
int width();
int height();
int bpp();
/*
uint toGL();
float texWidth();
float texHeight();
*/
protected:
SDL_Surface* surface();
}
int total = 0;
class Image : Drawable{
public:
this(SDL_Surface* s){
if(s == null)
throw new Exception("Image");
sur = s;
}
/// Loads an image with the filename checking to see if it has already been loaded into the cache
/// loads it as read-only
// static Image load(char[] filename){
// }
this(char[] filename){
sur = IMG_Load(std.string.toStringz(filename));
if(sur == null)
throw new Exception(immutableString("Load " ~ filename));
name = filename;
}
void replace(char[] filename){
if(t){
glDeleteTextures(1, &tex);
total--;
writef("[%s]OpenGL texture destroyed %d. %d remain\n", name, tex, total);
t = 0;
}
if(sur){
SDL_FreeSurface(sur);
sur = null;
}
sur = IMG_Load(std.string.toStringz(filename));
if(sur == null)
throw new Exception(immutableString("Load " ~ filename));
name = filename;
}
// loads a slice of an image
this(char[] filename, int x, int y, int wid, int hei){
/*
Image i = new Image(filename);
this(wid, hei);
scope Painter p = new Painter(this);
for(int a = 0; a < wid; a++)
for(int b = 0; b < hei; b++)
p.putpixel(XY(a, b), i.getPixel(XY(a + x, b + y)));
*/
SDL_Surface* s1;
s1 = IMG_Load(std.string.toStringz(filename));
if(s1 == null)
throw new Exception(immutableString("Loading " ~ filename));
scope(exit)
SDL_FreeSurface(s1);
sur = SDL_CreateRGBSurface(SDL_SWSURFACE, wid, hei, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000);
if(sur == null)
throw new Exception(immutableString("Create"));
for(int b = 0; b < hei; b++){
for(int a = 0; a < wid; a++){
if(b+y >= s1.h || a+x >= s1.w){
break;
// throw new Exception("eat my cum");
}
ubyte* wtf;
if(s1.format.BitsPerPixel == 32){
wtf = cast(ubyte*)(cast(ubyte*)s1.pixels + (b+y)*s1.pitch + (a+x) * 4);
}
else
if(s1.format.BitsPerPixel == 24)
wtf = cast(ubyte*)(cast(ubyte*)s1.pixels + (b+y)*s1.pitch + (a+x) * 3);
else
throw new Exception("fuck me in the ass");
ubyte* good = cast(ubyte*)(cast(ubyte*)sur.pixels + b*sur.pitch + a * 4);
good[0] = wtf[2];
good[1] = wtf[1];
good[2] = wtf[0];
good[3] = wtf[3];
}
}
/*
SDL_Rect r;
r.x = x;
r.y = y;
r.w = wid;
r.h = hei;
SDL_Rect r2;
r2.x = 0;
r2.y = 0;
r2.w = wid;
r2.h = hei;
if(SDL_BlitSurface(s1, &r, sur, &r2))
throw new Exception("Blit");
*/
}
this(int wid, int hei){
sur = SDL_CreateRGBSurface(SDL_SWSURFACE, wid, hei, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000);
if(sur == null)
throw new Exception("Create");
t = false;
}
~this(){
if(t){
glDeleteTextures(1, &tex);
total--;
writef("[%s]OpenGL texture destroyed %d. %d remain\n", name, tex, total);
}
if(sur)
SDL_FreeSurface(sur);
}
void flip(){
}
int width(){
return surface.w;
}
int height(){
return surface.h;
}
int bpp(){
return sur.format.BitsPerPixel;
}
Color getPixel(Point p){
ubyte* bufp;
Color a;
if(bpp == 32){
bufp = cast(ubyte*)(cast(ubyte*)surface.pixels + p.y*surface.pitch + p.x * 4);
a.a = bufp[3];
a.r = bufp[2];
a.g = bufp[1];
a.b = bufp[0];
}else{
bufp = cast(ubyte*)(cast(ubyte*)surface.pixels + p.y*surface.pitch + p.x * 3);
a.a = 255;
a.r = bufp[2];
a.g = bufp[1];
a.b = bufp[0];
}
return a;
}
uint toGL(){
if(t)
return tex;
else{
float[4] f;
tex = SDL_GL_LoadTexture(surface, f.ptr);
t = true;
total++;
texWidth = f[2];
texHeight = f[3];
// total++;
// writef("OpenGL texture created %d. %d exist\n", tex, total);
return tex;
}
}
protected:
SDL_Surface* surface(){
return sur;
}
private:
SDL_Surface* sur;
uint tex;
bool t;
float texWidth;
float texHeight;
char[] name;
}
bool useGL;
class Screen : Drawable{
public:
this(int xres = 1024, int yres = 768, int bpp = 24, bool oGL = false, bool fullScreen = false){//true){
// oGL = false;
oGL = true;
if(!oGL)
screen = SDL_SetVideoMode(xres, yres, bpp/*32*/, SDL_SWSURFACE);
else{
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
if(fullScreen){
screen = SDL_SetVideoMode(xres, yres, 24, SDL_OPENGL| SDL_FULLSCREEN);
}
else
screen = SDL_SetVideoMode(xres, yres, 0, SDL_OPENGL);
//screen = SDL_SetVideoMode(xres, yres, bpp, SDL_OPENGL);
if(screen is null)
throw new Exception("screen");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glOrtho(0, 1000, yres, 0, 0, 1);
glOrtho(0, xres, yres, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0,0,0,0);
// glViewport(0,0,1024,1024);
}
useGL = oGL;
if(screen == null)
throw new Exception("screen");
// SDL_SetAlpha(screen, SDL_SRCALPHA | SDL_RLEACCEL, 128);
xr = xres;
yr = yres;
}
void switchSplitScreenMode(int player, int numberOfPlayers, bool horizontal){
switch(numberOfPlayers){
default: assert(0);
case 1:
return;
// glViewport(0, 0, xr, yr);
break;
case 2:
switch(player){
default: assert(0);
case 0:
if(horizontal)
glViewport(0, yr / 2, xr, yr / 2);
else
glViewport(0, 0, xr / 2, yr);
break;
case 1:
if(horizontal)
glViewport(0, 0, xr, yr / 2);
else
glViewport(xr / 2, 0, xr / 2, yr);
break;
}
break;
case 3:
case 4:
switch(player){
default: assert(0);
case 0:
glViewport(0, yr / 2, xr / 2, yr / 2);
break;
case 1:
glViewport(xr / 2, yr / 2, xr / 2, yr / 2);
break;
case 2:
glViewport(0, 0, xr / 2, yr / 2);
break;
case 3:
glViewport(xr / 2, 0, xr / 2, yr / 2);
break;
}
break;
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, xr, yr, 0, 0, 1);
}
~this(){
delete FontEngine.instance;
}
Image screenshot(){
if(!useGL)
throw new Exception("Not yet implemented");
Image image = new Image(xr, yr);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, xr, yr, GL_BGRA, GL_UNSIGNED_BYTE, image.sur.pixels);
Image temp = new Image(xr, yr);
// FIXME
version(Windows)
return image;
// FIXME: this crashes on Windows
for(int i = 0; i < yr; i++)
stdcstring.memcpy(temp.sur.pixels + 4 * xr * i, image.sur.pixels + 4 * xr * (yr-1 - i), 4 * xr);
// memcpy(image.sur.pixels, tem.psur.pixels, xres * yres * 4);
return temp;
}
void flip(){
if(useGL)
SDL_GL_SwapBuffers();
else
SDL_Flip(screen);
}
int width(){
return xr;
}
int height(){
return yr;
}
int bpp(){
return 124;
}
/*
uint toGL(){
throw new Error;
}
float texWidth(){
return 1.0;
}
float texHeight(){
return 1.0;
}
*/
protected:
SDL_Surface* surface(){
return screen;
}
private:
SDL_Surface* screen;
int xr;
int yr;
}
scope class Painter{
public:
bool special;
bool manualFlipped;
Point translate;
this(Painter p, Point t){
s = p.s;
special = true;
translate = t;
}
this(Drawable d){
/+
in {
assert(!(s is null));
}
+/
s = d;
if(s is null)
throw new Exception("christ what were you thinking");
if ( !(useGL
&& s.bpp() == 124)
&& SDL_MUSTLOCK(s.surface()) ) {
if ( SDL_LockSurface(s.surface()) < 0 ) {
throw new Exception("locking");
}
locked = true;
}
}
~this(){
if(!manualFlipped){
if(glbegin)
endDrawingShapes();
if(!special){
if (locked){
SDL_UnlockSurface(s.surface);
}
s.flip();
}
}
}
void manualFlip(){
if(glbegin)
endDrawingShapes();
if(!special){
if (locked){
SDL_UnlockSurface(s.surface);
}
s.flip();
}
manualFlipped = true;
}
void setGLColor(Color color){
if(useGL && s.bpp == 124){
glColor4f(cast(float)color.r/255.0, cast(float)color.g/255.0, cast(float)color.b/255.0, cast(float)color.a / 255.0);
return;
}
}
void putpixel(Point where, Color color){
if(special) where += translate;
if(glbegin)
throw new Exception("Must end shape before doing anything else");
// if(color.a == 255)
// return;
int x = where.x;
int y = where.y;
if(x < 0 || x >= s.width || y < 0 || y >= s.height)
return;
if(useGL && s.bpp == 124){
// y = 480 - y;
glBegin(GL_POINTS);
setGLColor(color);
glVertex2f(cast(float)x, cast(float)y);
glEnd();
return;
}
ubyte *bufp;
if(s.bpp == 32){
bufp = cast(ubyte*)(cast(ubyte*)s.surface.pixels + y*s.surface.pitch + x * 4);
bufp[3] = cast(ubyte) color.a;
bufp[2] = cast(ubyte) color.r;
bufp[1] = cast(ubyte) color.g;
bufp[0] = cast(ubyte) color.b;
}else{
bufp = cast(ubyte*)(cast(ubyte*)s.surface.pixels + y*s.surface.pitch + x * 3);
if(color.a == 255){
bufp[2] = cast(ubyte) color.r;
bufp[1] = cast(ubyte) color.g;
bufp[0] = cast(ubyte) color.b;
}
else{
bufp[2] = cast(ubyte)(bufp[2] * (255-color.a) + (color.r * (color.a)) / 255);
bufp[1] = cast(ubyte)(bufp[1] * (255-color.a) + (color.g * (color.a)) / 255);
bufp[0] = cast(ubyte)(bufp[0] * (255-color.a) + (color.b * (color.a)) / 255);
}
}
}
void beginDrawingLines(){
if(glbegin)
throw new Exception("Can only draw one kind at a time");
glbegin = true;
if(useGL && s.bpp == 124)
glBegin(GL_LINES);
}
void beginDrawingConnectedLines(){
if(glbegin)
throw new Exception("Can only draw one kind at a time");
glbegin = true;
if(useGL && s.bpp == 124)
glBegin(GL_LINE_STRIP);
}
void beginDrawingPolygon(){
if(glbegin)
throw new Exception("Can only draw one kind at a time");
glbegin = true;
if(useGL && s.bpp == 124)
glBegin(GL_POLYGON);
}
void beginDrawingTriangles(){
if(glbegin)
throw new Exception("Can only draw one kind at a time");
glbegin = true;
if(useGL && s.bpp == 124)
glBegin(GL_TRIANGLES);
}
void beginDrawingBoxes(){
if(glbegin)
throw new Exception("Can only draw one kind at a time");
glbegin = true;
if(useGL && s.bpp == 124)
glBegin(GL_QUADS);
}
void beginDrawingPoints(){
if(glbegin)
throw new Exception("Can only draw one kind at a time");
glbegin = true;
if(useGL && s.bpp == 124)
glBegin(GL_POINTS);
}
void endDrawingShapes(){
if(!glbegin)
return;
glbegin = false;
if(useGL && s.bpp == 124)
glEnd();
}
void vertex(Point p){
if(special) p += translate;
if(!glbegin)
throw new Exception("Can't use vertex without beginning first");
if(useGL && s.bpp == 124)
glVertex2i(p.x, p.y);
}
bool glbegin;
void drawImageRotated(Point where, Image i, float a, Color color = RGB(255,255,255)){
if(i is null)
return;
glPushMatrix();
// glRotatef(a, cast(float)(where.x + 32) / s.width, cast(float)(where.y + 32) / s.height, 1);
glTranslatef(where.x, where.y, 0);
glRotatef(a, 0,0, 1);
drawImage(XY(-i.width/2,-i.height/2), i, i.width, i.height, color);
glPopMatrix();
}
void drawImage(Point where, Image i, Color c){
drawImage(where, i, 0, 0, c);
}
void drawImage(Point where, Image i, int W = 0, int H = 0, Color c = RGBA(255,255,255,255)){
if(i is null)
return;
if(special) where += translate;
if(glbegin)
throw new Exception("Must end shape before doing anything else");
if(useGL && s.bpp == 124){
int x = where.x;
int y = where.y;
int w = W == 0 ? i.width : W;
int h = H == 0 ? i.height : H;
// glColor4f(.5,.5,.5,1);
setGLColor(c);
glBindTexture(GL_TEXTURE_2D, i.toGL);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2i(x, y);
glTexCoord2f(i.texWidth, 0); glVertex2i(x+w, y);
glTexCoord2f(i.texWidth, i.texHeight); glVertex2i(x+w, y+h);
glTexCoord2f(0, i.texHeight); glVertex2i(x, y+h);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0); // unbind the texture... I guess
// I don't actually understand why that is needed
// but without it, everything drawn after it is wrong (too light or dark)
return;
}
if((W == 0 && H == 0) || (i.width == W && i.height == H)){
SDL_Rect r;
r.x = cast(short)( where.x);
r.y = cast(short)( where.y);
r.w = cast(short)( i.width);
r.h = cast(short)( i.height);
if(locked)
SDL_UnlockSurface(s.surface);
if(SDL_BlitSurface(i.surface, null, s.surface, &r) == -1)
throw new Exception("blit");
if ( SDL_MUSTLOCK(s.surface) ) {
if ( SDL_LockSurface(s.surface) < 0 ) {
throw new Exception("lock");
}
locked = true;
}
} else { // quick and dirty scaling needed
float dx = cast(float)i.width / cast(float)W;
float dy = cast(float)i.height / cast(float)H;
int X = where.x, Y = where.y;
for(float y = 0; y < i.height; y += dy){
for(float x = 0; x < i.width; x += dx){
putpixel(XY(X, Y), i.getPixel(XY(cast(int) x, cast(int) y)));
X++;
}
X = where.x;
Y++;
}
}
}
void drawText(Point where, in char[] text, Color foreground = RGB(255,255,255), int font = 0){
if(glbegin)
throw new Exception("Must end shape before doing anything else");
if(useGL && s.bpp == 124){
Image i = FontEngine.instance.renderText(text, RGB(255,255,255), font);
drawImage(where, i, foreground);
}else{
Image i = FontEngine.instance.renderText(text, foreground, font);
drawImage(where, i);
}
}
version(D_Version2) {
import std.format;
void drawTextf(T...)(Point where, T args) {
char[] t;
t.length = 80;
int a = 0;
void putc(dchar c){
if(a == t.length)
t.length = t.length + 80;
t[a] = cast(char) c;
a++;
}
formattedWrite(&putc, args);
t.length = a;
drawText(where, t);
}
} else
void drawTextf(Point where, ...){
char[] t;
t.length = 80;
int a = 0;
void putc(dchar c){
if(a == t.length)
t.length = t.length + 80;
t[a] = cast(char) c;
a++;
}
std.format.doFormat(&putc, _arguments, _argptr);
t.length = a;
drawText(where, t);
}
int wordLength(in char[] w, int font = 0){
int a,b;
FontEngine.instance.textSize(w, a, b, font);
return a;
}
int drawTextBoxed(Point where, char[] text, int width, int height, Color foreground = RGB(255,255,255), int font = 0){
if(glbegin)
throw new Exception("Must end shape before doing anything else");
int xc;
int yc = TTF_FontLineSkip(FontEngine.instance.fonts[font]);
int w = 0;
int h = 0;
int l;
char[] getWord(){
int a = l;
while(a < text.length && text[a] != ' ' && text[a] != '\n' && text[a] != '\t')
a++;
return text[l..a];
}
int wordLength(in char[] w){
int a,b;
FontEngine.instance.textSize(w, a, b, font);
return a;
}
Point ww = where;
while(l < text.length){
if(text[l] == '\n'){
l++;
goto newline;
}
if(wordLength(getWord()) + w > width){
goto newline;
}
if(!(w == 0 && text[l] == ' ')){
TTF_GlyphMetrics(FontEngine.instance.fonts[font], text[l], null,null,null,null,&xc);
drawText(ww, text[l..(l+1)], foreground, font);
w+=xc;
ww.x += xc;
}
l++;
if(w > (width - xc)){
newline:
w = 0;
h += yc;
ww.x = cast(short)(where.x);
ww.y += cast(short)(yc);
if(h > (height - yc))
break;
}
}
return l;
}
void drawTextCenteredHoriz(int top, char[] text, Color foreground, int font = 0){
Point where;
where.y = top;
int w, h;
TTF_SizeText(FontEngine.instance.fonts[font], std.string.toStringz(text), &w, &h);
where.x = (s.width - w) / 2;
drawText(where, text, foreground, font);
}
void line(Point start, Point end, Color color){
if(special){ start += translate; end += translate; }
if(glbegin)
throw new Exception("Must end shape before doing anything else");
if(useGL && s.bpp == 124){
setGLColor(color);
glBegin(GL_LINES);
glVertex2i(start.x, start.y);
glVertex2i(end.x, end.y);
glEnd();
}
}
void hline(Point start, int width, Color color){
if(useGL && s.bpp == 124){
line(start, XY(start.x + width, start.y), color);
return;
}
Point point = start;
for(int a = 0; a < width; a++){
putpixel(point, color);
point.x++;
}
}
void vline(Point start, int height, Color color){
if(useGL && s.bpp == 124){
line(start, XY(start.x, start.y + height), color);
return;
}
Point point = start;
for(int a = 0; a < height; a++){
putpixel(point, color);
point.y++;
}
}
void circle(Point center, int radius, Color color){
if(special) center += translate;
if(glbegin)
throw new Exception("Must end shape before doing anything else");
}
void arc(Point center, int radius, float start, float end, Color color){
if(special) center += translate;
if(glbegin)
throw new Exception("Must end shape before doing anything else");
// for(float a = start; a <= end; a+= (3.14159265358 / 50.0))
// putpixel((int)(cos(a) * (float)radius + center.x()),(int)( sin(a) * (float) radius + center.y()), color);
}
void box(Point upperLeft, Point lowerRight, Color color){
if(special) { upperLeft += translate; lowerRight += translate; }
if(glbegin)
throw new Exception("Must end shape before doing anything else");
if(useGL && s.bpp == 124){
int x1 = upperLeft.x;
int y1 = upperLeft.y;
int x2 = lowerRight.x;
int y2 = lowerRight.y;
glBegin(GL_QUADS);
//glColor3b(color.r, color.g, color.b);
setGLColor(color);
//glColor4f(1,1,1,1);
glVertex2i(x1, y1);
glVertex2i(x2, y1);
glVertex2i(x2, y2);
glVertex2i(x1, y2);
glEnd();
return;
}
SDL_Rect r;
r.x = cast(short) upperLeft.x;
r.y = cast(short) upperLeft.y;
r.w = cast(short) (lowerRight.x - upperLeft.x);
r.h = cast(short) (lowerRight.y - upperLeft.y);
if(s.bpp == 32)
SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b);
else
SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b);
}
void rect(Point upperLeft, Point lowerRight, Color color){
if(special) { upperLeft += translate; lowerRight += translate; }
if(glbegin)
throw new Exception("Must end shape before doing anything else");
if(useGL && s.bpp == 124){
int x1 = upperLeft.x;
int y1 = upperLeft.y;
int x2 = lowerRight.x;
int y2 = lowerRight.y;
glBegin(GL_LINE_LOOP);
//glColor3b(color.r, color.g, color.b);
setGLColor(color);
//glColor4f(1,1,1,1);
glVertex2i(x1, y1);
glVertex2i(x2+1, y1);
glVertex2i(x2, y2);
glVertex2i(x1, y2);
glEnd();
return;
}
/*
SDL_Rect r;
r.x = upperLeft.x;
r.y = upperLeft.y;
r.w = lowerRight.x - upperLeft.x;
r.h = lowerRight.y - upperLeft.y;
if(s.bpp == 32)
SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b);
else
SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b);
*/
}
void gbox(Point upperLeft, Point lowerRight, Color color1, Color color2, Color color3, Color color4){
if(special) { upperLeft += translate; lowerRight += translate; }
if(glbegin)
throw new Exception("Must end shape before doing anything else");
Color color = color1;
if(useGL && s.bpp == 124){
int x1 = upperLeft.x;
int y1 = upperLeft.y;
int x2 = lowerRight.x;
int y2 = lowerRight.y;
glBegin(GL_QUADS);
setGLColor(color1);
glVertex2i(x1, y1);
setGLColor(color2);
glVertex2i(x2, y1);
setGLColor(color4);
glVertex2i(x2, y2);
setGLColor(color3);
glVertex2i(x1, y2);
glEnd();
return;
}
SDL_Rect r;
r.x = cast(short) upperLeft.x;
r.y = cast(short) upperLeft.y;
r.w = cast(short) (lowerRight.x - upperLeft.x);
r.h = cast(short) (lowerRight.y - upperLeft.y);
if(s.bpp == 32)
SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b);
else
SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b);
}
void clear(){
if(useGL && s.bpp == 124){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
return;
}
box(XY(0,0), XY(s.width, s.height), RGB(0,0,0));
}
void fill(Color color){
box(XY(0,0), XY(s.width, s.height), color);
}
void blend(Color color){
if(useGL && s.bpp == 124){
box(XY(0,0), XY(s.width, s.height), color);
return;
}
ubyte *bufp;
bufp = cast(ubyte*)s.surface.pixels;
for(int y = 0; y < s.height; y++)
for(int x = 0; x < s.width; x++){
bufp[2] = cast(ubyte)((bufp[2] * (255-color.a) + color.r * color.a) / 255);
bufp[1] = cast(ubyte)((bufp[1] * (255-color.a) + color.g * color.a) / 255);
bufp[0] = cast(ubyte)((bufp[0] * (255-color.a) + color.b * color.a) / 255);
bufp += (s.bpp == 24 ? 3 : 4);
}
}
private:
Drawable s;
bool locked;
}
int SDL_BlitSurface
(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
return SDL_UpperBlit(src, srcrect, dst, dstrect);
}
bit SDL_MUSTLOCK(SDL_Surface *surface)
{
return surface.offset || ((surface.flags &
(SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_RLEACCEL)) != 0);
}
/* Quick utility function for texture creation */
int power_of_two(int input)
{
int value = 1;
while ( value < input ) {
value <<= 1;
}
return value;
}
uint SDL_GL_LoadTexture(SDL_Surface *surface, float *texcoord)
{
uint texture;
int w, h;
SDL_Surface *image;
SDL_Rect area;
uint saved_flags;
ubyte saved_alpha;
/* Use the surface width and height expanded to powers of 2 */
w = power_of_two(surface.w);
h = power_of_two(surface.h);
texcoord[0] = 0.0f; /* Min X */
texcoord[1] = 0.0f; /* Min Y */
texcoord[2] = cast(float)surface.w / cast(float)w; /* Max X */
texcoord[3] = cast(float)surface.h / cast(float)h; /* Max Y */
image = SDL_CreateRGBSurface(
SDL_SWSURFACE,
w, h,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
if ( image == null) {
throw new Exception("make image");
}
/* Save the alpha blending attributes */
saved_flags = surface.flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
saved_alpha = surface.format.alpha;
if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
SDL_SetAlpha(surface, 0, 0);
}
/* Copy the surface into the GL texture image */
area.x = 0;
area.y = 0;
area.w = cast(ushort) surface.w;
area.h = cast(ushort) surface.h;
SDL_BlitSurface(surface, &area, image, &area);
/* Restore the alpha blending attributes */
if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
SDL_SetAlpha(surface, saved_flags, saved_alpha);
}
/* Create an OpenGL texture for the image */
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
w, h,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
image.pixels);
SDL_FreeSurface(image); /* No longer needed */
return texture;
}
Color c1;
Color c2;
Color c3;
Color c4;
static this(){
c1 = RGBA(0,0,255,160);
c2 = RGBA(0,0,255,160);
c3 = RGBA(0,0,255,160);
c4 = RGBA(0,0,0,160);
}
void drawHighlightBox(Painter p, Point where, int width, int height = 16){
p.gbox(where, where + XY(width, height), XRGB(c1, 128), XRGB(c2, 128), XRGB(c3, 128), XRGB(c4, 128));
}
// Real size is width + 8, height + 8. Size given if of the client area
/*
Point drawShadedRect(Painter p, Point where, int width, int height){
int x = where.x;
int y = where.y;
Color gray = RGB(128,128,128);
p.box(XY(x + 2, y), XY( x + 2 + width + 2, y + 4), gray);
p.box(XY(x + 2, y + height + 4), XY( x + 2 + width + 2, y + 4 + height + 4 ), gray);
p.box(XY(x, y + 2), XY(x + 4, y + 2 + height + 2), gray);
p.box(XY(x + 4 + width, y + 2), XY(x + 4 + width + 4, y + 2 + height + 2), gray);
// p.putpixel(XY(x + 1, y + 1), gray);
// p.putpixel(XY(x + 1, y + 4 + 3 + height), gray);
// p.putpixel(XY(x + 4 + width + 3, y + 1), gray);
// p.putpixel(XY(x + 4 + width + 3, y + 4 + 3 + height), gray);
p.hline(XY(x + 4, y + 1), width + 2, white);
p.hline(XY(x + 4 - 2, y + 4 + height + 1), width + 2 + 1, white);
p.vline(XY(x + 1 - 1, y + 3), height + 2, white);
p.vline(XY(x + 4 + width + 3, y + 3), height + 2, white);
p.gbox(XY(x + 4, y + 4), XY(x + width + 4, y + height + 4), c1, c2, c3, c4);
return XY(x + 4, y + 4);
}
*/
const int BORDER_WIDTH = 4;
Point drawShadedRect(Painter p, Point where, int width, int height){
Color gray = RGB(128,128,128);
Point w;
// top section
w = where + XY( BORDER_WIDTH, 0);
p.box( w + XY(0, 0 * BORDER_WIDTH / 4),
w + XY(width, 1 * BORDER_WIDTH / 4),
gray);
p.box( w + XY(-BORDER_WIDTH / 2, 1 * BORDER_WIDTH / 4),
w + XY(BORDER_WIDTH / 2 + width, 3 * BORDER_WIDTH / 4),
white);
p.box( w + XY( -1 * BORDER_WIDTH / 4, 3 * BORDER_WIDTH / 4),
w + XY( 1 * BORDER_WIDTH / 4 + width , 4 * BORDER_WIDTH / 4),
black);
// bottom section
w = where + XY(BORDER_WIDTH, height + BORDER_WIDTH);
p.box( w + XY(-1 * BORDER_WIDTH / 4, 0 * BORDER_WIDTH / 4),
w + XY(1 * BORDER_WIDTH / 4 + width, 1 * BORDER_WIDTH / 4),
black);
p.box( w + XY(-BORDER_WIDTH / 2, 1 * BORDER_WIDTH / 4),
w + XY(BORDER_WIDTH / 2 + width, 3 * BORDER_WIDTH / 4),
white);
p.box( w + XY(-1 *BORDER_WIDTH / 4, 3 * BORDER_WIDTH / 4),
w + XY( 1 *BORDER_WIDTH / 4 + width, 4 * BORDER_WIDTH / 4),
gray);
// left section
w = where + XY( 0, BORDER_WIDTH);
p.box( w + XY(0 * BORDER_WIDTH / 4, -1),
w + XY(1 * BORDER_WIDTH / 4, height + 1),
gray);
p.box( w + XY(1 * BORDER_WIDTH / 4, -BORDER_WIDTH / 2),
w + XY(3 * BORDER_WIDTH / 4, BORDER_WIDTH / 2 + height),
white);
p.box( w + XY(3 * BORDER_WIDTH / 4, 0),
w + XY(4 * BORDER_WIDTH / 4, height),
black);
// right section
w = where + XY( BORDER_WIDTH + width, BORDER_WIDTH);
p.box( w + XY(0 * BORDER_WIDTH / 4, 0),
w + XY(1 * BORDER_WIDTH / 4, height),
black);
p.box( w + XY(1 * BORDER_WIDTH / 4, -BORDER_WIDTH / 2),
w + XY(3 * BORDER_WIDTH / 4, BORDER_WIDTH / 2 + height),
white);
p.box( w + XY(3 * BORDER_WIDTH / 4, -1),
w + XY(4 * BORDER_WIDTH / 4, 1 + height),
gray);
w = where + XY(BORDER_WIDTH, BORDER_WIDTH);
p.gbox(w, w + XY(width, height), c1, c2, c3, c4);
return w;
}