Build a small per-page kern matrix containing ONLY the (leftClass, rightClass) pairs reachable from codepoints in the current text. Class IDs are renumbered to a dense 1..N range so the resulting matrix is usedLeft × usedRight (typical Latin page: ~25×25 bytes) instead of the font's full ~180×200 (~36KB). Correctness: EpdFont::getKerning only touches `kernLeftClasses` / `kernRightClasses` / `kern
| 263 | // on this page simply returns class 0 (no kerning), which was the pre-existing |
| 264 | // behavior for any codepoint outside the kern classes. |
| 265 | bool SdCardFont::buildMiniKernMatrix(PerStyle& s, const uint32_t* codepoints, uint32_t cpCount) { |
| 266 | freeStyleMiniKern(s); |
| 267 | if (!s.kernLeftClasses || !s.kernRightClasses || s.header.kernLeftEntryCount == 0 || |
| 268 | s.header.kernRightEntryCount == 0) { |
| 269 | return true; // font has no kern classes — nothing to build |
| 270 | } |
| 271 | |
| 272 | // Step 1: mark used left/right classes via a 256-wide bitmap (class IDs are uint8_t). |
| 273 | bool usedLeft[256] = {}; |
| 274 | bool usedRight[256] = {}; |
| 275 | for (uint32_t i = 0; i < cpCount; i++) { |
| 276 | uint8_t lc = miniLookupKernClass(s.kernLeftClasses, s.header.kernLeftEntryCount, codepoints[i]); |
| 277 | if (lc) usedLeft[lc] = true; |
| 278 | uint8_t rc = miniLookupKernClass(s.kernRightClasses, s.header.kernRightEntryCount, codepoints[i]); |
| 279 | if (rc) usedRight[rc] = true; |
| 280 | } |
| 281 | |
| 282 | // Step 2: build renumber maps (oldClassId -> newClassId, 1-based) and |
| 283 | // reverse maps (newClassId -> oldClassId) for the SD read step. |
| 284 | uint8_t leftRenumber[256] = {}; |
| 285 | uint8_t rightRenumber[256] = {}; |
| 286 | uint8_t newToOldLeft[256] = {}; |
| 287 | uint8_t newToOldRight[256] = {}; |
| 288 | uint8_t numLeft = 0, numRight = 0; |
| 289 | for (int i = 1; i < 256; i++) { |
| 290 | if (usedLeft[i]) { |
| 291 | numLeft++; |
| 292 | leftRenumber[i] = numLeft; |
| 293 | newToOldLeft[numLeft] = static_cast<uint8_t>(i); |
| 294 | } |
| 295 | if (usedRight[i]) { |
| 296 | numRight++; |
| 297 | rightRenumber[i] = numRight; |
| 298 | newToOldRight[numRight] = static_cast<uint8_t>(i); |
| 299 | } |
| 300 | } |
| 301 | if (numLeft == 0 || numRight == 0) { |
| 302 | return true; // no kern pairs applicable on this page |
| 303 | } |
| 304 | |
| 305 | // Step 3: count how many codepoint→classId entries the mini class tables need. |
| 306 | // Each resident class table has one entry per kerned codepoint in the page. |
| 307 | uint16_t miniLeftCount = 0; |
| 308 | uint16_t miniRightCount = 0; |
| 309 | for (uint32_t i = 0; i < cpCount; i++) { |
| 310 | if (miniLookupKernClass(s.kernLeftClasses, s.header.kernLeftEntryCount, codepoints[i]) != 0) miniLeftCount++; |
| 311 | if (miniLookupKernClass(s.kernRightClasses, s.header.kernRightEntryCount, codepoints[i]) != 0) miniRightCount++; |
| 312 | } |
| 313 | |
| 314 | // Step 4: allocate the three mini buffers. The matrix is <1KB in practice |
| 315 | // (<30 × <30 × 1 byte) so fragmentation is a non-issue. |
| 316 | const uint32_t matrixBytes = static_cast<uint32_t>(numLeft) * numRight; |
| 317 | s.miniKernLeftClasses = new (std::nothrow) EpdKernClassEntry[miniLeftCount]; |
| 318 | s.miniKernRightClasses = new (std::nothrow) EpdKernClassEntry[miniRightCount]; |
| 319 | s.miniKernMatrix = new (std::nothrow) int8_t[matrixBytes]; |
| 320 | if (!s.miniKernLeftClasses || !s.miniKernRightClasses || !s.miniKernMatrix) { |
| 321 | LOG_ERR("SDCF", "Failed to allocate mini kern (%u+%u+%u bytes)", miniLeftCount * 3u, miniRightCount * 3u, |
| 322 | matrixBytes); |
nothing calls this directly
no test coverage detected