#include #include #include #include #include "Config.h" #include "Misc.h" #include "Graphics.h" #define INCLUDE_FONTDATA #include "font.h" #ifdef HIGH_QUALITY_RESAMPLE #include "resampler/resampler.h" #endif VideoBuffer::VideoBuffer(int width, int height): Width(width), Height(height) { Buffer = new pixel[width*height]; std::fill(Buffer, Buffer+(width*height), 0); }; VideoBuffer::VideoBuffer(const VideoBuffer & old): Width(old.Width), Height(old.Height) { Buffer = new pixel[old.Width*old.Height]; std::copy(old.Buffer, old.Buffer+(old.Width*old.Height), Buffer); }; VideoBuffer::VideoBuffer(VideoBuffer * old): Width(old->Width), Height(old->Height) { Buffer = new pixel[old->Width*old->Height]; std::copy(old->Buffer, old->Buffer+(old->Width*old->Height), Buffer); }; VideoBuffer::VideoBuffer(pixel * buffer, int width, int height): Width(width), Height(height) { Buffer = new pixel[width*height]; std::copy(buffer, buffer+(width*height), Buffer); } void VideoBuffer::Resize(float factor, bool resample) { int newWidth = ((float)Width)*factor; int newHeight = ((float)Height)*factor; Resize(newWidth, newHeight, resample); } void VideoBuffer::Resize(int width, int height, bool resample, bool fixedRatio) { int newWidth = width; int newHeight = height; pixel * newBuffer; if(newHeight == -1 && newWidth == -1) return; if(newHeight == -1 || newWidth == -1) { if(newHeight == -1) newHeight = ((float)Height)*((float)newWidth/(float)Width); if(newWidth == -1) newWidth = ((float)Width)*((float)newHeight/(float)Height); } else if(fixedRatio) { //Force proportions if(newWidth*Height > newHeight*Width) // same as nW/W > nH/H newWidth = (int)(Width * (newHeight/(float)Height)); else newHeight = (int)(Height * (newWidth/(float)Width)); } if(resample) newBuffer = Graphics::resample_img(Buffer, Width, Height, newWidth, newHeight); else newBuffer = Graphics::resample_img_nn(Buffer, Width, Height, newWidth, newHeight); if(newBuffer) { delete[] Buffer; Buffer = newBuffer; Width = newWidth; Height = newHeight; } } int VideoBuffer::SetCharacter(int x, int y, int c, int r, int g, int b, int a) { int i, j, w, bn = 0, ba = 0; char *rp = font_data + font_ptrs[c]; w = *(rp++); for (j=0; j>= 2; bn -= 2; } return x + w; } int VideoBuffer::BlendCharacter(int x, int y, int c, int r, int g, int b, int a) { int i, j, w, bn = 0, ba = 0; char *rp = font_data + font_ptrs[c]; w = *(rp++); for (j=0; j>= 2; bn -= 2; } return x + w; } int VideoBuffer::AddCharacter(int x, int y, int c, int r, int g, int b, int a) { int i, j, w, bn = 0, ba = 0; char *rp = font_data + font_ptrs[c]; w = *(rp++); for (j=0; j>= 2; bn -= 2; } return x + w; } VideoBuffer::~VideoBuffer() { delete[] Buffer; } /** * Common graphics functions, mostly static methods that provide * encoding/decoding of different formats and font metrics */ char * Graphics::GenerateGradient(pixel * colours, float * points, int pointcount, int size) { int cp, i, j; pixel ptemp; char * newdata = (char*)malloc(size * 3); float poss, pose, temp; memset(newdata, 0, size*3); //Sort the Colours and Points for (i = (pointcount - 1); i > 0; i--) { for (j = 1; j <= i; j++) { if (points[j-1] > points[j]) { temp = points[j-1]; points[j-1] = points[j]; points[j] = temp; ptemp = colours[j-1]; colours[j-1] = colours[j]; colours[j] = ptemp; } } } i = 0; j = 1; poss = points[i]; pose = points[j]; for (cp = 0; cp < size; cp++) { float cpos = (float)cp / (float)size, ccpos, cccpos; if(cpos > pose && j+1 < pointcount) { poss = points[++i]; pose = points[++j]; } ccpos = cpos - poss; cccpos = ccpos / (pose - poss); if(cccpos > 1.0f) cccpos = 1.0f; newdata[(cp*3)] = PIXR(colours[i])*(1.0f-cccpos) + PIXR(colours[j])*(cccpos); newdata[(cp*3)+1] = PIXG(colours[i])*(1.0f-cccpos) + PIXG(colours[j])*(cccpos); newdata[(cp*3)+2] = PIXB(colours[i])*(1.0f-cccpos) + PIXB(colours[j])*(cccpos); } return newdata; } void *Graphics::ptif_pack(pixel *src, int w, int h, int *result_size){ int i = 0, datalen = (w*h)*3, cx = 0, cy = 0; unsigned char *red_chan = (unsigned char*)calloc(1, w*h); unsigned char *green_chan = (unsigned char*)calloc(1, w*h); unsigned char *blue_chan = (unsigned char*)calloc(1, w*h); unsigned char *data = (unsigned char*)malloc(((w*h)*3)+8); unsigned char *result = (unsigned char*)malloc(((w*h)*3)+8); for(cx = 0; cx>8; result[6] = h; result[7] = h>>8; i -= 8; if(BZ2_bzBuffToBuffCompress((char *)(result+8), (unsigned *)&i, (char *)data, datalen, 9, 0, 0) != 0){ free(data); free(result); return NULL; } *result_size = i+8; free(data); return result; } pixel *Graphics::ptif_unpack(void *datain, int size, int *w, int *h){ int width, height, i, cx, cy, resCode; unsigned char *red_chan; unsigned char *green_chan; unsigned char *blue_chan; unsigned char *data = (unsigned char*)datain; unsigned char *undata; pixel *result; if(size<16){ printf("Image empty\n"); return NULL; } if(!(data[0]=='P' && data[1]=='T' && data[2]=='i')){ printf("Image header invalid\n"); return NULL; } width = data[4]|(data[5]<<8); height = data[6]|(data[7]<<8); i = (width*height)*3; undata = (unsigned char*)calloc(1, (width*height)*3); red_chan = (unsigned char*)calloc(1, width*height); green_chan = (unsigned char*)calloc(1, width*height); blue_chan = (unsigned char *)calloc(1, width*height); result = (pixel *)calloc(width*height, PIXELSIZE); resCode = BZ2_bzBuffToBuffDecompress((char *)undata, (unsigned *)&i, (char *)(data+8), size-8, 0, 0); if (resCode){ printf("Decompression failure, %d\n", resCode); free(red_chan); free(green_chan); free(blue_chan); free(undata); free(result); return NULL; } if(i != (width*height)*3){ printf("Result buffer size mismatch, %d != %d\n", i, (width*height)*3); free(red_chan); free(green_chan); free(blue_chan); free(undata); free(result); return NULL; } memcpy(red_chan, undata, width*height); memcpy(green_chan, undata+(width*height), width*height); memcpy(blue_chan, undata+((width*height)*2), width*height); for(cx = 0; cxget_clist_x(), resamplers[0]->get_clist_y(), filter_scale, filter_scale); samples[i] = new float[sourceWidth]; } unsigned char * resultImage = new unsigned char[resultHeight * resultPitch]; std::fill(resultImage, resultImage + (resultHeight*resultPitch), 0); //Resample time int resultY = 0; for (int sourceY = 0; sourceY < sourceHeight; sourceY++) { unsigned char * sourcePixel = &source[sourceY * sourcePitch]; //Move pixel components into channel samples for (int c = 0; c < PIXELCHANNELS; c++) { for (int x = 0; x < sourceWidth; x++) { samples[c][x] = sourcePixel[(x*PIXELSIZE)+c] * (1.0f/255.0f); } } //Put channel sample data into resampler for (int c = 0; c < PIXELCHANNELS; c++) { if (!resamplers[c]->put_line(&samples[c][0])) { printf("Out of memory!\n"); return NULL; } } //Perform resample and Copy components from resampler result samples to image buffer for ( ; ; ) { int comp_index; for (comp_index = 0; comp_index < PIXELCHANNELS; comp_index++) { const float* resultSamples = resamplers[comp_index]->get_line(); if (!resultSamples) break; unsigned char * resultPixel = &resultImage[(resultY * resultPitch) + comp_index]; for (int x = 0; x < resultWidth; x++) { int c = (int)(255.0f * resultSamples[x] + .5f); if (c < 0) c = 0; else if (c > 255) c = 255; *resultPixel = (unsigned char)c; resultPixel += PIXELSIZE; } } if (comp_index < PIXELCHANNELS) break; resultY++; } } //Clean up for(int i = 0; i < PIXELCHANNELS; i++) { delete resamplers[i]; delete[] samples[i]; } return (pixel*)resultImage; #else #ifdef DEBUG std::cout << "Resampling " << sw << "x" << sh << " to " << rw << "x" << rh << std::endl; #endif bool stairstep = false; if(rw < sw || rh < sh) { float fx = (float)(((float)sw)/((float)rw)); float fy = (float)(((float)sh)/((float)rh)); int fxint, fyint; double fxintp_t, fyintp_t; float fxf = modf(fx, &fxintp_t), fyf = modf(fy, &fyintp_t); fxint = fxintp_t; fyint = fyintp_t; if(((fxint & (fxint-1)) == 0 && fxf < 0.1f) || ((fyint & (fyint-1)) == 0 && fyf < 0.1f)) stairstep = true; #ifdef DEBUG if(stairstep) std::cout << "Downsampling by " << fx << "x" << fy << " using stairstepping" << std::endl; else std::cout << "Downsampling by " << fx << "x" << fy << " without stairstepping" << std::endl; #endif } int y, x, fxceil, fyceil; //int i,j,x,y,w,h,r,g,b,c; pixel *q = NULL; if(rw == sw && rh == sh){ //Don't resample q = new pixel[rw*rh]; std::copy(src, src+(rw*rh), q); } else if(!stairstep) { float fx, fy, fyc, fxc; double intp; pixel tr, tl, br, bl; q = new pixel[rw*rh]; //Bilinear interpolation for upscaling for (y=0; y=sw) fxceil = sw-1; if (fyceil>=sh) fyceil = sh-1; tr = src[sw*(int)floor(fy)+fxceil]; tl = src[sw*(int)floor(fy)+(int)floor(fx)]; br = src[sw*fyceil+fxceil]; bl = src[sw*fyceil+(int)floor(fx)]; q[rw*y+x] = PIXRGB( (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)), (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)), (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc)) ); } } else { //Stairstepping float fx, fy, fyc, fxc; double intp; pixel tr, tl, br, bl; int rrw = rw, rrh = rh; pixel * oq; oq = new pixel[sw*sh]; std::copy(src, src+(sw*sh), oq); rw = sw; rh = sh; while(rrw != rw && rrh != rh){ if(rw > rrw) rw *= 0.7; if(rh > rrh) rh *= 0.7; if(rw <= rrw) rw = rrw; if(rh <= rrh) rh = rrh; q = new pixel[rw*rh]; //Bilinear interpolation for (y=0; y=sw) fxceil = sw-1; if (fyceil>=sh) fyceil = sh-1; tr = oq[sw*(int)floor(fy)+fxceil]; tl = oq[sw*(int)floor(fy)+(int)floor(fx)]; br = oq[sw*fyceil+fxceil]; bl = oq[sw*fyceil+(int)floor(fx)]; q[rw*y+x] = PIXRGB( (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)), (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)), (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc)) ); } delete[] oq; oq = q; sw = rw; sh = rh; } } return q; #endif } pixel *Graphics::rescale_img(pixel *src, int sw, int sh, int *qw, int *qh, int f) { int i,j,x,y,w,h,r,g,b,c; pixel p, *q; w = (sw+f-1)/f; h = (sh+f-1)/f; q = (pixel *)malloc(w*h*PIXELSIZE); for (y=0; y1) { r = (r+c/2)/c; g = (g+c/2)/c; b = (b+c/2)/c; } q[y*w+x] = PIXRGB(r, g, b); } *qw = w; *qh = h; return q; } int Graphics::textwidth(const char *s) { int x = 0; for (; *s; s++) { if(((char)*s)=='\b') { if(!s[1]) break; s++; continue; } else if(*s == '\x0F') { if(!s[1] || !s[2] || !s[3]) break; s+=3; continue; } x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; } return x-1; } int Graphics::CharWidth(unsigned char c) { return font_data[font_ptrs[(int)c]]; } int Graphics::textnwidth(char *s, int n) { int x = 0; for (; *s; s++) { if (!n) break; if(((char)*s)=='\b') { if(!s[1]) break; s++; continue; } else if(*s == '\x0F') { if(!s[1] || !s[2] || !s[3]) break; s+=3; continue; } x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; n--; } return x-1; } void Graphics::textnpos(char *s, int n, int w, int *cx, int *cy) { int x = 0; int y = 0; int wordlen, charspace; while (*s&&n) { wordlen = strcspn(s," .,!?\n"); charspace = textwidthx(s, w-x); if (charspace=-1; s++) { if (!n) { break; } x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; if (x>=w) { x = 0; y += FONT_H+2; } n--; } } *cx = x-1; *cy = y; } int Graphics::textwidthx(char *s, int w) { int x=0,n=0,cw; for (; *s; s++) { if((char)*s == '\b') { if(!s[1]) break; s++; continue; } else if (*s == '\x0F') { if(!s[1] || !s[2] || !s[3]) break; s+=3; continue; } cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; if (x+(cw/2) >= w) break; x += cw; n++; } return n; } int Graphics::PositionAtCharIndex(char *s, int charIndex, int & positionX, int & positionY) { int x = 0, y = 0, lines = 1; for (; *s; s++) { if (!charIndex) break; if(*s == '\n') { lines++; x = 0; y += FONT_H+2; charIndex--; continue; } else if(*s =='\b') { if(!s[1]) break; s++; charIndex-=2; continue; } else if(*s == '\x0F') { if(!s[1] || !s[2] || !s[3]) break; s+=3; charIndex-=4; continue; } x += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; charIndex--; } positionX = x; positionY = y; return lines; } int Graphics::CharIndexAtPosition(char *s, int positionX, int positionY) { int x=0, y=0,charIndex=0,cw; for (; *s; s++) { if(*s == '\n') { x = 0; y += FONT_H+2; charIndex++; continue; } else if(*s == '\b') { if(!s[1]) break; s++; charIndex+=2; continue; } else if (*s == '\x0F') { if(!s[1] || !s[2] || !s[3]) break; s+=3; charIndex+=4; continue; } cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; if ((x+(cw/2) >= positionX && y+FONT_H >= positionY) || y > positionY) break; x += cw; charIndex++; } return charIndex; } int Graphics::textposxy(char *s, int width, int w, int h) { int x=0,y=0,n=0,cw, wordlen, charspace; while (*s) { wordlen = strcspn(s," .,!?\n"); charspace = textwidthx(s, width-x); if (charspace=-1; s++) { cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; if ((x+(cw/2) >= w && y+6 >= h)||(y+6 >= h+FONT_H+2)) return n++; x += cw; if (x>=width) { x = 0; y += FONT_H+2; } n++; } } return n; } int Graphics::textwrapheight(char *s, int width) { int x=0, height=FONT_H+2, cw; int wordlen; int charspace; while (*s) { wordlen = strcspn(s," .,!?\n"); charspace = textwidthx(s, width-x); if (charspace=-1; s++) { if (*s == '\n') { x = 0; height += FONT_H+2; } else if (*s == '\b') { if(!s[1]) break; s++; } else if (*s == '\x0F') { if(!s[1] || !s[2] || !s[3]) break; s+=3; } else { cw = font_data[font_ptrs[(int)(*(unsigned char *)s)]]; if (x+cw>=width) { x = 0; height += FONT_H+2; } x += cw; } } } return height; } void Graphics::textsize(const char * s, int & width, int & height) { if(!strlen(s)) { width = 0; height = FONT_H; return; } int cHeight = FONT_H, cWidth = 0, lWidth = 0; for (; *s; s++) { if (*s == '\n') { cWidth = 0; cHeight += FONT_H+2; } else if (*s == '\x0F') { if(!s[1] || !s[2] || !s[1]) break; s+=3; } else if (*s == '\b') { if(!s[1]) break; s++; } else { cWidth += font_data[font_ptrs[(int)(*(unsigned char *)s)]]; if(cWidth>lWidth) lWidth = cWidth; } } width = lWidth; height = cHeight; } void Graphics::draw_icon(int x, int y, Icon icon, unsigned char alpha, bool invert) { y--; switch(icon) { case IconOpen: if(invert) drawchar(x, y, 0x81, 0, 0, 0, alpha); else drawchar(x, y, 0x81, 255, 255, 255, alpha); break; case IconReload: if(invert) drawchar(x, y, 0x91, 0, 0, 0, alpha); else drawchar(x, y, 0x91, 255, 255, 255, alpha); break; case IconSave: if(invert) drawchar(x, y, 0x82, 0, 0, 0, alpha); else drawchar(x, y, 0x82, 255, 255, 255, alpha); break; case IconVoteUp: if(invert) { drawchar(x-11, y+1, 0xCB, 0, 100, 0, alpha); drawtext(x+2, y+1, "Vote", 0, 100, 0, alpha); } else { drawchar(x-11, y+1, 0xCB, 0, 187, 18, alpha); drawtext(x+2, y+1, "Vote", 0, 187, 18, alpha); } break; case IconVoteDown: if(invert) drawchar(x, y, 0xCA, 100, 10, 0, alpha); else drawchar(x, y, 0xCA, 187, 40, 0, alpha); break; case IconTag: if(invert) drawchar(x, y, 0x83, 0, 0, 0, alpha); else drawchar(x, y, 0x83, 255, 255, 255, alpha); break; case IconNew: if(invert) drawchar(x, y, 0x92, 0, 0, 0, alpha); else drawchar(x, y, 0x92, 255, 255, 255, alpha); break; case IconLogin: if(invert) drawchar(x, y+1, 0x84, 0, 0, 0, alpha); else drawchar(x, y+1, 0x84, 255, 255, 255, alpha); break; case IconSimulationSettings: if(invert) drawchar(x, y+1, 0xCF, 0, 0, 0, alpha); else drawchar(x, y+1, 0xCF, 255, 255, 255, alpha); break; case IconRenderSettings: if(invert) { drawchar(x, y+1, 0xD8, 255, 0, 0, alpha); drawchar(x, y+1, 0xD9, 0, 255, 0, alpha); drawchar(x, y+1, 0xDA, 0, 0, 255, alpha); } else { addchar(x, y+1, 0xD8, 255, 0, 0, alpha); addchar(x, y+1, 0xD9, 0, 255, 0, alpha); addchar(x, y+1, 0xDA, 0, 0, 255, alpha); } break; case IconPause: if(invert) drawchar(x, y, 0x90, 0, 0, 0, alpha); else drawchar(x, y, 0x90, 255, 255, 255, alpha); break; case IconFavourite: if(invert) drawchar(x, y, 0xCC, 100, 80, 32, alpha); else drawchar(x, y, 0xCC, 192, 160, 64, alpha); break; case IconReport: if(invert) drawchar(x, y, 0xE3, 140, 140, 0, alpha); else drawchar(x, y, 0xE3, 255, 255, 0, alpha); break; case IconUsername: if(invert) { drawchar(x, y, 0x8B, 32, 64, 128, alpha); drawchar(x, y, 0x8A, 0, 0, 0, alpha); } else { drawchar(x, y, 0x8B, 32, 64, 128, alpha); drawchar(x, y, 0x8A, 255, 255, 255, alpha); } break; case IconPassword: if(invert) { drawchar(x, y, 0x8C, 160, 144, 32, alpha); drawchar(x, y, 0x84, 0, 0, 0, alpha); } else { drawchar(x, y, 0x8C, 160, 144, 32, alpha); drawchar(x, y, 0x84, 255, 255, 255, alpha); } break; case IconClose: if(invert) drawchar(x, y, 0xAA, 20, 20, 20, alpha); else drawchar(x, y, 0xAA, 230, 230, 230, alpha); break; case IconVoteSort: if (invert) { drawchar(x, y, 0xA9, 44, 48, 32, alpha); drawchar(x, y, 0xA8, 32, 44, 32, alpha); drawchar(x, y, 0xA7, 128, 128, 128, alpha); } else { drawchar(x, y, 0xA9, 144, 48, 32, alpha); drawchar(x, y, 0xA8, 32, 144, 32, alpha); drawchar(x, y, 0xA7, 255, 255, 255, alpha); } break; case IconDateSort: if (invert) { drawchar(x, y, 0xA6, 32, 32, 32, alpha); } else { drawchar(x, y, 0xA6, 255, 255, 255, alpha); } break; case IconMyOwn: if (invert) { drawchar(x, y, 0x94, 192, 160, 64, alpha); drawchar(x, y, 0x93, 32, 32, 32, alpha); } else { drawchar(x, y, 0x94, 192, 160, 64, alpha); drawchar(x, y, 0x93, 255, 255, 255, alpha); } break; case IconSearch: drawchar(x, y, 0x8E, 30, 30, 180, alpha); drawchar(x, y, 0x8F, 255, 255, 255, alpha); break; case IconDelete: if(invert) { drawchar(x, y, 0x86, 159, 47, 31, alpha); drawchar(x, y, 0x85, 0, 0, 0, alpha); } else { drawchar(x, y, 0x86, 159, 47, 31, alpha); drawchar(x, y, 0x85, 255, 255, 255, alpha); } break; case IconAdd: if(invert) { drawchar(x, y, 0x86, 32, 144, 32, alpha); drawchar(x, y, 0x89, 0, 0, 0, alpha); } else { drawchar(x, y, 0x86, 32, 144, 32, alpha); drawchar(x, y, 0x89, 255, 255, 255, alpha); } break; case IconVelocity: drawchar(x+1, y, 0x98, 128, 160, 255, alpha); break; case IconPressure: if(invert) drawchar(x+1, y+1, 0x99, 180, 160, 16, alpha); else drawchar(x+1, y+1, 0x99, 255, 212, 32, alpha); break; case IconPersistant: if(invert) drawchar(x+1, y+1, 0x9A, 20, 20, 20, alpha); else drawchar(x+1, y+1, 0x9A, 212, 212, 212, alpha); break; case IconFire: drawchar(x+1, y+1, 0x9B, 255, 0, 0, alpha); drawchar(x+1, y+1, 0x9C, 255, 255, 64, alpha); break; case IconBlob: if(invert) drawchar(x+1, y, 0xBF, 55, 180, 55, alpha); else drawchar(x+1, y, 0xBF, 55, 255, 55, alpha); break; case IconHeat: drawchar(x+3, y, 0xBE, 255, 0, 0, alpha); if(invert) drawchar(x+3, y, 0xBD, 0, 0, 0, alpha); else drawchar(x+3, y, 0xBD, 255, 255, 255, alpha); break; case IconBlur: if(invert) drawchar(x+1, y, 0xC4, 50, 70, 180, alpha); else drawchar(x+1, y, 0xC4, 100, 150, 255, alpha); break; case IconGradient: if(invert) drawchar(x+1, y+1, 0xD3, 255, 50, 255, alpha); else drawchar(x+1, y+1, 0xD3, 205, 50, 205, alpha); break; case IconLife: if(invert) drawchar(x, y+1, 0xE0, 0, 0, 0, alpha); else drawchar(x, y+1, 0xE0, 255, 255, 255, alpha); break; case IconEffect: drawchar(x+1, y, 0xE1, 255, 255, 160, alpha); break; case IconGlow: drawchar(x+1, y, 0xDF, 200, 255, 255, alpha); break; case IconWarp: drawchar(x+1, y, 0xDE, 255, 255, 255, alpha); break; case IconBasic: if(invert) drawchar(x+1, y+1, 0xDB, 50, 50, 0, alpha); else drawchar(x+1, y+1, 0xDB, 255, 255, 200, alpha); break; case IconAltAir: if(invert) { drawchar(x+1, y+1, 0xD4, 180, 55, 55, alpha); drawchar(x+1, y+1, 0xD5, 55, 180, 55, alpha); } else { drawchar(x+1, y+1, 0xD4, 255, 55, 55, alpha); drawchar(x+1, y+1, 0xD5, 55, 255, 55, alpha); } break; default: if(invert) drawchar(x, y, 't', 0, 0, 0, alpha); else drawchar(x, y, 't', 255, 255, 255, alpha); break; } } void Graphics::draw_rgba_image(unsigned char *data, int x, int y, float alpha) { unsigned char w, h; int i, j; unsigned char r, g, b, a; if (!data) return; w = *(data++)&0xFF; h = *(data++)&0xFF; for (j=0; j