diff --git a/source/code/CppSdlImageHolder.cpp b/source/code/CppSdlImageHolder.cpp index ea47f38..9f97d3f 100644 --- a/source/code/CppSdlImageHolder.cpp +++ b/source/code/CppSdlImageHolder.cpp @@ -38,7 +38,6 @@ CppSdlImageHolder::CppSdlImageHolder(std::string filename) { throw std::runtime_error(std::string("Could not read file \""+filename+"\"")); } SDL_GetClipRect(data,&area); - OptimizeForBlit(); } CppSdlImageHolder::CppSdlImageHolder(char* rawdata, int datasize) { diff --git a/source/code/Libs/SDL_FontCache.c b/source/code/Libs/SDL_FontCache.c deleted file mode 100644 index 4533d7a..0000000 --- a/source/code/Libs/SDL_FontCache.c +++ /dev/null @@ -1,2544 +0,0 @@ -/* -SDL_FontCache v0.0.1: A font cache for SDL and SDL_ttf -by Jonathan Dearborn -Dedicated to the memory of Florian Hufsky - -License: - The short: - Use it however you'd like, but keep the copyright and license notice - whenever these files or parts of them are distributed in uncompiled form. - - The long: -Copyright (c) 2015 Jonathan Dearborn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include "SDL_FontCache.h" - -#include -#include -#include - -// Visual C does not support static inline -#ifndef static_inline - #ifdef _MSC_VER - #define static_inline static - #else - #define static_inline static inline - #endif -#endif - -#if SDL_VERSION_ATLEAST(2,0,0) - #define FC_GET_ALPHA(sdl_color) ((sdl_color).a) -#else - #define FC_GET_ALPHA(sdl_color) ((sdl_color).unused) -#endif - -// Need SDL_RenderIsClipEnabled() for proper clipping support -#if SDL_VERSION_ATLEAST(2,0,4) - #define ENABLE_SDL_CLIPPING -#endif - -#define FC_MIN(a,b) ((a) < (b)? (a) : (b)) -#define FC_MAX(a,b) ((a) > (b)? (a) : (b)) - - -#define FC_EXTRACT_VARARGS(buffer, start_args) \ -{ \ - va_list lst; \ - va_start(lst, start_args); \ - vsprintf(buffer, start_args, lst); \ - va_end(lst); \ -} - -// Extra pixels of padding around each glyph to avoid linear filtering artifacts -#define FC_CACHE_PADDING 1 - - - -static Uint8 has_clip(FC_Target* dest) -{ - #ifdef FC_USE_SDL_GPU - return dest->use_clip_rect; - #elif defined(ENABLE_SDL_CLIPPING) - return SDL_RenderIsClipEnabled(dest); - #else - return 0; - #endif -} - -static FC_Rect get_clip(FC_Target* dest) -{ - #ifdef FC_USE_SDL_GPU - return dest->clip_rect; - #elif defined(ENABLE_SDL_CLIPPING) - SDL_Rect r; - SDL_RenderGetClipRect(dest, &r); - return r; - #else - SDL_Rect r = {0, 0, 0, 0}; - return r; - #endif -} - -static void set_clip(FC_Target* dest, FC_Rect* rect) -{ - #ifdef FC_USE_SDL_GPU - if(rect != NULL) - GPU_SetClipRect(dest, *rect); - else - GPU_UnsetClip(dest); - #elif defined(ENABLE_SDL_CLIPPING) - SDL_RenderSetClipRect(dest, rect); - #endif -} - -static void set_color(FC_Image* src, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - #ifdef FC_USE_SDL_GPU - GPU_SetRGBA(src, r, g, b, a); - #else - SDL_SetTextureColorMod(src, r, g, b); - SDL_SetTextureAlphaMod(src, a); - #endif -} - - - -static char* new_concat(const char* a, const char* b) -{ - // Create new buffer - unsigned int size = strlen(a) + strlen(b); - char* new_string = (char*)malloc(size+1); - - // Concatenate strings in the new buffer - strcpy(new_string, a); - strcat(new_string, b); - - return new_string; -} - -static char* replace_concat(char** a, const char* b) -{ - char* new_string = new_concat(*a, b); - free(*a); - *a = new_string; - return *a; -} - - - - - -// Shared buffer for variadic text -static char* fc_buffer = NULL; - -static Uint8 fc_has_render_target_support = 0; - -const char* FC_GetStringASCII(void) -{ - static char* buffer = NULL; - if(buffer == NULL) - { - int i; - char c; - buffer = (char*)malloc(512); - memset(buffer, 0, 512); - i = 0; - c = 32; - while(1) - { - buffer[i] = c; - if(c == 126) - break; - ++i; - ++c; - } - } - return buffer; -} - -const char* FC_GetStringLatin1(void) -{ - static char* buffer = NULL; - if(buffer == NULL) - { - int i; - unsigned char c; - buffer = (char*)malloc(512); - memset(buffer, 0, 512); - i = 0; - c = 0xA0; - while(1) - { - buffer[i] = 0xC2; - buffer[i+1] = c; - if(c == 0xBF) - break; - i += 2; - ++c; - } - i += 2; - c = 0x80; - while(1) - { - buffer[i] = 0xC3; - buffer[i+1] = c; - if(c == 0xBF) - break; - i += 2; - ++c; - } - } - return buffer; -} - -const char* FC_GetStringASCII_Latin1(void) -{ - static char* buffer = NULL; - if(buffer == NULL) - buffer = new_concat(FC_GetStringASCII(), FC_GetStringLatin1()); - - return buffer; -} - -FC_Rect FC_MakeRect(float x, float y, float w, float h) -{ - FC_Rect r = {x, y, w, h}; - return r; -} - -FC_Scale FC_MakeScale(float x, float y) -{ - FC_Scale s = {x, y}; - - return s; -} - -SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - SDL_Color c = {r, g, b, a}; - - return c; -} - -FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color) -{ - FC_Effect e; - - e.alignment = alignment; - e.scale = scale; - e.color = color; - - return e; -} - -FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h) -{ - FC_GlyphData gd; - - gd.rect.x = x; - gd.rect.y = y; - gd.rect.w = w; - gd.rect.h = h; - gd.cache_level = cache_level; - - return gd; -} - -// Enough to hold all of the ascii characters and some. -#define FC_DEFAULT_NUM_BUCKETS 300 - -typedef struct FC_MapNode -{ - Uint32 key; - FC_GlyphData value; - struct FC_MapNode* next; - -} FC_MapNode; - -typedef struct FC_Map -{ - int num_buckets; - FC_MapNode** buckets; -} FC_Map; - - - -static FC_Map* FC_MapCreate(int num_buckets) -{ - int i; - FC_Map* map = (FC_Map*)malloc(sizeof(FC_Map)); - - map->num_buckets = num_buckets; - map->buckets = (FC_MapNode**)malloc(num_buckets * sizeof(FC_MapNode*)); - - for(i = 0; i < num_buckets; ++i) - { - map->buckets[i] = NULL; - } - - return map; -} - -/*static void FC_MapClear(FC_Map* map) -{ - int i; - if(map == NULL) - return; - - // Go through each bucket - for(i = 0; i < map->num_buckets; ++i) - { - // Delete the nodes in order - FC_MapNode* node = map->buckets[i]; - while(node != NULL) - { - FC_MapNode* last = node; - node = node->next; - free(last); - } - // Set the bucket to empty - map->buckets[i] = NULL; - } -}*/ - -static void FC_MapFree(FC_Map* map) -{ - int i; - if(map == NULL) - return; - - // Go through each bucket - for(i = 0; i < map->num_buckets; ++i) - { - // Delete the nodes in order - FC_MapNode* node = map->buckets[i]; - while(node != NULL) - { - FC_MapNode* last = node; - node = node->next; - free(last); - } - } - - free(map->buckets); - free(map); -} - -// Note: Does not handle duplicates in any special way. -static FC_GlyphData* FC_MapInsert(FC_Map* map, Uint32 codepoint, FC_GlyphData glyph) -{ - Uint32 index; - FC_MapNode* node; - if(map == NULL) - return NULL; - - // Get index for bucket - index = codepoint % map->num_buckets; - - // If this bucket is empty, create a node and return its value - if(map->buckets[index] == NULL) - { - node = map->buckets[index] = (FC_MapNode*)malloc(sizeof(FC_MapNode)); - node->key = codepoint; - node->value = glyph; - node->next = NULL; - return &node->value; - } - - for(node = map->buckets[index]; node != NULL; node = node->next) - { - // Find empty node and add a new one on. - if(node->next == NULL) - { - node->next = (FC_MapNode*)malloc(sizeof(FC_MapNode)); - node = node->next; - - node->key = codepoint; - node->value = glyph; - node->next = NULL; - return &node->value; - } - } - - return NULL; -} - -static FC_GlyphData* FC_MapFind(FC_Map* map, Uint32 codepoint) -{ - Uint32 index; - FC_MapNode* node; - if(map == NULL) - return NULL; - - // Get index for bucket - index = codepoint % map->num_buckets; - - // Go through list until we find a match - for(node = map->buckets[index]; node != NULL; node = node->next) - { - if(node->key == codepoint) - return &node->value; - } - - return NULL; -} - - - -struct FC_Font -{ - #ifndef FC_USE_SDL_GPU - SDL_Renderer* renderer; - #endif - - TTF_Font* ttf_source; // TTF_Font source of characters - Uint8 owns_ttf_source; // Can we delete the TTF_Font ourselves? - - FC_FilterEnum filter; - - SDL_Color default_color; - Uint16 height; - - Uint16 maxWidth; - Uint16 baseline; - int ascent; - int descent; - - int lineSpacing; - int letterSpacing; - - // Uses 32-bit (4-byte) Unicode codepoints to refer to each glyph - // Codepoints are little endian (reversed from UTF-8) so that something like 0x00000005 is ASCII 5 and the map can be indexed by ASCII values - FC_Map* glyphs; - - FC_GlyphData last_glyph; // Texture packing cursor - int glyph_cache_size; - int glyph_cache_count; - FC_Image** glyph_cache; - - char* loading_string; - -}; - -// Private -static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight); - - -static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); -static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); -static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); - - -static_inline SDL_Surface* FC_CreateSurface32(Uint32 width, Uint32 height) -{ - #if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); - #else - return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); - #endif -} - - -char* U8_alloc(unsigned int size) -{ - char* result; - if(size == 0) - return NULL; - - result = (char*)malloc(size); - result[0] = '\0'; - - return result; -} - -void U8_free(char* string) -{ - free(string); -} - -char* U8_strdup(const char* string) -{ - char* result; - if(string == NULL) - return NULL; - - result = (char*)malloc(strlen(string)+1); - strcpy(result, string); - - return result; -} - -int U8_strlen(const char* string) -{ - int length = 0; - if(string == NULL) - return 0; - - while(*string != '\0') - { - string = U8_next(string); - ++length; - } - - return length; -} - -int U8_charsize(const char* character) -{ - if(character == NULL) - return 0; - - if((unsigned char)*character <= 0x7F) - return 1; - else if((unsigned char)*character < 0xE0) - return 2; - else if((unsigned char)*character < 0xF0) - return 3; - else - return 4; - return 1; -} - -int U8_charcpy(char* buffer, const char* source, int buffer_size) -{ - int charsize; - if(buffer == NULL || source == NULL || buffer_size < 1) - return 0; - - charsize = U8_charsize(source); - if(charsize > buffer_size) - return 0; - - memcpy(buffer, source, charsize); - return charsize; -} - -const char* U8_next(const char* string) -{ - return string + U8_charsize(string); -} - -int U8_strinsert(char* string, int position, const char* source, int max_bytes) -{ - int pos_bytes; - int len = strlen(string); - int add_len = strlen(source); - int ulen = U8_strlen(string); - - if(position == -1) - position = ulen; - - if(string == NULL || source == NULL || position < 0 || position > ulen || len + add_len + 1 > max_bytes) - return 0; - - // Move string pointer to the proper position - pos_bytes = 0; - while(*string != '\0' && pos_bytes < position) - { - string = (char*)U8_next(string); - ++pos_bytes; - } - - // Move the rest of the string out of the way - memmove(string + add_len, string, len - pos_bytes + 1); - - // Copy in the new characters - memcpy(string, source, add_len); - - return 1; -} - -void U8_strdel(char* string, int position) -{ - if(string == NULL || position < 0) - return; - - while(*string != '\0') - { - if(position == 0) - { - int chars_to_erase = U8_charsize(string); - int remaining_bytes = strlen(string) + 1; - memmove(string, string + chars_to_erase, remaining_bytes); - break; - } - - string = (char*)U8_next(string); - --position; - } -} - - - - - -static_inline FC_Rect FC_RectUnion(FC_Rect A, FC_Rect B) -{ - float x,x2,y,y2; - x = FC_MIN(A.x, B.x); - y = FC_MIN(A.y, B.y); - x2 = FC_MAX(A.x+A.w, B.x+B.w); - y2 = FC_MAX(A.y+A.h, B.y+B.h); - { - FC_Rect result = {x, y, FC_MAX(0, x2 - x), FC_MAX(0, y2 - y)}; - return result; - } -} - -// Adapted from SDL_IntersectRect -static_inline FC_Rect FC_RectIntersect(FC_Rect A, FC_Rect B) -{ - FC_Rect result; - float Amin, Amax, Bmin, Bmax; - - // Horizontal intersection - Amin = A.x; - Amax = Amin + A.w; - Bmin = B.x; - Bmax = Bmin + B.w; - if(Bmin > Amin) - Amin = Bmin; - result.x = Amin; - if(Bmax < Amax) - Amax = Bmax; - result.w = Amax - Amin > 0 ? Amax - Amin : 0; - - // Vertical intersection - Amin = A.y; - Amax = Amin + A.h; - Bmin = B.y; - Bmax = Bmin + B.h; - if(Bmin > Amin) - Amin = Bmin; - result.y = Amin; - if(Bmax < Amax) - Amax = Bmax; - result.h = Amax - Amin > 0 ? Amax - Amin : 0; - - return result; -} - - - - - - - - - - - - - - -FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) -{ - float w = srcrect->w * xscale; - float h = srcrect->h * yscale; - FC_Rect result; - - // FIXME: Why does the scaled offset look so wrong? - #ifdef FC_USE_SDL_GPU - { - GPU_Rect r = *srcrect; - GPU_BlitScale(src, &r, dest, x + xscale*r.w/2.0f, y + r.h/2.0f, xscale, yscale); - } - #else - { - SDL_RendererFlip flip = SDL_FLIP_NONE; - if(xscale < 0) - { - xscale = -xscale; - flip = flip | SDL_FLIP_HORIZONTAL; - } - if(yscale < 0) - { - yscale = -yscale; - flip = flip | SDL_FLIP_VERTICAL; - } - - SDL_Rect r = *srcrect; - SDL_Rect dr = {(int)x, (int)y, (int)(xscale*r.w), (int)(yscale*r.h)}; - SDL_RenderCopyEx(dest, src, &r, &dr, 0, NULL, flip); - } - #endif - - result.x = x; - result.y = y; - result.w = w; - result.h = h; - return result; -} - -static FC_Rect (*fc_render_callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) = &FC_DefaultRenderCallback; - -void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)) -{ - if(callback == NULL) - fc_render_callback = &FC_DefaultRenderCallback; - else - fc_render_callback = callback; -} - -void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint) -{ - char a, b, c, d; - - if(result == NULL) - return; - - a = (codepoint >> 24) & 0xFF; - b = (codepoint >> 16) & 0xFF; - c = (codepoint >> 8) & 0xFF; - d = codepoint & 0xFF; - - if(a == 0) - { - if(b == 0) - { - if(c == 0) - { - result[0] = d; - result[1] = '\0'; - } - else - { - result[0] = c; - result[1] = d; - result[2] = '\0'; - } - } - else - { - result[0] = b; - result[1] = c; - result[2] = d; - result[3] = '\0'; - } - } - else - { - result[0] = a; - result[1] = b; - result[2] = c; - result[3] = d; - result[4] = '\0'; - } -} - -Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer) -{ - Uint32 result = 0; - const char* str; - if(c == NULL || *c == NULL) - return 0; - - str = *c; - if((unsigned char)*str <= 0x7F) - result = *str; - else if((unsigned char)*str < 0xE0) - { - result |= (unsigned char)(*str) << 8; - result |= (unsigned char)(*(str+1)); - if(advance_pointer) - *c += 1; - } - else if((unsigned char)*str < 0xF0) - { - result |= (unsigned char)(*str) << 16; - result |= (unsigned char)(*(str+1)) << 8; - result |= (unsigned char)(*(str+2)); - if(advance_pointer) - *c += 2; - } - else - { - result |= (unsigned char)(*str) << 24; - result |= (unsigned char)(*(str+1)) << 16; - result |= (unsigned char)(*(str+2)) << 8; - result |= (unsigned char)(*(str+3)); - if(advance_pointer) - *c += 3; - } - return result; -} - - -void FC_SetLoadingString(FC_Font* font, const char* string) -{ - if(font == NULL) - return; - - free(font->loading_string); - font->loading_string = U8_strdup(string); -} - - -void FC_SetBufferSize(unsigned int size) -{ - free(fc_buffer); - if(size > 0) - fc_buffer = (char*)malloc(size); - else - fc_buffer = (char*)malloc(1024); -} - - - - - -// Constructors - -static void FC_Init(FC_Font* font) -{ - if(font == NULL) - return; - - #ifndef FC_USE_SDL_GPU - font->renderer = NULL; - #endif - - font->ttf_source = NULL; - font->owns_ttf_source = 0; - - font->filter = FC_FILTER_NEAREST; - - font->default_color.r = 0; - font->default_color.g = 0; - font->default_color.b = 0; - FC_GET_ALPHA(font->default_color) = 255; - - font->height = 0; // ascent+descent - - font->maxWidth = 0; - font->baseline = 0; - font->ascent = 0; - font->descent = 0; - - font->lineSpacing = 0; - font->letterSpacing = 0; - - // Give a little offset for when filtering/mipmaps are used. Depending on mipmap level, this will still not be enough. - font->last_glyph.rect.x = FC_CACHE_PADDING; - font->last_glyph.rect.y = FC_CACHE_PADDING; - font->last_glyph.rect.w = 0; - font->last_glyph.rect.h = 0; - font->last_glyph.cache_level = 0; - - if(font->glyphs != NULL) - FC_MapFree(font->glyphs); - - font->glyphs = FC_MapCreate(FC_DEFAULT_NUM_BUCKETS); - - font->glyph_cache_size = 3; - font->glyph_cache_count = 0; - - - font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*)); - - if(font->loading_string == NULL) - font->loading_string = U8_strdup(FC_GetStringASCII()); - - if(fc_buffer == NULL) - fc_buffer = (char*)malloc(1024); -} - -static Uint8 FC_GrowGlyphCache(FC_Font* font) -{ - if(font == NULL) - return 0; - #ifdef FC_USE_SDL_GPU - GPU_Image* new_level = GPU_CreateImage(font->height * 12, font->height * 12, GPU_FORMAT_RGBA); - #else - SDL_Texture* new_level = SDL_CreateTexture(font->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, font->height * 12, font->height * 12); - #endif - if(new_level == NULL || !FC_SetGlyphCacheLevel(font, font->glyph_cache_count, new_level)) - { - FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); - #ifdef FC_USE_SDL_GPU - GPU_FreeImage(new_level); - #else - SDL_DestroyTexture(new_level); - #endif - return 0; - } - return 1; -} - -static Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface) -{ - if(font == NULL || data_surface == NULL) - return 0; - #ifdef FC_USE_SDL_GPU - GPU_Image* new_level = GPU_CopyImageFromSurface(data_surface); - if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) - GPU_SetImageFilter(new_level, GPU_FILTER_LINEAR); - else - GPU_SetImageFilter(new_level, GPU_FILTER_NEAREST); - #else - SDL_Texture* new_level; - if(!fc_has_render_target_support) - new_level = SDL_CreateTextureFromSurface(font->renderer, data_surface); - else - { - // Must upload with render target enabled so we can put more glyphs on later - SDL_Renderer* renderer = font->renderer; - - // Set filter mode for new texture - const char* old_filter_mode = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); - if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); - else - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - - new_level = SDL_CreateTexture(renderer, data_surface->format->format, SDL_TEXTUREACCESS_TARGET, data_surface->w, data_surface->h); - SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); - - // Reset filter mode for the temp texture - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); - - { - Uint8 r, g, b, a; - SDL_Texture* temp = SDL_CreateTextureFromSurface(renderer, data_surface); - SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_NONE); - SDL_SetRenderTarget(renderer, new_level); - - SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - SDL_SetRenderDrawColor(renderer, r, g, b, a); - - SDL_RenderCopy(renderer, temp, NULL, NULL); - SDL_SetRenderTarget(renderer, NULL); - - SDL_DestroyTexture(temp); - } - - // Reset to the old filter value - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_filter_mode); - - } - #endif - if(new_level == NULL || !FC_SetGlyphCacheLevel(font, cache_level, new_level)) - { - FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); - #ifdef FC_USE_SDL_GPU - GPU_FreeImage(new_level); - #else - SDL_DestroyTexture(new_level); - #endif - return 0; - } - return 1; -} - -static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight) -{ - FC_Map* glyphs = font->glyphs; - FC_GlyphData* last_glyph = &font->last_glyph; - Uint16 height = font->height + FC_CACHE_PADDING; - - if(last_glyph->rect.x + last_glyph->rect.w + width >= maxWidth - FC_CACHE_PADDING) - { - if(last_glyph->rect.y + height + height >= maxHeight - FC_CACHE_PADDING) - { - // Get ready to pack on the next cache level when it is ready - last_glyph->cache_level = font->glyph_cache_count; - last_glyph->rect.x = FC_CACHE_PADDING; - last_glyph->rect.y = FC_CACHE_PADDING; - last_glyph->rect.w = 0; - return NULL; - } - else - { - // Go to next row - last_glyph->rect.x = FC_CACHE_PADDING; - last_glyph->rect.y += height; - last_glyph->rect.w = 0; - } - } - - // Move to next space - last_glyph->rect.x += last_glyph->rect.w + 1 + FC_CACHE_PADDING; - last_glyph->rect.w = width; - - return FC_MapInsert(glyphs, codepoint, FC_MakeGlyphData(last_glyph->cache_level, last_glyph->rect.x, last_glyph->rect.y, last_glyph->rect.w, last_glyph->rect.h)); -} - - -FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level) -{ - if(font == NULL || cache_level < 0 || cache_level > font->glyph_cache_count) - return NULL; - - return font->glyph_cache[cache_level]; -} - -Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture) -{ - if(font == NULL || cache_level < 0) - return 0; - - // Must be sequentially added - if(cache_level > font->glyph_cache_count + 1) - return 0; - - if(cache_level == font->glyph_cache_count) - { - font->glyph_cache_count++; - - // Grow cache? - if(font->glyph_cache_count > font->glyph_cache_size) - { - // Copy old cache to new one - int i; - FC_Image** new_cache; - new_cache = (FC_Image**)malloc(font->glyph_cache_count * sizeof(FC_Image*)); - for(i = 0; i < font->glyph_cache_size; ++i) - new_cache[i] = font->glyph_cache[i]; - - // Save new cache - free(font->glyph_cache); - font->glyph_cache_size = font->glyph_cache_count; - font->glyph_cache = new_cache; - } - } - - font->glyph_cache[cache_level] = cache_texture; - return 1; -} - - -FC_Font* FC_CreateFont(void) -{ - FC_Font* font; - - font = (FC_Font*)malloc(sizeof(FC_Font)); - memset(font, 0, sizeof(FC_Font)); - - FC_Init(font); - - return font; -} - - -// Assume this many will be enough... -#define FC_LOAD_MAX_SURFACES 10 - -#ifdef FC_USE_SDL_GPU -Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color) -#else -Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color) -#endif -{ - if(font == NULL || ttf == NULL) - return 0; - #ifndef FC_USE_SDL_GPU - if(renderer == NULL) - return 0; - #endif - - FC_ClearFont(font); - - - // Might as well check render target support here - #ifdef FC_USE_SDL_GPU - fc_has_render_target_support = GPU_IsFeatureEnabled(GPU_FEATURE_RENDER_TARGETS); - #else - SDL_RendererInfo info; - SDL_GetRendererInfo(renderer, &info); - fc_has_render_target_support = (info.flags & SDL_RENDERER_TARGETTEXTURE); - - font->renderer = renderer; - #endif - - font->ttf_source = ttf; - - //font->line_height = TTF_FontLineSkip(ttf); - font->height = TTF_FontHeight(ttf); - font->ascent = TTF_FontAscent(ttf); - font->descent = -TTF_FontDescent(ttf); - - font->baseline = font->height - font->descent; - - font->default_color = color; - - { - SDL_Color white = {255, 255, 255, 255}; - SDL_Surface* glyph_surf; - char buff[5]; - const char* buff_ptr = buff; - const char* source_string; - Uint8 packed = 0; - - // Copy glyphs from the surface to the font texture and store the position data - // Pack row by row into a square texture - // Try figuring out dimensions that make sense for the font size. - unsigned int w = font->height*12; - unsigned int h = font->height*12; - SDL_Surface* surfaces[FC_LOAD_MAX_SURFACES]; - int num_surfaces = 1; - surfaces[0] = FC_CreateSurface32(w, h); - font->last_glyph.rect.x = FC_CACHE_PADDING; - font->last_glyph.rect.y = FC_CACHE_PADDING; - font->last_glyph.rect.w = 0; - font->last_glyph.rect.h = font->height; - - memset(buff, 0, 5); - source_string = font->loading_string; - for(; *source_string != '\0'; source_string = U8_next(source_string)) - { - if(!U8_charcpy(buff, source_string, 5)) - continue; - glyph_surf = TTF_RenderUTF8_Blended(ttf, buff, white); - if(glyph_surf == NULL) - continue; - - // Try packing. If it fails, create a new surface for the next cache level. - packed = (FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL); - if(!packed) - { - int i = num_surfaces-1; - if(num_surfaces >= FC_LOAD_MAX_SURFACES) - { - // Can't do any more! - FC_Log("SDL_FontCache error: Could not create enough cache surfaces to fit all of the loading string!\n"); - SDL_FreeSurface(glyph_surf); - break; - } - - // Upload the current surface to the glyph cache now so we can keep the cache level packing cursor up to date as we go. - FC_UploadGlyphCache(font, i, surfaces[i]); - SDL_FreeSurface(surfaces[i]); - #ifndef FC_USE_SDL_GPU - SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); - #endif - // Update the glyph cursor to the new cache level. We need to do this here because the actual cache lags behind our use of the packing above. - font->last_glyph.cache_level = num_surfaces; - - - surfaces[num_surfaces] = FC_CreateSurface32(w, h); - num_surfaces++; - } - - // Try packing for the new surface, then blit onto it. - if(packed || FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL) - { - SDL_SetSurfaceBlendMode(glyph_surf, SDL_BLENDMODE_NONE); - SDL_Rect srcRect = {0, 0, glyph_surf->w, glyph_surf->h}; - SDL_Rect destrect = font->last_glyph.rect; - SDL_BlitSurface(glyph_surf, &srcRect, surfaces[num_surfaces-1], &destrect); - } - - SDL_FreeSurface(glyph_surf); - } - - { - int i = num_surfaces-1; - FC_UploadGlyphCache(font, i, surfaces[i]); - SDL_FreeSurface(surfaces[i]); - #ifndef FC_USE_SDL_GPU - SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); - #endif - } - } - - return 1; -} - - -#ifdef FC_USE_SDL_GPU -Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) -#else -Uint8 FC_LoadFont(FC_Font* font, FC_Target* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) -#endif -{ - SDL_RWops* rwops; - - if(font == NULL) - return 0; - - rwops = SDL_RWFromFile(filename_ttf, "rb"); - - if(rwops == NULL) - { - FC_Log("Unable to open file for reading: %s \n", SDL_GetError()); - return 0; - } - - #ifdef FC_USE_SDL_GPU - return FC_LoadFont_RW(font, rwops, 1, pointSize, color, style); - #else - return FC_LoadFont_RW(font, renderer, rwops, 1, pointSize, color, style); - #endif -} - -#ifdef FC_USE_SDL_GPU -Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) -#else -Uint8 FC_LoadFont_RW(FC_Font* font, FC_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) -#endif -{ - Uint8 result; - TTF_Font* ttf; - Uint8 outline; - - if(font == NULL) - return 0; - - if(!TTF_WasInit() && TTF_Init() < 0) - { - FC_Log("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); - if(own_rwops) - SDL_RWclose(file_rwops_ttf); - return 0; - } - - ttf = TTF_OpenFontRW(file_rwops_ttf, own_rwops, pointSize); - - if(ttf == NULL) - { - FC_Log("Unable to load TrueType font: %s \n", TTF_GetError()); - if(own_rwops) - SDL_RWclose(file_rwops_ttf); - return 0; - } - - outline = (style & TTF_STYLE_OUTLINE); - if(outline) - { - style &= ~TTF_STYLE_OUTLINE; - TTF_SetFontOutline(ttf, 1); - } - TTF_SetFontStyle(ttf, style); - - #ifdef FC_USE_SDL_GPU - result = FC_LoadFontFromTTF(font, ttf, color); - #else - result = FC_LoadFontFromTTF(font, renderer, ttf, color); - #endif - - // Can only load new (uncached) glyphs if we can keep the SDL_RWops open. - font->owns_ttf_source = own_rwops; - if(!own_rwops) - { - TTF_CloseFont(font->ttf_source); - font->ttf_source = NULL; - } - - return result; -} - - -void FC_ClearFont(FC_Font* font) -{ - int i; - if(font == NULL) - return; - - // Release resources - if(font->owns_ttf_source) - TTF_CloseFont(font->ttf_source); - - font->owns_ttf_source = 0; - font->ttf_source = NULL; - - // Delete glyph map - FC_MapFree(font->glyphs); - font->glyphs = NULL; - - // Delete glyph cache - for(i = 0; i < font->glyph_cache_count; ++i) - { - #ifdef FC_USE_SDL_GPU - GPU_FreeImage(font->glyph_cache[i]); - #else - SDL_DestroyTexture(font->glyph_cache[i]); - #endif - } - free(font->glyph_cache); - font->glyph_cache = NULL; - - // Reset font - FC_Init(font); -} - - -void FC_FreeFont(FC_Font* font) -{ - int i; - if(font == NULL) - return; - - // Release resources - if(font->owns_ttf_source) - TTF_CloseFont(font->ttf_source); - - // Delete glyph map - FC_MapFree(font->glyphs); - - // Delete glyph cache - for(i = 0; i < font->glyph_cache_count; ++i) - { - #ifdef FC_USE_SDL_GPU - GPU_FreeImage(font->glyph_cache[i]); - #else - SDL_DestroyTexture(font->glyph_cache[i]); - #endif - } - free(font->glyph_cache); - - free(font->loading_string); - - free(font); -} - -int FC_GetNumCacheLevels(FC_Font* font) -{ - return font->glyph_cache_count; -} - -Uint8 FC_AddGlyphToCache(FC_Font* font, SDL_Surface* glyph_surface) -{ - if(font == NULL || glyph_surface == NULL) - return 0; - - SDL_SetSurfaceBlendMode(glyph_surface, SDL_BLENDMODE_NONE); - FC_Image* dest = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); - if(dest == NULL) - return 0; - - #ifdef FC_USE_SDL_GPU - { - GPU_Target* target = GPU_LoadTarget(dest); - if(target == NULL) - return 0; - GPU_Image* img = GPU_CopyImageFromSurface(glyph_surface); - GPU_SetImageFilter(img, GPU_FILTER_NEAREST); - GPU_SetBlendMode(img, GPU_BLEND_SET); - - SDL_Rect destrect = font->last_glyph.rect; - GPU_Blit(img, NULL, target, destrect.x + destrect.w/2, destrect.y + destrect.h/2); - - GPU_FreeImage(img); - GPU_FreeTarget(target); - } - #else - { - SDL_Texture* img = SDL_CreateTextureFromSurface(font->renderer, glyph_surface); - - SDL_Rect destrect = font->last_glyph.rect; - SDL_SetRenderTarget(font->renderer, dest); - SDL_RenderCopy(font->renderer, img, NULL, &destrect); - SDL_SetRenderTarget(font->renderer, NULL); - SDL_DestroyTexture(img); - } - #endif - - return 1; -} - -Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint) -{ - FC_GlyphData* e = FC_MapFind(font->glyphs, codepoint); - if(e == NULL || result == NULL) - { - char buff[5]; - int w, h; - SDL_Color white = {255, 255, 255, 255}; - SDL_Surface* surf; - FC_Image* cache_image; - - if(font->ttf_source == NULL) - return 0; - - FC_GetUTF8FromCodepoint(buff, codepoint); - - cache_image = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); - if(cache_image == NULL) - { - FC_Log("SDL_FontCache: Failed to load cache image, so cannot add new glyphs!\n"); - return 0; - } - #ifdef FC_USE_SDL_GPU - w = cache_image->w; - h = cache_image->h; - #else - SDL_QueryTexture(cache_image, NULL, NULL, &w, &h); - #endif - - surf = TTF_RenderUTF8_Blended(font->ttf_source, buff, white); - if(surf == NULL) - { - return 0; - } - - e = FC_PackGlyphData(font, codepoint, surf->w, w, h); - if(e == NULL) - { - // Grow the cache - FC_GrowGlyphCache(font); - - // Try packing again - e = FC_PackGlyphData(font, codepoint, surf->w, w, h); - if(e == NULL) - { - SDL_FreeSurface(surf); - return 0; - } - } - - // Render onto the cache texture - FC_AddGlyphToCache(font, surf); - - SDL_FreeSurface(surf); - } - - if(result != NULL && e != NULL) - *result = *e; - - return 1; -} - - -FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data) -{ - return FC_MapInsert(font->glyphs, codepoint, glyph_data); -} - - -// Drawing -static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) -{ - const char* c = text; - FC_Rect srcRect; - FC_Rect dstRect; - FC_Rect dirtyRect = FC_MakeRect(x, y, 0, 0); - - FC_GlyphData glyph; - Uint32 codepoint; - - float destX = x; - float destY = y; - float destH; - float destLineSpacing; - float destLetterSpacing; - - if(font == NULL) - return dirtyRect; - - destH = font->height * scale.y; - destLineSpacing = font->lineSpacing*scale.y; - destLetterSpacing = font->letterSpacing*scale.x; - - if(c == NULL || font->glyph_cache_count == 0 || dest == NULL) - return dirtyRect; - - int newlineX = x; - - for(; *c != '\0'; c++) - { - if(*c == '\n') - { - destX = newlineX; - destY += destH + destLineSpacing; - continue; - } - - codepoint = FC_GetCodepointFromUTF8(&c, 1); // Increments 'c' to skip the extra UTF-8 bytes - if(!FC_GetGlyphData(font, &glyph, codepoint)) - { - codepoint = ' '; - if(!FC_GetGlyphData(font, &glyph, codepoint)) - continue; // Skip bad characters - } - - if (codepoint == ' ') - { - destX += glyph.rect.w*scale.x + destLetterSpacing; - continue; - } - /*if(destX >= dest->w) - continue; - if(destY >= dest->h) - continue;*/ - - #ifdef FC_USE_SDL_GPU - srcRect.x = glyph.rect.x; - srcRect.y = glyph.rect.y; - srcRect.w = glyph.rect.w; - srcRect.h = glyph.rect.h; - #else - srcRect = glyph.rect; - #endif - dstRect = fc_render_callback(FC_GetGlyphCacheLevel(font, glyph.cache_level), &srcRect, dest, destX, destY, scale.x, scale.y); - if(dirtyRect.w == 0 || dirtyRect.h == 0) - dirtyRect = dstRect; - else - dirtyRect = FC_RectUnion(dirtyRect, dstRect); - - destX += glyph.rect.w*scale.x + destLetterSpacing; - } - - return dirtyRect; -} - -static void set_color_for_all_caches(FC_Font* font, SDL_Color color) -{ - // TODO: How can I predict which glyph caches are to be used? - FC_Image* img; - int i; - int num_levels = FC_GetNumCacheLevels(font); - for(i = 0; i < num_levels; ++i) - { - img = FC_GetGlyphCacheLevel(font, i); - set_color(img, color.r, color.g, color.b, FC_GET_ALPHA(color)); - } -} - -FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, font->default_color); - - return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); -} - - - -typedef struct FC_StringList -{ - char* value; - struct FC_StringList* next; -} FC_StringList; - -void FC_StringListFree(FC_StringList* node) -{ - // Delete the nodes in order - while(node != NULL) - { - FC_StringList* last = node; - node = node->next; - - free(last->value); - free(last); - } -} - -FC_StringList** FC_StringListPushBack(FC_StringList** node, char* value, Uint8 copy) -{ - // Get to the last node - while(node != NULL && *node != NULL) - { - node = &(*node)->next; - } - - *node = (FC_StringList*)malloc(sizeof(FC_StringList)); - - (*node)->value = (copy? U8_strdup(value) : value); - (*node)->next = NULL; - - return node; -} - -static FC_StringList* FC_Explode(const char* text, char delimiter) -{ - FC_StringList* head; - FC_StringList* new_node; - FC_StringList** node; - const char* start; - const char* end; - unsigned int size; - if(text == NULL) - return NULL; - - head = NULL; - node = &head; - - // Doesn't technically support UTF-8, but it's probably fine, right? - size = 0; - start = end = text; - while(1) - { - if(*end == delimiter || *end == '\0') - { - *node = (FC_StringList*)malloc(sizeof(FC_StringList)); - new_node = *node; - - new_node->value = (char*)malloc(size + 1); - memcpy(new_node->value, start, size); - new_node->value[size] = '\0'; - - new_node->next = NULL; - - if(*end == '\0') - break; - - node = &((*node)->next); - start = end+1; - size = 0; - } - else - ++size; - - ++end; - } - - return head; -} - -static FC_StringList* FC_ExplodeAndKeep(const char* text, char delimiter) -{ - FC_StringList* head; - FC_StringList* new_node; - FC_StringList** node; - const char* start; - const char* end; - unsigned int size; - if(text == NULL) - return NULL; - - head = NULL; - node = &head; - - // Doesn't technically support UTF-8, but it's probably fine, right? - size = 0; - start = end = text; - while(1) - { - if(*end == delimiter || *end == '\0') - { - *node = (FC_StringList*)malloc(sizeof(FC_StringList)); - new_node = *node; - - new_node->value = (char*)malloc(size + 1); - memcpy(new_node->value, start, size); - new_node->value[size] = '\0'; - - new_node->next = NULL; - - if(*end == '\0') - break; - - node = &((*node)->next); - start = end; - size = 1; - } - else - ++size; - - ++end; - } - - return head; -} - -static void FC_RenderAlign(FC_Font* font, FC_Target* dest, float x, float y, int width, FC_Scale scale, FC_AlignEnum align, const char* text) -{ - switch(align) - { - case FC_ALIGN_LEFT: - FC_RenderLeft(font, dest, x, y, scale, text); - break; - case FC_ALIGN_CENTER: - FC_RenderCenter(font, dest, x + width/2, y, scale, text); - break; - case FC_ALIGN_RIGHT: - FC_RenderRight(font, dest, x + width, y, scale, text); - break; - } -} - -static FC_StringList* FC_GetBufferFitToColumn(FC_Font* font, int width, FC_Scale scale, Uint8 keep_newlines) -{ - FC_StringList* result = NULL; - FC_StringList** current = &result; - - FC_StringList *ls, *iter; - - ls = (keep_newlines? FC_ExplodeAndKeep(fc_buffer, '\n') : FC_Explode(fc_buffer, '\n')); - for(iter = ls; iter != NULL; iter = iter->next) - { - char* line = iter->value; - - // If line is too long, then add words one at a time until we go over. - if(width > 0 && FC_GetWidth(font, "%s", line) > width) - { - FC_StringList *words, *word_iter; - - words = FC_Explode(line, ' '); - // Skip the first word for the iterator, so there will always be at least one word per line - line = new_concat(words->value, " "); - for(word_iter = words->next; word_iter != NULL; word_iter = word_iter->next) - { - char* line_plus_word = new_concat(line, word_iter->value); - char* word_plus_space = new_concat(word_iter->value, " "); - if(FC_GetWidth(font, line_plus_word) > width) - { - current = FC_StringListPushBack(current, line, 0); - - line = word_plus_space; - } - else - { - replace_concat(&line, word_plus_space); - free(word_plus_space); - } - free(line_plus_word); - } - current = FC_StringListPushBack(current, line, 0); - FC_StringListFree(words); - } - else - { - current = FC_StringListPushBack(current, line, 0); - iter->value = NULL; - } - } - FC_StringListFree(ls); - - return result; -} - -static void FC_DrawColumnFromBuffer(FC_Font* font, FC_Target* dest, FC_Rect box, int* total_height, FC_Scale scale, FC_AlignEnum align) -{ - int y = box.y; - FC_StringList *ls, *iter; - - ls = FC_GetBufferFitToColumn(font, box.w, scale, 0); - for(iter = ls; iter != NULL; iter = iter->next) - { - FC_RenderAlign(font, dest, box.x, y, box.w, scale, align, iter->value); - y += FC_GetLineHeight(font); - } - FC_StringListFree(ls); - - if(total_height != NULL) - *total_height = y - box.y; -} - -FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...) -{ - Uint8 useClip; - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(box.x, box.y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - useClip = has_clip(dest); - FC_Rect oldclip, newclip; - if(useClip) - { - oldclip = get_clip(dest); - newclip = FC_RectIntersect(oldclip, box); - } - else - newclip = box; - set_clip(dest, &newclip); - - set_color_for_all_caches(font, font->default_color); - - FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); - - if(useClip) - set_clip(dest, &oldclip); - else - set_clip(dest, NULL); - - return box; -} - -FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...) -{ - Uint8 useClip; - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(box.x, box.y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - useClip = has_clip(dest); - FC_Rect oldclip, newclip; - if(useClip) - { - oldclip = get_clip(dest); - newclip = FC_RectIntersect(oldclip, box); - } - else - newclip = box; - set_clip(dest, &newclip); - - set_color_for_all_caches(font, font->default_color); - - FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), align); - - if(useClip) - set_clip(dest, &oldclip); - else - set_clip(dest, NULL); - - return box; -} - -FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...) -{ - Uint8 useClip; - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(box.x, box.y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - useClip = has_clip(dest); - FC_Rect oldclip, newclip; - if(useClip) - { - oldclip = get_clip(dest); - newclip = FC_RectIntersect(oldclip, box); - } - else - newclip = box; - set_clip(dest, &newclip); - - set_color_for_all_caches(font, font->default_color); - - FC_DrawColumnFromBuffer(font, dest, box, NULL, scale, FC_ALIGN_LEFT); - - if(useClip) - set_clip(dest, &oldclip); - else - set_clip(dest, NULL); - - return box; -} - -FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...) -{ - Uint8 useClip; - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(box.x, box.y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - useClip = has_clip(dest); - FC_Rect oldclip, newclip; - if(useClip) - { - oldclip = get_clip(dest); - newclip = FC_RectIntersect(oldclip, box); - } - else - newclip = box; - set_clip(dest, &newclip); - - set_color_for_all_caches(font, color); - - FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); - - if(useClip) - set_clip(dest, &oldclip); - else - set_clip(dest, NULL); - - return box; -} - -FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...) -{ - Uint8 useClip; - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(box.x, box.y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - useClip = has_clip(dest); - FC_Rect oldclip, newclip; - if(useClip) - { - oldclip = get_clip(dest); - newclip = FC_RectIntersect(oldclip, box); - } - else - newclip = box; - set_clip(dest, &newclip); - - set_color_for_all_caches(font, effect.color); - - FC_DrawColumnFromBuffer(font, dest, box, NULL, effect.scale, effect.alignment); - - if(useClip) - set_clip(dest, &oldclip); - else - set_clip(dest, NULL); - - return box; -} - -FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) -{ - FC_Rect box = {x, y, width, 0}; - int total_height; - - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, font->default_color); - - FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); - - return FC_MakeRect(box.x, box.y, width, total_height); -} - -FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...) -{ - FC_Rect box = {x, y, width, 0}; - int total_height; - - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, font->default_color); - - switch(align) - { - case FC_ALIGN_CENTER: - box.x -= width/2; - break; - case FC_ALIGN_RIGHT: - box.x -= width; - break; - default: - break; - } - - FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), align); - - return FC_MakeRect(box.x, box.y, width, total_height); -} - -FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...) -{ - FC_Rect box = {x, y, width, 0}; - int total_height; - - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, font->default_color); - - FC_DrawColumnFromBuffer(font, dest, box, &total_height, scale, FC_ALIGN_LEFT); - - return FC_MakeRect(box.x, box.y, width, total_height); -} - -FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...) -{ - FC_Rect box = {x, y, width, 0}; - int total_height; - - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, color); - - FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); - - return FC_MakeRect(box.x, box.y, width, total_height); -} - -FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...) -{ - FC_Rect box = {x, y, width, 0}; - int total_height; - - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, effect.color); - - switch(effect.alignment) - { - case FC_ALIGN_CENTER: - box.x -= width/2; - break; - case FC_ALIGN_RIGHT: - box.x -= width; - break; - default: - break; - } - - FC_DrawColumnFromBuffer(font, dest, box, &total_height, effect.scale, effect.alignment); - - return FC_MakeRect(box.x, box.y, width, total_height); -} - -static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) -{ - FC_Rect result = {x, y, 0, 0}; - if(text == NULL || font == NULL) - return result; - - char* str = U8_strdup(text); - char* del = str; - char* c; - - // Go through str, when you find a \n, replace it with \0 and print it - // then move down, back, and continue. - for(c = str; *c != '\0';) - { - if(*c == '\n') - { - *c = '\0'; - result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); - *c = '\n'; - c++; - str = c; - y += scale.y*font->height; - } - else - c++; - } - - result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); - - free(del); - return result; -} - -static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) -{ - FC_Rect result = {x, y, 0, 0}; - if(text == NULL || font == NULL) - return result; - - char* str = U8_strdup(text); - char* del = str; - char* c; - - for(c = str; *c != '\0';) - { - if(*c == '\n') - { - *c = '\0'; - result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); - *c = '\n'; - c++; - str = c; - y += scale.y*font->height; - } - else - c++; - } - - result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); - - free(del); - return result; -} - - - -FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, font->default_color); - - return FC_RenderLeft(font, dest, x, y, scale, fc_buffer); -} - -FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, font->default_color); - - FC_Rect result; - switch(align) - { - case FC_ALIGN_LEFT: - result = FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); - break; - case FC_ALIGN_CENTER: - result = FC_RenderCenter(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); - break; - case FC_ALIGN_RIGHT: - result = FC_RenderRight(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); - break; - default: - result = FC_MakeRect(x, y, 0, 0); - break; - } - - return result; -} - -FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, color); - - return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); -} - - -FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return FC_MakeRect(x, y, 0, 0); - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - set_color_for_all_caches(font, effect.color); - - FC_Rect result; - switch(effect.alignment) - { - case FC_ALIGN_LEFT: - result = FC_RenderLeft(font, dest, x, y, effect.scale, fc_buffer); - break; - case FC_ALIGN_CENTER: - result = FC_RenderCenter(font, dest, x, y, effect.scale, fc_buffer); - break; - case FC_ALIGN_RIGHT: - result = FC_RenderRight(font, dest, x, y, effect.scale, fc_buffer); - break; - default: - result = FC_MakeRect(x, y, 0, 0); - break; - } - - return result; -} - - - - -// Getters - - -FC_FilterEnum FC_GetFilterMode(FC_Font* font) -{ - if(font == NULL) - return FC_FILTER_NEAREST; - - return font->filter; -} - -Uint16 FC_GetLineHeight(FC_Font* font) -{ - if(font == NULL) - return 0; - - return font->height; -} - -Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return 0; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - Uint16 numLines = 1; - const char* c; - - for (c = fc_buffer; *c != '\0'; c++) - { - if(*c == '\n') - numLines++; - } - - // Actual height of letter region + line spacing - return font->height*numLines + font->lineSpacing*(numLines - 1); //height*numLines; -} - -Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...) -{ - if(formatted_text == NULL || font == NULL) - return 0; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - const char* c; - Uint16 width = 0; - Uint16 bigWidth = 0; // Allows for multi-line strings - - for (c = fc_buffer; *c != '\0'; c++) - { - if(*c == '\n') - { - bigWidth = bigWidth >= width? bigWidth : width; - width = 0; - continue; - } - - FC_GlyphData glyph; - Uint32 codepoint = FC_GetCodepointFromUTF8(&c, 1); - if(FC_GetGlyphData(font, &glyph, codepoint) || FC_GetGlyphData(font, &glyph, ' ')) - width += glyph.rect.w; - } - bigWidth = bigWidth >= width? bigWidth : width; - - return bigWidth; -} - -// If width == -1, use no width limit -FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...) -{ - FC_Rect result = {0, 0, 1, FC_GetLineHeight(font)}; - FC_StringList *ls, *iter; - int num_lines = 0; - Uint8 done = 0; - - if(formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL) - return result; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); - for(iter = ls; iter != NULL; iter = iter->next) - { - char* line; - int i = 0; - - ++num_lines; - for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) - { - ++i; - --position_index; - if(position_index == 0) - { - // FIXME: Doesn't handle box-wrapped newlines correctly - line = (char*)U8_next(line); - line[0] = '\0'; - result.x = FC_GetWidth(font, "%s", iter->value); - done = 1; - break; - } - } - if(done) - break; - } - FC_StringListFree(ls); - - if(num_lines > 0) - { - result.y = (num_lines - 1) * FC_GetLineHeight(font); - } - - return result; -} - - -Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...) -{ - int y = 0; - - FC_StringList *ls, *iter; - - if(font == NULL) - return 0; - - if(formatted_text == NULL || width == 0) - return font->height; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); - for(iter = ls; iter != NULL; iter = iter->next) - { - y += FC_GetLineHeight(font); - } - FC_StringListFree(ls); - - return y; -} - -static int FC_GetAscentFromCodepoint(FC_Font* font, Uint32 codepoint) -{ - FC_GlyphData glyph; - - if(font == NULL) - return 0; - - // FIXME: Store ascent so we can return it here - FC_GetGlyphData(font, &glyph, codepoint); - return glyph.rect.h; -} - -static int FC_GetDescentFromCodepoint(FC_Font* font, Uint32 codepoint) -{ - FC_GlyphData glyph; - - if(font == NULL) - return 0; - - // FIXME: Store descent so we can return it here - FC_GetGlyphData(font, &glyph, codepoint); - return glyph.rect.h; -} - -int FC_GetAscent(FC_Font* font, const char* formatted_text, ...) -{ - Uint32 codepoint; - int max, ascent; - const char* c; - - if(font == NULL) - return 0; - - if(formatted_text == NULL) - return font->ascent; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - max = 0; - c = fc_buffer; - - while(*c != '\0') - { - codepoint = FC_GetCodepointFromUTF8(&c, 1); - if(codepoint != 0) - { - ascent = FC_GetAscentFromCodepoint(font, codepoint); - if(ascent > max) - max = ascent; - } - ++c; - } - return max; -} - -int FC_GetDescent(FC_Font* font, const char* formatted_text, ...) -{ - Uint32 codepoint; - int max, descent; - const char* c; - - if(font == NULL) - return 0; - - if(formatted_text == NULL) - return font->descent; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - max = 0; - c = fc_buffer; - - while(*c != '\0') - { - codepoint = FC_GetCodepointFromUTF8(&c, 1); - if(codepoint != 0) - { - descent = FC_GetDescentFromCodepoint(font, codepoint); - if(descent > max) - max = descent; - } - ++c; - } - return max; -} - -int FC_GetBaseline(FC_Font* font) -{ - if(font == NULL) - return 0; - - return font->baseline; -} - -int FC_GetSpacing(FC_Font* font) -{ - if(font == NULL) - return 0; - - return font->letterSpacing; -} - -int FC_GetLineSpacing(FC_Font* font) -{ - if(font == NULL) - return 0; - - return font->lineSpacing; -} - -Uint16 FC_GetMaxWidth(FC_Font* font) -{ - if(font == NULL) - return 0; - - return font->maxWidth; -} - -SDL_Color FC_GetDefaultColor(FC_Font* font) -{ - if(font == NULL) - { - SDL_Color c = {0,0,0,255}; - return c; - } - - return font->default_color; -} - - -Uint8 FC_InRect(float x, float y, FC_Rect input_rect) -{ - return (input_rect.x <= x && x <= input_rect.x + input_rect.w && input_rect.y <= y && y <= input_rect.y + input_rect.h); -} - -// TODO: Make it work with alignment -Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...) -{ - FC_StringList *ls, *iter; - Uint8 done = 0; - int height = FC_GetLineHeight(font); - Uint16 position = 0; - int current_x = 0; - int current_y = 0; - FC_GlyphData glyph_data; - - if(formatted_text == NULL || column_width == 0 || font == NULL) - return 0; - - FC_EXTRACT_VARARGS(fc_buffer, formatted_text); - - ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); - for(iter = ls; iter != NULL; iter = iter->next) - { - char* line; - - for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) - { - if(FC_GetGlyphData(font, &glyph_data, FC_GetCodepointFromUTF8((const char**)&line, 0))) - { - if(FC_InRect(x, y, FC_MakeRect(current_x, current_y, glyph_data.rect.w, glyph_data.rect.h))) - { - done = 1; - break; - } - - current_x += glyph_data.rect.w; - } - position++; - } - if(done) - break; - - current_x = 0; - current_y += height; - if(y < current_y) - break; - } - FC_StringListFree(ls); - - return position; -} - - - - -// Setters - - -void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter) -{ - if(font == NULL) - return; - - if(font->filter != filter) - { - font->filter = filter; - - #ifdef FC_USE_SDL_GPU - // Update each texture to use this filter mode - { - int i; - GPU_FilterEnum gpu_filter = GPU_FILTER_NEAREST; - if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) - gpu_filter = GPU_FILTER_LINEAR; - - for(i = 0; i < font->glyph_cache_count; ++i) - { - GPU_SetImageFilter(font->glyph_cache[i], gpu_filter); - } - } - #endif - } -} - - -void FC_SetSpacing(FC_Font* font, int LetterSpacing) -{ - if(font == NULL) - return; - - font->letterSpacing = LetterSpacing; -} - -void FC_SetLineSpacing(FC_Font* font, int LineSpacing) -{ - if(font == NULL) - return; - - font->lineSpacing = LineSpacing; -} - -void FC_SetDefaultColor(FC_Font* font, SDL_Color color) -{ - if(font == NULL) - return; - - font->default_color = color; -} - - - - - - diff --git a/source/code/Libs/SDL_FontCache.cpp b/source/code/Libs/SDL_FontCache.cpp new file mode 100644 index 0000000..451b937 --- /dev/null +++ b/source/code/Libs/SDL_FontCache.cpp @@ -0,0 +1,2488 @@ +/* +SDL_FontCache v0.0.1: A font cache for SDL and SDL_ttf +by Jonathan Dearborn +Dedicated to the memory of Florian Hufsky + +License: + The short: + Use it however you'd like, but keep the copyright and license notice + whenever these files or parts of them are distributed in uncompiled form. + + The long: +Copyright (c) 2015 Jonathan Dearborn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "SDL_FontCache.h" + +#include +#include +#include + +// Visual C does not support static inline +#ifndef static_inline +#ifdef _MSC_VER +#define static_inline static +#else +#define static_inline static inline +#endif +#endif + +#if SDL_VERSION_ATLEAST(2,0,0) +#define FC_GET_ALPHA(sdl_color) ((sdl_color).a) +#else +#define FC_GET_ALPHA(sdl_color) ((sdl_color).unused) +#endif + +// Need SDL_RenderIsClipEnabled() for proper clipping support +#if SDL_VERSION_ATLEAST(2,0,4) +#define ENABLE_SDL_CLIPPING +#endif + +#define FC_MIN(a,b) ((a) < (b)? (a) : (b)) +#define FC_MAX(a,b) ((a) > (b)? (a) : (b)) + + +#define FC_EXTRACT_VARARGS(buffer, start_args) \ +{ \ + va_list lst; \ + va_start(lst, start_args); \ + vsprintf(buffer, start_args, lst); \ + va_end(lst); \ +} + +// Extra pixels of padding around each glyph to avoid linear filtering artifacts +#define FC_CACHE_PADDING 1 + + + +static Uint8 has_clip(FC_Target* dest) { +#ifdef FC_USE_SDL_GPU + return dest->use_clip_rect; +#elif defined(ENABLE_SDL_CLIPPING) + return SDL_RenderIsClipEnabled(dest); +#else + return 0; +#endif +} + +static FC_Rect get_clip(FC_Target* dest) { +#ifdef FC_USE_SDL_GPU + return dest->clip_rect; +#elif defined(ENABLE_SDL_CLIPPING) + SDL_Rect r; + SDL_RenderGetClipRect(dest, &r); + return r; +#else + SDL_Rect r = {0, 0, 0, 0}; + return r; +#endif +} + +static void set_clip(FC_Target* dest, FC_Rect* rect) { +#ifdef FC_USE_SDL_GPU + if (rect != NULL) { + GPU_SetClipRect(dest, *rect); + } + else { + GPU_UnsetClip(dest); + } +#elif defined(ENABLE_SDL_CLIPPING) + SDL_RenderSetClipRect(dest, rect); +#endif +} + +static void set_color(FC_Image* src, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { +#ifdef FC_USE_SDL_GPU + GPU_SetRGBA(src, r, g, b, a); +#else + SDL_SetTextureColorMod(src, r, g, b); + SDL_SetTextureAlphaMod(src, a); +#endif +} + + + +static char* new_concat(const char* a, const char* b) { + // Create new buffer + unsigned int size = strlen(a) + strlen(b); + char* new_string = (char*)malloc(size+1); + + // Concatenate strings in the new buffer + strcpy(new_string, a); + strcat(new_string, b); + + return new_string; +} + +static char* replace_concat(char** a, const char* b) { + char* new_string = new_concat(*a, b); + free(*a); + *a = new_string; + return *a; +} + + + + + +// Shared buffer for variadic text +static char* fc_buffer = NULL; + +static Uint8 fc_has_render_target_support = 0; + +const char* FC_GetStringASCII(void) { + static char* buffer = NULL; + if (buffer == NULL) { + int i; + char c; + buffer = (char*)malloc(512); + memset(buffer, 0, 512); + i = 0; + c = 32; + while (1) { + buffer[i] = c; + if (c == 126) { + break; + } + ++i; + ++c; + } + } + return buffer; +} + +const char* FC_GetStringLatin1(void) { + static char* buffer = NULL; + if (buffer == NULL) { + int i; + unsigned char c; + buffer = (char*)malloc(512); + memset(buffer, 0, 512); + i = 0; + c = 0xA0; + while (1) { + buffer[i] = 0xC2; + buffer[i+1] = c; + if (c == 0xBF) { + break; + } + i += 2; + ++c; + } + i += 2; + c = 0x80; + while (1) { + buffer[i] = 0xC3; + buffer[i+1] = c; + if (c == 0xBF) { + break; + } + i += 2; + ++c; + } + } + return buffer; +} + +const char* FC_GetStringASCII_Latin1(void) { + static char* buffer = NULL; + if (buffer == NULL) { + buffer = new_concat(FC_GetStringASCII(), FC_GetStringLatin1()); + } + + return buffer; +} + +FC_Rect FC_MakeRect(float x, float y, float w, float h) { + FC_Rect r = {x, y, w, h}; + return r; +} + +FC_Scale FC_MakeScale(float x, float y) { + FC_Scale s = {x, y}; + + return s; +} + +SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { + SDL_Color c = {r, g, b, a}; + + return c; +} + +FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color) { + FC_Effect e; + + e.alignment = alignment; + e.scale = scale; + e.color = color; + + return e; +} + +FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h) { + FC_GlyphData gd; + + gd.rect.x = x; + gd.rect.y = y; + gd.rect.w = w; + gd.rect.h = h; + gd.cache_level = cache_level; + + return gd; +} + +// Enough to hold all of the ascii characters and some. +#define FC_DEFAULT_NUM_BUCKETS 300 + +typedef struct FC_MapNode { + Uint32 key; + FC_GlyphData value; + struct FC_MapNode* next; + +} FC_MapNode; + +typedef struct FC_Map { + int num_buckets; + FC_MapNode** buckets; +} FC_Map; + + + +static FC_Map* FC_MapCreate(int num_buckets) { + int i; + FC_Map* map = (FC_Map*)malloc(sizeof(FC_Map)); + + map->num_buckets = num_buckets; + map->buckets = (FC_MapNode**)malloc(num_buckets * sizeof(FC_MapNode*)); + + for (i = 0; i < num_buckets; ++i) { + map->buckets[i] = NULL; + } + + return map; +} + +/*static void FC_MapClear(FC_Map* map) +{ + int i; + if(map == NULL) + return; + + // Go through each bucket + for(i = 0; i < map->num_buckets; ++i) + { + // Delete the nodes in order + FC_MapNode* node = map->buckets[i]; + while(node != NULL) + { + FC_MapNode* last = node; + node = node->next; + free(last); + } + // Set the bucket to empty + map->buckets[i] = NULL; + } +}*/ + +static void FC_MapFree(FC_Map* map) { + int i; + if (map == NULL) { + return; + } + + // Go through each bucket + for (i = 0; i < map->num_buckets; ++i) { + // Delete the nodes in order + FC_MapNode* node = map->buckets[i]; + while (node != NULL) { + FC_MapNode* last = node; + node = node->next; + free(last); + } + } + + free(map->buckets); + free(map); +} + +// Note: Does not handle duplicates in any special way. +static FC_GlyphData* FC_MapInsert(FC_Map* map, Uint32 codepoint, FC_GlyphData glyph) { + Uint32 index; + FC_MapNode* node; + if (map == NULL) { + return NULL; + } + + // Get index for bucket + index = codepoint % map->num_buckets; + + // If this bucket is empty, create a node and return its value + if (map->buckets[index] == NULL) { + node = map->buckets[index] = (FC_MapNode*)malloc(sizeof(FC_MapNode)); + node->key = codepoint; + node->value = glyph; + node->next = NULL; + return &node->value; + } + + for (node = map->buckets[index]; node != NULL; node = node->next) { + // Find empty node and add a new one on. + if (node->next == NULL) { + node->next = (FC_MapNode*)malloc(sizeof(FC_MapNode)); + node = node->next; + + node->key = codepoint; + node->value = glyph; + node->next = NULL; + return &node->value; + } + } + + return NULL; +} + +static FC_GlyphData* FC_MapFind(FC_Map* map, Uint32 codepoint) { + Uint32 index; + FC_MapNode* node; + if (map == NULL) { + return NULL; + } + + // Get index for bucket + index = codepoint % map->num_buckets; + + // Go through list until we find a match + for (node = map->buckets[index]; node != NULL; node = node->next) { + if (node->key == codepoint) { + return &node->value; + } + } + + return NULL; +} + + + +struct FC_Font { +#ifndef FC_USE_SDL_GPU + SDL_Renderer* renderer; +#endif + + TTF_Font* ttf_source; // TTF_Font source of characters + Uint8 owns_ttf_source; // Can we delete the TTF_Font ourselves? + + FC_FilterEnum filter; + + SDL_Color default_color; + Uint16 height; + + Uint16 maxWidth; + Uint16 baseline; + int ascent; + int descent; + + int lineSpacing; + int letterSpacing; + + // Uses 32-bit (4-byte) Unicode codepoints to refer to each glyph + // Codepoints are little endian (reversed from UTF-8) so that something like 0x00000005 is ASCII 5 and the map can be indexed by ASCII values + FC_Map* glyphs; + + FC_GlyphData last_glyph; // Texture packing cursor + int glyph_cache_size; + int glyph_cache_count; + FC_Image** glyph_cache; + + char* loading_string; + +}; + +// Private +static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight); + + +static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); +static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); +static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); + + +static_inline SDL_Surface* FC_CreateSurface32(Uint32 width, Uint32 height) { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); +#else + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); +#endif +} + + +char* U8_alloc(unsigned int size) { + char* result; + if (size == 0) { + return NULL; + } + + result = (char*)malloc(size); + result[0] = '\0'; + + return result; +} + +void U8_free(char* string) { + free(string); +} + +char* U8_strdup(const char* string) { + char* result; + if (string == NULL) { + return NULL; + } + + result = (char*)malloc(strlen(string)+1); + strcpy(result, string); + + return result; +} + +int U8_strlen(const char* string) { + int length = 0; + if (string == NULL) { + return 0; + } + + while (*string != '\0') { + string = U8_next(string); + ++length; + } + + return length; +} + +int U8_charsize(const char* character) { + if (character == NULL) { + return 0; + } + + if ((unsigned char)*character <= 0x7F) { + return 1; + } + else if ((unsigned char)*character < 0xE0) { + return 2; + } + else if ((unsigned char)*character < 0xF0) { + return 3; + } + else { + return 4; + } + return 1; +} + +int U8_charcpy(char* buffer, const char* source, int buffer_size) { + int charsize; + if (buffer == NULL || source == NULL || buffer_size < 1) { + return 0; + } + + charsize = U8_charsize(source); + if (charsize > buffer_size) { + return 0; + } + + memcpy(buffer, source, charsize); + return charsize; +} + +const char* U8_next(const char* string) { + return string + U8_charsize(string); +} + +int U8_strinsert(char* string, int position, const char* source, int max_bytes) { + int pos_bytes; + int len = strlen(string); + int add_len = strlen(source); + int ulen = U8_strlen(string); + + if (position == -1) { + position = ulen; + } + + if (string == NULL || source == NULL || position < 0 || position > ulen || len + add_len + 1 > max_bytes) { + return 0; + } + + // Move string pointer to the proper position + pos_bytes = 0; + while (*string != '\0' && pos_bytes < position) { + string = (char*)U8_next(string); + ++pos_bytes; + } + + // Move the rest of the string out of the way + memmove(string + add_len, string, len - pos_bytes + 1); + + // Copy in the new characters + memcpy(string, source, add_len); + + return 1; +} + +void U8_strdel(char* string, int position) { + if (string == NULL || position < 0) { + return; + } + + while (*string != '\0') { + if (position == 0) { + int chars_to_erase = U8_charsize(string); + int remaining_bytes = strlen(string) + 1; + memmove(string, string + chars_to_erase, remaining_bytes); + break; + } + + string = (char*)U8_next(string); + --position; + } +} + + + + + +static_inline FC_Rect FC_RectUnion(FC_Rect A, FC_Rect B) { + float x,x2,y,y2; + x = FC_MIN(A.x, B.x); + y = FC_MIN(A.y, B.y); + x2 = FC_MAX(A.x+A.w, B.x+B.w); + y2 = FC_MAX(A.y+A.h, B.y+B.h); + { + FC_Rect result = {x, y, FC_MAX(0, x2 - x), FC_MAX(0, y2 - y)}; + return result; + } +} + +// Adapted from SDL_IntersectRect +static_inline FC_Rect FC_RectIntersect(FC_Rect A, FC_Rect B) { + FC_Rect result; + float Amin, Amax, Bmin, Bmax; + + // Horizontal intersection + Amin = A.x; + Amax = Amin + A.w; + Bmin = B.x; + Bmax = Bmin + B.w; + if (Bmin > Amin) { + Amin = Bmin; + } + result.x = Amin; + if (Bmax < Amax) { + Amax = Bmax; + } + result.w = Amax - Amin > 0 ? Amax - Amin : 0; + + // Vertical intersection + Amin = A.y; + Amax = Amin + A.h; + Bmin = B.y; + Bmax = Bmin + B.h; + if (Bmin > Amin) { + Amin = Bmin; + } + result.y = Amin; + if (Bmax < Amax) { + Amax = Bmax; + } + result.h = Amax - Amin > 0 ? Amax - Amin : 0; + + return result; +} + + + + + + + + + + + + + + +FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) { + float w = srcrect->w * xscale; + float h = srcrect->h * yscale; + FC_Rect result; + + // FIXME: Why does the scaled offset look so wrong? +#ifdef FC_USE_SDL_GPU + { + GPU_Rect r = *srcrect; + GPU_BlitScale(src, &r, dest, x + xscale*r.w/2.0f, y + r.h/2.0f, xscale, yscale); + } +#else + { + int flip = SDL_FLIP_NONE; + if (xscale < 0) { + xscale = -xscale; + flip |= SDL_FLIP_HORIZONTAL; + } + if (yscale < 0) { + yscale = -yscale; + flip |= SDL_FLIP_VERTICAL; + } + + SDL_Rect r = *srcrect; + SDL_Rect dr = {(int)x, (int)y, (int)(xscale*r.w), (int)(yscale*r.h)}; + SDL_RenderCopyEx(dest, src, &r, &dr, 0, NULL, (SDL_RendererFlip)flip); + } +#endif + + result.x = x; + result.y = y; + result.w = w; + result.h = h; + return result; +} + +static FC_Rect (*fc_render_callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) = &FC_DefaultRenderCallback; + +void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)) { + if (callback == NULL) { + fc_render_callback = &FC_DefaultRenderCallback; + } + else { + fc_render_callback = callback; + } +} + +void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint) { + char a, b, c, d; + + if (result == NULL) { + return; + } + + a = (codepoint >> 24) & 0xFF; + b = (codepoint >> 16) & 0xFF; + c = (codepoint >> 8) & 0xFF; + d = codepoint & 0xFF; + + if (a == 0) { + if (b == 0) { + if (c == 0) { + result[0] = d; + result[1] = '\0'; + } + else { + result[0] = c; + result[1] = d; + result[2] = '\0'; + } + } + else { + result[0] = b; + result[1] = c; + result[2] = d; + result[3] = '\0'; + } + } + else { + result[0] = a; + result[1] = b; + result[2] = c; + result[3] = d; + result[4] = '\0'; + } +} + +Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer) { + Uint32 result = 0; + const char* str; + if (c == NULL || *c == NULL) { + return 0; + } + + str = *c; + if ((unsigned char)*str <= 0x7F) { + result = *str; + } + else if ((unsigned char)*str < 0xE0) { + result |= (unsigned char)(*str) << 8; + result |= (unsigned char)(*(str+1)); + if (advance_pointer) { + *c += 1; + } + } + else if ((unsigned char)*str < 0xF0) { + result |= (unsigned char)(*str) << 16; + result |= (unsigned char)(*(str+1)) << 8; + result |= (unsigned char)(*(str+2)); + if (advance_pointer) { + *c += 2; + } + } + else { + result |= (unsigned char)(*str) << 24; + result |= (unsigned char)(*(str+1)) << 16; + result |= (unsigned char)(*(str+2)) << 8; + result |= (unsigned char)(*(str+3)); + if (advance_pointer) { + *c += 3; + } + } + return result; +} + + +void FC_SetLoadingString(FC_Font* font, const char* string) { + if (font == NULL) { + return; + } + + free(font->loading_string); + font->loading_string = U8_strdup(string); +} + + +void FC_SetBufferSize(unsigned int size) { + free(fc_buffer); + if (size > 0) { + fc_buffer = (char*)malloc(size); + } + else { + fc_buffer = (char*)malloc(1024); + } +} + + + + + +// Constructors + +static void FC_Init(FC_Font* font) { + if (font == NULL) { + return; + } + +#ifndef FC_USE_SDL_GPU + font->renderer = NULL; +#endif + + font->ttf_source = NULL; + font->owns_ttf_source = 0; + + font->filter = FC_FILTER_NEAREST; + + font->default_color.r = 0; + font->default_color.g = 0; + font->default_color.b = 0; + FC_GET_ALPHA(font->default_color) = 255; + + font->height = 0; // ascent+descent + + font->maxWidth = 0; + font->baseline = 0; + font->ascent = 0; + font->descent = 0; + + font->lineSpacing = 0; + font->letterSpacing = 0; + + // Give a little offset for when filtering/mipmaps are used. Depending on mipmap level, this will still not be enough. + font->last_glyph.rect.x = FC_CACHE_PADDING; + font->last_glyph.rect.y = FC_CACHE_PADDING; + font->last_glyph.rect.w = 0; + font->last_glyph.rect.h = 0; + font->last_glyph.cache_level = 0; + + if (font->glyphs != NULL) { + FC_MapFree(font->glyphs); + } + + font->glyphs = FC_MapCreate(FC_DEFAULT_NUM_BUCKETS); + + font->glyph_cache_size = 3; + font->glyph_cache_count = 0; + + + font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*)); + + if (font->loading_string == NULL) { + font->loading_string = U8_strdup(FC_GetStringASCII()); + } + + if (fc_buffer == NULL) { + fc_buffer = (char*)malloc(1024); + } +} + +static Uint8 FC_GrowGlyphCache(FC_Font* font) { + if (font == NULL) { + return 0; + } +#ifdef FC_USE_SDL_GPU + GPU_Image* new_level = GPU_CreateImage(font->height * 12, font->height * 12, GPU_FORMAT_RGBA); +#else + SDL_Texture* new_level = SDL_CreateTexture(font->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, font->height * 12, font->height * 12); +#endif + if (new_level == NULL || !FC_SetGlyphCacheLevel(font, font->glyph_cache_count, new_level)) { + FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); +#ifdef FC_USE_SDL_GPU + GPU_FreeImage(new_level); +#else + SDL_DestroyTexture(new_level); +#endif + return 0; + } + return 1; +} + +static Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface) { + if (font == NULL || data_surface == NULL) { + return 0; + } +#ifdef FC_USE_SDL_GPU + GPU_Image* new_level = GPU_CopyImageFromSurface(data_surface); + if (FC_GetFilterMode(font) == FC_FILTER_LINEAR) { + GPU_SetImageFilter(new_level, GPU_FILTER_LINEAR); + } + else { + GPU_SetImageFilter(new_level, GPU_FILTER_NEAREST); + } +#else + SDL_Texture* new_level; + if (!fc_has_render_target_support) { + new_level = SDL_CreateTextureFromSurface(font->renderer, data_surface); + } + else { + // Must upload with render target enabled so we can put more glyphs on later + SDL_Renderer* renderer = font->renderer; + + // Set filter mode for new texture + const char* old_filter_mode = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); + if (FC_GetFilterMode(font) == FC_FILTER_LINEAR) { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + } + else { + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + } + + new_level = SDL_CreateTexture(renderer, data_surface->format->format, SDL_TEXTUREACCESS_TARGET, data_surface->w, data_surface->h); + SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); + + // Reset filter mode for the temp texture + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + + { + Uint8 r, g, b, a; + SDL_Texture* temp = SDL_CreateTextureFromSurface(renderer, data_surface); + SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_NONE); + SDL_SetRenderTarget(renderer, new_level); + + SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_SetRenderDrawColor(renderer, r, g, b, a); + + SDL_RenderCopy(renderer, temp, NULL, NULL); + SDL_SetRenderTarget(renderer, NULL); + + SDL_DestroyTexture(temp); + } + + // Reset to the old filter value + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_filter_mode); + + } +#endif + if (new_level == NULL || !FC_SetGlyphCacheLevel(font, cache_level, new_level)) { + FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); +#ifdef FC_USE_SDL_GPU + GPU_FreeImage(new_level); +#else + SDL_DestroyTexture(new_level); +#endif + return 0; + } + return 1; +} + +static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight) { + FC_Map* glyphs = font->glyphs; + FC_GlyphData* last_glyph = &font->last_glyph; + Uint16 height = font->height + FC_CACHE_PADDING; + + if (last_glyph->rect.x + last_glyph->rect.w + width >= maxWidth - FC_CACHE_PADDING) { + if (last_glyph->rect.y + height + height >= maxHeight - FC_CACHE_PADDING) { + // Get ready to pack on the next cache level when it is ready + last_glyph->cache_level = font->glyph_cache_count; + last_glyph->rect.x = FC_CACHE_PADDING; + last_glyph->rect.y = FC_CACHE_PADDING; + last_glyph->rect.w = 0; + return NULL; + } + else { + // Go to next row + last_glyph->rect.x = FC_CACHE_PADDING; + last_glyph->rect.y += height; + last_glyph->rect.w = 0; + } + } + + // Move to next space + last_glyph->rect.x += last_glyph->rect.w + 1 + FC_CACHE_PADDING; + last_glyph->rect.w = width; + + return FC_MapInsert(glyphs, codepoint, FC_MakeGlyphData(last_glyph->cache_level, last_glyph->rect.x, last_glyph->rect.y, last_glyph->rect.w, last_glyph->rect.h)); +} + + +FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level) { + if (font == NULL || cache_level < 0 || cache_level > font->glyph_cache_count) { + return NULL; + } + + return font->glyph_cache[cache_level]; +} + +Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture) { + if (font == NULL || cache_level < 0) { + return 0; + } + + // Must be sequentially added + if (cache_level > font->glyph_cache_count + 1) { + return 0; + } + + if (cache_level == font->glyph_cache_count) { + font->glyph_cache_count++; + + // Grow cache? + if (font->glyph_cache_count > font->glyph_cache_size) { + // Copy old cache to new one + int i; + FC_Image** new_cache; + new_cache = (FC_Image**)malloc(font->glyph_cache_count * sizeof(FC_Image*)); + for (i = 0; i < font->glyph_cache_size; ++i) { + new_cache[i] = font->glyph_cache[i]; + } + + // Save new cache + free(font->glyph_cache); + font->glyph_cache_size = font->glyph_cache_count; + font->glyph_cache = new_cache; + } + } + + font->glyph_cache[cache_level] = cache_texture; + return 1; +} + + +FC_Font* FC_CreateFont(void) { + FC_Font* font; + + font = (FC_Font*)malloc(sizeof(FC_Font)); + memset(font, 0, sizeof(FC_Font)); + + FC_Init(font); + + return font; +} + + +// Assume this many will be enough... +#define FC_LOAD_MAX_SURFACES 10 + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color) +#else +Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color) +#endif +{ + if (font == NULL || ttf == NULL) { + return 0; + } +#ifndef FC_USE_SDL_GPU + if (renderer == NULL) { + return 0; + } +#endif + + FC_ClearFont(font); + + + // Might as well check render target support here +#ifdef FC_USE_SDL_GPU + fc_has_render_target_support = GPU_IsFeatureEnabled(GPU_FEATURE_RENDER_TARGETS); +#else + SDL_RendererInfo info; + SDL_GetRendererInfo(renderer, &info); + fc_has_render_target_support = (info.flags & SDL_RENDERER_TARGETTEXTURE); + + font->renderer = renderer; +#endif + + font->ttf_source = ttf; + + //font->line_height = TTF_FontLineSkip(ttf); + font->height = TTF_FontHeight(ttf); + font->ascent = TTF_FontAscent(ttf); + font->descent = -TTF_FontDescent(ttf); + + font->baseline = font->height - font->descent; + + font->default_color = color; + + { + SDL_Color white = {255, 255, 255, 255}; + SDL_Surface* glyph_surf; + char buff[5]; + const char* buff_ptr = buff; + const char* source_string; + Uint8 packed = 0; + + // Copy glyphs from the surface to the font texture and store the position data + // Pack row by row into a square texture + // Try figuring out dimensions that make sense for the font size. + unsigned int w = font->height*12; + unsigned int h = font->height*12; + SDL_Surface* surfaces[FC_LOAD_MAX_SURFACES]; + int num_surfaces = 1; + surfaces[0] = FC_CreateSurface32(w, h); + font->last_glyph.rect.x = FC_CACHE_PADDING; + font->last_glyph.rect.y = FC_CACHE_PADDING; + font->last_glyph.rect.w = 0; + font->last_glyph.rect.h = font->height; + + memset(buff, 0, 5); + source_string = font->loading_string; + for (; *source_string != '\0'; source_string = U8_next(source_string)) { + if (!U8_charcpy(buff, source_string, 5)) { + continue; + } + glyph_surf = TTF_RenderUTF8_Blended(ttf, buff, white); + if (glyph_surf == NULL) { + continue; + } + + // Try packing. If it fails, create a new surface for the next cache level. + packed = (FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL); + if (!packed) { + int i = num_surfaces-1; + if (num_surfaces >= FC_LOAD_MAX_SURFACES) { + // Can't do any more! + FC_Log("SDL_FontCache error: Could not create enough cache surfaces to fit all of the loading string!\n"); + SDL_FreeSurface(glyph_surf); + break; + } + + // Upload the current surface to the glyph cache now so we can keep the cache level packing cursor up to date as we go. + FC_UploadGlyphCache(font, i, surfaces[i]); + SDL_FreeSurface(surfaces[i]); +#ifndef FC_USE_SDL_GPU + SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); +#endif + // Update the glyph cursor to the new cache level. We need to do this here because the actual cache lags behind our use of the packing above. + font->last_glyph.cache_level = num_surfaces; + + + surfaces[num_surfaces] = FC_CreateSurface32(w, h); + num_surfaces++; + } + + // Try packing for the new surface, then blit onto it. + if (packed || FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL) { + SDL_SetSurfaceBlendMode(glyph_surf, SDL_BLENDMODE_NONE); + SDL_Rect srcRect = {0, 0, glyph_surf->w, glyph_surf->h}; + SDL_Rect destrect = font->last_glyph.rect; + SDL_BlitSurface(glyph_surf, &srcRect, surfaces[num_surfaces-1], &destrect); + } + + SDL_FreeSurface(glyph_surf); + } + + { + int i = num_surfaces-1; + FC_UploadGlyphCache(font, i, surfaces[i]); + SDL_FreeSurface(surfaces[i]); +#ifndef FC_USE_SDL_GPU + SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); +#endif + } + } + + return 1; +} + + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) +#else +Uint8 FC_LoadFont(FC_Font* font, FC_Target* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) +#endif +{ + SDL_RWops* rwops; + + if (font == NULL) { + return 0; + } + + rwops = SDL_RWFromFile(filename_ttf, "rb"); + + if (rwops == NULL) { + FC_Log("Unable to open file for reading: %s \n", SDL_GetError()); + return 0; + } + +#ifdef FC_USE_SDL_GPU + return FC_LoadFont_RW(font, rwops, 1, pointSize, color, style); +#else + return FC_LoadFont_RW(font, renderer, rwops, 1, pointSize, color, style); +#endif +} + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) +#else +Uint8 FC_LoadFont_RW(FC_Font* font, FC_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) +#endif +{ + Uint8 result; + TTF_Font* ttf; + Uint8 outline; + + if (font == NULL) { + return 0; + } + + if (!TTF_WasInit() && TTF_Init() < 0) { + FC_Log("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); + if (own_rwops) { + SDL_RWclose(file_rwops_ttf); + } + return 0; + } + + ttf = TTF_OpenFontRW(file_rwops_ttf, own_rwops, pointSize); + + if (ttf == NULL) { + FC_Log("Unable to load TrueType font: %s \n", TTF_GetError()); + if (own_rwops) { + SDL_RWclose(file_rwops_ttf); + } + return 0; + } + + outline = (style & TTF_STYLE_OUTLINE); + if (outline) { + style &= ~TTF_STYLE_OUTLINE; + TTF_SetFontOutline(ttf, 1); + } + TTF_SetFontStyle(ttf, style); + +#ifdef FC_USE_SDL_GPU + result = FC_LoadFontFromTTF(font, ttf, color); +#else + result = FC_LoadFontFromTTF(font, renderer, ttf, color); +#endif + + // Can only load new (uncached) glyphs if we can keep the SDL_RWops open. + font->owns_ttf_source = own_rwops; + if (!own_rwops) { + TTF_CloseFont(font->ttf_source); + font->ttf_source = NULL; + } + + return result; +} + + +void FC_ClearFont(FC_Font* font) { + int i; + if (font == NULL) { + return; + } + + // Release resources + if (font->owns_ttf_source) { + TTF_CloseFont(font->ttf_source); + } + + font->owns_ttf_source = 0; + font->ttf_source = NULL; + + // Delete glyph map + FC_MapFree(font->glyphs); + font->glyphs = NULL; + + // Delete glyph cache + for (i = 0; i < font->glyph_cache_count; ++i) { +#ifdef FC_USE_SDL_GPU + GPU_FreeImage(font->glyph_cache[i]); +#else + SDL_DestroyTexture(font->glyph_cache[i]); +#endif + } + free(font->glyph_cache); + font->glyph_cache = NULL; + + // Reset font + FC_Init(font); +} + + +void FC_FreeFont(FC_Font* font) { + int i; + if (font == NULL) { + return; + } + + // Release resources + if (font->owns_ttf_source) { + TTF_CloseFont(font->ttf_source); + } + + // Delete glyph map + FC_MapFree(font->glyphs); + + // Delete glyph cache + for (i = 0; i < font->glyph_cache_count; ++i) { +#ifdef FC_USE_SDL_GPU + GPU_FreeImage(font->glyph_cache[i]); +#else + SDL_DestroyTexture(font->glyph_cache[i]); +#endif + } + free(font->glyph_cache); + + free(font->loading_string); + + free(font); +} + +int FC_GetNumCacheLevels(FC_Font* font) { + return font->glyph_cache_count; +} + +Uint8 FC_AddGlyphToCache(FC_Font* font, SDL_Surface* glyph_surface) { + if (font == NULL || glyph_surface == NULL) { + return 0; + } + + SDL_SetSurfaceBlendMode(glyph_surface, SDL_BLENDMODE_NONE); + FC_Image* dest = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); + if (dest == NULL) { + return 0; + } + +#ifdef FC_USE_SDL_GPU + { + GPU_Target* target = GPU_LoadTarget(dest); + if (target == NULL) { + return 0; + } + GPU_Image* img = GPU_CopyImageFromSurface(glyph_surface); + GPU_SetImageFilter(img, GPU_FILTER_NEAREST); + GPU_SetBlendMode(img, GPU_BLEND_SET); + + SDL_Rect destrect = font->last_glyph.rect; + GPU_Blit(img, NULL, target, destrect.x + destrect.w/2, destrect.y + destrect.h/2); + + GPU_FreeImage(img); + GPU_FreeTarget(target); + } +#else + { + SDL_Texture* img = SDL_CreateTextureFromSurface(font->renderer, glyph_surface); + + SDL_Rect destrect = font->last_glyph.rect; + SDL_SetRenderTarget(font->renderer, dest); + SDL_RenderCopy(font->renderer, img, NULL, &destrect); + SDL_SetRenderTarget(font->renderer, NULL); + SDL_DestroyTexture(img); + } +#endif + + return 1; +} + +Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint) { + FC_GlyphData* e = FC_MapFind(font->glyphs, codepoint); + if (e == NULL || result == NULL) { + char buff[5]; + int w, h; + SDL_Color white = {255, 255, 255, 255}; + SDL_Surface* surf; + FC_Image* cache_image; + + if (font->ttf_source == NULL) { + return 0; + } + + FC_GetUTF8FromCodepoint(buff, codepoint); + + cache_image = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); + if (cache_image == NULL) { + FC_Log("SDL_FontCache: Failed to load cache image, so cannot add new glyphs!\n"); + return 0; + } +#ifdef FC_USE_SDL_GPU + w = cache_image->w; + h = cache_image->h; +#else + SDL_QueryTexture(cache_image, NULL, NULL, &w, &h); +#endif + + surf = TTF_RenderUTF8_Blended(font->ttf_source, buff, white); + if (surf == NULL) { + return 0; + } + + e = FC_PackGlyphData(font, codepoint, surf->w, w, h); + if (e == NULL) { + // Grow the cache + FC_GrowGlyphCache(font); + + // Try packing again + e = FC_PackGlyphData(font, codepoint, surf->w, w, h); + if (e == NULL) { + SDL_FreeSurface(surf); + return 0; + } + } + + // Render onto the cache texture + FC_AddGlyphToCache(font, surf); + + SDL_FreeSurface(surf); + } + + if (result != NULL && e != NULL) { + *result = *e; + } + + return 1; +} + + +FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data) { + return FC_MapInsert(font->glyphs, codepoint, glyph_data); +} + + +// Drawing +static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) { + const char* c = text; + FC_Rect srcRect; + FC_Rect dstRect; + FC_Rect dirtyRect = FC_MakeRect(x, y, 0, 0); + + FC_GlyphData glyph; + Uint32 codepoint; + + float destX = x; + float destY = y; + float destH; + float destLineSpacing; + float destLetterSpacing; + + if (font == NULL) { + return dirtyRect; + } + + destH = font->height * scale.y; + destLineSpacing = font->lineSpacing*scale.y; + destLetterSpacing = font->letterSpacing*scale.x; + + if (c == NULL || font->glyph_cache_count == 0 || dest == NULL) { + return dirtyRect; + } + + int newlineX = x; + + for (; *c != '\0'; c++) { + if (*c == '\n') { + destX = newlineX; + destY += destH + destLineSpacing; + continue; + } + + codepoint = FC_GetCodepointFromUTF8(&c, 1); // Increments 'c' to skip the extra UTF-8 bytes + if (!FC_GetGlyphData(font, &glyph, codepoint)) { + codepoint = ' '; + if (!FC_GetGlyphData(font, &glyph, codepoint)) { + continue; // Skip bad characters + } + } + + if (codepoint == ' ') { + destX += glyph.rect.w*scale.x + destLetterSpacing; + continue; + } + /*if(destX >= dest->w) + continue; + if(destY >= dest->h) + continue;*/ + +#ifdef FC_USE_SDL_GPU + srcRect.x = glyph.rect.x; + srcRect.y = glyph.rect.y; + srcRect.w = glyph.rect.w; + srcRect.h = glyph.rect.h; +#else + srcRect = glyph.rect; +#endif + dstRect = fc_render_callback(FC_GetGlyphCacheLevel(font, glyph.cache_level), &srcRect, dest, destX, destY, scale.x, scale.y); + if (dirtyRect.w == 0 || dirtyRect.h == 0) { + dirtyRect = dstRect; + } + else { + dirtyRect = FC_RectUnion(dirtyRect, dstRect); + } + + destX += glyph.rect.w*scale.x + destLetterSpacing; + } + + return dirtyRect; +} + +static void set_color_for_all_caches(FC_Font* font, SDL_Color color) { + // TODO: How can I predict which glyph caches are to be used? + FC_Image* img; + int i; + int num_levels = FC_GetNumCacheLevels(font); + for (i = 0; i < num_levels; ++i) { + img = FC_GetGlyphCacheLevel(font, i); + set_color(img, color.r, color.g, color.b, FC_GET_ALPHA(color)); + } +} + +FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); +} + + + +typedef struct FC_StringList { + char* value; + struct FC_StringList* next; +} FC_StringList; + +void FC_StringListFree(FC_StringList* node) { + // Delete the nodes in order + while (node != NULL) { + FC_StringList* last = node; + node = node->next; + + free(last->value); + free(last); + } +} + +FC_StringList** FC_StringListPushBack(FC_StringList** node, char* value, Uint8 copy) { + // Get to the last node + while (node != NULL && *node != NULL) { + node = &(*node)->next; + } + + *node = (FC_StringList*)malloc(sizeof(FC_StringList)); + + (*node)->value = (copy? U8_strdup(value) : value); + (*node)->next = NULL; + + return node; +} + +static FC_StringList* FC_Explode(const char* text, char delimiter) { + FC_StringList* head; + FC_StringList* new_node; + FC_StringList** node; + const char* start; + const char* end; + unsigned int size; + if (text == NULL) { + return NULL; + } + + head = NULL; + node = &head; + + // Doesn't technically support UTF-8, but it's probably fine, right? + size = 0; + start = end = text; + while (1) { + if (*end == delimiter || *end == '\0') { + *node = (FC_StringList*)malloc(sizeof(FC_StringList)); + new_node = *node; + + new_node->value = (char*)malloc(size + 1); + memcpy(new_node->value, start, size); + new_node->value[size] = '\0'; + + new_node->next = NULL; + + if (*end == '\0') { + break; + } + + node = &((*node)->next); + start = end+1; + size = 0; + } + else { + ++size; + } + + ++end; + } + + return head; +} + +static FC_StringList* FC_ExplodeAndKeep(const char* text, char delimiter) { + FC_StringList* head; + FC_StringList* new_node; + FC_StringList** node; + const char* start; + const char* end; + unsigned int size; + if (text == NULL) { + return NULL; + } + + head = NULL; + node = &head; + + // Doesn't technically support UTF-8, but it's probably fine, right? + size = 0; + start = end = text; + while (1) { + if (*end == delimiter || *end == '\0') { + *node = (FC_StringList*)malloc(sizeof(FC_StringList)); + new_node = *node; + + new_node->value = (char*)malloc(size + 1); + memcpy(new_node->value, start, size); + new_node->value[size] = '\0'; + + new_node->next = NULL; + + if (*end == '\0') { + break; + } + + node = &((*node)->next); + start = end; + size = 1; + } + else { + ++size; + } + + ++end; + } + + return head; +} + +static void FC_RenderAlign(FC_Font* font, FC_Target* dest, float x, float y, int width, FC_Scale scale, FC_AlignEnum align, const char* text) { + switch (align) { + case FC_ALIGN_LEFT: + FC_RenderLeft(font, dest, x, y, scale, text); + break; + case FC_ALIGN_CENTER: + FC_RenderCenter(font, dest, x + width/2, y, scale, text); + break; + case FC_ALIGN_RIGHT: + FC_RenderRight(font, dest, x + width, y, scale, text); + break; + } +} + +static FC_StringList* FC_GetBufferFitToColumn(FC_Font* font, int width, FC_Scale scale, Uint8 keep_newlines) { + FC_StringList* result = NULL; + FC_StringList** current = &result; + + FC_StringList* ls, *iter; + + ls = (keep_newlines? FC_ExplodeAndKeep(fc_buffer, '\n') : FC_Explode(fc_buffer, '\n')); + for (iter = ls; iter != NULL; iter = iter->next) { + char* line = iter->value; + + // If line is too long, then add words one at a time until we go over. + if (width > 0 && FC_GetWidth(font, "%s", line) > width) { + FC_StringList* words, *word_iter; + + words = FC_Explode(line, ' '); + // Skip the first word for the iterator, so there will always be at least one word per line + line = new_concat(words->value, " "); + for (word_iter = words->next; word_iter != NULL; word_iter = word_iter->next) { + char* line_plus_word = new_concat(line, word_iter->value); + char* word_plus_space = new_concat(word_iter->value, " "); + if (FC_GetWidth(font, line_plus_word) > width) { + current = FC_StringListPushBack(current, line, 0); + + line = word_plus_space; + } + else { + replace_concat(&line, word_plus_space); + free(word_plus_space); + } + free(line_plus_word); + } + current = FC_StringListPushBack(current, line, 0); + FC_StringListFree(words); + } + else { + current = FC_StringListPushBack(current, line, 0); + iter->value = NULL; + } + } + FC_StringListFree(ls); + + return result; +} + +static void FC_DrawColumnFromBuffer(FC_Font* font, FC_Target* dest, FC_Rect box, int* total_height, FC_Scale scale, FC_AlignEnum align) { + int y = box.y; + FC_StringList* ls, *iter; + + ls = FC_GetBufferFitToColumn(font, box.w, scale, 0); + for (iter = ls; iter != NULL; iter = iter->next) { + FC_RenderAlign(font, dest, box.x, y, box.w, scale, align, iter->value); + y += FC_GetLineHeight(font); + } + FC_StringListFree(ls); + + if (total_height != NULL) { + *total_height = y - box.y; + } +} + +FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...) { + Uint8 useClip; + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(box.x, box.y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if (useClip) { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else { + newclip = box; + } + set_clip(dest, &newclip); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + if (useClip) { + set_clip(dest, &oldclip); + } + else { + set_clip(dest, NULL); + } + + return box; +} + +FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...) { + Uint8 useClip; + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(box.x, box.y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if (useClip) { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else { + newclip = box; + } + set_clip(dest, &newclip); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), align); + + if (useClip) { + set_clip(dest, &oldclip); + } + else { + set_clip(dest, NULL); + } + + return box; +} + +FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...) { + Uint8 useClip; + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(box.x, box.y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if (useClip) { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else { + newclip = box; + } + set_clip(dest, &newclip); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, scale, FC_ALIGN_LEFT); + + if (useClip) { + set_clip(dest, &oldclip); + } + else { + set_clip(dest, NULL); + } + + return box; +} + +FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...) { + Uint8 useClip; + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(box.x, box.y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if (useClip) { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else { + newclip = box; + } + set_clip(dest, &newclip); + + set_color_for_all_caches(font, color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + if (useClip) { + set_clip(dest, &oldclip); + } + else { + set_clip(dest, NULL); + } + + return box; +} + +FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...) { + Uint8 useClip; + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(box.x, box.y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if (useClip) { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else { + newclip = box; + } + set_clip(dest, &newclip); + + set_color_for_all_caches(font, effect.color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, effect.scale, effect.alignment); + + if (useClip) { + set_clip(dest, &oldclip); + } + else { + set_clip(dest, NULL); + } + + return box; +} + +FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) { + FC_Rect box = {x, y, width, 0}; + int total_height; + + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...) { + FC_Rect box = {x, y, width, 0}; + int total_height; + + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + switch (align) { + case FC_ALIGN_CENTER: + box.x -= width/2; + break; + case FC_ALIGN_RIGHT: + box.x -= width; + break; + default: + break; + } + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), align); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...) { + FC_Rect box = {x, y, width, 0}; + int total_height; + + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, scale, FC_ALIGN_LEFT); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...) { + FC_Rect box = {x, y, width, 0}; + int total_height; + + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, color); + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...) { + FC_Rect box = {x, y, width, 0}; + int total_height; + + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, effect.color); + + switch (effect.alignment) { + case FC_ALIGN_CENTER: + box.x -= width/2; + break; + case FC_ALIGN_RIGHT: + box.x -= width; + break; + default: + break; + } + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, effect.scale, effect.alignment); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) { + FC_Rect result = {x, y, 0, 0}; + if (text == NULL || font == NULL) { + return result; + } + + char* str = U8_strdup(text); + char* del = str; + char* c; + + // Go through str, when you find a \n, replace it with \0 and print it + // then move down, back, and continue. + for (c = str; *c != '\0';) { + if (*c == '\n') { + *c = '\0'; + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); + *c = '\n'; + c++; + str = c; + y += scale.y*font->height; + } + else { + c++; + } + } + + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); + + free(del); + return result; +} + +static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) { + FC_Rect result = {x, y, 0, 0}; + if (text == NULL || font == NULL) { + return result; + } + + char* str = U8_strdup(text); + char* del = str; + char* c; + + for (c = str; *c != '\0';) { + if (*c == '\n') { + *c = '\0'; + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); + *c = '\n'; + c++; + str = c; + y += scale.y*font->height; + } + else { + c++; + } + } + + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); + + free(del); + return result; +} + + + +FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + return FC_RenderLeft(font, dest, x, y, scale, fc_buffer); +} + +FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + FC_Rect result; + switch (align) { + case FC_ALIGN_LEFT: + result = FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); + break; + case FC_ALIGN_CENTER: + result = FC_RenderCenter(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); + break; + case FC_ALIGN_RIGHT: + result = FC_RenderRight(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); + break; + default: + result = FC_MakeRect(x, y, 0, 0); + break; + } + + return result; +} + +FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, color); + + return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); +} + + +FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return FC_MakeRect(x, y, 0, 0); + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, effect.color); + + FC_Rect result; + switch (effect.alignment) { + case FC_ALIGN_LEFT: + result = FC_RenderLeft(font, dest, x, y, effect.scale, fc_buffer); + break; + case FC_ALIGN_CENTER: + result = FC_RenderCenter(font, dest, x, y, effect.scale, fc_buffer); + break; + case FC_ALIGN_RIGHT: + result = FC_RenderRight(font, dest, x, y, effect.scale, fc_buffer); + break; + default: + result = FC_MakeRect(x, y, 0, 0); + break; + } + + return result; +} + + + + +// Getters + + +FC_FilterEnum FC_GetFilterMode(FC_Font* font) { + if (font == NULL) { + return FC_FILTER_NEAREST; + } + + return font->filter; +} + +Uint16 FC_GetLineHeight(FC_Font* font) { + if (font == NULL) { + return 0; + } + + return font->height; +} + +Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return 0; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + Uint16 numLines = 1; + const char* c; + + for (c = fc_buffer; *c != '\0'; c++) { + if (*c == '\n') { + numLines++; + } + } + + // Actual height of letter region + line spacing + return font->height*numLines + font->lineSpacing*(numLines - 1); //height*numLines; +} + +Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...) { + if (formatted_text == NULL || font == NULL) { + return 0; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + const char* c; + Uint16 width = 0; + Uint16 bigWidth = 0; // Allows for multi-line strings + + for (c = fc_buffer; *c != '\0'; c++) { + if (*c == '\n') { + bigWidth = bigWidth >= width? bigWidth : width; + width = 0; + continue; + } + + FC_GlyphData glyph; + Uint32 codepoint = FC_GetCodepointFromUTF8(&c, 1); + if (FC_GetGlyphData(font, &glyph, codepoint) || FC_GetGlyphData(font, &glyph, ' ')) { + width += glyph.rect.w; + } + } + bigWidth = bigWidth >= width? bigWidth : width; + + return bigWidth; +} + +// If width == -1, use no width limit +FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...) { + FC_Rect result = {0, 0, 1, FC_GetLineHeight(font)}; + FC_StringList* ls, *iter; + int num_lines = 0; + Uint8 done = 0; + + if (formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL) { + return result; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); + for (iter = ls; iter != NULL; iter = iter->next) { + char* line; + int i = 0; + + ++num_lines; + for (line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) { + ++i; + --position_index; + if (position_index == 0) { + // FIXME: Doesn't handle box-wrapped newlines correctly + line = (char*)U8_next(line); + line[0] = '\0'; + result.x = FC_GetWidth(font, "%s", iter->value); + done = 1; + break; + } + } + if (done) { + break; + } + } + FC_StringListFree(ls); + + if (num_lines > 0) { + result.y = (num_lines - 1) * FC_GetLineHeight(font); + } + + return result; +} + + +Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...) { + int y = 0; + + FC_StringList* ls, *iter; + + if (font == NULL) { + return 0; + } + + if (formatted_text == NULL || width == 0) { + return font->height; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); + for (iter = ls; iter != NULL; iter = iter->next) { + y += FC_GetLineHeight(font); + } + FC_StringListFree(ls); + + return y; +} + +static int FC_GetAscentFromCodepoint(FC_Font* font, Uint32 codepoint) { + FC_GlyphData glyph; + + if (font == NULL) { + return 0; + } + + // FIXME: Store ascent so we can return it here + FC_GetGlyphData(font, &glyph, codepoint); + return glyph.rect.h; +} + +static int FC_GetDescentFromCodepoint(FC_Font* font, Uint32 codepoint) { + FC_GlyphData glyph; + + if (font == NULL) { + return 0; + } + + // FIXME: Store descent so we can return it here + FC_GetGlyphData(font, &glyph, codepoint); + return glyph.rect.h; +} + +int FC_GetAscent(FC_Font* font, const char* formatted_text, ...) { + Uint32 codepoint; + int max, ascent; + const char* c; + + if (font == NULL) { + return 0; + } + + if (formatted_text == NULL) { + return font->ascent; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + max = 0; + c = fc_buffer; + + while (*c != '\0') { + codepoint = FC_GetCodepointFromUTF8(&c, 1); + if (codepoint != 0) { + ascent = FC_GetAscentFromCodepoint(font, codepoint); + if (ascent > max) { + max = ascent; + } + } + ++c; + } + return max; +} + +int FC_GetDescent(FC_Font* font, const char* formatted_text, ...) { + Uint32 codepoint; + int max, descent; + const char* c; + + if (font == NULL) { + return 0; + } + + if (formatted_text == NULL) { + return font->descent; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + max = 0; + c = fc_buffer; + + while (*c != '\0') { + codepoint = FC_GetCodepointFromUTF8(&c, 1); + if (codepoint != 0) { + descent = FC_GetDescentFromCodepoint(font, codepoint); + if (descent > max) { + max = descent; + } + } + ++c; + } + return max; +} + +int FC_GetBaseline(FC_Font* font) { + if (font == NULL) { + return 0; + } + + return font->baseline; +} + +int FC_GetSpacing(FC_Font* font) { + if (font == NULL) { + return 0; + } + + return font->letterSpacing; +} + +int FC_GetLineSpacing(FC_Font* font) { + if (font == NULL) { + return 0; + } + + return font->lineSpacing; +} + +Uint16 FC_GetMaxWidth(FC_Font* font) { + if (font == NULL) { + return 0; + } + + return font->maxWidth; +} + +SDL_Color FC_GetDefaultColor(FC_Font* font) { + if (font == NULL) { + SDL_Color c = {0,0,0,255}; + return c; + } + + return font->default_color; +} + + +Uint8 FC_InRect(float x, float y, FC_Rect input_rect) { + return (input_rect.x <= x && x <= input_rect.x + input_rect.w && input_rect.y <= y && y <= input_rect.y + input_rect.h); +} + +// TODO: Make it work with alignment +Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...) { + FC_StringList* ls, *iter; + Uint8 done = 0; + int height = FC_GetLineHeight(font); + Uint16 position = 0; + int current_x = 0; + int current_y = 0; + FC_GlyphData glyph_data; + + if (formatted_text == NULL || column_width == 0 || font == NULL) { + return 0; + } + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); + for (iter = ls; iter != NULL; iter = iter->next) { + char* line; + + for (line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) { + if (FC_GetGlyphData(font, &glyph_data, FC_GetCodepointFromUTF8((const char**)&line, 0))) { + if (FC_InRect(x, y, FC_MakeRect(current_x, current_y, glyph_data.rect.w, glyph_data.rect.h))) { + done = 1; + break; + } + + current_x += glyph_data.rect.w; + } + position++; + } + if (done) { + break; + } + + current_x = 0; + current_y += height; + if (y < current_y) { + break; + } + } + FC_StringListFree(ls); + + return position; +} + + + + +// Setters + + +void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter) { + if (font == NULL) { + return; + } + + if (font->filter != filter) { + font->filter = filter; + +#ifdef FC_USE_SDL_GPU + // Update each texture to use this filter mode + { + int i; + GPU_FilterEnum gpu_filter = GPU_FILTER_NEAREST; + if (FC_GetFilterMode(font) == FC_FILTER_LINEAR) { + gpu_filter = GPU_FILTER_LINEAR; + } + + for (i = 0; i < font->glyph_cache_count; ++i) { + GPU_SetImageFilter(font->glyph_cache[i], gpu_filter); + } + } +#endif + } +} + + +void FC_SetSpacing(FC_Font* font, int LetterSpacing) { + if (font == NULL) { + return; + } + + font->letterSpacing = LetterSpacing; +} + +void FC_SetLineSpacing(FC_Font* font, int LineSpacing) { + if (font == NULL) { + return; + } + + font->lineSpacing = LineSpacing; +} + +void FC_SetDefaultColor(FC_Font* font, SDL_Color color) { + if (font == NULL) { + return; + } + + font->default_color = color; +} + + + + + + diff --git a/source/code/Makefile b/source/code/Makefile index 53f4e42..b639edf 100644 --- a/source/code/Makefile +++ b/source/code/Makefile @@ -45,7 +45,7 @@ endif BASE_LIBS += -lphysfs -OFILES=main.o highscore.o ReadKeyboard.o joypad.o listFiles.o common.o stats.o CppSdlImageHolder.o Libs/NFont.o MenuSystem.o menudef.o puzzlehandler.o ${SAGO_O_FILES} +OFILES=main.o highscore.o ReadKeyboard.o joypad.o listFiles.o common.o stats.o Libs/NFont.o Libs/SDL_FontCache.o MenuSystem.o menudef.o puzzlehandler.o ${SAGO_O_FILES} ifeq ($(CROSS),i686-pc-mingw32-) OFILES += winicon.res diff --git a/source/code/MenuSystem.cpp b/source/code/MenuSystem.cpp index 2b0f4d1..5c2a26b 100644 --- a/source/code/MenuSystem.cpp +++ b/source/code/MenuSystem.cpp @@ -21,7 +21,6 @@ http://blockattack.sf.net =========================================================================== */ -#include #include "MenuSystem.h" #include "common.h" @@ -33,21 +32,14 @@ int mousey; using namespace std; -/*Draws a image from on a given Surface. Takes source image, destination surface and coordinates*/ -inline void DrawIMG(SDL_Surface* img, SDL_Surface* target, int x, int y) { - SDL_Rect dest; - dest.x = x; - dest.y = y; - SDL_BlitSurface(img, nullptr, target, &dest); -} ButtonGfx standardButton; -void ButtonGfx::setSurfaces(shared_ptr marked, shared_ptr unmarked) { +void ButtonGfx::setSurfaces(sago::SagoSprite& marked, sago::SagoSprite& unmarked) { this->marked = marked; this->unmarked = unmarked; - xsize=(marked)->GetWidth(); - ysize=(marked)->GetHeight(); + xsize=(marked).GetWidth(); + ysize=(marked).GetHeight(); if (verboseLevel) { cout << "Surfaces set, size: " <marked->PaintTo(screen, x, y); + gfx->marked.Draw(screen, SDL_GetTicks(), x, y); } else { - gfx->unmarked->PaintTo(screen,x,y); + gfx->unmarked.Draw(screen, SDL_GetTicks(), x, y); } - gfx->thefont.setDest(screen); - gfx->thefont.drawCenter(x+gfx->xsize/2,y+gfx->ysize/2-gfx->thefont.getHeight("%s", label.c_str())/2, "%s", label.c_str()); + gfx->thefont.draw(screen, x+gfx->xsize/2,y+gfx->ysize/2-gfx->thefont.getHeight("%s", label.c_str())/2, NFont::CENTER, "%s", label.c_str()); } void Button::setPopOnRun(bool popOnRun) { @@ -126,14 +117,14 @@ int Button::getHeight() { } void Menu::drawSelf() { - DrawIMG(backgroundImage,screen,0,0); + backgroundImage.Draw(screen, SDL_GetTicks(), 0, 0); vector::iterator it; for (it = buttons.begin(); it < buttons.end(); it++) { (*it)->drawToScreen(); } exit.drawToScreen(); - standardButton.thefont.draw(50, 50, "%s", title.c_str()); - mouse->PaintTo(screen,mousex,mousey); + standardButton.thefont.draw(screen, 50, 50, "%s", title.c_str()); + mouse.Draw(screen, SDL_GetTicks(), mousex, mousey); } void Menu::performClick(int x,int y) { @@ -171,14 +162,14 @@ void Menu::addButton(Button* b) { placeButtons(); } -Menu::Menu(SDL_Surface* screen) { +Menu::Menu(SDL_Renderer* screen) { this->screen = screen; buttons = vector(10); isSubmenu = true; exit.setLabel( _("Back") ); } -Menu::Menu(SDL_Surface* screen,bool submenu) { +Menu::Menu(SDL_Renderer* screen,bool submenu) { this->screen = screen; buttons = vector(0); isSubmenu = submenu; @@ -190,7 +181,7 @@ Menu::Menu(SDL_Surface* screen,bool submenu) { } } -Menu::Menu(SDL_Surface* screen, const string& title, bool submenu) { +Menu::Menu(SDL_Renderer* screen, const string& title, bool submenu) { this->screen = screen; buttons = vector(0); isSubmenu = submenu; @@ -298,6 +289,6 @@ void Menu::run() { } drawSelf(); - SDL_Flip(screen); + SDL_RenderPresent(screen); } } diff --git a/source/code/MenuSystem.h b/source/code/MenuSystem.h index 342b74e..6212cf2 100644 --- a/source/code/MenuSystem.h +++ b/source/code/MenuSystem.h @@ -30,21 +30,22 @@ http://blockattack.sf.net #include #include "Libs/NFont.h" #include "CppSdlImageHolder.hpp" +#include "sago/SagoSprite.hpp" #include //The ButtonGfx object hold common media for all buttons, so we can reskin them by only changeing one pointer struct ButtonGfx { //Holds the graphic for a button that is selected - std::shared_ptr marked; + sago::SagoSprite marked; //Holds the graphic for a button that is not selected - std::shared_ptr unmarked; + sago::SagoSprite unmarked; //The size of the buttons, so we don't have to ask w and h from the SDL Surfaces each time int xsize; int ysize; //A TTFont used for writing the label on the buttons NFont thefont; - void setSurfaces(std::shared_ptr marked, std::shared_ptr unmarked); + void setSurfaces(sago::SagoSprite& marked, sago::SagoSprite& unmarked); }; extern ButtonGfx standardButton; @@ -101,7 +102,7 @@ private: bool isSubmenu; //True if the menu is a submenu int marked; //The index of the marked button (for keyboard up/down) bool running; //The menu is running. The menu will terminate then this is false - SDL_Surface *screen; //Pointer to the screen to draw to + SDL_Renderer *screen; //Pointer to the screen to draw to std::string title; // SDL_Surface *background; //Pointer to the background image @@ -111,9 +112,9 @@ private: public: //numberOfItems is the expected numberOfItems for vector initialization //SubMenu is true by default - Menu(SDL_Surface *screen,bool isSubmenu); - Menu(SDL_Surface *screen); - Menu(SDL_Surface *screen, const std::string& title, bool isSubmenu); + Menu(SDL_Renderer *screen,bool isSubmenu); + Menu(SDL_Renderer *screen); + Menu(SDL_Renderer *screen, const std::string& title, bool isSubmenu); //Add a button to the menu void addButton(Button *b); diff --git a/source/code/ReadKeyboard.cpp b/source/code/ReadKeyboard.cpp index 4ee5278..b7c658e 100644 --- a/source/code/ReadKeyboard.cpp +++ b/source/code/ReadKeyboard.cpp @@ -47,7 +47,7 @@ ReadKeyboard::ReadKeyboard(const char* oldName) { void ReadKeyboard::putchar(const std::string& thing) { if (text_string.length() < maxLength) { - text_string.insert(position, thing); +// text_string.insert(position, thing); utf8::advance(position, 1, text_string.end()); } } @@ -62,7 +62,8 @@ void ReadKeyboard::removeChar() { } bool ReadKeyboard::ReadKey(const SDL_Event& key) { - if ( + return false; + /*if ( (key.unicode >= 'a' && key.unicode <= 'z') || (key.unicode >= '0' && key.unicode <= '9') || (key.unicode >= 'A' && key.unicode <= 'Z')) { @@ -72,10 +73,12 @@ bool ReadKeyboard::ReadKey(const SDL_Event& key) { if (key.unicode == '.' || key.unicode == ',') { ReadKeyboard::putchar('.'); } - return ReadKey(key.sym); + return ReadKey(key.sym);*/ } bool ReadKeyboard::ReadKey(SDL_Keycode keyPressed) { + return false; + /* if (keyPressed == SDLK_SPACE) { ReadKeyboard::putchar(' '); return true; @@ -110,7 +113,7 @@ bool ReadKeyboard::ReadKey(SDL_Keycode keyPressed) { utf8::next(position, text_string.end()); return true; } - return true; + return true;*/ } const char* ReadKeyboard::GetString() { diff --git a/source/code/global.hpp b/source/code/global.hpp index 6a379d2..da70f50 100644 --- a/source/code/global.hpp +++ b/source/code/global.hpp @@ -27,12 +27,13 @@ http://blockattack.sf.net #include "CppSdlImageHolder.hpp" #include "Libs/NFont.h" #include +#include "sago/SagoSpriteHolder.hpp" void MainMenu(); void ResetFullscreen(); -extern std::shared_ptr menuMarked; -extern std::shared_ptr menuUnmarked; +extern sago::SagoSprite menuMarked; +extern sago::SagoSprite menuUnmarked; extern NFont nf_scoreboard_font; extern bool MusicEnabled; //true if background music is enabled extern bool SoundEnabled; //true if sound effects is enabled @@ -40,8 +41,8 @@ extern bool bFullscreen; //true if game is running fullscreen extern std::string player1name; extern std::string player2name; extern SDL_Renderer *screen; //The whole screen; -extern std::shared_ptr mouse; -extern SDL_Surface* backgroundImage; +extern sago::SagoSprite mouse; +extern sago::SagoSprite backgroundImage; extern bool highPriority; extern int verboseLevel; diff --git a/source/code/joypad.cpp b/source/code/joypad.cpp index 8c680a2..d50d661 100644 --- a/source/code/joypad.cpp +++ b/source/code/joypad.cpp @@ -98,9 +98,9 @@ Joypad::Joypad() { but1REL=true; but2REL=true; int joynum = 0; - while ((SDL_JoystickOpened(joynum))&&(joynum=Joypad_number) { working = false; } diff --git a/source/code/main.cpp b/source/code/main.cpp index 6b58f25..6ddaa34 100644 --- a/source/code/main.cpp +++ b/source/code/main.cpp @@ -92,87 +92,13 @@ http://blockattack.sf.net using namespace std; -SDL_Surface* IMG_Load2(const char* path) { - if (!PHYSFS_exists(path)) { - cerr << "Error: File not in blockattack.data: " << path << endl; - return nullptr; //file doesn't exist - } - - PHYSFS_file* myfile = PHYSFS_openRead(path); - - // Get the lenght of the file - unsigned int m_size = PHYSFS_fileLength(myfile); - - // Get the file data. - char* m_data = new char[m_size]; - - int length_read = PHYSFS_read (myfile, m_data, 1, m_size); - - if (length_read != (int)m_size) { - delete [] m_data; - m_data = 0; - PHYSFS_close(myfile); - cerr << "Error: Curropt data file!" << endl; - return nullptr; - } - - PHYSFS_close(myfile); - -// And this is how you load an image from a memory buffer with SDL - SDL_RWops* rw = SDL_RWFromMem (m_data, m_size); - - //The above might fail an return null. - if (!rw) { - delete [] m_data; - m_data = 0; - PHYSFS_close(myfile); - cerr << "Error: Curropt data file!" << endl; - return nullptr; - } - - SDL_Surface* surface = IMG_Load_RW(rw,true); //the second argument tells the function to three RWops - - return surface; -} - -shared_ptr IMG_Load3(string path) { - if (!PHYSFS_exists(path.c_str())) { - cerr << "Error: File not in blockattack.data: " << path << endl; - throw exception(); - } - - PHYSFS_file* myfile = PHYSFS_openRead(path.c_str()); - - // Get the lenght of the file - unsigned int m_size = PHYSFS_fileLength(myfile); - - // Get the file data. - char* m_data = new char[m_size]; - - int length_read = PHYSFS_read (myfile, m_data, 1, m_size); - - if (length_read != (int)m_size) { - delete [] m_data; - m_data = 0; - PHYSFS_close(myfile); - cerr << "Error: Curropt data file!" << endl; - throw exception(); - } - - PHYSFS_close(myfile); - - shared_ptr surface(new CppSdl::CppSdlImageHolder(m_data, m_size)); - - return surface; -} - static void UnloadImages(); -static int InitImages(); +static int InitImages(sago::SagoSpriteHolder& holder); static string oldThemePath = "default"; static bool loaded = false; -void loadTheme(const string& themeName) { +void loadTheme(sago::SagoSpriteHolder& holder, const string& themeName) { if (loaded) { UnloadImages(); } @@ -186,7 +112,7 @@ void loadTheme(const string& themeName) { //Look in blockattack.data PHYSFS_addToSearchPath(((string)SHAREDIR+"/blockattack.data").c_str(), 1); //Look in folder - PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + PHYSFS_addToSearchPath( ((string) PHYSFS_getBaseDir()+"/data").c_str(), 1); //Look in home folder #if defined(__unix__) || defined(_WIN32) PHYSFS_addToSearchPath(home.c_str(), 1); @@ -196,7 +122,7 @@ void loadTheme(const string& themeName) { Config::getInstance()->setString("themename", themeName); } if (themeName.compare("default")==0 || (themeName.compare("start")==0)) { - InitImages(); + InitImages(holder); loaded =true; return; //Nothing more to do } @@ -205,7 +131,7 @@ void loadTheme(const string& themeName) { #if defined(__unix__) || defined(_WIN32) PHYSFS_addToSearchPath((home+(string)"/"+oldThemePath).c_str(), 0); #endif - InitImages(); + InitImages(holder); loaded = true; } @@ -351,105 +277,75 @@ Mix_Chunk* Mix_LoadWAV2(const char* path) { } //Load all image files to memory -static int InitImages() { - if (!((backgroundImage = IMG_Load2("gfx/background.png")) - && (bOptions = IMG_Load2("gfx/bOptions.png")) - && (bConfigure = IMG_Load2("gfx/bConfigure.png")) - && (bSelectPuzzle = IMG_Load2("gfx/bSelectPuzzle.png")) - && (bHighScore = IMG_Load2("gfx/bHighScore.png")) - && (bBack = IMG_Load2("gfx/bBlank.png")) - && (bForward = IMG_Load2("gfx/bForward.png")) - && (blackLine = IMG_Load2("gfx/blackLine.png")) - && (stageBobble = IMG_Load2("gfx/iStageClearLimit.png")) - && (bricks[0] = IMG_Load2("gfx/bricks/blue.png")) - && (bricks[1] = IMG_Load2("gfx/bricks/green.png")) - && (bricks[2] = IMG_Load2("gfx/bricks/purple.png")) - && (bricks[3] = IMG_Load2("gfx/bricks/red.png")) - && (bricks[4] = IMG_Load2("gfx/bricks/turkish.png")) - && (bricks[5] = IMG_Load2("gfx/bricks/yellow.png")) - && (bricks[6] = IMG_Load2("gfx/bricks/grey.png")) - && (crossover = IMG_Load2("gfx/crossover.png")) - && (balls[0] = IMG_Load2("gfx/balls/ballBlue.png")) - && (balls[1] = IMG_Load2("gfx/balls/ballGreen.png")) - && (balls[2] = IMG_Load2("gfx/balls/ballPurple.png")) - && (balls[3] = IMG_Load2("gfx/balls/ballRed.png")) - && (balls[4] = IMG_Load2("gfx/balls/ballTurkish.png")) - && (balls[5] = IMG_Load2("gfx/balls/ballYellow.png")) - && (balls[6] = IMG_Load2("gfx/balls/ballGray.png")) - && (cursor[0] = IMG_Load2("gfx/animations/cursor/1.png")) - && (cursor[1] = IMG_Load2("gfx/animations/cursor/2.png")) - && (bomb[0] = IMG_Load2("gfx/animations/bomb/bomb_1.png")) - && (bomb[1] = IMG_Load2("gfx/animations/bomb/bomb_2.png")) - && (ready[0] = IMG_Load2("gfx/animations/ready/ready_1.png")) - && (ready[1] = IMG_Load2("gfx/animations/ready/ready_2.png")) - && (explosion[0] = IMG_Load2("gfx/animations/explosion/0.png")) - && (explosion[1] = IMG_Load2("gfx/animations/explosion/1.png")) - && (explosion[2] = IMG_Load2("gfx/animations/explosion/2.png")) - && (explosion[3] = IMG_Load2("gfx/animations/explosion/3.png")) - && (counter[0] = IMG_Load2("gfx/counter/1.png")) - && (counter[1] = IMG_Load2("gfx/counter/2.png")) - && (counter[2] = IMG_Load2("gfx/counter/3.png")) - && (backBoard = IMG_Load2("gfx/BackBoard.png")) //not used, we just test if it exists :) - && (iGameOver = IMG_Load2("gfx/iGameOver.png")) - && (iWinner = IMG_Load2("gfx/iWinner.png")) - && (iDraw = IMG_Load2("gfx/iDraw.png")) - && (iLoser = IMG_Load2("gfx/iLoser.png")) - && (iChainBack = IMG_Load2("gfx/chainFrame.png")) -// && (optionsBack = IMG_Load2("gfx/options.png")) - && (bOn = IMG_Load2("gfx/bOn.png")) - && (bOff = IMG_Load2("gfx/bOff.png")) -// && (bChange = IMG_Load2("gfx/bChange.png")) - && (b1024 = IMG_Load2("gfx/b1024.png")) - && (dialogBox = IMG_Load2("gfx/dialogbox.png")) -// && (fileDialogBox = IMG_Load2("gfx/fileDialogbox.png")) - && (iLevelCheck = IMG_Load2("gfx/iLevelCheck.png")) - && (iLevelCheckBox = IMG_Load2("gfx/iLevelCheckBox.png")) - && (iLevelCheckBoxMarked = IMG_Load2("gfx/iLevelCheckBoxMarked.png")) - && (iCheckBoxArea = IMG_Load2("gfx/iCheckBoxArea.png")) - && (boardBackBack = IMG_Load2("gfx/boardBackBack.png")) - && (changeButtonsBack = IMG_Load2("gfx/changeButtonsBack.png")) - && (garbageTL = IMG_Load2("gfx/garbage/garbageTL.png")) - && (garbageT = IMG_Load2("gfx/garbage/garbageT.png")) - && (garbageTR = IMG_Load2("gfx/garbage/garbageTR.png")) - && (garbageR = IMG_Load2("gfx/garbage/garbageR.png")) - && (garbageBR = IMG_Load2("gfx/garbage/garbageBR.png")) - && (garbageB = IMG_Load2("gfx/garbage/garbageB.png")) - && (garbageBL = IMG_Load2("gfx/garbage/garbageBL.png")) - && (garbageL = IMG_Load2("gfx/garbage/garbageL.png")) - && (garbageFill = IMG_Load2("gfx/garbage/garbageFill.png")) - && (garbageML = IMG_Load2("gfx/garbage/garbageML.png")) - && (garbageM = IMG_Load2("gfx/garbage/garbageM.png")) - && (garbageMR = IMG_Load2("gfx/garbage/garbageMR.png")) - && (garbageGM = IMG_Load2("gfx/garbage/garbageGM.png")) - && (garbageGML = IMG_Load2("gfx/garbage/garbageGML.png")) - && (garbageGMR = IMG_Load2("gfx/garbage/garbageGMR.png")) - && (smiley[0] = IMG_Load2("gfx/smileys/0.png")) - && (smiley[1] = IMG_Load2("gfx/smileys/1.png")) - && (smiley[2] = IMG_Load2("gfx/smileys/2.png")) - && (smiley[3] = IMG_Load2("gfx/smileys/3.png")) - //new in 1.3.2 - && (transCover = IMG_Load2("gfx/transCover.png")) - //end new in 1.3.2 - //new in 1.4.0 - && (bSkip = IMG_Load2("gfx/bBlank.png")) - && (bNext = IMG_Load2("gfx/bBlank.png")) - && (bRetry = IMG_Load2("gfx/bBlank.png")) - )) - //if there was a problem ie. "File not found" - { - cerr << "Error: Failed to load image file: " << SDL_GetError() << endl; - exit(1); - } - try { - bNewGame = IMG_Load3("gfx/bNewGame.png"); - mouse = IMG_Load3("gfx/mouse.png"); - menuMarked = IMG_Load3("gfx/menu/marked.png"); - menuUnmarked = IMG_Load3("gfx/menu/unmarked.png"); - } - catch (exception& e) { - cerr << e.what() << endl; - exit(1); - } +static int InitImages(sago::SagoSpriteHolder& holder) { + bricks[0] = holder.GetSprite("blue"); + bricks[1] = holder.GetSprite("green"); + bricks[2] = holder.GetSprite("purple"); + bricks[3] = holder.GetSprite("red"); + bricks[4] = holder.GetSprite("turkish"); + bricks[5] = holder.GetSprite("yellow"); + bricks[6] = holder.GetSprite("grey"); + bomb = holder.GetSprite("block_bomb"); + backgroundImage = holder.GetSprite("background"); + bHighScore = holder.GetSprite("b_highscore"); + bBack = holder.GetSprite("b_back"); + bForward = holder.GetSprite("b_forward"); + blackLine = holder.GetSprite("black_line"); + stageBobble = holder.GetSprite("i_stage_clear_limit"); + crossover = holder.GetSprite("crossover"); + balls[0] = holder.GetSprite("ball_blue"); + balls[1] = holder.GetSprite("ball_green"); + balls[2] = holder.GetSprite("ball_purple"); + balls[3] = holder.GetSprite("ball_red"); + balls[4] = holder.GetSprite("ball_turkish"); + balls[5] = holder.GetSprite("ball_yellow"); + balls[6] = holder.GetSprite("ball_gray"); + cursor = holder.GetSprite("cursor"); + ready = holder.GetSprite("ready"); + explosion[0] = holder.GetSprite("explosion0"); + explosion[2] = holder.GetSprite("explosion1"); + explosion[3] = holder.GetSprite("explosion2"); + explosion[4] = holder.GetSprite("explosion3"); + counter[0] = holder.GetSprite("counter_1"); + counter[1] = holder.GetSprite("counter_2"); + counter[2] = holder.GetSprite("counter_3"); + iGameOver = holder.GetSprite("i_game_over"); + iWinner = holder.GetSprite("i_winner"); + iDraw = holder.GetSprite("i_draw"); + iLoser = holder.GetSprite("i_loser"); + //iChainFrame = holder.GetSprite("chain_frame"); + dialogBox = holder.GetSprite("dialogbox"); + iLevelCheck = holder.GetSprite("i_level_check"); + iLevelCheckBox = holder.GetSprite("i_level_check_box"); + iLevelCheckBoxMarked = holder.GetSprite("i_level_check_box_marked"); + iCheckBoxArea = holder.GetSprite("i_check_box_area"); + boardBackBack = holder.GetSprite("board_back_back"); + garbageTL = holder.GetSprite("garbage_tl"); + garbageT = holder.GetSprite("garbage_t"); + garbageTR = holder.GetSprite("garbage_tr"); + garbageR = holder.GetSprite("garbage_r"); + garbageBR = holder.GetSprite("garbage_br"); + garbageB = holder.GetSprite("garbage_b"); + garbageBL = holder.GetSprite("garbage_bl"); + garbageL = holder.GetSprite("garbage_l"); + garbageFill = holder.GetSprite("garbage_fill"); + garbageML = holder.GetSprite("garbage_ml"); + garbageM = holder.GetSprite("garbage_m"); + garbageMR = holder.GetSprite("garbage_mr"); + garbageGM = holder.GetSprite("garbage_gm"); + garbageGML = holder.GetSprite("garbage_gml"); + garbageGMR = holder.GetSprite("garbage_gmr"); + smiley[0] = holder.GetSprite("smileys0"); + smiley[1] = holder.GetSprite("smileys1"); + smiley[2] = holder.GetSprite("smileys2"); + smiley[3] = holder.GetSprite("smileys3"); + transCover = holder.GetSprite("trans_cover"); + bSkip = holder.GetSprite("b_skip"); + bNext = holder.GetSprite("b_next"); + bRetry = holder.GetSprite("b_retry"); + mouse = holder.GetSprite("mouse"); + menuMarked = holder.GetSprite("menu_marked"); + menuUnmarked = holder.GetSprite("menu_unmarked"); SDL_Color nf_button_color, nf_standard_blue_color, nf_standard_small_color; memset(&nf_button_color,0,sizeof(SDL_Color)); @@ -491,83 +387,6 @@ void UnloadImages() { if (verboseLevel) { cout << "Unloading data..." << endl; } - if (!NoSound) { //Only unload then it has been loaded! - Mix_HaltMusic(); - Mix_FreeMusic(bgMusic); - Mix_FreeMusic(highbeatMusic); - Mix_FreeChunk(boing); - Mix_FreeChunk(applause); - Mix_FreeChunk(photoClick); - Mix_FreeChunk(counterChunk); - Mix_FreeChunk(counterFinalChunk); - Mix_FreeChunk(typingChunk); - } - //Free surfaces: - //I think this will crash, at least it happend to me... - //Chrashes no more. Caused by an undocumented double free - SDL_FreeSurface(backgroundImage); - SDL_FreeSurface(bOptions); - SDL_FreeSurface(bConfigure); - SDL_FreeSurface(bSelectPuzzle); - SDL_FreeSurface(bHighScore); - SDL_FreeSurface(blackLine); - SDL_FreeSurface(stageBobble); - SDL_FreeSurface(bricks[0]); - SDL_FreeSurface(bricks[1]); - SDL_FreeSurface(bricks[2]); - SDL_FreeSurface(bricks[3]); - SDL_FreeSurface(bricks[4]); - SDL_FreeSurface(bricks[5]); - SDL_FreeSurface(bricks[6]); - SDL_FreeSurface(crossover); - SDL_FreeSurface(balls[0]); - SDL_FreeSurface(balls[1]); - SDL_FreeSurface(balls[2]); - SDL_FreeSurface(balls[3]); - SDL_FreeSurface(balls[4]); - SDL_FreeSurface(balls[5]); - SDL_FreeSurface(balls[6]); - SDL_FreeSurface(cursor[0]); - SDL_FreeSurface(cursor[1]); - SDL_FreeSurface(backBoard); //not used, we just test if it exists :) - SDL_FreeSurface(iGameOver); - SDL_FreeSurface(iWinner); - SDL_FreeSurface(iDraw); - SDL_FreeSurface(iLoser); - SDL_FreeSurface(iChainBack); - SDL_FreeSurface(bOn); - SDL_FreeSurface(bOff); - SDL_FreeSurface(b1024); - SDL_FreeSurface(dialogBox); - //SDL_FreeSurface(fileDialogBox); - SDL_FreeSurface(iLevelCheck); - SDL_FreeSurface(iLevelCheckBox); - SDL_FreeSurface(iLevelCheckBoxMarked); - SDL_FreeSurface(iCheckBoxArea); - SDL_FreeSurface(boardBackBack); - SDL_FreeSurface(changeButtonsBack); - SDL_FreeSurface(garbageTL); - SDL_FreeSurface(garbageT); - SDL_FreeSurface(garbageTR); - SDL_FreeSurface(garbageR); - SDL_FreeSurface(garbageBR); - SDL_FreeSurface(garbageB); - SDL_FreeSurface(garbageBL); - SDL_FreeSurface(garbageL); - SDL_FreeSurface(garbageFill); - SDL_FreeSurface(garbageML); - SDL_FreeSurface(garbageM); - SDL_FreeSurface(garbageMR); - SDL_FreeSurface(garbageGML); - SDL_FreeSurface(garbageGM); - SDL_FreeSurface(garbageGMR); - SDL_FreeSurface(smiley[0]); - SDL_FreeSurface(smiley[1]); - SDL_FreeSurface(smiley[2]); - SDL_FreeSurface(smiley[3]); - SDL_FreeSurface(transCover); - mouse->MakeNull(); - bNewGame->MakeNull(); } @@ -923,7 +742,7 @@ public: } void DrawImgBoardBounded(sago::SagoSprite& img, int x, int y) const { - DrawIMG_Bounded(img, screen, x+topx, y+topy, topx, topy, topx + backBoard->w, topy + backBoard->h); + DrawIMG_Bounded(img, screen, x+topx, y+topy, topx, topy, topx + backBoard.GetWidth(), topy + backBoard.GetHeight()); } void PrintTextCenteredBoard(int x, int y, const char* text) { @@ -994,10 +813,10 @@ private: if ((board[j][i]%10 != -1) && (board[j][i]%10 < 7) && ((board[j][i]/1000000)%10==0)) { DrawImgBoardBounded(bricks[board[j][i]%10], j*bsize, bsize*12-i*bsize-pixels); if ((board[j][i]/BLOCKWAIT)%10==1) { - DrawImgBoard(bomb[(ticks/BOMBTIME)%2], j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoard(bomb, j*bsize, bsize*12-i*bsize-pixels); } if ((board[j][i]/BLOCKHANG)%10==1) { - DrawImgBoardBounded(ready[(ticks/READYTIME)%2], j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(ready, j*bsize, bsize*12-i*bsize-pixels); } } @@ -1132,12 +951,11 @@ public: void DoPaintJob() { DrawIMG(boardBackBack,screen,this->GetTopX()-60,this->GetTopY()-68); - nf_scoreboard_font.draw(this->GetTopX()+310,this->GetTopY()-68+148,_("Score:") ); - nf_scoreboard_font.draw(this->GetTopX()+310,this->GetTopY()-68+197,_("Time:") ); - nf_scoreboard_font.draw(this->GetTopX()+310,this->GetTopY()-68+246,_("Chain:") ); - nf_scoreboard_font.draw(this->GetTopX()+310,this->GetTopY()-68+295,_("Speed:") ); + nf_scoreboard_font.draw(screen, this->GetTopX()+310,this->GetTopY()-68+148,_("Score:") ); + nf_scoreboard_font.draw(screen, this->GetTopX()+310,this->GetTopY()-68+197,_("Time:") ); + nf_scoreboard_font.draw(screen, this->GetTopX()+310,this->GetTopY()-68+246,_("Chain:") ); + nf_scoreboard_font.draw(screen, this->GetTopX()+310,this->GetTopY()-68+295,_("Speed:") ); DrawImgBoard(backBoard, 0, 0); - nf_standard_blue_font.setDest(screen); //reset to screen at the end of this funciton! PaintBricks(); if (stageClear) { @@ -1146,7 +964,7 @@ public: if (puzzleMode&&(!bGameOver)) { //We need to write nr. of moves left! strHolder = "Moves left: " + itoa(MovesLeft); - nf_standard_blue_font.draw(topx+5, topy+5, "%s",strHolder.c_str()); + nf_standard_blue_font.draw(screen, topx+5, topy+5, "%s",strHolder.c_str()); } if (puzzleMode && stageButtonStatus == SBpuzzleMode) { @@ -1164,7 +982,7 @@ public: } else { strHolder = "Last puzzle"; - nf_standard_blue_font.draw(topx+5, topy+5, "%s",strHolder.c_str()); + nf_standard_blue_font.draw(screen, topx+5, topy+5, "%s",strHolder.c_str()); } } if (stageClear && stageButtonStatus == SBstageClear) { @@ -1182,7 +1000,7 @@ public: } else { strHolder = "Last stage"; - nf_standard_blue_font.draw(topx+5, topy+5, "%s",strHolder.c_str()); + nf_standard_blue_font.draw(screen, topx+5, topy+5, "%s",strHolder.c_str()); } } @@ -1190,11 +1008,11 @@ public: if (AI_Enabled&&(!bGameOver)) { strHolder = "AI_status: " + itoa(AIstatus)+ ", "+ itoa(AIlineToClear); //NFont_Write( 5, 5, strHolder.c_str()); - nf_standard_blue_font.draw(topx+5, topy+5, "%s",strHolder.c_str()); + nf_standard_blue_font.draw(screen, topx+5, topy+5, "%s",strHolder.c_str()); } #endif if (!bGameOver) { - DrawImgBoard(cursor[(ticks/600)%2],cursorx*bsize-4,11*bsize-cursory*bsize-pixels-4); + DrawImgBoard(cursor,cursorx*bsize-4,11*bsize-cursory*bsize-pixels-4); } if (ticksisShuttingDown()) { DrawIMG(dialogBox,screen,x,y); NFont_Write(screen, x+40,y+76,rk.GetString()); @@ -1336,12 +1154,12 @@ bool OpenDialogbox(int x, int y, std::string& name) { accept = false; } else if (!(event.key.keysym.sym == SDLK_BACKSPACE)) { - if ((rk.ReadKey(event.key.keysym))&&(SoundEnabled)&&(!NoSound)) { + if ((rk.ReadKey(event))&&(SoundEnabled)&&(!NoSound)) { Mix_PlayChannel(1,typingChunk,0); } } else if (event.key.keysym.sym == SDLK_BACKSPACE) { - if ((rk.ReadKey(event.key.keysym))&&(SoundEnabled)&&(!NoSound)) { + if ((rk.ReadKey(event))&&(SoundEnabled)&&(!NoSound)) { Mix_PlayChannel(1,typingChunk,0); } } @@ -1349,12 +1167,11 @@ bool OpenDialogbox(int x, int y, std::string& name) { } //while(event) - SDL_Flip(screen); //Update screen + SDL_RenderPresent(screen); //Update screen } //while(!done) name = rk.GetString(); bScreenLocked = false; showDialog = false; - unicodeScope.Release(); return accept; } @@ -1362,10 +1179,10 @@ bool OpenDialogbox(int x, int y, std::string& name) { void DrawHighscores(int x, int y, bool endless) { DrawIMG(backgroundImage,screen,0,0); if (endless) { - nf_standard_blue_font.draw(x+100,y+100, "%s",_("Endless:") ); + nf_standard_blue_font.draw(screen, x+100,y+100, "%s",_("Endless:") ); } else { - nf_standard_blue_font.draw(x+100,y+100, "%s",_("Time Trial:") ); + nf_standard_blue_font.draw(screen, x+100,y+100, "%s",_("Time Trial:") ); } for (int i =0; i<10; i++) { char playerScore[32]; @@ -1382,8 +1199,8 @@ void DrawHighscores(int x, int y, bool endless) { else { strcpy(playerName,theTopScoresTimeTrial.getScoreName(i)); } - nf_standard_blue_font.draw(x+420,y+150+i*35, "%s",playerScore); - nf_standard_blue_font.draw(x+60,y+150+i*35, "%s",playerName); + nf_standard_blue_font.draw(screen, x+420,y+150+i*35, "%s",playerScore); + nf_standard_blue_font.draw(screen, x+60,y+150+i*35, "%s",playerName); } } @@ -1478,9 +1295,9 @@ void OpenScoresDisplay() { //Draw buttons: DrawIMG(bHighScore,screen,scoreX,scoreY); DrawIMG(bBack,screen,backX,backY); - nf_button_font.drawCenter(backX+60,backY+10,_("Back")); + nf_button_font.draw(screen, backX+60,backY+10, NFont::CENTER ,_("Back")); DrawIMG(bNext,screen,nextX,nextY); - nf_button_font.drawCenter(nextX+60,nextY+10,_("Next")); + nf_button_font.draw(screen, nextX+60,nextY+10, NFont::CENTER,_("Next")); //Draw page number string pageXofY = (boost::format(_("Page %1% of %2%") )%(page+1)%numberOfPages).str(); @@ -1560,9 +1377,8 @@ void OpenScoresDisplay() { } } - //DrawIMG(mouse,screen,mousex,mousey); - mouse->PaintTo(screen,mousex,mousey); - SDL_Flip(screen); //Update screen + mouse.Draw(screen, SDL_GetTicks(), mousex, mousey); + SDL_RenderPresent(screen); } @@ -1586,11 +1402,10 @@ bool OpenFileDialogbox(int x, int y, char* name) { while (!done && !Config::getInstance()->isShuttingDown()) { DrawIMG(backgroundImage,screen,0,0); DrawIMG(bForward,screen,x+460,y+420); - nf_button_font.drawCenter(x+20+60, y+420+10, _("Forward")); + nf_button_font.draw(screen, x+20+60, y+420+10, NFont::CENTER, _("Forward")); DrawIMG(bBack,screen,x+20,y+420); - nf_button_font.drawCenter(x+20+60, y+420+10, _("Back")); + nf_button_font.draw(screen, x+20+60, y+420+10, NFont::CENTER, _("Back")); const int nrOfFiles = 10; - DrawIMG(changeButtonsBack,screen,x,y); for (int i=0; iPaintTo(screen,mousex,mousey); - SDL_Flip(screen); //Update screen + mouse.Draw(screen, SDL_GetTicks(), mousex, mousey); + SDL_RenderPresent(screen); //Update screen } return true; } @@ -1670,8 +1484,9 @@ static void DrawBalls() { //cout << "Printing text: " << theTextManeger.textArray[i].getText() << endl; int x = theTextManeger.textArray[i].getX()-12; int y = theTextManeger.textArray[i].getY()-12; - DrawIMG(iChainBack,screen,x,y); - nf_standard_small_font.drawCenter(x+12,y+7, "%s",theTextManeger.textArray[i].getText()); + //DrawIMG(iChainBack,screen,x,y); + + nf_standard_small_font.draw(screen, x+12,y+7, NFont::CENTER, "%s",theTextManeger.textArray[i].getText()); } } //for } //DrawBalls @@ -1871,10 +1686,10 @@ void DrawEverything(int xsize, int ysize,BlockGameSdl* theGame, BlockGameSdl* th } //NFont_Write(screen, 800,4,FPS); - nf_standard_blue_font.draw(800, 4, "%s", FPS); + nf_standard_blue_font.draw(screen, 800, 4, "%s", FPS); #endif - //SDL_Flip(screen); Update screen is now called outside DrawEvrything, bacause the mouse needs to be painted + //SDL_RenderPresent(screen); Update screen is now called outside DrawEvrything, bacause the mouse needs to be painted } @@ -2011,7 +1826,7 @@ int PuzzleLevelSelect(int Type) { } } - SDL_GetKeyState(nullptr); + SDL_GetKeyboardState(nullptr); SDL_GetMouseState(&mousex,&mousey); if (mousex != oldmousex || mousey != oldmousey) { @@ -2072,9 +1887,8 @@ int PuzzleLevelSelect(int Type) { NFont_Write(screen, 200,600,totalString.c_str()); } - //DrawIMG(mouse,screen,mousex,mousey); - mouse->PaintTo(screen,mousex,mousey); - SDL_Flip(screen); //draws it all to the screen + mouse.Draw(screen, SDL_GetTicks(), mousex, mousey); + SDL_RenderPresent(screen); //draws it all to the screen } DrawIMG(backgroundImage, screen, 0, 0); @@ -2416,41 +2230,41 @@ int main(int argc, char* argv[]) { joyplay2 = (bool)configSettings->getInt("joypad2"); if (configSettings->exists("player1keyup")) { - keySettings[0].up = (SDLKey)configSettings->getInt("player1keyup"); + keySettings[0].up = (SDL_Keycode)configSettings->getInt("player1keyup"); } if (configSettings->exists("player1keydown")) { - keySettings[0].down = (SDLKey)configSettings->getInt("player1keydown"); + keySettings[0].down = (SDL_Keycode)configSettings->getInt("player1keydown"); } if (configSettings->exists("player1keyleft")) { - keySettings[0].left = (SDLKey)configSettings->getInt("player1keyleft"); + keySettings[0].left = (SDL_Keycode)configSettings->getInt("player1keyleft"); } if (configSettings->exists("player1keyright")) { - keySettings[0].right = (SDLKey)configSettings->getInt("player1keyright"); + keySettings[0].right = (SDL_Keycode)configSettings->getInt("player1keyright"); } if (configSettings->exists("player1keychange")) { - keySettings[0].change = (SDLKey)configSettings->getInt("player1keychange"); + keySettings[0].change = (SDL_Keycode)configSettings->getInt("player1keychange"); } if (configSettings->exists("player1keypush")) { - keySettings[0].push = (SDLKey)configSettings->getInt("player1keypush"); + keySettings[0].push = (SDL_Keycode)configSettings->getInt("player1keypush"); } if (configSettings->exists("player2keyup")) { - keySettings[2].up = (SDLKey)configSettings->getInt("player2keyup"); + keySettings[2].up = (SDL_Keycode)configSettings->getInt("player2keyup"); } if (configSettings->exists("player2keydown")) { - keySettings[2].down = (SDLKey)configSettings->getInt("player2keydown"); + keySettings[2].down = (SDL_Keycode)configSettings->getInt("player2keydown"); } if (configSettings->exists("player2keyleft")) { - keySettings[2].left = (SDLKey)configSettings->getInt("player2keyleft"); + keySettings[2].left = (SDL_Keycode)configSettings->getInt("player2keyleft"); } if (configSettings->exists("player2keyright")) { - keySettings[2].right = (SDLKey)configSettings->getInt("player2keyright"); + keySettings[2].right = (SDL_Keycode)configSettings->getInt("player2keyright"); } if (configSettings->exists("player2keychange")) { - keySettings[2].change = (SDLKey)configSettings->getInt("player2keychange"); + keySettings[2].change = (SDL_Keycode)configSettings->getInt("player2keychange"); } if (configSettings->exists("player2keypush")) { - keySettings[2].push = (SDLKey)configSettings->getInt("player2keypush"); + keySettings[2].push = (SDL_Keycode)configSettings->getInt("player2keypush"); } if (configSettings->exists("player1name")) { player1name = configSettings->getString("player1name"); @@ -2501,11 +2315,14 @@ int main(int argc, char* argv[]) { //Init the file system abstraction layer PHYSFS_init(argv[0]); + PHYSFS_addToSearchPath( ((string) PHYSFS_getBaseDir()+"/data").c_str(), 1); //Load default theme - loadTheme(Config::getInstance()->getString("themename")); + sago::SagoDataHolder d(renderer); + sago::SagoSpriteHolder spriteholder(d); + loadTheme(spriteholder, Config::getInstance()->getString("themename")); //Now sets the icon: - SDL_Surface* icon = IMG_Load2("gfx/icon.png"); - SDL_WM_SetIcon(icon,nullptr); + //SDL_Surface* icon = IMG_Load2("gfx/icon.png"); + //SDL_WM_SetIcon(icon,nullptr); if (verboseLevel) { cout << "Images loaded" << endl; @@ -2538,7 +2355,7 @@ int main(int argc, char* argv[]) { } DrawIMG(backgroundImage, screen, 0, 0); DrawEverything(xsize,ysize,&theGame,&theGame2); - SDL_Flip(screen); + SDL_RenderPresent(screen); //game loop MainMenu(); @@ -2601,7 +2418,7 @@ int main(int argc, char* argv[]) { int runGame(int gametype, int level) { - Uint8* keys; + const Uint8* keys; int mousex, mousey; //Mouse coordinates bScreenLocked = false; theTopScoresEndless = Highscore(1); @@ -2723,7 +2540,7 @@ int runGame(int gametype, int level) { mustsetupgame = false; DrawIMG(backgroundImage, screen, 0, 0); DrawEverything(xsize,ysize,&theGame,&theGame2); - SDL_Flip(screen); + SDL_RenderPresent(screen); } if (!(highPriority)) { @@ -2851,7 +2668,7 @@ int runGame(int gametype, int level) { **************************** Repeating start ************************** **********************************************************************/ - keys = SDL_GetKeyState(nullptr); + keys = SDL_GetKeyboardState(nullptr); //Also the joysticks: //Repeating not implemented @@ -3078,7 +2895,7 @@ int runGame(int gametype, int level) { **********************************************************************/ - SDL_GetKeyState(nullptr); + SDL_GetKeyboardState(nullptr); SDL_GetMouseState(&mousex,&mousey); @@ -3301,8 +3118,8 @@ int runGame(int gametype, int level) { oldMousex = mousex; oldMousey = mousey; //Draw the mouse: - mouse->PaintTo(screen,mousex,mousey); - SDL_Flip(screen); + mouse.Draw(screen, SDL_GetTicks(), mousex, mousey); + SDL_RenderPresent(screen); } //game loop return 0; } diff --git a/source/code/mainVars.hpp b/source/code/mainVars.hpp index de70e7e..4f66632 100644 --- a/source/code/mainVars.hpp +++ b/source/code/mainVars.hpp @@ -36,93 +36,86 @@ http://blockattack.sf.net #define BOMBTIME 200 #define CURSORTIME 200 +#ifndef SHAREDIR +#define SHAREDIR "." +#endif + const char sharedir[] = SHAREDIR; //All graphic in the game (as pointers): -SDL_Surface *backgroundImage; //Stores the background image -static SDL_Surface *backBoard; //Stores the background to the board -static std::shared_ptr bNewGame; //The New Game botton -static SDL_Surface *bOptions; //The Options botton -static SDL_Surface *bConfigure; //The configure button -static SDL_Surface *bSelectPuzzle; //The Select Puzzle Button -static SDL_Surface *bBack; //The "Back" button -static SDL_Surface *bForward; //The "forward" button -static SDL_Surface *iChainBack; +sago::SagoSprite backgroundImage; //Stores the background image +static sago::SagoSprite backBoard; //Stores the background to the board +static sago::SagoSprite bBack; //The "Back" button +static sago::SagoSprite bForward; //The "forward" button #if NETWORK -//static SDL_Surface *bNetwork; -//static SDL_Surface *bConnect; -//static SDL_Surface *bHost; +//static sago::SagoSprite bNetwork; +//static sago::SagoSprite bConnect; +//static sago::SagoSprite bHost; #endif -static SDL_Surface *bHighScore; //The High Score botton -static SDL_Surface *blackLine; //The seperator in stage clear -static SDL_Surface *stageBobble; //The bobble instage clear +static sago::SagoSprite bHighScore; //The High Score botton +static sago::SagoSprite blackLine; //The seperator in stage clear +static sago::SagoSprite stageBobble; //The bobble instage clear SDL_Renderer *screen; //The whole screen; -static SDL_Surface *iGameOver; //The gameOver image -static SDL_Surface *iWinner; //the "winner" image -static SDL_Surface *iDraw; //the "draw" image -static SDL_Surface *iLoser; //the "loser" image +static sago::SagoSprite iGameOver; //The gameOver image +static sago::SagoSprite iWinner; //the "winner" image +static sago::SagoSprite iDraw; //the "draw" image +static sago::SagoSprite iLoser; //the "loser" image //Animations: -static SDL_Surface *cursor[2]; //The animated cursor -static SDL_Surface *bomb[2]; //Bomb then the bricks should blow -static SDL_Surface *ready[2]; //Before the blocks fall -static SDL_Surface *explosion[4]; //Then a block explodes +static sago::SagoSprite cursor; //The animated cursor +static sago::SagoSprite bomb; +static sago::SagoSprite ready; //Before the blocks fall +static sago::SagoSprite explosion[4]; //Then a block explodes //Animations end -static SDL_Surface *counter[3]; //Counts down from 3 +static sago::SagoSprite counter[3]; //Counts down from 3 static sago::SagoSprite bricks[7]; //The bricks, saved in an array of pointers -static SDL_Surface *crossover; //Cross the bricks that will be cleared soon -static SDL_Surface *balls[7]; //The balls (the small ones that jump around) -static SDL_Surface *changeButtonsBack; -static SDL_Surface *dialogBox; -static SDL_Surface *bOn; -static SDL_Surface *bOff; -static SDL_Surface *b1024; -static SDL_Surface *iLevelCheck; //To the level select screen -static SDL_Surface *iLevelCheckBox; -static SDL_Surface *iLevelCheckBoxMarked; -static SDL_Surface *iCheckBoxArea; -static SDL_Surface *boardBackBack; -static SDL_Surface *garbageTL; //the Garbage Blocks -static SDL_Surface *garbageT; -static SDL_Surface *garbageTR; -static SDL_Surface *garbageR; -static SDL_Surface *garbageBR; -static SDL_Surface *garbageB; -static SDL_Surface *garbageBL; -static SDL_Surface *garbageL; -static SDL_Surface *garbageFill; -static SDL_Surface *garbageM; -static SDL_Surface *garbageML; -static SDL_Surface *garbageMR; -static SDL_Surface *smiley[4]; -static SDL_Surface *garbageGM; -static SDL_Surface *garbageGML; -static SDL_Surface *garbageGMR; -static SDL_Surface *transCover; //The transperant block, covers the upcomming +static sago::SagoSprite crossover; //Cross the bricks that will be cleared soon +static sago::SagoSprite balls[7]; //The balls (the small ones that jump around) +static sago::SagoSprite dialogBox; +static sago::SagoSprite iLevelCheck; //To the level select screen +static sago::SagoSprite iLevelCheckBox; +static sago::SagoSprite iLevelCheckBoxMarked; +static sago::SagoSprite iCheckBoxArea; +static sago::SagoSprite boardBackBack; +static sago::SagoSprite garbageTL; //the Garbage Blocks +static sago::SagoSprite garbageT; +static sago::SagoSprite garbageTR; +static sago::SagoSprite garbageR; +static sago::SagoSprite garbageBR; +static sago::SagoSprite garbageB; +static sago::SagoSprite garbageBL; +static sago::SagoSprite garbageL; +static sago::SagoSprite garbageFill; +static sago::SagoSprite garbageM; +static sago::SagoSprite garbageML; +static sago::SagoSprite garbageMR; +static sago::SagoSprite smiley[4]; +static sago::SagoSprite garbageGM; +static sago::SagoSprite garbageGML; +static sago::SagoSprite garbageGMR; +static sago::SagoSprite transCover; //The transperant block, covers the upcomming #if LEVELEDITOR -static SDL_Surface *bCreateFile; -static SDL_Surface *bDeletePuzzle; -static SDL_Surface *bLoadFile; -static SDL_Surface *bMoveBack; -static SDL_Surface *bMoveDown; -static SDL_Surface *bMoveForward; -static SDL_Surface *bMoveLeft; -static SDL_Surface *bMoveRight; -static SDL_Surface *bMoveUp; -static SDL_Surface *bNewPuzzle; -static SDL_Surface *bSaveFileAs; -static SDL_Surface *bSavePuzzle; -static SDL_Surface *bSaveToFile; -static SDL_Surface *bTestPuzzle; +static sago::SagoSprite bCreateFile; +static sago::SagoSprite bDeletePuzzle; +static sago::SagoSprite bLoadFile; +static sago::SagoSprite bMoveBack; +static sago::SagoSprite bMoveDown; +static sago::SagoSprite bMoveForward; +static sago::SagoSprite bMoveLeft; +static sago::SagoSprite bMoveRight; +static sago::SagoSprite bMoveUp; +static sago::SagoSprite bNewPuzzle; +static sago::SagoSprite bSaveFileAs; +static sago::SagoSprite bSavePuzzle; +static sago::SagoSprite bSaveToFile; +static sago::SagoSprite bTestPuzzle; #endif -static SDL_Surface *bSkip; -static SDL_Surface *bRetry; -static SDL_Surface *bNext; - -std::shared_ptr menuMarked; -std::shared_ptr menuUnmarked; -std::shared_ptr mouse; +static sago::SagoSprite bSkip; +static sago::SagoSprite bRetry; +static sago::SagoSprite bNext; -static SDL_Surface *tmp; //a temporary surface to use DisplayFormat +sago::SagoSprite menuMarked; +sago::SagoSprite menuUnmarked; +sago::SagoSprite mouse; static NFont nf_button_font; //Font used for buttons! NFont nf_scoreboard_font; diff --git a/source/code/menudef.cpp b/source/code/menudef.cpp index 36d33cc..5b26cad 100644 --- a/source/code/menudef.cpp +++ b/source/code/menudef.cpp @@ -36,12 +36,12 @@ static void PrintHi(Button* b) { //Stores the controls struct control { - SDLKey up; - SDLKey down; - SDLKey left; - SDLKey right; - SDLKey change; - SDLKey push; + SDL_Keycode up; + SDL_Keycode down; + SDL_Keycode left; + SDL_Keycode right; + SDL_Keycode change; + SDL_Keycode push; }; bool OpenDialogbox(int x, int y, std::string& name); @@ -49,7 +49,7 @@ void OpenScoresDisplay(); extern control keySettings[3]; //Function to return the name of a key, to be displayed... -static string getKeyName(SDLKey key) { +static string getKeyName(SDL_Keycode key) { string keyname(SDL_GetKeyName(key)); if (verboseLevel) { cout << key << " translated to " << keyname << endl; @@ -59,15 +59,15 @@ static string getKeyName(SDLKey key) { class Button_changekey : public Button { private: - SDLKey* m_key2change; + SDL_Keycode* m_key2change; string m_keyname; public: - Button_changekey(SDLKey* key, string keyname); + Button_changekey(SDL_Keycode* key, string keyname); void doAction(); }; -Button_changekey::Button_changekey(SDLKey* key, string keyname) { +Button_changekey::Button_changekey(SDL_Keycode* key, string keyname) { m_key2change = key; m_keyname = keyname; setLabel(m_keyname+" : "+getKeyName(*m_key2change)); diff --git a/source/code/sago/SagoSprite.cpp b/source/code/sago/SagoSprite.cpp index b269dcd..cb2244e 100644 --- a/source/code/sago/SagoSprite.cpp +++ b/source/code/sago/SagoSprite.cpp @@ -46,12 +46,12 @@ SagoSprite::SagoSprite(const SagoDataHolder& texHolder, const std::string& textu data->aniFrameTime = animationFrameLength; } -SagoSprite::SagoSprite(const SagoSprite& base) : data(new SagoSpriteData(*other.data)) { +SagoSprite::SagoSprite(const SagoSprite& base) : data(new SagoSpriteData(*base.data)) { } -SagoSprite& operator=(const SagoSprite& base) { - *data = *other.data; +SagoSprite& SagoSprite::operator=(const SagoSprite& base) { + *data = *base.data; return *this; } @@ -131,4 +131,11 @@ void SagoSprite::SetOrigin(const SDL_Rect& newOrigin) { data->origin = newOrigin; } +int SagoSprite::GetWidth() const { + return data->imgCord.w; +} +int SagoSprite::GetHeight() const { + return data->imgCord.h; +} + } //namespace sago \ No newline at end of file diff --git a/source/code/sago/SagoSprite.hpp b/source/code/sago/SagoSprite.hpp index 8443192..bb7d9ec 100644 --- a/source/code/sago/SagoSprite.hpp +++ b/source/code/sago/SagoSprite.hpp @@ -66,6 +66,8 @@ public: void SetOrigin(const SDL_Rect& newOrigin); SagoSprite(const SagoSprite& base); SagoSprite& operator=(const SagoSprite& base); + int GetWidth() const; + int GetHeight() const; virtual ~SagoSprite(); private: struct SagoSpriteData;