15 Commits

Author SHA1 Message Date
G.K.MacGregor
88ea066481 Tag version 0.6.1-alpha 2023-06-24 18:05:05 +01:00
G.K.MacGregor
7b59c78cee Show correct G0 and G2 character set from page in widgets 2023-06-23 18:39:36 +01:00
G.K.MacGregor
c8f61d4d2c Fix default palette detection of CLUTs 1:7 and 3:7 2023-06-20 18:42:01 +01:00
G.K.MacGregor
bd894a523e Show language names with mod'd G0/G2 character set triplet 2023-06-15 18:55:47 +01:00
G.K.MacGregor
27ae092b5e Lock out CLUT 0-1 editing when Level is not 3.5 2023-06-14 18:21:23 +01:00
G.K.MacGregor
43dcccb00c Fix to reserved value in mode menu 2023-06-12 21:54:59 +01:00
G.K.MacGregor
0d0db2f8b6 Implement modified G0 and G2 character set designation triplet 2023-06-12 16:37:50 +01:00
G.K.MacGregor
61c52fe7cc Fix resetting reserved bit for display attributes 2023-05-21 18:32:25 +01:00
G.K.MacGregor
4b6ea4398d Implement Level 3.5 bold and italic font style
Proportional font attributes are tracked within the decoder, but the effect
is not rendered.
2023-05-21 15:33:21 +01:00
G.K.MacGregor
ed821bde45 Use proper composition mode when copying lines to flash buffers 2023-05-14 17:39:08 +01:00
G.K.MacGregor
955a1262e9 Clear invocation QMaps on empty triplet list
Fixes a bug where local enhancements hung around when switching to a subpage
without enhancements.
2023-05-09 23:35:29 +01:00
G.K.MacGregor
021fbfa60f Rewrite the decoder
A complete rewrite of decode.cpp and decode.h to make it easier to follow
and maintain. Rather than a set of "layers" descended from C++ classes, the
for-loop across the rows and columns explicitly lays out the order of
processing the Level 1 characters and attributes, the X/26 enhancements
that affect the page directly as well as those within each Invocation of an
Object, followed by selecting which Invoked Object to place in the character
cell or in the absence of an Object the underlying page fragment will be
placed in the cell.

The new decoder has the following improvements over the old...
- Local Enhancement Data has priority over Active Objects.
- Active Objects can set Full Screen and Full Row colours.
- Incremental/decremental flash phases are tracked correctly within Objects.
- X/26 characters overwriting the bottom half of Level 1 Double Height rows.
- Interaction between the underlying page and Objects where characters of
  different sizes overlap.
2023-05-07 19:25:06 +01:00
G.K.MacGregor
801efa570c Also track Active Position of Level 1.5 decoding
This is needed for the upcoming decoder write as decode.cpp will track the
Active Position from the internal data of x26triplets.

If decode.cpp is rewritten again to track the Active Position independently
of x26triplets this commit may be reverted, but then again tracking how the
Active Position moves on both levels 1.5 and 2.5+ may be useful in terms of
warning the user of differing results on different decoder levels.
2023-05-02 18:56:06 +01:00
G.K.MacGregor
0b50b064db Set address when inserting a Address Row 0 triplet 2023-04-12 21:18:22 +01:00
G.K.MacGregor
c5e3fd5668 Optimise rendering of pages with flashing characters
Previously every character on the page was drawn up to six times. Now the
page is drawn once, quickly copied up to five times and then only the
flashing characters are redrawn on the copies.
2023-03-19 17:38:52 +00:00
15 changed files with 1523 additions and 1240 deletions

View File

@@ -29,8 +29,7 @@ The following X/26 enhancement triplets are not rendered by the editor, although
- Invocation of Objects from POP and GPOP pages. - Invocation of Objects from POP and GPOP pages.
- DRCS characters. - DRCS characters.
- Modified G0 and G2 character set designation using X/26 triplets with mode 01000. - Modified G0 and G2 character set designation using X/26 triplets with mode 01000.
- Full screen and full row colours set by Active Objects. - Proportional font spacing on Level 3.5
- Level 3.5 font style: bold, italic and proportional spacing.
## Using the X/26 triplet editor ## Using the X/26 triplet editor
The X/26 triplet editor sorts all the triplet modes available into categories, which are: The X/26 triplet editor sorts all the triplet modes available into categories, which are:

1672
decode.cpp

File diff suppressed because it is too large Load Diff

418
decode.h
View File

