Created HiresScreenWidget and optimized display code

This commit is contained in:
Mark Long 2016-10-21 19:14:45 -05:00
parent 5ba9a0155e
commit 581ac6fa5e
9 changed files with 773 additions and 577 deletions

View File

@ -55,7 +55,9 @@ SOURCES += \
src/ui/widgets/characterwidget.cpp \
src/ui/viewers/applesoftfiledetailviewer.cpp \
src/ui/widgets/hexconverter.cpp \
src/ui/viewers/viewerbase.cpp
src/ui/viewers/viewerbase.cpp \
src/ui/widgets/CharacterSetExplorer.cpp \
src/ui/widgets/HiresScreenWidget.cpp
HEADERS += \
@ -93,7 +95,9 @@ HEADERS += \
src/ui/widgets/hexconverter.h \
src/ui/widgets/hrcgcontrolsinfo.h \
src/ui/viewers/viewerbase.h \
src/ui/viewers/fileviewerinterface.h
src/ui/viewers/fileviewerinterface.h \
src/ui/widgets/CharacterSetExplorer.h \
src/ui/widgets/HiresScreenWidget.h
FORMS += \
src/ui/catalogwidget.ui \
@ -105,4 +109,5 @@ FORMS += \
src/ui/viewers/applesoftfiledetailviewer.ui \
src/ui/widgets/hexconverter.ui \
src/ui/widgets/hrcgcontrolsinfo.ui \
src/ui/viewers/viewerbase.ui
src/ui/viewers/viewerbase.ui \
src/ui/widgets/CharacterSetExplorer.ui

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>107</width>
<height>117</height>
<width>538</width>
<height>483</height>
</rect>
</property>
<property name="windowTitle">

View File

