Implement Level 3.5 bold and italic font style

Proportional font attributes are tracked within the decoder, but the effect
is not rendered.
This commit is contained in:
G.K.MacGregor
2023-05-21 15:33:21 +01:00
parent ed821bde45
commit 4b6ea4398d
4 changed files with 166 additions and 4 deletions

View File

@@ -113,6 +113,7 @@ void TeletextPageDecode::Invocation::buildMap(int level)
case 0x23: // Background colour
case 0x27: // Additional flash functions
case 0x2c: // Display attributes
case 0x2e: // Font style
m_attributeMap.insert(qMakePair(targetRow, targetColumn), triplet);
m_rightMostColumn.insert(targetRow, targetColumn);
break;
@@ -143,8 +144,15 @@ TeletextPageDecode::textPainter TeletextPageDecode::s_blankPainter;
TeletextPageDecode::TeletextPageDecode()
{
if (s_instances == 0) {
for (int c=0; c<72; c++)
for (int c=0; c<72; c++) {
s_blankPainter.bottomHalfCell[c].character.code = 0x00;
s_blankPainter.setProportionalRows[c] = 0;
s_blankPainter.clearProportionalRows[c] = 0;
s_blankPainter.setBoldRows[c] = 0;
s_blankPainter.clearBoldRows[c] = 0;
s_blankPainter.setItalicRows[c] = 0;
s_blankPainter.clearItalicRows[c] = 0;
}
s_blankPainter.rightHalfCell.character.code = 0x00;
}
s_instances++;
@@ -495,6 +503,7 @@ void TeletextPageDecode::decodeRow(int r)
bool adapBackground = false;
bool adapFlash = false;
bool adapDisplayAttrs = false;
bool adapStyle = false;
for (int c=0; c<72; c++) {
textCell previousCellContents = m_cell[r][c];
@@ -512,6 +521,9 @@ void TeletextPageDecode::decodeRow(int r)
m_level1ActivePainter.attribute.display.conceal = false;
m_level1ActivePainter.attribute.display.invert = false;
m_level1ActivePainter.attribute.display.underlineSeparated = false;
m_level1ActivePainter.attribute.style.proportional = false;
m_level1ActivePainter.attribute.style.bold = false;
m_level1ActivePainter.attribute.style.italic = false;
if (m_level >= 2) {
m_level1ActivePainter.attribute.foregroundCLUT = 7 | m_foregroundRemap[m_levelOnePage->colourTableRemap()];
@@ -599,6 +611,41 @@ void TeletextPageDecode::decodeRow(int r)
painter = (t == 0) ? &m_level1ActivePainter : &m_adapPassPainter[t-1][i];
if (m_level == 3) {
// Reset font style "row spread" at start of row and side panels
if (c == 0 || c == 40 || c == 56)
painter->styleSpreadRows = 0;
// Apply any font style attributes from previous rows
// For m_level1ActivePainter, ensure we deal with font style row counters only once
if (t >= 1 || i == 0) {
if (painter->clearProportionalRows[c] != 0) {
painter->attribute.style.proportional = false;
painter->clearProportionalRows[c]--;
}
if (painter->setProportionalRows[c] != 0) {
painter->attribute.style.proportional = true;
painter->setProportionalRows[c]--;
}
if (painter->clearBoldRows[c] != 0) {
painter->attribute.style.bold = false;
painter->clearBoldRows[c]--;
}
if (painter->setBoldRows[c] != 0) {
painter->attribute.style.bold = true;
painter->setBoldRows[c]--;
}
if (painter->clearItalicRows[c] != 0) {
painter->attribute.style.italic = false;
painter->clearItalicRows[c]--;
}
if (painter->setItalicRows[c] != 0) {
painter->attribute.style.italic = true;
painter->setItalicRows[c]--;
}
}
}
// Adaptive Invocation painter: pick up the attributes we're NOT adapting from
// m_level1ActivePainter, which by now has taken into account all the attributes
// from the Level 1 page, Active Objects and the Local Enhancement Data
@@ -611,6 +658,8 @@ void TeletextPageDecode::decodeRow(int r)
painter->attribute.flash = m_level1ActivePainter.attribute.flash;
if (!adapDisplayAttrs)
painter->attribute.display = m_level1ActivePainter.attribute.display;
if (!adapStyle)
painter->attribute.style = m_level1ActivePainter.attribute.style;
}
// QMultiMap::values returns QList with most recent value first...
@@ -668,10 +717,36 @@ void TeletextPageDecode::decodeRow(int r)
if (t == 0 && !painter->attribute.display.underlineSeparated)
level1SeparatedMosaics = false;
break;
case 0x2e: // Font style
if (m_level != 3)
break;
if (applyAdapt)
adapStyle = true;
painter->attribute.style.proportional = triplet.data() & 0x01;
painter->attribute.style.bold = triplet.data() & 0x02;
painter->attribute.style.italic = triplet.data() & 0x04;
painter->styleSpreadRows = triplet.data() >> 4;
break;
}
}
painter->result.attribute = painter->attribute;
// Font style attribute that spreads across more than one row
if (m_level == 3 && painter->styleSpreadRows != 0) {
if (painter->attribute.style.proportional)
painter->setProportionalRows[c] = painter->styleSpreadRows;
else
painter->clearProportionalRows[c] = painter->styleSpreadRows;
if (painter->attribute.style.bold)
painter->setBoldRows[c] = painter->styleSpreadRows;
else
painter->clearBoldRows[c] = painter->styleSpreadRows;
if (painter->attribute.style.italic)
painter->setItalicRows[c] = painter->styleSpreadRows;
else
painter->clearItalicRows[c] = painter->styleSpreadRows;
}
}
}
@@ -875,7 +950,7 @@ void TeletextPageDecode::decodeRow(int r)
m_adapPassPainter[0][adapInvokeAttrs].attribute.display.doubleHeight = false;
m_adapPassPainter[0][adapInvokeAttrs].attribute.display.doubleWidth = false;
adapInvokeAttrs = -1;
adapForeground = adapBackground = adapFlash = adapDisplayAttrs = false;
adapForeground = adapBackground = adapFlash = adapDisplayAttrs = adapStyle = false;
}
// Level 1 set-after spacing attributes