@@ -20,239 +20,52 @@
#ifndef DECODE_H #ifndef DECODE_H
#define DECODE_H #define DECODE_H
#include <QList>
#include <QMap> #include <QMap>
#include <QMultiMap> #include <QMultiMap>
#include <QPair>
#include <vector>
#include "levelonepage.h" #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<int, int>, QPair<int, int>> 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 class TeletextPageDecode : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum CharacterFragment { NormalSize, DoubleHeightTopHalf, DoubleHeightBottomHalf, DoubleWidthLeftHalf, DoubleWidthRightHalf, DoubleSizeTopLeftQuarter, DoubleSizeTopRightQuarter, DoubleSizeBottomLeftQuarter, DoubleSizeBottomRightQuarter }; enum CharacterFragment { NormalSize, DoubleHeightTopHalf, DoubleHeightBottomHalf, DoubleWidthLeftHalf, DoubleWidthRightHalf, DoubleSizeTopLeftQuarter, DoubleSizeTopRightQuarter, DoubleSizeBottomLeftQuarter, DoubleSizeBottomRightQuarter };
enum RowHeight { NormalHeight, TopHalf, BottomHalf };
TeletextPageDecode(); TeletextPageDecode();
~TeletextPageDecode(); ~TeletextPageDecode();
bool refresh(int r, int c) const { return m_refresh[r][c]; } bool refresh(int r, int c) const { return m_refresh[r][c]; }
void setRefresh(int, int, bool); void setRefresh(int, int, bool);
void decodePage(); void decodePage();
void decodeRow(int r);
LevelOnePage *teletextPage() const { return m_levelOnePage; }; LevelOnePage *teletextPage() const { return m_levelOnePage; };
void setTeletextPage(LevelOnePage *); void setTeletextPage(LevelOnePage *);
void updateSidePanels(); void updateSidePanels();
void buildEnhanceMap(TextLayer *, int=0);
unsigned char cellCharacterCode(int r, int c) { return cellAtCharacterOrigin(r, c).character.code; }; unsigned char cellCharacterCode(int r, int c) const { return m_cell[r][c].character.code; };
int cellCharacterSet(int r, int c) { return cellAtCharacterOrigin(r, c).character.set; }; int cellCharacterSet(int r, int c) const { return m_cell[r][c].character.set; };
int cellCharacterDiacritical(int r, int c) { return cellAtCharacterOrigin(r, c).character.diacritical; }; int cellCharacterDiacritical(int r, int c) const { return m_cell[r][c].character.diacritical; };
int cellForegroundCLUT(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.foreColour; }; int cellForegroundCLUT(int r, int c) const { return m_cell[r][c].attribute.foregroundCLUT; };
int cellBackgroundCLUT(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.backColour; }; int cellBackgroundCLUT(int r, int c) const { return m_cell[r][c].attribute.backgroundCLUT; };
QColor cellForegroundQColor(int, int); QColor cellForegroundQColor(int, int);
QColor cellBackgroundQColor(int, int); QColor cellBackgroundQColor(int, int);
QColor cellFlashForegroundQColor(int, int); QColor cellFlashForegroundQColor(int, int);
int cellFlashMode(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.flash.mode; }; int cellFlashMode(int r, int c) const { return m_cell[r][c].attribute.flash.mode; };
int cellFlashRatePhase(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.flash.ratePhase; }; int cellFlashRatePhase(int r, int c) const { return m_cell[r][c].attribute.flash.ratePhase; };
int cellFlash2HzPhaseNumber(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.flash.phaseNumber; }; int cellFlash2HzPhaseNumber(int r, int c) const { return m_cell[r][c].attribute.flash.phase2HzShown; };
CharacterFragment cellCharacterFragment(int, int) const; CharacterFragment cellCharacterFragment(int r, int c) const { return m_cell[r][c].fragment; };
bool cellBoxed(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.display.boxingWindow; }; bool cellBoxed(int r, int c) const { return m_cell[r][c].attribute.display.boxingWindow; };
bool cellConceal(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.display.conceal; }; bool cellConceal(int r, int c) const { return m_cell[r][c].attribute.display.conceal; };
bool cellUnderlined(int r, int c) { return cellAtCharacterOrigin(r, c).attribute.display.underlineSeparated && cellAtCharacterOrigin(r, c).character.set < 24; }; 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_cell[r][c].level1Mosaic; }; bool level1MosaicAttribute(int r, int c) const { return m_cellLevel1Mosaic[r][c]; };
int level1CharSet(int r, int c) const { return m_cell[r][c].level1CharSet; }; 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 fullScreenQColor() const { return m_finalFullScreenQColor; };
QColor fullRowQColor(int r) const { return m_fullRowQColor[r]; }; QColor fullRowQColor(int r) const { return m_fullRowQColor[r]; };
@@ -270,37 +83,196 @@ signals:
protected: protected:
inline void setFullScreenColour(int); inline void setFullScreenColour(int);
inline void setFullRowColour(int, int); inline void setFullRowColour(int, int);
textCell& cellAtCharacterOrigin(int, int);
int m_finalFullScreenColour, m_level; int m_finalFullScreenColour, m_level;
QColor m_finalFullScreenQColor; QColor m_finalFullScreenQColor;
int m_leftSidePanelColumns, m_rightSidePanelColumns; int m_leftSidePanelColumns, m_rightSidePanelColumns;
Level1Layer m_level1Layer;
std::vector<TextLayer *> m_textLayer;
const int m_foregroundRemap[8] = { 0, 0, 0, 8, 8, 16, 16, 16 }; 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 }; const int m_backgroundRemap[8] = { 0, 8, 16, 8, 16, 8, 16, 24 };
private: private:
class Invocation;
enum ColourPart { Foreground, Background, FlashForeground }; 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;
};
struct displayAttributes {
bool doubleHeight=false;
bool doubleWidth=false;
bool boxingWindow=false;
bool conceal=false;
bool invert=false;
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 ||
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;
fontStyle style;
};
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 ||
lhs.style.proportional != rhs.style.proportional ||
lhs.style.bold != rhs.style.bold ||
lhs.style.italic != rhs.style.italic;
}
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];
int g0CharSet=0;
int g2CharSet=7;
int styleSpreadRows=0;
int setProportionalRows[72], clearProportionalRows[72];
int setBoldRows[72], clearBoldRows[72];
int setItalicRows[72], clearItalicRows[72];
};
const QMap<int, int> m_level1CharacterMap {
{ 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 }
};
const QMap<int, int> m_g0CharacterMap {
{ 0x20, 1 }, { 0x24, 2 }, { 0x25, 3 },
{ 0x37, 4 },
{ 0x47, 5 },
{ 0x55, 6 }, { 0x57, 5 }
};
const QMap<int, int> m_g2CharacterMap {
{ 0x20, 8 }, { 0x24, 8 }, { 0x25, 8 },
{ 0x37, 9 },
{ 0x40, 10 }, { 0x44, 10 }, { 0x47, 10 },
{ 0x55, 10 }, { 0x57, 10 }
};
class Invocation
{
public:
Invocation();
X26TripletList *tripletList() const { return m_tripletList; };
void clear();
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<QPair<int, int>> charPositions() const { return m_characterMap.uniqueKeys(); };
QList<QPair<int, int>> attrPositions() const { return m_attributeMap.uniqueKeys(); };
QList<X26Triplet> charactersMappedAt(int r, int c) const { return m_characterMap.values(qMakePair(r, c)); };
QList<X26Triplet> 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<X26Triplet> 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<QPair<int, int>, X26Triplet> m_characterMap;
QMultiMap<QPair<int, int>, X26Triplet> m_attributeMap;
QMap<int, int> m_rightMostColumn;
int m_fullScreenCLUT;
QMultiMap<int, X26Triplet> 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<X26Triplet>);
inline void rotateFlashMovement(flashFunctions &);
textCell m_cell[25][72];
bool m_refresh[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; LevelOnePage* m_levelOnePage;
int m_fullRowColour[25]; int m_fullRowColour[25];
QColor m_fullRowQColor[25]; QColor m_fullRowQColor[25];
}; QList<Invocation> m_invocations[3];
Invocation m_localEnhancements;
textPainter m_level1ActivePainter;
QList<textPainter> m_adapPassPainter[2];
int m_level1DefaultCharSet, m_level1SecondCharSet;
int m_defaultG0andG2, m_secondG0andG2;
static const QMap<int, int> g0CharacterMap { RowHeight m_rowHeight[25];
{ 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 }
}; };
#endif #endif

View File

