From 0314a148490377c71e2e7d7089bb724d73bffe41 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Wed, 29 Aug 2012 13:53:53 -0500 Subject: [PATCH] New renderer starting to work. Image loading still not working and font sizing is still off. Resolution is hardcoded. --- Makefile | 6 +- src/Font.cpp | 257 +++++++++++++++++++++++++++++++++ src/Font.h | 51 +++++++ src/Renderer.cpp | 3 - src/Renderer.h | 19 +-- src/Renderer_draw.cpp | 188 ------------------------ src/Renderer_draw_gl.cpp | 188 ++++++++++++++++++++++++ src/Renderer_init_rpi.cpp | 178 +++++++++++++++++++++++ src/SystemData.cpp | 5 + src/components/GuiGameList.cpp | 4 +- src/components/GuiGameList.h | 2 + src/components/GuiImage.cpp | 246 +++++++++++++++---------------- src/components/GuiImage.h | 12 +- src/main.cpp | 29 ++-- 14 files changed, 831 insertions(+), 357 deletions(-) create mode 100644 src/Font.cpp create mode 100644 src/Font.h delete mode 100644 src/Renderer_draw.cpp create mode 100644 src/Renderer_draw_gl.cpp create mode 100644 src/Renderer_init_rpi.cpp diff --git a/Makefile b/Makefile index a421c73b6..2d20becc7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC=g++ -CFLAGS=-c -Wall -LDFLAGS=-lSDL -lSDL_ttf -lSDL_image -lSDL_gfx -lboost_system -lboost_filesystem -SRCSOURCES=main.cpp Renderer.cpp Renderer_draw.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp MathExp.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp components/GuiTheme.cpp pugiXML/pugixml.cpp +CFLAGS=-c -Wall -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/usr/include/freetype2 -I/usr/include/SDL -I/usr/include +LDFLAGS=-L/opt/vc/lib -lbcm_host -lEGL -lGLESv2 -lfreetype -lSDL -lboost_system -lboost_filesystem -lfreeimage +SRCSOURCES=main.cpp Renderer.cpp Renderer_init_rpi.cpp Font.cpp Renderer_draw_gl.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp MathExp.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp components/GuiTheme.cpp pugiXML/pugixml.cpp SOURCES=$(addprefix src/,$(SRCSOURCES)) OBJECTS=$(SOURCES:.cpp=.o) EXECUTABLE=emulationstation diff --git a/src/Font.cpp b/src/Font.cpp new file mode 100644 index 000000000..11f125f66 --- /dev/null +++ b/src/Font.cpp @@ -0,0 +1,257 @@ +#include "Font.h" +#include +#include +#include +#include "Renderer.h" + +FT_Library Font::sLibrary; + +int Font::getDpiX() { return 300; } +int Font::getDpiY() { return 300; } + +void Font::initLibrary() +{ + if(FT_Init_FreeType(&sLibrary)) + { + std::cout << "Error initializing FreeType!\n"; + } +} + +Font::Font(std::string path, int size) +{ + if(FT_New_Face(sLibrary, path.c_str(), 0, &face)) + { + std::cout << "Error creating font face! (path: " << path.c_str() << "\n"; + while(true); + } + + FT_Set_Char_Size(face, 0, size * 64, getDpiX(), getDpiY()); + + buildAtlas(); +} + +void Font::buildAtlas() +{ + //find the size we should use + FT_GlyphSlot g = face->glyph; + int w = 0; + int h = 0; + + /*for(int i = 32; i < 128; i++) + { + if(FT_Load_Char(face, i, FT_LOAD_RENDER)) + { + fprintf(stderr, "Loading character %c failed!\n", i); + continue; + } + + w += g->bitmap.width; + h = std::max(h, g->bitmap.rows); + }*/ + + //GLES requires a power of two + //the max size (GL_MAX_TEXTURE_SIZE) is pretty small too, like 3300, so just force it + w = 2048; + h = 2048; + + textureWidth = w; + textureHeight = h; + + + + //create the texture + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL); + + //copy the glyphs into the texture + int x = 0; + int y = 0; + int maxHeight = 0; + for(int i = 32; i < 128; i++) + { + if(FT_Load_Char(face, i, FT_LOAD_RENDER)) + continue; + + //prints rendered texture to the console + /*std::cout << "uploading at x: " << x << ", w: " << g->bitmap.width << " h: " << g->bitmap.rows << "\n"; + + for(int k = 0; k < g->bitmap.rows; k++) + { + for(int j = 0; j < g->bitmap.width; j++) + { + if(g->bitmap.buffer[g->bitmap.width * k + j]) + std::cout << "."; + else + std::cout << " "; + } + std::cout << "\n"; + }*/ + + if(x + g->bitmap.width >= textureWidth) + { + x = 0; + y += maxHeight; + maxHeight = 0; + } + + if(g->bitmap.rows > maxHeight) + maxHeight = g->bitmap.rows; + + glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); + + + charData[i].texX = x; + charData[i].texY = 0; + charData[i].texW = g->bitmap.width; + charData[i].texH = g->bitmap.rows; + charData[i].advX = g->metrics.horiAdvance >> 6; + charData[i].advY = g->advance.y >> 6; + charData[i].bearingY = g->metrics.horiBearingY >> 6; + + x += g->bitmap.width; + } + + glBindTexture(GL_TEXTURE_2D, 0); + + std::cout << "generated texture \"" << textureID << "\" (w: " << w << " h: " << h << ")" << std::endl; + std::cout << "(final x: " << x << " y: " << y << ")" << std::endl; +} + +Font::~Font() +{ + FT_Done_Face(face); + + glDeleteTextures(1, &textureID); +} + + + +//why these aren't in an array: +//openGL reads these in the order they are in memory +//if I use an array, it will be 4 x values then 4 y values +//it'll read XX, XX, YY instead of XY, XY, XY +struct point { + GLfloat pos0x; + GLfloat pos0y; + + GLfloat pos1x; + GLfloat pos1y; + + GLfloat pos2x; + GLfloat pos2y; +}; + +struct tex { + GLfloat tex0x; + GLfloat tex0y; + + GLfloat tex1x; + GLfloat tex1y; + + GLfloat tex2x; + GLfloat tex2y; +}; + +void Font::drawText(std::string text, int startx, int starty, int color) +{ + + starty += (face->height / face->units_per_EM) << 6; //(face->height / face->units_per_EM) << 6; + + int pointCount = text.length() * 2; + point* points = new point[pointCount]; + tex* texs = new tex[pointCount]; + + + glBindTexture(GL_TEXTURE_2D, textureID); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + + //texture atlas width/height + float tw = (float)textureWidth; + float th = (float)textureHeight; + + int p = 0; + int i = 0; + + float x = startx; + float y = starty; + for(; p < pointCount; i++, p++) + { + int letter = text[i]; + + if(letter < 32 || letter >= 128) + continue; + + points[p].pos0x = x; points[p].pos0y = y + charData[letter].texH - charData[letter].bearingY; + points[p].pos1x = x + charData[letter].texW; points[p].pos1y = y - charData[letter].bearingY; + points[p].pos2x = x; points[p].pos2y = y - charData[letter].bearingY; + + texs[p].tex0x = charData[letter].texX / tw; texs[p].tex0y = (charData[letter].texY + charData[letter].texH) / th; + texs[p].tex1x = (charData[letter].texX + charData[letter].texW) / tw; texs[p].tex1y = charData[letter].texY / th; + texs[p].tex2x = charData[letter].texX / tw; texs[p].tex2y = charData[letter].texY / th; + + + p++; + + points[p].pos0x = x; points[p].pos0y = y + charData[letter].texH - charData[letter].bearingY; + points[p].pos1x = x + charData[letter].texW; points[p].pos1y = y - charData[letter].bearingY; + points[p].pos2x = x + charData[letter].texW; points[p].pos2y = y + charData[letter].texH - charData[letter].bearingY; + + texs[p].tex0x = charData[letter].texX / tw; texs[p].tex0y = (charData[letter].texY + charData[letter].texH) / th; + texs[p].tex1x = (charData[letter].texX + charData[letter].texW) / tw; texs[p].tex1y = charData[letter].texY / th; + texs[p].tex2x = texs[p].tex1x; texs[p].tex2y = texs[p].tex0y; + + + x += charData[letter].advX; + } + + GLubyte* colors = new GLubyte[pointCount * 3 * 4]; + Renderer::buildGLColorArray(colors, color, pointCount * 3); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, points); + glTexCoordPointer(2, GL_FLOAT, 0, texs); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + + glDrawArrays(GL_TRIANGLES, 0, pointCount * 3); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + //not sure if this properly deletes or not + delete[] points; +} + +void Font::sizeText(std::string text, int* w, int* h) +{ + if(w != NULL) + { + int cwidth = 0; + for(unsigned int i = 0; i < text.length(); i++) + { + cwidth += charData[(int)text[i]].advX; + } + + *w = cwidth; + } + + if(h != NULL) + *h = (face->height / face->units_per_EM) << 6; +} diff --git a/src/Font.h b/src/Font.h new file mode 100644 index 000000000..920328a20 --- /dev/null +++ b/src/Font.h @@ -0,0 +1,51 @@ +//A truetype font loader and renderer, using FreeType 2 and OpenGL. + +#ifndef _FONT_H_ +#define _FONT_H_ + +#include +#include +#include +#include FT_FREETYPE_H + +class Font +{ +public: + static void initLibrary(); + + Font(std::string path, int size); + ~Font(); + + FT_Face face; + + struct charPosData { + int texX; + int texY; + int texW; + int texH; + + int advX; + int advY; + + int bearingY; + }; + + charPosData charData[128]; + + GLuint textureID; + + void drawText(std::string text, int startx, int starty, int color); + void sizeText(std::string text, int* w, int* h); +private: + + static int getDpiX(); + static int getDpiY(); + static FT_Library sLibrary; + + void buildAtlas(); + + int textureWidth; + int textureHeight; +}; + +#endif diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 01fb86155..14e0b8905 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -3,9 +3,6 @@ std::vector renderVector; -unsigned int Renderer::getScreenWidth() { return Renderer::screen ? Renderer::screen->w : 640; } //1024; } -unsigned int Renderer::getScreenHeight() { return Renderer::screen ? Renderer::screen->h : 480; } - void Renderer::registerComponent(GuiComponent* comp) { renderVector.push_back(comp); diff --git a/src/Renderer.h b/src/Renderer.h index 608338459..7f5a2fa6c 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -1,14 +1,9 @@ #ifndef _RENDERER_H_ #define _RENDERER_H_ -#define LAYER_COUNT 3 - -#define BIT(x) (1 << (x)) - #include -#include -#include #include +#include "Font.h" class GuiComponent; @@ -18,22 +13,20 @@ namespace Renderer void unregisterComponent(GuiComponent* comp); void deleteAll(); - void render(); + bool init(int w, int h); + void deinit(); - extern SDL_Surface* screen; - extern TTF_Font* font; + void render(); unsigned int getScreenWidth(); unsigned int getScreenHeight(); enum FontSize { SMALL, MEDIUM, LARGE }; - bool loadFonts(); - extern bool loadedFonts; - extern TTF_Font* fonts[3]; //should be FontSize size but I don't remember the syntax - extern int fontHeight[3]; //same int getFontHeight(FontSize size); //sometimes font size is needed before fonts have been loaded; this takes care of that + void buildGLColorArray(GLubyte* ptr, int color, unsigned int vertCount); //drawing commands + void swapBuffers(); void drawRect(int x, int y, int w, int h, int color); void drawText(std::string text, int x, int y, int color, FontSize fontsize = MEDIUM); void drawCenteredText(std::string text, int xOffset, int y, int color, FontSize fontsize = MEDIUM); diff --git a/src/Renderer_draw.cpp b/src/Renderer_draw.cpp deleted file mode 100644 index 187b38e90..000000000 --- a/src/Renderer_draw.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "Renderer.h" -#include -#include -#include -#include -#include - -SDL_Surface* Renderer::screen; -bool Renderer::loadedFonts = false; -TTF_Font* Renderer::fonts[3]; -int Renderer::fontHeight[3]; - - -//a utility function to convert an int (usually defined as hex) to the SDL color struct -SDL_Color getSDLColor(int& int_color) -{ - //taken off a forum post - //#if SDL_BYTEORDER == SDL_BIG_ENDIAN - SDL_Color ret={(int_color & 0x00ff0000)/0x10000,(int_color & 0x0000ff00)/0x100,(int_color & 0x000000ff),0}; - //#else - // SDL_Color ret={(int_color & 0x000000ff),(int_color & 0x0000ff00)/0x100,(int_color & 0x00ff0000)/0x10000,0}; - //#endif - - return ret; -} - -void Renderer::drawRect(int x, int y, int h, int w, int color) -{ - SDL_Rect rect = {x, y, h, w}; - - //this is really dumb - FillRect takes a uint32, everything else uses getSDLColor - //the internal ints that we use (e.g. 0x999999) are rendered wrong - //i'm really bad at bitshifting so I don't know how to do this in a more efficient way - SDL_Color conv = getSDLColor(color); - color = SDL_MapRGB(Renderer::screen->format, conv.r, conv.g, conv.b); - - SDL_FillRect(Renderer::screen, &rect, color); -} - -bool Renderer::loadFonts() -{ - std::string fontPath = "LinLibertine_R.ttf"; - - //if our font isn't foud, make a last-ditch effort to load a system font - if(!boost::filesystem::exists(fontPath)) - { - std::cerr << "Error - " << fontPath << " font not found. Falling back to "; - fontPath = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"; - std::cerr << fontPath << " (which may not exist).\n"; - } - - int sizeArray[] = {Renderer::getScreenWidth() * 0.015, Renderer::getScreenWidth() * 0.03, Renderer::getScreenWidth() * 0.062}; - - - //the three here should be the font count but, again, I don't remember the syntax - for(unsigned int i = 0; i < 3; i++) - { - TTF_Font* font = TTF_OpenFont(fontPath.c_str(), sizeArray[i]); - if(!font) - { - std::cerr << "Error - could not load font!\n"; - std::cerr << TTF_GetError() << "\n"; - return false; - } - fonts[i] = font; - TTF_SizeText(font, "HEIGHT", NULL, &fontHeight[i]); //gets the height of the string "HEIGHT" in this font (in pixels) - } - - loadedFonts = true; - return true; -} - -int Renderer::getFontHeight(FontSize size) -{ - if(!loadedFonts) - loadFonts(); - - return fontHeight[size]; -} - -void Renderer::drawText(std::string text, int x, int y, int color, FontSize fontsize) -{ - if(!loadedFonts) - loadFonts(); - - TTF_Font* font = fonts[fontsize]; - - //SDL_Color is a struct of four bytes, with the first three being colors. An int is four bytes. - //So, we can just pretend the int is an SDL_Color. - //SDL_Color* sdlcolor = (SDL_Color*)&color; - SDL_Color sdlcolor = getSDLColor(color); - - SDL_Surface* textSurf = TTF_RenderText_Blended(font, text.c_str(), sdlcolor); - if(textSurf == NULL) - { - std::cerr << "Error - could not render text \"" << text << "\" to surface!\n"; - std::cerr << TTF_GetError() << "\n"; - return; - } - - SDL_Rect dest = {x, y}; - SDL_BlitSurface(textSurf, NULL, screen, &dest); - SDL_FreeSurface(textSurf); -} - -void Renderer::drawCenteredText(std::string text, int xOffset, int y, int color, FontSize fontsize) -{ - if(!loadedFonts) - loadFonts(); - - TTF_Font* font = fonts[fontsize]; - - int w, h; - TTF_SizeText(font, text.c_str(), &w, &h); - - int x = (int)getScreenWidth() - w; - x *= 0.5; - - x += xOffset * 0.5; - - drawText(text, x, y, color, fontsize); -} - -//this could probably be optimized -//draws text and ensures it's never longer than xLen -void Renderer::drawWrappedText(std::string text, int xStart, int yStart, int xLen, int color, FontSize fontsize) -{ - if(!loadedFonts) - loadFonts(); - - TTF_Font* font = fonts[fontsize]; - - int y = yStart; - - std::string line, word, temp; - int w, h; - size_t space, newline; - - while(text.length() > 0 || !line.empty()) //while there's text or we still have text to render - { - space = text.find(' ', 0); - if(space == std::string::npos) - space = text.length() - 1; - - - word = text.substr(0, space + 1); - - //check if the next word contains a newline - newline = word.find('\n', 0); - if(newline != std::string::npos) - { - word = word.substr(0, newline); - text.erase(0, newline + 1); - }else{ - text.erase(0, space + 1); - } - - temp = line + word; - - TTF_SizeText(font, temp.c_str(), &w, &h); - - //if we're on the last word and it'll fit on the line, just add it to the line - if((w <= xLen && text.length() == 0) || newline != std::string::npos) - { - line = temp; - word = ""; - } - - - //if the next line will be too long or we're on the last of the text, render it - if(w > xLen || text.length() == 0 || newline != std::string::npos) - { - //render line now - if(w > 0) //make sure it's not blank - drawText(line, xStart, y, color, fontsize); - - //increment y by height and some extra padding for the next line - y += h + 4; - - //move the word we skipped to the next line - line = word; - }else{ - //there's still space, continue building the line - line = temp; - } - - } -} diff --git a/src/Renderer_draw_gl.cpp b/src/Renderer_draw_gl.cpp new file mode 100644 index 000000000..08336fc3c --- /dev/null +++ b/src/Renderer_draw_gl.cpp @@ -0,0 +1,188 @@ +#include "Renderer.h" +#include +#include +#include "Font.h" + +namespace Renderer { + bool loadedFonts = false; + + unsigned int getScreenWidth() { return 1680; } + unsigned int getScreenHeight() { return 1050; } + + void setColor4bArray(GLubyte* array, int color) + { + array[0] = (color & 0x00ff0000) / 0x10000; + array[1] = (color & 0x0000ff00) / 0x100; + array[2] = (color & 0x000000ff); + array[3] = 255; + } + + void buildGLColorArray(GLubyte* ptr, int color, unsigned int vertCount) + { + for(unsigned int i = 0; i < vertCount; i++) + { + setColor4bArray(ptr, color); + ptr += 4; + } + } + + void drawRect(int x, int y, int w, int h, int color) + { + GLfloat points[12]; + + points[0] = x; points [1] = y; + points[2] = x; points[3] = y + h; + points[4] = x + w; points[5] = y; + + points[6] = x + w; points[7] = y; + points[8] = x; points[9] = y + h; + points[10] = x + w; points[11] = y + h; + + GLubyte colors[6*4]; + buildGLColorArray(colors, color, 6); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, points); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } + + Font* defaultFont = NULL; + void loadFonts() + { + std::cout << "loading fonts\n"; + + if(defaultFont != NULL) + delete defaultFont; + + defaultFont = new Font("LinLibertine_R.ttf", 12); + + loadedFonts = true; + } + + void unloadFonts() + { + if(defaultFont) + { + delete defaultFont; + defaultFont = NULL; + } + + loadedFonts = false; + } + + Font* getFont(FontSize size) + { + return defaultFont; + } + + + int getFontHeight(FontSize size) + { + if(!loadedFonts) + loadFonts(); + + int h; + getFont(size)->sizeText("", NULL, &h); + + return h; + } + + void drawText(std::string text, int x, int y, int color, FontSize font) + { + if(!loadedFonts) + loadFonts(); + + getFont(font)->drawText(text, x, y, color); + } + + void drawCenteredText(std::string text, int xOffset, int y, int color, FontSize fontsize) + { + if(!loadedFonts) + loadFonts(); + + Font* font = getFont(fontsize); + + int w, h; + font->sizeText(text, &w, &h); + + int x = (int)getScreenWidth() - w; + x *= 0.5; + + x += xOffset * 0.5; + + drawText(text, x, y, color, fontsize); + } + + //this could probably be optimized + //draws text and ensures it's never longer than xLen + void drawWrappedText(std::string text, int xStart, int yStart, int xLen, int color, FontSize fontsize) + { + if(!loadedFonts) + loadFonts(); + + Font* font = getFont(fontsize); + + int y = yStart; + + std::string line, word, temp; + int w, h; + size_t space, newline; + + while(text.length() > 0 || !line.empty()) //while there's text or we still have text to render + { + space = text.find(' ', 0); + if(space == std::string::npos) + space = text.length() - 1; + + + word = text.substr(0, space + 1); + + //check if the next word contains a newline + newline = word.find('\n', 0); + if(newline != std::string::npos) + { + word = word.substr(0, newline); + text.erase(0, newline + 1); + }else{ + text.erase(0, space + 1); + } + + temp = line + word; + + font->sizeText(temp, &w, &h); + + //if we're on the last word and it'll fit on the line, just add it to the line + if((w <= xLen && text.length() == 0) || newline != std::string::npos) + { + line = temp; + word = ""; + } + + + //if the next line will be too long or we're on the last of the text, render it + if(w > xLen || text.length() == 0 || newline != std::string::npos) + { + //render line now + if(w > 0) //make sure it's not blank + drawText(line, xStart, y, color, fontsize); + + //increment y by height and some extra padding for the next line + y += h + 4; + + //move the word we skipped to the next line + line = word; + }else{ + //there's still space, continue building the line + line = temp; + } + + } + } + +}; diff --git a/src/Renderer_init_rpi.cpp b/src/Renderer_init_rpi.cpp new file mode 100644 index 000000000..6ecf3534c --- /dev/null +++ b/src/Renderer_init_rpi.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include "Font.h" + +namespace Renderer +{ + EGLDisplay display; + EGLSurface surface; + EGLContext context; + EGLConfig config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + bool createSurface() + { + std::cout << "Creating surface...\n"; + + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if(display == EGL_NO_DISPLAY) + { + std::cerr << "Error getting display!\n"; + return false; + } + + bool result = eglInitialize(display, NULL, NULL); + if(result == EGL_FALSE) + { + std::cerr << "Error initializing display!\n"; + return false; + } + + result = eglBindAPI(EGL_OPENGL_ES_API); + if(result == EGL_FALSE) + { + std::cerr << "Error binding API!\n"; + return false; + } + + + static const EGLint config_attributes[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + GLint numConfigs; + result = eglChooseConfig(display, config_attributes, &config, 1, &numConfigs); + + if(result == EGL_FALSE) + { + std::cerr << "Error choosing config!\n"; + return false; + } + std::cout << "numConfigs: " << numConfigs << "\n"; + + + context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL); + if(context == EGL_NO_CONTEXT) + { + std::cout << "Error: " << eglGetError() << "\n"; + + std::cerr << "Error getting context!\n"; + return false; + } + + + + + + unsigned int display_width; + unsigned int display_height; + + bool success = graphics_get_display_size(0, &display_width, &display_height); //0 = LCD + + if(success < 0) + { + std::cerr << "Error getting display size!\n"; + return false; + } + + std::cout << "Display size is " << display_width << "x" << display_height << "\n"; + + + dst_rect.x = 0; dst_rect.y = 0; + dst_rect.width = display_width; dst_rect.height = display_height; + + src_rect.x = 0; src_rect.y = 0; + src_rect.width = display_width << 16; src_rect.height = display_height << 16; + + dispman_display = vc_dispmanx_display_open(0); //0 = LCD + dispman_update = vc_dispmanx_update_start(0); + + dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer*/, &dst_rect, 0 /*src*/, &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0 /*clamp*/, DISPMANX_NO_ROTATE /*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = display_width; nativewindow.height = display_height; + vc_dispmanx_update_submit_sync(dispman_update); + + + + + surface = eglCreateWindowSurface(display, config, &nativewindow, NULL); + if(surface == EGL_NO_SURFACE) + { + std::cerr << "Error creating window surface!\n"; + return false; + } + + result = eglMakeCurrent(display, surface, surface, context); + if(result == EGL_FALSE) + { + std::cerr << "Error with eglMakeCurrent!\n"; + return false; + } + + + std::cout << "success!\n"; + + return true; + } + + void swapBuffers() + { + eglSwapBuffers(display, surface); + glClear(GL_COLOR_BUFFER_BIT); + } + + void destroySurface() + { + eglSwapBuffers(display, surface); + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, surface); + eglDestroyContext(display, context); + eglTerminate(display); + } + + bool init(int w, int h) + { + bcm_host_init(); + bool surface = createSurface(); + + if(!surface) + return false; + + Font::initLibrary(); + + glViewport(0, 0, 1680, 1050); + glOrthof(0, 1680, 1050, 0, -1.0, 1.0); + glClearColor(0.5f, 1.0f, 1.0f, 1.0f); + + return true; + } + + void unloadFonts(); + void deinit() + { + unloadFonts(); + destroySurface(); + bcm_host_deinit(); + } +}; diff --git a/src/SystemData.cpp b/src/SystemData.cpp index cf7e26736..54e0e1030 100644 --- a/src/SystemData.cpp +++ b/src/SystemData.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "Renderer.h" std::vector SystemData::sSystemVector; @@ -70,6 +71,8 @@ void SystemData::launchGame(GameData* game) //suspend SDL joystick events (these'll pile up even while something else is running) SDL_JoystickEventState(0); + Renderer::deinit(); + std::string command = mLaunchCommand; command = strreplace(command, "%ROM%", game->getBashPath()); @@ -81,6 +84,8 @@ void SystemData::launchGame(GameData* game) std::cout << "...launch terminated!\n"; + Renderer::init(0, 0); + //re-enable SDL joystick events SDL_JoystickEventState(1); } diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp index e463713fb..4041ed0d8 100644 --- a/src/components/GuiGameList.cpp +++ b/src/components/GuiGameList.cpp @@ -4,8 +4,6 @@ #include "GuiMenu.h" #include -//#define DRAWFRAMERATE - #ifdef DRAWFRAMERATE #include extern float FRAMERATE; @@ -88,7 +86,7 @@ void GuiGameList::setSystemId(int id) void GuiGameList::onRender() { - Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFF); + //Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFF); if(mTheme) mTheme->render(); diff --git a/src/components/GuiGameList.h b/src/components/GuiGameList.h index d5536cf34..e2456b82c 100644 --- a/src/components/GuiGameList.h +++ b/src/components/GuiGameList.h @@ -11,6 +11,8 @@ #include "../GameData.h" #include "../FolderData.h" +#define DRAWFRAMERATE + class GuiGameList : GuiComponent { public: diff --git a/src/components/GuiImage.cpp b/src/components/GuiImage.cpp index 40d33920b..36e5310f7 100644 --- a/src/components/GuiImage.cpp +++ b/src/components/GuiImage.cpp @@ -1,15 +1,13 @@ #include "GuiImage.h" -#include -#include #include #include -int GuiImage::getWidth() { if(mSurface) return mSurface->w; else return 0; } -int GuiImage::getHeight() { if(mSurface) return mSurface->h; else return 0; } +int GuiImage::getWidth() { return mWidth; } +int GuiImage::getHeight() { return 100; } GuiImage::GuiImage(int offsetX, int offsetY, std::string path, unsigned int maxWidth, unsigned int maxHeight, bool resizeExact) { - mSurface = NULL; + mTextureID = 0; mOffsetX = offsetX; mOffsetY = offsetY; @@ -18,6 +16,9 @@ GuiImage::GuiImage(int offsetX, int offsetY, std::string path, unsigned int maxW mOriginX = 0.5; mOriginY = 0.5; + mWidth = 0; + mHeight = 0; + mTiled = false; mMaxWidth = maxWidth; @@ -32,92 +33,96 @@ GuiImage::GuiImage(int offsetX, int offsetY, std::string path, unsigned int maxW GuiImage::~GuiImage() { - if(mSurface) - SDL_FreeSurface(mSurface); + unloadImage(); } void GuiImage::loadImage(std::string path) { - if(boost::filesystem::exists(path)) + //make sure the file *exists* + if(!boost::filesystem::exists(path)) { - //start loading the image - SDL_Surface* newSurf = IMG_Load(path.c_str()); - - //if we started loading something else or failed to load, don't bother resizing - if(newSurf == NULL) - { - std::cerr << "Error loading image.\n"; - return; - } - - - resizeSurface(&newSurf); - - //convert it into display format for faster rendering - SDL_Surface* dispSurf; - if(mUseAlpha) - dispSurf = SDL_DisplayFormatAlpha(newSurf); - else - dispSurf = SDL_DisplayFormat(newSurf); - - SDL_FreeSurface(newSurf); - newSurf = dispSurf; - - - //finally set the image and delete the old one - if(mSurface) - SDL_FreeSurface(mSurface); - - mSurface = newSurf; - - updateRect(); - - }else{ std::cerr << "File \"" << path << "\" not found!\n"; + return; } + + FREE_IMAGE_FORMAT format = FIF_UNKNOWN; + FIBITMAP* image = NULL; + BYTE* imageData = NULL; + unsigned int width, height; + + //detect the filetype + //format = FreeImage_GetFileType(path.c_str(), 0); + if(format == FIF_UNKNOWN) + format = FreeImage_GetFIFFromFilename(path.c_str()); + if(format == FIF_UNKNOWN) + { + std::cerr << "Error - could not detect filetype for image \"" << path << "\"!\n"; + return; + } + + + //make sure we can read this filetype first, then load it + if(FreeImage_FIFSupportsReading(format)) + { + std::cout << "Loading image..."; + image = FreeImage_Load(format, path.c_str()); + std::cout << "success\n"; + }else{ + std::cerr << "Error - file format reading not supported for image \"" << path << "\"!\n"; + return; + } + + //make sure it loaded properly + if(!image) + { + std::cerr << "Error loading image \"" << path << "\"!\n"; + return; + } + + imageData = FreeImage_GetBits(image); + if(!imageData) + { + std::cerr << "Error retriving bits from image \"" << path << "\"!\n"; + return; + } + + + + width = FreeImage_GetWidth(image); + height = FreeImage_GetHeight(image); + + if(!width || !height) + { + std::cerr << "Width or height are zero for image \"" << path << "\"!\n"; + return; + } + + //force power of two for testing + width = 512; height = 512; + + //now for the openGL texture stuff + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData); + + mWidth = width; + mHeight = height; + + //free the image data + FreeImage_Unload(image); + + std::cout << "Image load successful, w: " << mWidth << " h: " << mHeight << " texID: " << mTextureID << "\n"; } -//enjoy this overly-complicated pointer stuff that results from splitting a function too late -void GuiImage::resizeSurface(SDL_Surface** surfRef) +void GuiImage::unloadImage() { - if(mTiled) - return; - - SDL_Surface* newSurf = *surfRef; - if(mResizeExact) + if(mTextureID) { - double scaleX = (double)mMaxWidth / (double)newSurf->w; - double scaleY = (double)mMaxHeight / (double)newSurf->h; + std::cout << "deleting texture\n"; + glDeleteTextures(1, &mTextureID); - if(scaleX == 0) - scaleX = scaleY; - if(scaleY == 0) - scaleY = scaleX; - - SDL_Surface* resSurf = zoomSurface(newSurf, scaleX, scaleY, SMOOTHING_OFF); - SDL_FreeSurface(newSurf); - newSurf = resSurf; - }else{ - if(mMaxWidth && newSurf->w > mMaxWidth) - { - double scale = (double)mMaxWidth / (double)newSurf->w; - - SDL_Surface* resSurf = zoomSurface(newSurf, scale, scale, SMOOTHING_OFF); - SDL_FreeSurface(newSurf); - newSurf = resSurf; - } - - if(mMaxHeight && newSurf->h > mMaxHeight) - { - double scale = (double)mMaxHeight / (double)newSurf->h; - - SDL_Surface* resSurf = zoomSurface(newSurf, scale, scale, SMOOTHING_OFF); - SDL_FreeSurface(newSurf); - newSurf = resSurf; - } + mTextureID = 0; } - - *surfRef = newSurf; } void GuiImage::setImage(std::string path) @@ -127,32 +132,16 @@ void GuiImage::setImage(std::string path) mPath = path; - if(mSurface) - { - SDL_FreeSurface(mSurface); - mSurface = NULL; - } - + unloadImage(); if(!path.empty()) loadImage(path); } -void GuiImage::updateRect() -{ - mRect.x = mOffsetX /*- mSurface->w*/ - (mSurface->w * mOriginX); - mRect.y = mOffsetY - (mSurface->h * mOriginY); - mRect.w = mSurface->w; - mRect.h = mSurface->h; -} - void GuiImage::setOrigin(float originX, float originY) { mOriginX = originX; mOriginY = originY; - - if(mSurface) - updateRect(); } void GuiImage::setTiling(bool tile) @@ -166,36 +155,51 @@ void GuiImage::setTiling(bool tile) void GuiImage::setAlpha(bool useAlpha) { mUseAlpha = useAlpha; - - if(mSurface) - { - SDL_FreeSurface(mSurface); - mSurface = NULL; - loadImage(mPath); - } } -bool dbg = false; - void GuiImage::onRender() { - if(mSurface) + if(mTextureID) { - if(mTiled) - { - SDL_Rect rect = mRect; - for(int x = 0; x < mMaxWidth / mSurface->w + 0.5; x++) - { - for(int y = 0; y < mMaxHeight / mSurface->h + 0.5; y++) - { - SDL_BlitSurface(mSurface, NULL, Renderer::screen, &rect); - rect.y += mSurface->h; - } - rect.x += mSurface->w; - rect.y = mRect.y; - } - }else{ - SDL_BlitSurface(mSurface, NULL, Renderer::screen, &mRect); - } + glBindTexture(GL_TEXTURE_2D, mTextureID); + glEnable(GL_TEXTURE_2D); + + + GLfloat points[12]; + points[0] = mOffsetX - (mWidth * mOriginX); points[1] = mOffsetY - (mHeight * mOriginY); + points[2] = mOffsetX - (mWidth * mOriginX); points[3] = mOffsetY + (mHeight * (1 - mOriginY)); + points[4] = mOffsetX + (mWidth * (1 - mOriginX)); points[5] = mOffsetY - (mHeight * mOriginY); + + points[6] = mOffsetX + (mWidth * (1 - mOriginX)); points[7] = mOffsetY - (mHeight * mOriginY); + points[8] = mOffsetX - (mWidth * mOriginX); points[9] = mOffsetY + (mHeight * (1 - mOriginY)); + points[10] = mOffsetX + (mWidth * (1 -mOriginX)); points[11] = mOffsetY + (mHeight * (1 - mOriginY)); + + //std::cout << "x: " << points[0] << " y: " << points[1] << " to x: " << points[10] << " y: " << points[11] << std::endl; + //std::cout << "(w: " << mWidth << " h: " << mHeight << ")" << std::endl; + + GLfloat texs[12]; + texs[0] = 0; texs[1] = 0; + texs[2] = 0; texs[3] = 1; + texs[4] = 1; texs[5] = 0; + + texs[6] = 1; texs[7] = 0; + texs[8] = 0; texs[9] = 1; + texs[10] = 1; texs[11] = 1; + + + + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, points); + glTexCoordPointer(2, GL_FLOAT, 0, texs); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glDisable(GL_TEXTURE_2D); } } diff --git a/src/components/GuiImage.h b/src/components/GuiImage.h index 9ca600488..d0982cd08 100644 --- a/src/components/GuiImage.h +++ b/src/components/GuiImage.h @@ -2,9 +2,9 @@ #define _GUIIMAGE_H_ #include "../GuiComponent.h" -#include -#include #include +#include +#include class GuiImage : public GuiComponent { @@ -28,14 +28,14 @@ private: bool mResizeExact, mTiled, mUseAlpha; void loadImage(std::string path); - void resizeSurface(SDL_Surface** surfRef); - void updateRect(); + void unloadImage(); std::string mPath; - SDL_Surface* mSurface; int mOffsetX, mOffsetY; - SDL_Rect mRect; + unsigned int mWidth, mHeight; + + GLuint mTextureID; }; #endif diff --git a/src/main.cpp b/src/main.cpp index 5b783d077..f653b7e7c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,10 @@ #include -#include -#include #include "Renderer.h" #include "components/GuiGameList.h" #include "SystemData.h" #include #include "components/GuiInputConfig.h" +#include bool PARSEGAMELISTONLY = false; bool IGNOREGAMELIST = false; @@ -29,13 +28,9 @@ int main(int argc, char* argv[]) std::cerr << "Are you in the 'video' and 'input' groups? Are you running with X closed?\n"; return 1; } - if(TTF_Init() != 0) - { - std::cerr << "Error - could not initialize SDL_ttf!\n"; - std::cerr << " " << TTF_GetError() << "\n"; - return 1; - } + SDL_Surface* sdlScreen = SDL_SetVideoMode(1, 1, 0, SDL_SWSURFACE); + std::cout << "Fake SDL window is " << sdlScreen->w << "x" << sdlScreen->h << "\n"; int width = 0; int height = 0; @@ -61,21 +56,14 @@ int main(int argc, char* argv[]) } } - Renderer::screen = SDL_SetVideoMode(width, height, 16, SDL_SWSURFACE); //| SDL_FULLSCREEN); - - if(Renderer::screen == NULL) + if(!Renderer::init(width, height)) { - std::cerr << "Error - could not set video mode!\n"; - std::cerr << " " << SDL_GetError() << "\n"; - std::cerr << "\nYou may want to try using -w and -h to specify a resolution.\n"; + std::cerr << "Error initializing renderer!\n"; return 1; - }else{ - std::cout << "Video mode is " << Renderer::screen->w << "x" << Renderer::screen->h << "\n"; } - SDL_ShowCursor(false); - SDL_JoystickEventState(SDL_ENABLE); + SDL_JoystickEventState(SDL_ENABLE); //make sure the config directory exists @@ -187,15 +175,16 @@ int main(int argc, char* argv[]) GuiComponent::processTicks(deltaTime); Renderer::render(); - SDL_Flip(Renderer::screen); + Renderer::swapBuffers(); } + Renderer::deleteAll(); + Renderer::deinit(); SystemData::deleteSystems(); std::cout << "EmulationStation cleanly shutting down...\n"; - TTF_Quit(); SDL_Quit(); return 0; }