| 1 |  |  | /* | 
    
    | 2 |  |  |  *  The ManaPlus Client | 
    
    | 3 |  |  |  *  Copyright (C) 2004-2009  The Mana World Development Team | 
    
    | 4 |  |  |  *  Copyright (C) 2009-2010  The Mana Developers | 
    
    | 5 |  |  |  *  Copyright (C) 2009  Aethyra Development Team | 
    
    | 6 |  |  |  *  Copyright (C) 2011-2019  The ManaPlus Developers | 
    
    | 7 |  |  |  *  Copyright (C) 2019-2021  Andrei Karas | 
    
    | 8 |  |  |  * | 
    
    | 9 |  |  |  *  This file is part of The ManaPlus Client. | 
    
    | 10 |  |  |  * | 
    
    | 11 |  |  |  *  This program is free software; you can redistribute it and/or modify | 
    
    | 12 |  |  |  *  it under the terms of the GNU General Public License as published by | 
    
    | 13 |  |  |  *  the Free Software Foundation; either version 2 of the License, or | 
    
    | 14 |  |  |  *  any later version. | 
    
    | 15 |  |  |  * | 
    
    | 16 |  |  |  *  This program is distributed in the hope that it will be useful, | 
    
    | 17 |  |  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
    
    | 18 |  |  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
    
    | 19 |  |  |  *  GNU General Public License for more details. | 
    
    | 20 |  |  |  * | 
    
    | 21 |  |  |  *  You should have received a copy of the GNU General Public License | 
    
    | 22 |  |  |  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
    
    | 23 |  |  |  */ | 
    
    | 24 |  |  |  | 
    
    | 25 |  |  | /*      _______   __   __   __   ______   __   __   _______   __   __ | 
    
    | 26 |  |  |  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\ | 
    
    | 27 |  |  |  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / / | 
    
    | 28 |  |  |  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / / | 
    
    | 29 |  |  |  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / / | 
    
    | 30 |  |  |  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ / | 
    
    | 31 |  |  |  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/ | 
    
    | 32 |  |  |  * | 
    
    | 33 |  |  |  * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson | 
    
    | 34 |  |  |  * | 
    
    | 35 |  |  |  * | 
    
    | 36 |  |  |  * Per Larsson a.k.a finalman | 
    
    | 37 |  |  |  * Olof Naessén a.k.a jansem/yakslem | 
    
    | 38 |  |  |  * | 
    
    | 39 |  |  |  * Visit: http://guichan.sourceforge.net | 
    
    | 40 |  |  |  * | 
    
    | 41 |  |  |  * License: (BSD) | 
    
    | 42 |  |  |  * Redistribution and use in source and binary forms, with or without | 
    
    | 43 |  |  |  * modification, are permitted provided that the following conditions | 
    
    | 44 |  |  |  * are met: | 
    
    | 45 |  |  |  * 1. Redistributions of source code must retain the above copyright | 
    
    | 46 |  |  |  *    notice, this list of conditions and the following disclaimer. | 
    
    | 47 |  |  |  * 2. Redistributions in binary form must reproduce the above copyright | 
    
    | 48 |  |  |  *    notice, this list of conditions and the following disclaimer in | 
    
    | 49 |  |  |  *    the documentation and/or other materials provided with the | 
    
    | 50 |  |  |  *    distribution. | 
    
    | 51 |  |  |  * 3. Neither the name of Guichan nor the names of its contributors may | 
    
    | 52 |  |  |  *    be used to endorse or promote products derived from this software | 
    
    | 53 |  |  |  *    without specific prior written permission. | 
    
    | 54 |  |  |  * | 
    
    | 55 |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
    
    | 56 |  |  |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
    
    | 57 |  |  |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
    
    | 58 |  |  |  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
    
    | 59 |  |  |  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
    
    | 60 |  |  |  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | 
    
    | 61 |  |  |  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
    
    | 62 |  |  |  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | 
    
    | 63 |  |  |  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
    
    | 64 |  |  |  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
    
    | 65 |  |  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
    
    | 66 |  |  |  */ | 
    
    | 67 |  |  |  | 
    
    | 68 |  |  | #include "gui/fonts/font.h" | 
    
    | 69 |  |  |  | 
    
    | 70 |  |  | #include "fs/files.h" | 
    
    | 71 |  |  | #include "fs/paths.h" | 
    
    | 72 |  |  |  | 
    
    | 73 |  |  | #include "fs/virtfs/tools.h" | 
    
    | 74 |  |  | #ifdef USE_SDL2 | 
    
    | 75 |  |  | #include "fs/virtfs/rwops.h" | 
    
    | 76 |  |  | #endif  // USE_SDL2 | 
    
    | 77 |  |  |  | 
    
    | 78 |  |  | #include "gui/fonts/textchunk.h" | 
    
    | 79 |  |  |  | 
    
    | 80 |  |  | #include "render/graphics.h" | 
    
    | 81 |  |  |  | 
    
    | 82 |  |  | #include "resources/imagehelper.h" | 
    
    | 83 |  |  |  | 
    
    | 84 |  |  | #include "resources/image/image.h" | 
    
    | 85 |  |  |  | 
    
    | 86 |  |  | #include "utils/checkutils.h" | 
    
    | 87 |  |  | #include "utils/delete2.h" | 
    
    | 88 |  |  | #include "utils/sdlcheckutils.h" | 
    
    | 89 |  |  | #include "utils/stringutils.h" | 
    
    | 90 |  |  | #include "utils/timer.h" | 
    
    | 91 |  |  |  | 
    
    | 92 |  |  | #include "debug.h" | 
    
    | 93 |  |  |  | 
    
    | 94 |  |  | const unsigned int CACHE_SIZE = 256; | 
    
    | 95 |  |  | const unsigned int CACHE_SIZE_SMALL1 = 2; | 
    
    | 96 |  |  | const unsigned int CACHE_SIZE_SMALL2 = 50; | 
    
    | 97 |  |  | const unsigned int CACHE_SIZE_SMALL3 = 170; | 
    
    | 98 |  |  | const unsigned int CLEAN_TIME = 7; | 
    
    | 99 |  |  |  | 
    
    | 100 |  |  | bool Font::mSoftMode(false); | 
    
    | 101 |  |  |  | 
    
    | 102 |  |  | extern char *restrict strBuf; | 
    
    | 103 |  |  |  | 
    
    | 104 |  |  | static int fontCounter; | 
    
    | 105 |  |  |  | 
    
    | 106 |  | 764 | Font::Font(std::string filename, | 
    
    | 107 |  |  |            int size, | 
    
    | 108 |  | 764 |            const int style) : | 
    
    | 109 |  |  |     mFont(nullptr), | 
    
    | 110 |  |  |     mCreateCounter(0), | 
    
    | 111 |  |  |     mDeleteCounter(0), | 
    
    | 112 | ✓✓✓✗ ✗✗
 | 764 |     mCleanTime(cur_time + CLEAN_TIME) | 
    
    | 113 |  |  | { | 
    
    | 114 | ✓✓ | 764 |     if (fontCounter == 0) | 
    
    | 115 |  |  |     { | 
    
    | 116 |  | 127 |         mSoftMode = imageHelper->useOpenGL() == RENDER_SOFTWARE; | 
    
    | 117 | ✓✗✗✓ 
 | 127 |         if (TTF_Init() == -1) | 
    
    | 118 |  |  |         { | 
    
    | 119 |  |  |             logger->error("Unable to initialize SDL_ttf: " + | 
    
    | 120 |  |  |                 std::string(SDL_GetError())); | 
    
    | 121 |  |  |         } | 
    
    | 122 |  |  |     } | 
    
    | 123 |  |  |  | 
    
    | 124 | ✗✓ | 764 |     if (size < 4) | 
    
    | 125 |  |  |     { | 
    
    | 126 |  |  |         reportAlways("Error: requested load font %s with size %d", | 
    
    | 127 |  |  |             filename.c_str(), | 
    
    | 128 |  |  |             size) | 
    
    | 129 |  |  |         size = 4; | 
    
    | 130 |  |  |     } | 
    
    | 131 |  |  |  | 
    
    | 132 | ✓✓ | 764 |     if (fontCounter == 0) | 
    
    | 133 |  |  |     { | 
    
    | 134 | ✓✗ | 127 |         strBuf = new char[65535]; | 
    
    | 135 |  | 127 |         memset(strBuf, 0, 65535); | 
    
    | 136 |  |  |     } | 
    
    | 137 |  |  |  | 
    
    | 138 |  | 764 |     ++fontCounter; | 
    
    | 139 |  |  |  | 
    
    | 140 | ✓✗ | 764 |     fixDirSeparators(filename); | 
    
    | 141 |  | 764 |     logger->log("Attempt to load font: %s", | 
    
    | 142 | ✓✗ | 764 |         filename.c_str()); | 
    
    | 143 | ✓✗ | 764 |     mFont = openFont(filename.c_str(), size); | 
    
    | 144 |  |  |  | 
    
    | 145 | ✗✓ | 764 |     if (mFont == nullptr) | 
    
    | 146 |  |  |     { | 
    
    | 147 |  |  |         logger->log("Error normal loading font " + filename); | 
    
    | 148 |  |  |  | 
    
    | 149 |  |  |         filename = "fonts/dejavusans.ttf"; | 
    
    | 150 |  |  |         mFont = openFont(fixDirSeparators(filename).c_str(), size); | 
    
    | 151 |  |  |         if (mFont == nullptr) | 
    
    | 152 |  |  |         { | 
    
    | 153 |  |  | #ifdef UNITTESTS | 
    
    | 154 |  |  |             reportAlways("Font load failed %s", | 
    
    | 155 |  |  |                 filename.c_str()) | 
    
    | 156 |  |  | #endif  // UNITTESTS | 
    
    | 157 |  |  |             logger->error("Font::Font: " + | 
    
    | 158 |  |  |                 std::string(SDL_GetError())); | 
    
    | 159 |  |  |         } | 
    
    | 160 |  |  |         else | 
    
    | 161 |  |  |         { | 
    
    | 162 |  |  |             logger->log("Loaded fallback font %s, %d", | 
    
    | 163 |  |  |                 filename.c_str(), | 
    
    | 164 |  |  |                 size); | 
    
    | 165 |  |  |         } | 
    
    | 166 |  |  |     } | 
    
    | 167 |  |  |     else | 
    
    | 168 |  |  |     { | 
    
    | 169 |  | 764 |         logger->log("Loaded font %s, %d", | 
    
    | 170 |  |  |             filename.c_str(), | 
    
    | 171 | ✓✗ | 764 |             size); | 
    
    | 172 |  |  |     } | 
    
    | 173 |  |  |  | 
    
    | 174 | ✓✗ | 764 |     TTF_SetFontStyle(mFont, style); | 
    
    | 175 | ✗✗ | 764 | } | 
    
    | 176 |  |  |  | 
    
    | 177 | ✓✗✓✓ 
 | 1528 | Font::~Font() | 
    
    | 178 |  |  | { | 
    
    | 179 |  | 764 |     TTF_CloseFont(mFont); | 
    
    | 180 |  | 764 |     mFont = nullptr; | 
    
    | 181 |  | 764 |     --fontCounter; | 
    
    | 182 |  | 764 |     clear(); | 
    
    | 183 |  |  |  | 
    
    | 184 | ✓✓ | 764 |     if (fontCounter == 0) | 
    
    | 185 |  |  |     { | 
    
    | 186 |  | 127 |         TTF_Quit(); | 
    
    | 187 | ✓✗ | 127 |         delete []strBuf; | 
    
    | 188 |  |  |     } | 
    
    | 189 |  | 764 | } | 
    
    | 190 |  |  |  | 
    
    | 191 |  | 764 | TTF_Font *Font::openFont(const char *const name, | 
    
    | 192 |  |  |                          const int size) | 
    
    | 193 |  |  | { | 
    
    | 194 |  |  | #ifdef USE_SDL2 | 
    
    | 195 | ✓✗ | 3056 |     SDL_RWops *const rw = VirtFs::rwopsOpenRead(name); | 
    
    | 196 | ✓✗ | 764 |     if (rw) | 
    
    | 197 |  |  |     { | 
    
    | 198 |  | 764 |         logger->log("Loading virtfs font file: %s", | 
    
    | 199 |  | 764 |             name); | 
    
    | 200 |  | 764 |         return TTF_OpenFontIndexRW(rw, 1, size, 0); | 
    
    | 201 |  |  |     } | 
    
    | 202 |  |  | #endif | 
    
    | 203 |  |  |     const std::string path = VirtFs::getPath(name); | 
    
    | 204 |  |  |     if (Files::existsLocal(path) == false) | 
    
    | 205 |  |  |     { | 
    
    | 206 |  |  | #ifndef UNITTESTS | 
    
    | 207 |  |  |         // +++ in future need trigger assert in unit tests here too | 
    
    | 208 |  |  |         reportAlways("Font::openFont font not exists: %s", | 
    
    | 209 |  |  |             path.c_str()) | 
    
    | 210 |  |  | #endif  // UNITTESTS | 
    
    | 211 |  |  |         return nullptr; | 
    
    | 212 |  |  |     } | 
    
    | 213 |  |  |     logger->log("Loading physical font file: %s", | 
    
    | 214 |  |  |         path.c_str()); | 
    
    | 215 |  |  |     return TTF_OpenFontIndex(path.c_str(), | 
    
    | 216 |  |  |         size, 0); | 
    
    | 217 |  |  | } | 
    
    | 218 |  |  |  | 
    
    | 219 |  |  | void Font::loadFont(std::string filename, | 
    
    | 220 |  |  |                     const int size, | 
    
    | 221 |  |  |                     const int style) | 
    
    | 222 |  |  | { | 
    
    | 223 |  |  |     if (fontCounter == 0 && TTF_Init() == -1) | 
    
    | 224 |  |  |     { | 
    
    | 225 |  |  |         logger->log("Unable to initialize SDL_ttf: " + | 
    
    | 226 |  |  |                     std::string(SDL_GetError())); | 
    
    | 227 |  |  |         return; | 
    
    | 228 |  |  |     } | 
    
    | 229 |  |  |  | 
    
    | 230 |  |  |     fixDirSeparators(filename); | 
    
    | 231 |  |  |     TTF_Font *const font = openFont(filename.c_str(), size); | 
    
    | 232 |  |  |  | 
    
    | 233 |  |  |     if (font == nullptr) | 
    
    | 234 |  |  |     { | 
    
    | 235 |  |  |         logger->log("Font::Font: " + | 
    
    | 236 |  |  |                     std::string(SDL_GetError())); | 
    
    | 237 |  |  |         return; | 
    
    | 238 |  |  |     } | 
    
    | 239 |  |  |  | 
    
    | 240 |  |  |     if (mFont != nullptr) | 
    
    | 241 |  |  |         TTF_CloseFont(mFont); | 
    
    | 242 |  |  |  | 
    
    | 243 |  |  |     mFont = font; | 
    
    | 244 |  |  |     TTF_SetFontStyle(mFont, style); | 
    
    | 245 |  |  |     clear(); | 
    
    | 246 |  |  | } | 
    
    | 247 |  |  |  | 
    
    | 248 |  |  | void Font::clear() | 
    
    | 249 |  |  | { | 
    
    | 250 | ✗✗✗✗ ✓✓
 | 196348 |     for (size_t f = 0; f < CACHES_NUMBER; f ++) | 
    
    | 251 |  | 195584 |         mCache[f].clear(); | 
    
    | 252 |  |  | } | 
    
    | 253 |  |  |  | 
    
    | 254 |  | 50 | void Font::drawString(Graphics *const graphics, | 
    
    | 255 |  |  |                       Color col, | 
    
    | 256 |  |  |                       const Color &col2, | 
    
    | 257 |  |  |                       const std::string &text, | 
    
    | 258 |  |  |                       const int x, const int y) | 
    
    | 259 |  |  | { | 
    
    | 260 |  |  |     BLOCK_START("Font::drawString") | 
    
    | 261 | ✓✓ | 50 |     if (text.empty()) | 
    
    | 262 |  |  |     { | 
    
    | 263 |  |  |         BLOCK_END("Font::drawString") | 
    
    | 264 |  |  |         return; | 
    
    | 265 |  |  |     } | 
    
    | 266 |  |  |  | 
    
    | 267 |  |  | //    Color col = graphics->getColor(); | 
    
    | 268 |  |  | //    const Color &col2 = graphics->getColor2(); | 
    
    | 269 |  | 49 |     const float alpha = static_cast<float>(col.a) / 255.0F; | 
    
    | 270 |  |  |  | 
    
    | 271 |  |  |     /* The alpha value is ignored at string generation so avoid caching the | 
    
    | 272 |  |  |      * same text with different alpha values. | 
    
    | 273 |  |  |      */ | 
    
    | 274 |  | 49 |     col.a = 255; | 
    
    | 275 |  |  |  | 
    
    | 276 |  | 49 |     const unsigned char chr = text[0]; | 
    
    | 277 |  | 49 |     TextChunkList *const cache = &mCache[chr]; | 
    
    | 278 |  |  |  | 
    
    | 279 |  | 49 |     std::map<TextChunkSmall, TextChunk*> &search = cache->search; | 
    
    | 280 |  |  |     std::map<TextChunkSmall, TextChunk*>::iterator i | 
    
    | 281 |  | 147 |         = search.find(TextChunkSmall(text, col, col2)); | 
    
    | 282 | ✗✓ | 49 |     if (i != search.end()) | 
    
    | 283 |  |  |     { | 
    
    | 284 |  |  |         TextChunk *const chunk2 = (*i).second; | 
    
    | 285 |  |  |         cache->moveToFirst(chunk2); | 
    
    | 286 |  |  |         Image *const image = chunk2->img; | 
    
    | 287 |  |  |         if (image != nullptr) | 
    
    | 288 |  |  |         { | 
    
    | 289 |  |  |             image->setAlpha(alpha); | 
    
    | 290 |  |  |             graphics->drawImage(image, x, y); | 
    
    | 291 |  |  |         } | 
    
    | 292 |  |  |     } | 
    
    | 293 |  |  |     else | 
    
    | 294 |  |  |     { | 
    
    | 295 | ✗✓ | 49 |         if (cache->size >= CACHE_SIZE) | 
    
    | 296 |  |  |         { | 
    
    | 297 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 298 |  |  |             mDeleteCounter ++; | 
    
    | 299 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 300 |  |  |  | 
    
    | 301 |  |  |             cache->removeBack(); | 
    
    | 302 |  |  |         } | 
    
    | 303 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 304 |  |  |         mCreateCounter ++; | 
    
    | 305 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 306 |  |  |  | 
    
    | 307 | ✓✗ | 49 |         TextChunk *chunk2 = new TextChunk(text, col, col2, this); | 
    
    | 308 |  |  |  | 
    
    | 309 |  | 49 |         chunk2->generate(mFont, alpha); | 
    
    | 310 |  | 49 |         cache->insertFirst(chunk2); | 
    
    | 311 |  |  |  | 
    
    | 312 |  | 49 |         const Image *const image = chunk2->img; | 
    
    | 313 | ✓✗ | 49 |         if (image != nullptr) | 
    
    | 314 |  | 49 |             graphics->drawImage(image, x, y); | 
    
    | 315 |  |  |     } | 
    
    | 316 |  |  |     BLOCK_END("Font::drawString") | 
    
    | 317 |  |  | } | 
    
    | 318 |  |  |  | 
    
    | 319 |  |  | void Font::slowLogic(const int rnd) | 
    
    | 320 |  |  | { | 
    
    | 321 |  |  |     BLOCK_START("Font::slowLogic") | 
    
    | 322 |  |  |     if (mCleanTime == 0) | 
    
    | 323 |  |  |     { | 
    
    | 324 |  |  |         mCleanTime = cur_time + CLEAN_TIME + rnd; | 
    
    | 325 |  |  |     } | 
    
    | 326 |  |  |     else if (mCleanTime < cur_time) | 
    
    | 327 |  |  |     { | 
    
    | 328 |  |  |         doClean(); | 
    
    | 329 |  |  |         mCleanTime = cur_time + CLEAN_TIME + rnd; | 
    
    | 330 |  |  |     } | 
    
    | 331 |  |  |     BLOCK_END("Font::slowLogic") | 
    
    | 332 |  |  | } | 
    
    | 333 |  |  |  | 
    
    | 334 |  | 9633 | int Font::getWidth(const std::string &text) const | 
    
    | 335 |  |  | { | 
    
    | 336 | ✓✓ | 9633 |     if (text.empty()) | 
    
    | 337 |  |  |         return 0; | 
    
    | 338 |  |  |  | 
    
    | 339 |  | 9421 |     const unsigned char chr = text[0]; | 
    
    | 340 |  | 9421 |     TextChunkList *const cache = &mCache[chr]; | 
    
    | 341 |  |  |  | 
    
    | 342 |  | 9421 |     std::map<std::string, TextChunk*> &search = cache->searchWidth; | 
    
    | 343 |  | 9421 |     std::map<std::string, TextChunk*>::iterator i = search.find(text); | 
    
    | 344 | ✗✓ | 9421 |     if (i != search.end()) | 
    
    | 345 |  |  |     { | 
    
    | 346 |  |  |         TextChunk *const chunk = (*i).second; | 
    
    | 347 |  |  |         cache->moveToFirst(chunk); | 
    
    | 348 |  |  |         const Image *const image = chunk->img; | 
    
    | 349 |  |  |         if (image != nullptr) | 
    
    | 350 |  |  |             return image->getWidth(); | 
    
    | 351 |  |  |         return 0; | 
    
    | 352 |  |  |     } | 
    
    | 353 |  |  |  | 
    
    | 354 |  |  |     // if string was not drawed | 
    
    | 355 |  |  |     int w; | 
    
    | 356 |  |  |     int h; | 
    
    | 357 | ✓✗ | 28263 |     getSafeUtf8String(text, strBuf); | 
    
    | 358 |  | 9421 |     TTF_SizeUTF8(mFont, strBuf, &w, &h); | 
    
    | 359 |  | 9421 |     return w; | 
    
    | 360 |  |  | } | 
    
    | 361 |  |  |  | 
    
    | 362 |  | 2322 | int Font::getHeight() const | 
    
    | 363 |  |  | { | 
    
    | 364 |  | 2322 |     return TTF_FontHeight(mFont); | 
    
    | 365 |  |  | } | 
    
    | 366 |  |  |  | 
    
    | 367 |  |  | void Font::doClean() | 
    
    | 368 |  |  | { | 
    
    | 369 |  |  |     for (unsigned int f = 0; f < CACHES_NUMBER; f ++) | 
    
    | 370 |  |  |     { | 
    
    | 371 |  |  |         TextChunkList *const cache = &mCache[f]; | 
    
    | 372 |  |  |         const size_t size = CAST_SIZE(cache->size); | 
    
    | 373 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 374 |  |  |         logger->log("ptr: %u, size: %ld", f, size); | 
    
    | 375 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 376 |  |  |  | 
    
    | 377 |  |  |         if (size > CACHE_SIZE_SMALL3) | 
    
    | 378 |  |  |         { | 
    
    | 379 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 380 |  |  |             mDeleteCounter += 100; | 
    
    | 381 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 382 |  |  |  | 
    
    | 383 |  |  |             cache->removeBack(100); | 
    
    | 384 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 385 |  |  |             logger->log("delete3"); | 
    
    | 386 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 387 |  |  |         } | 
    
    | 388 |  |  |         else if (size > CACHE_SIZE_SMALL2) | 
    
    | 389 |  |  |         { | 
    
    | 390 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 391 |  |  |             mDeleteCounter += 20; | 
    
    | 392 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 393 |  |  |  | 
    
    | 394 |  |  |             cache->removeBack(20); | 
    
    | 395 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 396 |  |  |             logger->log("delete2"); | 
    
    | 397 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 398 |  |  |         } | 
    
    | 399 |  |  |         else if (size > CACHE_SIZE_SMALL1) | 
    
    | 400 |  |  |         { | 
    
    | 401 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 402 |  |  |             mDeleteCounter ++; | 
    
    | 403 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 404 |  |  |  | 
    
    | 405 |  |  |             cache->removeBack(); | 
    
    | 406 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 407 |  |  |             logger->log("delete1"); | 
    
    | 408 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 409 |  |  |         } | 
    
    | 410 |  |  |     } | 
    
    | 411 |  |  | } | 
    
    | 412 |  |  |  | 
    
    | 413 |  |  | int Font::getStringIndexAt(const std::string& text, const int x) const | 
    
    | 414 |  |  | { | 
    
    | 415 |  |  |     const size_t sz = text.size(); | 
    
    | 416 |  |  |     for (size_t i = 0; i < sz; ++i) | 
    
    | 417 |  |  |     { | 
    
    | 418 |  |  |         if (getWidth(text.substr(0, i)) > x) | 
    
    | 419 |  |  |             return CAST_S32(i); | 
    
    | 420 |  |  |     } | 
    
    | 421 |  |  |  | 
    
    | 422 |  |  |     return CAST_S32(sz); | 
    
    | 423 |  |  | } | 
    
    | 424 |  |  |  | 
    
    | 425 |  |  | const TextChunkList *Font::getCache() const noexcept2 | 
    
    | 426 |  |  | { | 
    
    | 427 |  |  |     return mCache; | 
    
    | 428 |  |  | } | 
    
    | 429 |  |  |  | 
    
    | 430 |  | 236 | void Font::generate(TextChunk &chunk) | 
    
    | 431 |  |  | { | 
    
    | 432 |  | 236 |     const std::string &text = chunk.text; | 
    
    | 433 | ✓✓ | 236 |     if (text.empty()) | 
    
    | 434 |  | 41 |         return; | 
    
    | 435 |  |  |  | 
    
    | 436 |  | 195 |     const unsigned char chr = text[0]; | 
    
    | 437 |  | 195 |     TextChunkList *const cache = &mCache[chr]; | 
    
    | 438 |  | 195 |     Color &col = chunk.color; | 
    
    | 439 |  | 195 |     Color &col2 = chunk.color2; | 
    
    | 440 |  | 195 |     const int oldAlpha = col.a; | 
    
    | 441 |  | 195 |     col.a = 255; | 
    
    | 442 |  |  |  | 
    
    | 443 |  | 390 |     TextChunkSmall key(text, col, col2); | 
    
    | 444 |  | 195 |     std::map<TextChunkSmall, TextChunk*> &search = cache->search; | 
    
    | 445 |  | 195 |     std::map<TextChunkSmall, TextChunk*>::iterator i = search.find(key); | 
    
    | 446 | ✗✓ | 195 |     if (i != search.end()) | 
    
    | 447 |  |  |     { | 
    
    | 448 |  |  |         TextChunk *const chunk2 = (*i).second; | 
    
    | 449 |  |  |         cache->moveToFirst(chunk2); | 
    
    | 450 |  |  | //        search.erase(key); | 
    
    | 451 |  |  |         cache->remove(chunk2); | 
    
    | 452 |  |  |         chunk.img = chunk2->img; | 
    
    | 453 |  |  |         chunk2->img = nullptr; | 
    
    | 454 |  |  |         delete chunk2; | 
    
    | 455 |  |  | //        logger->log("cached image: " + chunk.text); | 
    
    | 456 |  |  |     } | 
    
    | 457 |  |  |     else | 
    
    | 458 |  |  |     { | 
    
    | 459 | ✗✓ | 195 |         if (cache->size >= CACHE_SIZE) | 
    
    | 460 |  |  |         { | 
    
    | 461 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 462 |  |  |             mDeleteCounter ++; | 
    
    | 463 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 464 |  |  |  | 
    
    | 465 |  |  |             cache->removeBack(); | 
    
    | 466 |  |  |         } | 
    
    | 467 |  |  | #ifdef DEBUG_FONT_COUNTERS | 
    
    | 468 |  |  |         mCreateCounter ++; | 
    
    | 469 |  |  | #endif  // DEBUG_FONT_COUNTERS | 
    
    | 470 |  |  |  | 
    
    | 471 |  | 195 |         const float alpha = static_cast<float>(chunk.color.a) / 255.0F; | 
    
    | 472 | ✓✗ | 195 |         chunk.generate(mFont, alpha); | 
    
    | 473 |  |  | //        logger->log("generate image: " + chunk.text); | 
    
    | 474 |  |  |     } | 
    
    | 475 |  | 195 |     col.a = oldAlpha; | 
    
    | 476 |  |  | } | 
    
    | 477 |  |  |  | 
    
    | 478 |  | 431 | void Font::insertChunk(TextChunk *const chunk) | 
    
    | 479 |  |  | { | 
    
    | 480 | ✓✗✓✓ ✗✓✓✓
 
 | 862 |     if ((chunk == nullptr) || chunk->text.empty() || (chunk->img == nullptr)) | 
    
    | 481 |  |  |         return; | 
    
    | 482 |  |  | //    logger->log("insert chunk: text=%s, color: %d,%d,%d", | 
    
    | 483 |  |  | //        chunk->text.c_str(), chunk->color.r, chunk->color.g, chunk->color.b); | 
    
    | 484 |  | 318 |     const unsigned char chr = chunk->text[0]; | 
    
    | 485 |  | 159 |     TextChunkList *const cache = &mCache[chr]; | 
    
    | 486 |  |  |  | 
    
    | 487 |  | 159 |     std::map<TextChunkSmall, TextChunk*> &search = cache->search; | 
    
    | 488 |  |  |     std::map<TextChunkSmall, TextChunk*>::iterator i | 
    
    | 489 |  | 318 |         = search.find(TextChunkSmall(chunk->text, | 
    
    | 490 |  | 159 |         chunk->color, chunk->color2)); | 
    
    | 491 | ✓✓ | 159 |     if (i != search.end()) | 
    
    | 492 |  |  |     { | 
    
    | 493 | ✓✗ | 3 |         delete2(chunk->img) | 
    
    | 494 |  | 3 |         return; | 
    
    | 495 |  |  |     } | 
    
    | 496 |  |  |  | 
    
    | 497 |  |  |     TextChunk *const chunk2 = new TextChunk(chunk->text, | 
    
    | 498 | ✓✗ | 156 |         chunk->color, chunk->color2, chunk->textFont); | 
    
    | 499 |  | 156 |     chunk2->img = chunk->img; | 
    
    | 500 |  | 156 |     cache->insertFirst(chunk2); | 
    
    | 501 |  |  | } |