@@ -297,7 +297,7 @@ bool LevelOnePage::packetExists(int packetNumber, int designationCode) const
return !isPaletteDefault(16, 31); return !isPaletteDefault(16, 31);
} }
if (designationCode == 4) if (designationCode == 4)
return !isPaletteDefault(0,15); return !isPaletteDefault(0, 15);
} }
return PageBase::packetExists(packetNumber, designationCode); return PageBase::packetExists(packetNumber, designationCode);
@@ -396,7 +396,7 @@ bool LevelOnePage::isPaletteDefault(int colour) const
bool LevelOnePage::isPaletteDefault(int fromColour, int toColour) const bool LevelOnePage::isPaletteDefault(int fromColour, int toColour) const
{ {
for (int i=fromColour; i<toColour; i++) for (int i=fromColour; i<=toColour; i++)
if (m_CLUT[i] != m_defaultCLUT[i]) if (m_CLUT[i] != m_defaultCLUT[i])
return false; return false;
@@ -412,7 +412,7 @@ int LevelOnePage::levelRequired() const
// TODO Check for X/28/1 for DCLUT for mode 1-3 DRCS characters - return 3 // TODO Check for X/28/1 for DCLUT for mode 1-3 DRCS characters - return 3
// Assume Level 2.5 if any X/28 page enhancements are present, otherwise assume Level 1 // Assume Level 2.5 if any X/28 page enhancements are present, otherwise assume Level 1
int levelSeen = (!isPaletteDefault(16,31) || m_leftSidePanelDisplayed || m_rightSidePanelDisplayed || m_defaultScreenColour !=0 || m_defaultRowColour !=0 || m_blackBackgroundSubst || m_colourTableRemap !=0 || m_defaultCharSet != 0 || m_secondCharSet != 0xf) ? 2 : 0; int levelSeen = (!isPaletteDefault(16, 31) || m_leftSidePanelDisplayed || m_rightSidePanelDisplayed || m_defaultScreenColour !=0 || m_defaultRowColour !=0 || m_blackBackgroundSubst || m_colourTableRemap !=0 || m_defaultCharSet != 0 || m_secondCharSet != 0xf) ? 2 : 0;
// If there's no X/26 triplets, exit here as Level 1 or 2.5 // If there's no X/26 triplets, exit here as Level 1 or 2.5
if (m_enhancements.isEmpty()) if (m_enhancements.isEmpty())

View File

@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
QApplication::setApplicationDisplayName(QApplication::applicationName()); QApplication::setApplicationDisplayName(QApplication::applicationName());
QApplication::setOrganizationName("gkmac.co.uk"); QApplication::setOrganizationName("gkmac.co.uk");
QApplication::setOrganizationDomain("gkmac.co.uk"); QApplication::setOrganizationDomain("gkmac.co.uk");
QApplication::setApplicationVersion("0.6-alpha"); QApplication::setApplicationVersion("0.6.1-alpha");
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription(QApplication::applicationName()); parser.setApplicationDescription(QApplication::applicationName());
parser.addHelpOption(); parser.addHelpOption();

View File

@@ -96,7 +96,10 @@ void TeletextWidget::subPageSelected()
void TeletextWidget::refreshRow(int rowChanged) 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(); update();
} }
@@ -181,8 +184,10 @@ void TeletextWidget::setShowControlCodes(bool showControlCodes)
void TeletextWidget::setControlBit(int bitNumber, bool active) void TeletextWidget::setControlBit(int bitNumber, bool active)
{ {
m_levelOnePage->setControlBit(bitNumber, active); m_levelOnePage->setControlBit(bitNumber, active);
if (bitNumber == 1 || bitNumber == 2) if (bitNumber == 1 || bitNumber == 2) {
m_pageDecode.decodePage(); m_pageDecode.decodePage();
m_pageRender.renderPage(true);
}
} }
void TeletextWidget::setDefaultCharSet(int newDefaultCharSet) void TeletextWidget::setDefaultCharSet(int newDefaultCharSet)

View File

@@ -836,10 +836,10 @@ void MainWindow::createStatusBar()
statusBar()->addPermanentWidget(m_levelRadioButton[i]); statusBar()->addPermanentWidget(m_levelRadioButton[i]);
} }
m_levelRadioButton[0]->toggle(); m_levelRadioButton[0]->toggle();
connect(m_levelRadioButton[0], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(0); m_textWidget->update(); }); connect(m_levelRadioButton[0], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(0); m_textWidget->update(); m_paletteDockWidget->setLevel3p5Accepted(false); });
connect(m_levelRadioButton[1], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(1); m_textWidget->update(); }); connect(m_levelRadioButton[1], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(1); m_textWidget->update(); m_paletteDockWidget->setLevel3p5Accepted(false);});
connect(m_levelRadioButton[2], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(2); m_textWidget->update(); }); connect(m_levelRadioButton[2], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(2); m_textWidget->update(); m_paletteDockWidget->setLevel3p5Accepted(false);});
connect(m_levelRadioButton[3], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(3); m_textWidget->update(); }); connect(m_levelRadioButton[3], &QAbstractButton::clicked, [=]() { m_textWidget->pageDecode()->setLevel(3); m_textWidget->update(); m_paletteDockWidget->setLevel3p5Accepted(true);});
statusBar()->showMessage(tr("Ready")); statusBar()->showMessage(tr("Ready"));
} }
@@ -952,6 +952,8 @@ void MainWindow::loadFile(const QString &fileName)
levelSeen = m_textWidget->document()->levelRequired(); levelSeen = m_textWidget->document()->levelRequired();
m_levelRadioButton[levelSeen]->toggle(); m_levelRadioButton[levelSeen]->toggle();
m_textWidget->pageDecode()->setLevel(levelSeen); m_textWidget->pageDecode()->setLevel(levelSeen);
if (levelSeen == 3)
m_paletteDockWidget->setLevel3p5Accepted(true);
updatePageWidgets(); updatePageWidgets();
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();

View File

