Charlie Jiang pushed to branch gsoc-2022-chariri-3 at FreeType / FreeType Demo Programs
Commits:
-
e9fcd920
by Charlie Jiang at 2022-08-24T00:00:06+08:00
11 changed files:
- src/ftinspect/CMakeLists.txt
- src/ftinspect/engine/engine.cpp
- src/ftinspect/engine/engine.hpp
- + src/ftinspect/engine/rendering.cpp
- + src/ftinspect/engine/rendering.hpp
- src/ftinspect/engine/stringrenderer.cpp
- src/ftinspect/glyphcomponents/glyphbitmap.cpp
- src/ftinspect/glyphcomponents/glyphcontinuous.cpp
- src/ftinspect/meson.build
- src/ftinspect/panels/settingpanel.cpp
- src/ftinspect/panels/singular.cpp
Changes:
... | ... | @@ -28,6 +28,7 @@ add_executable(ftinspect |
28 | 28 | "engine/fontinfo.cpp"
|
29 | 29 | "engine/fontinfonamesmapping.cpp"
|
30 | 30 | "engine/mmgx.cpp"
|
31 | + "engine/rendering.cpp"
|
|
31 | 32 | "engine/renderutils.cpp"
|
32 | 33 | |
33 | 34 | "glyphcomponents/glyphbitmap.cpp"
|
... | ... | @@ -163,9 +163,7 @@ Engine::Engine() |
163 | 163 | }
|
164 | 164 |
|
165 | 165 | queryEngine();
|
166 | - |
|
167 | - setForeground(QColor(Qt::black).rgba());
|
|
168 | - setBackground(QColor(Qt::white).rgba());
|
|
166 | + renderingEngine_ = std::make_unique<RenderingEngine>(this);
|
|
169 | 167 | }
|
170 | 168 | |
171 | 169 | |
... | ... | @@ -649,57 +647,6 @@ Engine::loadGlyphWithoutUpdate(int glyphIndex, |
649 | 647 | }
|
650 | 648 | |
651 | 649 | |
652 | -bool
|
|
653 | -Engine::convertGlyphToBitmapGlyph(FT_Glyph src,
|
|
654 | - FT_Glyph* out)
|
|
655 | -{
|
|
656 | - if (src->format == FT_GLYPH_FORMAT_BITMAP)
|
|
657 | - {
|
|
658 | - *out = src;
|
|
659 | - return false;
|
|
660 | - }
|
|
661 | - if (src->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
662 | - {
|
|
663 | - *out = NULL;
|
|
664 | - return false;
|
|
665 | - // TODO support SVG
|
|
666 | - }
|
|
667 | - |
|
668 | - if (src->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
669 | - {
|
|
670 | - FT_Glyph out2 = src;
|
|
671 | - auto error = FT_Glyph_To_Bitmap(&out2,
|
|
672 | - static_cast<FT_Render_Mode>(renderMode_),
|
|
673 | - nullptr,
|
|
674 | - false);
|
|
675 | - if (error)
|
|
676 | - {
|
|
677 | - *out = NULL;
|
|
678 | - return false;
|
|
679 | - }
|
|
680 | - *out = out2;
|
|
681 | - return true;
|
|
682 | - }
|
|
683 | - |
|
684 | - *out = NULL;
|
|
685 | - return false;
|
|
686 | -}
|
|
687 | - |
|
688 | - |
|
689 | -FT_Bitmap
|
|
690 | -Engine::convertBitmapTo8Bpp(FT_Bitmap* bitmap)
|
|
691 | -{
|
|
692 | - FT_Bitmap out;
|
|
693 | - out.buffer = NULL;
|
|
694 | - auto error = FT_Bitmap_Convert(library_, bitmap, &out, 1);
|
|
695 | - if (error)
|
|
696 | - {
|
|
697 | - // XXX handling?
|
|
698 | - }
|
|
699 | - return out;
|
|
700 | -}
|
|
701 | - |
|
702 | - |
|
703 | 650 | bool
|
704 | 651 | Engine::renderReady()
|
705 | 652 | {
|
... | ... | @@ -751,7 +698,7 @@ Engine::setGamma(double gamma) |
751 | 698 | if (gamma_ != gamma)
|
752 | 699 | {
|
753 | 700 | gamma_ = gamma;
|
754 | - calculateForegroundTable();
|
|
701 | + renderingEngine_->calculateForegroundTable();
|
|
755 | 702 | }
|
756 | 703 | }
|
757 | 704 | |
... | ... | @@ -824,28 +771,6 @@ Engine::applyMMGXDesignCoords(FT_Fixed* coords, |
824 | 771 | }
|
825 | 772 | |
826 | 773 | |
827 | -void
|
|
828 | -Engine::setForeground(QRgb foreground)
|
|
829 | -{
|
|
830 | - if (foregroundTable_.size() != 256 || foreground != foregroundColor_)
|
|
831 | - {
|
|
832 | - foregroundColor_ = foreground;
|
|
833 | - calculateForegroundTable();
|
|
834 | - }
|
|
835 | -}
|
|
836 | - |
|
837 | - |
|
838 | -void
|
|
839 | -Engine::setBackground(QRgb background)
|
|
840 | -{
|
|
841 | - if (foregroundTable_.size() != 256 || background != backgroundColor_)
|
|
842 | - {
|
|
843 | - backgroundColor_ = background;
|
|
844 | - calculateForegroundTable();
|
|
845 | - }
|
|
846 | -}
|
|
847 | - |
|
848 | - |
|
849 | 774 | void
|
850 | 775 | Engine::update()
|
851 | 776 | {
|
... | ... | @@ -1023,400 +948,6 @@ Engine::loadPaletteInfos() |
1023 | 948 | }
|
1024 | 949 | |
1025 | 950 | |
1026 | -void
|
|
1027 | -Engine::calculateForegroundTable()
|
|
1028 | -{
|
|
1029 | - foregroundTable_.resize(256);
|
|
1030 | - |
|
1031 | - // Yes I know this is horribly slow, but we're only calculating the table once
|
|
1032 | - // and can use it for all rendering if the color and gamma isn't changing.
|
|
1033 | - |
|
1034 | - double br = std::pow(qRed(backgroundColor_) / 255.0, gamma_);
|
|
1035 | - double bg = std::pow(qGreen(backgroundColor_) / 255.0, gamma_);
|
|
1036 | - double bb = std::pow(qBlue(backgroundColor_) / 255.0, gamma_);
|
|
1037 | - double invGamma = 1 / gamma_;
|
|
1038 | - |
|
1039 | - for (int i = 0; i <= 0xFF; i++)
|
|
1040 | - {
|
|
1041 | - double foreAlpha = i * qAlpha(foregroundColor_) / 255.0 / 255.0;
|
|
1042 | - double backAlpha = 1 - foreAlpha;
|
|
1043 | - double r = std::pow(qRed(foregroundColor_) / 255.0, gamma_);
|
|
1044 | - double g = std::pow(qGreen(foregroundColor_) / 255.0, gamma_);
|
|
1045 | - double b = std::pow(qBlue(foregroundColor_) / 255.0, gamma_);
|
|
1046 | - |
|
1047 | - r = br * backAlpha + r * foreAlpha;
|
|
1048 | - g = bg * backAlpha + g * foreAlpha;
|
|
1049 | - b = bb * backAlpha + b * foreAlpha;
|
|
1050 | - |
|
1051 | - r = std::pow(r, invGamma);
|
|
1052 | - g = std::pow(g, invGamma);
|
|
1053 | - b = std::pow(b, invGamma);
|
|
1054 | - |
|
1055 | - foregroundTable_[i]
|
|
1056 | - = qRgba(static_cast<int>(r * 255),
|
|
1057 | - static_cast<int>(g * 255),
|
|
1058 | - static_cast<int>(b * 255),
|
|
1059 | - 255);
|
|
1060 | - }
|
|
1061 | -}
|
|
1062 | - |
|
1063 | - |
|
1064 | -void
|
|
1065 | -convertLCDToARGB(FT_Bitmap& bitmap,
|
|
1066 | - QImage& image,
|
|
1067 | - bool isBGR,
|
|
1068 | - QVector<QRgb>& colorTable)
|
|
1069 | -{
|
|
1070 | - int height = bitmap.rows;
|
|
1071 | - int width = bitmap.width / 3;
|
|
1072 | - int width3 = bitmap.width;
|
|
1073 | - |
|
1074 | - unsigned char* srcPtr = bitmap.buffer;
|
|
1075 | - unsigned* dstPtr = reinterpret_cast<unsigned*>(image.bits());
|
|
1076 | - |
|
1077 | - int offR = !isBGR ? 0 : 2;
|
|
1078 | - int offG = 1;
|
|
1079 | - int offB = isBGR ? 0 : 2;
|
|
1080 | - for (int i = 0; i < height; i++)
|
|
1081 | - {
|
|
1082 | - for (int j = 0; j < width3; j += 3)
|
|
1083 | - {
|
|
1084 | - unsigned char ar = srcPtr[j + offR];
|
|
1085 | - unsigned char ag = srcPtr[j + offG];
|
|
1086 | - unsigned char ab = srcPtr[j + offB];
|
|
1087 | - unsigned dr = colorTable[ar] & 0xFF;
|
|
1088 | - unsigned dg = colorTable[ag] & 0xFF;
|
|
1089 | - unsigned db = colorTable[ab] & 0xFF;
|
|
1090 | - *dstPtr = (0xFFu << 24) | (dr << 16) | (dg << 8) | db;
|
|
1091 | - dstPtr++;
|
|
1092 | - }
|
|
1093 | - srcPtr += bitmap.pitch;
|
|
1094 | - dstPtr += image.bytesPerLine() / 4 - width; // skip blank area
|
|
1095 | - }
|
|
1096 | -}
|
|
1097 | - |
|
1098 | - |
|
1099 | -void
|
|
1100 | -convertLCDVToARGB(FT_Bitmap& bitmap,
|
|
1101 | - QImage& image,
|
|
1102 | - bool isBGR,
|
|
1103 | - QVector<QRgb>& colorTable)
|
|
1104 | -{
|
|
1105 | - int height = bitmap.rows / 3;
|
|
1106 | - int width = bitmap.width;
|
|
1107 | - int srcPitch = bitmap.pitch;
|
|
1108 | - |
|
1109 | - unsigned char* srcPtr = bitmap.buffer;
|
|
1110 | - unsigned* dstPtr = reinterpret_cast<unsigned*>(image.bits());
|
|
1111 | - |
|
1112 | - int offR = !isBGR ? 0 : 2 * srcPitch;
|
|
1113 | - int offG = srcPitch;
|
|
1114 | - int offB = isBGR ? 0 : 2 * srcPitch;
|
|
1115 | - for (int i = 0; i < height; i++)
|
|
1116 | - {
|
|
1117 | - for (int j = 0; j < width; j++)
|
|
1118 | - {
|
|
1119 | - unsigned char ar = srcPtr[j + offR];
|
|
1120 | - unsigned char ag = srcPtr[j + offG];
|
|
1121 | - unsigned char ab = srcPtr[j + offB];
|
|
1122 | - unsigned dr = colorTable[ar] & 0xFF;
|
|
1123 | - unsigned dg = colorTable[ag] & 0xFF;
|
|
1124 | - unsigned db = colorTable[ab] & 0xFF;
|
|
1125 | - *dstPtr = (0xFFu << 24) | (dr << 16) | (dg << 8) | db;
|
|
1126 | - dstPtr++;
|
|
1127 | - }
|
|
1128 | - srcPtr += 3ull * srcPitch; // move 3 lines
|
|
1129 | - dstPtr += image.bytesPerLine() / 4 - width; // skip blank area
|
|
1130 | - }
|
|
1131 | -}
|
|
1132 | - |
|
1133 | - |
|
1134 | -QImage*
|
|
1135 | -Engine::convertBitmapToQImage(FT_Bitmap* src)
|
|
1136 | -{
|
|
1137 | - QImage* result = NULL;
|
|
1138 | -
|
|
1139 | - auto& bmap = *src;
|
|
1140 | - bool ownBitmap = false;
|
|
1141 | - |
|
1142 | - int width = INT_MAX, height = INT_MAX;
|
|
1143 | - if (bmap.width < INT_MAX)
|
|
1144 | - width = static_cast<int>(bmap.width);
|
|
1145 | - if (bmap.rows < INT_MAX)
|
|
1146 | - height = static_cast<int>(bmap.rows);
|
|
1147 | - auto format = QImage::Format_Indexed8; // goto crossing init
|
|
1148 | - |
|
1149 | - if (bmap.pixel_mode == FT_PIXEL_MODE_GRAY2
|
|
1150 | - || bmap.pixel_mode == FT_PIXEL_MODE_GRAY4)
|
|
1151 | - {
|
|
1152 | - bmap = convertBitmapTo8Bpp(&bmap);
|
|
1153 | - if (!bmap.buffer)
|
|
1154 | - goto cleanup;
|
|
1155 | - ownBitmap = true;
|
|
1156 | - }
|
|
1157 | - |
|
1158 | - if (bmap.pixel_mode == FT_PIXEL_MODE_LCD)
|
|
1159 | - width /= 3;
|
|
1160 | - else if (bmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
|
|
1161 | - height /= 3;
|
|
1162 | - |
|
1163 | - switch (bmap.pixel_mode)
|
|
1164 | - {
|
|
1165 | - case FT_PIXEL_MODE_MONO:
|
|
1166 | - format = QImage::Format_Mono;
|
|
1167 | - break;
|
|
1168 | - case FT_PIXEL_MODE_GRAY:
|
|
1169 | - format = QImage::Format_Indexed8;
|
|
1170 | - break;
|
|
1171 | - case FT_PIXEL_MODE_BGRA:
|
|
1172 | - // XXX "ARGB" here means BGRA due to endianness - may be problematic
|
|
1173 | - // on big-endian machines
|
|
1174 | - format = QImage::Format_ARGB32_Premultiplied;
|
|
1175 | - break;
|
|
1176 | - case FT_PIXEL_MODE_LCD:
|
|
1177 | - case FT_PIXEL_MODE_LCD_V:
|
|
1178 | - format = QImage::Format_ARGB32;
|
|
1179 | - break;
|
|
1180 | - default:
|
|
1181 | - goto cleanup;
|
|
1182 | - }
|
|
1183 | - |
|
1184 | - switch (bmap.pixel_mode)
|
|
1185 | - {
|
|
1186 | - case FT_PIXEL_MODE_MONO:
|
|
1187 | - case FT_PIXEL_MODE_GRAY:
|
|
1188 | - case FT_PIXEL_MODE_BGRA:
|
|
1189 | - {
|
|
1190 | - QImage image(bmap.buffer,
|
|
1191 | - width, height,
|
|
1192 | - bmap.pitch,
|
|
1193 | - format);
|
|
1194 | - if (bmap.pixel_mode == FT_PIXEL_MODE_GRAY)
|
|
1195 | - image.setColorTable(foregroundTable_);
|
|
1196 | - else if (bmap.pixel_mode == FT_PIXEL_MODE_MONO)
|
|
1197 | - {
|
|
1198 | - image.setColorCount(2);
|
|
1199 | - image.setColor(0, static_cast<QRgb>(0)); // transparent
|
|
1200 | - image.setColor(1, foregroundTable_[0xFF]);
|
|
1201 | - }
|
|
1202 | - result = new QImage(image.copy());
|
|
1203 | - // Don't directly use `image` since we're destroying the image
|
|
1204 | - }
|
|
1205 | - break;
|
|
1206 | - case FT_PIXEL_MODE_LCD:;
|
|
1207 | - result = new QImage(width, height, format);
|
|
1208 | - convertLCDToARGB(bmap, *result, lcdUsesBGR_, foregroundTable_);
|
|
1209 | - break;
|
|
1210 | - case FT_PIXEL_MODE_LCD_V:;
|
|
1211 | - result = new QImage(width, height, format);
|
|
1212 | - convertLCDVToARGB(bmap, *result, lcdUsesBGR_, foregroundTable_);
|
|
1213 | - break;
|
|
1214 | - }
|
|
1215 | - |
|
1216 | -cleanup:
|
|
1217 | - if (ownBitmap)
|
|
1218 | - FT_Bitmap_Done(library_, &bmap);
|
|
1219 | - |
|
1220 | - return result;
|
|
1221 | -}
|
|
1222 | - |
|
1223 | - |
|
1224 | -QImage*
|
|
1225 | -Engine::convertGlyphToQImage(FT_Glyph src,
|
|
1226 | - QRect* outRect,
|
|
1227 | - bool inverseRectY)
|
|
1228 | -{
|
|
1229 | - FT_BitmapGlyph bitmapGlyph;
|
|
1230 | - bool ownBitmapGlyph
|
|
1231 | - = convertGlyphToBitmapGlyph(src, reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
|
|
1232 | - if (!bitmapGlyph)
|
|
1233 | - return NULL;
|
|
1234 | - |
|
1235 | - auto result = convertBitmapToQImage(&bitmapGlyph->bitmap);
|
|
1236 | - |
|
1237 | - if (result && outRect)
|
|
1238 | - {
|
|
1239 | - outRect->setLeft(bitmapGlyph->left);
|
|
1240 | - if (inverseRectY)
|
|
1241 | - outRect->setTop(-bitmapGlyph->top);
|
|
1242 | - else
|
|
1243 | - outRect->setTop(bitmapGlyph->top);
|
|
1244 | - if (bitmapGlyph->bitmap.width < INT_MAX)
|
|
1245 | - outRect->setWidth(static_cast<int>(bitmapGlyph->bitmap.width));
|
|
1246 | - else
|
|
1247 | - outRect->setWidth(INT_MAX);
|
|
1248 | - |
|
1249 | - if (bitmapGlyph->bitmap.rows < INT_MAX)
|
|
1250 | - outRect->setHeight(static_cast<int>(bitmapGlyph->bitmap.rows));
|
|
1251 | - else
|
|
1252 | - outRect->setHeight(INT_MAX);
|
|
1253 | - }
|
|
1254 | - |
|
1255 | - if (ownBitmapGlyph)
|
|
1256 | - FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
|
|
1257 | - |
|
1258 | - return result;
|
|
1259 | -}
|
|
1260 | - |
|
1261 | - |
|
1262 | -QPoint
|
|
1263 | -Engine::computeGlyphOffset(FT_Glyph glyph, bool inverseY)
|
|
1264 | -{
|
|
1265 | - if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
1266 | - {
|
|
1267 | - FT_BBox cbox;
|
|
1268 | - FT_Outline_Get_CBox(&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
|
|
1269 | - &cbox);
|
|
1270 | - cbox.xMin &= ~63;
|
|
1271 | - cbox.yMin &= ~63;
|
|
1272 | - cbox.xMax = (cbox.xMax + 63) & ~63;
|
|
1273 | - cbox.yMax = (cbox.yMax + 63) & ~63;
|
|
1274 | - if (inverseY)
|
|
1275 | - cbox.yMax = -cbox.yMax;
|
|
1276 | - return { static_cast<int>(cbox.xMin / 64),
|
|
1277 | - static_cast<int>(cbox.yMax / 64) };
|
|
1278 | - }
|
|
1279 | - if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
|
|
1280 | - {
|
|
1281 | - auto bg = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
1282 | - if (inverseY)
|
|
1283 | - return { bg->left, -bg->top };
|
|
1284 | - return { bg->left, bg->top };
|
|
1285 | - }
|
|
1286 | - |
|
1287 | - return {};
|
|
1288 | -}
|
|
1289 | - |
|
1290 | - |
|
1291 | -QImage*
|
|
1292 | -Engine::tryDirectRenderColorLayers(int glyphIndex,
|
|
1293 | - QRect* outRect,
|
|
1294 | - bool inverseRectY)
|
|
1295 | -{
|
|
1296 | - if (palette_ == NULL
|
|
1297 | - || !useColorLayer_
|
|
1298 | - || paletteIndex_ >= paletteData_.num_palettes)
|
|
1299 | - return NULL;
|
|
1300 | - |
|
1301 | - FT_LayerIterator iter = {};
|
|
1302 | -
|
|
1303 | - FT_UInt layerGlyphIdx = 0;
|
|
1304 | - FT_UInt layerColorIdx = 0;
|
|
1305 | - |
|
1306 | - bool next = FT_Get_Color_Glyph_Layer(ftSize_->face,
|
|
1307 | - glyphIndex,
|
|
1308 | - &layerGlyphIdx,
|
|
1309 | - &layerColorIdx,
|
|
1310 | - &iter);
|
|
1311 | - if (!next)
|
|
1312 | - return NULL;
|
|
1313 | - |
|
1314 | - // temporarily change lf
|
|
1315 | - auto oldLoadFlags = imageType_.flags;
|
|
1316 | - auto loadFlags = oldLoadFlags;
|
|
1317 | - loadFlags &= ~FT_LOAD_COLOR;
|
|
1318 | - loadFlags |= FT_LOAD_RENDER;
|
|
1319 | - |
|
1320 | - loadFlags &= ~FT_LOAD_TARGET_(0xF);
|
|
1321 | - loadFlags |= FT_LOAD_TARGET_NORMAL;
|
|
1322 | - imageType_.flags = loadFlags;
|
|
1323 | - |
|
1324 | - FT_Bitmap bitmap = {};
|
|
1325 | - FT_Bitmap_Init(&bitmap);
|
|
1326 | - |
|
1327 | - FT_Vector bitmapOffset = {};
|
|
1328 | - bool failed = false;
|
|
1329 | - |
|
1330 | - do
|
|
1331 | - {
|
|
1332 | - FT_Vector slotOffset;
|
|
1333 | - FT_Glyph glyph;
|
|
1334 | - if (FTC_ImageCache_Lookup(imageCache_,
|
|
1335 | - &imageType_,
|
|
1336 | - layerGlyphIdx,
|
|
1337 | - &glyph,
|
|
1338 | - NULL))
|
|
1339 | - {
|
|
1340 | - // XXX Error handling
|
|
1341 | - failed = true;
|
|
1342 | - break;
|
|
1343 | - }
|
|
1344 | - |
|
1345 | - if (glyph->format != FT_GLYPH_FORMAT_BITMAP)
|
|
1346 | - continue;
|
|
1347 | - |
|
1348 | - auto bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
1349 | - slotOffset.x = bitmapGlyph->left << 6;
|
|
1350 | - slotOffset.y = bitmapGlyph->top << 6;
|
|
1351 | - |
|
1352 | - FT_Color color = {};
|
|
1353 | - |
|
1354 | - if (layerColorIdx == 0xFFFF)
|
|
1355 | - {
|
|
1356 | - // TODO: FT_Palette_Get_Foreground_Color: #1134
|
|
1357 | - if (paletteData_.palette_flags
|
|
1358 | - && (paletteData_.palette_flags[paletteIndex_]
|
|
1359 | - & FT_PALETTE_FOR_DARK_BACKGROUND))
|
|
1360 | - {
|
|
1361 | - /* white opaque */
|
|
1362 | - color.blue = 0xFF;
|
|
1363 | - color.green = 0xFF;
|
|
1364 | - color.red = 0xFF;
|
|
1365 | - color.alpha = 0xFF;
|
|
1366 | - }
|
|
1367 | - else
|
|
1368 | - {
|
|
1369 | - /* black opaque */
|
|
1370 | - color.blue = 0x00;
|
|
1371 | - color.green = 0x00;
|
|
1372 | - color.red = 0x00;
|
|
1373 | - color.alpha = 0xFF;
|
|
1374 | - }
|
|
1375 | - }
|
|
1376 | - else if (layerColorIdx < paletteData_.num_palette_entries)
|
|
1377 | - color = palette_[layerColorIdx];
|
|
1378 | - else
|
|
1379 | - continue;
|
|
1380 | - |
|
1381 | - if (FT_Bitmap_Blend(library_,
|
|
1382 | - &bitmapGlyph->bitmap, slotOffset,
|
|
1383 | - &bitmap, &bitmapOffset,
|
|
1384 | - color))
|
|
1385 | - {
|
|
1386 | - // XXX error
|
|
1387 | - failed = true;
|
|
1388 | - break;
|
|
1389 | - }
|
|
1390 | - } while (FT_Get_Color_Glyph_Layer(ftSize_->face,
|
|
1391 | - glyphIndex,
|
|
1392 | - &layerGlyphIdx,
|
|
1393 | - &layerColorIdx,
|
|
1394 | - &iter));
|
|
1395 | - |
|
1396 | - imageType_.flags = oldLoadFlags;
|
|
1397 | - if (failed)
|
|
1398 | - {
|
|
1399 | - FT_Bitmap_Done(library_, &bitmap);
|
|
1400 | - return NULL;
|
|
1401 | - }
|
|
1402 | - |
|
1403 | - auto img = convertBitmapToQImage(&bitmap);
|
|
1404 | - if (outRect)
|
|
1405 | - {
|
|
1406 | - outRect->moveLeft(static_cast<int>(bitmapOffset.x >> 6));
|
|
1407 | - if (inverseRectY)
|
|
1408 | - outRect->moveTop(static_cast<int>(-bitmapOffset.y >> 6));
|
|
1409 | - else
|
|
1410 | - outRect->moveTop(static_cast<int>(bitmapOffset.y >> 6));
|
|
1411 | - outRect->setSize(img->size());
|
|
1412 | - }
|
|
1413 | - |
|
1414 | - FT_Bitmap_Done(library_, &bitmap);
|
|
1415 | - |
|
1416 | - return img;
|
|
1417 | -}
|
|
1418 | - |
|
1419 | - |
|
1420 | 951 | QHash<FT_Glyph_Format, QString> glyphFormatNamesCache;
|
1421 | 952 | QHash<FT_Glyph_Format, QString>&
|
1422 | 953 | glyphFormatNames()
|
... | ... | @@ -10,8 +10,10 @@ |
10 | 10 | #include "paletteinfo.hpp"
|
11 | 11 | #include "fontinfo.hpp"
|
12 | 12 | #include "mmgx.hpp"
|
13 | +#include "rendering.hpp"
|
|
13 | 14 | |
14 | 15 | #include <vector>
|
16 | +#include <memory>
|
|
15 | 17 | #include <utility>
|
16 | 18 | #include <QString>
|
17 | 19 | #include <QMap>
|
... | ... | @@ -90,29 +92,6 @@ public: |
90 | 92 | FTC_Node* outNode = NULL,
|
91 | 93 | bool forceRender = false);
|
92 | 94 | |
93 | - // Return `true` if you need to free `out`
|
|
94 | - // `out` will be set to NULL in cases of error
|
|
95 | - bool convertGlyphToBitmapGlyph(FT_Glyph src, FT_Glyph* out);
|
|
96 | - FT_Bitmap convertBitmapTo8Bpp(FT_Bitmap* bitmap);
|
|
97 | - QImage* convertBitmapToQImage(FT_Bitmap* src);
|
|
98 | - QImage* convertGlyphToQImage(FT_Glyph src,
|
|
99 | - QRect* outRect,
|
|
100 | - bool inverseRectY);
|
|
101 | - QPoint computeGlyphOffset(FT_Glyph glyph, bool inverseY);
|
|
102 | - |
|
103 | - /*
|
|
104 | - * Directly render the glyph at the specified index
|
|
105 | - * to a `QImage`. If you want to perform color-layer
|
|
106 | - * rendering, call this before trying to load the
|
|
107 | - * glyph and do normal rendering, If the returning
|
|
108 | - * value is non-NULL, then there's no need to
|
|
109 | - * load the glyph the normal way, just draw the `QImage`.
|
|
110 | - * Will return NULL if not enabled or color layers not available.
|
|
111 | - */
|
|
112 | - QImage* tryDirectRenderColorLayers(int glyphIndex,
|
|
113 | - QRect* outRect,
|
|
114 | - bool inverseRectY = false);
|
|
115 | - |
|
116 | 95 | // reload current triplet, but with updated settings, useful for updating
|
117 | 96 | // `ftSize_` only
|
118 | 97 | void reloadFont();
|
... | ... | @@ -128,6 +107,7 @@ public: |
128 | 107 | |
129 | 108 | FT_Library ftLibrary() const { return library_; }
|
130 | 109 | FTC_Manager cacheManager() { return cacheManager_; }
|
110 | + FTC_ImageCache imageCacheManager() { return imageCache_; }
|
|
131 | 111 | |
132 | 112 | int dpi() { return dpi_; }
|
133 | 113 | double pointSize() { return pointSize_; }
|
... | ... | @@ -137,7 +117,7 @@ public: |
137 | 117 | int numberOfOpenedFonts();
|
138 | 118 | int currentFontIndex() { return curFontIndex_; }
|
139 | 119 | FT_Face currentFallbackFtFace() { return ftFallbackFace_; }
|
140 | - // FT_Size currentFtSize() { return ftSize_; }
|
|
120 | + FT_Size currentFtSize() { return ftSize_; }
|
|
141 | 121 | int currentFontType() const { return fontType_; }
|
142 | 122 | const QString& currentFamilyName() { return curFamilyName_; }
|
143 | 123 | const QString& currentStyleName() { return curStyleName_; }
|
... | ... | @@ -170,13 +150,26 @@ public: |
170 | 150 | std::vector<int> currentFontFixedSizes();
|
171 | 151 | FontFileManager& fontFileManager() { return fontFileManager_; }
|
172 | 152 | EngineDefaultValues& engineDefaults() { return engineDefaults_; }
|
153 | + |
|
154 | + double gamma() { return gamma_; }
|
|
173 | 155 | bool antiAliasingEnabled() { return antiAliasingEnabled_; }
|
174 | 156 | bool doHinting() { return doHinting_; }
|
175 | 157 | bool embeddedBitmapEnabled() { return embeddedBitmap_; }
|
176 | 158 | bool lcdUsingSubPixelPositioning() { return lcdSubPixelPositioning_; }
|
159 | + bool useColorLayer() { return useColorLayer_; }
|
|
160 | + bool lcdUsesBGR() { return lcdUsesBGR_; }
|
|
161 | + FT_Render_Mode
|
|
162 | + renderMode()
|
|
163 | + {
|
|
164 | + return static_cast<FT_Render_Mode>(renderMode_);
|
|
165 | + }
|
|
166 | + FTC_ImageType imageType() { return &imageType_; }
|
|
167 | + |
|
168 | + int paletteIndex() { return paletteIndex_; }
|
|
169 | + FT_Color* currentPalette() { return palette_; }
|
|
170 | + FT_Palette_Data& currentFontPaletteData() { return paletteData_; }
|
|
177 | 171 | |
178 | - QRgb foreground() { return foregroundColor_; }
|
|
179 | - QRgb background() { return backgroundColor_; }
|
|
172 | + RenderingEngine* renderingEngine() { return renderingEngine_.get(); }
|
|
180 | 173 | |
181 | 174 | //////// Setters (direct or indirect)
|
182 | 175 | |
... | ... | @@ -217,9 +210,6 @@ public: |
217 | 210 | void setStemDarkening(bool darkening);
|
218 | 211 | void applyMMGXDesignCoords(FT_Fixed* coords, size_t count);
|
219 | 212 | |
220 | - void setForeground(QRgb foreground);
|
|
221 | - void setBackground(QRgb background);
|
|
222 | - |
|
223 | 213 | //////// Misc
|
224 | 214 | |
225 | 215 | friend FT_Error faceRequester(FTC_FaceID,
|
... | ... | @@ -287,14 +277,10 @@ private: |
287 | 277 | double gamma_;
|
288 | 278 | unsigned long loadFlags_;
|
289 | 279 | |
290 | - QRgb backgroundColor_;
|
|
291 | - QRgb foregroundColor_;
|
|
292 | - QRgb foregroundColorBlended_;
|
|
293 | - QVector<QRgb> foregroundTable_;
|
|
280 | + std::unique_ptr<RenderingEngine> renderingEngine_;
|
|
294 | 281 | |
295 | 282 | void queryEngine();
|
296 | 283 | void loadPaletteInfos();
|
297 | - void calculateForegroundTable();
|
|
298 | 284 | |
299 | 285 | // Safe to put the impl to the cpp.
|
300 | 286 | template <class Func>
|
1 | +// rendering.cpp
|
|
2 | + |
|
3 | +// Copyright (C) 2022 by Charlie Jiang.
|
|
4 | + |
|
5 | +#include "rendering.hpp"
|
|
6 | + |
|
7 | +#include <cmath>
|
|
8 | +#include <freetype/ftbitmap.h>
|
|
9 | + |
|
10 | +#include "engine.hpp"
|
|
11 | + |
|
12 | + |
|
13 | +RenderingEngine::RenderingEngine(Engine* engine)
|
|
14 | +: engine_(engine)
|
|
15 | +{
|
|
16 | + setForeground(QColor(Qt::black).rgba());
|
|
17 | + setBackground(QColor(Qt::white).rgba());
|
|
18 | +}
|
|
19 | + |
|
20 | + |
|
21 | +void
|
|
22 | +RenderingEngine::setForeground(QRgb foreground)
|
|
23 | +{
|
|
24 | + if (foregroundTable_.size() != 256 || foreground != foregroundColor_)
|
|
25 | + {
|
|
26 | + foregroundColor_ = foreground;
|
|
27 | + calculateForegroundTable();
|
|
28 | + }
|
|
29 | +}
|
|
30 | + |
|
31 | + |
|
32 | +void
|
|
33 | +RenderingEngine::setBackground(QRgb background)
|
|
34 | +{
|
|
35 | + if (foregroundTable_.size() != 256 || background != backgroundColor_)
|
|
36 | + {
|
|
37 | + backgroundColor_ = background;
|
|
38 | + calculateForegroundTable();
|
|
39 | + }
|
|
40 | +}
|
|
41 | + |
|
42 | + |
|
43 | +void
|
|
44 | +RenderingEngine::calculateForegroundTable()
|
|
45 | +{
|
|
46 | + foregroundTable_.resize(256);
|
|
47 | + auto gamma = engine_->gamma();
|
|
48 | + |
|
49 | + // Yes I know this is horribly slow, but we're only calculating the table once
|
|
50 | + // and can use it for all rendering if the color and gamma isn't changing.
|
|
51 | + |
|
52 | + double br = std::pow(qRed(backgroundColor_) / 255.0, gamma);
|
|
53 | + double bg = std::pow(qGreen(backgroundColor_) / 255.0, gamma);
|
|
54 | + double bb = std::pow(qBlue(backgroundColor_) / 255.0, gamma);
|
|
55 | + double invGamma = 1 / gamma;
|
|
56 | + |
|
57 | + for (int i = 0; i <= 0xFF; i++)
|
|
58 | + {
|
|
59 | + double foreAlpha = i * qAlpha(foregroundColor_) / 255.0 / 255.0;
|
|
60 | + double backAlpha = 1 - foreAlpha;
|
|
61 | + double r = std::pow(qRed(foregroundColor_) / 255.0, gamma);
|
|
62 | + double g = std::pow(qGreen(foregroundColor_) / 255.0, gamma);
|
|
63 | + double b = std::pow(qBlue(foregroundColor_) / 255.0, gamma);
|
|
64 | + |
|
65 | + r = br * backAlpha + r * foreAlpha;
|
|
66 | + g = bg * backAlpha + g * foreAlpha;
|
|
67 | + b = bb * backAlpha + b * foreAlpha;
|
|
68 | + |
|
69 | + r = std::pow(r, invGamma);
|
|
70 | + g = std::pow(g, invGamma);
|
|
71 | + b = std::pow(b, invGamma);
|
|
72 | + |
|
73 | + foregroundTable_[i]
|
|
74 | + = qRgba(static_cast<int>(r * 255),
|
|
75 | + static_cast<int>(g * 255),
|
|
76 | + static_cast<int>(b * 255),
|
|
77 | + 255);
|
|
78 | + }
|
|
79 | +}
|
|
80 | + |
|
81 | + |
|
82 | +bool
|
|
83 | +RenderingEngine::convertGlyphToBitmapGlyph(FT_Glyph src,
|
|
84 | + FT_Glyph* out)
|
|
85 | +{
|
|
86 | + if (src->format == FT_GLYPH_FORMAT_BITMAP)
|
|
87 | + {
|
|
88 | + *out = src;
|
|
89 | + return false;
|
|
90 | + }
|
|
91 | + if (src->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
92 | + {
|
|
93 | + *out = NULL;
|
|
94 | + return false;
|
|
95 | + // TODO support SVG
|
|
96 | + }
|
|
97 | + |
|
98 | + if (src->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
99 | + {
|
|
100 | + FT_Glyph out2 = src;
|
|
101 | + auto error = FT_Glyph_To_Bitmap(&out2,
|
|
102 | + engine_->renderMode(),
|
|
103 | + nullptr,
|
|
104 | + false);
|
|
105 | + if (error)
|
|
106 | + {
|
|
107 | + *out = NULL;
|
|
108 | + return false;
|
|
109 | + }
|
|
110 | + *out = out2;
|
|
111 | + return true;
|
|
112 | + }
|
|
113 | + |
|
114 | + *out = NULL;
|
|
115 | + return false;
|
|
116 | +}
|
|
117 | + |
|
118 | + |
|
119 | +FT_Bitmap
|
|
120 | +RenderingEngine::convertBitmapTo8Bpp(FT_Bitmap* bitmap)
|
|
121 | +{
|
|
122 | + FT_Bitmap out;
|
|
123 | + out.buffer = NULL;
|
|
124 | + auto error = FT_Bitmap_Convert(engine_->ftLibrary(), bitmap, &out, 1);
|
|
125 | + if (error)
|
|
126 | + {
|
|
127 | + // XXX handling?
|
|
128 | + }
|
|
129 | + return out;
|
|
130 | +}
|
|
131 | + |
|
132 | + |
|
133 | +void
|
|
134 | +convertLCDToARGB(FT_Bitmap& bitmap,
|
|
135 | + QImage& image,
|
|
136 | + bool isBGR,
|
|
137 | + QVector<QRgb>& colorTable)
|
|
138 | +{
|
|
139 | + int height = bitmap.rows;
|
|
140 | + int width = bitmap.width / 3;
|
|
141 | + int width3 = bitmap.width;
|
|
142 | + |
|
143 | + unsigned char* srcPtr = bitmap.buffer;
|
|
144 | + unsigned* dstPtr = reinterpret_cast<unsigned*>(image.bits());
|
|
145 | + |
|
146 | + int offR = !isBGR ? 0 : 2;
|
|
147 | + int offG = 1;
|
|
148 | + int offB = isBGR ? 0 : 2;
|
|
149 | + for (int i = 0; i < height; i++)
|
|
150 | + {
|
|
151 | + for (int j = 0; j < width3; j += 3)
|
|
152 | + {
|
|
153 | + unsigned char ar = srcPtr[j + offR];
|
|
154 | + unsigned char ag = srcPtr[j + offG];
|
|
155 | + unsigned char ab = srcPtr[j + offB];
|
|
156 | + unsigned dr = colorTable[ar] & 0xFF;
|
|
157 | + unsigned dg = colorTable[ag] & 0xFF;
|
|
158 | + unsigned db = colorTable[ab] & 0xFF;
|
|
159 | + *dstPtr = (0xFFu << 24) | (dr << 16) | (dg << 8) | db;
|
|
160 | + dstPtr++;
|
|
161 | + }
|
|
162 | + srcPtr += bitmap.pitch;
|
|
163 | + dstPtr += image.bytesPerLine() / 4 - width; // skip blank area
|
|
164 | + }
|
|
165 | +}
|
|
166 | + |
|
167 | + |
|
168 | +void
|
|
169 | +convertLCDVToARGB(FT_Bitmap& bitmap,
|
|
170 | + QImage& image,
|
|
171 | + bool isBGR,
|
|
172 | + QVector<QRgb>& colorTable)
|
|
173 | +{
|
|
174 | + int height = bitmap.rows / 3;
|
|
175 | + int width = bitmap.width;
|
|
176 | + int srcPitch = bitmap.pitch;
|
|
177 | + |
|
178 | + unsigned char* srcPtr = bitmap.buffer;
|
|
179 | + unsigned* dstPtr = reinterpret_cast<unsigned*>(image.bits());
|
|
180 | + |
|
181 | + int offR = !isBGR ? 0 : 2 * srcPitch;
|
|
182 | + int offG = srcPitch;
|
|
183 | + int offB = isBGR ? 0 : 2 * srcPitch;
|
|
184 | + for (int i = 0; i < height; i++)
|
|
185 | + {
|
|
186 | + for (int j = 0; j < width; j++)
|
|
187 | + {
|
|
188 | + unsigned char ar = srcPtr[j + offR];
|
|
189 | + unsigned char ag = srcPtr[j + offG];
|
|
190 | + unsigned char ab = srcPtr[j + offB];
|
|
191 | + unsigned dr = colorTable[ar] & 0xFF;
|
|
192 | + unsigned dg = colorTable[ag] & 0xFF;
|
|
193 | + unsigned db = colorTable[ab] & 0xFF;
|
|
194 | + *dstPtr = (0xFFu << 24) | (dr << 16) | (dg << 8) | db;
|
|
195 | + dstPtr++;
|
|
196 | + }
|
|
197 | + srcPtr += 3ull * srcPitch; // move 3 lines
|
|
198 | + dstPtr += image.bytesPerLine() / 4 - width; // skip blank area
|
|
199 | + }
|
|
200 | +}
|
|
201 | + |
|
202 | + |
|
203 | +QImage*
|
|
204 | +RenderingEngine::convertBitmapToQImage(FT_Bitmap* src)
|
|
205 | +{
|
|
206 | + QImage* result = NULL;
|
|
207 | +
|
|
208 | + auto& bmap = *src;
|
|
209 | + bool ownBitmap = false;
|
|
210 | + |
|
211 | + int width = INT_MAX, height = INT_MAX;
|
|
212 | + if (bmap.width < INT_MAX)
|
|
213 | + width = static_cast<int>(bmap.width);
|
|
214 | + if (bmap.rows < INT_MAX)
|
|
215 | + height = static_cast<int>(bmap.rows);
|
|
216 | + auto format = QImage::Format_Indexed8; // goto crossing init
|
|
217 | + |
|
218 | + if (bmap.pixel_mode == FT_PIXEL_MODE_GRAY2
|
|
219 | + || bmap.pixel_mode == FT_PIXEL_MODE_GRAY4)
|
|
220 | + {
|
|
221 | + bmap = convertBitmapTo8Bpp(&bmap);
|
|
222 | + if (!bmap.buffer)
|
|
223 | + goto cleanup;
|
|
224 | + ownBitmap = true;
|
|
225 | + }
|
|
226 | + |
|
227 | + if (bmap.pixel_mode == FT_PIXEL_MODE_LCD)
|
|
228 | + width /= 3;
|
|
229 | + else if (bmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
|
|
230 | + height /= 3;
|
|
231 | + |
|
232 | + switch (bmap.pixel_mode)
|
|
233 | + {
|
|
234 | + case FT_PIXEL_MODE_MONO:
|
|
235 | + format = QImage::Format_Mono;
|
|
236 | + break;
|
|
237 | + case FT_PIXEL_MODE_GRAY:
|
|
238 | + format = QImage::Format_Indexed8;
|
|
239 | + break;
|
|
240 | + case FT_PIXEL_MODE_BGRA:
|
|
241 | + // XXX "ARGB" here means BGRA due to endianness - may be problematic
|
|
242 | + // on big-endian machines
|
|
243 | + format = QImage::Format_ARGB32_Premultiplied;
|
|
244 | + break;
|
|
245 | + case FT_PIXEL_MODE_LCD:
|
|
246 | + case FT_PIXEL_MODE_LCD_V:
|
|
247 | + format = QImage::Format_ARGB32;
|
|
248 | + break;
|
|
249 | + default:
|
|
250 | + goto cleanup;
|
|
251 | + }
|
|
252 | + |
|
253 | + switch (bmap.pixel_mode)
|
|
254 | + {
|
|
255 | + case FT_PIXEL_MODE_MONO:
|
|
256 | + case FT_PIXEL_MODE_GRAY:
|
|
257 | + case FT_PIXEL_MODE_BGRA:
|
|
258 | + {
|
|
259 | + QImage image(bmap.buffer,
|
|
260 | + width, height,
|
|
261 | + bmap.pitch,
|
|
262 | + format);
|
|
263 | + if (bmap.pixel_mode == FT_PIXEL_MODE_GRAY)
|
|
264 | + image.setColorTable(foregroundTable_);
|
|
265 | + else if (bmap.pixel_mode == FT_PIXEL_MODE_MONO)
|
|
266 | + {
|
|
267 | + image.setColorCount(2);
|
|
268 | + image.setColor(0, static_cast<QRgb>(0)); // transparent
|
|
269 | + image.setColor(1, foregroundTable_[0xFF]);
|
|
270 | + }
|
|
271 | + result = new QImage(image.copy());
|
|
272 | + // Don't directly use `image` since we're destroying the image
|
|
273 | + }
|
|
274 | + break;
|
|
275 | + case FT_PIXEL_MODE_LCD:;
|
|
276 | + result = new QImage(width, height, format);
|
|
277 | + convertLCDToARGB(bmap, *result, engine_->lcdUsesBGR(), foregroundTable_);
|
|
278 | + break;
|
|
279 | + case FT_PIXEL_MODE_LCD_V:;
|
|
280 | + result = new QImage(width, height, format);
|
|
281 | + convertLCDVToARGB(bmap, *result, engine_->lcdUsesBGR(), foregroundTable_);
|
|
282 | + break;
|
|
283 | + }
|
|
284 | + |
|
285 | +cleanup:
|
|
286 | + if (ownBitmap)
|
|
287 | + FT_Bitmap_Done(engine_->ftLibrary(), &bmap);
|
|
288 | + |
|
289 | + return result;
|
|
290 | +}
|
|
291 | + |
|
292 | + |
|
293 | +QImage*
|
|
294 | +RenderingEngine::convertGlyphToQImage(FT_Glyph src,
|
|
295 | + QRect* outRect,
|
|
296 | + bool inverseRectY)
|
|
297 | +{
|
|
298 | + FT_BitmapGlyph bitmapGlyph;
|
|
299 | + bool ownBitmapGlyph
|
|
300 | + = convertGlyphToBitmapGlyph(src, reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
|
|
301 | + if (!bitmapGlyph)
|
|
302 | + return NULL;
|
|
303 | + |
|
304 | + auto result = convertBitmapToQImage(&bitmapGlyph->bitmap);
|
|
305 | + |
|
306 | + if (result && outRect)
|
|
307 | + {
|
|
308 | + outRect->setLeft(bitmapGlyph->left);
|
|
309 | + if (inverseRectY)
|
|
310 | + outRect->setTop(-bitmapGlyph->top);
|
|
311 | + else
|
|
312 | + outRect->setTop(bitmapGlyph->top);
|
|
313 | + if (bitmapGlyph->bitmap.width < INT_MAX)
|
|
314 | + outRect->setWidth(static_cast<int>(bitmapGlyph->bitmap.width));
|
|
315 | + else
|
|
316 | + outRect->setWidth(INT_MAX);
|
|
317 | + |
|
318 | + if (bitmapGlyph->bitmap.rows < INT_MAX)
|
|
319 | + outRect->setHeight(static_cast<int>(bitmapGlyph->bitmap.rows));
|
|
320 | + else
|
|
321 | + outRect->setHeight(INT_MAX);
|
|
322 | + }
|
|
323 | + |
|
324 | + if (ownBitmapGlyph)
|
|
325 | + FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
|
|
326 | + |
|
327 | + return result;
|
|
328 | +}
|
|
329 | + |
|
330 | + |
|
331 | +QPoint
|
|
332 | +RenderingEngine::computeGlyphOffset(FT_Glyph glyph, bool inverseY)
|
|
333 | +{
|
|
334 | + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
335 | + {
|
|
336 | + FT_BBox cbox;
|
|
337 | + FT_Outline_Get_CBox(&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
|
|
338 | + &cbox);
|
|
339 | + cbox.xMin &= ~63;
|
|
340 | + cbox.yMin &= ~63;
|
|
341 | + cbox.xMax = (cbox.xMax + 63) & ~63;
|
|
342 | + cbox.yMax = (cbox.yMax + 63) & ~63;
|
|
343 | + if (inverseY)
|
|
344 | + cbox.yMax = -cbox.yMax;
|
|
345 | + return { static_cast<int>(cbox.xMin / 64),
|
|
346 | + static_cast<int>(cbox.yMax / 64) };
|
|
347 | + }
|
|
348 | + if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
|
|
349 | + {
|
|
350 | + auto bg = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
351 | + if (inverseY)
|
|
352 | + return { bg->left, -bg->top };
|
|
353 | + return { bg->left, bg->top };
|
|
354 | + }
|
|
355 | + |
|
356 | + return {};
|
|
357 | +}
|
|
358 | + |
|
359 | + |
|
360 | +QImage*
|
|
361 | +RenderingEngine::tryDirectRenderColorLayers(int glyphIndex,
|
|
362 | + QRect* outRect,
|
|
363 | + bool inverseRectY)
|
|
364 | +{
|
|
365 | + auto& paletteData = engine_->currentFontPaletteData();
|
|
366 | + auto paletteIndex = engine_->paletteIndex();
|
|
367 | + auto palette = engine_->currentPalette();
|
|
368 | + if (palette == NULL
|
|
369 | + || !engine_->useColorLayer()
|
|
370 | + || paletteIndex >= paletteData.num_palettes)
|
|
371 | + return NULL;
|
|
372 | + |
|
373 | + FT_LayerIterator iter = {};
|
|
374 | +
|
|
375 | + FT_UInt layerGlyphIdx = 0;
|
|
376 | + FT_UInt layerColorIdx = 0;
|
|
377 | + |
|
378 | + bool next = FT_Get_Color_Glyph_Layer(engine_->currentFtSize()->face,
|
|
379 | + glyphIndex,
|
|
380 | + &layerGlyphIdx,
|
|
381 | + &layerColorIdx,
|
|
382 | + &iter);
|
|
383 | + if (!next)
|
|
384 | + return NULL;
|
|
385 | + |
|
386 | + // temporarily change lf
|
|
387 | + auto imageType = engine_->imageType();
|
|
388 | + auto oldLoadFlags = imageType->flags;
|
|
389 | + auto loadFlags = oldLoadFlags;
|
|
390 | + loadFlags &= ~FT_LOAD_COLOR;
|
|
391 | + loadFlags |= FT_LOAD_RENDER;
|
|
392 | + |
|
393 | + loadFlags &= ~FT_LOAD_TARGET_(0xF);
|
|
394 | + loadFlags |= FT_LOAD_TARGET_NORMAL;
|
|
395 | + imageType->flags = loadFlags;
|
|
396 | + |
|
397 | + FT_Bitmap bitmap = {};
|
|
398 | + FT_Bitmap_Init(&bitmap);
|
|
399 | + |
|
400 | + FT_Vector bitmapOffset = {};
|
|
401 | + bool failed = false;
|
|
402 | + |
|
403 | + do
|
|
404 | + {
|
|
405 | + FT_Vector slotOffset;
|
|
406 | + FT_Glyph glyph;
|
|
407 | + if (FTC_ImageCache_Lookup(engine_->imageCacheManager(),
|
|
408 | + imageType,
|
|
409 | + layerGlyphIdx,
|
|
410 | + &glyph,
|
|
411 | + NULL))
|
|
412 | + {
|
|
413 | + // XXX Error handling
|
|
414 | + failed = true;
|
|
415 | + break;
|
|
416 | + }
|
|
417 | + |
|
418 | + if (glyph->format != FT_GLYPH_FORMAT_BITMAP)
|
|
419 | + continue;
|
|
420 | + |
|
421 | + auto bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
422 | + slotOffset.x = bitmapGlyph->left << 6;
|
|
423 | + slotOffset.y = bitmapGlyph->top << 6;
|
|
424 | + |
|
425 | + FT_Color color = {};
|
|
426 | + |
|
427 | + if (layerColorIdx == 0xFFFF)
|
|
428 | + {
|
|
429 | + // TODO: FT_Palette_Get_Foreground_Color: #1134
|
|
430 | + if (paletteData.palette_flags
|
|
431 | + && (paletteData.palette_flags[paletteIndex]
|
|
432 | + & FT_PALETTE_FOR_DARK_BACKGROUND))
|
|
433 | + {
|
|
434 | + /* white opaque */
|
|
435 | + color.blue = 0xFF;
|
|
436 | + color.green = 0xFF;
|
|
437 | + color.red = 0xFF;
|
|
438 | + color.alpha = 0xFF;
|
|
439 | + }
|
|
440 | + else
|
|
441 | + {
|
|
442 | + /* black opaque */
|
|
443 | + color.blue = 0x00;
|
|
444 | + color.green = 0x00;
|
|
445 | + color.red = 0x00;
|
|
446 | + color.alpha = 0xFF;
|
|
447 | + }
|
|
448 | + }
|
|
449 | + else if (layerColorIdx < paletteData.num_palette_entries)
|
|
450 | + color = palette[layerColorIdx];
|
|
451 | + else
|
|
452 | + continue;
|
|
453 | + |
|
454 | + if (FT_Bitmap_Blend(engine_->ftLibrary(),
|
|
455 | + &bitmapGlyph->bitmap, slotOffset,
|
|
456 | + &bitmap, &bitmapOffset,
|
|
457 | + color))
|
|
458 | + {
|
|
459 | + // XXX error
|
|
460 | + failed = true;
|
|
461 | + break;
|
|
462 | + }
|
|
463 | + } while (FT_Get_Color_Glyph_Layer(engine_->currentFtSize()->face,
|
|
464 | + glyphIndex,
|
|
465 | + &layerGlyphIdx,
|
|
466 | + &layerColorIdx,
|
|
467 | + &iter));
|
|
468 | + |
|
469 | + imageType->flags = oldLoadFlags;
|
|
470 | + if (failed)
|
|
471 | + {
|
|
472 | + FT_Bitmap_Done(engine_->ftLibrary(), &bitmap);
|
|
473 | + return NULL;
|
|
474 | + }
|
|
475 | + |
|
476 | + auto img = convertBitmapToQImage(&bitmap);
|
|
477 | + if (outRect)
|
|
478 | + {
|
|
479 | + outRect->moveLeft(static_cast<int>(bitmapOffset.x >> 6));
|
|
480 | + if (inverseRectY)
|
|
481 | + outRect->moveTop(static_cast<int>(-bitmapOffset.y >> 6));
|
|
482 | + else
|
|
483 | + outRect->moveTop(static_cast<int>(bitmapOffset.y >> 6));
|
|
484 | + outRect->setSize(img->size());
|
|
485 | + }
|
|
486 | + |
|
487 | + FT_Bitmap_Done(engine_->ftLibrary(), &bitmap);
|
|
488 | + |
|
489 | + return img;
|
|
490 | +}
|
|
491 | + |
|
492 | + |
|
493 | +// end of rendering.cpp |
1 | +// rendering.hpp
|
|
2 | + |
|
3 | +// Copyright (C) 2022 by Charlie Jiang.
|
|
4 | + |
|
5 | +#pragma once
|
|
6 | + |
|
7 | +#include <QColor>
|
|
8 | +#include <QImage>
|
|
9 | +#include <freetype/freetype.h>
|
|
10 | +#include <freetype/ftglyph.h>
|
|
11 | + |
|
12 | +class Engine;
|
|
13 | +class RenderingEngine
|
|
14 | +{
|
|
15 | +public:
|
|
16 | + RenderingEngine(Engine* engine);
|
|
17 | + |
|
18 | + void setForeground(QRgb foreground);
|
|
19 | + void setBackground(QRgb background);
|
|
20 | + void calculateForegroundTable();
|
|
21 | + |
|
22 | + QRgb foreground() { return foregroundColor_; }
|
|
23 | + QRgb background() { return backgroundColor_; }
|
|
24 | + |
|
25 | + // Return `true` if you need to free `out`
|
|
26 | + // `out` will be set to NULL in cases of error
|
|
27 | + bool convertGlyphToBitmapGlyph(FT_Glyph src, FT_Glyph* out);
|
|
28 | + FT_Bitmap convertBitmapTo8Bpp(FT_Bitmap* bitmap);
|
|
29 | + QImage* convertBitmapToQImage(FT_Bitmap* src);
|
|
30 | + QImage* convertGlyphToQImage(FT_Glyph src,
|
|
31 | + QRect* outRect,
|
|
32 | + bool inverseRectY);
|
|
33 | + QPoint computeGlyphOffset(FT_Glyph glyph, bool inverseY);
|
|
34 | + |
|
35 | + /*
|
|
36 | + * Directly render the glyph at the specified index
|
|
37 | + * to a `QImage`. If you want to perform color-layer
|
|
38 | + * rendering, call this before trying to load the
|
|
39 | + * glyph and do normal rendering, If the returning
|
|
40 | + * value is non-NULL, then there's no need to
|
|
41 | + * load the glyph the normal way, just draw the `QImage`.
|
|
42 | + * Will return NULL if not enabled or color layers not available.
|
|
43 | + */
|
|
44 | + QImage* tryDirectRenderColorLayers(int glyphIndex,
|
|
45 | + QRect* outRect,
|
|
46 | + bool inverseRectY = false);
|
|
47 | + |
|
48 | +private:
|
|
49 | + Engine* engine_;
|
|
50 | + |
|
51 | + QRgb backgroundColor_;
|
|
52 | + QRgb foregroundColor_;
|
|
53 | + QVector<QRgb> foregroundTable_;
|
|
54 | +};
|
|
55 | + |
|
56 | +// end of rendering.hpp |
... | ... | @@ -565,7 +565,8 @@ StringRenderer::renderLine(int x, |
565 | 565 | |
566 | 566 | QRect rect;
|
567 | 567 | QImage* colorLayerImage
|
568 | - = engine_->tryDirectRenderColorLayers(ctx.glyphIndex, &rect, true);
|
|
568 | + = engine_->renderingEngine()->tryDirectRenderColorLayers(ctx.glyphIndex,
|
|
569 | + &rect, true);
|
|
569 | 570 | |
570 | 571 | if (colorLayerImage)
|
571 | 572 | {
|
... | ... | @@ -30,10 +30,12 @@ GlyphBitmap::GlyphBitmap(int glyphIndex, |
30 | 30 | Engine* engine)
|
31 | 31 | {
|
32 | 32 | QRect bRect;
|
33 | - image_ = engine->tryDirectRenderColorLayers(glyphIndex, &bRect, true);
|
|
33 | + image_ = engine->renderingEngine()->tryDirectRenderColorLayers(glyphIndex,
|
|
34 | + &bRect, true);
|
|
34 | 35 | |
35 | 36 | if (!image_)
|
36 | - image_ = engine->convertGlyphToQImage(glyph, &bRect, true);
|
|
37 | + image_ = engine->renderingEngine()->convertGlyphToQImage(glyph, &bRect,
|
|
38 | + true);
|
|
37 | 39 | boundingRect_ = bRect; // QRect to QRectF
|
38 | 40 | }
|
39 | 41 |
... | ... | @@ -109,7 +109,7 @@ void |
109 | 109 | GlyphContinuous::paintEvent(QPaintEvent* event)
|
110 | 110 | {
|
111 | 111 | QPainter painter(this);
|
112 | - painter.fillRect(rect(), engine_->background());
|
|
112 | + painter.fillRect(rect(), engine_->renderingEngine()->background());
|
|
113 | 113 | |
114 | 114 | if (glyphCache_.empty())
|
115 | 115 | fillCache();
|
... | ... | @@ -423,7 +423,9 @@ GlyphContinuous::saveSingleGlyph(FT_Glyph glyph, |
423 | 423 | return;
|
424 | 424 | |
425 | 425 | QRect rect;
|
426 | - QImage* image = engine_->convertGlyphToQImage(glyph, &rect, true);
|
|
426 | + QImage* image = engine_->renderingEngine()->convertGlyphToQImage(glyph,
|
|
427 | + &rect,
|
|
428 | + true);
|
|
427 | 429 | saveSingleGlyphImage(image, rect, penPos, glyph->advance, gctx);
|
428 | 430 | }
|
429 | 431 |
... | ... | @@ -29,6 +29,7 @@ if qt5_dep.found() |
29 | 29 | 'engine/fontinfo.cpp',
|
30 | 30 | 'engine/fontinfonamesmapping.cpp',
|
31 | 31 | 'engine/mmgx.cpp',
|
32 | + 'engine/rendering.cpp',
|
|
32 | 33 | 'engine/renderutils.cpp',
|
33 | 34 | |
34 | 35 | 'glyphcomponents/glyphbitmap.cpp',
|
... | ... | @@ -369,8 +369,8 @@ SettingPanel::syncSettings() |
369 | 369 | antiAliasingComboBox_->currentIndex()
|
370 | 370 | == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel);
|
371 | 371 | |
372 | - engine_->setForeground(foregroundColor_.rgba());
|
|
373 | - engine_->setBackground(backgroundColor_.rgba());
|
|
372 | + engine_->renderingEngine()->setForeground(foregroundColor_.rgba());
|
|
373 | + engine_->renderingEngine()->setBackground(backgroundColor_.rgba());
|
|
374 | 374 | mmgxPanel_->syncSettings();
|
375 | 375 | }
|
376 | 376 |
... | ... | @@ -87,7 +87,8 @@ SingularTab::drawGlyph() |
87 | 87 | currentGlyphPointNumbersItem_ = NULL;
|
88 | 88 | }
|
89 | 89 | |
90 | - glyphView_->setBackgroundBrush(QColor(engine_->background()));
|
|
90 | + glyphView_->setBackgroundBrush(
|
|
91 | + QColor(engine_->renderingEngine()->background()));
|
|
91 | 92 | |
92 | 93 | syncSettings();
|
93 | 94 | FT_Glyph glyph = engine_->loadGlyph(currentGlyphIndex_);
|