Commits:
-
1f97743b
by Charlie Jiang
at 2022-07-26T16:13:41+08:00
* src/ftinspect/models/customcomboboxmodels.cpp: Disable subpixel light too.
When "Light" AA mode is disabled, the SubPixel-Light mode should be disabled
as well.
-
7532d1a0
by Charlie Jiang
at 2022-07-26T19:00:43+08:00
[ftinspect] Support color layer font.
TODO: After implemented SFNT, allow showing of the palette names.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
Add fields to hold palettes info and functions to retrieve palette.
Add options about color layer rendering.
Add `tryDirectRenderColorLayers` to perform color layer rendering.
* src/ftinspect/engine/stringrenderer.cpp,
src/ftinspect/engine/stringrenderer.hpp: Support color layered rendering.
* src/ftinspect/rendering/glyphcontinuous.cpp,
src/ftinspect/rendering/glyphcontinuous.hpp: Add a callback to draw
pre-converted images.
* src/ftinspect/engine/paletteinfo.cpp,
src/ftinspect/engine/paletteinfo.hpp: New files. `PaletteInfo` holds
metadata about palettes.
* src/ftinspect/panels/continuous.cpp: Bugfix: Sync settings before calling
renderer to reload all glyphs.
* src/ftinspect/panels/settingpanel.cpp,
src/ftinspect/panels/settingpanel.hpp:
Add "Enable Color Layer" and "Palette" options. Add code populating
palettes from the engine, so rename `checkHinting` to a more general
`onFontChanged`. Minor lifecycle bugfix.
* src/ftinspect/maingui.cpp: Rename `SettingPanel::checkHinting` to
`onFontChanged`.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
-
6a264517
by Charlie Jiang
at 2022-07-26T19:03:03+08:00
[ftinspect] Support color layer rendering in Singular View.
* src/ftinspect/rendering/glyphbitmap.cpp,
src/ftinspect/rendering/glyphbitmap.hpp:
Add an extra argument `glyphIndex` in the constructor and attempt to
render the glyph as color layered in the first.
* src/ftinspect/panels/singular.cpp: Pass glyph index to `GlyphBitmap`.
18 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/paletteinfo.cpp"
|
26
|
27
|
"engine/stringrenderer.cpp"
|
27
|
28
|
|
28
|
29
|
"rendering/glyphbitmap.cpp"
|
src/ftinspect/engine/engine.cpp
... |
... |
@@ -343,6 +343,9 @@ Engine::loadFont(int fontIndex, |
343
|
343
|
ftSize_ = NULL;
|
344
|
344
|
curFamilyName_ = QString();
|
345
|
345
|
curStyleName_ = QString();
|
|
346
|
+
|
|
347
|
+ curCharMaps_.clear();
|
|
348
|
+ curPaletteInfos_.clear();
|
346
|
349
|
}
|
347
|
350
|
else
|
348
|
351
|
{
|
... |
... |
@@ -362,6 +365,8 @@ Engine::loadFont(int fontIndex, |
362
|
365
|
curCharMaps_.reserve(face->num_charmaps);
|
363
|
366
|
for (int i = 0; i < face->num_charmaps; i++)
|
364
|
367
|
curCharMaps_.emplace_back(i, face->charmaps[i]);
|
|
368
|
+
|
|
369
|
+ loadPaletteInfos();
|
365
|
370
|
}
|
366
|
371
|
|
367
|
372
|
curNumGlyphs_ = numGlyphs;
|
... |
... |
@@ -380,6 +385,22 @@ Engine::reloadFont() |
380
|
385
|
}
|
381
|
386
|
|
382
|
387
|
|
|
388
|
+void
|
|
389
|
+Engine::loadPalette()
|
|
390
|
+{
|
|
391
|
+ palette_ = NULL;
|
|
392
|
+ if (paletteData_.num_palettes == 0
|
|
393
|
+ || paletteIndex_ < 0
|
|
394
|
+ || paletteData_.num_palettes <= paletteIndex_)
|
|
395
|
+ return;
|
|
396
|
+
|
|
397
|
+ FT_Palette_Select(ftSize_->face,
|
|
398
|
+ static_cast<FT_UShort>(paletteIndex_),
|
|
399
|
+ &palette_);
|
|
400
|
+ // XXX error handling
|
|
401
|
+}
|
|
402
|
+
|
|
403
|
+
|
383
|
404
|
void
|
384
|
405
|
Engine::removeFont(int fontIndex, bool closeFile)
|
385
|
406
|
{
|
... |
... |
@@ -817,6 +838,24 @@ Engine::queryEngine() |
817
|
838
|
}
|
818
|
839
|
|
819
|
840
|
|
|
841
|
+void
|
|
842
|
+Engine::loadPaletteInfos()
|
|
843
|
+{
|
|
844
|
+ curPaletteInfos_.clear();
|
|
845
|
+
|
|
846
|
+ if (FT_Palette_Data_Get(ftSize_->face, &paletteData_))
|
|
847
|
+ {
|
|
848
|
+ // XXX Error handling
|
|
849
|
+ paletteData_.num_palettes = 0;
|
|
850
|
+ return;
|
|
851
|
+ }
|
|
852
|
+
|
|
853
|
+ curPaletteInfos_.reserve(paletteData_.num_palettes);
|
|
854
|
+ for (int i = 0; i < paletteData_.num_palettes; ++i)
|
|
855
|
+ curPaletteInfos_.emplace_back(ftSize_->face, paletteData_, i);
|
|
856
|
+}
|
|
857
|
+
|
|
858
|
+
|
820
|
859
|
void
|
821
|
860
|
convertLCDToARGB(FT_Bitmap& bitmap,
|
822
|
861
|
QImage& image,
|
... |
... |
@@ -978,6 +1017,131 @@ Engine::computeGlyphOffset(FT_Glyph glyph, bool inverseY) |
978
|
1017
|
}
|
979
|
1018
|
|
980
|
1019
|
|
|
1020
|
+QImage*
|
|
1021
|
+Engine::tryDirectRenderColorLayers(int glyphIndex,
|
|
1022
|
+ QRect* outRect)
|
|
1023
|
+{
|
|
1024
|
+ if (palette_ == NULL
|
|
1025
|
+ || !useColorLayer_
|
|
1026
|
+ || paletteIndex_ >= paletteData_.num_palettes)
|
|
1027
|
+ return NULL;
|
|
1028
|
+
|
|
1029
|
+ FT_LayerIterator iter = {};
|
|
1030
|
+
|
|
1031
|
+ FT_UInt layerGlyphIdx = 0;
|
|
1032
|
+ FT_UInt layerColorIdx = 0;
|
|
1033
|
+
|
|
1034
|
+ bool next = FT_Get_Color_Glyph_Layer(ftSize_->face,
|
|
1035
|
+ glyphIndex,
|
|
1036
|
+ &layerGlyphIdx,
|
|
1037
|
+ &layerColorIdx,
|
|
1038
|
+ &iter);
|
|
1039
|
+ if (!next)
|
|
1040
|
+ return NULL;
|
|
1041
|
+
|
|
1042
|
+ // temporarily change lf
|
|
1043
|
+ auto oldLoadFlags = imageType_.flags;
|
|
1044
|
+ auto loadFlags = oldLoadFlags;
|
|
1045
|
+ loadFlags &= ~FT_LOAD_COLOR;
|
|
1046
|
+ loadFlags |= FT_LOAD_RENDER;
|
|
1047
|
+
|
|
1048
|
+ loadFlags &= ~FT_LOAD_TARGET_(0xF);
|
|
1049
|
+ loadFlags |= FT_LOAD_TARGET_NORMAL;
|
|
1050
|
+ imageType_.flags = loadFlags;
|
|
1051
|
+
|
|
1052
|
+ FT_Bitmap bitmap = {};
|
|
1053
|
+ FT_Bitmap_Init(&bitmap);
|
|
1054
|
+
|
|
1055
|
+ FT_Vector bitmapOffset = {};
|
|
1056
|
+ bool failed = false;
|
|
1057
|
+
|
|
1058
|
+ do
|
|
1059
|
+ {
|
|
1060
|
+ FT_Vector slotOffset;
|
|
1061
|
+ FT_Glyph glyph;
|
|
1062
|
+ if (FTC_ImageCache_Lookup(imageCache_,
|
|
1063
|
+ &imageType_,
|
|
1064
|
+ layerGlyphIdx,
|
|
1065
|
+ &glyph,
|
|
1066
|
+ NULL))
|
|
1067
|
+ {
|
|
1068
|
+ // XXX Error handling
|
|
1069
|
+ failed = true;
|
|
1070
|
+ break;
|
|
1071
|
+ }
|
|
1072
|
+
|
|
1073
|
+ if (glyph->format != FT_GLYPH_FORMAT_BITMAP)
|
|
1074
|
+ continue;
|
|
1075
|
+
|
|
1076
|
+ auto bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
1077
|
+ slotOffset.x = bitmapGlyph->left << 6;
|
|
1078
|
+ slotOffset.y = bitmapGlyph->top << 6;
|
|
1079
|
+
|
|
1080
|
+ FT_Color color = {};
|
|
1081
|
+
|
|
1082
|
+ if (layerColorIdx == 0xFFFF)
|
|
1083
|
+ {
|
|
1084
|
+ // TODO: FT_Palette_Get_Foreground_Color: #1134
|
|
1085
|
+ if (paletteData_.palette_flags
|
|
1086
|
+ && (paletteData_.palette_flags[paletteIndex_]
|
|
1087
|
+ & FT_PALETTE_FOR_DARK_BACKGROUND))
|
|
1088
|
+ {
|
|
1089
|
+ /* white opaque */
|
|
1090
|
+ color.blue = 0xFF;
|
|
1091
|
+ color.green = 0xFF;
|
|
1092
|
+ color.red = 0xFF;
|
|
1093
|
+ color.alpha = 0xFF;
|
|
1094
|
+ }
|
|
1095
|
+ else
|
|
1096
|
+ {
|
|
1097
|
+ /* black opaque */
|
|
1098
|
+ color.blue = 0x00;
|
|
1099
|
+ color.green = 0x00;
|
|
1100
|
+ color.red = 0x00;
|
|
1101
|
+ color.alpha = 0xFF;
|
|
1102
|
+ }
|
|
1103
|
+ }
|
|
1104
|
+ else if (layerColorIdx < paletteData_.num_palette_entries)
|
|
1105
|
+ color = palette_[layerColorIdx];
|
|
1106
|
+ else
|
|
1107
|
+ continue;
|
|
1108
|
+
|
|
1109
|
+ if (FT_Bitmap_Blend(library_,
|
|
1110
|
+ &bitmapGlyph->bitmap, slotOffset,
|
|
1111
|
+ &bitmap, &bitmapOffset,
|
|
1112
|
+ color))
|
|
1113
|
+ {
|
|
1114
|
+ // XXX error
|
|
1115
|
+ failed = true;
|
|
1116
|
+ break;
|
|
1117
|
+ }
|
|
1118
|
+ } while (FT_Get_Color_Glyph_Layer(ftSize_->face,
|
|
1119
|
+ glyphIndex,
|
|
1120
|
+ &layerGlyphIdx,
|
|
1121
|
+ &layerColorIdx,
|
|
1122
|
+ &iter));
|
|
1123
|
+
|
|
1124
|
+ imageType_.flags = oldLoadFlags;
|
|
1125
|
+ if (failed)
|
|
1126
|
+ {
|
|
1127
|
+ FT_Bitmap_Done(library_, &bitmap);
|
|
1128
|
+ return NULL;
|
|
1129
|
+ }
|
|
1130
|
+
|
|
1131
|
+ auto img = convertBitmapToQImage(&bitmap);
|
|
1132
|
+ if (outRect)
|
|
1133
|
+ {
|
|
1134
|
+ outRect->setSize(img->size());
|
|
1135
|
+ outRect->setLeft(bitmapOffset.x >> 6);
|
|
1136
|
+ outRect->setTop(bitmapOffset.y >> 6);
|
|
1137
|
+ }
|
|
1138
|
+
|
|
1139
|
+ FT_Bitmap_Done(library_, &bitmap);
|
|
1140
|
+
|
|
1141
|
+ return img;
|
|
1142
|
+}
|
|
1143
|
+
|
|
1144
|
+
|
981
|
1145
|
QHash<FT_Glyph_Format, QString> glyphFormatNamesCache;
|
982
|
1146
|
QHash<FT_Glyph_Format, QString>&
|
983
|
1147
|
glyphFormatNames()
|
src/ftinspect/engine/engine.hpp
... |
... |
@@ -7,6 +7,7 @@ |
7
|
7
|
|
8
|
8
|
#include "fontfilemanager.hpp"
|
9
|
9
|
#include "charmap.hpp"
|
|
10
|
+#include "paletteinfo.hpp"
|
10
|
11
|
|
11
|
12
|
#include <vector>
|
12
|
13
|
#include <QString>
|
... |
... |
@@ -19,6 +20,7 @@ |
19
|
20
|
#include <freetype/ftoutln.h>
|
20
|
21
|
#include <freetype/ftcache.h>
|
21
|
22
|
#include <freetype/ftlcdfil.h>
|
|
23
|
+#include <freetype/ftcolor.h>
|
22
|
24
|
|
23
|
25
|
|
24
|
26
|
// This structure maps the (font, face, instance) index triplet to abstract
|
... |
... |
@@ -94,9 +96,22 @@ public: |
94
|
96
|
bool inverseRectY);
|
95
|
97
|
QPoint computeGlyphOffset(FT_Glyph glyph, bool inverseY);
|
96
|
98
|
|
|
99
|
+ /*
|
|
100
|
+ * Directly render the glyph at the specified index
|
|
101
|
+ * to a `QImage`. If you want to perform color-layer
|
|
102
|
+ * rendering, call this before trying to load the
|
|
103
|
+ * glyph and do normal rendering, If the returning
|
|
104
|
+ * value is non-NULL, then there's no need to
|
|
105
|
+ * load the glyph the normal way, just draw the `QImage`.
|
|
106
|
+ * Will return NULL if not enabled or color layers not available.
|
|
107
|
+ */
|
|
108
|
+ QImage* tryDirectRenderColorLayers(int glyphIndex,
|
|
109
|
+ QRect* outRect);
|
|
110
|
+
|
97
|
111
|
// reload current triplet, but with updated settings, useful for updating
|
98
|
112
|
// `ftSize_` only
|
99
|
|
- void reloadFont();
|
|
113
|
+ void reloadFont();
|
|
114
|
+ void loadPalette();
|
100
|
115
|
|
101
|
116
|
void openFonts(QStringList fontFileNames);
|
102
|
117
|
void removeFont(int fontIndex, bool closeFile = true);
|
... |
... |
@@ -132,6 +147,7 @@ public: |
132
|
147
|
FT_Vector currentFontKerning(int glyphIndex, int prevIndex);
|
133
|
148
|
|
134
|
149
|
std::vector<CharMapInfo>& currentFontCharMaps() { return curCharMaps_; }
|
|
150
|
+ std::vector<PaletteInfo>& currentFontPalettes() { return curPaletteInfos_; }
|
135
|
151
|
FontFileManager& fontFileManager() { return fontFileManager_; }
|
136
|
152
|
EngineDefaultValues& engineDefaults() { return engineDefaults_; }
|
137
|
153
|
bool antiAliasingEnabled() { return antiAliasingEnabled_; }
|
... |
... |
@@ -164,6 +180,8 @@ public: |
164
|
180
|
void setRenderMode(int mode) { renderMode_ = mode; }
|
165
|
181
|
void setAntiAliasingEnabled(bool enabled) { antiAliasingEnabled_ = enabled; }
|
166
|
182
|
void setEmbeddedBitmap(bool force) { embeddedBitmap_ = force; }
|
|
183
|
+ void setUseColorLayer(bool colorLayer) { useColorLayer_ = colorLayer; }
|
|
184
|
+ void setPaletteIndex(int index) { paletteIndex_ = index; }
|
167
|
185
|
void setLCDUsesBGR(bool isBGR) { lcdUsesBGR_ = isBGR; }
|
168
|
186
|
void setLCDSubPixelPositioning(bool sp) { lcdSubPixelPositioning_ = sp; }
|
169
|
187
|
|
... |
... |
@@ -191,6 +209,7 @@ private: |
191
|
209
|
QString curStyleName_;
|
192
|
210
|
int curNumGlyphs_ = -1;
|
193
|
211
|
std::vector<CharMapInfo> curCharMaps_;
|
|
212
|
+ std::vector<PaletteInfo> curPaletteInfos_;
|
194
|
213
|
|
195
|
214
|
FT_Library library_;
|
196
|
215
|
FTC_Manager cacheManager_;
|
... |
... |
@@ -199,8 +218,10 @@ private: |
199
|
218
|
FTC_CMapCache cmapCache_;
|
200
|
219
|
|
201
|
220
|
FTC_ScalerRec scaler_ = {};
|
202
|
|
- FT_Size ftSize_;
|
203
|
221
|
FTC_ImageTypeRec imageType_;
|
|
222
|
+ FT_Size ftSize_;
|
|
223
|
+ FT_Palette_Data paletteData_ = {};
|
|
224
|
+ FT_Color* palette_ = NULL;
|
204
|
225
|
|
205
|
226
|
EngineDefaultValues engineDefaults_;
|
206
|
227
|
|
... |
... |
@@ -219,6 +240,8 @@ private: |
219
|
240
|
bool doBlueZoneHinting_;
|
220
|
241
|
bool showSegments_;
|
221
|
242
|
bool embeddedBitmap_;
|
|
243
|
+ bool useColorLayer_;
|
|
244
|
+ int paletteIndex_ = -1;
|
222
|
245
|
int antiAliasingTarget_;
|
223
|
246
|
bool lcdUsesBGR_;
|
224
|
247
|
bool lcdSubPixelPositioning_;
|
... |
... |
@@ -228,6 +251,7 @@ private: |
228
|
251
|
unsigned long loadFlags_;
|
229
|
252
|
|
230
|
253
|
void queryEngine();
|
|
254
|
+ void loadPaletteInfos();
|
231
|
255
|
|
232
|
256
|
public:
|
233
|
257
|
|
src/ftinspect/engine/paletteinfo.cpp
|
1
|
+// paletteinfo.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "paletteinfo.hpp"
|
|
6
|
+
|
|
7
|
+PaletteInfo::PaletteInfo(FT_Face face,
|
|
8
|
+ FT_Palette_Data& data,
|
|
9
|
+ int index)
|
|
10
|
+: index(index)
|
|
11
|
+{
|
|
12
|
+ if (data.palette_name_ids)
|
|
13
|
+ {
|
|
14
|
+ auto id = data.palette_name_ids[index];
|
|
15
|
+ FT_SfntName sname;
|
|
16
|
+ FT_Get_Sfnt_Name(face, id, &sname);
|
|
17
|
+ name = "(SFNT no impl)";
|
|
18
|
+ // TODO: Get SFNT Name: After implemented SFNT names functionality.
|
|
19
|
+ }
|
|
20
|
+ else
|
|
21
|
+ {
|
|
22
|
+ name = "(unnamed)";
|
|
23
|
+ }
|
|
24
|
+}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+// end of paletteinfo.cpp |
src/ftinspect/engine/paletteinfo.hpp
|
1
|
+// paletteinfo.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include <QString>
|
|
8
|
+
|
|
9
|
+#include <freetype/freetype.h>
|
|
10
|
+#include <freetype/ftcolor.h>
|
|
11
|
+#include <freetype/ftsnames.h>
|
|
12
|
+
|
|
13
|
+struct PaletteInfo
|
|
14
|
+{
|
|
15
|
+ int index;
|
|
16
|
+ QString name;
|
|
17
|
+
|
|
18
|
+ PaletteInfo(FT_Face face, FT_Palette_Data& data, int index);
|
|
19
|
+};
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+// end of paletteinfo.hpp |
src/ftinspect/engine/stringrenderer.cpp
... |
... |
@@ -150,6 +150,7 @@ void |
150
|
150
|
StringRenderer::prepareRendering()
|
151
|
151
|
{
|
152
|
152
|
engine_->reloadFont();
|
|
153
|
+ engine_->loadPalette();
|
153
|
154
|
if (kerningDegree_ != KD_None)
|
154
|
155
|
trackingKerning_ = engine_->currentFontTrackingKerning(kerningDegree_);
|
155
|
156
|
else
|
... |
... |
@@ -499,70 +500,83 @@ StringRenderer::renderLine(int x, |
499
|
500
|
if (!ctx.glyph)
|
500
|
501
|
continue;
|
501
|
502
|
|
502
|
|
- // copy the glyph because we're doing manipulation
|
503
|
|
- auto error = FT_Glyph_Copy(ctx.glyph, &image);
|
504
|
|
- if (error)
|
505
|
|
- continue;
|
|
503
|
+ advance = vertical_ ? ctx.vadvance : ctx.hadvance;
|
506
|
504
|
|
507
|
|
- glyphPreprocessCallback_(&image);
|
|
505
|
+ QRect rect;
|
|
506
|
+ QImage* colorLayerImage
|
|
507
|
+ = engine_->tryDirectRenderColorLayers(ctx.glyphIndex, &rect);
|
508
|
508
|
|
509
|
|
- if (image->format != FT_GLYPH_FORMAT_BITMAP)
|
|
509
|
+ if (colorLayerImage)
|
510
|
510
|
{
|
511
|
|
- if (vertical_)
|
512
|
|
- error = FT_Glyph_Transform(image, NULL, &ctx.vvector);
|
513
|
|
-
|
514
|
|
- if (!error)
|
515
|
|
- {
|
516
|
|
- if (matrixEnabled_)
|
517
|
|
- error = FT_Glyph_Transform(image, &matrix_, &pen);
|
518
|
|
- else
|
519
|
|
- error = FT_Glyph_Transform(image, NULL, &pen);
|
520
|
|
- }
|
521
|
|
-
|
522
|
|
- if (error)
|
523
|
|
- {
|
524
|
|
- FT_Done_Glyph(image);
|
525
|
|
- continue;
|
526
|
|
- }
|
|
511
|
+ rect.setX(rect.x() + (pen.x >> 6));
|
|
512
|
+ rect.setY(height - rect.y() - (pen.y >> 6));
|
|
513
|
+ renderImageCallback_(colorLayerImage, rect);
|
527
|
514
|
}
|
528
|
515
|
else
|
529
|
516
|
{
|
530
|
|
- auto bitmap = reinterpret_cast<FT_BitmapGlyph>(image);
|
|
517
|
+ // copy the glyph because we're doing manipulation
|
|
518
|
+ auto error = FT_Glyph_Copy(ctx.glyph, &image);
|
|
519
|
+ if (error)
|
|
520
|
+ continue;
|
|
521
|
+
|
|
522
|
+ glyphPreprocessCallback_(&image);
|
531
|
523
|
|
532
|
|
- if (vertical_)
|
|
524
|
+ if (image->format != FT_GLYPH_FORMAT_BITMAP)
|
533
|
525
|
{
|
534
|
|
- bitmap->left += (ctx.vvector.x + pen.x) >> 6;
|
535
|
|
- bitmap->top += (ctx.vvector.y + pen.y) >> 6;
|
|
526
|
+ if (vertical_)
|
|
527
|
+ error = FT_Glyph_Transform(image, NULL, &ctx.vvector);
|
|
528
|
+
|
|
529
|
+ if (!error)
|
|
530
|
+ {
|
|
531
|
+ if (matrixEnabled_)
|
|
532
|
+ error = FT_Glyph_Transform(image, &matrix_, &pen);
|
|
533
|
+ else
|
|
534
|
+ error = FT_Glyph_Transform(image, NULL, &pen);
|
|
535
|
+ }
|
|
536
|
+
|
|
537
|
+ if (error)
|
|
538
|
+ {
|
|
539
|
+ FT_Done_Glyph(image);
|
|
540
|
+ continue;
|
|
541
|
+ }
|
536
|
542
|
}
|
537
|
543
|
else
|
538
|
544
|
{
|
539
|
|
- bitmap->left += pen.x >> 6;
|
540
|
|
- bitmap->top += pen.y >> 6;
|
|
545
|
+ auto bitmap = reinterpret_cast<FT_BitmapGlyph>(image);
|
|
546
|
+
|
|
547
|
+ if (vertical_)
|
|
548
|
+ {
|
|
549
|
+ bitmap->left += (ctx.vvector.x + pen.x) >> 6;
|
|
550
|
+ bitmap->top += (ctx.vvector.y + pen.y) >> 6;
|
|
551
|
+ }
|
|
552
|
+ else
|
|
553
|
+ {
|
|
554
|
+ bitmap->left += pen.x >> 6;
|
|
555
|
+ bitmap->top += pen.y >> 6;
|
|
556
|
+ }
|
541
|
557
|
}
|
542
|
|
- }
|
543
|
558
|
|
544
|
|
- advance = vertical_ ? ctx.vadvance : ctx.hadvance;
|
|
559
|
+ if (matrixEnabled_)
|
|
560
|
+ FT_Vector_Transform(&advance, &matrix_);
|
545
|
561
|
|
546
|
|
- if (matrixEnabled_)
|
547
|
|
- FT_Vector_Transform(&advance, &matrix_);
|
|
562
|
+ FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_PIXELS, &bbox);
|
548
|
563
|
|
549
|
|
- FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_PIXELS, &bbox);
|
|
564
|
+ // check bounding box; if it is completely outside the
|
|
565
|
+ // display surface, we don't need to render it
|
|
566
|
+ if (bbox.xMax >= 0
|
|
567
|
+ && bbox.yMax >= 0
|
|
568
|
+ && bbox.xMin <= width
|
|
569
|
+ && bbox.yMin <= height)
|
|
570
|
+ {
|
|
571
|
+ FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
|
|
572
|
+ renderCallback_(image, penPos);
|
|
573
|
+ }
|
550
|
574
|
|
551
|
|
- // check bounding box; if it is completely outside the
|
552
|
|
- // display surface, we don't need to render it
|
553
|
|
- if (bbox.xMax >= 0
|
554
|
|
- && bbox.yMax >= 0
|
555
|
|
- && bbox.xMin <= width
|
556
|
|
- && bbox.yMin <= height)
|
557
|
|
- {
|
558
|
|
- FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
|
559
|
|
- renderCallback_(image, penPos);
|
|
575
|
+ FT_Done_Glyph(image);
|
560
|
576
|
}
|
561
|
|
-
|
|
577
|
+
|
562
|
578
|
pen.x += advance.x;
|
563
|
579
|
pen.y += advance.y;
|
564
|
|
-
|
565
|
|
- FT_Done_Glyph(image);
|
566
|
580
|
}
|
567
|
581
|
|
568
|
582
|
return offset + totalCount;
|
src/ftinspect/engine/stringrenderer.hpp
... |
... |
@@ -62,6 +62,12 @@ public: |
62
|
62
|
* position.
|
63
|
63
|
*/
|
64
|
64
|
using RenderCallback = std::function<void(FT_Glyph, FT_Vector)>;
|
|
65
|
+ /*
|
|
66
|
+ * For color layered fonts, this will direct render the QImage for you.
|
|
67
|
+ * TODO: Remove `RenderCallback` and do QImage creation in this class?
|
|
68
|
+ * The receiver is responsible for deleteing the QImage.
|
|
69
|
+ */
|
|
70
|
+ using RenderImageCallback = std::function<void(QImage*, QRect)>;
|
65
|
71
|
/*
|
66
|
72
|
* The glyph pointer may be replaced. In that case, ownership is transfered
|
67
|
73
|
* to the renderer, and the new glyph will be eventually freed by
|
... |
... |
@@ -90,6 +96,11 @@ public: |
90
|
96
|
renderCallback_ = std::move(cb);
|
91
|
97
|
}
|
92
|
98
|
void
|
|
99
|
+ setImageCallback(RenderImageCallback cb)
|
|
100
|
+ {
|
|
101
|
+ renderImageCallback_ = std::move(cb);
|
|
102
|
+ }
|
|
103
|
+ void
|
93
|
104
|
setPreprocessCallback(PreprocessCallback cb)
|
94
|
105
|
{
|
95
|
106
|
glyphPreprocessCallback_ = std::move(cb);
|
... |
... |
@@ -173,6 +184,7 @@ private: |
173
|
184
|
bool matrixEnabled_ = false;
|
174
|
185
|
|
175
|
186
|
RenderCallback renderCallback_;
|
|
187
|
+ RenderImageCallback renderImageCallback_;
|
176
|
188
|
PreprocessCallback glyphPreprocessCallback_;
|
177
|
189
|
LineBeginCallback lineBeginCallback_;
|
178
|
190
|
|
src/ftinspect/maingui.cpp
... |
... |
@@ -148,7 +148,7 @@ void |
148
|
148
|
MainGUI::onTripletChanged()
|
149
|
149
|
{
|
150
|
150
|
auto state = settingPanel_->blockSignals(true);
|
151
|
|
- settingPanel_->checkHinting();
|
|
151
|
+ settingPanel_->onFontChanged();
|
152
|
152
|
settingPanel_->blockSignals(state);
|
153
|
153
|
reloadCurrentTabFont();
|
154
|
154
|
}
|
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/paletteinfo.cpp',
|
26
|
27
|
'engine/stringrenderer.cpp',
|
27
|
28
|
|
28
|
29
|
'rendering/glyphbitmap.cpp',
|
src/ftinspect/models/customcomboboxmodels.cpp
... |
... |
@@ -270,8 +270,10 @@ QVariant |
270
|
270
|
AntiAliasingComboBoxModel::data(const QModelIndex& index,
|
271
|
271
|
int role) const
|
272
|
272
|
{
|
|
273
|
+ auto row = index.row();
|
273
|
274
|
if (role == Qt::ForegroundRole)
|
274
|
|
- if (index.row() == AntiAliasing_Light && !lightAntiAliasingEnabled_)
|
|
275
|
+ if ((row == AntiAliasing_Light || row == AntiAliasing_Light_SubPixel)
|
|
276
|
+ && !lightAntiAliasingEnabled_)
|
275
|
277
|
return QApplication::palette().color(QPalette::Disabled,
|
276
|
278
|
QPalette::Text);
|
277
|
279
|
return SimpleComboBoxModelImpl::data(index, role);
|
... |
... |
@@ -281,7 +283,9 @@ AntiAliasingComboBoxModel::data(const QModelIndex& index, |
281
|
283
|
Qt::ItemFlags
|
282
|
284
|
AntiAliasingComboBoxModel::flags(const QModelIndex& index) const
|
283
|
285
|
{
|
284
|
|
- if (index.row() == AntiAliasing_Light && !lightAntiAliasingEnabled_)
|
|
286
|
+ auto row = index.row();
|
|
287
|
+ if ((row == AntiAliasing_Light || row == AntiAliasing_Light_SubPixel)
|
|
288
|
+ && !lightAntiAliasingEnabled_)
|
285
|
289
|
return Qt::ItemFlags {};
|
286
|
290
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
287
|
291
|
}
|
src/ftinspect/panels/continuous.cpp
... |
... |
@@ -230,6 +230,7 @@ ContinuousTab::charMapChanged() |
230
|
230
|
}
|
231
|
231
|
updateLimitIndex();
|
232
|
232
|
|
|
233
|
+ syncSettings();
|
233
|
234
|
canvas_->stringRenderer().reloadAll();
|
234
|
235
|
repaintGlyph();
|
235
|
236
|
lastCharMapIndex_ = newIndex;
|
src/ftinspect/panels/settingpanel.cpp
... |
... |
@@ -25,14 +25,14 @@ SettingPanel::antiAliasingModeIndex() |
25
|
25
|
void
|
26
|
26
|
SettingPanel::checkAllSettings()
|
27
|
27
|
{
|
28
|
|
- checkHinting();
|
|
28
|
+ onFontChanged();
|
29
|
29
|
checkAutoHinting();
|
30
|
30
|
checkAntiAliasing();
|
31
|
31
|
}
|
32
|
32
|
|
33
|
33
|
|
34
|
34
|
void
|
35
|
|
-SettingPanel::checkHinting()
|
|
35
|
+SettingPanel::onFontChanged()
|
36
|
36
|
{
|
37
|
37
|
if (hintingCheckBox_->isChecked())
|
38
|
38
|
{
|
... |
... |
@@ -76,6 +76,48 @@ SettingPanel::checkHinting() |
76
|
76
|
|
77
|
77
|
emit repaintNeeded();
|
78
|
78
|
}
|
|
79
|
+
|
|
80
|
+ populatePalettes();
|
|
81
|
+}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+void
|
|
85
|
+SettingPanel::populatePalettes()
|
|
86
|
+{
|
|
87
|
+ auto needToReload = false;
|
|
88
|
+ auto& newPalettes = engine_->currentFontPalettes();
|
|
89
|
+ if (newPalettes.size() != paletteComboBox_->count())
|
|
90
|
+ needToReload = true;
|
|
91
|
+ else
|
|
92
|
+ for (int i = 0; i < newPalettes.size(); ++i)
|
|
93
|
+ {
|
|
94
|
+ auto oldNameVariant = paletteComboBox_->itemData(i);
|
|
95
|
+ if (!oldNameVariant.canConvert<QString>())
|
|
96
|
+ {
|
|
97
|
+ needToReload = true;
|
|
98
|
+ break;
|
|
99
|
+ }
|
|
100
|
+ if (oldNameVariant.toString() != newPalettes[i].name)
|
|
101
|
+ {
|
|
102
|
+ needToReload = true;
|
|
103
|
+ break;
|
|
104
|
+ }
|
|
105
|
+ }
|
|
106
|
+ if (!needToReload)
|
|
107
|
+ return;
|
|
108
|
+
|
|
109
|
+ {
|
|
110
|
+ QSignalBlocker blocker(paletteComboBox_);
|
|
111
|
+ paletteComboBox_->clear();
|
|
112
|
+ for (int i = 0; i < newPalettes.size(); ++i)
|
|
113
|
+ paletteComboBox_->addItem(
|
|
114
|
+ QString("%1: %2")
|
|
115
|
+ .arg(i)
|
|
116
|
+ .arg(newPalettes[i].name),
|
|
117
|
+ newPalettes[i].name);
|
|
118
|
+ }
|
|
119
|
+
|
|
120
|
+ emit fontReloadNeeded();
|
79
|
121
|
}
|
80
|
122
|
|
81
|
123
|
|
... |
... |
@@ -166,6 +208,14 @@ SettingPanel::checkAntiAliasing() |
166
|
208
|
}
|
167
|
209
|
|
168
|
210
|
|
|
211
|
+void
|
|
212
|
+SettingPanel::checkPalette()
|
|
213
|
+{
|
|
214
|
+ paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked());
|
|
215
|
+ emit repaintNeeded();
|
|
216
|
+}
|
|
217
|
+
|
|
218
|
+
|
169
|
219
|
void
|
170
|
220
|
SettingPanel::syncSettings()
|
171
|
221
|
{
|
... |
... |
@@ -190,6 +240,9 @@ SettingPanel::syncSettings() |
190
|
240
|
engine_->setGamma(gammaSlider_->value());
|
191
|
241
|
|
192
|
242
|
engine_->setEmbeddedBitmap(embeddedBitmapCheckBox_->isChecked());
|
|
243
|
+ engine_->setPaletteIndex(paletteComboBox_->currentIndex());
|
|
244
|
+
|
|
245
|
+ engine_->setUseColorLayer(colorLayerCheckBox_->isChecked());
|
193
|
246
|
engine_->setLCDUsesBGR(aaSettings.isBGR);
|
194
|
247
|
engine_->setLCDSubPixelPositioning(
|
195
|
248
|
antiAliasingComboBox_->currentIndex()
|
... |
... |
@@ -210,12 +263,15 @@ SettingPanel::createConnections() |
210
|
263
|
connect(lcdFilterComboBox_,
|
211
|
264
|
QOverload<int>::of(&QComboBox::currentIndexChanged),
|
212
|
265
|
this, &SettingPanel::repaintNeeded);
|
|
266
|
+ connect(paletteComboBox_,
|
|
267
|
+ QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
|
268
|
+ &SettingPanel::repaintNeeded);
|
213
|
269
|
|
214
|
270
|
connect(gammaSlider_, &QSlider::valueChanged,
|
215
|
271
|
this, &SettingPanel::repaintNeeded);
|
216
|
272
|
|
217
|
273
|
connect(hintingCheckBox_, &QCheckBox::clicked,
|
218
|
|
- this, &SettingPanel::checkHinting);
|
|
274
|
+ this, &SettingPanel::onFontChanged);
|
219
|
275
|
|
220
|
276
|
connect(horizontalHintingCheckBox_, &QCheckBox::clicked,
|
221
|
277
|
this, &SettingPanel::repaintNeeded);
|
... |
... |
@@ -230,6 +286,8 @@ SettingPanel::createConnections() |
230
|
286
|
this, &SettingPanel::checkAutoHinting);
|
231
|
287
|
connect(embeddedBitmapCheckBox_, &QCheckBox::clicked,
|
232
|
288
|
this, &SettingPanel::fontReloadNeeded);
|
|
289
|
+ connect(colorLayerCheckBox_, &QCheckBox::clicked,
|
|
290
|
+ this, &SettingPanel::checkPalette);
|
233
|
291
|
}
|
234
|
292
|
|
235
|
293
|
|
... |
... |
@@ -252,6 +310,7 @@ SettingPanel::createLayout() |
252
|
310
|
blueZoneHintingCheckBox_ = new QCheckBox(tr("Blue-Zone Hinting"), this);
|
253
|
311
|
segmentDrawingCheckBox_ = new QCheckBox(tr("Segment Drawing"), this);
|
254
|
312
|
embeddedBitmapCheckBox_ = new QCheckBox(tr("Enable Embedded Bitmap"), this);
|
|
313
|
+ colorLayerCheckBox_ = new QCheckBox(tr("Enable Color Layer"), this);
|
255
|
314
|
|
256
|
315
|
antiAliasingLabel_ = new QLabel(tr("Anti-Aliasing"), this);
|
257
|
316
|
antiAliasingLabel_->setAlignment(Qt::AlignRight);
|
... |
... |
@@ -261,7 +320,7 @@ SettingPanel::createLayout() |
261
|
320
|
antiAliasingComboBox_->setModel(antiAliasingComboBoxModel_);
|
262
|
321
|
antiAliasingLabel_->setBuddy(antiAliasingComboBox_);
|
263
|
322
|
|
264
|
|
- lcdFilterLabel_ = new QLabel(tr("LCD Filter"));
|
|
323
|
+ lcdFilterLabel_ = new QLabel(tr("LCD Filter"), this);
|
265
|
324
|
lcdFilterLabel_->setAlignment(Qt::AlignRight);
|
266
|
325
|
|
267
|
326
|
lcdFilterComboboxModel_ = new LCDFilterComboBoxModel(this);
|
... |
... |
@@ -269,6 +328,11 @@ SettingPanel::createLayout() |
269
|
328
|
lcdFilterComboBox_->setModel(lcdFilterComboboxModel_);
|
270
|
329
|
lcdFilterLabel_->setBuddy(lcdFilterComboBox_);
|
271
|
330
|
|
|
331
|
+ paletteLabel_ = new QLabel(tr("Palette: "), this);
|
|
332
|
+
|
|
333
|
+ paletteComboBox_ = new QComboBox(this);
|
|
334
|
+ paletteLabel_->setBuddy(paletteComboBox_);
|
|
335
|
+
|
272
|
336
|
int width;
|
273
|
337
|
// make all labels have the same width
|
274
|
338
|
width = hintingModeLabel_->minimumSizeHint().width();
|
... |
... |
@@ -327,6 +391,10 @@ SettingPanel::createLayout() |
327
|
391
|
gammaLayout_->addWidget(gammaLabel_);
|
328
|
392
|
gammaLayout_->addWidget(gammaSlider_);
|
329
|
393
|
|
|
394
|
+ paletteLayout_ = new QHBoxLayout;
|
|
395
|
+ paletteLayout_->addWidget(paletteLabel_);
|
|
396
|
+ paletteLayout_->addWidget(paletteComboBox_);
|
|
397
|
+
|
330
|
398
|
generalTabLayout_ = new QVBoxLayout;
|
331
|
399
|
generalTabLayout_->addWidget(hintingCheckBox_);
|
332
|
400
|
generalTabLayout_->addLayout(hintingModeLayout_);
|
... |
... |
@@ -343,6 +411,8 @@ SettingPanel::createLayout() |
343
|
411
|
generalTabLayout_->addStretch(1);
|
344
|
412
|
generalTabLayout_->addLayout(gammaLayout_);
|
345
|
413
|
generalTabLayout_->addWidget(embeddedBitmapCheckBox_);
|
|
414
|
+ generalTabLayout_->addWidget(colorLayerCheckBox_);
|
|
415
|
+ generalTabLayout_->addLayout(paletteLayout_);
|
346
|
416
|
generalTabLayout_->addSpacing(20); // XXX px
|
347
|
417
|
generalTabLayout_->addStretch(1);
|
348
|
418
|
|
... |
... |
@@ -393,6 +463,7 @@ SettingPanel::setDefaults() |
393
|
463
|
verticalHintingCheckBox_->setChecked(true);
|
394
|
464
|
blueZoneHintingCheckBox_->setChecked(true);
|
395
|
465
|
embeddedBitmapCheckBox_->setChecked(false);
|
|
466
|
+ colorLayerCheckBox_->setChecked(true);
|
396
|
467
|
|
397
|
468
|
gammaSlider_->setValue(18); // 1.8
|
398
|
469
|
}
|
src/ftinspect/panels/settingpanel.hpp
... |
... |
@@ -36,10 +36,11 @@ signals: |
36
|
36
|
|
37
|
37
|
public slots:
|
38
|
38
|
void checkAllSettings();
|
39
|
|
- void checkHinting();
|
|
39
|
+ void onFontChanged();
|
40
|
40
|
void checkHintingMode();
|
41
|
41
|
void checkAutoHinting();
|
42
|
42
|
void checkAntiAliasing();
|
|
43
|
+ void checkPalette();
|
43
|
44
|
|
44
|
45
|
private:
|
45
|
46
|
Engine* engine_;
|
... |
... |
@@ -56,6 +57,7 @@ private: |
56
|
57
|
QLabel* antiAliasingLabel_;
|
57
|
58
|
QLabel* hintingModeLabel_;
|
58
|
59
|
QLabel* lcdFilterLabel_;
|
|
60
|
+ QLabel* paletteLabel_;
|
59
|
61
|
|
60
|
62
|
QCheckBox* hintingCheckBox_;
|
61
|
63
|
QCheckBox* horizontalHintingCheckBox_;
|
... |
... |
@@ -64,6 +66,7 @@ private: |
64
|
66
|
QCheckBox* segmentDrawingCheckBox_;
|
65
|
67
|
QCheckBox* autoHintingCheckBox_;
|
66
|
68
|
QCheckBox* embeddedBitmapCheckBox_;
|
|
69
|
+ QCheckBox* colorLayerCheckBox_;
|
67
|
70
|
|
68
|
71
|
AntiAliasingComboBoxModel* antiAliasingComboBoxModel_;
|
69
|
72
|
HintingModeComboBoxModel* hintingModeComboBoxModel_;
|
... |
... |
@@ -72,6 +75,7 @@ private: |
72
|
75
|
QComboBox* hintingModeComboBox_;
|
73
|
76
|
QComboBox* antiAliasingComboBox_;
|
74
|
77
|
QComboBox* lcdFilterComboBox_;
|
|
78
|
+ QComboBox* paletteComboBox_;
|
75
|
79
|
|
76
|
80
|
QSlider* gammaSlider_;
|
77
|
81
|
|
... |
... |
@@ -84,6 +88,7 @@ private: |
84
|
88
|
QHBoxLayout* antiAliasingLayout_;
|
85
|
89
|
QHBoxLayout* lcdFilterLayout_;
|
86
|
90
|
QHBoxLayout* gammaLayout_;
|
|
91
|
+ QHBoxLayout* paletteLayout_;
|
87
|
92
|
|
88
|
93
|
QVBoxLayout* generalTabLayout_;
|
89
|
94
|
|
... |
... |
@@ -92,6 +97,8 @@ private: |
92
|
97
|
void createConnections();
|
93
|
98
|
void createLayout();
|
94
|
99
|
void setDefaults();
|
|
100
|
+
|
|
101
|
+ void populatePalettes();
|
95
|
102
|
};
|
96
|
103
|
|
97
|
104
|
|
src/ftinspect/panels/singular.cpp
... |
... |
@@ -94,7 +94,8 @@ SingularTab::drawGlyph() |
94
|
94
|
if (showBitmapCheckBox_->isChecked())
|
95
|
95
|
{
|
96
|
96
|
currentGlyphBitmapItem_
|
97
|
|
- = new GlyphBitmap(glyph,
|
|
97
|
+ = new GlyphBitmap(currentGlyphIndex_,
|
|
98
|
+ glyph,
|
98
|
99
|
engine_);
|
99
|
100
|
glyphScene_->addItem(currentGlyphBitmapItem_);
|
100
|
101
|
}
|
src/ftinspect/rendering/glyphbitmap.cpp
... |
... |
@@ -14,12 +14,21 @@ |
14
|
14
|
#include <freetype/ftbitmap.h>
|
15
|
15
|
|
16
|
16
|
|
17
|
|
-GlyphBitmap::GlyphBitmap(FT_Glyph glyph,
|
|
17
|
+GlyphBitmap::GlyphBitmap(int glyphIndex,
|
|
18
|
+ FT_Glyph glyph,
|
18
|
19
|
Engine* engine)
|
19
|
20
|
{
|
20
|
21
|
QRect bRect;
|
|
22
|
+ image_ = engine->tryDirectRenderColorLayers(glyphIndex, &bRect);
|
|
23
|
+ if (image_)
|
|
24
|
+ {
|
|
25
|
+ bRect.setTop(-bRect.top());
|
|
26
|
+ boundingRect_ = bRect; // QRect to QRectF
|
|
27
|
+ return;
|
|
28
|
+ }
|
|
29
|
+
|
21
|
30
|
image_ = engine->convertGlyphToQImage(glyph, &bRect, true);
|
22
|
|
- boundingRect_ = bRect; // QRectF to QRect
|
|
31
|
+ boundingRect_ = bRect; // QRect to QRectF
|
23
|
32
|
}
|
24
|
33
|
|
25
|
34
|
|
src/ftinspect/rendering/glyphbitmap.hpp
... |
... |
@@ -20,7 +20,8 @@ class GlyphBitmap |
20
|
20
|
: public QGraphicsItem
|
21
|
21
|
{
|
22
|
22
|
public:
|
23
|
|
- GlyphBitmap(FT_Glyph glyph,
|
|
23
|
+ GlyphBitmap(int glyphIndex,
|
|
24
|
+ FT_Glyph glyph,
|
24
|
25
|
Engine* engine);
|
25
|
26
|
~GlyphBitmap() override;
|
26
|
27
|
QRectF boundingRect() const override;
|
src/ftinspect/rendering/glyphcontinuous.cpp
... |
... |
@@ -101,6 +101,11 @@ GlyphContinuous::paintByRenderer(QPainter* painter) |
101
|
101
|
{
|
102
|
102
|
drawSingleGlyph(painter, glyph, penPos);
|
103
|
103
|
});
|
|
104
|
+ stringRenderer_.setImageCallback(
|
|
105
|
+ [&](QImage* image, QRect pos)
|
|
106
|
+ {
|
|
107
|
+ drawSingleGlyphImage(painter, image, pos);
|
|
108
|
+ });
|
104
|
109
|
stringRenderer_.setPreprocessCallback(
|
105
|
110
|
[&](FT_Glyph* ptr)
|
106
|
111
|
{
|
... |
... |
@@ -285,4 +290,17 @@ GlyphContinuous::drawSingleGlyph(QPainter* painter, |
285
|
290
|
}
|
286
|
291
|
|
287
|
292
|
|
|
293
|
+void
|
|
294
|
+GlyphContinuous::drawSingleGlyphImage(QPainter* painter,
|
|
295
|
+ QImage* image,
|
|
296
|
+ QRect pos)
|
|
297
|
+{
|
|
298
|
+ // TODO red square?
|
|
299
|
+
|
|
300
|
+ pos.setLeft(pos.left() + sizeIndicatorOffset_);
|
|
301
|
+ painter->drawImage(pos, *image);
|
|
302
|
+ delete image;
|
|
303
|
+}
|
|
304
|
+
|
|
305
|
+
|
288
|
306
|
// end of glyphcontinuous.cpp |
src/ftinspect/rendering/glyphcontinuous.hpp
... |
... |
@@ -103,6 +103,9 @@ private: |
103
|
103
|
void drawSingleGlyph(QPainter* painter,
|
104
|
104
|
FT_Glyph glyph,
|
105
|
105
|
FT_Vector penPos);
|
|
106
|
+ void drawSingleGlyphImage(QPainter* painter,
|
|
107
|
+ QImage* image,
|
|
108
|
+ QRect pos);
|
106
|
109
|
};
|
107
|
110
|
|
108
|
111
|
|
|