@@ -39,6 +39,9 @@ PaletteDockWidget::PaletteDockWidget(TeletextWidget *parent): QDockWidget(parent
m_parentMainWidget = parent; m_parentMainWidget = parent;
m_level3p5Accepted = false; // true when Level 3.5 radio button is checked in main window
m_level3p5Seen = false; // true when CLUT 0 or 1 is redefined
this->setObjectName("PaletteDockWidget"); this->setObjectName("PaletteDockWidget");
this->setWindowTitle("Palette"); this->setWindowTitle("Palette");
for (int c=0; c<=7; c++) for (int c=0; c<=7; c++)
@@ -57,6 +60,9 @@ PaletteDockWidget::PaletteDockWidget(TeletextWidget *parent): QDockWidget(parent
connect(m_colourButton[i], &QAbstractButton::clicked, [=]() { selectColour(i); }); connect(m_colourButton[i], &QAbstractButton::clicked, [=]() { selectColour(i); });
} }
} }
updateLevel3p5Cursor();
m_showHexValuesCheckBox = new QCheckBox(tr("Show colour hex values")); m_showHexValuesCheckBox = new QCheckBox(tr("Show colour hex values"));
paletteGridLayout->addWidget(m_showHexValuesCheckBox, 5, 1, 1, 8); paletteGridLayout->addWidget(m_showHexValuesCheckBox, 5, 1, 1, 8);
connect(m_showHexValuesCheckBox, &QCheckBox::stateChanged, this, &PaletteDockWidget::updateAllColourButtons); connect(m_showHexValuesCheckBox, &QCheckBox::stateChanged, this, &PaletteDockWidget::updateAllColourButtons);
@@ -70,26 +76,34 @@ void PaletteDockWidget::updateColourButton(int colourIndex)
{ {
if (colourIndex == 8) if (colourIndex == 8)
return; return;
QString colourString = QString("%1").arg(m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex), 3, 16, QChar('0')); QString colourString = QString("%1").arg(m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex), 3, 16, QChar('0'));
if (m_showHexValuesCheckBox->isChecked()) if (m_showHexValuesCheckBox->isChecked())
m_colourButton[colourIndex]->setText(colourString); m_colourButton[colourIndex]->setText(colourString);
else else
m_colourButton[colourIndex]->setText(nullptr); m_colourButton[colourIndex]->setText(nullptr);
// Set text itself to black or white so it can be seen over background colour - http://alienryderflex.com/hsp.html
int r = m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex) >> 8; int r = m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex) >> 8;
int g = (m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex) >> 4) & 0xf; int g = (m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex) >> 4) & 0xf;
int b = m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex) & 0xf; int b = m_parentMainWidget->document()->currentSubPage()->CLUT(colourIndex) & 0xf;
// Set text itself to black or white so it can be seen over background colour - http://alienryderflex.com/hsp.html
char blackOrWhite = (sqrt(r*r*0.299 + g*g*0.587 + b*b*0.114) > 7.647) ? '0' : 'f'; char blackOrWhite = (sqrt(r*r*0.299 + g*g*0.587 + b*b*0.114) > 7.647) ? '0' : 'f';
QString qss = QString("background-color: #%1; color: #%2%2%2; border: none").arg(colourString).arg(blackOrWhite); QString qss = QString("background-color: #%1; color: #%2%2%2; border: none").arg(colourString).arg(blackOrWhite);
m_colourButton[colourIndex]->setStyleSheet(qss); m_colourButton[colourIndex]->setStyleSheet(qss);
if (m_parentMainWidget->document()->currentSubPage()->isPaletteDefault(colourIndex)) if (m_parentMainWidget->document()->currentSubPage()->isPaletteDefault(colourIndex)) {
// Default colour was set, disable Reset button if all colours in row are default as well // Default colour was set, disable Reset button if all colours in a CLUT row are default as well
m_resetButton[colourIndex>>3]->setEnabled(!m_parentMainWidget->document()->currentSubPage()->isPaletteDefault(colourIndex & 0x18, colourIndex | 0x07)); m_resetButton[colourIndex >> 3]->setEnabled(!m_parentMainWidget->document()->currentSubPage()->isPaletteDefault(colourIndex & 0x18, colourIndex | 0x07));
else // Check if CLUTs 0 and 1 are all default if necessary
m_resetButton[colourIndex>>3]->setEnabled(true); if (colourIndex < 16 && m_parentMainWidget->document()->currentSubPage()->isPaletteDefault(0, 15))
setLevel3p5Seen(false);
} else {
// Custom colour was set, enable Reset button for that CLUT
m_resetButton[colourIndex >> 3]->setEnabled(true);
if (colourIndex < 16)
setLevel3p5Seen(true);
}
} }
void PaletteDockWidget::updateAllColourButtons() void PaletteDockWidget::updateAllColourButtons()
@@ -98,6 +112,41 @@ void PaletteDockWidget::updateAllColourButtons()
updateColourButton(i); updateColourButton(i);
} }
void PaletteDockWidget::setLevel3p5Accepted(bool b)
{
if (b == m_level3p5Accepted)
return;
m_level3p5Accepted = b;
updateLevel3p5Cursor();
}
void PaletteDockWidget::setLevel3p5Seen(bool b)
{
if (b == m_level3p5Seen)
return;
m_level3p5Seen = b;
updateLevel3p5Cursor();
}
void PaletteDockWidget::updateLevel3p5Cursor()
{
const Qt::CursorShape cursor = (m_level3p5Accepted || m_level3p5Seen) ? Qt::ArrowCursor : Qt::ForbiddenCursor;
if (m_colourButton[0]->cursor() == cursor)
return;
for (int i=0; i<16; i++) {
if (i == 8)
continue;
m_colourButton[i]->setCursor(cursor);
}
}
void PaletteDockWidget::showEvent(QShowEvent *event) void PaletteDockWidget::showEvent(QShowEvent *event)
{ {
Q_UNUSED(event); Q_UNUSED(event);
@@ -106,6 +155,9 @@ void PaletteDockWidget::showEvent(QShowEvent *event)
void PaletteDockWidget::selectColour(int colourIndex) void PaletteDockWidget::selectColour(int colourIndex)
{ {
if (colourIndex < 16 && !m_level3p5Accepted && !m_level3p5Seen)
return;
const QColor newColour = QColorDialog::getColor(m_parentMainWidget->document()->currentSubPage()->CLUTtoQColor(colourIndex), this, "Select Colour"); const QColor newColour = QColorDialog::getColor(m_parentMainWidget->document()->currentSubPage()->CLUTtoQColor(colourIndex), this, "Select Colour");
if (newColour.isValid()) if (newColour.isValid())

View File

@@ -36,18 +36,22 @@ public:
public slots: public slots:
void updateColourButton(int); void updateColourButton(int);
void setLevel3p5Accepted(bool);
protected: protected:
void showEvent(QShowEvent *); void showEvent(QShowEvent *);
private slots: private slots:
void selectColour(int); void selectColour(int);
void setLevel3p5Seen(bool);
void updateLevel3p5Cursor();
private: private:
void resetCLUT(int); void resetCLUT(int);
QPushButton *m_colourButton[32], *m_resetButton[4]; QPushButton *m_colourButton[32], *m_resetButton[4];
QCheckBox *m_showHexValuesCheckBox; QCheckBox *m_showHexValuesCheckBox;
bool m_level3p5Accepted, m_level3p5Seen;
TeletextWidget *m_parentMainWidget; TeletextWidget *m_parentMainWidget;
}; };

View File

@@ -55,9 +55,11 @@ TeletextPageRender::TeletextPageRender()
m_showControlCodes = false; m_showControlCodes = false;
m_flashBuffersHz = 0; m_flashBuffersHz = 0;
for (int r=0; r<25; r++) for (int r=0; r<25; r++) {
m_flashingRow[r] = 0;
for (int c=0; c<40; c++) for (int c=0; c<40; c++)
m_controlCodeCache[r][c] = 0x7f; m_controlCodeCache[r][c] = 0x7f;
}
} }
TeletextPageRender::~TeletextPageRender() TeletextPageRender::~TeletextPageRender()
@@ -71,6 +73,39 @@ void TeletextPageRender::setDecoder(TeletextPageDecode *decoder)
m_decoder = 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) inline void TeletextPageRender::drawFromFontBitmap(QPainter &pixmapPainter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment)
{ {
switch (characterFragment) { switch (characterFragment) {
@@ -150,6 +185,8 @@ inline void TeletextPageRender::drawCharacter(QPainter &pixmapPainter, int r, in
pixmapPainter.fillRect(c*12, r*10, 12, 10, pixmapPainter.background().color()); pixmapPainter.fillRect(c*12, r*10, 12, 10, pixmapPainter.background().color());
else if (characterCode == 0x7f && characterSet == 24) else if (characterCode == 0x7f && characterSet == 24)
pixmapPainter.fillRect(c*12, r*10, 12, 10, pixmapPainter.pen().color()); 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 else
drawFromFontBitmap(pixmapPainter, r, c, characterCode, characterSet, characterFragment); drawFromFontBitmap(pixmapPainter, r, c, characterCode, characterSet, characterFragment);
@@ -180,226 +217,225 @@ inline void TeletextPageRender::drawCharacter(QPainter &pixmapPainter, int r, in
pixmapPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); pixmapPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
} }
void TeletextPageRender::renderPage(bool force) inline void TeletextPageRender::drawBoldOrItalicCharacter(QPainter &pixmapPainter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment)
{ {
QPainter pixmapPainter[6]; QBitmap bitmap = QBitmap(12, 10);
int previousFlashBuffersHz = m_flashBuffersHz; QPainter bitmapPainter;
// If force-rendering (such as showing a new or different subpage) then // TODO italic glyph-making is VERY slow!
// we don't render flashing cells initially as it greatly speeds things up if (m_decoder->cellItalic(r, c)) {
// when rendering on top of an already flashing page. bitmap.clear();
// At the end of this method, if flashing cells are present then this method
// recursively calls itself with force=false to render the flashing cells. bitmapPainter.begin(&bitmap);
if (force) { bitmapPainter.setBackgroundMode(Qt::OpaqueMode);
m_flashBuffersHz = 0; bitmapPainter.drawPixmap(1, 0, *m_fontBitmap.rawBitmap(), (characterCode-32)*12, characterSet*10, 11, 3);
previousFlashBuffersHz = 0; bitmapPainter.drawPixmap(0, 3, *m_fontBitmap.rawBitmap(), (characterCode-32)*12, characterSet*10+3, 12, 3);
emit flashChanged(0); 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);
pixmapPainter[0].begin(m_pagePixmap[0]);
pixmapPainter[0].setBackgroundMode(Qt::OpaqueMode);
if (m_flashBuffersHz != 0) {
pixmapPainter[3].begin(m_pagePixmap[3]);
pixmapPainter[3].setBackgroundMode(Qt::OpaqueMode);
if (m_flashBuffersHz == 2) {
pixmapPainter[1].begin(m_pagePixmap[1]);
pixmapPainter[1].setBackgroundMode(Qt::OpaqueMode);
pixmapPainter[2].begin(m_pagePixmap[2]);
pixmapPainter[2].setBackgroundMode(Qt::OpaqueMode);
pixmapPainter[4].begin(m_pagePixmap[4]);
pixmapPainter[4].setBackgroundMode(Qt::OpaqueMode);
pixmapPainter[5].begin(m_pagePixmap[5]);
pixmapPainter[5].setBackgroundMode(Qt::OpaqueMode);
}
}
for (int r=0; r<25; r++)
for (int c=0; c<72; c++) {
bool controlCodeChanged = false;
// Ensure that shown control codes are refreshed
if (m_showControlCodes && c < 40 && (m_controlCodeCache[r][c] != 0x7f || m_decoder->teletextPage()->character(r, c) < 0x20)) {
controlCodeChanged = m_controlCodeCache[r][c] != m_decoder->teletextPage()->character(r, c);
if (controlCodeChanged) {
if (m_decoder->teletextPage()->character(r, c) < 0x20)
m_controlCodeCache[r][c] = m_decoder->teletextPage()->character(r, c);
else
m_controlCodeCache[r][c] = 0x7f;
}
}
if (m_decoder->refresh(r, c) || force || controlCodeChanged) {
unsigned char characterCode;
int characterSet, characterDiacritical;
if (!m_reveal && m_decoder->cellConceal(r, c)) {
characterCode = 0x20;
characterSet = 0;
characterDiacritical = 0;
} else {
characterCode = m_decoder->cellCharacterCode(r, c);
characterSet = m_decoder->cellCharacterSet(r, c);
characterDiacritical = m_decoder->cellCharacterDiacritical(r, c);
}
// QSet::insert won't insert a duplicate value already in the set
// QSet::remove doesn't mind if we try to remove a value that's not there
if (m_flashBuffersHz == 0 && m_decoder->cellFlashMode(r, c) != 0) {
if (m_decoder->cellFlashRatePhase(r, c) == 0)
m_flash1HzCells.insert(qMakePair(r, c));
else
m_flash2HzCells.insert(qMakePair(r, c));
if (!force)
updateFlashBuffers();
} else if (m_decoder->cellFlashMode(r, c) == 0) {
m_flash1HzCells.remove(qMakePair(r, c));
m_flash2HzCells.remove(qMakePair(r, c));
if (!force)
updateFlashBuffers();
} else if (m_decoder->cellFlashRatePhase(r, c) == 0) {
m_flash1HzCells.insert(qMakePair(r, c));
m_flash2HzCells.remove(qMakePair(r, c));
if (!force)
updateFlashBuffers();
} else {
m_flash1HzCells.remove(qMakePair(r, c));
m_flash2HzCells.insert(qMakePair(r, c));
if (!force)
updateFlashBuffers();
}
// If flash rate has gone up, prepare painters for the other buffers
if (m_flashBuffersHz > previousFlashBuffersHz) {
if (previousFlashBuffersHz == 0) {
pixmapPainter[3].begin(m_pagePixmap[3]);
pixmapPainter[3].setBackgroundMode(Qt::OpaqueMode);
}
if (m_flashBuffersHz == 2) {
pixmapPainter[1].begin(m_pagePixmap[1]);
pixmapPainter[1].setBackgroundMode(Qt::OpaqueMode);
pixmapPainter[2].begin(m_pagePixmap[2]);
pixmapPainter[2].setBackgroundMode(Qt::OpaqueMode);
pixmapPainter[4].begin(m_pagePixmap[4]);
pixmapPainter[4].setBackgroundMode(Qt::OpaqueMode);
pixmapPainter[5].begin(m_pagePixmap[5]);
pixmapPainter[5].setBackgroundMode(Qt::OpaqueMode);
}
previousFlashBuffersHz = m_flashBuffersHz;
}
// If flash rate has gone down, end the painters so we don't crash
// if the pixmaps get copied due to the flash rate going up again
if (m_flashBuffersHz < previousFlashBuffersHz) {
if (previousFlashBuffersHz == 2) {
pixmapPainter[1].end();
pixmapPainter[2].end();
pixmapPainter[4].end();
pixmapPainter[5].end();
}
if (m_flashBuffersHz == 0)
pixmapPainter[3].end();
previousFlashBuffersHz = m_flashBuffersHz;
}
if (m_flashBuffersHz == 0) {
pixmapPainter[0].setPen(m_decoder->cellForegroundQColor(r, c));
if (!m_mix || m_decoder->cellBoxed(r, c))
pixmapPainter[0].setBackground(m_decoder->cellBackgroundQColor(r, c));
else
pixmapPainter[0].setBackground(Qt::transparent);
drawCharacter(pixmapPainter[0], r, c, characterCode, characterSet, characterDiacritical, m_decoder->cellCharacterFragment(r, c));
} else {
for (int i=0; i<6; i++) {
if (m_flashBuffersHz == 1 && (i == 1 || i == 2 || i == 4 || i == 5))
continue;
bool phaseOn;
if (m_decoder->cellFlashRatePhase(r, c) == 0)
phaseOn = (i < 3) ^ (m_decoder->cellFlashMode(r, c) == 2);
else
phaseOn = ((i == m_decoder->cellFlash2HzPhaseNumber(r, c)-1) || (i == m_decoder->cellFlash2HzPhaseNumber(r, c)+2)) ^ (m_decoder->cellFlashMode(r, c) == 2);
if (!m_mix || m_decoder->cellBoxed(r, c))
pixmapPainter[i].setBackground(m_decoder->cellBackgroundQColor(r, c));
else
pixmapPainter[i].setBackground(Qt::transparent);
if (m_decoder->cellFlashMode(r, c) == 3 && !phaseOn)
pixmapPainter[i].setPen(m_decoder->cellFlashForegroundQColor(r, c));
else
pixmapPainter[i].setPen(m_decoder->cellForegroundQColor(r, c));
if ((m_decoder->cellFlashMode(r, c) == 1 || m_decoder->cellFlashMode(r, c) == 2) && !phaseOn)
// Character 0x00 draws space without underline
drawCharacter(pixmapPainter[i], r, c, 0x00, 0, 0, m_decoder->cellCharacterFragment(r, c));
else
drawCharacter(pixmapPainter[i], r, c, characterCode, characterSet, characterDiacritical, m_decoder->cellCharacterFragment(r, c));
}
}
if (m_showControlCodes && c < 40 && m_decoder->teletextPage()->character(r, c) < 0x20) {
pixmapPainter[0].setBackground(QColor(0, 0, 0, 128));
pixmapPainter[0].setPen(QColor(255, 255, 255, 224));
pixmapPainter[0].drawPixmap(c*12, r*10, *m_fontBitmap.rawBitmap(), (m_decoder->teletextPage()->character(r, c)+32)*12, 250, 12, 10);
if (m_flashBuffersHz == 1) {
pixmapPainter[3].setBackground(QColor(0, 0, 0, 128));
pixmapPainter[3].setPen(QColor(255, 255, 255, 224));
pixmapPainter[3].drawPixmap(c*12, r*10, *m_fontBitmap.rawBitmap(), (m_decoder->teletextPage()->character(r, c)+32)*12, 250, 12, 10);
} else if (m_flashBuffersHz == 2)
for (int i=1; i<6; i++) {
pixmapPainter[i].setBackground(QColor(0, 0, 0, 128));
pixmapPainter[i].setPen(QColor(255, 255, 255, 224));
pixmapPainter[i].drawPixmap(c*12, r*10, *m_fontBitmap.rawBitmap(), (m_decoder->teletextPage()->character(r, c)+32)*12, 250, 12, 10);
}
}
if (force && m_decoder->cellFlashMode(r, c) != 0)
m_decoder->setRefresh(r, c, true);
else
m_decoder->setRefresh(r, c, false);
}
}
pixmapPainter[0].end();
if (m_flashBuffersHz != 0) {
pixmapPainter[3].end();
if (m_flashBuffersHz == 2) {
pixmapPainter[1].end();
pixmapPainter[2].end();
pixmapPainter[4].end();
pixmapPainter[5].end();
}
}
if (force && (!m_flash1HzCells.isEmpty() || !m_flash2HzCells.isEmpty()))
renderPage();
} }
void TeletextPageRender::updateFlashBuffers() void TeletextPageRender::renderPage(bool force)
{ {
int highestFlashHz; for (int r=0; r<25; r++)
renderRow(r, 0, force);
}
if (!m_flash2HzCells.isEmpty()) void TeletextPageRender::renderRow(int r, int ph, bool force)
highestFlashHz = 2; {
else QPainter pixmapPainter;
highestFlashHz = !m_flash1HzCells.isEmpty(); int flashingRow = 0;
bool rowRefreshed = false;
if (highestFlashHz == m_flashBuffersHz) pixmapPainter.begin(m_pagePixmap[ph]);
return; pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
if (highestFlashHz > m_flashBuffersHz) { for (int c=0; c<72; c++) {
if (m_flashBuffersHz == 0) bool controlCodeChanged = false;
*m_pagePixmap[3] = m_pagePixmap[0]->copy();
if (highestFlashHz == 2) { // Ensure that shown control codes are refreshed
*m_pagePixmap[1] = m_pagePixmap[0]->copy(); if (ph == 0 && m_showControlCodes && c < 40 && (m_controlCodeCache[r][c] != 0x7f || m_decoder->teletextPage()->character(r, c) < 0x20)) {
*m_pagePixmap[2] = m_pagePixmap[0]->copy(); controlCodeChanged = m_controlCodeCache[r][c] != m_decoder->teletextPage()->character(r, c);
*m_pagePixmap[4] = m_pagePixmap[3]->copy(); if (controlCodeChanged) {
*m_pagePixmap[5] = m_pagePixmap[3]->copy(); if (m_decoder->teletextPage()->character(r, c) < 0x20)
m_controlCodeCache[r][c] = m_decoder->teletextPage()->character(r, c);
else
m_controlCodeCache[r][c] = 0x7f;
}
}
if (ph == 0) {
if (m_decoder->cellFlashMode(r, c) != 0)
flashingRow = (m_decoder->cellFlashRatePhase(r, c) == 0) ? 1 : 2;
} else
force = m_decoder->cellFlashMode(r, c) != 0;
// If drawing into a flash pixmap buffer, "force" is set on a flashing cell only
// and since the refresh and controlCodeChanged variables will be false at this point
// only flashing cells will be drawn
if (m_decoder->refresh(r, c) || force || controlCodeChanged) {
unsigned char characterCode;
int characterSet, characterDiacritical;
rowRefreshed = true;
if (!m_reveal && m_decoder->cellConceal(r, c)) {
characterCode = 0x20;
characterSet = 0;
characterDiacritical = 0;
} else {
characterCode = m_decoder->cellCharacterCode(r, c);
characterSet = m_decoder->cellCharacterSet(r, c);
characterDiacritical = m_decoder->cellCharacterDiacritical(r, c);
}
if (m_decoder->cellFlashMode(r, c) == 0)
pixmapPainter.setPen(m_decoder->cellForegroundQColor(r, c));
else {
// Flashing cell, decide if phase in this cycle is on or off
bool phaseOn;
if (m_decoder->cellFlashRatePhase(r, c) == 0)
phaseOn = (ph < 3) ^ (m_decoder->cellFlashMode(r, c) == 2);
else
phaseOn = ((ph == m_decoder->cellFlash2HzPhaseNumber(r, c)-1) || (ph == m_decoder->cellFlash2HzPhaseNumber(r, c)+2)) ^ (m_decoder->cellFlashMode(r, c) == 2);
// If flashing to adjacent CLUT select the appropriate foreground colour
if (m_decoder->cellFlashMode(r, c) == 3 && !phaseOn)
pixmapPainter.setPen(m_decoder->cellFlashForegroundQColor(r, c));
else
pixmapPainter.setPen(m_decoder->cellForegroundQColor(r, c));
// If flashing mode is Normal or Invert, draw a space instead of a character on phase
if ((m_decoder->cellFlashMode(r, c) == 1 || m_decoder->cellFlashMode(r, c) == 2) && !phaseOn) {
// Character 0x00 draws space without underline
characterCode = 0x00;
characterSet = 0;
characterDiacritical = 0;
}
}
if (!m_mix || m_decoder->cellBoxed(r, c))
pixmapPainter.setBackground(m_decoder->cellBackgroundQColor(r, c));
else
pixmapPainter.setBackground(Qt::transparent);
drawCharacter(pixmapPainter, r, c, characterCode, characterSet, characterDiacritical, m_decoder->cellCharacterFragment(r, c));
if (m_showControlCodes && c < 40 && m_decoder->teletextPage()->character(r, c) < 0x20) {
pixmapPainter.setBackground(QColor(0, 0, 0, 128));
pixmapPainter.setPen(QColor(255, 255, 255, 224));
pixmapPainter.drawPixmap(c*12, r*10, *m_fontBitmap.rawBitmap(), (m_decoder->teletextPage()->character(r, c)+32)*12, 250, 12, 10);
}
} }
} }
m_flashBuffersHz = highestFlashHz; pixmapPainter.end();
if (ph != 0)
return;
if (flashingRow == 3)
flashingRow = 2;
if (flashingRow != m_flashingRow[r])
setRowFlashStatus(r, flashingRow);
for (int c=0; c<72; c++)
m_decoder->setRefresh(r, c, false);
// If row had changes rendered and flashing is present anywhere on the screen,
// copy this rendered line into the other flash pixmap buffers and then re-render
// the flashing cells in those buffers
if (rowRefreshed && m_flashBuffersHz > 0) {
pixmapPainter.begin(m_pagePixmap[3]);
pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
pixmapPainter.drawPixmap(0, r*10, *m_pagePixmap[0], 0, r*10, 864, 10);
pixmapPainter.end();
renderRow(r, 3);
if (m_flashBuffersHz == 2) {
pixmapPainter.begin(m_pagePixmap[1]);
pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
pixmapPainter.drawPixmap(0, r*10, *m_pagePixmap[0], 0, r*10, 864, 10);
pixmapPainter.end();
pixmapPainter.begin(m_pagePixmap[2]);
pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
pixmapPainter.drawPixmap(0, r*10, *m_pagePixmap[0], 0, r*10, 864, 10);
pixmapPainter.end();
pixmapPainter.begin(m_pagePixmap[4]);
pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
pixmapPainter.drawPixmap(0, r*10, *m_pagePixmap[3], 0, r*10, 864, 10);
pixmapPainter.end();
pixmapPainter.begin(m_pagePixmap[5]);
pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
pixmapPainter.drawPixmap(0, r*10, *m_pagePixmap[3], 0, r*10, 864, 10);
pixmapPainter.end();
renderRow(r, 1);
renderRow(r, 2);
renderRow(r, 4);
renderRow(r, 5);
}
}
}
void TeletextPageRender::setRowFlashStatus(int r, int rowFlashHz)
{
m_flashingRow[r] = rowFlashHz;
if (rowFlashHz == m_flashBuffersHz)
return;
if (rowFlashHz < m_flashBuffersHz) {
// New flash Hz for this row is lower than the entire screen flash Hz
// Check the other rows if they still need flashing at the current flash Hz
// If not, reduce the screen flash Hz
int highestRowFlashHz = rowFlashHz;
for (int ri=0; ri<25; ri++)
if (m_flashingRow[ri] > highestRowFlashHz) {
highestRowFlashHz = m_flashingRow[ri];
if (highestRowFlashHz == 2)
break;
}
if (highestRowFlashHz > rowFlashHz)
rowFlashHz = highestRowFlashHz;
if (rowFlashHz == m_flashBuffersHz)
return;
m_flashBuffersHz = rowFlashHz;
emit flashChanged(m_flashBuffersHz);
return;
}
// If we get here, new flash Hz for this row is higher than the entire flash Hz
// so prepare the pixmap flash buffers
if (m_flashBuffersHz == 0)
*m_pagePixmap[3] = m_pagePixmap[0]->copy();
if (rowFlashHz == 2) {
*m_pagePixmap[1] = m_pagePixmap[0]->copy();
*m_pagePixmap[2] = m_pagePixmap[0]->copy();
*m_pagePixmap[4] = m_pagePixmap[3]->copy();
*m_pagePixmap[5] = m_pagePixmap[3]->copy();
}
m_flashBuffersHz = rowFlashHz;
emit flashChanged(m_flashBuffersHz); emit flashChanged(m_flashBuffersHz);
} }

View File

@@ -21,7 +21,6 @@
#define RENDER_H #define RENDER_H
#include <QBitmap> #include <QBitmap>
#include <QSet>
#include <QPixmap> #include <QPixmap>
#include "decode.h" #include "decode.h"
@@ -67,14 +66,16 @@ protected:
QPixmap* m_pagePixmap[6]; QPixmap* m_pagePixmap[6];
unsigned char m_controlCodeCache[25][40]; unsigned char m_controlCodeCache[25][40];
bool m_reveal, m_mix, m_showControlCodes; bool m_reveal, m_mix, m_showControlCodes;
QSet<QPair<int, int>> m_flash1HzCells;
QSet<QPair<int, int>> m_flash2HzCells;
int m_flashBuffersHz; int m_flashBuffersHz;
int m_flashingRow[25];
private: private:
inline void drawFromBitmap(QPainter &, int, int, const QBitmap, TeletextPageDecode::CharacterFragment);
inline void drawFromFontBitmap(QPainter &, int, int, unsigned char, int, 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 drawCharacter(QPainter &, int, int, unsigned char, int, int, TeletextPageDecode::CharacterFragment);
void updateFlashBuffers(); inline void drawBoldOrItalicCharacter(QPainter &, int, int, unsigned char, int, TeletextPageDecode::CharacterFragment);
void renderRow(int, int, bool force=false);
void setRowFlashStatus(int, int);
TeletextPageDecode *m_decoder; TeletextPageDecode *m_decoder;
}; };

View File

@@ -218,7 +218,7 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
QMenu *reservedColumnSubMenu = pdcSubMenu->addMenu(tr("Reserved column")); QMenu *reservedColumnSubMenu = pdcSubMenu->addMenu(tr("Reserved column"));
newModeMenuAction(reservedColumnSubMenu, 0x24); newModeMenuAction(reservedColumnSubMenu, 0x24);
newModeMenuAction(reservedColumnSubMenu, 0x25); newModeMenuAction(reservedColumnSubMenu, 0x25);
newModeMenuAction(reservedColumnSubMenu, 0x26); newModeMenuAction(reservedColumnSubMenu, 0x2a);
} }
m_cookedModePushButton->setMenu(m_cookedModeMenu); m_cookedModePushButton->setMenu(m_cookedModeMenu);
@@ -749,16 +749,13 @@ void X26DockWidget::updateAllCookedTripletWidgets(const QModelIndex &index)
case 0x29: // G0 character case 0x29: // G0 character
case 0x2b: // G3 character at Level 2.5 case 0x2b: // G3 character at Level 2.5
case 0x2f ... 0x3f: // G2 character, G0 character with diacritical case 0x2f ... 0x3f: // G2 character, G0 character with diacritical
// TODO non-Latin G0 and G2 character sets
m_characterCodeComboBox->blockSignals(true); m_characterCodeComboBox->blockSignals(true);
if (modeExt == 0x22 || modeExt == 0x2b) if (modeExt == 0x21)
m_characterListModel.setCharacterSet(26);
else if (modeExt == 0x2f)
m_characterListModel.setCharacterSet(7);
else if (modeExt == 0x21)
m_characterListModel.setCharacterSet(24); m_characterListModel.setCharacterSet(24);
else if (modeExt == 0x22 || modeExt == 0x2b)
m_characterListModel.setCharacterSet(26);
else else
m_characterListModel.setCharacterSet(0); m_characterListModel.setCharacterSet(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
m_characterCodeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt()-32); m_characterCodeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt()-32);
m_characterCodeComboBox->blockSignals(false); m_characterCodeComboBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(2); m_tripletParameterStackedLayout->setCurrentIndex(2);
@@ -1074,6 +1071,9 @@ void X26DockWidget::insertTriplet(int modeExt, bool after)
// For character triplets, ensure Data is not reserved // For character triplets, ensure Data is not reserved
if (modeExt == 0x21 || modeExt == 0x22 || modeExt == 0x29 || modeExt == 0x2b || modeExt >= 0x2f) if (modeExt == 0x21 || modeExt == 0x22 || modeExt == 0x29 || modeExt == 0x2b || modeExt >= 0x2f)
newTriplet.setData(0x20); newTriplet.setData(0x20);
// For Address Row 0, set Address
if (modeExt == 0x07)
newTriplet.setAddress(63);
// For Termination Marker, set Address and Mode // For Termination Marker, set Address and Mode
if (modeExt == 0x1f) { if (modeExt == 0x1f) {
newTriplet.setAddress(63); newTriplet.setAddress(63);

View File

@@ -266,7 +266,30 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
return result; return result;
} }
break; break;
//TODO case 0x28: // G0 and G2 designation case 0x28: // Modified G0 and G2 character set
switch (triplet.data()) {
case 0x20:
return QString("0x20 Cyrillic 1 Serbian/Croatian");
case 0x24:
return QString("0x24 Cyrillic 2 Russian/Bulgarian");
case 0x25:
return QString("0x25 Cyrillic 3 Ukranian");
case 0x36:
return QString("0x36 Latin");
case 0x37:
return QString("0x37 Greek");
case 0x40:
case 0x44:
return QString("0x%1 G0 Latin, G2 Arabic").arg(triplet.data(), 2, 16);
case 0x47:
case 0x57:
return QString("0x%1 Arabic").arg(triplet.data(), 2, 16);
case 0x55:
return QString("0x55 G0 Hebrew, G2 Arabic");
}
if (triplet.data() < 0x27)
return QString("0x%1 Latin").arg(triplet.data(), 2, 16, QChar('0'));
break;
case 0x2c: // Display attributes case 0x2c: // Display attributes
if (triplet.data() & 0x02) if (triplet.data() & 0x02)
result.append("Boxing "); result.append("Boxing ");
@@ -314,7 +337,6 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
result.chop(1); result.chop(1);
result.append(QString(", %1 row(s)").arg(triplet.data() >> 4)); result.append(QString(", %1 row(s)").arg(triplet.data() >> 4));
return result; return result;
case 0x28: // Modified G0 and G2 character set
case 0x26: // PDC case 0x26: // PDC
return QString("0x%1").arg(triplet.data(), 2, 16, QChar('0')); return QString("0x%1").arg(triplet.data(), 2, 16, QChar('0'));
default: // Reserved default: // Reserved
@@ -346,16 +368,11 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
if (triplet.data() >= 0x20) if (triplet.data() >= 0x20)
return m_fontBitmap.rawBitmap()->copy((triplet.data()-32)*12, 26*10, 12, 10); return m_fontBitmap.rawBitmap()->copy((triplet.data()-32)*12, 26*10, 12, 10);
break; break;
case 0x2f: // G2 character
// TODO non-Latin G2 character sets
if (triplet.data() >= 0x20)
return m_fontBitmap.rawBitmap()->copy((triplet.data()-32)*12, 7*10, 12, 10);
break;
case 0x29: // G0 character case 0x29: // G0 character
case 0x2f: // G2 character
case 0x30 ... 0x3f: // G0 diacritical mark case 0x30 ... 0x3f: // G0 diacritical mark
// TODO non-Latin G0 character sets
if (triplet.data() >= 0x20) if (triplet.data() >= 0x20)
return m_fontBitmap.rawBitmap()->copy((triplet.data()-32)*12, 0, 12, 10); return m_fontBitmap.rawBitmap()->copy((triplet.data()-32)*12, m_parentMainWidget->pageDecode()->cellCharacterSet(triplet.activePositionRow(), triplet.activePositionColumn())*10, 12, 10);
break; break;
}; };
@@ -471,9 +488,16 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
return triplet.data() >> 4; return triplet.data() >> 4;
} }
break; break;
case 0x29: // G0 character
case 0x2f: // G2 character
case 0x30 ... 0x3f: // G0 diacritical mark
// Qt::UserRole+1 is character number, returned by default below
if (role == Qt::UserRole+2) // Character set
return m_parentMainWidget->pageDecode()->cellCharacterSet(triplet.activePositionRow(), triplet.activePositionColumn());
break;
}; };
// Fpr other triplet modes, return the complete data value // For characters and other triplet modes, return the complete data value
if (role == Qt::UserRole+1) if (role == Qt::UserRole+1)
return triplet.data(); return triplet.data();
@@ -618,7 +642,7 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role
if (triplet.data() >= 0x18) if (triplet.data() >= 0x18)
m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 0, role)); m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 0, role));
break; break;
case 0x28: // Display attributes case 0x2c: // Display attributes
case 0x2e: // Font style case 0x2e: // Font style
// Clear reserved bit D3 // Clear reserved bit D3
if (triplet.data() & 0x08) if (triplet.data() & 0x08)