@ -6,6 +6,7 @@
#include <QDebug>
#include <QResizeEvent>
#include <QSettings>
#include <QGridLayout>
#include <math.h>
@ -13,285 +14,14 @@
HiresViewWidget::HiresViewWidget(QWidget *parent) :
FileViewerInterface(parent)
{
QGridLayout *gv = new QGridLayout(this);
setLayout(gv);
hrsw = new HiresScreenWidget(this);
gv->addWidget(hrsw);
resize(561,384);
QSettings settings;
m_viewMode = static_cast<ViewMode>(settings.value("HiresViewWidget.ViewMode",Color2).toInt());
m_showScanLines = settings.value("HiresViewWidget.ShowScanLines",true).toBool();
if (m_rowTable == 0) { makeOffsetTable(); }
m_pixmap = QPixmap(561,384);
QPainter painter(&m_pixmap);
painter.setBrush(Qt::black);
painter.drawRect(0,0,this->width(),this->height());
formatGroup = new QActionGroup(this);
monochromeAction = new QAction("Monochrome Display",this);
monochromeAction->setCheckable(true);
monochromeAction->setChecked(false);
formatGroup->addAction(monochromeAction);
ntscAction = new QAction("NTSC Display",this);
ntscAction->setCheckable(true);
ntscAction->setChecked(true);
formatGroup->addAction(ntscAction);
perPixelColorAction= new QAction("Per Pixel Color Display",this);
perPixelColorAction->setCheckable(true);
perPixelColorAction->setChecked(false);
formatGroup->addAction(perPixelColorAction);
showScanLinesAction = new QAction("Show Scan Lines",this);
showScanLinesAction->setCheckable(true);
showScanLinesAction->setChecked(m_showScanLines);
connect(ntscAction, SIGNAL(toggled(bool)), this, SLOT(handleNtscAction(bool)));
connect(monochromeAction, SIGNAL(toggled(bool)), this, SLOT(handleMonochromeAction(bool)));
connect(perPixelColorAction, SIGNAL(toggled(bool)), this, SLOT(handlePerPixelColorAction(bool)));
connect(showScanLinesAction, SIGNAL(toggled(bool)), this, SLOT(handleShowScanLinesAction(bool)));
}
void HiresViewWidget::handleNtscAction(bool toggled) {
if (toggled) {
m_viewMode = Color2;
update();
}
QSettings settings;
settings.setValue("HiresViewWidget.ViewMode",m_viewMode);
}
void HiresViewWidget::handleMonochromeAction(bool toggled) {
if (toggled) {
m_viewMode = Monochrome;
update();
}
QSettings settings;
settings.setValue("HiresViewWidget.ViewMode",m_viewMode);
}
void HiresViewWidget::handlePerPixelColorAction(bool toggled) {
if (toggled) {
m_viewMode = Color1;
update();
}
QSettings settings;
settings.setValue("HiresViewWidget.ViewMode",m_viewMode);
}
void HiresViewWidget::handleShowScanLinesAction(bool toggled) {
m_showScanLines = toggled;
update();
QSettings settings;
settings.setValue("HiresViewWidget.ShowScanLines",toggled);
}
void HiresViewWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
// if (m_pixmap.size() != this->size()) {
// m_pixmap = m_pixmap.scaled(this->size(),Qt::KeepAspectRatio);
// }
drawPixmap();
QPainter painter(this);
QPixmap tmppixmap = m_pixmap;
// tmppixmap = tmppixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
painter.drawPixmap(0,0,tmppixmap);
}
void HiresViewWidget::resizeEvent(QResizeEvent *)
{
}
void HiresViewWidget::drawPixmap() {
QPainter pmpainter(&m_pixmap);
pmpainter.setBrush(Qt::black);
pmpainter.setPen(Qt::black);
pmpainter.drawRect(0,0,m_pixmap.width(),m_pixmap.height());
if (m_viewMode == Monochrome)
{
pmpainter.setPen(Qt::white);
pmpainter.setBrush(Qt::white);
quint8 chunkCount = 0;
// for (int idx = 0; idx < m_data.size() ; idx+=40) {
int idx = 0;
while (idx < m_data.size()) {
int rowcol = findRowCol(idx);
// int xoff = (rowcol / 10000) * 7;
int yoff = (rowcol % 10000);
QBitArray bits(561,false);
int bitoffset = 0;
bool lastbit = false;
for (int jdx = 0; jdx < 40; jdx++)
{
quint8 byte = m_data[idx++];
QBitArray dataBits = byteToBits(byte);
bool highBit = dataBits.at(0);
if (highBit) {
bits[bitoffset++] = lastbit;
}
for (int xdx = 1; xdx < 7; xdx++) {
bits[bitoffset++] = dataBits.at(xdx);
bits[bitoffset++] = dataBits.at(xdx);
}
lastbit = dataBits.at(7);
bits[bitoffset++] = dataBits.at(7);
if (!highBit) {
bits[bitoffset++] = dataBits.at(7);
}
}
drawMonoLine(pmpainter, yoff*2, bits);
if (!m_showScanLines) drawMonoLine(pmpainter, yoff*2+1, bits);
chunkCount++;
if (chunkCount == 3) {
chunkCount = 0;
idx+=8;
}
}
} else if (m_viewMode == Color1) {
pmpainter.setPen(Qt::white);
pmpainter.setBrush(Qt::white);
for (int idx = 0; idx < m_data.size() ; idx++) {
int rowcol = findRowCol(idx);
quint8 byte = m_data[idx];
bool highBit = byte & 0x80;
QColor oddColor = highBit? QColor(orangeColor) : QColor(greenColor);
QColor evenColor = highBit? QColor(blueColor) : QColor(purpleColor);
int xoff = (rowcol / 10000) * 7;
int yoff = (rowcol % 10000)*2;
quint8 cOffset = 0;// highBit?1:0;
quint8 doubleScan = 0;
if (!m_showScanLines)
{
doubleScan = 1;
}
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x01) { pmpainter.drawRect((cOffset+xoff*2)+0,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?evenColor:oddColor);
pmpainter.setBrush(xoff & 0x01?evenColor:oddColor);
if (byte & 0x02) { pmpainter.drawRect((cOffset+xoff*2)+2,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x04) { pmpainter.drawRect((cOffset+xoff*2)+4,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?evenColor:oddColor);
pmpainter.setBrush(xoff & 0x01?evenColor:oddColor);
if (byte & 0x08) { pmpainter.drawRect((cOffset+xoff*2)+6,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x10) { pmpainter.drawRect((cOffset+xoff*2)+8,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?evenColor:oddColor);
pmpainter.setBrush(xoff & 0x01?evenColor:oddColor);
if (byte & 0x20) { pmpainter.drawRect((cOffset+xoff*2)+10,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x40) { pmpainter.drawRect((cOffset+xoff*2)+12,yoff,cOffset?0:1,doubleScan); }
if (idx >= (280*192)) break;
}
} else if (m_viewMode == Color2) {
pmpainter.setPen(Qt::white);
pmpainter.setBrush(Qt::white);
quint8 chunkCount = 0;
// for (int idx = 0; idx < m_data.size() ; idx+=40) {
int idx = 0;
while (idx < m_data.size()) {
int rowcol = findRowCol(idx);
// int xoff = (rowcol / 10000) * 7;
int yoff = (rowcol % 10000);
QBitArray bits(561,false);
int bitoffset = 0;
bool lastbit = false;
for (int jdx = 0; jdx < 40; jdx++)
{
quint8 byte = m_data[idx++];
QBitArray dataBits = byteToBits(byte);
bool highBit = dataBits.at(0);
if (highBit) {
bits[bitoffset++] = lastbit;
}
for (int xdx = 1; xdx < 7; xdx++) {
bits[bitoffset++] = dataBits.at(xdx);
bits[bitoffset++] = dataBits.at(xdx);
}
lastbit = dataBits.at(7);
bits[bitoffset++] = dataBits.at(7);
if (!highBit) {
bits[bitoffset++] = dataBits.at(7);
}
}
drawNtscLine(pmpainter, yoff*2, bits);
if (!m_showScanLines) {
drawNtscLine(pmpainter, yoff*2+1, bits);
}
chunkCount++;
if (chunkCount == 3) {
chunkCount = 0;
idx+=8;
}
}
}
}
void HiresViewWidget::setFile(BinaryFile *file) {
m_file = file;
@ -299,238 +29,17 @@ void HiresViewWidget::setFile(BinaryFile *file) {
QString title = QString("Image: %1").arg(m_file->filename());
setWindowTitle(title);
setData(file->data());
hrsw->setData(file->data());
}
void HiresViewWidget::setData(QByteArray data) {
m_data = data;
drawPixmap();
}
void HiresViewWidget::drawMonoLine(QPainter &painter, int lineNum, QBitArray data) {
for (int idx = 0; idx < data.count(); idx++) {
if (data.at(idx))
{
painter.setPen(Qt::white);
} else {
painter.setPen(Qt::black);
}
painter.drawPoint(idx,lineNum);
}
}
QColor HiresViewWidget::getColorFromBits(QBitArray bits, quint8 phase)
{
quint8 bitval = (bits[0] * 0x08) +
(bits[1] * 0x04) +
(bits[2] * 0x02) +
(bits[3] * 0x01);
phase %= 4;
//qDebug() << bits << phase;
if (bitval == 0) { return blackColor; }
if (bitval == 1 && phase == 0) return brownColor;
if (bitval == 2 && phase == 1) return brownColor;
if (bitval == 4 && phase == 2) return brownColor;
if (bitval == 8 && phase == 3) return brownColor;
if (bitval == 2 && phase == 0) return darkGreenColor;
if (bitval == 4 && phase == 1) return darkGreenColor;
if (bitval == 8 && phase == 2) return darkGreenColor;
if (bitval == 1 && phase == 3) return darkGreenColor;
if (bitval == 3 && phase == 0) return greenColor;
if (bitval == 6 && phase == 1) return greenColor;
if (bitval == 12 && phase == 2) return greenColor;
if (bitval == 9 && phase == 3) return greenColor;
if (bitval == 4 && phase == 0) return darkBlueColor;
if (bitval == 8 && phase == 1) return darkBlueColor;
if (bitval == 1 && phase == 2) return darkBlueColor;
if (bitval == 2 && phase == 3) return darkBlueColor;
if (bitval == 5 && phase == 0) return grayColor;
if (bitval == 10 && phase == 1) return grayColor;
if (bitval == 5 && phase == 2) return grayColor;
if (bitval == 10 && phase == 3) return grayColor;
if (bitval == 6 && phase == 0) return blueColor;
if (bitval == 12 && phase == 1) return blueColor;
if (bitval == 9 && phase == 2) return blueColor;
if (bitval == 3 && phase == 3) return blueColor;
if (bitval == 7 && phase == 0) return aquaColor;
if (bitval == 14 && phase == 1) return aquaColor;
if (bitval == 13 && phase == 2) return aquaColor;
if (bitval == 11 && phase == 3) return aquaColor;
if (bitval == 8 && phase == 0) return redColor;
if (bitval == 1 && phase == 1) return redColor;
if (bitval == 2 && phase == 2) return redColor;
if (bitval == 4 && phase == 3) return redColor;
if (bitval == 9 && phase == 0) return orangeColor;
if (bitval == 3 && phase == 1) return orangeColor;
if (bitval == 6 && phase == 2) return orangeColor;
if (bitval == 12 && phase == 3) return orangeColor;
if (bitval == 10 && phase == 0) return gray2Color;
if (bitval == 5 && phase == 1) return gray2Color;
if (bitval == 10 && phase == 2) return gray2Color;
if (bitval == 5 && phase == 3) return gray2Color;
if (bitval == 11 && phase == 0) return yellowColor;
if (bitval == 7 && phase == 1) return yellowColor;
if (bitval == 14 && phase == 2) return yellowColor;
if (bitval == 13 && phase == 3) return yellowColor;
if (bitval == 12 && phase == 0) return purpleColor;
if (bitval == 9 && phase == 1) return purpleColor;
if (bitval == 3 && phase == 2) return purpleColor;
if (bitval == 6 && phase == 3) return purpleColor;
if (bitval == 13 && phase == 0) return pinkColor;
if (bitval == 11 && phase == 1) return pinkColor;
if (bitval == 7 && phase == 2) return pinkColor;
if (bitval == 14 && phase == 3) return pinkColor;
if (bitval == 14 && phase == 0) return lightBlueColor;
if (bitval == 13 && phase == 1) return lightBlueColor;
if (bitval == 11 && phase == 2) return lightBlueColor;
if (bitval == 7 && phase == 3) return lightBlueColor;
return whiteColor;
}
void HiresViewWidget::drawNtscLine(QPainter &painter, int lineNum, QBitArray data) {
QVector<QColor> colors;
colors.resize(data.count()+3);
for (int idx = 0; idx < data.count(); idx++) {
QBitArray tmp(4);
tmp[0]=data.at(idx+0);
if (idx < data.count()-1) tmp[1]=data.at(idx+1); else tmp[1] = false;
if (idx < data.count()-2) tmp[2]=data.at(idx+2); else tmp[2] = false;
if (idx < data.count()-3) tmp[3]=data.at(idx+3); else tmp[3] = false;
colors[idx] = getColorFromBits(tmp,idx %4);
colors[idx+1] = getColorFromBits(tmp,idx %4);
colors[idx+2] = getColorFromBits(tmp,idx %4);
colors[idx+3] = getColorFromBits(tmp,idx %4);
}
for (int idx = 0; idx < colors.count(); idx++)
{
painter.setPen(colors.at(idx));
painter.setBrush(colors.at(idx));
painter.drawPoint(idx,lineNum);
}
}
QBitArray HiresViewWidget::byteToBits(quint8 byte) {
QBitArray bits(8);
bits.setBit(0,byte & 0x80);
bits.setBit(7,byte & 0x40);
bits.setBit(6,byte & 0x20);
bits.setBit(5,byte & 0x10);
bits.setBit(4,byte & 0x08);
bits.setBit(3,byte & 0x04);
bits.setBit(2,byte & 0x02);
bits.setBit(1,byte & 0x01);
return bits;
}
void HiresViewWidget::setMode(HiresViewWidget::ViewMode viewmode)
{
m_viewMode = viewmode;
update();
}
int HiresViewWidget::findRowCol(int offset) {
int retval = 0;
retval = (*m_rowTable)[offset];
return retval;
}
void HiresViewWidget::makeOffsetTable() {
m_rowTable = new QMap<int,int>();
for (int idx = 0; idx < 8192; idx++)
{
(*m_rowTable)[idx] = -1; // Fill the memory holes. Brute force, but it works.
}
int count = 0;
int outer = 0x0000;
for (int idx = 0; idx < 8; idx++) {
int inner = 0x0000;
for (int jdx = 0; jdx < 8; jdx++) {
for (int kdx = 0; kdx<40;kdx++) {
(*m_rowTable)[outer+inner+kdx] = count + (kdx*10000);
}
count++;
inner += 0x0400;
}
outer += 0x0080;
}
outer = 0x0028;
for (int idx = 0; idx < 8; idx++) {
int inner = 0x0000;
for (int jdx = 0; jdx < 8; jdx++) {
for (int kdx = 0; kdx<40;kdx++) {
(*m_rowTable)[outer+inner+kdx] = count + (kdx*10000);
}
count++;
inner += 0x0400;
}
outer += 0x0080;
}
outer = 0x0050;
for (int idx = 0; idx < 8; idx++) {
int inner = 0x0000;
for (int jdx = 0; jdx < 8; jdx++) {
for (int kdx = 0; kdx<40;kdx++) {
(*m_rowTable)[outer+inner+kdx] = count + (kdx*10000);
}
count++;
inner += 0x0400;
}
outer += 0x0080;
}
}
void HiresViewWidget::contextMenuEvent(QContextMenuEvent *event) {
QMenu menu(this);
menu.addAction(monochromeAction);
menu.addAction(ntscAction);
menu.addAction(perPixelColorAction);
menu.addSeparator();
menu.addAction(showScanLinesAction);
menu.exec(event->globalPos());
}
bool HiresViewWidget::optionsMenuItems(QMenu *menu)
{
menu->addAction(monochromeAction);
menu->addAction(ntscAction);
menu->addAction(perPixelColorAction);
menu->addAction(hrsw->monochromeAction());
menu->addAction(hrsw->ntscAction());
menu->addAction(hrsw->perPixelColorAction());
menu->addSeparator();
menu->addAction(showScanLinesAction);
menu->addAction(hrsw->showScanLinesAction());
return true;
}
@ -543,5 +52,3 @@ void HiresViewWidget::setFile(GenericFile *file)
}
}
QMap<int,int> *HiresViewWidget::m_rowTable = 0;

View File

@ -3,6 +3,7 @@
#include "binaryfile.h"
#include "fileviewerinterface.h"
#include "HiresScreenWidget.h"
#include <QWidget>
#include <QPixmap>
@ -13,22 +14,6 @@
#include <QAction>
#include <QMenu>
static const QColor blackColor = QColor(0,0,0);
static const QColor brownColor = QColor();
static const QColor darkGreenColor = QColor(96,114,3);
static const QColor greenColor = QColor(20,245,60);
static const QColor darkBlueColor = QColor(96,78,189);
static const QColor grayColor = QColor(156,156,156);
static const QColor blueColor = QColor(20,207,253);
static const QColor aquaColor = QColor(114,255,208);
static const QColor redColor = QColor(227,30,96);
static const QColor orangeColor = QColor(255,106,60);
static const QColor gray2Color = QColor(156,156,156);
static const QColor yellowColor = QColor(208,221,141);
static const QColor purpleColor = QColor(255,68,253);
static const QColor pinkColor = QColor(255,160,208);
static const QColor lightBlueColor = QColor(208,195,255);
static const QColor whiteColor = QColor(255,255,255);
class HiresViewWidget : public FileViewerInterface
@ -36,64 +21,15 @@ class HiresViewWidget : public FileViewerInterface
Q_OBJECT
public:
enum ViewMode {
Monochrome,
Color1,
Color2
};
explicit HiresViewWidget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
static QBitArray byteToBits(quint8 byte);
void contextMenuEvent(QContextMenuEvent *);
virtual bool optionsMenuItems(QMenu *);
signals:
explicit HiresViewWidget(QWidget *parent = 0);
virtual bool optionsMenuItems(QMenu *);
public slots:
void setFile(GenericFile *file);
void setFile(BinaryFile *file);
void setData(QByteArray data);
void setMode(ViewMode);
protected slots:
void handleNtscAction(bool toggled);
void handleMonochromeAction(bool toggled);
void handlePerPixelColorAction(bool toggled);
void handleShowScanLinesAction(bool toggled);
private:
QPixmap m_pixmap;
QByteArray m_data;
int findRowCol(int offset);
static QMap<int,int> *m_rowTable;
void makeOffsetTable();
ViewMode m_viewMode;
void drawNtscLine(QPainter &painter,int linenum, QBitArray data);
void drawMonoLine(QPainter &painter, int lineNum, QBitArray data);
QColor getColorFromBits(QBitArray bits, quint8 phase);
QAction *monochromeAction;
QAction *ntscAction;
QAction *perPixelColorAction;
QAction *showScanLinesAction;
QActionGroup *formatGroup;
bool m_showScanLines;
void drawPixmap();
HiresScreenWidget *hrsw;
BinaryFile *m_file;
};

View File

@ -0,0 +1,14 @@
#include "CharacterSetExplorer.h"
#include "ui_CharacterSetExplorer.h"
CharacterSetExplorer::CharacterSetExplorer(QWidget *parent) :
QDialog(parent),
ui(new Ui::CharacterSetExplorer)
{
ui->setupUi(this);
}
CharacterSetExplorer::~CharacterSetExplorer()
{
delete ui;
}

View File

@ -0,0 +1,22 @@
#ifndef CHARACTERSETEXPLORER_H
#define CHARACTERSETEXPLORER_H
#include <QDialog>
namespace Ui {
class CharacterSetExplorer;
}
class CharacterSetExplorer : public QDialog
{
Q_OBJECT
public:
explicit CharacterSetExplorer(QWidget *parent = 0);
~CharacterSetExplorer();
private:
Ui::CharacterSetExplorer *ui;
};
#endif // CHARACTERSETEXPLORER_H

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CharacterSetExplorer</class>
<widget class="QDialog" name="CharacterSetExplorer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>423</width>
<height>352</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout" rowstretch="1,999999">
<item row="1" column="0">
<widget class="QWidget" name="drawWidget" native="true"/>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Text:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="inputText"/>
</item>
<item>
<widget class="QPushButton" name="drawButton">
<property name="text">
<string>Draw</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,518 @@
#include "HiresScreenWidget.h"
#include "binaryfile.h"
#include <QPainter>
#include <QMap>
#include <QDebug>
#include <QResizeEvent>
#include <QSettings>
#include "util.h"
#include <math.h>
HiresScreenWidget::HiresScreenWidget(QWidget *parent) :
QWidget(parent)
{
if (m_rawAddressToColRowList.size() == 0 ||
m_appleAddressToColRowList.size() == 0)
{
makeAddressTables();
}
resize(561,384);
QSettings settings;
m_viewMode = static_cast<ViewMode>(settings.value("HiresScreenWidget.ViewMode",NTSCColor).toInt());
m_showScanLines = settings.value("HiresScreenWidget.ShowScanLines",true).toBool();
m_pixmap = QPixmap(561,384);
QPainter painter(&m_pixmap);
painter.setBrush(Qt::black);
painter.drawRect(0,0,this->width(),this->height());
formatGroup = new QActionGroup(this);
m_monochromeAction = new QAction("Monochrome Display",this);
m_monochromeAction->setCheckable(true);
m_monochromeAction->setChecked((m_viewMode == Monochrome));
formatGroup->addAction(m_monochromeAction);
m_ntscAction = new QAction("NTSC Display",this);
m_ntscAction->setCheckable(true);
m_ntscAction->setChecked((m_viewMode == NTSCColor));
formatGroup->addAction(m_ntscAction);
m_perPixelColorAction= new QAction("Per-Pixel Color Display",this);
m_perPixelColorAction->setCheckable(true);
m_perPixelColorAction->setChecked((m_viewMode == PerPixelColor));
formatGroup->addAction(m_perPixelColorAction);
m_showScanLinesAction = new QAction("Show Scan Lines",this);
m_showScanLinesAction->setCheckable(true);
m_showScanLinesAction->setChecked(m_showScanLines);
connect(m_ntscAction, SIGNAL(toggled(bool)), this, SLOT(handleNtscAction(bool)));
connect(m_monochromeAction, SIGNAL(toggled(bool)), this, SLOT(handleMonochromeAction(bool)));
connect(m_perPixelColorAction, SIGNAL(toggled(bool)), this, SLOT(handlePerPixelColorAction(bool)));
connect(m_showScanLinesAction, SIGNAL(toggled(bool)), this, SLOT(handleShowScanLinesAction(bool)));
}
void HiresScreenWidget::handleNtscAction(bool toggled) {
if (toggled) {
m_viewMode = NTSCColor;
update();
}
QSettings settings;
settings.setValue("HiresScreenWidget.ViewMode",m_viewMode);
}
void HiresScreenWidget::handleMonochromeAction(bool toggled) {
if (toggled) {
m_viewMode = Monochrome;
update();
}
QSettings settings;
settings.setValue("HiresScreenWidget.ViewMode",m_viewMode);
}
void HiresScreenWidget::handlePerPixelColorAction(bool toggled) {
if (toggled) {
m_viewMode = PerPixelColor;
update();
}
QSettings settings;
settings.setValue("HiresScreenWidget.ViewMode",m_viewMode);
}
void HiresScreenWidget::handleShowScanLinesAction(bool toggled) {
m_showScanLines = toggled;
update();
QSettings settings;
settings.setValue("HiresScreenWidget.ShowScanLines",toggled);
}
void HiresScreenWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
// if (m_pixmap.size() != this->size()) {
// m_pixmap = m_pixmap.scaled(this->size(),Qt::KeepAspectRatio);
// }
drawPixmap();
QPainter painter(this);
QPixmap tmppixmap = m_pixmap;
// tmppixmap = tmppixmap.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
painter.drawPixmap(0,0,tmppixmap);
}
void HiresScreenWidget::resizeEvent(QResizeEvent *)
{
}
void HiresScreenWidget::drawPixmap() {
QPainter pmpainter(&m_pixmap);
pmpainter.setBrush(Qt::black);
pmpainter.setPen(Qt::black);
pmpainter.drawRect(0,0,m_pixmap.width(),m_pixmap.height());
if (m_viewMode == Monochrome || m_viewMode == NTSCColor)
{
pmpainter.setPen(Qt::white);
pmpainter.setBrush(Qt::white);
quint8 chunkCount = 0;
int idx = 0;
while (idx < qMin(m_data.size(),8192)) {
ColRow cr = getColRowFromAppleAddress(idx);
int yoff = cr.row();
QBitArray bits(561,false);
int bitoffset = 0;
bool lastbit = false;
for (int jdx = 0; jdx < 40; jdx++)
{
quint8 byte = m_data[idx++];
QBitArray dataBits = byteToBits(byte);
bool highBit = dataBits.at(0);
if (highBit) {
bits[bitoffset++] = lastbit;
}
for (int xdx = 1; xdx < 7; xdx++) {
bits[bitoffset++] = dataBits.at(xdx);
bits[bitoffset++] = dataBits.at(xdx);
}
lastbit = dataBits.at(7);
bits[bitoffset++] = dataBits.at(7);
if (!highBit) {
bits[bitoffset++] = dataBits.at(7);
}
}
if (m_viewMode == Monochrome)
{
drawMonoLine(pmpainter, yoff*2, bits);
if (!m_showScanLines) drawMonoLine(pmpainter, yoff*2+1, bits);
}
else
{
drawNtscLine(pmpainter, yoff*2, bits);
if (!m_showScanLines) drawNtscLine(pmpainter, yoff*2+1, bits);
}
chunkCount++;
if (chunkCount == 3) {
chunkCount = 0;
idx+=8; // Skip screen hole
}
}
}
else if (m_viewMode == PerPixelColor)
{
pmpainter.setPen(Qt::white);
pmpainter.setBrush(Qt::white);
for (int idx = 0; idx < qMin(m_data.size(),8192) ; idx++) {
ColRow cr = getColRowFromAppleAddress(idx);
quint8 byte = m_data[idx];
bool highBit = byte & 0x80;
QColor oddColor = highBit? QColor(orangeColor) : QColor(greenColor);
QColor evenColor = highBit? QColor(blueColor) : QColor(purpleColor);
int xoff = cr.col() * 7;
int yoff = cr.row() * 2;
quint8 cOffset = 0;// highBit?1:0;
quint8 doubleScan = 0;
if (!m_showScanLines)
{
doubleScan = 1;
}
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x01) { pmpainter.drawRect((cOffset+xoff*2)+0,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?evenColor:oddColor);
pmpainter.setBrush(xoff & 0x01?evenColor:oddColor);
if (byte & 0x02) { pmpainter.drawRect((cOffset+xoff*2)+2,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x04) { pmpainter.drawRect((cOffset+xoff*2)+4,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?evenColor:oddColor);
pmpainter.setBrush(xoff & 0x01?evenColor:oddColor);
if (byte & 0x08) { pmpainter.drawRect((cOffset+xoff*2)+6,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x10) { pmpainter.drawRect((cOffset+xoff*2)+8,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?evenColor:oddColor);
pmpainter.setBrush(xoff & 0x01?evenColor:oddColor);
if (byte & 0x20) { pmpainter.drawRect((cOffset+xoff*2)+10,yoff,1,doubleScan); }
pmpainter.setPen(xoff & 0x01?oddColor:evenColor);
pmpainter.setBrush(xoff & 0x01?oddColor:evenColor);
if (byte & 0x40) { pmpainter.drawRect((cOffset+xoff*2)+12,yoff,cOffset?0:1,doubleScan); }
if (idx >= (280*192)) break;
}
}
}
void HiresScreenWidget::setUnpackedData(QByteArray unpackedData)
{
QByteArray packedData = packData(unpackedData);
setData(packedData);
}
QByteArray HiresScreenWidget::packData(QByteArray unpackedData)
{
QByteArray packedData;
packedData.resize(8192);
packedData.fill(0x00);
for (int idx = 0; idx < qMin(unpackedData.count(),8192); idx++)
{
ColRow cr = getColRowFromRawAddress(idx);
packedData[cr.appleAddress()] = unpackedData[idx];
}
return packedData;
}
int HiresScreenWidget::getLineAddressOffset(int line)
{
static QList<quint16> blockOffset {
0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380,
0x0028, 0x00A0, 0x0128, 0x01A8, 0x0228, 0x02A8, 0x0328, 0x03A8,
0x0040, 0x00D0, 0x0150, 0x00D0, 0x0250, 0x02D0, 0x0350, 0x03D0
};
static QList<quint16> boxSub {
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00
};
int block = line / 8;
int sub = line % 8;
return blockOffset[block] + boxSub[sub];
}
void HiresScreenWidget::setData(QByteArray data) {
m_data = data;
drawPixmap();
}
void HiresScreenWidget::drawMonoLine(QPainter &painter, int lineNum, QBitArray data) {
for (int idx = 0; idx < data.count(); idx++) {
if (data.at(idx))
{
painter.setPen(Qt::white);
} else {
painter.setPen(Qt::black);
}
painter.drawPoint(idx,lineNum);
}
}
QColor HiresScreenWidget::getColorFromBits(QBitArray bits, quint8 phase)
{
quint8 bitval = (bits[0] * 0x08) +
(bits[1] * 0x04) +
(bits[2] * 0x02) +
(bits[3] * 0x01);
phase %= 4;
if (bitval == 0) { return blackColor; }
if (bitval == 1 && phase == 0) return brownColor;
if (bitval == 2 && phase == 1) return brownColor;
if (bitval == 4 && phase == 2) return brownColor;
if (bitval == 8 && phase == 3) return brownColor;
if (bitval == 2 && phase == 0) return darkGreenColor;
if (bitval == 4 && phase == 1) return darkGreenColor;
if (bitval == 8 && phase == 2) return darkGreenColor;
if (bitval == 1 && phase == 3) return darkGreenColor;
if (bitval == 3 && phase == 0) return greenColor;
if (bitval == 6 && phase == 1) return greenColor;
if (bitval == 12 && phase == 2) return greenColor;
if (bitval == 9 && phase == 3) return greenColor;
if (bitval == 4 && phase == 0) return darkBlueColor;
if (bitval == 8 && phase == 1) return darkBlueColor;
if (bitval == 1 && phase == 2) return darkBlueColor;
if (bitval == 2 && phase == 3) return darkBlueColor;
if (bitval == 5 && phase == 0) return grayColor;
if (bitval == 10 && phase == 1) return grayColor;
if (bitval == 5 && phase == 2) return grayColor;
if (bitval == 10 && phase == 3) return grayColor;
if (bitval == 6 && phase == 0) return blueColor;
if (bitval == 12 && phase == 1) return blueColor;
if (bitval == 9 && phase == 2) return blueColor;
if (bitval == 3 && phase == 3) return blueColor;
if (bitval == 7 && phase == 0) return aquaColor;
if (bitval == 14 && phase == 1) return aquaColor;
if (bitval == 13 && phase == 2) return aquaColor;
if (bitval == 11 && phase == 3) return aquaColor;
if (bitval == 8 && phase == 0) return redColor;
if (bitval == 1 && phase == 1) return redColor;
if (bitval == 2 && phase == 2) return redColor;
if (bitval == 4 && phase == 3) return redColor;
if (bitval == 9 && phase == 0) return orangeColor;
if (bitval == 3 && phase == 1) return orangeColor;
if (bitval == 6 && phase == 2) return orangeColor;
if (bitval == 12 && phase == 3) return orangeColor;
if (bitval == 10 && phase == 0) return gray2Color;
if (bitval == 5 && phase == 1) return gray2Color;
if (bitval == 10 && phase == 2) return gray2Color;
if (bitval == 5 && phase == 3) return gray2Color;
if (bitval == 11 && phase == 0) return yellowColor;
if (bitval == 7 && phase == 1) return yellowColor;
if (bitval == 14 && phase == 2) return yellowColor;
if (bitval == 13 && phase == 3) return yellowColor;
if (bitval == 12 && phase == 0) return purpleColor;
if (bitval == 9 && phase == 1) return purpleColor;
if (bitval == 3 && phase == 2) return purpleColor;
if (bitval == 6 && phase == 3) return purpleColor;
if (bitval == 13 && phase == 0) return pinkColor;
if (bitval == 11 && phase == 1) return pinkColor;
if (bitval == 7 && phase == 2) return pinkColor;
if (bitval == 14 && phase == 3) return pinkColor;
if (bitval == 14 && phase == 0) return lightBlueColor;
if (bitval == 13 && phase == 1) return lightBlueColor;
if (bitval == 11 && phase == 2) return lightBlueColor;
if (bitval == 7 && phase == 3) return lightBlueColor;
return whiteColor;
}
void HiresScreenWidget::drawNtscLine(QPainter &painter, int lineNum, QBitArray data) {
QVector<QColor> colors;
colors.resize(data.count()+3);
for (int idx = 0; idx < data.count(); idx++) {
QBitArray tmp(4);
tmp[0]=data.at(idx+0);
if (idx < data.count()-1) tmp[1]=data.at(idx+1); else tmp[1] = false;
if (idx < data.count()-2) tmp[2]=data.at(idx+2); else tmp[2] = false;
if (idx < data.count()-3) tmp[3]=data.at(idx+3); else tmp[3] = false;
colors[idx] = getColorFromBits(tmp,idx %4);
colors[idx+1] = getColorFromBits(tmp,idx %4);
colors[idx+2] = getColorFromBits(tmp,idx %4);
colors[idx+3] = getColorFromBits(tmp,idx %4);
}
for (int idx = 0; idx < colors.count(); idx++)
{
painter.setPen(colors.at(idx));
painter.setBrush(colors.at(idx));
painter.drawPoint(idx,lineNum);
}
}
QBitArray HiresScreenWidget::byteToBits(quint8 byte) {
QBitArray bits(8);
bits.setBit(0,byte & 0x80);
bits.setBit(7,byte & 0x40);
bits.setBit(6,byte & 0x20);
bits.setBit(5,byte & 0x10);
bits.setBit(4,byte & 0x08);
bits.setBit(3,byte & 0x04);
bits.setBit(2,byte & 0x02);
bits.setBit(1,byte & 0x01);
return bits;
}
void HiresScreenWidget::setMode(HiresScreenWidget::ViewMode viewmode)
{
m_viewMode = viewmode;
update();
}
void HiresScreenWidget::contextMenuEvent(QContextMenuEvent *event) {
QMenu menu(this);
menu.addAction(m_monochromeAction);
menu.addAction(m_ntscAction);
menu.addAction(m_perPixelColorAction);
menu.addSeparator();
menu.addAction(m_showScanLinesAction);
menu.exec(event->globalPos());
}
HiresScreenWidget::ColRow HiresScreenWidget::getColRowFromAppleAddress(quint16 address)
{
if (address > 8191) {
qDebug() << "Invalid apple address " << address;
address = 8191;
}
return m_appleAddressToColRowList[address];
}
HiresScreenWidget::ColRow HiresScreenWidget::getColRowFromRawAddress(quint16 address)
{
if (address > 8191) {
qDebug() << "Invalid raw address " << address;
address = 8191;
}
return m_rawAddressToColRowList[address];
}
void HiresScreenWidget::makeAddressTables()
{
qDebug() << " ****** makeAddressTables()";
m_rawAddressToColRowList.resize(8192);
m_appleAddressToColRowList.resize(8192);
for (int row = 0; row < 192; row++)
{
for (int col = 0; col < 40; col++)
{
ColRow cr(col,row);
// qDebug() << "Col: " << col << "Row: " << row << "Raw: " << cr.rawAddress() << " Apple: " << cr.appleAddress();
m_rawAddressToColRowList[cr.rawAddress()] = cr;
m_appleAddressToColRowList[cr.appleAddress()] = cr;
}
}
#ifdef DEBUGHOLES
int num = 0;
quint16 lasthole = 0;
for (int idx = 0; idx < 8192; idx++)
{
if (!m_appleAddressToColRowList[idx].isDefined())
{
if (lasthole != 0 && lasthole != (idx-1)) { qDebug() << "\n"; }
qDebug() << "Hole" << num << "at" << idx+0x2000 << uint16ToHex(idx+0x2000); ;
num++;
lasthole = idx;
}
}
#endif
}
QVector<HiresScreenWidget::ColRow> HiresScreenWidget::m_rawAddressToColRowList
= QVector<HiresScreenWidget::ColRow>();
QVector<HiresScreenWidget::ColRow> HiresScreenWidget::m_appleAddressToColRowList
= QVector<HiresScreenWidget::ColRow>();

View File

@ -0,0 +1,149 @@
#ifndef HIRESSCREENWIDGET_H
#define HIRESSCREENWIDGET_H
#include "binaryfile.h"
#include <QWidget>
#include <QPixmap>
#include <QByteArray>
#include <QPair>
#include <QMap>
#include <QBitArray>
#include <QAction>
#include <QMenu>
#include <QVector>
#include <QDebug>
static const QColor blackColor = QColor(0,0,0);
static const QColor grayColor = QColor(156,156,156);
static const QColor gray2Color = QColor(156,156,156);
static const QColor whiteColor = QColor(255,255,255);
static const QColor darkBlueColor = QColor(96,78,189);
static const QColor lightBlueColor = QColor(208,195,255);
static const QColor purpleColor = QColor(255,68,253);
static const QColor redColor = QColor(227,30,96);
static const QColor pinkColor = QColor(255,160,208);
static const QColor orangeColor = QColor(255,106,60);
static const QColor brownColor = QColor(96,114,3);
static const QColor yellowColor = QColor(208,221,141);
static const QColor greenColor = QColor(20,245,60);
static const QColor darkGreenColor = QColor(0,163,96);
static const QColor aquaColor = QColor(114,255,208);
static const QColor blueColor = QColor(20,207,253);
class HiresScreenWidget : public QWidget
{
Q_OBJECT
public:
class ColRow {
public:
ColRow() { m_col = 0; m_row = 0; m_undefined = true; }
ColRow(quint8 col, quint8 row) { setColRow(col,row); }
void setColRow(quint8 col, quint8 row) { m_col = col; m_row = row; calc(); }
quint8 col() const { return m_col; }
quint8 row() const { return m_row; }
bool isDefined() const { return !m_undefined; }
bool isUndefined() const { return m_undefined; }
quint16 appleAddress() { return m_appleAddress; }
quint16 rawAddress() { return m_rawAddress; }
private:
quint8 m_col;
quint8 m_row;
quint16 m_appleAddress;
quint16 m_rawAddress;
bool m_undefined;
void calc() {
if (m_col > 39) { qDebug() << "Col out of range: " << m_col; m_col = 39; }
if (m_row > 191) { qDebug() << "Row out of range: " << m_row; m_row = 191; }
m_rawAddress = (m_row*40) + m_col;
unsigned short blockOffset[] = {
0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380,
0x0028, 0x00A8, 0x0128, 0x01A8, 0x0228, 0x02A8, 0x0328, 0x03A8,
0x0050, 0x00D0, 0x0150, 0x01D0, 0x0250, 0x02D0, 0x0350, 0x03D0
};
unsigned short boxSub[] = {
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00
};
m_appleAddress = blockOffset[m_row/8]+boxSub[m_row%8] + m_col;
m_undefined = false;
}
};
enum ViewMode {
Monochrome,
PerPixelColor,
NTSCColor
};
explicit HiresScreenWidget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
static QBitArray byteToBits(quint8 byte);
void contextMenuEvent(QContextMenuEvent *);
QAction *monochromeAction() { return m_monochromeAction; }
QAction *ntscAction() { return m_ntscAction; }
QAction *perPixelColorAction() { return m_perPixelColorAction; }
QAction *showScanLinesAction() { return m_showScanLinesAction; }
ColRow getColRowFromAppleAddress(quint16 address);
ColRow getColRowFromRawAddress(quint16 address);
signals:
public slots:
void setData(QByteArray data);
void setMode(ViewMode);
void setUnpackedData(QByteArray unpackedData);
protected:
int getLineAddressOffset(int line);
QByteArray packData(QByteArray unpackedData);
protected slots:
void handleNtscAction(bool toggled);
void handleMonochromeAction(bool toggled);
void handlePerPixelColorAction(bool toggled);
void handleShowScanLinesAction(bool toggled);
private:
void makeOffsetTable();
void makeAddressTables();
QColor getColorFromBits(QBitArray bits, quint8 phase);
void drawNtscLine(QPainter &painter,int linenum, QBitArray data);
void drawMonoLine(QPainter &painter, int lineNum, QBitArray data);
void drawPixmap();
QPixmap m_pixmap;
QByteArray m_data;
ViewMode m_viewMode;
QAction *m_monochromeAction;
QAction *m_ntscAction;
QAction *m_perPixelColorAction;
QAction *m_showScanLinesAction;
QActionGroup *formatGroup;
bool m_showScanLines;
static QVector<ColRow> m_rawAddressToColRowList;
static QVector<ColRow> m_appleAddressToColRowList;
};
#endif // HIRESSCREENWIDGET_H