#include "wxGLString.h" #ifdef __WXMAC__ #include "OpenGL/gl.h" #else #include #endif #include "wx/wx.h" GLuint* loadImage(wxImage* img) { GLuint* ID = new GLuint[1]; glGenTextures(1, &ID[0]); glBindTexture(GL_TEXTURE_2D, *ID); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); const int w = img->GetWidth(), h = img->GetHeight(); // note: must make a local copy before passing the data to OpenGL, as GetData() returns RGB // and we want the Alpha channel. Furthermore, the current rendering is black-on-white, we'll // convert it to an alpha channel by the way (not all platforms support transparency in wxDCs // so it's the easiest way to go) GLubyte* bitmapData = img->GetData(); GLubyte* imageData = NULL; int bytesPerPixel = 4; int imageSize = w * h * bytesPerPixel; imageData = (GLubyte*)malloc(imageSize); int rev_val = h - 1; for(int y = 0; y < h; y++) { for(int x = 0; x < w; x++) { imageData[(x + y * w) * bytesPerPixel + 0] = 255; imageData[(x + y * w) * bytesPerPixel + 1] = 255; imageData[(x + y * w) * bytesPerPixel + 2] = 255; // alpha imageData[(x + y * w) * bytesPerPixel + 3] = 255 - bitmapData[(x + (rev_val - y) * w) * 3]; } // next } // next glTexImage2D(GL_TEXTURE_2D, 0, bytesPerPixel, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); free(imageData); // set texture parameters as you wish glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // GL_CLAMP_TO_EDGE glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); return ID; } class TextTexture { friend class wxGLString; friend class wxGLStringArray; friend class wxGLStringNumber; private: GLuint* ID = NULL; protected: GLuint* getID(); TextTexture(); TextTexture(wxBitmap& bmp); void load(wxImage* img); public: ~TextTexture(); }; #if 0 #pragma mark - #pragma mark TextGLDrawable implementation #endif TextGLDrawable::TextGLDrawable(TextTexture* image_arg) { x = 0; y = 0; angle = 0; xscale = 1; yscale = 1; xflip = false; yflip = false; if(image_arg) setImage(image_arg); else image = NULL; tex_coord_x1 = 0; tex_coord_y1 = 1; tex_coord_x2 = 1; tex_coord_y2 = 0; } void TextGLDrawable::setFlip(bool x, bool y) { xflip = x; yflip = y; } void TextGLDrawable::move(double x, double y) { TextGLDrawable::x = x; TextGLDrawable::y = y; } void TextGLDrawable::scale(float x, float y) { TextGLDrawable::xscale = x; TextGLDrawable::yscale = y; } void TextGLDrawable::scale(float k) { TextGLDrawable::xscale = k; TextGLDrawable::yscale = k; } void TextGLDrawable::setImage(TextTexture* image) { TextGLDrawable::image = image; } void TextGLDrawable::rotate(int angle) { TextGLDrawable::angle = angle; } void TextGLDrawable::render() const { assert(image); glPushMatrix(); glTranslatef(x - w / 2, y - h / 2, 0); if(xscale != 1 || yscale != 1) glScalef(xscale, yscale, 1); if(angle != 0) glRotatef(angle, 0, 0, 1); glBegin(GL_QUADS); glTexCoord2f(xflip ? tex_coord_x2 : tex_coord_x1, yflip ? tex_coord_y2 : tex_coord_y1); glVertex2f(0, 0); glTexCoord2f(xflip ? tex_coord_x1 : tex_coord_x2, yflip ? tex_coord_y2 : tex_coord_y1); glVertex2f(w, 0); glTexCoord2f(xflip ? tex_coord_x1 : tex_coord_x2, yflip ? tex_coord_y1 : tex_coord_y2); glVertex2f(w, h); glTexCoord2f(xflip ? tex_coord_x2 : tex_coord_x1, yflip ? tex_coord_y1 : tex_coord_y2); glVertex2f(0, h); glEnd(); glPopMatrix(); } #if 0 #pragma mark - #pragma mark TextTexture implementation #endif TextTexture::TextTexture() {} TextTexture::TextTexture(wxBitmap& bmp) { wxImage img = bmp.ConvertToImage(); load(&img); } void TextTexture::load(wxImage* img) { ID = loadImage(img); } GLuint* TextTexture::getID() { return ID; } TextTexture::~TextTexture() { glDeleteTextures(1, ID); if(ID) delete ID; // Memory leak? } #if 0 #pragma mark - #pragma mark wxGLString implementation #endif wxGLString::wxGLString() : wxString(wxT("")) , TextGLDrawable() { img = NULL; } wxGLString::wxGLString(wxString message) : wxString(message) , TextGLDrawable() { img = NULL; } void wxGLString::operator=(wxString& string) { (*((wxString*)this)) = string; } void wxGLString::bind() const { if(img->getID()[0]) glBindTexture(GL_TEXTURE_2D, img->getID()[0]); } void wxGLString::calculateSize(wxDC* dc, const bool ignore_font /* when from array */) { if(!ignore_font) { if(font.IsOk()) dc->SetFont(font); else dc->SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT)); } dc->GetTextExtent(*this, &w, &h); } void wxGLString::consolidate(wxDC* dc) { calculateSize(dc); const int power_of_2_w = std::max(32, (int)pow((double)2, (int)ceil((float)log((double)w) / log(2.0)))); const int power_of_2_h = std::max(32, (int)pow((double)2, (int)ceil((float)log((double)h) / log(2.0)))); wxBitmap bmp(power_of_2_w, power_of_2_h); assert(bmp.IsOk()); { wxMemoryDC temp_dc(bmp); temp_dc.SetBrush(*wxWHITE_BRUSH); temp_dc.Clear(); if(font.IsOk()) temp_dc.SetFont(font); else temp_dc.SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT)); temp_dc.DrawText(*this, 0, 0); } if(img) delete img; img = new TextTexture(bmp); TextGLDrawable::texw = power_of_2_w; TextGLDrawable::texh = power_of_2_h; TextGLDrawable::tex_coord_x2 = (float)w / (float)power_of_2_w; TextGLDrawable::tex_coord_y2 = 1 - (float)h / (float)power_of_2_h; TextGLDrawable::tex_coord_y1 = 1; TextGLDrawable::setImage(img); } void wxGLString::consolidateFromArray(wxDC* dc, double x, double y) { dc->DrawText(*this, x, y); } void wxGLString::setFont(wxFont font) { wxGLString::font = font; } void wxGLString::render(const double x, const double y) { TextGLDrawable::move(x, y); TextGLDrawable::render(); } wxGLString::~wxGLString() { if(img) delete img; } #if 0 #pragma mark - #pragma mark wxGLNumberRenderer implementation #endif wxGLNumberRenderer::wxGLNumberRenderer() : wxGLString(wxT("0 1 2 3 4 5 6 7 8 9 . - ")) { number_location = new int[13]; } wxGLNumberRenderer::~wxGLNumberRenderer() { delete[] number_location; } void wxGLNumberRenderer::consolidate(wxDC* dc) { wxGLString::consolidate(dc); if(font.IsOk()) dc->SetFont(font); else dc->SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT)); number_location[0] = 0; number_location[1] = dc->GetTextExtent(wxT("0 ")).GetWidth(); number_location[2] = dc->GetTextExtent(wxT("0 1 ")).GetWidth(); number_location[3] = dc->GetTextExtent(wxT("0 1 2 ")).GetWidth(); number_location[4] = dc->GetTextExtent(wxT("0 1 2 3 ")).GetWidth(); number_location[5] = dc->GetTextExtent(wxT("0 1 2 3 4 ")).GetWidth(); number_location[6] = dc->GetTextExtent(wxT("0 1 2 3 4 5 ")).GetWidth(); number_location[7] = dc->GetTextExtent(wxT("0 1 2 3 4 5 6 ")).GetWidth(); number_location[8] = dc->GetTextExtent(wxT("0 1 2 3 4 5 6 7 ")).GetWidth(); number_location[9] = dc->GetTextExtent(wxT("0 1 2 3 4 5 6 7 8 ")).GetWidth(); number_location[10] = dc->GetTextExtent(wxT("0 1 2 3 4 5 6 7 8 9 ")).GetWidth(); number_location[11] = dc->GetTextExtent(wxT("0 1 2 3 4 5 6 7 8 9 . ")).GetWidth(); number_location[12] = dc->GetTextExtent(wxT("0 1 2 3 4 5 6 7 8 9 . - ")).GetWidth(); space_w = dc->GetTextExtent(wxT(" ")).GetWidth(); } void wxGLNumberRenderer::renderNumber(int i, double x, double y) { wxString s; s << i; renderNumber(s, x, y); } void wxGLNumberRenderer::renderNumber(float f, double x, double y) { wxString s; s << f; renderNumber(s, x, y); } void wxGLNumberRenderer::renderNumber(wxString s, double x, double y) { const int full_string_w = TextGLDrawable::texw; const int char_amount = s.Length(); for(int c = 0; c < char_amount; c++) { int charid = -1; char schar = s[c]; switch(schar) { case '0': charid = 0; break; case '1': charid = 1; break; case '2': charid = 2; break; case '3': charid = 3; break; case '4': charid = 4; break; case '5': charid = 5; break; case '6': charid = 6; break; case '7': charid = 7; break; case '8': charid = 8; break; case '9': charid = 9; break; case '.': case ',': charid = 10; break; case '-': charid = 11; break; default: printf("Warning: character %c unexpected in number!\n", schar); continue; } assert(charid != -1); TextGLDrawable::tex_coord_x1 = (float)number_location[charid] / (float)full_string_w; TextGLDrawable::tex_coord_x2 = (float)(number_location[charid + 1] - space_w) / (float)full_string_w; const int char_width = number_location[charid + 1] - number_location[charid] - space_w; TextGLDrawable::w = char_width; TextGLDrawable::move(x, y); TextGLDrawable::render(); x += char_width; } // next // TextGLDrawable::w = full_string_w; } #if 0 #pragma mark - #pragma mark wxGLStringArray implementation #endif wxGLStringArray::wxGLStringArray() { img = NULL; } wxGLStringArray::wxGLStringArray(const wxString strings_arg[], int amount) { img = NULL; for(int n = 0; n < amount; n++) strings.push_back(wxGLString(strings_arg[n])); } wxGLStringArray::~wxGLStringArray() { if(img) delete img; } wxGLString& wxGLStringArray::get(const int id) { return strings[id]; } void wxGLStringArray::bind() { if(img->getID()[0]) glBindTexture(GL_TEXTURE_2D, img->getID()[0]); } void wxGLStringArray::addString(wxString string) { strings.push_back(wxGLString(string)); } void wxGLStringArray::setFont(wxFont font) { wxGLStringArray::font = font; } void wxGLStringArray::consolidate(wxDC* dc) { int x = 0, y = 0; if(font.IsOk()) dc->SetFont(font); else dc->SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT)); // find how much space we need int longest_string = 0; const int amount = strings.size(); for(int n = 0; n < amount; n++) { strings[n].calculateSize(dc, true); y += strings[n].h; if(strings[n].w > longest_string) longest_string = strings[n].w; } // next const int average_string_height = y / amount; // split in multiple columns if necessary int column_amount = 1; while(amount / column_amount > 30 && column_amount < 10) column_amount++; const int power_of_2_w = pow((double)2, (int)ceil((float)log((double)longest_string * (double)column_amount) / log(2.0))); const int power_of_2_h = pow((double)2, (int)ceil((float)log((double)y / (double)column_amount) / log(2.0))); // std::cout << "bitmap size : " << power_of_2_w << ", " << power_of_2_h << " // " << column_amount << " columns" // << std::endl; wxBitmap bmp(power_of_2_w, power_of_2_h); assert(bmp.IsOk()); { wxMemoryDC temp_dc(bmp); temp_dc.SetBrush(*wxWHITE_BRUSH); temp_dc.Clear(); y = 0; x = 0; if(font.IsOk()) temp_dc.SetFont(font); else temp_dc.SetFont(wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT)); for(int n = 0; n < amount; n++) { strings[n].consolidateFromArray(&temp_dc, x, y); strings[n].tex_coord_x1 = (float)x / (float)power_of_2_w; strings[n].tex_coord_y1 = 1.0 - (float)y / (float)power_of_2_h; strings[n].tex_coord_x2 = (float)(x + strings[n].w) / (float)power_of_2_w; strings[n].tex_coord_y2 = 1.0 - (float)(y + strings[n].h) / (float)power_of_2_h; y += strings[n].h; if(y > power_of_2_h - average_string_height) // check if we need to switch to next column { y = 0; x += longest_string; } } } if(img) delete img; img = new TextTexture(bmp); for(int n = 0; n < amount; n++) strings[n].setImage(img); }