View File

@@ -74,6 +74,7 @@ void X26TripletList::updateInternalData()
ActivePosition activePosition; ActivePosition activePosition;
X26Triplet *triplet; X26Triplet *triplet;
// Check for errors, and fill in where the Active Position goes for Level 2.5 and above
for (int i=0; i < m_list.size(); i++) { for (int i=0; i < m_list.size(); i++) {
triplet = &m_list[i]; triplet = &m_list[i];
triplet->m_error = X26Triplet::NoError; triplet->m_error = X26Triplet::NoError;
@@ -173,6 +174,21 @@ void X26TripletList::updateInternalData()
if (triplet->m_data >= 0x18) if (triplet->m_data >= 0x18)
triplet->m_reservedData = true; triplet->m_reservedData = true;
break; break;
case 0x28: // Modified G0 and G2 character set
if (triplet->m_data > 0x26)
switch (triplet->m_data) {
case 0x36:
case 0x37:
case 0x40:
case 0x44:
case 0x47:
case 0x55:
case 0x57:
break;
default:
triplet->m_reservedData = true;
}
break;
case 0x2d: // DRCS character case 0x2d: // DRCS character
if ((triplet->m_data & 0x3f) >= 48) if ((triplet->m_data & 0x3f) >= 48)
// Should really be an error? // Should really be an error?
@@ -182,6 +198,34 @@ void X26TripletList::updateInternalData()
triplet->m_activePositionRow = activePosition.row(); triplet->m_activePositionRow = activePosition.row();
triplet->m_activePositionColumn = activePosition.column(); triplet->m_activePositionColumn = activePosition.column();
} }
// Now work out where the Active Position goes on a Level 1.5 decoder
activePosition.reset();
for (int i=0; i < m_list.size(); i++) {
triplet = &m_list[i];
if (triplet->modeExt() == 0x1f) // Termination marker
break;
switch (triplet->modeExt()) {
case 0x04: // Set Active Position;
activePosition.setRow(triplet->addressRow());
break;
case 0x07: // Address row 0
if (triplet->m_address == 63 && activePosition.setRow(0))
activePosition.setColumn(8);
break;
case 0x22: // G3 mosaic character at level 1.5
case 0x2f ... 0x3f: // G2 character or G0 diacritical mark
activePosition.setColumn(triplet->addressColumn());
break;
}
triplet->m_activePositionRow1p5 = activePosition.row();
triplet->m_activePositionColumn1p5 = activePosition.column();
}
} }
void X26TripletList::append(const X26Triplet &value) void X26TripletList::append(const X26Triplet &value)

