freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Git][freetype/freetype-demos][gsoc-2022-chariri-2] [ftinspect] Finish a


From: Charlie Jiang (@cqjjjzr)
Subject: [Git][freetype/freetype-demos][gsoc-2022-chariri-2] [ftinspect] Finish as well as refactor the "Continuous Mode".
Date: Wed, 20 Jul 2022 09:22:58 +0000

Charlie Jiang pushed to branch gsoc-2022-chariri-2 at FreeType / FreeType Demo Programs

Commits:

  • 4f3a8eba
    by Charlie Jiang at 2022-07-20T17:22:27+08:00
    [ftinspect] Finish as well as refactor the "Continuous Mode".
    
    This commit finishs the "Continuous Mode", add several options adopted from
    `ftstring` util, and refactors the whole glyph drawing process with logic
    from `FTDemo_String_XXX` functions.
    
    All code related to populating chars to render, loading and positioning
    glyphs is moved to a new class named `StringRenderer`. The old
    `GlyphContinuous` is now only responsible for calling into the renderer,
    doing glyph transformations like emboldening and stroking, and drawing the
    glyphs generated from `StringRenderer`.
    
    * src/ftinspect/engine/stringrenderer.hpp,
      src/ftinspect/engine/stringrenderer.cpp: New files.
    
    * src/ftinspect/rendering/glyphcontinuous.cpp,
      src/ftinspect/rendering/glyphcontinuous.hpp:
      Move out several fields, and refactor using `StringRenderer`. This class
      no longer manages lifecycle of any glyphs, outlines or bitmaps.
    
    * src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
      Add `lcdSubPixelPositioning_`, some getters/setters.
      Add cache-less glyph loading (via `FT_Load_Glyph` and face slots).
      Add track kerning and per pair kerning fetching.
      Add a function to get the first Unicode charmap index of the active font.
      Add argument `outNode` and `forceRender` to `loadGlyphWithoutUpdate`,
      so the receiver can pin the glyph to a node to avoid accidentally
      flushing. `forceRender` is not used, though.
    
    * src/ftinspect/panels/continuous.cpp, src/ftinspect/panels/continuous.hpp:
      Add position, kerning options; properly sync settings and flush caches of
      the string renderer.
    
    * src/ftinspect/panels/settingpanel.cpp:
      Add support to the "Light (Sub Pixel)" AA mode.
      Make changes to "Embedded bitmap" option trigger font reload request,
      or the cache in the string renderer won't be flushed in time.
    
    * src/ftinspect/models/customcomboboxmodels.hpp,
      src/ftinspect/models/customcomboboxmodels.cpp:
      Add "Light (Sub Pixel)" Anti-Aliasing mode.
    
    * src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
    

13 changed files:

