494 lines
24 KiB
C++
494 lines
24 KiB
C++
#include "textSubtitlesRender.h"
|
|
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
#include "math.h"
|
|
#include "memory.h"
|
|
#include "stdio.h"
|
|
#include "utf8Converter.h"
|
|
#include "vodCoreException.h"
|
|
#include "vod_common.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace
|
|
{
|
|
const std::unordered_map<std::string, uint32_t> defaultPallette = {make_pair("black", 0x000000),
|
|
make_pair("aqua", 0x00ffff),
|
|
make_pair("red", 0xff0000),
|
|
make_pair("green", 0x008000),
|
|
make_pair("blue", 0x0000ff),
|
|
make_pair("fuchsia", 0xff00ff),
|
|
make_pair("gray", 0x808080),
|
|
make_pair("lime", 0x00ff00),
|
|
make_pair("maroon", 0x800000),
|
|
make_pair("navy", 0x000080),
|
|
make_pair("olive", 0x808000),
|
|
make_pair("purple", 0x800080),
|
|
make_pair("silver", 0xc0c0c0),
|
|
make_pair("tea", 0x008080),
|
|
make_pair("white", 0xffffff),
|
|
make_pair("yellow", 0xffff00),
|
|
make_pair("orange", 0xffa500),
|
|
make_pair("violet", 0xEE82EE),
|
|
|
|
make_pair("aliceblue", 0xf0f8ff),
|
|
make_pair("antiquewhite", 0xfaebd7),
|
|
make_pair("aquamarine", 0x7fffd4),
|
|
make_pair("azure", 0xf0ffff),
|
|
make_pair("beige", 0xf5f5dc),
|
|
make_pair("bisque", 0xffe4c4),
|
|
make_pair("blanchedalmond", 0xffebcd),
|
|
make_pair("blueviolet", 0x8a2be2),
|
|
make_pair("brown", 0xa52a2a),
|
|
make_pair("burlywood", 0xdeb887),
|
|
make_pair("cadetblue", 0x5f9ea0),
|
|
make_pair("chartreuse", 0x7fff00),
|
|
make_pair("chocolate", 0xd2691e),
|
|
make_pair("cora", 0xff7f50),
|
|
make_pair("cornflowerblue", 0x6495ed),
|
|
make_pair("cornsilk", 0xfff8dc),
|
|
make_pair("crimson", 0xdc143c),
|
|
make_pair("darkblue", 0x00008b),
|
|
make_pair("darkcyan", 0x008b8b),
|
|
make_pair("darkgoldenrod", 0xb8860b),
|
|
make_pair("darkgray", 0xa9a9a9),
|
|
make_pair("darkgreen", 0x006400),
|
|
make_pair("darkkhaki", 0xbdb76b),
|
|
make_pair("darkmagenta", 0x8b008b),
|
|
make_pair("darkolivegreen", 0x556b2f),
|
|
make_pair("darkorange", 0xff8c00),
|
|
make_pair("darkorchid", 0x9932cc),
|
|
make_pair("darkred", 0x8b0000),
|
|
make_pair("darksalmon", 0xe9967a),
|
|
make_pair("darkseagreen", 0x8fbc8f),
|
|
make_pair("darkslateblue", 0x483d8b),
|
|
make_pair("darkslategray", 0x2f4f4f),
|
|
make_pair("darkturquoise", 0x00ced1),
|
|
make_pair("darkviolet", 0x9400d3),
|
|
make_pair("deeppink", 0xff1493),
|
|
make_pair("deepskyblue", 0x00bfff),
|
|
make_pair("dimgray", 0x696969),
|
|
make_pair("dodgerblue", 0x1e90ff),
|
|
make_pair("firebrick", 0xb22222),
|
|
make_pair("floralwhite", 0xfffaf0),
|
|
make_pair("forestgreen", 0x228b22),
|
|
make_pair("gainsboro", 0xdcdcdc),
|
|
make_pair("ghostwhite", 0xf8f8ff),
|
|
make_pair("gold", 0xffd700),
|
|
make_pair("goldenrod", 0xdaa520),
|
|
make_pair("greenyellow", 0xadff2f),
|
|
make_pair("honeydew", 0xf0fff0),
|
|
make_pair("hotpink", 0xff69b4),
|
|
make_pair("indianred", 0xcd5c5c),
|
|
make_pair("indigo", 0x4b0082),
|
|
make_pair("ivory", 0xfffff0),
|
|
make_pair("khaki", 0xf0e68c),
|
|
make_pair("lavender", 0xe6e6fa),
|
|
make_pair("lavenderblush", 0xfff0f5),
|
|
make_pair("lawngreen", 0x7cfc00),
|
|
make_pair("lemonchiffon", 0xfffacd),
|
|
make_pair("lightblue", 0xadd8e6),
|
|
make_pair("lightcora", 0xf08080),
|
|
make_pair("lightcyan", 0xe0ffff),
|
|
make_pair("lightgoldenrodyellow", 0xfafad2),
|
|
make_pair("lightgreen", 0x90ee90),
|
|
make_pair("lightgrey", 0xd3d3d3),
|
|
make_pair("lightpink", 0xffb6c1),
|
|
make_pair("lightsalmon", 0xffa07a),
|
|
make_pair("lightseagreen", 0x20b2aa),
|
|
make_pair("lightskyblue", 0x87cefa),
|
|
make_pair("lightslategray", 0x778899),
|
|
make_pair("lightsteelblue", 0xb0c4de),
|
|
make_pair("lightyellow", 0xffffe0),
|
|
make_pair("limegreen", 0x32cd32),
|
|
make_pair("linen", 0xfaf0e6),
|
|
make_pair("magenta", 0xff00ff),
|
|
make_pair("mediumauqamarine", 0x66cdaa),
|
|
make_pair("mediumblue", 0x0000cd),
|
|
make_pair("mediumorchid", 0xba55d3),
|
|
make_pair("mediumpurple", 0x9370d8),
|
|
make_pair("mediumseagreen", 0x3cb371),
|
|
make_pair("mediumslateblue", 0x7b68ee),
|
|
make_pair("mediumspringgreen", 0x00fa9a),
|
|
make_pair("mediumturquoise", 0x48d1cc),
|
|
make_pair("mediumvioletred", 0xc71585),
|
|
make_pair("midnightblue", 0x191970),
|
|
make_pair("mintcream", 0xf5fffa),
|
|
make_pair("mistyrose", 0xffe4e1),
|
|
make_pair("moccasin", 0xffe4b5),
|
|
make_pair("navajowhite", 0xffdead),
|
|
make_pair("oldlace", 0xfdf5e6),
|
|
make_pair("olivedrab", 0x688e23),
|
|
make_pair("orangered", 0xff4500),
|
|
make_pair("orchid", 0xda70d6),
|
|
make_pair("palegoldenrod", 0xeee8aa),
|
|
make_pair("palegreen", 0x98fb98),
|
|
make_pair("paleturquoise", 0xafeeee),
|
|
make_pair("palevioletred", 0xd87093),
|
|
make_pair("papayawhip", 0xffefd5),
|
|
make_pair("peachpuff", 0xffdab9),
|
|
make_pair("peru", 0xcd853f),
|
|
make_pair("pink", 0xffc0cb),
|
|
make_pair("plum", 0xdda0dd),
|
|
make_pair("powderblue", 0xb0e0e6),
|
|
make_pair("rosybrown", 0xbc8f8f),
|
|
make_pair("royalblue", 0x4169e1),
|
|
make_pair("saddlebrown", 0x8b4513),
|
|
make_pair("salmon", 0xfa8072),
|
|
make_pair("sandybrown", 0xf4a460),
|
|
make_pair("seagreen", 0x2e8b57),
|
|
make_pair("seashel", 0xfff5ee),
|
|
make_pair("sienna", 0xa0522d),
|
|
make_pair("skyblue", 0x87ceeb),
|
|
make_pair("slateblue", 0x6a5acd),
|
|
make_pair("slategray", 0x708090),
|
|
make_pair("snow", 0xfffafa),
|
|
make_pair("springgreen", 0x00ff7f),
|
|
make_pair("steelblue", 0x4682b4),
|
|
make_pair("tan", 0xd2b48c),
|
|
make_pair("thistle", 0xd8bfd8),
|
|
make_pair("tomato", 0xff6347),
|
|
make_pair("turquoise", 0x40e0d0),
|
|
make_pair("wheat", 0xf5deb3),
|
|
make_pair("whitesmoke", 0xf5f5f5),
|
|
make_pair("yellowgreen", 0x9acd32)};
|
|
|
|
size_t findUnquotedStr(const string& str, const string& substr)
|
|
{
|
|
if (substr.size() == 0)
|
|
return string::npos;
|
|
bool quote = false;
|
|
for (size_t i = 0; i < str.size(); i++)
|
|
{
|
|
if (str[i] == '\"' || str[i] == '\'')
|
|
quote = !quote;
|
|
else if (!quote && str[i] == substr[0])
|
|
{
|
|
bool found = true;
|
|
for (size_t j = 1; j < substr.size(); j++)
|
|
{
|
|
if (i + j >= str.size() || str[i + j] != substr[j])
|
|
{
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
return i;
|
|
}
|
|
}
|
|
return string::npos;
|
|
}
|
|
} // namespace
|
|
|
|
namespace text_subtitles
|
|
{
|
|
TextSubtitlesRender::TextSubtitlesRender() : m_width(0), m_height(0)
|
|
{
|
|
m_pData = 0;
|
|
m_borderWidth = 0;
|
|
}
|
|
|
|
TextSubtitlesRender::~TextSubtitlesRender() {}
|
|
|
|
string findFontArg(const string& text, size_t pos)
|
|
{
|
|
bool delFound = false;
|
|
size_t firstPos = 0;
|
|
for (size_t i = pos; i < text.size(); i++)
|
|
{
|
|
if (text[i] == '=')
|
|
delFound = true;
|
|
else if (delFound)
|
|
{
|
|
if (text[i] == ' ')
|
|
{
|
|
if (firstPos != 0)
|
|
return text.substr(firstPos, i - firstPos);
|
|
}
|
|
else if (firstPos == 0)
|
|
firstPos = i;
|
|
}
|
|
}
|
|
if (firstPos != 0)
|
|
return text.substr(firstPos, text.size() - firstPos);
|
|
else
|
|
return "";
|
|
}
|
|
|
|
uint32_t rgbSwap(uint32_t color)
|
|
{
|
|
auto rgb = (uint8_t*)&color;
|
|
uint8_t tmp = rgb[0];
|
|
rgb[0] = rgb[2];
|
|
rgb[2] = tmp;
|
|
return color;
|
|
}
|
|
|
|
int TextSubtitlesRender::browserSizeToRealSize(int bSize, double rSize)
|
|
{
|
|
if (bSize > DEFAULT_BROWSER_STYLE_FS)
|
|
for (int i = DEFAULT_BROWSER_STYLE_FS; i < bSize; i++) rSize *= BROWSER_FONT_STYLE_INC_COEFF;
|
|
else if (bSize < DEFAULT_BROWSER_STYLE_FS)
|
|
for (int i = bSize; i < DEFAULT_BROWSER_STYLE_FS; i++) rSize /= BROWSER_FONT_STYLE_INC_COEFF;
|
|
return (int)rSize;
|
|
}
|
|
|
|
vector<pair<Font, string>> TextSubtitlesRender::processTxtLine(const std::string& line, vector<Font>& fontStack)
|
|
{
|
|
if (fontStack.size() == 0)
|
|
{
|
|
fontStack.push_back(m_font);
|
|
fontStack[0].m_size = DEFAULT_BROWSER_STYLE_FS;
|
|
}
|
|
Font curFont = fontStack[fontStack.size() - 1];
|
|
|
|
vector<pair<Font, string>> rez;
|
|
size_t prevTextPos = 0;
|
|
size_t bStartPos = 0;
|
|
bool bStartFound = false;
|
|
for (size_t i = 0; i < line.size(); i++)
|
|
{
|
|
if (line[i] == '<')
|
|
{
|
|
bStartPos = i;
|
|
bStartFound = true;
|
|
}
|
|
else if (line[i] == '>' && bStartFound)
|
|
{
|
|
bool isTag = false;
|
|
bool endTag = false;
|
|
string tagStr = trimStr(line.substr(bStartPos + 1, i - bStartPos - 1));
|
|
string ltagStr = tagStr;
|
|
for (auto& j : ltagStr) j = (char)towlower(j);
|
|
if (ltagStr == "i" || ltagStr == "italic")
|
|
{
|
|
curFont.m_opts |= Font::ITALIC;
|
|
isTag = true;
|
|
}
|
|
else if (ltagStr == "/i" || ltagStr == "/italic")
|
|
{
|
|
endTag = true;
|
|
}
|
|
else if (ltagStr == "b" || ltagStr == "bold")
|
|
{
|
|
curFont.m_opts |= Font::BOLD;
|
|
isTag = true;
|
|
}
|
|
else if (ltagStr == "/b" || ltagStr == "/bold")
|
|
{
|
|
endTag = true;
|
|
}
|
|
else if (ltagStr == "u" || ltagStr == "underline")
|
|
{
|
|
curFont.m_opts |= Font::UNDERLINE;
|
|
isTag = true;
|
|
}
|
|
else if (ltagStr == "/u" || ltagStr == "underline")
|
|
{
|
|
endTag = true;
|
|
}
|
|
else if (ltagStr == "f" || ltagStr == "force")
|
|
{
|
|
curFont.m_opts |= Font::FORCED;
|
|
isTag = true;
|
|
}
|
|
else if (ltagStr == "/f" || ltagStr == "/force")
|
|
{
|
|
endTag = true;
|
|
}
|
|
else if (ltagStr == "strike")
|
|
{
|
|
curFont.m_opts |= Font::STRIKE_OUT;
|
|
isTag = true;
|
|
}
|
|
else if (ltagStr == "/strike")
|
|
{
|
|
endTag = true;
|
|
}
|
|
else if (strStartWith(ltagStr, "font "))
|
|
{
|
|
size_t fontNamePos = findUnquotedStr(ltagStr, "name"); // ltagStr.find("name");
|
|
if (fontNamePos == string::npos)
|
|
fontNamePos = findUnquotedStr(ltagStr, "face");
|
|
if (fontNamePos != string::npos)
|
|
curFont.m_name = unquoteStr(findFontArg(tagStr, fontNamePos /*, lastIndexPos*/));
|
|
|
|
size_t colorPos = findUnquotedStr(ltagStr, "color");
|
|
if (colorPos != string::npos)
|
|
{
|
|
auto arg = unquoteStr(findFontArg(ltagStr, colorPos));
|
|
auto it = defaultPallette.find(arg);
|
|
if (it != std::end(defaultPallette))
|
|
{
|
|
curFont.m_color = it->second;
|
|
if (arg.size() > 0 && (arg[0] == '#' || arg[0] == 'x'))
|
|
curFont.m_color = strToInt32u(arg.substr(1, 16384).c_str(), 16);
|
|
else if (arg.size() > 1 && (arg[0] == '0' || arg[1] == 'x'))
|
|
curFont.m_color = strToInt32u(arg.substr(2, 16384).c_str(), 16);
|
|
else
|
|
curFont.m_color = strToInt32u(arg.c_str(), 10);
|
|
}
|
|
if ((curFont.m_color & 0xff000000u) == 0)
|
|
curFont.m_color |= 0xff000000u;
|
|
}
|
|
size_t fontSizePos = findUnquotedStr(ltagStr, "size");
|
|
if (fontSizePos != string::npos)
|
|
{
|
|
string arg = unquoteStr(findFontArg(tagStr, fontSizePos));
|
|
if (arg.size() > 0)
|
|
{
|
|
if (arg[0] == '+' || arg[0] == '-')
|
|
curFont.m_size += strToInt32(arg.c_str(), 10);
|
|
else
|
|
curFont.m_size = strToInt32u(arg.c_str(), 10);
|
|
}
|
|
}
|
|
isTag = true;
|
|
}
|
|
else if (strStartWith(tagStr, "/font"))
|
|
{
|
|
endTag = true;
|
|
}
|
|
if (isTag || endTag)
|
|
{
|
|
if (bStartPos > prevTextPos)
|
|
{
|
|
string msg = line.substr(prevTextPos, bStartPos - prevTextPos);
|
|
rez.push_back(make_pair(fontStack[fontStack.size() - 1], msg));
|
|
}
|
|
if (isTag)
|
|
fontStack.push_back(curFont);
|
|
else if (fontStack.size() > 1)
|
|
{
|
|
fontStack.resize(fontStack.size() - 1);
|
|
curFont = fontStack[fontStack.size() - 1];
|
|
}
|
|
prevTextPos = i + 1;
|
|
}
|
|
bStartFound = false;
|
|
}
|
|
}
|
|
if (line.size() > (unsigned)prevTextPos)
|
|
rez.push_back(make_pair(curFont, line.substr(prevTextPos, line.size() - prevTextPos)));
|
|
double rSize = m_initFont.m_size;
|
|
for (auto& i : rez) i.first.m_size = browserSizeToRealSize(i.first.m_size, rSize);
|
|
return rez;
|
|
}
|
|
|
|
bool TextSubtitlesRender::rasterText(const std::string& text)
|
|
{
|
|
bool forced = false;
|
|
memset(m_pData, 0, (size_t)m_width * m_height * 4);
|
|
vector<Font> fontStack;
|
|
vector<string> lines = splitStr(text.c_str(), '\n');
|
|
int curY = 0;
|
|
m_initFont = m_font;
|
|
for (auto& i : lines)
|
|
{
|
|
vector<pair<Font, string>> txtParts = processTxtLine(i, fontStack);
|
|
for (auto& j : txtParts)
|
|
{
|
|
if (j.first.m_opts & Font::FORCED)
|
|
forced = true;
|
|
}
|
|
|
|
int ySize = 0;
|
|
int tWidth = 0;
|
|
int maxHeight = 0;
|
|
int maxBaseLine = 0;
|
|
vector<int> xSize;
|
|
for (auto& j : txtParts)
|
|
{
|
|
setFont(j.first);
|
|
SIZE mSize;
|
|
getTextSize(j.second, &mSize);
|
|
ySize = FFMAX(ySize, mSize.cy);
|
|
maxHeight = FFMAX(maxHeight, getLineSpacing());
|
|
maxBaseLine = FFMAX(maxBaseLine, getBaseline());
|
|
xSize.push_back(mSize.cx);
|
|
tWidth += mSize.cx;
|
|
}
|
|
int xOffs = (m_width - tWidth) / 2;
|
|
int curX = 0;
|
|
for (size_t j = 0; j < txtParts.size(); j++)
|
|
{
|
|
Font font(txtParts[j].first);
|
|
setFont(font);
|
|
|
|
RECT rect = {curX + xOffs, curY + (maxHeight - getLineSpacing()) - (maxBaseLine - getBaseline()), m_width,
|
|
m_height};
|
|
drawText(txtParts[j].second, &rect);
|
|
|
|
curX += xSize[j];
|
|
}
|
|
curY += (int)(ySize * m_font.m_lineSpacing);
|
|
}
|
|
flushRasterBuffer();
|
|
m_font = m_initFont;
|
|
|
|
return forced;
|
|
}
|
|
|
|
const static uint32_t BORDER_COLOR = 0xff020202;
|
|
const static uint32_t BORDER_COLOR_TMP = RGB(0x1, 0x1, 0x1);
|
|
|
|
inline void setBPoint(uint32_t* addr)
|
|
{
|
|
if (*addr == 0)
|
|
*addr = BORDER_COLOR_TMP;
|
|
}
|
|
|
|
void TextSubtitlesRender::addBorder(int borderWidth, uint8_t* data, int width, int height)
|
|
{
|
|
// add black border
|
|
for (int i = 0; i < borderWidth; ++i)
|
|
{
|
|
auto dst = (uint32_t*)data;
|
|
for (int y = 0; y < height; ++y)
|
|
{
|
|
for (int x = 0; x < width; ++x)
|
|
{
|
|
if (*dst != 0 && *dst != BORDER_COLOR_TMP)
|
|
{
|
|
if (x > 0)
|
|
{
|
|
setBPoint(dst - 1);
|
|
if (y > 0)
|
|
setBPoint(dst - 1 - width);
|
|
if (y < height - 1)
|
|
setBPoint(dst - 1 + width);
|
|
}
|
|
if (y > 0)
|
|
setBPoint(dst - width);
|
|
if (y < height - 1)
|
|
setBPoint(dst + width);
|
|
if (x < width - 1)
|
|
{
|
|
setBPoint(dst + 1);
|
|
if (y > 0)
|
|
setBPoint(dst + 1 - width);
|
|
if (y < height - 1)
|
|
setBPoint(dst + 1 + width);
|
|
}
|
|
}
|
|
dst++;
|
|
}
|
|
}
|
|
dst = (uint32_t*)data;
|
|
for (int y = 0; y < height; ++y)
|
|
for (int x = 0; x < width; ++x)
|
|
{
|
|
if (*dst == BORDER_COLOR_TMP)
|
|
*dst = BORDER_COLOR;
|
|
dst++;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
} // namespace text_subtitles
|