View File

@@ -61,6 +61,8 @@ public:
int activePositionRow() const { return m_activePositionRow; } int activePositionRow() const { return m_activePositionRow; }
int activePositionColumn() const { return m_activePositionColumn; } int activePositionColumn() const { return m_activePositionColumn; }
int activePositionRow1p5() const { return m_activePositionRow1p5; }
int activePositionColumn1p5() const { return m_activePositionColumn1p5; }
X26TripletError error() const { return m_error; } X26TripletError error() const { return m_error; }
bool reservedMode() const { return m_reservedMode; } bool reservedMode() const { return m_reservedMode; }
bool reservedData() const { return m_reservedData; } bool reservedData() const { return m_reservedData; }
@@ -68,9 +70,13 @@ public:
friend class X26TripletList; friend class X26TripletList;
private: private:
// Only these variables are manipulated by the X26Triplet class
int m_address, m_mode, m_data; int m_address, m_mode, m_data;
// and the following are filled in by X26TripletList::updateInternalData()
int m_activePositionRow = -1; int m_activePositionRow = -1;
int m_activePositionColumn = -1; int m_activePositionColumn = -1;
int m_activePositionRow1p5 = -1;
int m_activePositionColumn1p5 = -1;
X26TripletError m_error = NoError; X26TripletError m_error = NoError;
bool m_reservedMode = false; bool m_reservedMode = false;
bool m_reservedData = false; bool m_reservedData = false;