From 4b6ea4398d901f81f58fcf81716ce80c7f5fccf6 Mon Sep 17 00:00:00 2001 From: "G.K.MacGregor" Date: Sun, 21 May 2023 15:33:21 +0100 Subject: [PATCH] Implement Level 3.5 bold and italic font style Proportional font attributes are tracked within the decoder, but the effect is not rendered. --- decode.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- decode.h | 22 +++++++++++++-- render.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++ render.h | 2 ++ 4 files changed, 166 insertions(+), 4 deletions(-) diff --git a/decode.cpp b/decode.cpp index c665c72..f522f1e 100644 --- a/decode.cpp +++ b/decode.cpp @@ -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 diff --git a/decode.h b/decode.h index 1870e0f..1f9c498 100644 --- a/decode.h +++ b/decode.h @@ -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 m_g0CharacterMap { diff --git a/render.cpp b/render.cpp index 20cc23a..87cc7d3 100644 --- a/render.cpp +++ b/render.cpp @@ -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++) diff --git a/render.h b/render.h index 2440274..84b3350 100644 --- a/render.h +++ b/render.h @@ -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);