diff --git a/decode.cpp b/decode.cpp
index cb84155..8258e13 100644
--- a/decode.cpp
+++ b/decode.cpp
@@ -17,21 +17,145 @@
* along with QTeletextMaker. If not, see .
*/
-#include
-//#include
-#include
-#include
-#include
-
#include "decode.h"
+#include
+#include
+
+TeletextPageDecode::Invocation::Invocation()
+{
+ m_tripletList = nullptr;
+ m_startTripletNumber = 0;
+ m_endTripletNumber = -1;
+ m_originRow = 0;
+ m_originColumn = 0;
+ m_fullScreenCLUT = -1;
+}
+
+void TeletextPageDecode::Invocation::setTripletList(X26TripletList *tripletList)
+{
+ m_tripletList = tripletList;
+}
+
+void TeletextPageDecode::Invocation::setStartTripletNumber(int n)
+{
+ m_startTripletNumber = n;
+}
+
+void TeletextPageDecode::Invocation::setEndTripletNumber(int n)
+{
+ m_endTripletNumber = n;
+}
+
+void TeletextPageDecode::Invocation::setOrigin(int row, int column)
+{
+ m_originRow = row;
+ m_originColumn = column;
+}
+
+void TeletextPageDecode::Invocation::buildMap(int level)
+{
+ int endTripletNumber;
+
+ if (m_endTripletNumber == -1)
+ endTripletNumber = m_tripletList->size();
+ else
+ endTripletNumber = m_endTripletNumber;
+
+ m_characterMap.clear();
+ m_attributeMap.clear();
+ m_rightMostColumn.clear();
+ m_fullScreenCLUT = -1;
+ m_fullRowCLUTMap.clear();
+
+ for (int i=m_startTripletNumber; i<=endTripletNumber; i++) {
+ const X26Triplet triplet = m_tripletList->at(i);
+
+ if (triplet.error() != 0)
+ continue;
+
+ int targetRow, targetColumn;
+
+ if (level == 1) {
+ targetRow = m_originRow + triplet.activePositionRow1p5();
+ targetColumn = m_originColumn + triplet.activePositionColumn1p5();
+ } else {
+ targetRow = m_originRow + triplet.activePositionRow();
+ targetColumn = m_originColumn + triplet.activePositionColumn();
+ }
+
+ if (triplet.activePositionRow() == -1)
+ targetRow++;
+ if (triplet.activePositionColumn() == -1)
+ targetColumn++;
+
+ if (targetRow > 24 || targetColumn > 71)
+ continue;
+
+ switch (triplet.modeExt()) {
+ case 0x21: // G1 character
+ case 0x22: // G3 character at Level 1.5
+ case 0x29: // G0 character
+ case 0x2b: // G3 character at Level 2.5
+ case 0x2f: // G2 character
+ case 0x30 ... 0x3f: // G0 character with diacritical
+ m_characterMap.insert(qMakePair(targetRow, targetColumn), triplet);
+ // Store rightmost column in this row for Adaptive Object attribute tracking
+ // QMap stores one value per key, QMap::insert will replace the value if the key already exists
+ m_rightMostColumn.insert(targetRow, targetColumn);
+ break;
+ case 0x20: // Foreground colour
+ case 0x23: // Background colour
+ case 0x27: // Additional flash functions
+ case 0x2c: // Display attributes
+ m_attributeMap.insert(qMakePair(targetRow, targetColumn), triplet);
+ m_rightMostColumn.insert(targetRow, targetColumn);
+ break;
+ case 0x00: // Full screen colour
+ if ((triplet.data() & 0x60) != 0x00)
+ break;
+ m_fullScreenCLUT = triplet.data();
+ // Full Screen Colour triplet overrides both the X/28 Full Screen Colour AND Full Row Colour.
+ // For the latter, place a Full Row Colour "down to bottom" at the Active Position.
+ m_fullRowCLUTMap.insert(targetRow, X26Triplet(triplet.address(), triplet.mode(), triplet.data() | 0x60));
+ break;
+ case 0x01: // Full row colour
+ m_fullRowCLUTMap.insert(targetRow, triplet);
+ break;
+ case 0x07: // Address row 0
+ if (targetRow == 0)
+ m_fullRowCLUTMap.insert(targetRow, triplet);
+ break;
+ }
+ }
+}
+
+
+int TeletextPageDecode::s_instances = 0;
+
+TeletextPageDecode::textPainter TeletextPageDecode::s_blankPainter;
+
TeletextPageDecode::TeletextPageDecode()
{
+ if (s_instances == 0) {
+ for (int c=0; c<72; c++)
+ s_blankPainter.bottomHalfCell[c].character.code = 0x00;
+ s_blankPainter.rightHalfCell.character.code = 0x00;
+ }
+ s_instances++;
+
m_level = 0;
- for (int r=0; r<25; r++)
- for (int c=0; c<72; c++)
+ for (int r=0; r<25; r++) {
+ m_rowHeight[r] = NormalHeight;
+ for (int c=0; c<72; c++) {
+ if (c < 40) {
+ m_cellLevel1Mosaic[r][c] = false;
+ m_cellLevel1CharSet[r][c] = 0;
+ }
m_refresh[r][c] = true;
+ }
+ }
m_finalFullScreenColour = 0;
m_finalFullScreenQColor.setRgb(0, 0, 0);
@@ -40,16 +164,11 @@ TeletextPageDecode::TeletextPageDecode()
m_fullRowQColor[r].setRgb(0, 0, 0);
}
m_leftSidePanelColumns = m_rightSidePanelColumns = 0;
- m_textLayer.push_back(&m_level1Layer);
- m_textLayer.push_back(new EnhanceLayer);
}
TeletextPageDecode::~TeletextPageDecode()
{
- while (m_textLayer.size()>1) {
- delete m_textLayer.back();
- m_textLayer.pop_back();
- }
+ s_instances--;
}
void TeletextPageDecode::setRefresh(int r, int c, bool refresh)
@@ -60,7 +179,7 @@ void TeletextPageDecode::setRefresh(int r, int c, bool refresh)
void TeletextPageDecode::setTeletextPage(LevelOnePage *newCurrentPage)
{
m_levelOnePage = newCurrentPage;
- m_level1Layer.setTeletextPage(newCurrentPage);
+ m_localEnhancements.setTripletList(m_levelOnePage->enhancements());
updateSidePanels();
}
@@ -75,6 +194,7 @@ void TeletextPageDecode::setLevel(int level)
for (int c=0; c<72; c++)
m_refresh[r][c] = true;
+ updateSidePanels();
decodePage();
}
@@ -99,385 +219,773 @@ void TeletextPageDecode::updateSidePanels()
}
}
-void TeletextPageDecode::buildEnhanceMap(TextLayer *enhanceLayer, int tripletNumber)
+void TeletextPageDecode::buildInvocationList(Invocation &invocation, int objectType)
{
- bool terminatorFound=false;
- ActivePosition activePosition;
- const X26Triplet *x26Triplet;
- int originModifierR=0;
- int originModifierC=0;
+ if (invocation.tripletList()->isEmpty())
+ return;
- do {
- x26Triplet = &m_levelOnePage->enhancements()->at(tripletNumber);
- if (x26Triplet->isRowTriplet())
- // Row address group
- switch (x26Triplet->mode()) {
- case 0x00: // Full screen colour
- if (m_level >= 2 && ((x26Triplet->data() & 0x60) == 0x00) && !activePosition.isDeployed())
- enhanceLayer->setFullScreenColour(x26Triplet->data());
- break;
- case 0x01: // Full row colour
- if (m_level >= 2 && activePosition.setRow(x26Triplet->addressRow()) && ((x26Triplet->data() & 0x60) == 0x00 || (x26Triplet->data() & 0x60) == 0x60))
- enhanceLayer->setFullRowColour(activePosition.row(), x26Triplet->data() & 0x1f, (x26Triplet->data() & 0x60) == 0x60);
- break;
- case 0x04: // Set active position
- if (activePosition.setRow(x26Triplet->addressRow()) && m_level >= 2 && x26Triplet->data() < 40)
- activePosition.setColumn(x26Triplet->data());
- break;
- case 0x07: // Address row 0
- if (x26Triplet->address() == 0x3f && !activePosition.isDeployed()) {
- activePosition.setRow(0);
- activePosition.setColumn(8);
- if (m_level >= 2 && ((x26Triplet->data() & 0x60) == 0x00 || (x26Triplet->data() & 0x60) == 0x60))
- enhanceLayer->setFullRowColour(0, x26Triplet->data() & 0x1f, (x26Triplet->data() & 0x60) == 0x60);
- }
- break;
- case 0x10: // Origin modifier
- if (m_level >= 2 && (tripletNumber+1) < m_levelOnePage->enhancements()->size() && m_levelOnePage->enhancements()->at(tripletNumber+1).mode() >= 0x11 && m_levelOnePage->enhancements()->at(tripletNumber+1).mode() <= 0x13 && x26Triplet->address() >= 40 && x26Triplet->data() < 72) {
- originModifierR = x26Triplet->address()-40;
- originModifierC = x26Triplet->data();
- }
- break;
- case 0x11 ... 0x13: // Invoke Object
- if (m_level >= 2) {
- if ((x26Triplet->address() & 0x18) == 0x08) {
- // Local Object
- // Check if the pointer in the Invocation triplet is valid
- // Can't point to triplets 13-15; only triplets 0-12 per packet
- if ((x26Triplet->data() & 0x0f) > 12)
- break;
- int tripletPointer = ((x26Triplet->data() >> 4) | ((x26Triplet->address() & 1) << 3)) * 13 + (x26Triplet->data() & 0x0f);
- // Can't point to triplet beyond the end of the Local Enhancement Data
- if ((tripletPointer+1) >= m_levelOnePage->enhancements()->size())
- break;
- // Check if we're pointing to an actual Object Definition of the same type
- if ((x26Triplet->mode() | 0x04) != m_levelOnePage->enhancements()->at(tripletPointer).mode())
- break;
- // The Object Definition can't declare it's at triplet 13-15; only triplets 0-12 per packet
- if ((m_levelOnePage->enhancements()->at(tripletPointer).data() & 0x0f) > 12)
- break;
- // Check if the Object Definition triplet is where it declares it is
- if ((((m_levelOnePage->enhancements()->at(tripletPointer).data() >> 4) | ((m_levelOnePage->enhancements()->at(tripletPointer).address() & 1) << 3)) * 13 + (m_levelOnePage->enhancements()->at(tripletPointer).data() & 0x0f)) != tripletPointer)
- break;
- // Check if (sub)Object type can be invoked by Object type we're within
- if (enhanceLayer->objectType() >= (x26Triplet->mode() & 0x03))
- break;
- // Is the object required at the current presentation Level?
- if (m_level == 2 && (m_levelOnePage->enhancements()->at(tripletPointer).address() & 0x08) == 0x00)
- break;
- if (m_level == 3 && (m_levelOnePage->enhancements()->at(tripletPointer).address() & 0x10) == 0x00)
- break;
- EnhanceLayer *newLayer = new EnhanceLayer;
- m_textLayer.push_back(newLayer);
- newLayer->setObjectType(x26Triplet->mode() & 0x03);
- newLayer->setOrigin(enhanceLayer->originR() + activePosition.row() + originModifierR, enhanceLayer->originC() + activePosition.column() + originModifierC);
- buildEnhanceMap(newLayer, tripletPointer+1);
- } else
- qDebug("POP or GPOP");
- originModifierR = originModifierC = 0;
- }
- break;
- case 0x15 ... 0x17: // Define Object, also used as terminator
- terminatorFound = true;
- break;
- case 0x1f: // Terminator
- if (x26Triplet->address() == 63)
- terminatorFound = true;
- break;
+ int i;
+
+ for (i=invocation.startTripletNumber(); isize(); i++) {
+ const X26Triplet triplet = invocation.tripletList()->at(i);
+
+ if (triplet.modeExt() == 0x1f && triplet.address() == 63)
+ // Termination marker
+ break;
+ if (triplet.modeExt() >= 0x15 && triplet.modeExt() <= 0x17)
+ // Object Definition, also used as terminator
+ break;
+ if (m_level >= 2 && triplet.modeExt() >= 0x11 && triplet.modeExt() <= 0x13 && triplet.error() == 0) {
+ // Object Invocation
+ //TODO POP and GPOP objects
+ if (triplet.objectSource() != X26Triplet::LocalObject) {
+ qDebug("POP or GPOP");
+ continue;
}
- else {
- // Column address group
- bool columnTripletActioned = true;
- switch (x26Triplet->mode()) {
- // First we deal with column triplets that are also valid at Level 1.5
- case 0x0b: // G3 mosaic character at Level 2.5
- if (m_level <= 1)
- break;
- // fall-through
- case 0x02: // G3 mosaic character at Level 1.5
- case 0x0f: // G2 character
- case 0x10 ... 0x1f: // Diacritical mark
- if (activePosition.setColumn(x26Triplet->addressColumn()) && x26Triplet->data() >= 0x20)
- enhanceLayer->enhanceMap.insert(qMakePair(activePosition.row(), activePosition.column()), qMakePair(x26Triplet->mode() | 0x20, x26Triplet->data()));
- break;
- // Make sure that PDC and reserved triplets don't affect the Active Position
- case 0x04 ... 0x06: // 0x04 and 0x05 are reserved, 0x06 for PDC
- case 0x0a: // Reserved
- break;
- default:
- columnTripletActioned = false;
+
+ // Check if (sub)Object type can be invoked by Object type we're within
+ if (triplet.modeExt() - 0x11 <= objectType)
+ continue;
+
+ // See if Object Definition is required at selected level
+ if (m_level == 2 && (invocation.tripletList()->at(triplet.objectLocalIndex()).address() & 0x08) == 0x00)
+ continue;
+ if (m_level == 3 && (invocation.tripletList()->at(triplet.objectLocalIndex()).address() & 0x10) == 0x00)
+ continue;
+
+ // Work out the absolute position where the Object is invoked
+ int originRow = invocation.originRow() + triplet.activePositionRow();
+ int originColumn = invocation.originColumn() + triplet.activePositionColumn();
+ // -1, -1 happens if Object is invoked before the Active Position is deployed
+ if (triplet.activePositionRow() == -1)
+ originRow++;
+ if (triplet.activePositionColumn() == -1)
+ originColumn++;
+ // Use Origin Modifier in previous triplet if there's one there
+ if (i > 0 && invocation.tripletList()->at(i-1).modeExt() == 0x10) {
+ originRow += invocation.tripletList()->at(i-1).address()-40;
+ originColumn += invocation.tripletList()->at(i-1).data();
}
- // All remaining possible column triplets at Level 2.5 affect the Active Position
- if (m_level >= 2 && !columnTripletActioned && activePosition.setColumn(x26Triplet->addressColumn()))
- enhanceLayer->enhanceMap.insert(qMakePair(activePosition.row(), activePosition.column()), qMakePair(x26Triplet->mode() | 0x20, x26Triplet->data()));
+
+ // Add the Invocation to the list, and recurse
+ Invocation newInvocation;
+ const int newObjectType = triplet.modeExt() - 0x11;
+
+ newInvocation.setTripletList(invocation.tripletList());
+ newInvocation.setStartTripletNumber(triplet.objectLocalIndex()+1);
+ newInvocation.setOrigin(originRow, originColumn);
+ m_invocations[newObjectType].append(newInvocation);
+ buildInvocationList(m_invocations[newObjectType].last(), newObjectType);
}
- tripletNumber++;
- } while (!terminatorFound && tripletNumber < m_levelOnePage->enhancements()->size());
+ }
+
+ invocation.setEndTripletNumber(i-1);
+ invocation.buildMap(m_level);
+}
+
+TeletextPageDecode::textCharacter TeletextPageDecode::characterFromTriplets(const QList triplets, int g0CharSet)
+{
+ textCharacter result;
+ result.code = 0x00;
+
+ // QMultiMap::values returns a QList with the most recently inserted value sorted first,
+ // so do the loop backwards to iterate from least to most recent value
+ for (int a=triplets.size()-1; a>=0; a--) {
+ const X26Triplet triplet = triplets.at(a);
+
+ if (triplet.data() < 0x20)
+ continue;
+
+ const unsigned char charCode = triplet.data();
+
+ // Deal with Level 1.5 valid characters first
+ switch (triplet.modeExt()) {
+ case 0x22: // G3 character at Level 1.5
+ result = { charCode, 26, 0 };
+ break;
+ case 0x2f: // G2 character
+ result.code = charCode;
+ // Duplicated from decodePage
+ switch (m_level1DefaultCharSet) {
+ case 1:
+ case 2:
+ case 3:
+ // Cyrillic G2
+ result.set = 8;
+ break;
+ case 4:
+ // Greek G2
+ result.set = 9;
+ break;
+ case 5:
+ // Arabic G2
+ result.set = 10;
+ break;
+ default:
+ // Latin G2
+ result.set = 7;
+ break;
+ }
+ result.diacritical = 0;
+ break;
+ case 0x30 ... 0x3f: // G0 character with diacritical
+ result = { charCode, g0CharSet, triplet.mode() & 0xf };
+ break;
+ }
+
+ if (m_level == 1)
+ continue;
+
+ // Now deal with Level 2.5 characters
+ switch (triplet.modeExt()) {
+ case 0x21: // G1 character
+ result.code = charCode;
+ if (triplet.data() & 0x20)
+ result.set = 24;
+ else
+ result.set = g0CharSet;
+ result.diacritical = 0;
+ break;
+ case 0x29: // G0 character
+ result = { charCode, g0CharSet, 0 };
+ break;
+ case 0x2b: // G3 character at Level 2.5
+ result = { charCode, 26, 0 };
+ break;
+ }
+ }
+
+ return result;
}
void TeletextPageDecode::decodePage()
{
- int currentFullRowColour, downwardsFullRowColour;
- int renderedFullScreenColour;
- struct {
- bool operator() (TextLayer *i, TextLayer *j) { return (i->objectType() < j->objectType()); }
- } compareLayer;
+ m_invocations[0].clear();
+ m_invocations[1].clear();
+ m_invocations[2].clear();
-// QTime renderPageTime;
+ buildInvocationList(m_localEnhancements, -1);
-// renderPageTime.start();
+ // Append Local Enhancement Data to end of Active Object QList
+ m_invocations[0].append(m_localEnhancements);
- updateSidePanels();
+ m_level1ActivePainter = s_blankPainter;
- while (m_textLayer.size()>2) {
- delete m_textLayer.back();
- m_textLayer.pop_back();
+ m_adapPassPainter[0].clear();
+ m_adapPassPainter[1].clear();
+
+ for (int t=1; t<3; t++)
+ for (int i=0; i= 2) {
+ // Pick up default full screen/row colours from X/28
+ setFullScreenColour(m_levelOnePage->defaultScreenColour());
+ int downwardsRowCLUT = m_levelOnePage->defaultRowColour();
+
+ // Check for Full Screen Colour X/26 triplets in Local Enhancement Data and Active Objects
+ for (int i=0; i fullRowColoursHere = m_invocations[0].at(i).fullRowColoursMappedAt(r);
+
+ // QMultiMap::values returns QList with most recent value first...
+ for (int a=fullRowColoursHere.size()-1; a>=0; a--) {
+ thisFullRowColour = fullRowColoursHere.at(a).data() & 0x1f;
+ if ((fullRowColoursHere.at(a).data() & 0x60) == 0x60)
+ downwardsRowCLUT = thisFullRowColour;
+ }
+ }
+
+ setFullRowColour(r, thisFullRowColour);
+ }
+ } else {
+ setFullScreenColour(0);
+ for (int r=0; r<25; r++)
+ setFullRowColour(r, 0);
}
- renderedFullScreenColour = (m_level >= 2) ? m_levelOnePage->defaultScreenColour() : 0;
- downwardsFullRowColour = (m_level >= 2) ? m_levelOnePage->defaultRowColour() : 0;
- setFullScreenColour(renderedFullScreenColour);
- for (int r=0; r<25; r++)
- setFullRowColour(r, downwardsFullRowColour);
+ m_level1DefaultCharSet = m_g0CharacterMap.value(((m_levelOnePage->defaultCharSet() << 3) | m_levelOnePage->defaultNOS()), 0);
+ if (m_levelOnePage->secondCharSet() != 0xf)
+ m_level1SecondCharSet = m_g0CharacterMap.value(((m_levelOnePage->secondCharSet() << 3) | m_levelOnePage->secondNOS()), 0);
+ else
+ m_level1SecondCharSet = m_level1DefaultCharSet;
- m_textLayer[1]->enhanceMap.clear();
+ // This will be true if the Level 1 character set is non-Latin
+ if (m_level1DefaultCharSet <= 6)
+ m_x26DefaultG0CharSet = m_level1DefaultCharSet;
+ else
+ m_x26DefaultG0CharSet = 0;
- if (m_level > 0 && !m_levelOnePage->enhancements()->isEmpty()) {
- m_textLayer[1]->setFullScreenColour(-1);
- for (int r=0; r<25; r++)
- m_textLayer[1]->setFullRowColour(r, -1, false);
- buildEnhanceMap(m_textLayer[1]);
+ switch (m_level1DefaultCharSet) {
+ case 1:
+ case 2:
+ case 3:
+ // Cyrillic G2
+ m_x26DefaultG2CharSet = 8;
+ break;
+ case 4:
+ // Greek G2
+ m_x26DefaultG2CharSet = 9;
+ break;
+ case 5:
+ // Arabic G2
+ m_x26DefaultG2CharSet = 10;
+ break;
+ default:
+ // Latin G2
+ m_x26DefaultG2CharSet = 7;
+ break;
+ }
- if (m_textLayer.size() > 2)
- std::stable_sort(m_textLayer.begin()+2, m_textLayer.end(), compareLayer);
+ // Work out rows containing top and bottom halves of Level 1 double height characters
+ for (int r=1; r<24; r++) {
+ bool doubleHeightAttributeFound = false;
- if (m_level >= 2) {
- if (m_textLayer[1]->fullScreenColour() != -1)
- downwardsFullRowColour = m_textLayer[1]->fullScreenColour();
- for (int r=0; r<25; r++) {
- for (int l=0; l<2; l++) {
- if (r == 0 && m_textLayer[l]->fullScreenColour() != - 1)
- renderedFullScreenColour = m_textLayer[l]->fullScreenColour();
- if (m_textLayer[l]->fullRowColour(r) == - 1)
- currentFullRowColour = downwardsFullRowColour;
- else {
- currentFullRowColour = m_textLayer[l]->fullRowColour(r);
- if (m_textLayer[l]->fullRowDownwards(r))
- downwardsFullRowColour = currentFullRowColour;
- }
- }
- setFullRowColour(r ,currentFullRowColour);
+ for (int c=0; c<40; c++)
+ if (m_levelOnePage->character(r, c) == 0x0d || m_levelOnePage->character(r, c) == 0x0f) {
+ doubleHeightAttributeFound = true;
+ break;
}
- setFullScreenColour(renderedFullScreenColour);
- }
+
+ if (doubleHeightAttributeFound && r < 23) {
+ m_rowHeight[r] = TopHalf;
+ r++;
+ m_rowHeight[r] = BottomHalf;
+ } else
+ m_rowHeight[r] = NormalHeight;
}
for (int r=0; r<25; r++)
decodeRow(r);
-// qDebug("Full page render: %d ms", renderPageTime.elapsed());
}
void TeletextPageDecode::decodeRow(int r)
{
- int c;
- int phaseNumberRender = 0;
- bool decodeNextRow = false;
- bool applyRightHalf = false;
- bool previouslyDoubleHeight, previouslyBottomHalf, underlined;
- bool doubleHeightFound = false;
- textCharacter resultCharacter, layerCharacter;
- applyAttributes layerApplyAttributes;
- textAttributes underlyingAttributes, resultAttributes;
- int level1CharSet;
+ int level1ForegroundCLUT = 7;
+ bool level1Mosaics = false;
+ bool level1SeparatedMosaics = false;
+ bool level1HoldMosaics = false;
+ unsigned char level1HoldMosaicCharacter = 0x20;
+ bool level1HoldMosaicSeparated = false;
+ int level1CharSet = 0;
+ bool level1EscapeSwitch = false;
+ int x26G0CharSet = 0;
- for (c=0; c<72; c++) {
- textCell oldTextCell = m_cell[r][c];
+ textPainter *painter;
- resultAttributes = underlyingAttributes;
- for (int l=0; lcharacter(r, c);
- if (layerCharacter.code != 0x00)
- resultCharacter = layerCharacter;
- if (l == 0) {
-// m_cell[r][c].level1Mosaic = (resultCharacter.set == 24 || resultCharacter.set == 25) && m_levelOnePage->character(r, c) >= 0x20;
- m_cell[r][c].level1Mosaic = (resultCharacter.set == 24 || resultCharacter.set == 25);
- if (!m_cell[r][c].level1Mosaic)
- level1CharSet = resultCharacter.set;
- m_cell[r][c].level1CharSet = level1CharSet;
- }
+ // Used for tracking which Adaptive Invocation is applying which attribute type(s)
+ // A.7.1 and A.7.2 of the spec says Adaptive Objects can't overlap but can be interleaved
+ // if they don't have attributes, so we only need to track one
+ int adapInvokeAttrs = -1;
+ bool adapForeground = false;
+ bool adapBackground = false;
+ bool adapFlash = false;
+ bool adapDisplayAttrs = false;
- layerApplyAttributes = { false, false, false, false, false, false, false, false };
- m_textLayer[l]->attributes(r, c, &layerApplyAttributes);
- if (layerApplyAttributes.copyAboveAttributes) {
- resultAttributes = m_cell[r-1][c].attribute;
- layerApplyAttributes.copyAboveAttributes = false;
- break;
+ for (int c=0; c<72; c++) {
+ textCell previousCellContents = m_cell[r][c];
+
+ // Start of row default conditions, also when crossing into and across side panels
+ if (c == 0 || c == 40 || c == 56) {
+ level1CharSet = m_level1DefaultCharSet;
+ x26G0CharSet = m_x26DefaultG0CharSet;
+
+ m_level1ActivePainter.attribute.flash.mode = 0;
+ m_level1ActivePainter.attribute.flash.ratePhase = 0;
+ m_level1ActivePainter.attribute.display.doubleHeight = false;
+ m_level1ActivePainter.attribute.display.doubleWidth = false;
+ m_level1ActivePainter.attribute.display.boxingWindow = false;
+ m_level1ActivePainter.attribute.display.conceal = false;
+ m_level1ActivePainter.attribute.display.invert = false;
+ m_level1ActivePainter.attribute.display.underlineSeparated = false;
+
+ if (m_level >= 2) {
+ m_level1ActivePainter.attribute.foregroundCLUT = 7 | m_foregroundRemap[m_levelOnePage->colourTableRemap()];
+ if (m_levelOnePage->blackBackgroundSubst() || c >= 40)
+ m_level1ActivePainter.attribute.backgroundCLUT = m_fullRowColour[r];
+ else
+ m_level1ActivePainter.attribute.backgroundCLUT = m_backgroundRemap[m_levelOnePage->colourTableRemap()];
+ } else {
+ m_level1ActivePainter.attribute.foregroundCLUT = 7;
+ m_level1ActivePainter.attribute.backgroundCLUT = 0;
}
- if (layerApplyAttributes.applyForeColour) {
- resultAttributes.foreColour = layerApplyAttributes.attribute.foreColour;
- if (l == 0 && m_level >= 2)
- resultAttributes.foreColour |= m_foregroundRemap[m_levelOnePage->colourTableRemap()];
- }
- if (layerApplyAttributes.applyBackColour) {
- resultAttributes.backColour = layerApplyAttributes.attribute.backColour;
- if (l == 0) {
- if (m_level >= 2)
- if (resultAttributes.backColour == 0x20)
- resultAttributes.backColour = (c > 39 || m_levelOnePage->blackBackgroundSubst()) ? m_fullRowColour[r] : m_backgroundRemap[m_levelOnePage->colourTableRemap()];
+ }
+
+ // Level 1 set-at and "set-between" spacing attributes
+ if (c < 40 && m_rowHeight[r] != BottomHalf)
+ switch (m_levelOnePage->character(r, c)) {
+ case 0x09: // Steady
+ m_level1ActivePainter.attribute.flash.mode = 0;
+ m_level1ActivePainter.attribute.flash.ratePhase = 0;
+ break;
+ case 0x0a: // End box
+ // "Set-between" - requires two consecutive "end box" codes
+ if (c > 0 && m_levelOnePage->character(r, c-1) == 0x0a)
+ m_level1ActivePainter.attribute.display.boxingWindow = false;
+ break;
+ case 0x0b: // Start box
+ // "Set-between" - requires two consecutive "start box" codes
+ if (c > 0 && m_levelOnePage->character(r, c-1) == 0x0b)
+ m_level1ActivePainter.attribute.display.boxingWindow = true;
+ break;
+ case 0x0c: // Normal size
+ if (m_level1ActivePainter.attribute.display.doubleHeight || m_level1ActivePainter.attribute.display.doubleWidth) {
+ // Change of size resets hold mosaic character
+ level1HoldMosaicCharacter = 0x20;
+ level1HoldMosaicSeparated = false;
+ }
+ m_level1ActivePainter.attribute.display.doubleHeight = false;
+ m_level1ActivePainter.attribute.display.doubleWidth = false;
+ break;
+ case 0x18: // Conceal
+ m_level1ActivePainter.attribute.display.conceal = true;
+ break;
+ case 0x19: // Contiguous mosaics
+ // This spacing attribute cannot cancel an X/26 underlined/separated attribute
+ if (!m_level1ActivePainter.attribute.display.underlineSeparated)
+ level1SeparatedMosaics = false;
+ break;
+ case 0x1a: // Separated mosaics
+ level1SeparatedMosaics = true;
+ break;
+ case 0x1c: // Black background
+ if (m_level >= 2) {
+ if (m_levelOnePage->blackBackgroundSubst())
+ m_level1ActivePainter.attribute.backgroundCLUT = m_fullRowColour[r];
else
- resultAttributes.backColour |= m_backgroundRemap[m_levelOnePage->colourTableRemap()];
+ m_level1ActivePainter.attribute.backgroundCLUT = m_backgroundRemap[m_levelOnePage->colourTableRemap()];
+ } else
+ m_level1ActivePainter.attribute.backgroundCLUT = 0;
+ break;
+ case 0x1d: // New background
+ if (m_level >= 2)
+ m_level1ActivePainter.attribute.backgroundCLUT = level1ForegroundCLUT | m_backgroundRemap[m_levelOnePage->colourTableRemap()];
else
- if (resultAttributes.backColour == 0x20)
- resultAttributes.backColour = 0x00;
+ m_level1ActivePainter.attribute.backgroundCLUT = level1ForegroundCLUT;
+ break;
+ case 0x1e: // Hold mosaics
+ level1HoldMosaics = true;
+ break;
+ }
+
+ if (m_level < 2)
+ m_level1ActivePainter.result.attribute = m_level1ActivePainter.attribute;
+ else{
+ // Deal with incremental and decremental flash
+ rotateFlashMovement(m_level1ActivePainter.attribute.flash);
+
+ for (int t=0; t<2; t++)
+ for (int i=0; i attributesHere = m_invocations[t].at(i).attributesMappedAt(r, c);
+
+ painter = (t == 0) ? &m_level1ActivePainter : &m_adapPassPainter[t-1][i];
+
+ // 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
+ if (t == 1) {
+ if (!adapForeground)
+ painter->attribute.foregroundCLUT = m_level1ActivePainter.attribute.foregroundCLUT;
+ if (!adapBackground)
+ painter->attribute.backgroundCLUT = m_level1ActivePainter.attribute.backgroundCLUT;
+ if (!adapFlash)
+ painter->attribute.flash = m_level1ActivePainter.attribute.flash;
+ if (!adapDisplayAttrs)
+ painter->attribute.display = m_level1ActivePainter.attribute.display;
+ }
+
+ // QMultiMap::values returns QList with most recent value first...
+ for (int a=attributesHere.size()-1; a>=0; a--) {
+ const X26Triplet triplet = attributesHere.at(a);
+
+ bool applyAdapt = false;
+
+ // Adaptive Invocation that is applying an attribute
+ // If we're not tracking an Adaptive Invocation yet, start tracking this one
+ // Otherwise check if this Invocation is the the same one as we are tracking
+ if (t == 1) {
+ if (adapInvokeAttrs == -1) {
+ adapInvokeAttrs = i;
+ applyAdapt = true;
+ } else if (adapInvokeAttrs == i)
+ applyAdapt = true;
+// else
+// qDebug("Multiple adaptive object attributes");
+ }
+
+ switch (triplet.modeExt()) {
+ case 0x20: // Foreground colour
+ if (applyAdapt)
+ adapForeground = true;
+ painter->attribute.foregroundCLUT = triplet.data();
+ break;
+ case 0x23: // Background colour
+ if (applyAdapt)
+ adapBackground = true;
+ painter->attribute.backgroundCLUT = triplet.data();
+ break;
+ case 0x27: // Additional flash functions
+ if (applyAdapt)
+ adapFlash = true;
+ painter->attribute.flash.mode = triplet.data() & 0x03;
+ painter->attribute.flash.ratePhase = triplet.data() >> 2;
+ // For incremental/decremental 2Hz flash, start at phase 1
+ if (painter->attribute.flash.mode != 0 && painter->attribute.flash.ratePhase & 0x4)
+ painter->attribute.flash.phase2HzShown = 1;
+ else
+ painter->attribute.flash.phase2HzShown = painter->attribute.flash.ratePhase;
+ break;
+ case 0x2c: // Display attributes
+ if (applyAdapt)
+ adapDisplayAttrs = true;
+ painter->attribute.display.doubleHeight = triplet.data() & 0x01;
+ painter->attribute.display.boxingWindow = triplet.data() & 0x02;
+ painter->attribute.display.conceal = triplet.data() & 0x04;
+ painter->attribute.display.invert = triplet.data() & 0x10;
+ painter->attribute.display.underlineSeparated = triplet.data() & 0x20;
+ painter->attribute.display.doubleWidth = triplet.data() & 0x40;
+ // Cancelling separated mosaics with X/26 attribute
+ // also cancels the Level 1 separated mosaic attribute
+ if (t == 0 && !painter->attribute.display.underlineSeparated)
+ level1SeparatedMosaics = false;
+ break;
+ }
+ }
+
+ painter->result.attribute = painter->attribute;
}
}
- if (layerApplyAttributes.applyFlash) {
- //BUG Adaptive Objects disrupt inc/dec flash
- resultAttributes.flash = layerApplyAttributes.attribute.flash;
- if (resultAttributes.flash.mode != 0)
- phaseNumberRender = (resultAttributes.flash.ratePhase == 4 || resultAttributes.flash.ratePhase == 5) ? 1 : resultAttributes.flash.ratePhase;
+ // Level 1 character
+ if (c < 40 && m_rowHeight[r] != BottomHalf) {
+ m_level1ActivePainter.result.character.diacritical = 0;
+ if (m_levelOnePage->character(r, c) >= 0x20) {
+ m_level1ActivePainter.result.character.code = m_levelOnePage->character(r, c);
+ // Set to true on mosaic character - not on blast through alphanumerics
+ m_cellLevel1Mosaic[r][c] = level1Mosaics && (m_levelOnePage->character(r, c) & 0x20);
+ if (m_cellLevel1Mosaic[r][c]) {
+ m_level1ActivePainter.result.character.set = 24 + (level1SeparatedMosaics || m_level1ActivePainter.attribute.display.underlineSeparated);
+ level1HoldMosaicCharacter = m_levelOnePage->character(r, c);
+ level1HoldMosaicSeparated = level1SeparatedMosaics;
+ } else
+ m_level1ActivePainter.result.character.set = level1CharSet;
+ } else if (level1HoldMosaics) {
+ m_level1ActivePainter.result.character = { level1HoldMosaicCharacter, 24 + level1HoldMosaicSeparated, 0 };
+ } else
+ m_level1ActivePainter.result.character = { 0x20, 0, 0 };
+ } else
+ // In side panel or on bottom half of Level 1 double height row, no Level 1 characters here
+ m_level1ActivePainter.result.character = { 0x20, 0, 0 };
+
+ if (c < 40)
+ m_cellLevel1CharSet[r][c] = level1CharSet;
+
+ // X/26 characters
+
+ // Used to track if character was placed by X/26 data
+ // 0=Level 1 character, 1=Active Object or Local Enhancement Data, 2=Adaptive Object
+ int x26Character = 0;
+
+ if (m_level == 1 && !m_invocations[0].isEmpty()) {
+ // For Level 1.5 only do characters from Local Enhancements
+ // which is the last entry on the Active Objects QList
+ const textCharacter result = characterFromTriplets(m_invocations[0].constLast().charactersMappedAt(r, c), x26G0CharSet);
+
+ if (result.code != 0x00) {
+ m_level1ActivePainter.result.character = result;
+ x26Character = 1;
}
- if (layerApplyAttributes.applyDisplayAttributes)
- resultAttributes.display = layerApplyAttributes.attribute.display;
- else {
- // Selecting contiguous mosaics wih a triplet will override an earlier Level 1 separated mosaics attribute until a further Level 1 contiguous mosaic attribute is encountered
- resultAttributes.display.forceContiguous = (layerApplyAttributes.applyContiguousOnly) ? false : underlyingAttributes.display.forceContiguous;
- if (layerApplyAttributes.applyTextSizeOnly) {
- resultAttributes.display.doubleHeight = layerApplyAttributes.attribute.display.doubleHeight;
- resultAttributes.display.doubleWidth = layerApplyAttributes.attribute.display.doubleWidth;
+ } else if (m_level >= 2)
+ for (int t=0; t<3; t++)
+ for (int i=0; iresult.character = m_level1ActivePainter.result.character;
+ x26Character = 2;
+ continue;
+ }
+
+ painter->result.character = result;
+ if (painter->result.character.set == 24 && painter->attribute.display.underlineSeparated)
+ painter->result.character.set++;
+
+ if (t < 2 && result.code != 0x00)
+ x26Character = t + 1;
}
- if (layerApplyAttributes.applyBoxingOnly)
- resultAttributes.display.boxingWindow = layerApplyAttributes.attribute.display.boxingWindow;
- if (layerApplyAttributes.applyConcealOnly || layerApplyAttributes.applyForeColour)
- resultAttributes.display.conceal = layerApplyAttributes.attribute.display.conceal;
+
+ // Allow Active Objects or Local Enhancement Data to overlap bottom half of a Level 1 double height row
+ // where the character on the top half is normal size or double width
+ if (m_rowHeight[r] == BottomHalf && c < 40 && x26Character == 1 && m_level1ActivePainter.bottomHalfCell[c].fragment == NormalSize)
+ m_level1ActivePainter.bottomHalfCell[c].character.code = 0x00;
+
+ // Allow Adaptive Objects to always overlap bottom half of a Level 1 double height row
+ if (m_rowHeight[r] == BottomHalf && c < 40 && x26Character == 2)
+ m_level1ActivePainter.bottomHalfCell[c].character.code = 0x00;
+
+ // Work out which fragment of an enlarged character to display
+ for (int t=0; t<3; t++) {
+ for (int i=0; irightHalfCell.character.code != 0x00) {
+ painter->result = painter->rightHalfCell;
+ // Corner cases of right half of double-width characters overlapping
+ // a Level 1 double height row need this to avoid spurious characters below
+ if (painter->result.fragment == DoubleWidthRightHalf)
+ painter->bottomHalfCell[c].character.code = 0x00;
+ painter->rightHalfCell.character.code = 0x00;
+ cellCovered = true;
+ } else if (painter->bottomHalfCell[c].character.code != 0x00) {
+ painter->result = painter->bottomHalfCell[c];
+ painter->bottomHalfCell[c].character.code = 0x00;
+ cellCovered = true;
+ }
+
+ if (!cellCovered) {
+ // Cell is not covered by previous enlarged character
+ // Work out which fragments of enlarged characters are needed from size attributes,
+ // place origin of character here and other fragments into right half or bottom half
+ // painter buffer ready to be picked up on the next iteration
+ bool doubleHeight = painter->attribute.display.doubleHeight;
+ bool doubleWidth = painter->attribute.display.doubleWidth;
+
+ if (r == 0 || r > 22)
+ doubleHeight = false;
+ if (c == 39 || c == 39+m_rightSidePanelColumns || c == 71-m_leftSidePanelColumns || c == 71)
+ doubleWidth = false;
+
+ if (doubleHeight) {
+ if (doubleWidth) {
+ // Double size
+ painter->result.fragment = DoubleSizeTopLeftQuarter;
+ painter->bottomHalfCell[c] = painter->result;
+ painter->bottomHalfCell[c].fragment = DoubleSizeBottomLeftQuarter;
+ painter->rightHalfCell = painter->result;
+ painter->rightHalfCell.fragment = DoubleSizeTopRightQuarter;
+ painter->bottomHalfCell[c+1] = painter->result;
+ painter->bottomHalfCell[c+1].fragment = DoubleSizeBottomRightQuarter;
+ } else {
+ // Double height
+ painter->result.fragment = DoubleHeightTopHalf;
+ painter->bottomHalfCell[c] = painter->result;
+ painter->bottomHalfCell[c].fragment = DoubleHeightBottomHalf;
+ }
+ } else if (doubleWidth) {
+ // Double width
+ painter->result.fragment = DoubleWidthLeftHalf;
+ painter->rightHalfCell = painter->result;
+ painter->rightHalfCell.fragment = DoubleWidthRightHalf;
+ } else
+ // Normal size
+ painter->result.fragment = NormalSize;
+
+ // Now the enlargements and fragments are worked out, prevent Adaptive Objects that
+ // are NOT applying Display Attributes from trying to overlap wrong fragments of characters
+ // on the underlying page
+ if (t == 1 && !adapDisplayAttrs && painter->result.fragment != m_level1ActivePainter.result.fragment) {
+ painter->result.character.code = 0x00;
+ if (painter->result.fragment == DoubleWidthLeftHalf || painter->result.fragment == DoubleSizeTopLeftQuarter)
+ painter->rightHalfCell.character.code = 0x00;
+ if (painter->result.fragment == DoubleHeightTopHalf || painter->result.fragment == DoubleSizeTopLeftQuarter)
+ painter->bottomHalfCell[c].character.code = 0x00;
+ if (painter->result.fragment == DoubleSizeTopLeftQuarter && c < 71)
+ painter->bottomHalfCell[c+1].character.code = 0x00;
+ }
+ }
+
+ if (t == 0)
+ break;
}
- if (m_textLayer[l]->objectType() <= 1)
- underlyingAttributes = resultAttributes;
-
- if (m_level == 0)
- break;
}
- underlined = false;
- if (resultAttributes.display.underlineSeparated) {
- if (resultCharacter.set == 24)
- resultCharacter.set = 25;
- else
- underlined = resultCharacter.set < 24;
- }
- if (resultAttributes.display.forceContiguous && resultCharacter.set == 25)
- resultCharacter.set = 24;
-
- resultAttributes.flash.phaseNumber = phaseNumberRender;
-
- previouslyDoubleHeight = m_cell[r][c].attribute.display.doubleHeight;
- previouslyBottomHalf = m_cell[r][c].bottomHalf;
-
- m_cell[r][c].character = resultCharacter;
- m_cell[r][c].attribute = resultAttributes;
-
- if (m_cell[r][c] != oldTextCell) {
- m_refresh[r][c] = true;
-
- if (static_cast(m_textLayer[0])->rowHeight(r) == Level1Layer::TopHalf) {
- m_refresh[r+1][c] = true;
- decodeNextRow = true;
+ // Top half of Level 1 double height row: normal or double width characters will cause a space
+ // with the same attributes to be on the bottom half
+ // Also occurs with bottom halves of X/26 double height characters overlapping a
+ // Level 1 top half row
+ if (m_rowHeight[r] == TopHalf && c < 40) {
+ if (m_level1ActivePainter.result.fragment != DoubleHeightTopHalf && m_level1ActivePainter.result.fragment != DoubleSizeTopLeftQuarter && m_level1ActivePainter.result.fragment != DoubleSizeTopRightQuarter) {
+ m_level1ActivePainter.bottomHalfCell[c] = m_level1ActivePainter.result;
+ m_level1ActivePainter.bottomHalfCell[c].character = { 0x20, 0, 0 };
+ m_level1ActivePainter.bottomHalfCell[c].fragment = NormalSize;
}
- if ((m_cell[r][c].attribute.display.doubleHeight || oldTextCell.attribute.display.doubleHeight) && r < 25)
- m_refresh[r+1][c] = true;
- if ((m_cell[r][c].attribute.display.doubleWidth || oldTextCell.attribute.display.doubleWidth) && c < 72)
- m_refresh[r][c+1] = true;
- if (((m_cell[r][c].attribute.display.doubleHeight && m_cell[r][c].attribute.display.doubleWidth) || (oldTextCell.attribute.display.doubleHeight && oldTextCell.attribute.display.doubleWidth)) && r < 25 && c < 72)
- m_refresh[r+1][c+1] = true;
}
- if (resultAttributes.flash.ratePhase == 4 && ++phaseNumberRender == 4)
- phaseNumberRender = 1;
- if (resultAttributes.flash.ratePhase == 5 && --phaseNumberRender == 0)
- phaseNumberRender = 3;
+ // Now we've finally worked out what characters and attributes are in place on
+ // the underlying page and Invoked Objects, work out which of those to actually render
+ if (m_level < 2)
+ m_cell[r][c] = m_level1ActivePainter.result;
+ else {
+ bool objectCell = false;
- if (r > 0)
- m_cell[r][c].bottomHalf = m_cell[r-1][c].attribute.display.doubleHeight && !m_cell[r-1][c].bottomHalf;
- if ((resultAttributes.display.doubleHeight != previouslyDoubleHeight) || (m_cell[r][c].bottomHalf != previouslyBottomHalf))
- decodeNextRow = true;
- m_cell[r][c].rightHalf = applyRightHalf;
+ // Passive Objects highest priority, followed by Adaptive Objects
+ // Most recently invoked Object has priority
+ for (int t=1; t>=0; t--) {
+ for (int i=m_adapPassPainter[t].size()-1; i>=0; i--)
+ if (m_adapPassPainter[t][i].result.character.code != 0x00) {
+ m_cell[r][c] = m_adapPassPainter[t][i].result;
+ objectCell = true;
+ break;
+ }
+ if (objectCell)
+ break;
+ }
- if (resultAttributes.display.doubleHeight)
- doubleHeightFound = true;
- if (resultAttributes.display.doubleWidth || (m_cell[r][c].bottomHalf && c > 0 && m_cell[r-1][c-1].rightHalf))
- applyRightHalf ^= true;
- else
- applyRightHalf = false;
+ if (!objectCell)
+ // No Adaptive or Passive Object here: will either be Local Enhancement Data, Active Object
+ // or underlying Level 1 page
+ m_cell[r][c] = m_level1ActivePainter.result;
+ }
+
+ // Check for end of Adaptive Object row
+ if (adapInvokeAttrs != -1 && c == m_invocations[1].at(adapInvokeAttrs).rightMostColumn(r)) {
+ // Neutralise size attributes as they could interfere with double height stuff
+ // not sure if this is really necessary
+ m_adapPassPainter[0][adapInvokeAttrs].attribute.display.doubleHeight = false;
+ m_adapPassPainter[0][adapInvokeAttrs].attribute.display.doubleWidth = false;
+ adapInvokeAttrs = -1;
+ adapForeground = adapBackground = adapFlash = adapDisplayAttrs = false;
+ }
+
+ // Level 1 set-after spacing attributes
+ if (c < 40 && m_rowHeight[r] != BottomHalf)
+ switch (m_levelOnePage->character(r, c)) {
+ case 0x00 ... 0x07: // Alphanumeric and foreground colour
+ level1Mosaics = false;
+ level1ForegroundCLUT = m_levelOnePage->character(r, c);
+ if (m_level >= 2)
+ m_level1ActivePainter.attribute.foregroundCLUT = level1ForegroundCLUT | m_foregroundRemap[m_levelOnePage->colourTableRemap()];
+ else
+ m_level1ActivePainter.attribute.foregroundCLUT = level1ForegroundCLUT;
+ m_level1ActivePainter.attribute.display.conceal = false;
+ // Switch from mosaics to alpha resets hold mosaic character
+ level1HoldMosaicCharacter = 0x20;
+ level1HoldMosaicSeparated = false;
+ break;
+ case 0x10 ... 0x17: // Mosaic and foreground colour
+ level1Mosaics = true;
+ level1ForegroundCLUT = m_levelOnePage->character(r, c) & 0x07;
+ if (m_level >= 2)
+ m_level1ActivePainter.attribute.foregroundCLUT = level1ForegroundCLUT | m_foregroundRemap[m_levelOnePage->colourTableRemap()];
+ else
+ m_level1ActivePainter.attribute.foregroundCLUT = level1ForegroundCLUT;
+ m_level1ActivePainter.attribute.display.conceal = false;
+ break;
+ case 0x08: // Flashing
+ m_level1ActivePainter.attribute.flash.mode = 1;
+ m_level1ActivePainter.attribute.flash.ratePhase = 0;
+ break;
+ case 0x0d: // Double height
+ if (!m_level1ActivePainter.attribute.display.doubleHeight || m_level1ActivePainter.attribute.display.doubleWidth) {
+ // Change of size resets hold mosaic character
+ level1HoldMosaicCharacter = 0x20;
+ level1HoldMosaicSeparated = false;
+ }
+ m_level1ActivePainter.attribute.display.doubleHeight = true;
+ m_level1ActivePainter.attribute.display.doubleWidth = false;
+ break;
+ case 0x0e: // Double width
+ if (m_level1ActivePainter.attribute.display.doubleHeight || !m_level1ActivePainter.attribute.display.doubleWidth) {
+ // Change of size resets hold mosaic character
+ level1HoldMosaicCharacter = 0x20;
+ level1HoldMosaicSeparated = false;
+ }
+ m_level1ActivePainter.attribute.display.doubleHeight = false;
+ m_level1ActivePainter.attribute.display.doubleWidth = true;
+ break;
+ case 0x0f: // Double size
+ if (!m_level1ActivePainter.attribute.display.doubleHeight || !m_level1ActivePainter.attribute.display.doubleWidth) {
+ // Change of size resets hold mosaic character
+ level1HoldMosaicCharacter = 0x20;
+ level1HoldMosaicSeparated = false;
+ }
+ m_level1ActivePainter.attribute.display.doubleHeight = true;
+ m_level1ActivePainter.attribute.display.doubleWidth = true;
+ break;
+ case 0x1b: // ESC/switch
+ level1EscapeSwitch ^= true;
+ if (level1EscapeSwitch)
+ level1CharSet = m_level1SecondCharSet;
+ else
+ level1CharSet = m_level1DefaultCharSet;
+ break;
+ case 0x1f: // Release mosaics
+ level1HoldMosaics = false;
+ break;
+ }
+
+ if (m_cell[r][c] != previousCellContents)
+ setRefresh(r, c, true);
}
-
- if (decodeNextRow && r<24)
- decodeRow(r+1);
}
-textCell& TeletextPageDecode::cellAtCharacterOrigin(int r, int c)
+inline void TeletextPageDecode::rotateFlashMovement(flashFunctions &flash)
{
-/* if (m_cell[r][c].bottomHalf && r > 0) {
- if (m_cell[r][c].rightHalf && c > 0)
- // Double size
- return m_cell[r-1][c-1];
- else
- // Double height
- return m_cell[r-1][c];
- } else {
- if (m_cell[r][c].rightHalf && c > 0)
- // Double width
- return m_cell[r][c-1];
- else
- // Normal size
- return m_cell[r][c];
- }*/
- switch (cellCharacterFragment(r, c)) {
- case TeletextPageDecode::DoubleHeightBottomHalf:
- case TeletextPageDecode::DoubleSizeBottomLeftQuarter:
- return m_cell[r-1][c];
- case TeletextPageDecode::DoubleWidthRightHalf:
- case TeletextPageDecode::DoubleSizeTopRightQuarter:
- return m_cell[r][c-1];
- case TeletextPageDecode::DoubleSizeBottomRightQuarter:
- return m_cell[r-1][c-1];
- default:
- return m_cell[r][c];
+ if (flash.ratePhase == 4) {
+ flash.phase2HzShown++;
+ if (flash.phase2HzShown == 4)
+ flash.phase2HzShown = 1;
+ } else if (flash.ratePhase == 5) {
+ flash.phase2HzShown--;
+ if (flash.phase2HzShown == 0)
+ flash.phase2HzShown = 3;
}
}
QColor TeletextPageDecode::cellQColor(int r, int c, ColourPart colourPart)
{
- const textCell& cell = cellAtCharacterOrigin(r, c);
const bool newsFlashOrSubtitle = m_levelOnePage->controlBit(PageBase::C5Newsflash) || m_levelOnePage->controlBit(PageBase::C6Subtitle);
- int resultCLUT;
+ int resultCLUT = 0;
switch (colourPart) {
case Foreground:
- if (!cell.attribute.display.invert)
- resultCLUT = cell.attribute.foreColour;
+ if (!m_cell[r][c].attribute.display.invert)
+ resultCLUT = m_cell[r][c].attribute.foregroundCLUT;
else
- resultCLUT = cell.attribute.backColour;
+ resultCLUT = m_cell[r][c].attribute.backgroundCLUT;
break;
case Background:
- if (!cell.attribute.display.invert)
- resultCLUT = cell.attribute.backColour;
+ if (!m_cell[r][c].attribute.display.invert)
+ resultCLUT = m_cell[r][c].attribute.backgroundCLUT;
else
- resultCLUT = cell.attribute.foreColour;
+ resultCLUT = m_cell[r][c].attribute.foregroundCLUT;
break;
case FlashForeground:
- if (!cell.attribute.display.invert)
- resultCLUT = cell.attribute.foreColour ^ 8;
+ if (!m_cell[r][c].attribute.display.invert)
+ resultCLUT = m_cell[r][c].attribute.foregroundCLUT ^ 8;
else
- resultCLUT = cell.attribute.backColour ^ 8;
+ resultCLUT = m_cell[r][c].attribute.backgroundCLUT ^ 8;
break;
}
if (resultCLUT == 8) {
// Transparent CLUT - either Full Row Colour or Video
// Logic of table C.1 in spec implemented to find out which it is
- if (cell.attribute.display.boxingWindow != newsFlashOrSubtitle)
+ if (m_cell[r][c].attribute.display.boxingWindow != newsFlashOrSubtitle)
return QColor(Qt::transparent);
int rowColour;
@@ -493,7 +1001,7 @@ QColor TeletextPageDecode::cellQColor(int r, int c, ColourPart colourPart)
return QColor(Qt::transparent);
else
return m_levelOnePage->CLUTtoQColor(rowColour, m_level);
- } else if (!cell.attribute.display.boxingWindow && newsFlashOrSubtitle)
+ } else if (!m_cell[r][c].attribute.display.boxingWindow && newsFlashOrSubtitle)
return QColor(Qt::transparent);
return m_levelOnePage->CLUTtoQColor(resultCLUT, m_level);
@@ -514,31 +1022,20 @@ QColor TeletextPageDecode::cellFlashForegroundQColor(int r, int c)
return cellQColor(r, c, FlashForeground);
}
-TeletextPageDecode::CharacterFragment TeletextPageDecode::cellCharacterFragment(int r, int c) const
+TeletextPageDecode::textCell& TeletextPageDecode::cellAtCharacterOrigin(int r, int c)
{
- if (m_cell[r][c].bottomHalf && r > 0) {
- if (m_cell[r][c].rightHalf && c > 0)
- return CharacterFragment::DoubleSizeBottomRightQuarter;
- else if (m_cell[r-1][c].attribute.display.doubleWidth)
- return CharacterFragment::DoubleSizeBottomLeftQuarter;
- else
- return CharacterFragment::DoubleHeightBottomHalf;
- } else if (m_cell[r][c].rightHalf && c > 0) {
- if (m_cell[r][c-1].attribute.display.doubleHeight)
- return CharacterFragment::DoubleSizeTopRightQuarter;
- else
- return CharacterFragment::DoubleWidthRightHalf;
+ switch (cellCharacterFragment(r, c)) {
+ case TeletextPageDecode::DoubleHeightBottomHalf:
+ case TeletextPageDecode::DoubleSizeBottomLeftQuarter:
+ return m_cell[r-1][c];
+ case TeletextPageDecode::DoubleWidthRightHalf:
+ case TeletextPageDecode::DoubleSizeTopRightQuarter:
+ return m_cell[r][c-1];
+ case TeletextPageDecode::DoubleSizeBottomRightQuarter:
+ return m_cell[r-1][c-1];
+ default:
+ return m_cell[r][c];
}
-
- if (m_cell[r][c].attribute.display.doubleHeight) {
- if (m_cell[r][c].attribute.display.doubleWidth)
- return CharacterFragment::DoubleSizeTopLeftQuarter;
- else
- return CharacterFragment::DoubleHeightTopHalf;
- } else if (m_cell[r][c].attribute.display.doubleWidth)
- return CharacterFragment::DoubleWidthLeftHalf;
-
- return CharacterFragment::NormalSize;
}
inline void TeletextPageDecode::setFullScreenColour(int newColour)
@@ -568,431 +1065,10 @@ inline void TeletextPageDecode::setFullRowColour(int row, int newColour)
QColor newFullRowQColor = m_levelOnePage->CLUTtoQColor(newColour, m_level);
if (m_fullRowQColor[row] != newFullRowQColor) {
for (int c=0; c<72; c++) {
- if (m_cell[row][c].attribute.foreColour == 8 || m_cell[row][c].attribute.backColour == 8)
+ if (m_cell[row][c].attribute.foregroundCLUT == 8 || m_cell[row][c].attribute.backgroundCLUT == 8)
setRefresh(row, c, true);
}
m_fullRowQColor[row] = newFullRowQColor;
emit fullRowColourChanged(row, m_fullRowQColor[row]);
}
}
-
-void TextLayer::setTeletextPage(LevelOnePage *newCurrentPage) { m_levelOnePage = newCurrentPage; }
-void TextLayer::setFullScreenColour(int newColour) { m_layerFullScreenColour = newColour; }
-
-void TextLayer::setFullRowColour(int r, int newColour, bool newDownwards)
-{
- m_layerFullRowColour[r] = newColour;
- m_layerFullRowDownwards[r] = newDownwards;
-}
-
-void EnhanceLayer::setObjectType(int newObjectType) { m_objectType = newObjectType; }
-
-void EnhanceLayer::setOrigin(int r, int c)
-{
- m_originR = r;
- m_originC = c;
-}
-
-Level1Layer::Level1Layer()
-{
- for (int r=0; r<25; r++) {
- m_rowHasDoubleHeightAttr[r] = false;
- m_rowHeight[r] = Normal;
- }
-}
-
-EnhanceLayer::EnhanceLayer()
-{
- for (int r=0; r<25; r++) {
- m_layerFullRowColour[r] = -1;
- m_layerFullRowDownwards[r] = false;
- }
-}
-
-textCharacter EnhanceLayer::character(int r, int c)
-{
- r -= m_originR;
- c -= m_originC;
- if (r < 0 || c < 0)
- return { 0, 0 };
-
- // QPair.first is triplet mode, QPair.second is triplet data
- QList> enhancements = enhanceMap.values(qMakePair(r, c));
-
- if (enhancements.size() > 0)
- for (int i=0; i= 0x20)
- return { enhancements.at(i).second, (enhancements.at(i).second & 0x20) ? 24 : 0 };
- }
- return { 0, 0 };
-}
-
-void EnhanceLayer::attributes(int r, int c, applyAttributes *layerApplyAttributes)
-{
- r -= m_originR;
- c -= m_originC;
- if (r < 0 || c < 0)
- return;
- if (m_objectType == 2) {
- // Adaptive Object - find rightmost column addressed on this row if we haven't already
- if (r != m_rowCached) {
- m_rightMostColumn[r] = 0;
- m_rowCached = r;
- for (int cc=39; cc>0; cc--)
- if (enhanceMap.contains(qMakePair(r, cc))) {
- m_rightMostColumn[r] = cc;
- break;
- }
- }
- // On new row, default to attributes already on page
- // At end of rightmost column, let go of all attributes
- if (c == 0 || c == m_rightMostColumn[r]+1)
- m_applyAttributes = { false, false, false, false, false, false, false, false };
- else {
- // Re-apply attributes that Object has defined previously on this row
- if (m_applyAttributes.applyForeColour) {
- layerApplyAttributes->applyForeColour = true;
- layerApplyAttributes->attribute.foreColour = m_applyAttributes.attribute.foreColour;
- }
- if (m_applyAttributes.applyBackColour) {
- layerApplyAttributes->applyBackColour = true;
- layerApplyAttributes->attribute.backColour = m_applyAttributes.attribute.backColour;
- }
- //BUG Adaptive Objects disrupt inc/dec flash
- if (m_applyAttributes.applyFlash) {
- layerApplyAttributes->applyFlash = true;
- layerApplyAttributes->attribute.flash.mode = m_applyAttributes.attribute.flash.mode;
- layerApplyAttributes->attribute.flash.ratePhase = m_applyAttributes.attribute.flash.ratePhase;
- }
- if (m_applyAttributes.applyDisplayAttributes) {
- layerApplyAttributes->applyDisplayAttributes = true;
- layerApplyAttributes->attribute.display = m_applyAttributes.attribute.display;
- }
- }
- }
- if (m_objectType == 3) {
- if (r == 0 && c == 0) {
- // Passive Objects always start with all these default attributes
- m_applyAttributes.applyForeColour = true;
- m_applyAttributes.attribute.foreColour = 0x07;
- m_applyAttributes.applyBackColour = true;
- m_applyAttributes.attribute.backColour = 0x00;
- m_applyAttributes.applyDisplayAttributes = true;
- m_applyAttributes.applyFlash = true;
- m_applyAttributes.attribute.flash.mode = 0;
- m_applyAttributes.attribute.flash.ratePhase = 0;
- m_applyAttributes.attribute.display.doubleHeight = false;
- m_applyAttributes.attribute.display.doubleWidth = false;
- m_applyAttributes.attribute.display.boxingWindow = false;
- m_applyAttributes.attribute.display.conceal = false;
- m_applyAttributes.attribute.display.invert = false;
- m_applyAttributes.attribute.display.underlineSeparated = false;
- m_applyAttributes.attribute.display.forceContiguous = false;
- }
- if (character(r+m_originR, c+m_originC).code == 0x00)
- // Passive Object attributes only apply where it also defines a character
- // In this case, wrench the pointer-parameter to alter only the attributes of the Object
- layerApplyAttributes = &m_applyAttributes;
- else
- *layerApplyAttributes = m_applyAttributes;
- }
-
- // QPair.first is triplet mode, QPair.second is triplet data
- QList> enhancements = enhanceMap.values(qMakePair(r, c));
-
- for (int i=0; iapplyForeColour = true;
- layerApplyAttributes->attribute.foreColour = enhancements.at(i).second;
- }
- break;
- case 0x23: // Background colour
- if ((enhancements.at(i).second & 0x60) == 0) {
- layerApplyAttributes->applyBackColour = true;
- layerApplyAttributes->attribute.backColour = enhancements.at(i).second;
- }
- break;
- case 0x27: // Additional flash functions
- if ((enhancements.at(i).second & 0x60) == 0 && (enhancements.at(i).second & 0x18) != 0x18) { // Avoid reserved rate/phase
- layerApplyAttributes->applyFlash = true;
- layerApplyAttributes->attribute.flash.mode = enhancements.at(i).second & 0x03;
- layerApplyAttributes->attribute.flash.ratePhase = (enhancements.at(i).second >> 2) & 0x07;
- }
- break;
- case 0x2c: // Display attributes
- layerApplyAttributes->applyDisplayAttributes = true;
- layerApplyAttributes->attribute.display.doubleHeight = enhancements.at(i).second & 0x01;
- layerApplyAttributes->attribute.display.boxingWindow = enhancements.at(i).second & 0x02;
- layerApplyAttributes->attribute.display.conceal = enhancements.at(i).second & 0x04;
- layerApplyAttributes->attribute.display.invert = enhancements.at(i).second & 0x10;
- layerApplyAttributes->attribute.display.underlineSeparated = enhancements.at(i).second & 0x20;
- // Selecting contiguous mosaics wih a triplet will override an earlier Level 1 separated mosaics attribute
- layerApplyAttributes->attribute.display.forceContiguous = !layerApplyAttributes->attribute.display.underlineSeparated;
- layerApplyAttributes->attribute.display.doubleWidth = enhancements.at(i).second & 0x40;
- break;
- }
- if (m_objectType >= 2)
- m_applyAttributes = *layerApplyAttributes;
-}
-
-
-void Level1Layer::updateRowCache(int r)
-{
- level1CacheAttributes buildCacheAttributes;
- bool doubleHeightAttrFound = false;
-
- for (int c=0; c<40; c++) {
- unsigned char charCode = m_levelOnePage->character(r, c);
- // Set at spacing attributes
- switch (charCode) {
- case 0x0c: // Normal size
- if (buildCacheAttributes.sizeCode != 0x0c) // Size CHANGE resets held mosaic to space
- buildCacheAttributes.holdChar = 0x20;
- buildCacheAttributes.sizeCode = 0x0c;
- break;
- case 0x19: // Contiguous mosaics
- buildCacheAttributes.separated = false;
- break;
- case 0x1a: // Separated mosaics
- buildCacheAttributes.separated = true;
- break;
- case 0x1c: // Black background
- buildCacheAttributes.backColour = 0x00;
- break;
- case 0x1d: // New background
- buildCacheAttributes.backColour = buildCacheAttributes.foreColour & 0x07;
- break;
- case 0x1e: // Hold mosaics
- buildCacheAttributes.held = true;
- break;
- }
-
- if (buildCacheAttributes.mosaics && (charCode & 0x20)) {
- buildCacheAttributes.holdChar = charCode;
- buildCacheAttributes.holdSeparated = buildCacheAttributes.separated;
- }
-
- m_attributeCache[c] = buildCacheAttributes;
-
- // Set-after spacing attributes
- switch (charCode) {
- case 0x00 ... 0x07: // Alphanumeric + foreground colour
- buildCacheAttributes.foreColour = charCode;
- buildCacheAttributes.mosaics = false;
- buildCacheAttributes.holdChar = 0x20; // Switch from mosaics to alpha resets held mosaic
- buildCacheAttributes.holdSeparated = false;
- break;
- case 0x10 ... 0x17: // Mosaic + foreground colour
- buildCacheAttributes.foreColour = charCode & 0x07;
- buildCacheAttributes.mosaics = true;
- break;
- case 0x0d: // Double height
- case 0x0f: // Double size
- doubleHeightAttrFound = true;
- // fall-through
- case 0x0e: // Double width
- if (buildCacheAttributes.sizeCode != charCode) // Size CHANGE resets held mosaic to space
- buildCacheAttributes.holdChar = 0x20;
- buildCacheAttributes.sizeCode = charCode;
- break;
- case 0x1b: // ESC/switch
- buildCacheAttributes.escSwitch ^= true;
- break;
- case 0x1f: // Release mosaics
- buildCacheAttributes.held = false;
- break;
- }
- }
-
- if (doubleHeightAttrFound != m_rowHasDoubleHeightAttr[r]) {
- m_rowHasDoubleHeightAttr[r] = doubleHeightAttrFound;
- for (int dr=r; dr<24; dr++)
- if (m_rowHasDoubleHeightAttr[dr]) {
- m_rowHeight[dr] = TopHalf;
- m_rowHeight[++dr] = BottomHalf;
- } else
- m_rowHeight[dr] = Normal;
- }
-}
-
-textCharacter Level1Layer::character(int r, int c)
-{
- textCharacter result;
-
- if (r != m_rowCached)
- updateRowCache(r);
- if (c > 39 || m_rowHeight[r] == BottomHalf)
- return { 0x20, 0 };
- result.code = m_levelOnePage->character(r, c);
- if (m_levelOnePage->secondCharSet() != 0xf && m_attributeCache[c].escSwitch)
- result.set = g0CharacterMap.value(((m_levelOnePage->secondCharSet() << 3) | m_levelOnePage->secondNOS()), 0);
- else
- result.set = g0CharacterMap.value(((m_levelOnePage->defaultCharSet() << 3) | m_levelOnePage->defaultNOS()), 0);
- if (result.code < 0x20) {
- result.code = m_attributeCache[c].held ? m_attributeCache[c].holdChar : 0x20;
- if (m_attributeCache[c].held && c > 0)
- result.set = 24+m_attributeCache[c].holdSeparated;
-// else
-// result.set = m_attributeCache[c].mosaics*24;
- } else if (m_attributeCache[c].mosaics && (result.code & 0x20))
- result.set = 24+m_attributeCache[c].separated;
- return result;
-}
-
-void Level1Layer::attributes(int r, int c, applyAttributes *layerApplyAttributes)
-{
- unsigned char characterCode;
-
- if (m_rowHeight[r] == BottomHalf) {
- layerApplyAttributes->copyAboveAttributes = true;
- return;
- }
- if (r != m_rowCached)
- updateRowCache(r);
- if (c == 0 || c == 40 || c == 56) {
- // Start of row default conditions, also when crossing into side panels
- layerApplyAttributes->applyForeColour = true;
- layerApplyAttributes->attribute.foreColour = 0x07;
- layerApplyAttributes->applyBackColour = true;
- layerApplyAttributes->attribute.backColour = 0x20;
- layerApplyAttributes->applyDisplayAttributes = true;
- layerApplyAttributes->applyFlash = true;
- layerApplyAttributes->attribute.flash.mode = 0;
- layerApplyAttributes->attribute.flash.ratePhase = 0;
- layerApplyAttributes->attribute.display.doubleHeight = false;
- layerApplyAttributes->attribute.display.doubleWidth = false;
- layerApplyAttributes->attribute.display.boxingWindow = false;
- layerApplyAttributes->attribute.display.conceal = false;
- layerApplyAttributes->attribute.display.invert = false;
- layerApplyAttributes->attribute.display.underlineSeparated = false;
- layerApplyAttributes->attribute.display.forceContiguous = false;
- //TODO fontstyle
- }
- if (c > 39)
- return;
- if (c > 0) {
- // Set-after
- characterCode = m_levelOnePage->character(r, c-1);
- switch (characterCode) {
- case 0x00 ... 0x07: // Alphanumeric + Foreground colour
- case 0x10 ... 0x17: // Mosaic + Foreground colour
- layerApplyAttributes->applyForeColour = true;
- layerApplyAttributes->attribute.foreColour = characterCode & 0x07;
- layerApplyAttributes->attribute.display.conceal = false;
- break;
- case 0x08: // Flashing
- layerApplyAttributes->applyFlash = true;
- layerApplyAttributes->attribute.flash.mode = 1;
- layerApplyAttributes->attribute.flash.ratePhase = 0;
- break;
- case 0x0a: // End box
- if (m_levelOnePage->character(r, c) == 0x0a) {
- layerApplyAttributes->applyBoxingOnly = true;
- layerApplyAttributes->attribute.display.boxingWindow = false;
- }
- break;
- case 0x0b: // Start box
- if (m_levelOnePage->character(r, c) == 0x0b) {
- layerApplyAttributes->applyBoxingOnly = true;
- layerApplyAttributes->attribute.display.boxingWindow = true;
- }
- break;
- case 0x0d: // Double height
- layerApplyAttributes->applyTextSizeOnly = true;
- layerApplyAttributes->attribute.display.doubleHeight = true;
- layerApplyAttributes->attribute.display.doubleWidth = false;
- break;
- case 0x0e: // Double width
- layerApplyAttributes->applyTextSizeOnly = true;
- layerApplyAttributes->attribute.display.doubleHeight = false;
- layerApplyAttributes->attribute.display.doubleWidth = true;
- break;
- case 0x0f: // Double size
- layerApplyAttributes->applyTextSizeOnly = true;
- layerApplyAttributes->attribute.display.doubleHeight = true;
- layerApplyAttributes->attribute.display.doubleWidth = true;
- break;
- }
- }
- // Set-at
- characterCode = m_levelOnePage->character(r, c);
- switch (characterCode) {
- case 0x09: // Steady
- layerApplyAttributes->applyFlash = true;
- layerApplyAttributes->attribute.flash.mode = 0;
- layerApplyAttributes->attribute.flash.ratePhase = 0;
- break;
- case 0x0c: // Normal size
- layerApplyAttributes->applyTextSizeOnly = true;
- layerApplyAttributes->attribute.display.doubleHeight = false;
- layerApplyAttributes->attribute.display.doubleWidth = false;
- break;
- case 0x18: // Conceal
- layerApplyAttributes->applyConcealOnly = true;
- layerApplyAttributes->attribute.display.conceal = true;
- break;
- case 0x19: // Contiguous mosaics
- layerApplyAttributes->applyContiguousOnly = true;
- break;
- case 0x1c: // Black background
- layerApplyAttributes->applyBackColour = true;
- layerApplyAttributes->attribute.backColour = 0x20;
- break;
- case 0x1d: // New background
- layerApplyAttributes->applyBackColour = true;
- layerApplyAttributes->attribute.backColour = m_attributeCache[c].backColour;
- break;
- }
-}
-
-
-ActivePosition::ActivePosition()
-{
- m_row = m_column = -1;
-}
-
-bool ActivePosition::setRow(int newRow)
-{
- if (newRow < m_row)
- return false;
- if (newRow > m_row) {
- m_row = newRow;
- m_column = -1;
- }
- return true;
-}
-
-bool ActivePosition::setColumn(int newColumn)
-{
- if (newColumn < m_column)
- return false;
- if (m_row == -1)
- m_row = 0;
- m_column = newColumn;
- return true;
-}
-/*
-bool ActivePosition::setRowAndColumn(int newRow, int newColumn)
-{
- if (!setRow(newRow))
- return false;
- return setColumn(newColumn);
-}
-*/
diff --git a/decode.h b/decode.h
index e01b468..c428367 100644
--- a/decode.h
+++ b/decode.h
@@ -20,239 +20,49 @@
#ifndef DECODE_H
#define DECODE_H
+#include
#include
#include
-#include
-#include
#include "levelonepage.h"
-struct textCharacter {
- unsigned char code=0x20;
- int set=0;
- int diacritical=0;
-};
-
-inline bool operator!=(const textCharacter &lhs, const textCharacter &rhs)
-{
- return lhs.code != rhs.code ||
- lhs.set != rhs.set ||
- lhs.diacritical != rhs.diacritical;
-}
-
-struct displayAttributes {
- bool doubleHeight=false;
- bool doubleWidth=false;
- bool boxingWindow=false;
- bool conceal=false;
- bool invert=false;
- bool underlineSeparated=false;
- bool forceContiguous=false;
-};
-
-inline bool operator!=(const displayAttributes &lhs, const displayAttributes &rhs)
-{
- return lhs.doubleHeight != rhs.doubleHeight ||
- lhs.doubleWidth != rhs.doubleWidth ||
- lhs.boxingWindow != rhs.boxingWindow ||
- lhs.conceal != rhs.conceal ||
- lhs.invert != rhs.invert ||
- lhs.underlineSeparated != rhs.underlineSeparated ||
- lhs.forceContiguous != rhs.forceContiguous;
-}
-
-struct textAttributes {
- int foreColour=0x07;
- int backColour=0x00;
- struct flashFunctions {
- int mode=0;
- int ratePhase=0;
- int phaseNumber=0;
- } flash;
- displayAttributes display;
- /* font style */
-};
-
-inline bool operator!=(const textAttributes &lhs, const textAttributes &rhs)
-{
- return lhs.foreColour != rhs.foreColour ||
- lhs.backColour != rhs.backColour ||
- lhs.flash.mode != rhs.flash.mode ||
- lhs.flash.ratePhase != rhs.flash.ratePhase ||
- lhs.flash.phaseNumber != rhs.flash.phaseNumber ||
- lhs.display != rhs.display;
-}
-
-struct textCell {
- textCharacter character;
- textAttributes attribute;
- bool bottomHalf=false;
- bool rightHalf=false;
- bool level1Mosaic=false;
- int level1CharSet=0;
-};
-
-inline bool operator!=(const textCell &lhs, const textCell &rhs)
-{
- return lhs.character != rhs.character ||
- lhs.attribute != rhs.attribute ||
- lhs.bottomHalf != rhs.bottomHalf ||
- lhs.rightHalf != rhs.rightHalf ||
- lhs.level1Mosaic != rhs.level1Mosaic ||
- lhs.level1CharSet != rhs.level1CharSet;
-}
-
-struct applyAttributes {
- bool applyForeColour=false;
- bool applyBackColour=false;
- bool applyFlash=false;
- bool applyDisplayAttributes=false;
- bool applyTextSizeOnly=false;
- bool applyBoxingOnly=false;
- bool applyConcealOnly=false;
- bool applyContiguousOnly=false;
- bool copyAboveAttributes=false;
- textAttributes attribute;
-};
-
-class ActivePosition
-{
-public:
- ActivePosition();
- int row() const { return (m_row == -1) ? 0 : m_row; }
- int column() const { return (m_column == -1) ? 0 : m_column; }
- bool isDeployed() const { return m_row != -1; }
- bool setRow(int);
- bool setColumn(int);
-// bool setRowAndColumn(int, int);
-
-private:
- int m_row, m_column;
-};
-
-
-class TextLayer
-{
-public:
-// TextLayer(TeletextPage* thePage) : currentPage(thePage) { };
- virtual ~TextLayer() = default;
- void setTeletextPage(LevelOnePage *);
- virtual textCharacter character(int, int) =0;
- virtual void attributes(int, int, applyAttributes *) =0;
- virtual int fullScreenColour() const =0;
- virtual int fullRowColour(int) const =0;
- virtual bool fullRowDownwards(int) const =0;
- virtual int objectType() const =0;
- virtual int originR() const { return 0; };
- virtual int originC() const { return 0; };
- void setFullScreenColour(int);
- void setFullRowColour(int, int, bool);
-
- // Key QPair is row and column, value QPair is triplet mode and data
- QMultiMap, QPair> enhanceMap;
-
-protected:
- LevelOnePage* m_levelOnePage;
- int m_layerFullScreenColour=-1;
- int m_layerFullRowColour[25];
- bool m_layerFullRowDownwards[25];
- applyAttributes m_applyAttributes;
-};
-
-class EnhanceLayer: public TextLayer
-{
-public:
- EnhanceLayer();
- textCharacter character(int, int);
- void attributes(int, int, applyAttributes *);
- int fullScreenColour() const { return m_layerFullScreenColour; };
- int fullRowColour(int r) const { return m_layerFullRowColour[r]; };
- bool fullRowDownwards(int r) const { return m_layerFullRowDownwards[r]; };
- int objectType() const { return m_objectType; };
- int originR() const { return m_originR; };
- int originC() const { return m_originC; };
- void setObjectType(int);
- void setOrigin(int, int);
-
-protected:
- int m_objectType=0;
- int m_originR=0;
- int m_originC=0;
- int m_rowCached=-1;
- int m_rightMostColumn[25];
-};
-
-class Level1Layer: public TextLayer
-{
-public:
- enum RowHeight { Normal=-1, TopHalf, BottomHalf };
-
-// Level1Layer(TeletextPage *thePage) : TextLayer(thePage) { };
- Level1Layer();
- textCharacter character(int, int);
- void attributes(int, int, applyAttributes *);
- int fullScreenColour() const { return -1; };
- int fullRowColour(int) const { return -1; };
- bool fullRowDownwards(int) const { return false; };
- int objectType() const { return 0; }
- RowHeight rowHeight(int r) const { return m_rowHeight[r]; };
-
-private:
- void updateRowCache(int);
-
- struct level1CacheAttributes {
- int foreColour=0x07;
- int backColour=0x00;
- unsigned char sizeCode=0x0c;
- bool mosaics=false;
- bool separated=false;
- bool held=false;
- bool escSwitch=false;
- unsigned char holdChar=0x20;
- bool holdSeparated=false;
- };
- level1CacheAttributes m_attributeCache[40];
- int m_rowCached=-1;
- bool m_rowHasDoubleHeightAttr[25];
- RowHeight m_rowHeight[25];
-};
-
class TeletextPageDecode : public QObject
{
Q_OBJECT
public:
enum CharacterFragment { NormalSize, DoubleHeightTopHalf, DoubleHeightBottomHalf, DoubleWidthLeftHalf, DoubleWidthRightHalf, DoubleSizeTopLeftQuarter, DoubleSizeTopRightQuarter, DoubleSizeBottomLeftQuarter, DoubleSizeBottomRightQuarter };
+ enum RowHeight { NormalHeight, TopHalf, BottomHalf };
TeletextPageDecode();
~TeletextPageDecode();
bool refresh(int r, int c) const { return m_refresh[r][c]; }
void setRefresh(int, int, bool);
void decodePage();
- void decodeRow(int r);
LevelOnePage *teletextPage() const { return m_levelOnePage; };
void setTeletextPage(LevelOnePage *);
void updateSidePanels();
- void buildEnhanceMap(TextLayer *, int=0);
- unsigned char cellCharacterCode(int r, int c) { return cellAtCharacterOrigin(r, c).character.code; };
- int cellCharacterSet(int r, int c) { return cellAtCharacterOrigin(r, c).character.set; };
- int cellCharacterDiacritical(int r, int c) { return cellAtCharacterOrigin(r, c).character.diacritical; };
- int cellForegroundCLUT(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.foreColour; };
- int cellBackgroundCLUT(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.backColour; };
+ unsigned char cellCharacterCode(int r, int c) const { return m_cell[r][c].character.code; };
+ int cellCharacterSet(int r, int c) const { return m_cell[r][c].character.set; };
+ int cellCharacterDiacritical(int r, int c) const { return m_cell[r][c].character.diacritical; };
+ int cellForegroundCLUT(int r, int c) const { return m_cell[r][c].attribute.foregroundCLUT; };
+ int cellBackgroundCLUT(int r, int c) const { return m_cell[r][c].attribute.backgroundCLUT; };
QColor cellForegroundQColor(int, int);
QColor cellBackgroundQColor(int, int);
QColor cellFlashForegroundQColor(int, int);
- int cellFlashMode(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.flash.mode; };
- int cellFlashRatePhase(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.flash.ratePhase; };
- int cellFlash2HzPhaseNumber(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.flash.phaseNumber; };
- CharacterFragment cellCharacterFragment(int, int) const;
- bool cellBoxed(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.display.boxingWindow; };
- bool cellConceal(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.display.conceal; };
- bool cellUnderlined(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.display.underlineSeparated && cellAtCharacterOrigin(r, c).character.set < 24; };
+ int cellFlashMode(int r, int c) const { return m_cell[r][c].attribute.flash.mode; };
+ int cellFlashRatePhase(int r, int c) const { return m_cell[r][c].attribute.flash.ratePhase; };
+ int cellFlash2HzPhaseNumber(int r, int c) const { return m_cell[r][c].attribute.flash.phase2HzShown; };
+ CharacterFragment cellCharacterFragment(int r, int c) const { return m_cell[r][c].fragment; };
+ 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 level1MosaicAttribute(int r, int c) const { return m_cell[r][c].level1Mosaic; };
- int level1CharSet(int r, int c) const { return m_cell[r][c].level1CharSet; };
+ bool level1MosaicAttribute(int r, int c) const { return m_cellLevel1Mosaic[r][c]; };
+ int level1CharSet(int r, int c) const { return m_cellLevel1CharSet[r][c]; };
+
+ RowHeight rowHeight(int r) const { return m_rowHeight[r]; };
QColor fullScreenQColor() const { return m_finalFullScreenQColor; };
QColor fullRowQColor(int r) const { return m_fullRowQColor[r]; };
@@ -270,37 +80,165 @@ signals:
protected:
inline void setFullScreenColour(int);
inline void setFullRowColour(int, int);
- textCell& cellAtCharacterOrigin(int, int);
int m_finalFullScreenColour, m_level;
QColor m_finalFullScreenQColor;
int m_leftSidePanelColumns, m_rightSidePanelColumns;
- Level1Layer m_level1Layer;
- std::vector m_textLayer;
const int m_foregroundRemap[8] = { 0, 0, 0, 8, 8, 16, 16, 16 };
const int m_backgroundRemap[8] = { 0, 8, 16, 8, 16, 8, 16, 24 };
private:
+ class Invocation;
+
enum ColourPart { Foreground, Background, FlashForeground };
- QColor cellQColor(int, int, ColourPart);
+ struct textCharacter {
+ unsigned char code=0x20;
+ int set=0;
+ int diacritical=0;
+ };
+
+ friend inline bool operator!=(const textCharacter &lhs, const textCharacter &rhs)
+ {
+ return lhs.code != rhs.code ||
+ lhs.set != rhs.set ||
+ lhs.diacritical != rhs.diacritical;
+ }
+
+ struct flashFunctions {
+ int mode=0;
+ int ratePhase=0;
+ int phase2HzShown=0;
+ } flash;
+
+ struct displayAttributes {
+ bool doubleHeight=false;
+ bool doubleWidth=false;
+ bool boxingWindow=false;
+ bool conceal=false;
+ bool invert=false;
+ bool underlineSeparated=false;
+ };
+
+ friend inline bool operator!=(const displayAttributes &lhs, const displayAttributes &rhs)
+ {
+ return lhs.doubleHeight != rhs.doubleHeight ||
+ lhs.doubleWidth != rhs.doubleWidth ||
+ lhs.boxingWindow != rhs.boxingWindow ||
+ lhs.conceal != rhs.conceal ||
+ lhs.invert != rhs.invert ||
+ lhs.underlineSeparated != rhs.underlineSeparated;
+ }
+
+ struct textAttributes {
+ int foregroundCLUT=7;
+ int backgroundCLUT=0;
+ flashFunctions flash;
+ displayAttributes display;
+ };
+
+ friend inline bool operator!=(const textAttributes &lhs, const textAttributes &rhs)
+ {
+ return lhs.foregroundCLUT != rhs.foregroundCLUT ||
+ lhs.backgroundCLUT != rhs.backgroundCLUT ||
+ lhs.flash.mode != rhs.flash.mode ||
+ lhs.flash.ratePhase != rhs.flash.ratePhase ||
+ lhs.flash.phase2HzShown != rhs.flash.phase2HzShown ||
+ lhs.display != rhs.display;
+ }
+
+ struct textCell {
+ textCharacter character;
+ textAttributes attribute;
+ CharacterFragment fragment=NormalSize;
+ };
+
+ friend inline bool operator!=(const textCell &lhs, const textCell &rhs)
+ {
+ return lhs.character != rhs.character ||
+ lhs.attribute != rhs.attribute ||
+ lhs.fragment != rhs.fragment;
+ }
+
+ struct textPainter {
+ textAttributes attribute;
+ textCell result;
+ textCell rightHalfCell;
+ textCell bottomHalfCell[72];
+ };
+
+ const QMap m_g0CharacterMap {
+ { 0x00, 12 }, { 0x01, 15 }, { 0x02, 22 }, { 0x03, 16 }, { 0x04, 14 }, { 0x05, 19 }, { 0x06, 11 },
+ { 0x08, 18 }, { 0x09, 15 }, { 0x0a, 22 }, { 0x0b, 16 }, { 0x0c, 14 }, { 0x0e, 11 },
+ { 0x10, 12 }, { 0x11, 15 }, { 0x12, 22 }, { 0x13, 16 }, { 0x14, 14 }, { 0x15, 19 }, { 0x16, 23 },
+ { 0x1d, 21 }, { 0x1f, 20 },
+ { 0x20, 1 }, { 0x21, 15 }, { 0x22, 13 }, { 0x23, 17 }, { 0x24, 2 }, { 0x25, 3 }, { 0x26, 11 },
+ { 0x36, 23 }, { 0x37, 4 },
+ { 0x40, 12 }, { 0x44, 14 }, { 0x47, 5 },
+ { 0x55, 6 }, { 0x57, 5 }
+ };
+
+ class Invocation
+ {
+ public:
+ Invocation();
+
+ X26TripletList *tripletList() const { return m_tripletList; };
+ void setTripletList(X26TripletList *);
+ int startTripletNumber() const { return m_startTripletNumber; };
+ void setStartTripletNumber(int);
+ int endTripletNumber() const { return m_endTripletNumber; };
+ void setEndTripletNumber(int);
+ int originRow() const { return m_originRow; };
+ int originColumn() const { return m_originColumn; };
+ void setOrigin(int, int);
+ void buildMap(int);
+
+ QList> charPositions() const { return m_characterMap.uniqueKeys(); };
+ QList> attrPositions() const { return m_attributeMap.uniqueKeys(); };
+ QList charactersMappedAt(int r, int c) const { return m_characterMap.values(qMakePair(r, c)); };
+ QList attributesMappedAt(int r, int c) const { return m_attributeMap.values(qMakePair(r, c)); };
+ int rightMostColumn(int r) const { return m_rightMostColumn.value(r, -1); };
+ int fullScreenColour() const { return m_fullScreenCLUT; };
+ QList fullRowColoursMappedAt(int r) const { return m_fullRowCLUTMap.values(r); };
+
+ private:
+ X26TripletList *m_tripletList;
+ int m_startTripletNumber, m_endTripletNumber;
+ int m_originRow, m_originColumn;
+ // QPair is row and column
+ QMultiMap, X26Triplet> m_characterMap;
+ QMultiMap, X26Triplet> m_attributeMap;
+ QMap m_rightMostColumn;
+ int m_fullScreenCLUT;
+ QMultiMap m_fullRowCLUTMap;
+ };
+
+ static int s_instances;
+ static textPainter s_blankPainter;
+
+ void decodeRow(int r);
+ QColor cellQColor(int, int, ColourPart);
+ textCell& cellAtCharacterOrigin(int, int);
+ void buildInvocationList(Invocation &, int);
+ textCharacter characterFromTriplets(const QList, int);
+ inline void rotateFlashMovement(flashFunctions &);
- textCell m_cell[25][72];
bool m_refresh[25][72];
+ textCell m_cell[25][72];
+ bool m_cellLevel1Mosaic[25][40];
+ int m_cellLevel1CharSet[25][40];
LevelOnePage* m_levelOnePage;
int m_fullRowColour[25];
QColor m_fullRowQColor[25];
-};
+ QList m_invocations[3];
+ Invocation m_localEnhancements;
+ textPainter m_level1ActivePainter;
+ QList m_adapPassPainter[2];
+ int m_level1DefaultCharSet, m_level1SecondCharSet;
+ int m_x26DefaultG0CharSet, m_x26DefaultG2CharSet;
-static const QMap g0CharacterMap {
- { 0x00, 12 }, { 0x01, 15 }, { 0x02, 22 }, { 0x03, 16 }, { 0x04, 14 }, { 0x05, 19 }, { 0x06, 11 },
- { 0x08, 18 }, { 0x09, 15 }, { 0x0a, 22 }, { 0x0b, 16 }, { 0x0c, 14 }, { 0x0e, 11 },
- { 0x10, 12 }, { 0x11, 15 }, { 0x12, 22 }, { 0x13, 16 }, { 0x14, 14 }, { 0x15, 19 }, { 0x16, 23 },
- { 0x1d, 21 }, { 0x1f, 20 },
- { 0x20, 1 }, { 0x21, 15 }, { 0x22, 13 }, { 0x23, 17 }, { 0x24, 2 }, { 0x25, 3 }, { 0x26, 11 },
- { 0x36, 23 }, { 0x37, 4 },
- { 0x40, 12 }, { 0x44, 14 }, { 0x47, 5 },
- { 0x55, 6 }, { 0x57, 5 }
+ RowHeight m_rowHeight[25];
};
#endif
diff --git a/mainwidget.cpp b/mainwidget.cpp
index f12cbf1..7643ee9 100644
--- a/mainwidget.cpp
+++ b/mainwidget.cpp
@@ -96,7 +96,10 @@ void TeletextWidget::subPageSelected()
void TeletextWidget::refreshRow(int rowChanged)
{
- m_pageDecode.decodeRow(rowChanged);
+ Q_UNUSED(rowChanged);
+
+ // TODO trace signals where this is called so we can remove this
+ m_pageDecode.decodePage();
update();
}
@@ -181,8 +184,10 @@ void TeletextWidget::setShowControlCodes(bool showControlCodes)
void TeletextWidget::setControlBit(int bitNumber, bool active)
{
m_levelOnePage->setControlBit(bitNumber, active);
- if (bitNumber == 1 || bitNumber == 2)
+ if (bitNumber == 1 || bitNumber == 2) {
m_pageDecode.decodePage();
+ m_pageRender.renderPage(true);
+ }
}
void TeletextWidget::setDefaultCharSet(int newDefaultCharSet)