diff --git a/src/AudioManager.cpp b/src/AudioManager.cpp index c76e256b5..4cc5dcf37 100644 --- a/src/AudioManager.cpp +++ b/src/AudioManager.cpp @@ -28,17 +28,13 @@ void AudioManager::mixAudio(void *unused, Uint8 *stream, int len) } //mix sample into stream SDL_MixAudio(stream, &(sound->getData()[sound->getPosition()]), restLength, SDL_MIX_MAXVOLUME); - if (sound->getPosition() + restLength >= sound->getLength()) - { - //if the position is beyond the end of the buffer, stop playing the sample - sound->stop(); - } - else + if (sound->getPosition() + restLength < sound->getLength()) { //sample hasn't ended yet - sound->setPosition(sound->getPosition() + restLength); stillPlaying = true; } + //set new sound position. if this is at or beyond the end of the sample, it will stop automatically + sound->setPosition(sound->getPosition() + restLength); } //advance to next sound ++soundIt; diff --git a/src/Font.cpp b/src/Font.cpp index 89ae1c8f3..02eff9d3b 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -70,6 +70,7 @@ void Font::initLibrary() } Font::Font(std::string path, int size) + : fontScale(1.0f) { mPath = path; mSize = size; @@ -84,18 +85,7 @@ void Font::init() mMaxGlyphHeight = 0; - if(FT_New_Face(sLibrary, mPath.c_str(), 0, &face)) - { - LOG(LogError) << "Error creating font face! (path: " << mPath.c_str(); - return; - } - - //FT_Set_Char_Size(face, 0, size * 64, getDpiX(), getDpiY()); - FT_Set_Pixel_Sizes(face, 0, mSize); - buildAtlas(); - - FT_Done_Face(face); } void Font::deinit() @@ -106,6 +96,15 @@ void Font::deinit() void Font::buildAtlas() { + if(FT_New_Face(sLibrary, mPath.c_str(), 0, &face)) + { + LOG(LogError) << "Error creating font face! (path: " << mPath.c_str(); + return; + } + + //FT_Set_Char_Size(face, 0, size * 64, getDpiX(), getDpiY()); + FT_Set_Pixel_Sizes(face, 0, mSize); + //find the size we should use FT_GlyphSlot g = face->glyph; int w = 0; @@ -130,8 +129,6 @@ void Font::buildAtlas() textureWidth = w; textureHeight = h; - - //create the texture glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); @@ -171,7 +168,7 @@ void Font::buildAtlas() if(x + g->bitmap.width >= textureWidth) { x = 0; - y += maxHeight; + y += maxHeight + 1; //leave one pixel of space between glyphs maxHeight = 0; } @@ -185,23 +182,34 @@ void Font::buildAtlas() charData[i].texY = y; 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; + charData[i].advX = g->metrics.horiAdvance / 64.0f; + charData[i].advY = g->metrics.vertAdvance / 64.0f; + charData[i].bearingY = g->metrics.horiBearingY / 64.0f; if(charData[i].texH > mMaxGlyphHeight) mMaxGlyphHeight = charData[i].texH; - x += g->bitmap.width; - } - - if(y >= textureHeight) - { - LOG(LogError) << "Error - font size exceeded texture size! If you were doing something reasonable, tell Aloshi to fix it!"; + x += g->bitmap.width + 1; //leave one pixel of space between glyphs } glBindTexture(GL_TEXTURE_2D, 0); + FT_Done_Face(face); + + if((y + maxHeight) >= textureHeight) + { + //failed to create a proper font texture + LOG(LogError) << "Error - font with size " << mSize << " exceeded texture size! Trying again..."; + //try a 3/4th smaller size and redo initialization + fontScale *= 1.25f; + mSize = (int)(mSize * (1.0f / fontScale)); + deinit(); + init(); + } + else { + LOG(LogInfo) << "Created font with size " << mSize << std::endl; + } + //std::cout << "generated texture \"" << textureID << "\" (w: " << w << " h: " << h << ")" << std::endl; } @@ -249,62 +257,62 @@ void Font::drawText(std::string text, int startx, int starty, int color) return; } - - starty += mMaxGlyphHeight; - - //padding (another 0.5% is added to the bottom through the sizeText function) - starty += (int)(mMaxGlyphHeight * 0.1f); - - int pointCount = text.length() * 2; point* points = new point[pointCount]; tex* texs = new tex[pointCount]; GLubyte* colors = new GLubyte[pointCount * 3 * 4]; - 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 = (float)startx; - float y = (float)starty; - for(; p < pointCount; i++, p++) + float y = starty + mMaxGlyphHeight * 1.1f * fontScale; //padding (another 0.5% is added to the bottom through the sizeText function) + + int p = 0; + for(int i = 0; p < pointCount; i++, p++) { unsigned char letter = text[i]; if(letter < 32 || letter >= 128) letter = 127; //print [X] if character is not standard ASCII - 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; + points[p].pos0x = x; + points[p].pos0y = y + (charData[letter].texH - charData[letter].bearingY) * fontScale; + points[p].pos1x = x + charData[letter].texW * fontScale; + points[p].pos1y = y - charData[letter].bearingY * fontScale; + points[p].pos2x = x; + points[p].pos2y = points[p].pos1y; + 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].tex0x; + texs[p].tex2y = texs[p].tex1y; 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; + points[p].pos0x = points[p-1].pos0x; + points[p].pos0y = points[p-1].pos0y; + points[p].pos1x = points[p-1].pos1x; + points[p].pos1y = points[p-1].pos1y; + points[p].pos2x = points[p-1].pos1x; + points[p].pos2y = points[p-1].pos0y; - 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; + texs[p].tex0x = texs[p-1].tex0x; + texs[p].tex0y = texs[p-1].tex0y; + texs[p].tex1x = texs[p-1].tex1x; + texs[p].tex1y = texs[p-1].tex1y; + texs[p].tex2x = texs[p-1].tex1x; + texs[p].tex2y = texs[p-1].tex0y; - - x += charData[letter].advX; + x += charData[letter].advX * fontScale; } Renderer::buildGLColorArray(colors, color, pointCount * 3); @@ -340,18 +348,17 @@ void Font::sizeText(std::string text, int* w, int* h) if(letter < 32 || letter >= 128) letter = 127; - cwidth += charData[letter].advX; + cwidth += charData[letter].advX * fontScale; } - if(w != NULL) *w = cwidth; if(h != NULL) - *h = (int)(mMaxGlyphHeight + mMaxGlyphHeight * 0.5f); + *h = (int)(mMaxGlyphHeight * 1.5f * fontScale); } int Font::getHeight() { - return (int)(mMaxGlyphHeight * 1.5f); + return (int)(mMaxGlyphHeight * 1.5f * fontScale); } diff --git a/src/Font.h b/src/Font.h index 971d5128f..22644bdea 100644 --- a/src/Font.h +++ b/src/Font.h @@ -26,10 +26,10 @@ public: int texW; int texH; - int advX; - int advY; + float advX; + float advY; - int bearingY; + float bearingY; }; charPosData charData[128]; @@ -58,6 +58,7 @@ private: int textureWidth; //OpenGL texture width int textureHeight; //OpenGL texture height int mMaxGlyphHeight; + float fontScale; //! 1.0 if the font would be to big for the texture std::string mPath; int mSize; diff --git a/src/ImageIO.cpp b/src/ImageIO.cpp index 4f63e9537..f6f599817 100644 --- a/src/ImageIO.cpp +++ b/src/ImageIO.cpp @@ -20,19 +20,29 @@ std::vector ImageIO::loadFromMemoryRGBA32(const unsigned char * d FIBITMAP * fiBitmap = FreeImage_LoadFromMemory(format, fiMemory); if (fiBitmap != nullptr) { - //loaded. convert to 32bit - FIBITMAP * fiConverted = FreeImage_ConvertTo32Bits(fiBitmap); - if (fiConverted != nullptr) + //loaded. convert to 32bit if necessary + FIBITMAP * fiConverted = nullptr; + if (FreeImage_GetBPP(fiBitmap) != 32) { - width = FreeImage_GetWidth(fiConverted); - height = FreeImage_GetHeight(fiConverted); - unsigned int pitch = FreeImage_GetPitch(fiConverted); + FIBITMAP * fiConverted = FreeImage_ConvertTo32Bits(fiBitmap); + if (fiConverted != nullptr) + { + //free original bitmap data + FreeImage_Unload(fiBitmap); + fiBitmap = fiConverted; + } + } + if (fiBitmap != nullptr) + { + width = FreeImage_GetWidth(fiBitmap); + height = FreeImage_GetHeight(fiBitmap); + unsigned int pitch = FreeImage_GetPitch(fiBitmap); //loop through scanlines and add all pixel data to the return vector //this is necessary, because width*height*bpp might not be == pitch unsigned char * tempData = new unsigned char[width * height * 4]; for (size_t i = 0; i < height; i++) { - const BYTE * scanLine = FreeImage_GetScanLine(fiConverted, i); + const BYTE * scanLine = FreeImage_GetScanLine(fiBitmap, i); memcpy(tempData + (i * width * 4), scanLine, width * 4); } //convert from BGRA to RGBA @@ -47,11 +57,13 @@ std::vector ImageIO::loadFromMemoryRGBA32(const unsigned char * d ((RGBQUAD *)tempData)[i] = rgba; } rawData = std::vector(tempData, tempData + width * height * 4); - //free converted data - FreeImage_Unload(fiConverted); + //free bitmap data + FreeImage_Unload(fiBitmap); } - //free bitmap data - FreeImage_Unload(fiBitmap); + } + else + { + LOG(LogError) << "Error - Failed to load image from memory!"; } } else diff --git a/src/Renderer_init_sdlgl.cpp b/src/Renderer_init_sdlgl.cpp index f38b89b1b..f3500538d 100644 --- a/src/Renderer_init_sdlgl.cpp +++ b/src/Renderer_init_sdlgl.cpp @@ -41,7 +41,7 @@ namespace Renderer } //ATM it is best to just leave the window icon alone on windows. - //When compiled as a Windows application, ES at least has an icon int the taskbar + //When compiled as a Windows application, ES at least has an icon in the taskbar //The method below looks pretty shite as alpha isn't taken into account... #ifndef WIN32 //try loading PNG from memory @@ -51,9 +51,9 @@ namespace Renderer if (!rawData.empty()) { //SDL interprets each pixel as a 32-bit number, so our masks must depend on the endianness (byte order) of the machine #if SDL_BYTEORDER == SDL_BIG_ENDIAN - Uint32 rmask = 0xff000000; Uint32 gmask = 0x00ff0000; Uint32 bmask = 0x0000ff00; Uint32 amask = 0x000000ff; + Uint32 rmask = 0xff000000; Uint32 gmask = 0x0000ff00; Uint32 bmask = 0x00ff0000; Uint32 amask = 0x000000ff; #else - Uint32 rmask = 0x000000ff; Uint32 gmask = 0x0000ff00; Uint32 bmask = 0x00ff0000;Uint32 amask = 0xff000000; + Uint32 rmask = 0x000000ff; Uint32 gmask = 0x00ff0000; Uint32 bmask = 0x0000ff00; Uint32 amask = 0xff000000; #endif //try creating SDL surface from logo data SDL_Surface * logoSurface = SDL_CreateRGBSurfaceFrom((void *)rawData.data(), width, height, 32, width*4, rmask, gmask, bmask, amask); diff --git a/src/Sound.cpp b/src/Sound.cpp index e908a7716..000d7dc65 100644 --- a/src/Sound.cpp +++ b/src/Sound.cpp @@ -82,18 +82,19 @@ void Sound::play() if(mSampleData == NULL) return; + SDL_LockAudio(); if (playing) { //replay from start. rewind the sample to the beginning - SDL_LockAudio(); mSamplePos = 0; - SDL_UnlockAudio(); + } else { //flag our sample as playing playing = true; } + SDL_UnlockAudio(); //tell the AudioManager to start playing samples AudioManager::getInstance()->play(); } @@ -126,6 +127,8 @@ void Sound::setPosition(Uint32 newPosition) { mSamplePos = newPosition; if (mSamplePos >= mSampleLength) { + //got to or beyond the end of the sample. stop playing + playing = false; mSamplePos = 0; } } diff --git a/src/VolumeControl.cpp b/src/VolumeControl.cpp index 95ac7535a..1097ae2e1 100644 --- a/src/VolumeControl.cpp +++ b/src/VolumeControl.cpp @@ -215,6 +215,8 @@ void VolumeControl::deinit() #error TODO: Not implemented for MacOS yet!!! #elif defined(__linux__) if (mixerHandle != nullptr) { + snd_mixer_detach(mixerHandle, mixerCard); + snd_mixer_free(mixerHandle); snd_mixer_close(mixerHandle); mixerHandle = nullptr; mixerElem = nullptr; diff --git a/src/main.cpp b/src/main.cpp index 789f81241..092ce19b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "Renderer.h" #include "components/GuiGameList.h" #include "SystemData.h" @@ -201,12 +202,22 @@ int main(int argc, char* argv[]) if(DRAWFRAMERATE) { - float framerate = 1/((float)deltaTime)*1000; - std::stringstream ss; - ss << framerate; - std::string fps; - ss >> fps; - Renderer::drawText(fps, 50, 50, 0x00FF00FF, Renderer::getDefaultFont(Renderer::MEDIUM)); + static int timeElapsed = 0; + static int nrOfFrames = 0; + static std::string fpsString; + + nrOfFrames++; + timeElapsed += deltaTime; + //wait until half a second has passed to recalculate fps + if (timeElapsed >= 500) { + std::stringstream ss; + ss << std::fixed << std::setprecision(1) << (1000.0f * (float)nrOfFrames / (float)timeElapsed) << "fps, "; + ss << std::fixed << std::setprecision(2) << ((float)timeElapsed / (float)nrOfFrames) << "ms"; + fpsString = ss.str(); + nrOfFrames = 0; + timeElapsed = 0; + } + Renderer::drawText(fpsString, 50, 50, 0x00FF00FF, Renderer::getDefaultFont(Renderer::MEDIUM)); } //sleep if we're past our threshold