Changes:

  • src/ftinspect/CMakeLists.txt
    ... ... @@ -23,6 +23,7 @@ add_executable(ftinspect
    23 23
       "engine/engine.cpp"
    
    24 24
       "engine/fontfilemanager.cpp"
    
    25 25
       "engine/charmap.cpp"
    
    26
    +  "engine/stringrenderer.cpp"
    
    26 27
     
    
    27 28
       "rendering/glyphbitmap.cpp"
    
    28 29
       "rendering/glyphoutline.cpp"
    

  • src/ftinspect/engine/engine.cpp
    ... ... @@ -246,6 +246,17 @@ Engine::numberOfNamedInstances(int fontIndex,
    246 246
     }
    
    247 247
     
    
    248 248
     
    
    249
    +int
    
    250
    +Engine::currentFontFirstUnicodeCharMap()
    
    251
    +{
    
    252
    +  auto& charmaps = currentFontCharMaps();
    
    253
    +  for (auto& cmap : charmaps)
    
    254
    +    if (cmap.encoding == FT_ENCODING_UNICODE)
    
    255
    +      return cmap.index;
    
    256
    +  return -1;
    
    257
    +}
    
    258
    +
    
    259
    +
    
    249 260
     int
    
    250 261
     Engine::loadFont(int fontIndex,
    
    251 262
                      long faceIndex,
    
    ... ... @@ -363,7 +374,7 @@ Engine::removeFont(int fontIndex, bool closeFile)
    363 374
     unsigned
    
    364 375
     Engine::glyphIndexFromCharCode(int code, int charMapIndex)
    
    365 376
     {
    
    366
    -  if (charMapIndex == -1)
    
    377
    +  if (charMapIndex < 0)
    
    367 378
         return code;
    
    368 379
       return FTC_CMapCache_Lookup(cmapCache_, scaler_.face_id, charMapIndex, code);
    
    369 380
     }
    
    ... ... @@ -376,6 +387,42 @@ Engine::currentFontMetrics()
    376 387
     }
    
    377 388
     
    
    378 389
     
    
    390
    +FT_GlyphSlot
    
    391
    +Engine::currentFaceSlot()
    
    392
    +{
    
    393
    +  return ftSize_->face->glyph;
    
    394
    +}
    
    395
    +
    
    396
    +
    
    397
    +FT_Pos
    
    398
    +Engine::currentFontTrackingKerning(int degree)
    
    399
    +{
    
    400
    +  FT_Pos result;
    
    401
    +  // this function needs and returns points, not pixels
    
    402
    +  if (!FT_Get_Track_Kerning(ftSize_->face,
    
    403
    +                            static_cast<FT_Fixed>(scaler_.width) << 10, 
    
    404
    +                            -degree,
    
    405
    +                            &result))
    
    406
    +  {
    
    407
    +    result = static_cast<FT_Pos>((result / 1024.0 * scaler_.x_res) / 72.0);
    
    408
    +    return result;
    
    409
    +  }
    
    410
    +  return 0;
    
    411
    +}
    
    412
    +
    
    413
    +
    
    414
    +FT_Vector
    
    415
    +Engine::currentFontKerning(int glyphIndex,
    
    416
    +                           int prevIndex)
    
    417
    +{
    
    418
    +  FT_Vector kern = {0, 0};
    
    419
    +  FT_Get_Kerning(ftSize_->face, 
    
    420
    +                 prevIndex, glyphIndex, 
    
    421
    +                 FT_KERNING_UNFITTED, &kern);
    
    422
    +  return kern;
    
    423
    +}
    
    424
    +
    
    425
    +
    
    379 426
     QString
    
    380 427
     Engine::glyphName(int index)
    
    381 428
     {
    
    ... ... @@ -434,20 +481,33 @@ Engine::loadGlyph(int glyphIndex)
    434 481
     }
    
    435 482
     
    
    436 483
     
    
    484
    +int
    
    485
    +Engine::loadGlyphIntoSlotWithoutCache(int glyphIndex)
    
    486
    +{
    
    487
    +  return FT_Load_Glyph(ftSize_->face, glyphIndex, loadFlags_);
    
    488
    +}
    
    489
    +
    
    490
    +
    
    437 491
     FT_Glyph
    
    438
    -Engine::loadGlyphWithoutUpdate(int glyphIndex)
    
    492
    +Engine::loadGlyphWithoutUpdate(int glyphIndex, 
    
    493
    +                               FTC_Node* outNode,
    
    494
    +                               bool forceRender)
    
    439 495
     {
    
    440 496
       FT_Glyph glyph;
    
    497
    +  auto oldFlags = imageType_.flags;
    
    498
    +  if (forceRender)
    
    499
    +    imageType_.flags |= FT_LOAD_RENDER;
    
    441 500
       if (FTC_ImageCache_Lookup(imageCache_,
    
    442 501
                                 &imageType_,
    
    443 502
                                 glyphIndex,
    
    444 503
                                 &glyph,
    
    445
    -                            NULL))
    
    504
    +                            outNode))
    
    446 505
       {
    
    447 506
         // XXX error handling?
    
    448 507
         return NULL;
    
    449 508
       }
    
    450 509
     
    
    510
    +  imageType_.flags = oldFlags;
    
    451 511
       return glyph;
    
    452 512
     }
    
    453 513
     
    
    ... ... @@ -835,7 +895,7 @@ Engine::convertGlyphToQImage(FT_Glyph src, QRect* outRect)
    835 895
       if (result && outRect)
    
    836 896
       {
    
    837 897
         outRect->setLeft(bitmapGlyph->left);
    
    838
    -    outRect->setTop(-bitmapGlyph->top);
    
    898
    +    outRect->setTop(bitmapGlyph->top);
    
    839 899
         outRect->setWidth(bitmapGlyph->bitmap.width);
    
    840 900
         outRect->setHeight(bitmapGlyph->bitmap.rows);
    
    841 901
       }
    
    ... ... @@ -860,7 +920,7 @@ Engine::computeGlyphOffset(FT_Glyph glyph)
    860 920
         cbox.xMax = (cbox.xMax + 63) & ~63;
    
    861 921
         cbox.yMax = (cbox.yMax + 63) & ~63;
    
    862 922
         return { static_cast<int>(cbox.xMin) / 64,
    
    863
    -               static_cast<int>(-cbox.yMax / 64) };
    
    923
    +               static_cast<int>(cbox.yMax / 64) };
    
    864 924
       }
    
    865 925
       if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
    
    866 926
       {
    

  • src/ftinspect/engine/engine.hpp
    ... ... @@ -77,9 +77,12 @@ public:
    77 77
                    long faceIndex,
    
    78 78
                    int namedInstanceIndex); // return number of glyphs
    
    79 79
       FT_Glyph loadGlyph(int glyphIndex);
    
    80
    +  int loadGlyphIntoSlotWithoutCache(int glyphIndex);
    
    80 81
     
    
    81 82
       // Sometimes the engine is already updated, and we want to be faster
    
    82
    -  FT_Glyph loadGlyphWithoutUpdate(int glyphIndex);
    
    83
    +  FT_Glyph loadGlyphWithoutUpdate(int glyphIndex,
    
    84
    +                                  FTC_Node* outNode = NULL,
    
    85
    +                                  bool forceRender = false);
    
    83 86
     
    
    84 87
       // Return `true` if you need to free `out`
    
    85 88
       // `out` will be set to NULL in cases of error
    
    ... ... @@ -101,6 +104,9 @@ public:
    101 104
       //////// Getters
    
    102 105
     
    
    103 106
       FT_Library ftLibrary() const { return library_; }
    
    107
    +  FTC_Manager cacheManager() { return cacheManager_; }
    
    108
    +  int dpi() { return dpi_; }
    
    109
    +  int pointSize() { return pointSize_; }
    
    104 110
       int currentFontType() const { return fontType_; }
    
    105 111
       const QString& currentFamilyName() { return curFamilyName_; }
    
    106 112
       const QString& currentStyleName() { return curStyleName_; }
    
    ... ... @@ -110,15 +116,21 @@ public:
    110 116
       long numberOfFaces(int fontIndex);
    
    111 117
       int numberOfNamedInstances(int fontIndex,
    
    112 118
                                  long faceIndex);
    
    119
    +  int currentFontFirstUnicodeCharMap();
    
    113 120
       // Note: the current font face must be properly set
    
    114 121
       unsigned glyphIndexFromCharCode(int code, int charMapIndex);
    
    115 122
       FT_Size_Metrics const& currentFontMetrics();
    
    116
    -
    
    123
    +  FT_GlyphSlot currentFaceSlot();
    
    124
    +  FT_Pos currentFontTrackingKerning(int degree);
    
    125
    +  FT_Vector currentFontKerning(int glyphIndex, int prevIndex);
    
    126
    +  
    
    117 127
       std::vector<CharMapInfo>& currentFontCharMaps() { return curCharMaps_; }
    
    118 128
       FontFileManager& fontFileManager() { return fontFileManager_; }
    
    119 129
       EngineDefaultValues& engineDefaults() { return engineDefaults_; }
    
    120 130
       bool antiAliasingEnabled() { return antiAliasingEnabled_; }
    
    131
    +  bool doHinting() { return doHinting_; }
    
    121 132
       bool embeddedBitmapEnabled() { return embeddedBitmap_; }
    
    133
    +  bool lcdUsingSubPixelPositioning() { return lcdSubPixelPositioning_; }
    
    122 134
     
    
    123 135
       //////// Setters (direct or indirect)
    
    124 136
     
    
    ... ... @@ -146,6 +158,7 @@ public:
    146 158
       void setAntiAliasingEnabled(bool enabled) { antiAliasingEnabled_ = enabled; }
    
    147 159
       void setEmbeddedBitmap(bool force) { embeddedBitmap_ = force; }
    
    148 160
       void setLCDUsesBGR(bool isBGR) { lcdUsesBGR_ = isBGR; }
    
    161
    +  void setLCDSubPixelPositioning(bool sp) { lcdSubPixelPositioning_ = sp; }
    
    149 162
     
    
    150 163
       // Note: These 3 functions now takes actual mode/version from FreeType,
    
    151 164
       // instead of values from enum in MainGUI!
    
    ... ... @@ -201,6 +214,7 @@ private:
    201 214
       bool embeddedBitmap_;
    
    202 215
       int antiAliasingTarget_;
    
    203 216
       bool lcdUsesBGR_;
    
    217
    +  bool lcdSubPixelPositioning_;
    
    204 218
       int renderMode_;
    
    205 219
     
    
    206 220
       double gamma_;
    

  • src/ftinspect/engine/stringrenderer.cpp
    1
    +// stringrenderer.cpp
    
    2
    +
    
    3
    +// Copyright (C) 2022 by Charlie Jiang.
    
    4
    +
    
    5
    +#include "stringrenderer.hpp"
    
    6
    +
    
    7
    +#include "engine.hpp"
    
    8
    +
    
    9
    +#include <cmath>
    
    10
    +
    
    11
    +
    
    12
    +StringRenderer::StringRenderer(Engine* engine)
    
    13
    +: engine_(engine)
    
    14
    +{
    
    15
    +}
    
    16
    +
    
    17
    +
    
    18
    +StringRenderer::~StringRenderer()
    
    19
    +{
    
    20
    +  clearActive();
    
    21
    +}
    
    22
    +
    
    23
    +
    
    24
    +void
    
    25
    +StringRenderer::setCharMapIndex(int charMapIndex,
    
    26
    +                                int limitIndex)
    
    27
    +{
    
    28
    +  auto& charmaps = engine_->currentFontCharMaps();
    
    29
    +  if (charMapIndex < 0
    
    30
    +      || static_cast<uint>(charMapIndex) >= charmaps.size())
    
    31
    +    charMapIndex = -1;
    
    32
    +
    
    33
    +  charMapIndex_ = charMapIndex;
    
    34
    +  limitIndex_ = limitIndex;
    
    35
    +}
    
    36
    +
    
    37
    +
    
    38
    +void
    
    39
    +StringRenderer::setRotation(double rotation)
    
    40
    +{
    
    41
    +  rotation_ = rotation;
    
    42
    +
    
    43
    +  if (rotation <= -180)
    
    44
    +    rotation += 360;
    
    45
    +  if (rotation > 180)
    
    46
    +    rotation -= 360;
    
    47
    +
    
    48
    +  if (rotation == 0)
    
    49
    +  {
    
    50
    +    matrixEnabled_ = false;
    
    51
    +    return;
    
    52
    +  }
    
    53
    +
    
    54
    +  matrixEnabled_ = true;
    
    55
    +  double radian = rotation * 3.14159265 / 180.0;
    
    56
    +  auto cosinus = static_cast<FT_Fixed>(cos(radian) * 65536.0);
    
    57
    +  auto sinus = static_cast<FT_Fixed>(sin(radian) * 65536.0);
    
    58
    +
    
    59
    +  matrix_.xx = cosinus;
    
    60
    +  matrix_.yx = sinus;
    
    61
    +  matrix_.xy = -sinus;
    
    62
    +  matrix_.yy = cosinus;
    
    63
    +}
    
    64
    +
    
    65
    +
    
    66
    +void
    
    67
    +StringRenderer::setKerning(bool kerning)
    
    68
    +{
    
    69
    +  if (kerning)
    
    70
    +  {
    
    71
    +    kerningMode_ = KM_Normal;
    
    72
    +    kerningDegree_ = KD_Medium;
    
    73
    +  }
    
    74
    +  else
    
    75
    +  {
    
    76
    +    kerningMode_ = KM_None;
    
    77
    +    kerningDegree_ = KD_None;
    
    78
    +  }
    
    79
    +}
    
    80
    +
    
    81
    +
    
    82
    +void
    
    83
    +StringRenderer::reloadAll()
    
    84
    +{
    
    85
    +  clearActive(usingString_); // if "All Glyphs", then do a complete wipe
    
    86
    +  if (usingString_)
    
    87
    +    reloadGlyphIndices();
    
    88
    +}
    
    89
    +
    
    90
    +void
    
    91
    +StringRenderer::reloadGlyphs()
    
    92
    +{
    
    93
    +  clearActive(true);
    
    94
    +}
    
    95
    +
    
    96
    +
    
    97
    +void
    
    98
    +StringRenderer::setUseString(QString const& string)
    
    99
    +{
    
    100
    +  clearActive(); // clear existing
    
    101
    +  usingString_ = true;
    
    102
    +
    
    103
    +  long long totalCount = 0;
    
    104
    +  for (uint ch : string.toUcs4())
    
    105
    +  {
    
    106
    +    activeGlyphs_.emplace_back();
    
    107
    +    auto& it = activeGlyphs_.back();
    
    108
    +    it.charCode = static_cast<int>(ch);
    
    109
    +    it.glyphIndex = 0;
    
    110
    +    ++totalCount;
    
    111
    +    if (totalCount >= INT_MAX)
    
    112
    +      break;
    
    113
    +  }
    
    114
    +  reloadGlyphIndices();
    
    115
    +}
    
    116
    +
    
    117
    +
    
    118
    +void
    
    119
    +StringRenderer::setUseAllGlyphs()
    
    120
    +{
    
    121
    +  if (usingString_)
    
    122
    +    clearActive();
    
    123
    +  usingString_ = false;
    
    124
    +}
    
    125
    +
    
    126
    +
    
    127
    +void
    
    128
    +StringRenderer::reloadGlyphIndices()
    
    129
    +{
    
    130
    +  if (!usingString_)
    
    131
    +    return;
    
    132
    +  int charMapIndex = charMapIndex_;
    
    133
    +  auto& charmaps = engine_->currentFontCharMaps();
    
    134
    +  if (charMapIndex < 0
    
    135
    +      || static_cast<uint>(charMapIndex) >= charmaps.size()
    
    136
    +      || charmaps[charMapIndex].encoding != FT_ENCODING_UNICODE)
    
    137
    +    charMapIndex = engine_->currentFontFirstUnicodeCharMap();
    
    138
    +
    
    139
    +  if (charMapIndex < 0)
    
    140
    +    return;
    
    141
    +  for (auto& ctx : activeGlyphs_)
    
    142
    +  {
    
    143
    +    auto index = engine_->glyphIndexFromCharCode(ctx.charCode, charMapIndex);
    
    144
    +    ctx.glyphIndex = static_cast<int>(index);
    
    145
    +  }
    
    146
    +}
    
    147
    +
    
    148
    +
    
    149
    +void
    
    150
    +StringRenderer::prepareRendering()
    
    151
    +{
    
    152
    +  engine_->reloadFont();
    
    153
    +  if (kerningDegree_ != KD_None)
    
    154
    +    trackingKerning_ = engine_->currentFontTrackingKerning(kerningDegree_);
    
    155
    +  else
    
    156
    +    trackingKerning_ = 0;
    
    157
    +}
    
    158
    +
    
    159
    +
    
    160
    +void
    
    161
    +StringRenderer::loadSingleContext(GlyphContext* ctx,
    
    162
    +                                  GlyphContext* prev)
    
    163
    +{
    
    164
    +  if (ctx->cacheNode)
    
    165
    +  {
    
    166
    +    FTC_Node_Unref(ctx->cacheNode, engine_->cacheManager());
    
    167
    +    ctx->cacheNode = NULL;
    
    168
    +  }
    
    169
    +  else if (ctx->glyph)
    
    170
    +    FT_Done_Glyph(ctx->glyph); // when caching isn't used
    
    171
    +
    
    172
    +  // TODO use FTC?
    
    173
    +
    
    174
    +  // After `prepareRendering`, current size/face is properly set
    
    175
    +  FT_GlyphSlot slot = engine_->currentFaceSlot();
    
    176
    +  if (engine_->loadGlyphIntoSlotWithoutCache(ctx->glyphIndex) != 0)
    
    177
    +  {
    
    178
    +    ctx->glyph = NULL;
    
    179
    +    return;
    
    180
    +  }
    
    181
    +  if (FT_Get_Glyph(slot, &ctx->glyph) != 0)
    
    182
    +  {
    
    183
    +    ctx->glyph = NULL;
    
    184
    +    return;
    
    185
    +  }
    
    186
    +  auto& metrics = slot->metrics;
    
    187
    +  //ctx->glyph = engine_->loadGlyphWithoutUpdate(ctx->glyphIndex, 
    
    188
    +  //                                            &ctx->cacheNode);
    
    189
    +
    
    190
    +  if (!ctx->glyph)
    
    191
    +    return;
    
    192
    +
    
    193
    +  ctx->vvector.x = metrics.vertBearingX - metrics.horiBearingX;
    
    194
    +  ctx->vvector.y = -metrics.vertBearingY - metrics.horiBearingY;
    
    195
    +
    
    196
    +  ctx->vadvance.x = 0;
    
    197
    +  ctx->vadvance.y = -metrics.vertAdvance;
    
    198
    +
    
    199
    +  ctx->lsbDelta = slot->lsb_delta;
    
    200
    +  ctx->rsbDelta = slot->rsb_delta;
    
    201
    +
    
    202
    +  ctx->hadvance.x = metrics.horiAdvance;
    
    203
    +  ctx->hadvance.y = 0;
    
    204
    +
    
    205
    +  if (engine_->lcdUsingSubPixelPositioning())
    
    206
    +    ctx->hadvance.x += ctx->lsbDelta - ctx->rsbDelta;
    
    207
    +  prev->hadvance.x += trackingKerning_;
    
    208
    +
    
    209
    +  if (kerningMode_ != KM_None)
    
    210
    +  {
    
    211
    +    FT_Vector kern = engine_->currentFontKerning(ctx->glyphIndex, 
    
    212
    +                                        prev->glyphIndex);
    
    213
    +
    
    214
    +    prev->hadvance.x += kern.x;
    
    215
    +    prev->hadvance.y += kern.y;
    
    216
    +
    
    217
    +    if (!engine_->lcdUsingSubPixelPositioning() && kerningMode_ > KM_Normal)
    
    218
    +    {
    
    219
    +      if (prev->rsbDelta - ctx->lsbDelta > 32)
    
    220
    +        prev->hadvance.x -= 64;
    
    221
    +      else if (prev->rsbDelta - ctx->lsbDelta < -31)
    
    222
    +        prev->hadvance.x += 64;
    
    223
    +    }
    
    224
    +  }
    
    225
    +
    
    226
    +  if (!engine_->lcdUsingSubPixelPositioning() && engine_->doHinting())
    
    227
    +  {
    
    228
    +    prev->hadvance.x = (prev->hadvance.x + 32) & -64;
    
    229
    +    prev->hadvance.y = (prev->hadvance.y + 32) & -64;
    
    230
    +  }
    
    231
    +}
    
    232
    +
    
    233
    +
    
    234
    +void
    
    235
    +StringRenderer::loadStringGlyphs()
    
    236
    +{
    
    237
    +  if (!usingString_)
    
    238
    +    return;
    
    239
    +  GlyphContext* prev = &tempGlyphContext_; // = empty
    
    240
    +  tempGlyphContext_ = {};
    
    241
    +
    
    242
    +  for (auto& ctx : activeGlyphs_)
    
    243
    +  {
    
    244
    +    loadSingleContext(&ctx, prev);
    
    245
    +    prev = &ctx;
    
    246
    +  }
    
    247
    +
    
    248
    +  glyphCacheValid_ = true;
    
    249
    +}
    
    250
    +
    
    251
    +
    
    252
    +int
    
    253
    +StringRenderer::prepareLine(int offset,
    
    254
    +                            int lineWidth,
    
    255
    +                            FT_Vector& outActualLineWidth)
    
    256
    +{
    
    257
    +  int totalCount = 0;
    
    258
    +  outActualLineWidth = {0, 0};
    
    259
    +  if (!usingString_)
    
    260
    +  {
    
    261
    +    // The thing gets a little complicated when we're using "All Glyphs" mode
    
    262
    +    // The input sequence is actually infinite
    
    263
    +    // so we have to combine loading glyph into rendering, and can't preload
    
    264
    +    // all glyphs
    
    265
    +
    
    266
    +    // TODO: Low performance when the begin index is large.
    
    267
    +    // TODO: Optimize: use a sparse vector...!
    
    268
    +    // The problem is that when doing a `list::resize`, the ctor is called
    
    269
    +    // for unnecessarily many times.
    
    270
    +    GlyphContext* prev = &tempGlyphContext_;
    
    271
    +    tempGlyphContext_ = {};
    
    272
    +    for (unsigned n = offset; n < static_cast<unsigned>(limitIndex_);)
    
    273
    +    {
    
    274
    +      if (activeGlyphs_.capacity() <= n)
    
    275
    +        activeGlyphs_.reserve(static_cast<size_t>(n) * 2);
    
    276
    +      if (activeGlyphs_.size() <= n)
    
    277
    +        activeGlyphs_.resize(n + 1);
    
    278
    +
    
    279
    +      auto& ctx = activeGlyphs_[n];
    
    280
    +      ctx.charCode = static_cast<int>(n);
    
    281
    +      ctx.glyphIndex = static_cast<int>(
    
    282
    +          engine_->glyphIndexFromCharCode(static_cast<int>(n), charMapIndex_));
    
    283
    +
    
    284
    +      if (!ctx.glyph)
    
    285
    +        loadSingleContext(&ctx, prev);
    
    286
    +
    
    287
    +      if (outActualLineWidth.x + ctx.hadvance.x > lineWidth)
    
    288
    +        break;
    
    289
    +      outActualLineWidth.x += ctx.hadvance.x;
    
    290
    +      outActualLineWidth.y += ctx.hadvance.y;
    
    291
    +      ++n;
    
    292
    +      ++totalCount;
    
    293
    +      prev = &ctx;
    
    294
    +    }
    
    295
    +  }
    
    296
    +  else
    
    297
    +  {
    
    298
    +    if (!glyphCacheValid_)
    
    299
    +    {
    
    300
    +      clearActive(true);
    
    301
    +      loadStringGlyphs();
    
    302
    +    }
    
    303
    +
    
    304
    +    for (int n = offset; n < activeGlyphs_.size();)
    
    305
    +    {
    
    306
    +      auto& ctx = activeGlyphs_[n];
    
    307
    +      if (repeated_) // if repeated, we must stop when we touch the end of line
    
    308
    +      {
    
    309
    +        if (outActualLineWidth.x + ctx.hadvance.x > lineWidth)
    
    310
    +          break;
    
    311
    +        outActualLineWidth.x += ctx.hadvance.x;
    
    312
    +        outActualLineWidth.y += ctx.hadvance.y;
    
    313
    +        ++n;
    
    314
    +        n %= static_cast<int>(activeGlyphs_.size()); // safe
    
    315
    +      }
    
    316
    +      else if (vertical_)
    
    317
    +      {
    
    318
    +        outActualLineWidth.x += ctx.vadvance.x;
    
    319
    +        outActualLineWidth.y += ctx.vadvance.y;
    
    320
    +        ++n;
    
    321
    +      }
    
    322
    +      else
    
    323
    +      {
    
    324
    +        outActualLineWidth.x += ctx.hadvance.x;
    
    325
    +        outActualLineWidth.y += ctx.hadvance.y;
    
    326
    +        ++n;
    
    327
    +      }
    
    328
    +      ++totalCount;
    
    329
    +    }
    
    330
    +  }
    
    331
    +  return totalCount;
    
    332
    +}
    
    333
    +
    
    334
    +
    
    335
    +int
    
    336
    +StringRenderer::render(int width,
    
    337
    +                       int height,
    
    338
    +                       int offset)
    
    339
    +{
    
    340
    +  if (usingString_)
    
    341
    +    offset = 0;
    
    342
    +  if (limitIndex_ <= 0)
    
    343
    +    return 0;
    
    344
    +
    
    345
    +  // Separated into 3 modes:
    
    346
    +  // Waterfall, fill the whole canvas and only single string.
    
    347
    +
    
    348
    +  if (waterfall_)
    
    349
    +  {
    
    350
    +    // Waterfall
    
    351
    +
    
    352
    +    vertical_ = false;
    
    353
    +    auto originalSize = engine_->pointSize() * 64;
    
    354
    +    auto ptSize = originalSize;
    
    355
    +    auto ptHeight = 64 * 72 * height / engine_->dpi();
    
    356
    +    auto step = (ptSize * ptSize / ptHeight + 64) & ~63;
    
    357
    +    ptSize = ptSize - step * (ptSize / step); // modulo
    
    358
    +
    
    359
    +    int y = 0;
    
    360
    +    // no position param in "All Glyphs" mode
    
    361
    +    int x = static_cast<int>(usingString_ ? (width * position_) : 0);
    
    362
    +    int count = 0;
    
    363
    +
    
    364
    +    while (true)
    
    365
    +    {
    
    366
    +      ptSize += step;
    
    367
    +      engine_->setSizeByPoint(ptSize / 64);
    
    368
    +      clearActive(true);
    
    369
    +      prepareRendering(); // set size/face for engine, so metrics are valid
    
    370
    +      auto& metrics = engine_->currentFontMetrics();
    
    371
    +
    
    372
    +      if (ptSize == originalSize)
    
    373
    +      {
    
    374
    +        // TODO draw a blue line
    
    375
    +      }
    
    376
    +
    
    377
    +      engine_->setSizeByPoint(ptSize / 64);
    
    378
    +      y += (metrics.height >> 6) + 1;
    
    379
    +
    
    380
    +      if (y >= height)
    
    381
    +        break;
    
    382
    +
    
    383
    +      if (ptSize == originalSize)
    
    384
    +      {
    
    385
    +        // TODO draw a blue line
    
    386
    +      }
    
    387
    +
    
    388
    +      loadStringGlyphs();
    
    389
    +      auto lcount = renderLine(x, y + (metrics.descender >> 6),
    
    390
    +                                   width, height,
    
    391
    +                                   offset);
    
    392
    +      count = std::max(count, lcount);
    
    393
    +    }
    
    394
    +    engine_->setSizeByPoint(originalSize / 64);
    
    395
    +
    
    396
    +    return count;
    
    397
    +  }
    
    398
    +
    
    399
    +  if (repeated_ || !usingString_)
    
    400
    +  {
    
    401
    +    // Fill the whole canvas
    
    402
    +
    
    403
    +    prepareRendering();
    
    404
    +    auto& metrics = engine_->currentFontMetrics();
    
    405
    +    auto stepY = (metrics.height >> 6) + 1;
    
    406
    +    auto y = 4 + (metrics.ascender >> 6);
    
    407
    +    auto limitY = height + (metrics.descender >> 6);
    
    408
    +
    
    409
    +    for (; y < limitY; y += stepY)
    
    410
    +      offset = renderLine(0, y, width, height, offset);
    
    411
    +    return offset;
    
    412
    +  }
    
    413
    +
    
    414
    +  // Single string
    
    415
    +  prepareRendering();
    
    416
    +  auto& metrics = engine_->currentFontMetrics();
    
    417
    +  auto x = static_cast<int>(width * position_);
    
    418
    +  // Anchor at top-left in vertical mode, at the center in horizontal mode
    
    419
    +  auto y = vertical_ ? 0 : (height / 2);
    
    420
    +  y += 4 + (metrics.ascender >> 6);
    
    421
    +  return renderLine(x, y, width, height, offset);
    
    422
    +}
    
    423
    +
    
    424
    +
    
    425
    +int
    
    426
    +StringRenderer::renderLine(int x,
    
    427
    +                           int y,
    
    428
    +                           int width,
    
    429
    +                           int height,
    
    430
    +                           int offset)
    
    431
    +{
    
    432
    +  if (x < 0 || y < 0 || x > width || y > height)
    
    433
    +    return 0;
    
    434
    +  
    
    435
    +  y = height - y; // change to Cartesian coordinates
    
    436
    +
    
    437
    +  FT_Vector pen = { 0, 0 };
    
    438
    +  FT_Vector advance;
    
    439
    +
    
    440
    +  // When in "All Glyphs"  mode, no vertical support.
    
    441
    +  if (repeated_ || !usingString_)
    
    442
    +    vertical_ = false; // TODO: Support vertical + repeated
    
    443
    +
    
    444
    +  int lineLength = 64 * (vertical_ ? height : width);
    
    445
    +
    
    446
    +  // first prepare the line & determine the line length
    
    447
    +  int totalCount = prepareLine(offset, lineLength, pen);
    
    448
    +
    
    449
    +  // round to control initial pen position and preserve hinting...
    
    450
    +  // pen.x, y is the actual length now, and we multiple it by pos
    
    451
    +  auto centerFixed = static_cast<int>(0x10000 * position_);
    
    452
    +  if (!usingString_ || repeated_)
    
    453
    +    centerFixed = 0;
    
    454
    +  pen.x = FT_MulFix(pen.x, centerFixed) & ~63;
    
    455
    +  pen.y = FT_MulFix(pen.y, centerFixed) & ~63;
    
    456
    +
    
    457
    +  // ... unless rotating; XXX sbits
    
    458
    +  if (matrixEnabled_)
    
    459
    +    FT_Vector_Transform(&pen, &matrix_);
    
    460
    +
    
    461
    +  // get pen position: penPos = center - pos * width
    
    462
    +  pen.x = (x << 6) - pen.x;
    
    463
    +  pen.y = (y << 6) - pen.y;
    
    464
    +
    
    465
    +  for (int i = offset; i < totalCount + offset; i++)
    
    466
    +  {
    
    467
    +    auto& ctx = activeGlyphs_[i % activeGlyphs_.size()];
    
    468
    +    FT_Glyph image = NULL; // Remember to clean up
    
    469
    +    FT_BBox bbox;
    
    470
    +
    
    471
    +    if (!ctx.glyph)
    
    472
    +      continue;
    
    473
    +
    
    474
    +    // copy the glyph because we're doing manipulation
    
    475
    +    auto error = FT_Glyph_Copy(ctx.glyph, &image);
    
    476
    +    if (error)
    
    477
    +      continue;
    
    478
    +
    
    479
    +    glyphPreprocessCallback_(&image);
    
    480
    +
    
    481
    +    if (image->format != FT_GLYPH_FORMAT_BITMAP)
    
    482
    +    {
    
    483
    +      if (vertical_)
    
    484
    +        error = FT_Glyph_Transform(image, NULL, &ctx.vvector);
    
    485
    +
    
    486
    +      if (!error)
    
    487
    +      {
    
    488
    +        if (matrixEnabled_)
    
    489
    +          error = FT_Glyph_Transform(image, &matrix_, &pen);
    
    490
    +        else
    
    491
    +          error = FT_Glyph_Transform(image, NULL, &pen);
    
    492
    +      }
    
    493
    +
    
    494
    +      if (error)
    
    495
    +      {
    
    496
    +        FT_Done_Glyph(image);
    
    497
    +        continue;
    
    498
    +      }
    
    499
    +    }
    
    500
    +    else
    
    501
    +    {
    
    502
    +      auto bitmap = reinterpret_cast<FT_BitmapGlyph>(image);
    
    503
    +
    
    504
    +      if (vertical_)
    
    505
    +      {
    
    506
    +        bitmap->left += (ctx.vvector.x + pen.x) >> 6;
    
    507
    +        bitmap->top += (ctx.vvector.y + pen.y) >> 6;
    
    508
    +      }
    
    509
    +      else
    
    510
    +      {
    
    511
    +        bitmap->left += pen.x >> 6;
    
    512
    +        bitmap->top += pen.y >> 6;
    
    513
    +      }
    
    514
    +    }
    
    515
    +
    
    516
    +    advance = vertical_ ? ctx.vadvance : ctx.hadvance;
    
    517
    +
    
    518
    +    if (matrixEnabled_)
    
    519
    +      FT_Vector_Transform(&advance, &matrix_);
    
    520
    +
    
    521
    +    pen.x += advance.x;
    
    522
    +    pen.y += advance.y;
    
    523
    +
    
    524
    +    FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_PIXELS, &bbox);
    
    525
    +
    
    526
    +    // check bounding box; if it is completely outside the
    
    527
    +    // display surface, we don't need to render it
    
    528
    +    if (bbox.xMax > 0 
    
    529
    +        && bbox.yMax > 0
    
    530
    +        && bbox.xMin < width
    
    531
    +        && bbox.yMin < height)
    
    532
    +    {
    
    533
    +      renderCallback_(image);
    
    534
    +    }
    
    535
    +
    
    536
    +    FT_Done_Glyph(image);
    
    537
    +  }
    
    538
    +
    
    539
    +  // For repeating
    
    540
    +  if (usingString_ && activeGlyphs_.size())
    
    541
    +    return (offset + totalCount) % activeGlyphs_.size();
    
    542
    +  return offset + totalCount;
    
    543
    +}
    
    544
    +
    
    545
    +
    
    546
    +void
    
    547
    +StringRenderer::clearActive(bool glyphOnly)
    
    548
    +{
    
    549
    +  for (auto& ctx : activeGlyphs_)
    
    550
    +  {
    
    551
    +    if (ctx.cacheNode)
    
    552
    +      FTC_Node_Unref(ctx.cacheNode, engine_->cacheManager());
    
    553
    +    else if (ctx.glyph)
    
    554
    +      FT_Done_Glyph(ctx.glyph); // when caching isn't used
    
    555
    +    ctx.cacheNode = NULL;
    
    556
    +    ctx.glyph = NULL;
    
    557
    +  }
    
    558
    +  if (!glyphOnly)
    
    559
    +    activeGlyphs_.clear();
    
    560
    +
    
    561
    +  glyphCacheValid_ = false;
    
    562
    +}
    
    563
    +
    
    564
    +
    
    565
    +// end of stringrenderer.cpp

  • src/ftinspect/engine/stringrenderer.hpp
    1
    +// stringrenderer.hpp
    
    2
    +
    
    3
    +// Copyright (C) 2022 by Charlie Jiang.
    
    4
    +
    
    5
    +#pragma once
    
    6
    +
    
    7
    +#include <vector>
    
    8
    +#include <functional>
    
    9
    +
    
    10
    +#include <QString>
    
    11
    +
    
    12
    +#include <ft2build.h>
    
    13
    +#include <qslider.h>
    
    14
    +#include <freetype/freetype.h>
    
    15
    +#include <freetype/ftcache.h>
    
    16
    +#include <freetype/ftglyph.h>
    
    17
    +
    
    18
    +// adopted from `ftcommon.h`
    
    19
    +
    
    20
    +class Engine;
    
    21
    +struct GlyphContext
    
    22
    +{
    
    23
    +  int charCode = 0;
    
    24
    +  int glyphIndex = 0;
    
    25
    +  FT_Glyph glyph = NULL;
    
    26
    +  FTC_Node cacheNode = NULL;
    
    27
    +
    
    28
    +  FT_Pos lsbDelta = 0;    // delta caused by hinting
    
    29
    +  FT_Pos rsbDelta = 0;   // delta caused by hinting
    
    30
    +  FT_Vector hadvance = { 0, 0 }; // kerned horizontal advance
    
    31
    +
    
    32
    +  FT_Vector vvector = { 0, 0 };  // vert. origin => hori. origin
    
    33
    +  FT_Vector vadvance = { 0, 0 }; // vertical advance
    
    34
    +};
    
    35
    +
    
    36
    +// Class to populate chars to render, to load and properly position glyphs.
    
    37
    +// Use callbacks to receive characters.
    
    38
    +class StringRenderer
    
    39
    +{
    
    40
    +public:
    
    41
    +  StringRenderer(Engine* engine);
    
    42
    +  ~StringRenderer();
    
    43
    +
    
    44
    +  enum KerningDegree
    
    45
    +  {
    
    46
    +    KD_None = 0,
    
    47
    +    KD_Light,
    
    48
    +    KD_Medium,
    
    49
    +    KD_Tight
    
    50
    +  };
    
    51
    +
    
    52
    +  enum KerningMode
    
    53
    +  {
    
    54
    +    KM_None = 0,
    
    55
    +    KM_Normal,
    
    56
    +    KM_Smart
    
    57
    +  };
    
    58
    +
    
    59
    +  using RenderCallback = std::function<void(FT_Glyph)>;
    
    60
    +  /* The glyph pointer may be replaced. In that case, ownership is transfered
    
    61
    +   * to the renderer, and the new glyph will be eventually freed by
    
    62
    +   * the renderer. The callback is responsible to free the old glyph.
    
    63
    +   * This allows you to do the following:
    
    64
    +   * void callback(FT_Glyph* ptr) {
    
    65
    +   *     ....
    
    66
    +   *     auto oldPtr = *ptr;
    
    67
    +   *     *ptr = ....;
    
    68
    +   *     FT_Done_Glyph(olPtr);
    
    69
    +   * }
    
    70
    +   */
    
    71
    +  using PreprocessCallback = std::function<void(FT_Glyph*)>;
    
    72
    +
    
    73
    +  void setCharMapIndex(int charMapIndex, int limitIndex);
    
    74
    +  void setCallback(RenderCallback cb)
    
    75
    +  {
    
    76
    +    renderCallback_ = std::move(cb);
    
    77
    +  }
    
    78
    +  void setPreprocessCallback(PreprocessCallback cb)
    
    79
    +  {
    
    80
    +    glyphPreprocessCallback_ = std::move(cb);
    
    81
    +  }
    
    82
    +  void setRepeated(bool repeated) { repeated_ = repeated; }
    
    83
    +  void setVertical(bool vertical) { vertical_ = vertical; }
    
    84
    +  void setRotation(double rotation);
    
    85
    +  void setWaterfall(bool waterfall) { waterfall_ = waterfall; }
    
    86
    +  void setPosition(double pos) { position_ = pos; }
    
    87
    +  void setKerning(bool kerning);
    
    88
    +
    
    89
    +  // Need to be called when font or charMap changes
    
    90
    +  void setUseString(QString const& string);
    
    91
    +  void setUseAllGlyphs();
    
    92
    +  
    
    93
    +  int render(int width,
    
    94
    +             int height,
    
    95
    +             int offset);
    
    96
    +  int renderLine(int x, int y,
    
    97
    +                 int width, int height,
    
    98
    +                 int offset);
    
    99
    +
    
    100
    +  void reloadAll();      // text/font/charmap changes, will call
    
    101
    +                         // `reloadGlyphs`
    
    102
    +  void reloadGlyphs();   // any other parameter changes
    
    103
    +
    
    104
    +private:
    
    105
    +  Engine* engine_;
    
    106
    +
    
    107
    +  // Generally, rendering has those steps:
    
    108
    +  // 1. If in string mode, the string is load into `activeGlyphs_`
    
    109
    +  //    (in `updateString`)
    
    110
    +  // 2. The char codes in contexts are converted to glyph indices
    
    111
    +  //    (in `reloadGlyphIndices`)
    
    112
    +  // 3. If in string mode, glyphs are loaded into contexts.
    
    113
    +  //    (in `loadStringGlyphs`)
    
    114
    +  // 4. In `render` function, according to mode, `renderLine` is called line
    
    115
    +  //    by line (as well as `prepareRendering`).
    
    116
    +  // 5. In `renderLine`, if in all glyphs mode, glyphs from the begin index
    
    117
    +  //    are loaded until the line is full (if the glyph already exists, it will
    
    118
    +  //    be reused). If in string mode, it will directly use the prepared glyphs.
    
    119
    +  //    Preprocessing is done within this step, such as emboldening or stroking.
    
    120
    +  //    Eventually the `FT_Glyph` pointer is passed to the callback.
    
    121
    +  
    
    122
    +  GlyphContext tempGlyphContext_;
    
    123
    +  // This vector stores all active glyphs for rendering. When rendering strings,
    
    124
    +  // this is the container for chars, so DO NOT directly clear it to flush
    
    125
    +  // cache, you should clean glyph objects only. However when rendering all
    
    126
    +  // glyphs, it's generally to directly wipe the vector because it's dynamically
    
    127
    +  // generated in `render` function (see above).
    
    128
    +  //
    
    129
    +  // Note: Because of kerning, this list must be ordered and allow duplicate
    
    130
    +  //       characters.
    
    131
    +  //
    
    132
    +  // Actually this means 3 parts of storage: string charcode, glyph indices and
    
    133
    +  // glyph (+ all related info). Different parameter changes will trigger
    
    134
    +  // different levels of flushing.
    
    135
    +  std::vector<GlyphContext> activeGlyphs_;
    
    136
    +  bool glyphCacheValid_ = false;
    
    137
    +
    
    138
    +  int charMapIndex_ = 0;
    
    139
    +  int limitIndex_ = 0;
    
    140
    +  bool usingString_ = false;
    
    141
    +  bool waterfall_ = false;
    
    142
    +  bool repeated_ = false;
    
    143
    +  bool vertical_ = false;
    
    144
    +  double position_ = 0;
    
    145
    +  double rotation_ = 0;
    
    146
    +  int kerningDegree_ = KD_None;
    
    147
    +  KerningMode kerningMode_ = KM_None;
    
    148
    +  FT_Pos trackingKerning_ = 0;
    
    149
    +  FT_Matrix matrix_ = {};
    
    150
    +  bool matrixEnabled_ = false;
    
    151
    +
    
    152
    +  RenderCallback renderCallback_;
    
    153
    +  PreprocessCallback glyphPreprocessCallback_;
    
    154
    +
    
    155
    +  void reloadGlyphIndices();
    
    156
    +  void prepareRendering();
    
    157
    +  void loadSingleContext(GlyphContext* ctx, GlyphContext* prev);
    
    158
    +  // Need to be called when font, charMap or size changes;
    
    159
    +  void loadStringGlyphs();
    
    160
    +  // Returns total line count
    
    161
    +  int prepareLine(int offset, 
    
    162
    +                  int lineWidth,
    
    163
    +                  FT_Vector& outActualLineWidth);
    
    164
    +  void clearActive(bool glyphOnly = false);
    
    165
    +};
    \ No newline at end of file

  • src/ftinspect/meson.build
    ... ... @@ -23,6 +23,7 @@ if qt5_dep.found()
    23 23
         'engine/engine.cpp',
    
    24 24
         'engine/fontfilemanager.cpp',
    
    25 25
         'engine/charmap.cpp',
    
    26
    +    'engine/stringrenderer.cpp',
    
    26 27
     
    
    27 28
         'rendering/glyphbitmap.cpp',
    
    28 29
         'rendering/glyphoutline.cpp',
    

  • src/ftinspect/models/customcomboboxmodels.cpp
    ... ... @@ -241,6 +241,10 @@ AntiAliasingComboBoxModel::AntiAliasingComboBoxModel(QObject* parent)
    241 241
         {FT_LOAD_TARGET_LIGHT, FT_RENDER_MODE_LIGHT, false},
    
    242 242
         "Light"
    
    243 243
       };
    
    244
    +  items_[AntiAliasing_Light_SubPixel] = {
    
    245
    +    {FT_LOAD_TARGET_LIGHT, FT_RENDER_MODE_LIGHT, false},
    
    246
    +    "Light (Sub Pixel)"
    
    247
    +  };
    
    244 248
       items_[AntiAliasing_LCD] = {
    
    245 249
         {FT_LOAD_TARGET_LCD, FT_RENDER_MODE_LCD, false},
    
    246 250
         "LCD (RGB)"
    

  • src/ftinspect/models/customcomboboxmodels.hpp
    ... ... @@ -205,6 +205,7 @@ public:
    205 205
         AntiAliasing_None,
    
    206 206
         AntiAliasing_Normal,
    
    207 207
         AntiAliasing_Light,
    
    208
    +    AntiAliasing_Light_SubPixel,
    
    208 209
         AntiAliasing_LCD,
    
    209 210
         AntiAliasing_LCD_BGR,
    
    210 211
         AntiAliasing_LCD_Vertical,
    

  • src/ftinspect/panels/continuous.cpp
    ... ... @@ -41,6 +41,7 @@ ContinuousTab::reloadFont()
    41 41
       currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
    
    42 42
       setGlyphCount(qBound(0, currentGlyphCount_, INT_MAX));
    
    43 43
       setCharMaps(engine_->currentFontCharMaps());
    
    44
    +  canvas_->stringRenderer().reloadAll();
    
    44 45
       repaintGlyph();
    
    45 46
     }
    
    46 47
     
    
    ... ... @@ -54,16 +55,22 @@ ContinuousTab::syncSettings()
    54 55
       canvas_->setMode(mode);
    
    55 56
       canvas_->setSource(src);
    
    56 57
       canvas_->setBeginIndex(indexSelector_->currentIndex());
    
    57
    -  canvas_->setLimitIndex(glyphLimitIndex_);
    
    58
    -  canvas_->setCharMapIndex(charMapIndex()); // Not directly from the combo box
    
    58
    +  auto& sr = canvas_->stringRenderer();
    
    59
    +  sr.setWaterfall(waterfallCheckBox_->isChecked());
    
    60
    +  sr.setVertical(verticalCheckBox_->isChecked());
    
    61
    +  sr.setKerning(kerningCheckBox_->isChecked());
    
    62
    +  sr.setRotation(rotationSpinBox_->value());
    
    63
    +  sr.setPosition(positionSlider_->value() / 100.0);
    
    64
    +
    
    65
    +  // Not directly from the combo box
    
    66
    +  sr.setCharMapIndex(charMapIndex(), glyphLimitIndex_);
    
    67
    +
    
    68
    +  //sr.setCentered(centered_->isChecked());
    
    59 69
     
    
    60 70
       canvas_->setFancyParams(xEmboldeningSpinBox_->value(),
    
    61 71
                               yEmboldeningSpinBox_->value(),
    
    62 72
                               slantSpinBox_->value());
    
    63 73
       canvas_->setStrokeRadius(strokeRadiusSpinBox_->value());
    
    64
    -  canvas_->setRotation(rotationSpinBox_->value());
    
    65
    -  canvas_->setWaterfall(waterfallCheckBox_->isChecked());
    
    66
    -  canvas_->setVertical(verticalCheckBox_->isChecked());
    
    67 74
     }
    
    68 75
     
    
    69 76
     
    
    ... ... @@ -185,6 +192,8 @@ ContinuousTab::checkSource()
    185 192
       indexSelector_->setEnabled(src == GlyphContinuous::SRC_AllGlyphs);
    
    186 193
       sourceTextEdit_->setEnabled(isText);
    
    187 194
       verticalCheckBox_->setEnabled(isText);
    
    195
    +  positionSlider_->setEnabled(isText);
    
    196
    +  canvas_->setSource(src);
    
    188 197
     
    
    189 198
       repaintGlyph();
    
    190 199
     }
    
    ... ... @@ -206,8 +215,8 @@ ContinuousTab::charMapChanged()
    206 215
       }
    
    207 216
       updateLimitIndex();
    
    208 217
     
    
    218
    +  canvas_->stringRenderer().reloadAll();
    
    209 219
       repaintGlyph();
    
    210
    -
    
    211 220
       lastCharMapIndex_ = newIndex;
    
    212 221
     }
    
    213 222
     
    
    ... ... @@ -220,6 +229,14 @@ ContinuousTab::sourceTextChanged()
    220 229
     }
    
    221 230
     
    
    222 231
     
    
    232
    +void
    
    233
    +ContinuousTab::reloadGlyphsAndRepaint()
    
    234
    +{
    
    235
    +  canvas_->stringRenderer().reloadGlyphs();
    
    236
    +  repaintGlyph();
    
    237
    +}
    
    238
    +
    
    239
    +
    
    223 240
     void
    
    224 241
     ContinuousTab::wheelNavigate(int steps)
    
    225 242
     {
    
    ... ... @@ -266,8 +283,15 @@ ContinuousTab::createLayout()
    266 283
       sourceSelector_->insertItem(GlyphContinuous::SRC_TextStringRepeated,
    
    267 284
                                   tr("Text String (Repeated)"));
    
    268 285
     
    
    269
    -  verticalCheckBox_ = new QCheckBox(tr("Vertical Layout"), this);
    
    286
    +  verticalCheckBox_ = new QCheckBox(tr("Vertical"), this);
    
    270 287
       waterfallCheckBox_ = new QCheckBox(tr("Waterfall"), this);
    
    288
    +  kerningCheckBox_ = new QCheckBox(tr("Kerning"), this);
    
    289
    +
    
    290
    +  positionSlider_ = new QSlider(Qt::Horizontal, this);
    
    291
    +  positionSlider_->setMinimum(0);
    
    292
    +  positionSlider_->setMaximum(100);
    
    293
    +  positionSlider_->setTracking(true);
    
    294
    +  positionSlider_->setSingleStep(5);
    
    271 295
     
    
    272 296
       modeLabel_ = new QLabel(tr("Mode:"), this);
    
    273 297
       sourceLabel_ = new QLabel(tr("Text Source:"), this);
    
    ... ... @@ -277,6 +301,9 @@ ContinuousTab::createLayout()
    277 301
       slantLabel_ = new QLabel(tr("Slanting:"), this);
    
    278 302
       strokeRadiusLabel_ = new QLabel(tr("Stroke Radius:"), this);
    
    279 303
       rotationLabel_ = new QLabel(tr("Rotation:"), this);
    
    304
    +  positionLabel_ = new QLabel(tr("Pos"), this);
    
    305
    +
    
    306
    +  positionLabel_->setAlignment(Qt::AlignCenter);
    
    280 307
     
    
    281 308
       xEmboldeningSpinBox_ = new QDoubleSpinBox(this);
    
    282 309
       yEmboldeningSpinBox_ = new QDoubleSpinBox(this);
    
    ... ... @@ -300,6 +327,10 @@ ContinuousTab::createLayout()
    300 327
       rotationSpinBox_->setMinimum(-180);
    
    301 328
       rotationSpinBox_->setMaximum(180);
    
    302 329
     
    
    330
    +  positionLayout_ = new QVBoxLayout;
    
    331
    +  positionLayout_->addWidget(positionLabel_);
    
    332
    +  positionLayout_->addWidget(positionSlider_);
    
    333
    +
    
    303 334
       bottomLayout_ = new QGridLayout;
    
    304 335
       bottomLayout_->addWidget(sourceLabel_, 0, 0);
    
    305 336
       bottomLayout_->addWidget(modeLabel_, 1, 0);
    
    ... ... @@ -321,9 +352,11 @@ ContinuousTab::createLayout()
    321 352
       bottomLayout_->addWidget(rotationSpinBox_, 0, 3);
    
    322 353
     
    
    323 354
       bottomLayout_->addWidget(indexSelector_, 0, 4, 1, 1);
    
    324
    -  bottomLayout_->addWidget(sourceTextEdit_, 1, 4, 3, 3);
    
    325
    -  bottomLayout_->addWidget(waterfallCheckBox_, 0, 5);
    
    326
    -  bottomLayout_->addWidget(verticalCheckBox_, 0, 6);
    
    355
    +  bottomLayout_->addWidget(sourceTextEdit_, 1, 4, 3, 1);
    
    356
    +  bottomLayout_->addLayout(positionLayout_, 0, 5);
    
    357
    +  bottomLayout_->addWidget(waterfallCheckBox_, 1, 5);
    
    358
    +  bottomLayout_->addWidget(verticalCheckBox_, 2, 5);
    
    359
    +  bottomLayout_->addWidget(kerningCheckBox_, 3, 5);
    
    327 360
     
    
    328 361
       bottomLayout_->setColumnStretch(4, 1);
    
    329 362
     
    
    ... ... @@ -340,7 +373,7 @@ void
    340 373
     ContinuousTab::createConnections()
    
    341 374
     {
    
    342 375
       connect(sizeSelector_, &FontSizeSelector::valueChanged,
    
    343
    -          this, &ContinuousTab::repaintGlyph);
    
    376
    +          this, &ContinuousTab::reloadGlyphsAndRepaint);
    
    344 377
     
    
    345 378
       connect(canvas_, &GlyphContinuous::wheelResize, 
    
    346 379
               this, &ContinuousTab::wheelResize);
    
    ... ... @@ -378,8 +411,13 @@ ContinuousTab::createConnections()
    378 411
               this, &ContinuousTab::repaintGlyph);
    
    379 412
       connect(verticalCheckBox_, &QCheckBox::clicked,
    
    380 413
               this, &ContinuousTab::repaintGlyph);
    
    414
    +  connect(kerningCheckBox_, &QCheckBox::clicked,
    
    415
    +          this, &ContinuousTab::reloadGlyphsAndRepaint);
    
    381 416
       connect(sourceTextEdit_, &QPlainTextEdit::textChanged,
    
    382 417
               this, &ContinuousTab::sourceTextChanged);
    
    418
    +
    
    419
    +  connect(positionSlider_, &QSlider::valueChanged,
    
    420
    +          this, &ContinuousTab::repaintGlyph);
    
    383 421
     }
    
    384 422
     
    
    385 423
     
    
    ... ... @@ -391,6 +429,9 @@ ContinuousTab::setDefaults()
    391 429
       slantSpinBox_->setValue(0.22);
    
    392 430
       strokeRadiusSpinBox_->setValue(0.02);
    
    393 431
       rotationSpinBox_->setValue(0);
    
    432
    +
    
    433
    +  canvas_->setSourceText(sourceTextEdit_->toPlainText());
    
    434
    +  canvas_->setSource(GlyphContinuous::SRC_AllGlyphs);
    
    394 435
     }
    
    395 436
     
    
    396 437
     
    

  • src/ftinspect/panels/continuous.hpp
    ... ... @@ -48,6 +48,7 @@ public:
    48 48
       void checkSource();
    
    49 49
       void charMapChanged();
    
    50 50
       void sourceTextChanged();
    
    51
    +  void reloadGlyphsAndRepaint();
    
    51 52
     
    
    52 53
     private slots:
    
    53 54
       void wheelNavigate(int steps);
    
    ... ... @@ -75,6 +76,7 @@ private:
    75 76
       QLabel* slantLabel_;
    
    76 77
       QLabel* strokeRadiusLabel_;
    
    77 78
       QLabel* rotationLabel_;
    
    79
    +  QLabel* positionLabel_;
    
    78 80
     
    
    79 81
       QDoubleSpinBox* xEmboldeningSpinBox_;
    
    80 82
       QDoubleSpinBox* yEmboldeningSpinBox_;
    
    ... ... @@ -84,15 +86,18 @@ private:
    84 86
     
    
    85 87
       QCheckBox* verticalCheckBox_;
    
    86 88
       QCheckBox* waterfallCheckBox_;
    
    89
    +  QCheckBox* kerningCheckBox_;
    
    90
    +  QSlider* positionSlider_;
    
    87 91
     
    
    88 92
       GlyphIndexSelector* indexSelector_;
    
    89 93
       QPlainTextEdit* sourceTextEdit_;
    
    90 94
     
    
    91 95
       std::vector<CharMapInfo> charMaps_;
    
    92 96
     
    
    97
    +  QVBoxLayout* positionLayout_;
    
    93 98
       QGridLayout* bottomLayout_;
    
    94 99
       QVBoxLayout* mainLayout_;
    
    95
    -  
    
    100
    +
    
    96 101
       void createLayout();
    
    97 102
       void createConnections();
    
    98 103
     
    

  • src/ftinspect/panels/settingpanel.cpp
    ... ... @@ -191,6 +191,9 @@ SettingPanel::syncSettings()
    191 191
     
    
    192 192
       engine_->setEmbeddedBitmap(embeddedBitmapCheckBox_->isChecked());
    
    193 193
       engine_->setLCDUsesBGR(aaSettings.isBGR);
    
    194
    +  engine_->setLCDSubPixelPositioning(
    
    195
    +    antiAliasingComboBox_->currentIndex()
    
    196
    +    == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel);
    
    194 197
     }
    
    195 198
     
    
    196 199
     
    
    ... ... @@ -226,7 +229,7 @@ SettingPanel::createConnections()
    226 229
       connect(autoHintingCheckBox_, &QCheckBox::clicked,
    
    227 230
               this, &SettingPanel::checkAutoHinting);
    
    228 231
       connect(embeddedBitmapCheckBox_, &QCheckBox::clicked,
    
    229
    -          this, &SettingPanel::repaintNeeded);
    
    232
    +          this, &SettingPanel::fontReloadNeeded);
    
    230 233
     }
    
    231 234
     
    
    232 235
     
    

  • src/ftinspect/rendering/glyphcontinuous.cpp
    ... ... @@ -5,9 +5,7 @@
    5 5
     #include "glyphcontinuous.hpp"
    
    6 6
     
    
    7 7
     #include "../engine/engine.hpp"
    
    8
    -#include "../rendering/renderutils.hpp"
    
    9 8
     
    
    10
    -#include <cmath>
    
    11 9
     #include <QPainter>
    
    12 10
     #include <QWheelEvent>
    
    13 11
     
    
    ... ... @@ -15,7 +13,9 @@
    15 13
     
    
    16 14
     
    
    17 15
     GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
    
    18
    -: QWidget(parent), engine_(engine)
    
    16
    +: QWidget(parent),
    
    17
    +  engine_(engine),
    
    18
    +  stringRenderer_(engine)
    
    19 19
     {
    
    20 20
       setAcceptDrops(false);
    
    21 21
       setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
    ... ... @@ -26,11 +26,36 @@ GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
    26 26
     
    
    27 27
     GlyphContinuous::~GlyphContinuous()
    
    28 28
     {
    
    29
    -  cleanCloned();
    
    30 29
       FT_Stroker_Done(stroker_);
    
    31 30
     }
    
    32 31
     
    
    33 32
     
    
    33
    +void
    
    34
    +GlyphContinuous::setSource(Source source)
    
    35
    +{
    
    36
    +  source_ = source;
    
    37
    +  switch (source)
    
    38
    +  {
    
    39
    +  case SRC_AllGlyphs:
    
    40
    +    stringRenderer_.setUseAllGlyphs();
    
    41
    +    break;
    
    42
    +
    
    43
    +  case SRC_TextStringRepeated:
    
    44
    +  case SRC_TextString:
    
    45
    +    updateRendererText();
    
    46
    +    break;
    
    47
    +  }
    
    48
    +}
    
    49
    +
    
    50
    +
    
    51
    +void
    
    52
    +GlyphContinuous::setSourceText(QString text)
    
    53
    +{
    
    54
    +  text_ = std::move(text);
    
    55
    +  updateRendererText();
    
    56
    +}
    
    57
    +
    
    58
    +
    
    34 59
     void
    
    35 60
     GlyphContinuous::paintEvent(QPaintEvent* event)
    
    36 61
     {
    
    ... ... @@ -38,27 +63,10 @@ GlyphContinuous::paintEvent(QPaintEvent* event)
    38 63
       painter.begin(this);
    
    39 64
       painter.fillRect(rect(), Qt::white);
    
    40 65
     
    
    41
    -  if (limitIndex_ > 0)
    
    42
    -  {
    
    43
    -    prePaint();
    
    66
    +  prePaint();
    
    44 67
     
    
    45
    -    switch (source_)
    
    46
    -    {
    
    47
    -    case SRC_AllGlyphs:
    
    48
    -      switch (mode_)
    
    49
    -      {
    
    50
    -      case M_Normal:
    
    51
    -      case M_Fancy:
    
    52
    -      case M_Stroked:
    
    53
    -        paintAG(&painter);
    
    54
    -        break;
    
    55
    -      }
    
    56
    -      break;
    
    57
    -    case SRC_TextString:
    
    58
    -      break;
    
    59
    -    }
    
    60
    -    emit displayingCountUpdated(displayingCount_);
    
    61
    -  }
    
    68
    +  paintByRenderer(&painter);
    
    69
    +  emit displayingCountUpdated(displayingCount_);
    
    62 70
     
    
    63 71
       painter.end();
    
    64 72
     }
    
    ... ... @@ -76,7 +84,7 @@ GlyphContinuous::wheelEvent(QWheelEvent* event)
    76 84
     
    
    77 85
     
    
    78 86
     void
    
    79
    -GlyphContinuous::paintAG(QPainter* painter)
    
    87
    +GlyphContinuous::paintByRenderer(QPainter* painter)
    
    80 88
     {
    
    81 89
       if (mode_ == M_Stroked)
    
    82 90
       {
    
    ... ... @@ -87,92 +95,49 @@ GlyphContinuous::paintAG(QPainter* painter)
    87 95
                        0);
    
    88 96
       }
    
    89 97
     
    
    90
    -  for (int i = beginIndex_; i < limitIndex_; i++)
    
    91
    -  {
    
    92
    -    unsigned index = i;
    
    93
    -    if (charMapIndex_ >= 0)
    
    94
    -      index = engine_->glyphIndexFromCharCode(i, charMapIndex_);
    
    95
    -
    
    96
    -    if (!loadGlyph(index))
    
    97
    -      break;
    
    98
    -
    
    99
    -    // All Glyphs need no tranformation, and Waterfall isn't handled here.
    
    100
    -    switch (mode_)
    
    98
    +  stringRenderer_.setRepeated(source_ == SRC_TextStringRepeated);
    
    99
    +  stringRenderer_.setCallback(
    
    100
    +    [&](FT_Glyph glyph)
    
    101 101
         {
    
    102
    -    case M_Fancy:
    
    103
    -      transformGlyphFancy();
    
    104
    -      break;
    
    105
    -    case M_Stroked:
    
    106
    -      transformGlyphStroked();
    
    107
    -      break;
    
    108
    -    default:;
    
    109
    -    }
    
    110
    -
    
    111
    -    if (!paintChar(painter))
    
    112
    -      break;
    
    113
    -    cleanCloned();
    
    114
    -
    
    115
    -    displayingCount_++;
    
    116
    -  }
    
    117
    -  cleanCloned();
    
    102
    +      drawSingleGlyph(painter, glyph);
    
    103
    +    });
    
    104
    +  stringRenderer_.setPreprocessCallback(
    
    105
    +    [&](FT_Glyph* ptr)
    
    106
    +    {
    
    107
    +      preprocessGlyph(ptr);
    
    108
    +    });
    
    109
    +  displayingCount_ = stringRenderer_.render(width(), height(), beginIndex_);
    
    118 110
     }
    
    119 111
     
    
    120 112
     
    
    121 113
     void
    
    122
    -GlyphContinuous::transformGlyphFancy()
    
    114
    +GlyphContinuous::transformGlyphFancy(FT_Glyph glyph)
    
    123 115
     {
    
    124 116
       // adopted from ftview.c:289
    
    125
    -  /***************************************************************/
    
    126
    -  /*                                                             */
    
    127
    -  /*  2*2 affine transformation matrix, 16.16 fixed float format */
    
    128
    -  /*                                                             */
    
    129
    -  /*  Shear matrix:                                              */
    
    130
    -  /*                                                             */
    
    131
    -  /*         | x' |     | 1  k |   | x |          x' = x + ky    */
    
    132
    -  /*         |    |  =  |      | * |   |   <==>                  */
    
    133
    -  /*         | y' |     | 0  1 |   | y |          y' = y         */
    
    134
    -  /*                                                             */
    
    135
    -  /*        outline'     shear    outline                        */
    
    136
    -  /*                                                             */
    
    137
    -  /***************************************************************/
    
    138
    -
    
    139
    -  FT_Matrix shear;
    
    140
    -  FT_Pos xstr, ystr;
    
    141
    -
    
    142
    -  shear.xx = 1 << 16;
    
    143
    -  shear.xy = static_cast<FT_Fixed>(slant_ * (1 << 16));
    
    144
    -  shear.yx = 0;
    
    145
    -  shear.yy = 1 << 16;
    
    146
    -
    
    147
    -  xstr = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
    
    148
    -  ystr = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
    
    149
    -
    
    150
    -  if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
    
    117
    +  if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
    
    151 118
       {
    
    152
    -    if (!isGlyphCloned_)
    
    153
    -      cloneGlyph();
    
    154
    -    FT_Outline_Transform(&outline_, &shear);
    
    155
    -    if (FT_Outline_EmboldenXY(&outline_, xstr, ystr))
    
    119
    +    auto outline = reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
    
    120
    +    FT_Glyph_Transform(glyph, &shearMatrix_, NULL);
    
    121
    +    if (FT_Outline_EmboldenXY(&outline, emboldeningX_, emboldeningY_))
    
    156 122
         {
    
    157 123
           // XXX error handling?
    
    158 124
           return;
    
    159 125
         }
    
    160 126
     
    
    161
    -    if (glyph_->advance.x)
    
    162
    -      glyph_->advance.x += xstr;
    
    127
    +    if (glyph->advance.x)
    
    128
    +      glyph->advance.x += emboldeningX_;
    
    163 129
     
    
    164
    -    if (glyph_->advance.y)
    
    165
    -      glyph_->advance.y += ystr;
    
    130
    +    if (glyph->advance.y)
    
    131
    +      glyph->advance.y += emboldeningY_;
    
    166 132
       }
    
    167
    -  else if (glyph_->format == FT_GLYPH_FORMAT_BITMAP)
    
    133
    +  else if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
    
    168 134
       {
    
    169
    -    if (!isBitmapCloned_)
    
    170
    -      cloneBitmap();
    
    171
    -    xstr &= ~63;
    
    172
    -    ystr &= ~63;
    
    135
    +    auto xstr = emboldeningX_ & ~63;
    
    136
    +    auto ystr = emboldeningY_ & ~63;
    
    173 137
     
    
    138
    +    auto bitmap = &reinterpret_cast<FT_BitmapGlyph>(glyph)->bitmap;
    
    174 139
         // No shearing support for bitmap
    
    175
    -    FT_Bitmap_Embolden(engine_->ftLibrary(), &bitmap_, 
    
    140
    +    FT_Bitmap_Embolden(engine_->ftLibrary(), bitmap, 
    
    176 141
                            xstr, ystr);
    
    177 142
       }
    
    178 143
       else
    
    ... ... @@ -180,21 +145,16 @@ GlyphContinuous::transformGlyphFancy()
    180 145
     }
    
    181 146
     
    
    182 147
     
    
    183
    -void
    
    184
    -GlyphContinuous::transformGlyphStroked()
    
    148
    +FT_Glyph
    
    149
    +GlyphContinuous::transformGlyphStroked(FT_Glyph glyph)
    
    185 150
     {
    
    186 151
       // Well, here only outline glyph is supported.
    
    187
    -  if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
    
    188
    -    return;
    
    189
    -  auto oldGlyph = glyph_;
    
    190
    -  auto error = FT_Glyph_Stroke(&glyph_, stroker_, 0);
    
    191
    -  if (!error)
    
    192
    -  {
    
    193
    -    if (isGlyphCloned_)
    
    194
    -      FT_Done_Glyph(oldGlyph);
    
    195
    -    isGlyphCloned_ = true;
    
    196
    -    outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
    
    197
    -  }
    
    152
    +  if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
    
    153
    +    return NULL;
    
    154
    +  auto error = FT_Glyph_Stroke(&glyph, stroker_, 0);
    
    155
    +  if (error)
    
    156
    +    return NULL;
    
    157
    +  return glyph;
    
    198 158
     }
    
    199 159
     
    
    200 160
     
    
    ... ... @@ -203,162 +163,91 @@ GlyphContinuous::prePaint()
    203 163
     {
    
    204 164
       displayingCount_ = 0;
    
    205 165
       engine_->reloadFont();
    
    206
    -  metrics_ = engine_->currentFontMetrics();
    
    166
    +  if (engine_->currentFontNumberOfGlyphs() > 0)
    
    167
    +    metrics_ = engine_->currentFontMetrics();
    
    207 168
       x_ = 0;
    
    208 169
       // See ftview.c:42
    
    209 170
       y_ = ((metrics_.ascender - metrics_.descender + 63) >> 6) + 4;
    
    210 171
       stepY_ = ((metrics_.height + 63) >> 6) + 4;
    
    211
    -}
    
    212
    -
    
    213
    -
    
    214
    -bool
    
    215
    -GlyphContinuous::paintChar(QPainter* painter)
    
    216
    -{
    
    217
    -  // ftview.c:557
    
    218
    -  int width = glyph_->advance.x ? glyph_->advance.x >> 16
    
    219
    -                                : metrics_.y_ppem / 2;
    
    220
    -
    
    221
    -  if (!checkFitX(x_ + width))
    
    222
    -  {
    
    223
    -    x_ = 0;
    
    224
    -    y_ += stepY_;
    
    225
    -
    
    226
    -    if (!checkFitY(y_))
    
    227
    -      return false;
    
    228
    -  }
    
    229
    -
    
    230
    -  x_++; // extra space
    
    231
    -  if (glyph_->advance.x == 0)
    
    232
    -  {
    
    233
    -    // Draw a red square to indicate
    
    234
    -      painter->fillRect(x_, y_ - width, width, width,
    
    235
    -                        Qt::red);
    
    236
    -    x_ += width;
    
    237
    -  }
    
    238
    -
    
    239
    -  // The real drawing part
    
    240
    -  // XXX: this is different from what's being done in
    
    241
    -  // `ftcommon.c`:FTDemo_Draw_Slot: is this correct??
    
    242 172
     
    
    243
    -  QImage* image;
    
    173
    +  // Used by fancy:
    
    174
    +  // adopted from ftview.c:289
    
    175
    +  /***************************************************************/
    
    176
    +  /*                                                             */
    
    177
    +  /*  2*2 affine transformation matrix, 16.16 fixed float format */
    
    178
    +  /*                                                             */
    
    179
    +  /*  Shear matrix:                                              */
    
    180
    +  /*                                                             */
    
    181
    +  /*         | x' |     | 1  k |   | x |          x' = x + ky    */
    
    182
    +  /*         |    |  =  |      | * |   |   <==>                  */
    
    183
    +  /*         | y' |     | 0  1 |   | y |          y' = y         */
    
    184
    +  /*                                                             */
    
    185
    +  /*        outline'     shear    outline                        */
    
    186
    +  /*                                                             */
    
    187
    +  /***************************************************************/
    
    244 188
       
    
    245
    -  if (bitmap_.buffer) // Always prefer `bitmap_` since it can be manipulated
    
    246
    -    image = engine_->convertBitmapToQImage(&bitmap_);
    
    247
    -  else
    
    248
    -    image = engine_->convertGlyphToQImage(glyph_, NULL);
    
    249
    -  auto offset = engine_->computeGlyphOffset(glyph_);
    
    250 189
     
    
    251
    -  painter->drawImage(offset + QPoint(x_, y_),
    
    252
    -                     *image);
    
    253
    -  delete image;
    
    190
    +  shearMatrix_.xx = 1 << 16;
    
    191
    +  shearMatrix_.xy = static_cast<FT_Fixed>(slant_ * (1 << 16));
    
    192
    +  shearMatrix_.yx = 0;
    
    193
    +  shearMatrix_.yy = 1 << 16;
    
    254 194
     
    
    255
    -  x_ += width;
    
    256
    -  return true;
    
    257
    -}
    
    258
    -
    
    259
    -
    
    260
    -bool
    
    261
    -GlyphContinuous::loadGlyph(int index)
    
    262
    -{
    
    263
    -  if (isGlyphCloned_)
    
    264
    -    FT_Done_Glyph(glyph_);
    
    265
    -  glyph_ = engine_->loadGlyphWithoutUpdate(index);
    
    266
    -  isGlyphCloned_ = false;
    
    267
    -  if (!glyph_)
    
    268
    -    return false;
    
    269
    -  refreshOutlineOrBitmapFromGlyph();
    
    270
    -  return true;
    
    195
    +  emboldeningX_ = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
    
    196
    +  emboldeningY_ = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
    
    271 197
     }
    
    272 198
     
    
    273 199
     
    
    274 200
     void
    
    275
    -GlyphContinuous::cloneGlyph()
    
    201
    +GlyphContinuous::updateRendererText()
    
    276 202
     {
    
    277
    -  if (isGlyphCloned_)
    
    278
    -    return;
    
    279
    -  glyph_ = ::cloneGlyph(glyph_);
    
    280
    -  refreshOutlineOrBitmapFromGlyph();
    
    281
    -  isGlyphCloned_ = true;
    
    203
    +  stringRenderer_.setUseString(text_); // TODO this need to be called when font,
    
    204
    +                                       // size or charmap change
    
    282 205
     }
    
    283 206
     
    
    284 207
     
    
    285 208
     void
    
    286
    -GlyphContinuous::cloneBitmap()
    
    209
    +GlyphContinuous::preprocessGlyph(FT_Glyph* glyphPtr)
    
    287 210
     {
    
    288
    -  if (isBitmapCloned_)
    
    289
    -    return;
    
    290
    -  bitmap_ = ::cloneBitmap(engine_->ftLibrary(), &bitmap_);
    
    291
    -  isBitmapCloned_ = true;
    
    292
    -}
    
    293
    -
    
    294
    -
    
    295
    -void
    
    296
    -GlyphContinuous::refreshOutlineOrBitmapFromGlyph()
    
    297
    -{
    
    298
    -  if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
    
    211
    +  auto glyph = *glyphPtr;
    
    212
    +  switch (mode_)
    
    299 213
       {
    
    300
    -    outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
    
    301
    -
    
    302
    -    // Make `bitmap_` invalid
    
    303
    -    if (isBitmapCloned_)
    
    304
    -      FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
    
    305
    -    isBitmapCloned_ = false;
    
    306
    -    bitmap_.buffer = NULL;
    
    307
    -  }
    
    308
    -  else if (glyph_->format == FT_GLYPH_FORMAT_BITMAP)
    
    214
    +  case M_Fancy:
    
    215
    +    transformGlyphFancy(glyph);
    
    216
    +    break;
    
    217
    +  case M_Stroked:
    
    309 218
       {
    
    310
    -    // Initialize `bitmap_`
    
    311
    -    if (isBitmapCloned_)
    
    312
    -      FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
    
    313
    -    isBitmapCloned_ = false;
    
    314
    -    bitmap_ = reinterpret_cast<FT_BitmapGlyph>(glyph_)->bitmap;
    
    315
    -
    
    316
    -    outline_.points = NULL;
    
    219
    +    auto stroked = transformGlyphStroked(glyph);
    
    220
    +    if (stroked)
    
    221
    +    {
    
    222
    +      FT_Done_Glyph(glyph);
    
    223
    +      *glyphPtr = stroked;
    
    224
    +    }
    
    317 225
       }
    
    318
    -  else
    
    319
    -  {
    
    320
    -    // Both invalid.
    
    321
    -    outline_.points = NULL;
    
    322
    -
    
    323
    -    if (isBitmapCloned_)
    
    324
    -      FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
    
    325
    -    isBitmapCloned_ = false;
    
    326
    -    bitmap_.buffer = NULL;
    
    226
    +  break;
    
    327 227
       }
    
    328 228
     }
    
    329 229
     
    
    330 230
     
    
    331 231
     void
    
    332
    -GlyphContinuous::cleanCloned()
    
    232
    +GlyphContinuous::drawSingleGlyph(QPainter* painter, FT_Glyph glyph)
    
    333 233
     {
    
    334
    -  if (isGlyphCloned_)
    
    335
    -  {
    
    336
    -    if (glyph_->format == FT_GLYPH_FORMAT_BITMAP && !isBitmapCloned_)
    
    337
    -      bitmap_.buffer = NULL;
    
    338
    -
    
    339
    -    FT_Done_Glyph(glyph_);
    
    340
    -    isGlyphCloned_ = false;
    
    341
    -  }
    
    342
    -  if (isBitmapCloned_)
    
    234
    +  // ftview.c:557
    
    235
    +  int width = glyph->advance.x ? glyph->advance.x >> 16
    
    236
    +                                : metrics_.y_ppem / 2;
    
    237
    +  
    
    238
    +  if (glyph->advance.x == 0)
    
    343 239
       {
    
    344
    -    FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
    
    345
    -    bitmap_.buffer = NULL;
    
    346
    -    isBitmapCloned_ = false;
    
    240
    +    // Draw a red square to indicate
    
    241
    +      painter->fillRect(x_, y_ - width, width, width,
    
    242
    +                        Qt::red);
    
    347 243
       }
    
    348
    -}
    
    349 244
     
    
    245
    +  QRect rect;
    
    246
    +  QImage* image = engine_->convertGlyphToQImage(glyph, &rect);
    
    247
    +  rect.setTop(height() - rect.top());
    
    350 248
     
    
    351
    -bool
    
    352
    -GlyphContinuous::checkFitX(int x)
    
    353
    -{
    
    354
    -  return x < width() - 3;
    
    355
    -}
    
    356
    -
    
    357
    -
    
    358
    -bool
    
    359
    -GlyphContinuous::checkFitY(int y)
    
    360
    -{
    
    361
    -  return y < height() - 3;
    
    249
    +  painter->drawImage(rect.topLeft(), *image);
    
    250
    +  delete image;
    
    362 251
     }
    
    363 252
     
    
    364 253
     
    

  • src/ftinspect/rendering/glyphcontinuous.hpp
    ... ... @@ -5,6 +5,7 @@
    5 5
     #pragma once
    
    6 6
     
    
    7 7
     #include "graphicsdefault.hpp"
    
    8
    +#include "../engine/stringrenderer.hpp"
    
    8 9
     
    
    9 10
     #include <utility>
    
    10 11
     
    
    ... ... @@ -40,12 +41,11 @@ public:
    40 41
       };
    
    41 42
     
    
    42 43
       int displayingCount() { return displayingCount_; }
    
    44
    +  StringRenderer& stringRenderer() { return stringRenderer_; }
    
    43 45
     
    
    44 46
       // all those setters don't trigger repaint.
    
    45 47
       void setBeginIndex(int index) { beginIndex_ = index; }
    
    46
    -  void setLimitIndex(int index) { limitIndex_ = index; }
    
    47
    -  void setCharMapIndex(int index) { charMapIndex_ = index; }
    
    48
    -  void setSource(Source mode) { source_ = mode; }
    
    48
    +  void setSource(Source source);
    
    49 49
       void setMode(Mode mode) { mode_ = mode; }
    
    50 50
       void setFancyParams(double boldX, double boldY, double slant)
    
    51 51
       {
    
    ... ... @@ -54,10 +54,7 @@ public:
    54 54
         slant_ = slant;
    
    55 55
       }
    
    56 56
       void setStrokeRadius(double radius) { strokeRadius_ = radius; }
    
    57
    -  void setRotation(double rotation) { rotation_ = rotation; }
    
    58
    -  void setVertical(bool vertical) { vertical_ = vertical; }
    
    59
    -  void setWaterfall(bool waterfall) { waterfall_ = waterfall; }
    
    60
    -  void setSourceText(QString text) { text_ = std::move(text); }
    
    57
    +  void setSourceText(QString text);
    
    61 58
     
    
    62 59
     signals:
    
    63 60
       void wheelNavigate(int steps);
    
    ... ... @@ -70,56 +67,37 @@ protected:
    70 67
     
    
    71 68
     private:
    
    72 69
       Engine* engine_;
    
    70
    +  StringRenderer stringRenderer_;
    
    73 71
     
    
    74 72
       Source source_ = SRC_AllGlyphs;
    
    75 73
       Mode mode_ = M_Normal;
    
    76 74
       int beginIndex_;
    
    77
    -  int limitIndex_;
    
    78
    -  int charMapIndex_;
    
    79 75
       double boldX_, boldY_, slant_;
    
    80 76
       double strokeRadius_;
    
    81
    -  double rotation_;
    
    82
    -  bool vertical_;
    
    83
    -  bool waterfall_;
    
    84 77
       QString text_;
    
    85 78
     
    
    86 79
       int displayingCount_ = 0;
    
    87 80
       FT_Size_Metrics metrics_;
    
    88 81
       int x_ = 0, y_ = 0;
    
    89 82
       int stepY_ = 0;
    
    90
    -
    
    91
    -  // Pay especially attention to life cycles & ownerships of those objects:
    
    92
    -  // Note that outline and bitmap can be either invalid, owned by glyph or
    
    93
    -  // owned by `this`.
    
    94
    -  // If owned by `this`, then it's safe to do manipulation, and need to cleanup
    
    95
    -  // If owned by glyph, then must clone to do manipulation, and no cleanup
    
    96
    -  // In `loadGraph`, these 3 values will all be updated.
    
    97
    -  // Note that `glyph_` is a pointer, while `outline_` and `bitmap_` are structs
    
    98
    -  FT_Glyph glyph_;
    
    99
    -  FT_Outline outline_; // Using outline_->points == NULL to determine validity
    
    100
    -  FT_Bitmap bitmap_; // Using bitmap_->buffer == NULL to determine validity
    
    101
    -  // when glyph is cloned, outline is factually also cloned
    
    102
    -  // never manually clone your outline or you can't easily render it!
    
    103
    -  bool isGlyphCloned_ = false;
    
    104
    -  bool isBitmapCloned_ = false;
    
    83
    +  FT_Pos emboldeningX_, emboldeningY_;
    
    84
    +  FT_Matrix shearMatrix_;
    
    105 85
     
    
    106 86
       FT_Stroker stroker_;
    
    107 87
     
    
    108
    -  void paintAG(QPainter* painter);
    
    109
    -  void transformGlyphFancy();
    
    110
    -  void transformGlyphStroked();
    
    111
    -  void prePaint();
    
    112
    -  // return if there's enough space to paint the current char
    
    113
    -  bool paintChar(QPainter* painter);
    
    114
    -  bool loadGlyph(int index);
    
    88
    +  void paintByRenderer(QPainter* painter);
    
    115 89
     
    
    116
    -  void cloneGlyph();
    
    117
    -  void cloneBitmap();
    
    118
    -  void refreshOutlineOrBitmapFromGlyph();
    
    119
    -  void cleanCloned();
    
    90
    +  // These two are used indendpent of current glyph variables
    
    91
    +  // and assumes ownership of glyphs, but don't free them.
    
    92
    +  // However, remember to free the glyph returned from `transformGlyphStroked`
    
    93
    +  void transformGlyphFancy(FT_Glyph glyph);
    
    94
    +  FT_Glyph transformGlyphStroked(FT_Glyph glyph);
    
    120 95
     
    
    121
    -  bool checkFitX(int x);
    
    122
    -  bool checkFitY(int y);
    
    96
    +  void prePaint();
    
    97
    +  void updateRendererText();
    
    98
    +  void preprocessGlyph(FT_Glyph* glyphPtr);
    
    99
    +  void drawSingleGlyph(QPainter* painter,
    
    100
    +                       FT_Glyph glyph);
    
    123 101
     };
    
    124 102
     
    
    125 103
     
    


  • reply via email to

    [Prev in Thread] Current Thread [Next in Thread]