View File

@@ -58,6 +58,9 @@ public:
bool cellBoxed(int r, int c) const { return m_cell[r][c].attribute.display.boxingWindow; };
bool cellConceal(int r, int c) const { return m_cell[r][c].attribute.display.conceal; };
bool cellUnderlined(int r, int c) const { return cellCharacterSet(r, c) < 24 ? m_cell[r][c].attribute.display.underlineSeparated : false; };
bool cellBold(int r, int c) const { return m_cell[r][c].attribute.style.bold; };
bool cellItalic(int r, int c) const { return m_cell[r][c].attribute.style.italic; };
bool cellProportional(int r, int c) const { return m_cell[r][c].attribute.style.proportional; };
bool level1MosaicAttribute(int r, int c) const { return m_cellLevel1Mosaic[r][c]; };
int level1CharSet(int r, int c) const { return m_cellLevel1CharSet[r][c]; };
@@ -109,7 +112,7 @@ private:
int mode=0;
int ratePhase=0;
int phase2HzShown=0;
} flash;
};
struct displayAttributes {
bool doubleHeight=false;
@@ -120,6 +123,12 @@ private:
bool underlineSeparated=false;
};
struct fontStyle {
bool proportional=false;
bool bold=false;
bool italic=false;
};
friend inline bool operator!=(const displayAttributes &lhs, const displayAttributes &rhs)
{
return lhs.doubleHeight != rhs.doubleHeight ||
@@ -135,6 +144,7 @@ private:
int backgroundCLUT=0;
flashFunctions flash;
displayAttributes display;
fontStyle style;
};
friend inline bool operator!=(const textAttributes &lhs, const textAttributes &rhs)
@@ -144,7 +154,10 @@ private:
lhs.flash.mode != rhs.flash.mode ||
lhs.flash.ratePhase != rhs.flash.ratePhase ||
lhs.flash.phase2HzShown != rhs.flash.phase2HzShown ||
lhs.display != rhs.display;
lhs.display != rhs.display ||
lhs.style.proportional != rhs.style.proportional ||
lhs.style.bold != rhs.style.bold ||
lhs.style.italic != rhs.style.italic;
}
struct textCell {
@@ -165,6 +178,11 @@ private:
textCell result;
textCell rightHalfCell;
textCell bottomHalfCell[72];
int styleSpreadRows=0;
int setProportionalRows[72], clearProportionalRows[72];
int setBoldRows[72], clearBoldRows[72];
int setItalicRows[72], clearItalicRows[72];
};
const QMap<int, int> m_g0CharacterMap {

View File

@@ -73,6 +73,39 @@ void TeletextPageRender::setDecoder(TeletextPageDecode *decoder)
m_decoder = decoder;
}
inline void TeletextPageRender::drawFromBitmap(QPainter &pixmapPainter, int r, int c, const QBitmap bitmap, TeletextPageDecode::CharacterFragment characterFragment)
{
switch (characterFragment) {
case TeletextPageDecode::NormalSize:
pixmapPainter.drawPixmap(c*12, r*10, bitmap);
break;
case TeletextPageDecode::DoubleHeightTopHalf:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 0, 0, 12, 5);
break;
case TeletextPageDecode::DoubleHeightBottomHalf:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 0, 5, 12, 5);
break;
case TeletextPageDecode::DoubleWidthLeftHalf:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 0, 0, 6, 10);
break;
case TeletextPageDecode::DoubleWidthRightHalf:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 6, 0, 6, 10);
break;
case TeletextPageDecode::DoubleSizeTopLeftQuarter:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 0, 0, 6, 5);
break;
case TeletextPageDecode::DoubleSizeTopRightQuarter:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 6, 0, 6, 5);
break;
case TeletextPageDecode::DoubleSizeBottomLeftQuarter:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 0, 5, 6, 5);
break;
case TeletextPageDecode::DoubleSizeBottomRightQuarter:
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, bitmap, 6, 5, 6, 5);
break;
}
}
inline void TeletextPageRender::drawFromFontBitmap(QPainter &pixmapPainter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment)
{
switch (characterFragment) {
@@ -152,6 +185,8 @@ inline void TeletextPageRender::drawCharacter(QPainter &pixmapPainter, int r, in
pixmapPainter.fillRect(c*12, r*10, 12, 10, pixmapPainter.background().color());
else if (characterCode == 0x7f && characterSet == 24)
pixmapPainter.fillRect(c*12, r*10, 12, 10, pixmapPainter.pen().color());
else if ((m_decoder->cellBold(r, c) || m_decoder->cellItalic(r, c)) && characterSet < 24)
drawBoldOrItalicCharacter(pixmapPainter, r, c, characterCode, characterSet, characterFragment);
else
drawFromFontBitmap(pixmapPainter, r, c, characterCode, characterSet, characterFragment);
@@ -182,6 +217,38 @@ inline void TeletextPageRender::drawCharacter(QPainter &pixmapPainter, int r, in
pixmapPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
}
inline void TeletextPageRender::drawBoldOrItalicCharacter(QPainter &pixmapPainter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment)
{
QBitmap bitmap = QBitmap(12, 10);
QPainter bitmapPainter;
// TODO italic glyph-making is VERY slow!
if (m_decoder->cellItalic(r, c)) {
bitmap.clear();
bitmapPainter.begin(&bitmap);
bitmapPainter.setBackgroundMode(Qt::OpaqueMode);
bitmapPainter.drawPixmap(1, 0, *m_fontBitmap.rawBitmap(), (characterCode-32)*12, characterSet*10, 11, 3);
bitmapPainter.drawPixmap(0, 3, *m_fontBitmap.rawBitmap(), (characterCode-32)*12, characterSet*10+3, 12, 3);
bitmapPainter.drawPixmap(0, 6, *m_fontBitmap.rawBitmap(), (characterCode-32)*12+1, characterSet*10+6, 11, 4);
bitmapPainter.end();
} else
bitmap = m_fontBitmap.rawBitmap()->copy((characterCode-32)*12, characterSet*10, 12, 10);
if (m_decoder->cellBold(r, c)) {
QBitmap boldeningBitmap;
boldeningBitmap = bitmap.copy();
bitmapPainter.begin(&bitmap);
// No idea why we need this setPen workaround when character is made italic first?!
if (!m_decoder->cellItalic(r, c))
bitmapPainter.setPen(Qt::color0);
bitmapPainter.drawPixmap(1, 0, boldeningBitmap);
bitmapPainter.end();
}
drawFromBitmap(pixmapPainter, r, c, bitmap, characterFragment);
}
void TeletextPageRender::renderPage(bool force)
{
for (int r=0; r<25; r++)

View File

@@ -70,8 +70,10 @@ protected:
int m_flashingRow[25];
private:
inline void drawFromBitmap(QPainter &, int, int, const QBitmap, TeletextPageDecode::CharacterFragment);
inline void drawFromFontBitmap(QPainter &, int, int, unsigned char, int, TeletextPageDecode::CharacterFragment);
inline void drawCharacter(QPainter &, int, int, unsigned char, int, int, TeletextPageDecode::CharacterFragment);
inline void drawBoldOrItalicCharacter(QPainter &, int, int, unsigned char, int, TeletextPageDecode::CharacterFragment);
void renderRow(int, int, bool force=false);
void setRowFlashStatus(int, int);