From a6ec7651fb7140fb8b6c9a2d513954d840df707d Mon Sep 17 00:00:00 2001 From: Vort Date: Mon, 3 Aug 2020 19:31:47 +0300 Subject: [PATCH 1/3] TVScreenAnalog with subsample shift shader added --- plugins/channelrx/demodatv/atvdemod.h | 2 +- plugins/channelrx/demodatv/atvdemodbaseband.h | 2 +- plugins/channelrx/demodatv/atvdemodgui.cpp | 2 - plugins/channelrx/demodatv/atvdemodgui.ui | 6 +- plugins/channelrx/demodatv/atvdemodsink.cpp | 2 +- plugins/channelrx/demodatv/atvdemodsink.h | 33 ++- sdrgui/CMakeLists.txt | 2 + sdrgui/gui/glshadertvarray.cpp | 32 +-- sdrgui/gui/glshadertvarray.h | 4 - sdrgui/gui/tvscreen.cpp | 10 +- sdrgui/gui/tvscreen.h | 4 +- sdrgui/gui/tvscreenanalog.cpp | 246 ++++++++++++++++++ sdrgui/gui/tvscreenanalog.h | 88 +++++++ 13 files changed, 373 insertions(+), 60 deletions(-) create mode 100644 sdrgui/gui/tvscreenanalog.cpp create mode 100644 sdrgui/gui/tvscreenanalog.h diff --git a/plugins/channelrx/demodatv/atvdemod.h b/plugins/channelrx/demodatv/atvdemod.h index aae35a695..95133c5f3 100644 --- a/plugins/channelrx/demodatv/atvdemod.h +++ b/plugins/channelrx/demodatv/atvdemod.h @@ -87,7 +87,7 @@ public: } void setScopeSink(BasebandSampleSink* scopeSink) { m_basebandSink->setScopeSink(scopeSink); } - void setTVScreen(TVScreen *tvScreen) { m_basebandSink->setTVScreen(tvScreen); }; //!< set by the GUI + void setTVScreen(TVScreenAnalog *tvScreen) { m_basebandSink->setTVScreen(tvScreen); }; //!< set by the GUI double getMagSq() const { return m_basebandSink->getMagSq(); } //!< Beware this is scaled to 2^30 bool getBFOLocked() { return m_basebandSink->getBFOLocked(); } void setVideoTabIndex(int videoTabIndex) { m_basebandSink->setVideoTabIndex(videoTabIndex); } diff --git a/plugins/channelrx/demodatv/atvdemodbaseband.h b/plugins/channelrx/demodatv/atvdemodbaseband.h index f666bea08..a154a5a83 100644 --- a/plugins/channelrx/demodatv/atvdemodbaseband.h +++ b/plugins/channelrx/demodatv/atvdemodbaseband.h @@ -64,7 +64,7 @@ public: int getChannelSampleRate() const; double getMagSq() const { return m_sink.getMagSq(); } void setScopeSink(BasebandSampleSink* scopeSink) { m_sink.setScopeSink(scopeSink); } - void setTVScreen(TVScreen *tvScreen) { m_sink.setTVScreen(tvScreen); } + void setTVScreen(TVScreenAnalog *tvScreen) { m_sink.setTVScreen(tvScreen); } bool getBFOLocked() { return m_sink.getBFOLocked(); } void setVideoTabIndex(int videoTabIndex) { m_sink.setVideoTabIndex(videoTabIndex); } void setBasebandSampleRate(int sampleRate); //!< To be used when supporting thread is stopped diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index b03c7bb50..02d7f91cc 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -242,8 +242,6 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base m_tvSampleRate(48000) { ui->setupUi(this); - ui->screenTV->setColor(false); - ui->screenTV->setExtraColumns(true); setAttribute(Qt::WA_DeleteOnClose, true); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); diff --git a/plugins/channelrx/demodatv/atvdemodgui.ui b/plugins/channelrx/demodatv/atvdemodgui.ui index 37a18c348..02641c0fd 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.ui +++ b/plugins/channelrx/demodatv/atvdemodgui.ui @@ -1138,7 +1138,7 @@ QLayout::SetMinimumSize - + 0 @@ -1242,9 +1242,9 @@ 1 - TVScreen + TVScreenAnalog QWidget -
gui/tvscreen.h
+
gui/tvscreenanalog.h
1
diff --git a/plugins/channelrx/demodatv/atvdemodsink.cpp b/plugins/channelrx/demodatv/atvdemodsink.cpp index 70ed65d8e..2688640bd 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.cpp +++ b/plugins/channelrx/demodatv/atvdemodsink.cpp @@ -359,7 +359,7 @@ void ATVDemodSink::applyStandard(int sampleRate, const ATVDemodSettings& setting // what is left in a line for the image m_interleaved = true; m_numberOfVSyncLines = 4; - m_numberOfBlackLines = 58; + m_numberOfBlackLines = 59; m_firstVisibleLine = 27; m_numberSamplesHSyncCrop = (int) (0.085f * lineDuration * sampleRate); // 8.5% of full line empirically break; diff --git a/plugins/channelrx/demodatv/atvdemodsink.h b/plugins/channelrx/demodatv/atvdemodsink.h index 6c6a74d58..3469241f5 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.h +++ b/plugins/channelrx/demodatv/atvdemodsink.h @@ -32,7 +32,7 @@ #include "dsp/phasediscri.h" #include "audio/audiofifo.h" #include "util/movingaverage.h" -#include "gui/tvscreen.h" +#include "gui/tvscreenanalog.h" #include "atvdemodsettings.h" @@ -44,7 +44,7 @@ public: virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); void setScopeSink(BasebandSampleSink* scopeSink) { m_scopeSink = scopeSink; } - void setTVScreen(TVScreen *tvScreen) { m_registeredTVScreen = tvScreen; } //!< set by the GUI + void setTVScreen(TVScreenAnalog *tvScreen) { m_registeredTVScreen = tvScreen; } //!< set by the GUI double getMagSq() const { return m_magSqAverage; } //!< Beware this is scaled to 2^30 bool getBFOLocked(); void setVideoTabIndex(int videoTabIndex) { m_videoTabIndex = videoTabIndex; } @@ -107,7 +107,7 @@ private: SampleVector m_scopeSampleBuffer; //*************** ATV PARAMETERS *************** - TVScreen *m_registeredTVScreen; + TVScreenAnalog *m_registeredTVScreen; //int m_intNumberSamplePerLine; int m_numberSamplesPerHTopNom; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - nominal value @@ -198,7 +198,7 @@ private: inline void processSample(float& sample, int& sampleVideo) { // Filling pixel on the current line - reference index 0 at start of sync pulse - m_registeredTVScreen->setDataColor(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo, sampleVideo, sampleVideo); + m_registeredTVScreen->setDataColor(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo); if (m_settings.m_hSync) { @@ -286,8 +286,9 @@ private: m_hSyncShiftCount = 0; m_hSyncErrorCount = 0; } - m_registeredTVScreen->renderImage(0, - shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + m_registeredTVScreen->renderImage(); + //m_registeredTVScreen->renderImage(0, + // shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } if (m_vSyncDetectSampleCount > m_vSyncDetectThreshold && @@ -319,8 +320,14 @@ private: int rowIndex = m_lineIndex - m_firstVisibleLine; if (m_interleaved) rowIndex = rowIndex * 2 - m_fieldIndex; - m_registeredTVScreen->selectRow(rowIndex); - } + + // TODO: CHANGE + float shiftSamples = 0.0f; + if (m_hSyncShiftCount != 0) + shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; + m_registeredTVScreen->selectRow(rowIndex, + shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + } // Vertical sync is obtained by skipping horizontal sync on the line that triggers vertical sync (new frame) inline void processEOLHSkip() @@ -342,13 +349,17 @@ private: m_hSyncShiftCount = 0; m_hSyncErrorCount = 0; } - m_registeredTVScreen->renderImage(0, - shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + m_registeredTVScreen->renderImage(); + //m_registeredTVScreen->renderImage(0, + // shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); m_lineIndex = 0; m_rowIndex = 0; } - m_registeredTVScreen->selectRow(m_rowIndex); + // TODO: CHANGE + float shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; + m_registeredTVScreen->selectRow(m_rowIndex, + shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } }; diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index cde208ddc..3f3507065 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -51,6 +51,7 @@ set(sdrgui_SOURCES gui/transverterbutton.cpp gui/transverterdialog.cpp gui/tvscreen.cpp + gui/tvscreenanalog.cpp gui/valuedial.cpp gui/valuedialz.cpp @@ -123,6 +124,7 @@ set(sdrgui_HEADERS gui/transverterbutton.h gui/transverterdialog.h gui/tvscreen.h + gui/tvscreenanalog.h gui/valuedial.h gui/valuedialz.h diff --git a/sdrgui/gui/glshadertvarray.cpp b/sdrgui/gui/glshadertvarray.cpp index 68a4eb80d..e3375c46b 100644 --- a/sdrgui/gui/glshadertvarray.cpp +++ b/sdrgui/gui/glshadertvarray.cpp @@ -39,13 +39,11 @@ GLShaderTVArray::GLShaderTVArray(bool blnColor) : m_blnColor(blnColor) { m_blnAlphaBlend = false; m_blnAlphaReset = false; - m_blnExtraColumns = false; m_objProgram = 0; m_objImage = 0; m_objTexture = 0; m_intCols = 0; m_intRows = 0; - m_subsampleShift = 0.0f; m_blnInitialized = false; m_objCurrentRow = 0; @@ -112,8 +110,7 @@ void GLShaderTVArray::InitializeGL(int intCols, int intRows) } //Image container - int cols = intCols + (m_blnExtraColumns ? 2 : 0); - m_objImage = new QImage(cols, intRows, QImage::Format_RGBA8888); + m_objImage = new QImage(intCols, intRows, QImage::Format_RGBA8888); m_objImage->fill(QColor(0, 0, 0)); m_objTexture = new QOpenGLTexture(*m_objImage); @@ -158,23 +155,11 @@ void GLShaderTVArray::RenderPixels(unsigned char *chrData) QMatrix4x4 objQMatrix; - float rectHalfWidth = 1.0f; - float sampleSize = 2.0f / m_intCols; - if (m_blnExtraColumns) - rectHalfWidth += sampleSize; - float xShift = sampleSize * m_subsampleShift; - GLfloat arrVertices[] = // 2 3 // 1 4 - { - -rectHalfWidth + xShift, -1.0f, // 1 - -rectHalfWidth + xShift, 1.0f, // 2 - rectHalfWidth + xShift, 1.0f, // 3 - rectHalfWidth + xShift, 1.0f, // 3 - rectHalfWidth + xShift, -1.0f, // 4 - -rectHalfWidth + xShift, -1.0f // 1 - }; + //1 2 3 3 4 1 + { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f }; GLfloat arrTextureCoords[] = // 1 4 @@ -242,9 +227,8 @@ void GLShaderTVArray::RenderPixels(unsigned char *chrData) m_objTexture->bind(); - ptrF->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - m_intCols + (m_blnExtraColumns ? 2 : 0), m_intRows, GL_RGBA, - GL_UNSIGNED_BYTE, m_objImage->bits()); + ptrF->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_intCols, m_intRows, GL_RGBA, + GL_UNSIGNED_BYTE, m_objImage->bits()); ptrF->glEnableVertexAttribArray(0); // vertex ptrF->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, arrVertices); @@ -334,11 +318,9 @@ bool GLShaderTVArray::SetDataColor(int intCol, QRgb objColor) if (m_blnInitialized) { - if ((intCol < m_intCols + m_blnExtraColumns) && - (intCol >= -m_blnExtraColumns) && - (m_objCurrentRow != 0)) + if ((intCol < m_intCols) && (intCol >= 0) && (m_objCurrentRow != 0)) { - m_objCurrentRow[intCol + m_blnExtraColumns] = objColor; + m_objCurrentRow[intCol] = objColor; blnRslt = true; } } diff --git a/sdrgui/gui/glshadertvarray.h b/sdrgui/gui/glshadertvarray.h index 7b9a07600..559a7351f 100644 --- a/sdrgui/gui/glshadertvarray.h +++ b/sdrgui/gui/glshadertvarray.h @@ -43,8 +43,6 @@ public: GLShaderTVArray(bool blnColor); ~GLShaderTVArray(); - void setExtraColumns(bool blnExtraColumns) { m_blnExtraColumns = blnExtraColumns; } - void setSubsampleShift(float subsampleShift) { m_subsampleShift = subsampleShift; } void setColor(bool blnColor) { m_blnColor = blnColor; } void setAlphaBlend(bool blnAlphaBlend) { m_blnAlphaBlend = blnAlphaBlend; } void setAlphaReset() { m_blnAlphaReset = true; } @@ -73,7 +71,6 @@ protected: int m_intCols; int m_intRows; - float m_subsampleShift; QRgb * m_objCurrentRow; @@ -81,7 +78,6 @@ protected: bool m_blnColor; bool m_blnAlphaBlend; bool m_blnAlphaReset; - bool m_blnExtraColumns; }; #endif /* INCLUDE_GUI_GLTVSHADERARRAY_H_ */ diff --git a/sdrgui/gui/tvscreen.cpp b/sdrgui/gui/tvscreen.cpp index e1182999f..26ce7b490 100644 --- a/sdrgui/gui/tvscreen.cpp +++ b/sdrgui/gui/tvscreen.cpp @@ -37,7 +37,6 @@ TVScreen::TVScreen(bool blnColor, QWidget* parent) : m_objTimer.start(40); // capped at 25 FPS m_chrLastData = NULL; - m_subsampleShift = 0.0; m_blnConfigChanged = false; m_blnDataChanged = false; m_blnGLContextInitialized = false; @@ -59,11 +58,6 @@ void TVScreen::setColor(bool blnColor) m_objGLShaderArray.setColor(blnColor); } -void TVScreen::setExtraColumns(bool blnExtraColumns) -{ - m_objGLShaderArray.setExtraColumns(blnExtraColumns); -} - QRgb* TVScreen::getRowBuffer(int intRow) { if (!m_blnGLContextInitialized) @@ -74,10 +68,9 @@ QRgb* TVScreen::getRowBuffer(int intRow) return m_objGLShaderArray.GetRowBuffer(intRow); } -void TVScreen::renderImage(unsigned char * objData, float subsampleShift) +void TVScreen::renderImage(unsigned char * objData) { m_chrLastData = objData; - m_subsampleShift = subsampleShift; m_blnDataChanged = true; } @@ -184,7 +177,6 @@ void TVScreen::paintGL() m_intAskedRows = 0; } - m_objGLShaderArray.setSubsampleShift(m_subsampleShift); m_objGLShaderArray.RenderPixels(m_chrLastData); m_objMutex.unlock(); diff --git a/sdrgui/gui/tvscreen.h b/sdrgui/gui/tvscreen.h index 5ce4fa077..c796ee0ab 100644 --- a/sdrgui/gui/tvscreen.h +++ b/sdrgui/gui/tvscreen.h @@ -46,10 +46,9 @@ public: virtual ~TVScreen(); void setColor(bool blnColor); - void setExtraColumns(bool blnExtraColumns); void resizeTVScreen(int intCols, int intRows); void getSize(int& intCols, int& intRows) const; - void renderImage(unsigned char * objData, float subsampleShift = 0.0); + void renderImage(unsigned char * objData); QRgb* getRowBuffer(int intRow); void resetImage(); void resetImage(int alpha); @@ -74,7 +73,6 @@ private: bool m_blnGLContextInitialized; int m_intAskedCols; int m_intAskedRows; - float m_subsampleShift; // state diff --git a/sdrgui/gui/tvscreenanalog.cpp b/sdrgui/gui/tvscreenanalog.cpp new file mode 100644 index 000000000..c1ba3898a --- /dev/null +++ b/sdrgui/gui/tvscreenanalog.cpp @@ -0,0 +1,246 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Vort // +// Copyright (C) 2018 F4HKW // +// for F4EXB / SDRAngel // +// // +// OpenGL interface modernization. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "tvscreenanalog.h" + +static const char* vertexShaderSource = +"attribute highp vec4 vertex;\n" +"attribute highp vec2 texCoord;\n" +"varying highp vec2 texCoordVar;\n" +"void main() {\n" +" gl_Position = vertex;\n" +" texCoordVar = texCoord;\n" +"}\n"; + +static const char* fragmentShaderSource = +"uniform highp sampler2D uTexture1;\n" +"uniform highp sampler2D uTexture2;\n" +"uniform highp float imw;\n" +"uniform highp float imh;\n" +"uniform highp float tlw;\n" +"uniform highp float tlh;\n" +"varying highp vec2 texCoordVar;\n" +"void main() {\n" +" float tlhw = 0.5 * tlw;" +" float tlhh = 0.5 * tlh;" +" float tys = (texCoordVar.y + tlhh) * imh;\n" +" float p1y = floor(tys) * tlh - tlhh;\n" +" float p3y = p1y + tlh;\n" +" float tshift1 = texture2D(uTexture2, vec2(0.0, p1y)).r;\n" +" float tshift3 = texture2D(uTexture2, vec2(0.0, p3y)).r;\n" +" float shift1 = (1.0 - tshift1 * 2.0) * tlw;\n" +" float shift3 = (1.0 - tshift3 * 2.0) * tlw;\n" +" float txs1 = (texCoordVar.x + shift1 + tlhw) * imw;\n" +" float txs3 = (texCoordVar.x + shift3 + tlhw) * imw;\n" +" float p1x = floor(txs1) * tlw - tlhw;\n" +" float p3x = floor(txs3) * tlw - tlhw;\n" +" float p2x = p1x + tlw;\n" +" float p4x = p3x + tlw;\n" +" float p1 = texture2D(uTexture1, vec2(p1x, p1y)).r;\n" +" float p2 = texture2D(uTexture1, vec2(p2x, p1y)).r;\n" +" float p3 = texture2D(uTexture1, vec2(p3x, p3y)).r;\n" +" float p4 = texture2D(uTexture1, vec2(p4x, p3y)).r;\n" +" float p12 = mix(p1, p2, fract(txs1));\n" +" float p34 = mix(p3, p4, fract(txs3));\n" +" float p = mix(p12, p34, fract(tys));\n" +" gl_FragColor = vec4(p);\n" +"}\n"; + +TVScreenAnalog::TVScreenAnalog(QWidget *parent) + : QGLWidget(parent) +{ + m_objCurrentRow = nullptr; + m_isDataChanged = false; + m_time = 0.0f; + m_cols = 1; + m_rows = 1; + + connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick())); + m_objTimer.start(40); // capped at 25 FPS +} + +void TVScreenAnalog::cleanup() +{ + m_shader = nullptr; + m_imageTexture = nullptr; + m_objCurrentRow = nullptr; + m_lineShiftsTexture = nullptr; +} + +void TVScreenAnalog::resizeTVScreen(int intCols, int intRows) +{ + qDebug("TVScreen::resizeTVScreen: cols: %d, rows: %d", intCols, intRows); + m_cols = intCols + 4; + m_rows = intRows; +} + +void TVScreenAnalog::resizeGL(int intWidth, int intHeight) +{ + glViewport(0, 0, intWidth, intHeight); +} + +void TVScreenAnalog::initializeGL() +{ + m_objMutex.lock(); + initializeOpenGLFunctions(); + + m_shader = std::make_shared(this); + m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); + m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); + m_shader->link(); + + m_vertexAttribIndex = m_shader->attributeLocation("vertex"); + m_texCoordAttribIndex = m_shader->attributeLocation("texCoord"); + m_objTextureLoc1 = m_shader->uniformLocation("uTexture1"); + m_objTextureLoc2 = m_shader->uniformLocation("uTexture2"); + m_objImageWidthLoc = m_shader->uniformLocation("imw"); + m_objImageHeightLoc = m_shader->uniformLocation("imh"); + m_objTexelWidthLoc = m_shader->uniformLocation("tlw"); + m_objTexelHeightLoc = m_shader->uniformLocation("tlh"); + + + initializeTextures(); + + connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, + this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget + + m_objMutex.unlock(); +} + +void TVScreenAnalog::initializeTextures() +{ + m_image = std::make_shared(m_cols, m_rows, QImage::Format_ARGB32); + m_lineShifts = std::make_shared(1, m_rows, QImage::Format_ARGB32); + m_image->fill(0); + m_lineShifts->fill(127); + + m_imageTexture = std::make_shared(*m_image, QOpenGLTexture::DontGenerateMipMaps); + m_lineShiftsTexture = std::make_shared(*m_lineShifts, QOpenGLTexture::DontGenerateMipMaps); + m_imageTexture->setMinificationFilter(QOpenGLTexture::Nearest); + m_imageTexture->setMagnificationFilter(QOpenGLTexture::Nearest); + m_lineShiftsTexture->setMinificationFilter(QOpenGLTexture::Nearest); + m_lineShiftsTexture->setMagnificationFilter(QOpenGLTexture::Nearest); + m_imageTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToBorder); + m_imageTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); + m_lineShiftsTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); + m_lineShiftsTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); +} + +void TVScreenAnalog::renderImage() +{ + m_isDataChanged = true; +} + +void TVScreenAnalog::tick() +{ + if (m_isDataChanged) { + update(); + } +} + +void TVScreenAnalog::selectRow(int intLine, float shift) +{ + if (!m_image || m_image->height() != m_rows) + return; + + if ((intLine < m_rows) && (intLine >= 0)) + { + m_objCurrentRow = (int*)m_image->scanLine(intLine); + m_lineShifts->setPixel(0, intLine, (1.0f + shift) * 127.5f); + } + else + { + m_objCurrentRow = nullptr; + } +} + +void TVScreenAnalog::setDataColor(int intCol, int objColor) +{ + if ((intCol < m_cols - 2) && + (intCol >= -2) && + (m_objCurrentRow != nullptr)) + { + m_objCurrentRow[intCol + 2] = objColor; + } +} + +void TVScreenAnalog::paintGL() +{ + if (!m_objMutex.tryLock(2)) + return; + + m_isDataChanged = false; + + if (m_image->width() != m_cols || m_image->height() != m_rows) + initializeTextures(); + + float imageWidth = m_image->width(); + float imageHeight = m_image->height(); + float texelWidth = 1.0f / imageWidth; + float texelHeight = 1.0f / imageHeight; + + m_shader->bind(); + m_shader->setUniformValue(m_objTextureLoc1, 0); + m_shader->setUniformValue(m_objTextureLoc2, 1); + m_shader->setUniformValue(m_objImageWidthLoc, imageWidth); + m_shader->setUniformValue(m_objImageHeightLoc, imageHeight); + m_shader->setUniformValue(m_objTexelWidthLoc, texelWidth); + m_shader->setUniformValue(m_objTexelHeightLoc, texelHeight); + + glActiveTexture(GL_TEXTURE0); + m_imageTexture->bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + m_image->width(), m_image->height(), GL_RGBA, GL_UNSIGNED_BYTE, m_image->constScanLine(0)); + + glActiveTexture(GL_TEXTURE1); + m_lineShiftsTexture->bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + 1, m_image->height(), GL_RGBA, GL_UNSIGNED_BYTE, m_lineShifts->constScanLine(0)); + + float rectHalfWidth = 1.0f + 4 * texelWidth; + GLfloat vertices[] = + { + -rectHalfWidth, -1.0f, + -rectHalfWidth, 1.0f, + rectHalfWidth, 1.0f, + rectHalfWidth, -1.0f + }; + + static const GLfloat arrTextureCoords[] = + { + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f + }; + + glVertexAttribPointer(m_vertexAttribIndex, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glEnableVertexAttribArray(m_vertexAttribIndex); + glVertexAttribPointer(m_texCoordAttribIndex, 2, GL_FLOAT, GL_FALSE, 0, arrTextureCoords); + glEnableVertexAttribArray(m_texCoordAttribIndex); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisableVertexAttribArray(m_vertexAttribIndex); + glDisableVertexAttribArray(m_texCoordAttribIndex); + + m_shader->release(); + + m_objMutex.unlock(); +} \ No newline at end of file diff --git a/sdrgui/gui/tvscreenanalog.h b/sdrgui/gui/tvscreenanalog.h new file mode 100644 index 000000000..70a1cedb8 --- /dev/null +++ b/sdrgui/gui/tvscreenanalog.h @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Vort // +// Copyright (C) 2018 F4HKW // +// for F4EXB / SDRAngel // +// // +// OpenGL interface modernization. // +// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_TVSCREENANALOG_H +#define INCLUDE_TVSCREENANALOG_H + +#include "export.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +class SDRGUI_API TVScreenAnalog : public QGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + + QTimer m_objTimer; + QMutex m_objMutex; + + bool m_isDataChanged; + + int m_objTextureLoc1; + int m_objTextureLoc2; + int m_objImageWidthLoc; + int m_objImageHeightLoc; + int m_objTexelWidthLoc; + int m_objTexelHeightLoc; + int m_vertexAttribIndex; + int m_texCoordAttribIndex; + std::shared_ptr m_shader; + + float m_time; + + int m_cols; + int m_rows; + + int* m_objCurrentRow; + + std::shared_ptr m_image; + std::shared_ptr m_lineShifts; + std::shared_ptr m_imageTexture; + std::shared_ptr m_lineShiftsTexture; + +public: + TVScreenAnalog(QWidget *parent); + + void resizeTVScreen(int intCols, int intRows); + void selectRow(int intLine, float shift); + void setDataColor(int intCol, int objColor); + void renderImage(); + +private: + void initializeTextures(); + void initializeGL() override; + void paintGL() override; + void resizeGL(int width, int height); + +private slots: + void cleanup(); + void tick(); +}; + +#endif // INCLUDE_TVSCREENANALOG_H From 44d199cfab495921669f6f5168691a9e0e5ae20b Mon Sep 17 00:00:00 2001 From: Vort Date: Tue, 4 Aug 2020 17:02:06 +0300 Subject: [PATCH 2/3] Move TVScreen data to separate object --- plugins/channelrx/demodatv/atvdemodsink.cpp | 2 + plugins/channelrx/demodatv/atvdemodsink.h | 8 +- sdrgui/gui/tvscreenanalog.cpp | 127 ++++++++------------ sdrgui/gui/tvscreenanalog.h | 106 +++++++++++++--- 4 files changed, 145 insertions(+), 98 deletions(-) diff --git a/plugins/channelrx/demodatv/atvdemodsink.cpp b/plugins/channelrx/demodatv/atvdemodsink.cpp index 2688640bd..75c73cae8 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.cpp +++ b/plugins/channelrx/demodatv/atvdemodsink.cpp @@ -486,6 +486,7 @@ void ATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque m_samplesPerLine - m_numberSamplesPerLineSignals, m_settings.m_nbLines - m_numberOfBlackLines ); + m_tvScreenData = m_registeredTVScreen->getData(); } m_fieldIndex = 0; @@ -579,6 +580,7 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force) m_samplesPerLine - m_numberSamplesPerLineSignals, m_settings.m_nbLines - m_numberOfBlackLines ); + m_tvScreenData = m_registeredTVScreen->getData(); } m_fieldIndex = 0; diff --git a/plugins/channelrx/demodatv/atvdemodsink.h b/plugins/channelrx/demodatv/atvdemodsink.h index 3469241f5..8c250cb31 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.h +++ b/plugins/channelrx/demodatv/atvdemodsink.h @@ -20,6 +20,7 @@ #include #include +#include #include "dsp/channelsamplesink.h" #include "dsp/basebandsamplesink.h" @@ -108,6 +109,7 @@ private: //*************** ATV PARAMETERS *************** TVScreenAnalog *m_registeredTVScreen; + std::shared_ptr m_tvScreenData; //int m_intNumberSamplePerLine; int m_numberSamplesPerHTopNom; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - nominal value @@ -198,7 +200,7 @@ private: inline void processSample(float& sample, int& sampleVideo) { // Filling pixel on the current line - reference index 0 at start of sync pulse - m_registeredTVScreen->setDataColor(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo); + m_tvScreenData->setSampleValue(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo); if (m_settings.m_hSync) { @@ -325,7 +327,7 @@ private: float shiftSamples = 0.0f; if (m_hSyncShiftCount != 0) shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; - m_registeredTVScreen->selectRow(rowIndex, + m_tvScreenData->selectRow(rowIndex, shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } @@ -358,7 +360,7 @@ private: // TODO: CHANGE float shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; - m_registeredTVScreen->selectRow(m_rowIndex, + m_tvScreenData->setSampleValue(m_rowIndex, shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } }; diff --git a/sdrgui/gui/tvscreenanalog.cpp b/sdrgui/gui/tvscreenanalog.cpp index c1ba3898a..d61300a9f 100644 --- a/sdrgui/gui/tvscreenanalog.cpp +++ b/sdrgui/gui/tvscreenanalog.cpp @@ -31,8 +31,8 @@ static const char* vertexShaderSource = "}\n"; static const char* fragmentShaderSource = -"uniform highp sampler2D uTexture1;\n" -"uniform highp sampler2D uTexture2;\n" +"uniform highp sampler2D tex1;\n" +"uniform highp sampler2D tex2;\n" "uniform highp float imw;\n" "uniform highp float imh;\n" "uniform highp float tlw;\n" @@ -44,8 +44,8 @@ static const char* fragmentShaderSource = " float tys = (texCoordVar.y + tlhh) * imh;\n" " float p1y = floor(tys) * tlh - tlhh;\n" " float p3y = p1y + tlh;\n" -" float tshift1 = texture2D(uTexture2, vec2(0.0, p1y)).r;\n" -" float tshift3 = texture2D(uTexture2, vec2(0.0, p3y)).r;\n" +" float tshift1 = texture2D(tex2, vec2(0.0, p1y)).r;\n" +" float tshift3 = texture2D(tex2, vec2(0.0, p3y)).r;\n" " float shift1 = (1.0 - tshift1 * 2.0) * tlw;\n" " float shift3 = (1.0 - tshift3 * 2.0) * tlw;\n" " float txs1 = (texCoordVar.x + shift1 + tlhw) * imw;\n" @@ -54,10 +54,10 @@ static const char* fragmentShaderSource = " float p3x = floor(txs3) * tlw - tlhw;\n" " float p2x = p1x + tlw;\n" " float p4x = p3x + tlw;\n" -" float p1 = texture2D(uTexture1, vec2(p1x, p1y)).r;\n" -" float p2 = texture2D(uTexture1, vec2(p2x, p1y)).r;\n" -" float p3 = texture2D(uTexture1, vec2(p3x, p3y)).r;\n" -" float p4 = texture2D(uTexture1, vec2(p4x, p3y)).r;\n" +" float p1 = texture2D(tex1, vec2(p1x, p1y)).r;\n" +" float p2 = texture2D(tex1, vec2(p2x, p1y)).r;\n" +" float p3 = texture2D(tex1, vec2(p3x, p3y)).r;\n" +" float p4 = texture2D(tex1, vec2(p4x, p3y)).r;\n" " float p12 = mix(p1, p2, fract(txs1));\n" " float p34 = mix(p3, p4, fract(txs3));\n" " float p = mix(p12, p34, fract(tys));\n" @@ -67,11 +67,8 @@ static const char* fragmentShaderSource = TVScreenAnalog::TVScreenAnalog(QWidget *parent) : QGLWidget(parent) { - m_objCurrentRow = nullptr; m_isDataChanged = false; - m_time = 0.0f; - m_cols = 1; - m_rows = 1; + m_data = std::make_shared(5, 1); connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick())); m_objTimer.start(40); // capped at 25 FPS @@ -81,15 +78,21 @@ void TVScreenAnalog::cleanup() { m_shader = nullptr; m_imageTexture = nullptr; - m_objCurrentRow = nullptr; m_lineShiftsTexture = nullptr; } +std::shared_ptr TVScreenAnalog::getData() +{ + return m_data; +} + void TVScreenAnalog::resizeTVScreen(int intCols, int intRows) { qDebug("TVScreen::resizeTVScreen: cols: %d, rows: %d", intCols, intRows); - m_cols = intCols + 4; - m_rows = intRows; + + int colsAdj = intCols + 4; + if (m_data->getWidth() != colsAdj || m_data->getHeight() != intRows) + m_data = std::make_shared(colsAdj, intRows); } void TVScreenAnalog::resizeGL(int intWidth, int intHeight) @@ -99,7 +102,6 @@ void TVScreenAnalog::resizeGL(int intWidth, int intHeight) void TVScreenAnalog::initializeGL() { - m_objMutex.lock(); initializeOpenGLFunctions(); m_shader = std::make_shared(this); @@ -109,31 +111,30 @@ void TVScreenAnalog::initializeGL() m_vertexAttribIndex = m_shader->attributeLocation("vertex"); m_texCoordAttribIndex = m_shader->attributeLocation("texCoord"); - m_objTextureLoc1 = m_shader->uniformLocation("uTexture1"); - m_objTextureLoc2 = m_shader->uniformLocation("uTexture2"); - m_objImageWidthLoc = m_shader->uniformLocation("imw"); - m_objImageHeightLoc = m_shader->uniformLocation("imh"); - m_objTexelWidthLoc = m_shader->uniformLocation("tlw"); - m_objTexelHeightLoc = m_shader->uniformLocation("tlh"); - - - initializeTextures(); + m_textureLoc1 = m_shader->uniformLocation("tex1"); + m_textureLoc2 = m_shader->uniformLocation("tex2"); + m_imageWidthLoc = m_shader->uniformLocation("imw"); + m_imageHeightLoc = m_shader->uniformLocation("imh"); + m_texelWidthLoc = m_shader->uniformLocation("tlw"); + m_texelHeightLoc = m_shader->uniformLocation("tlh"); connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget - - m_objMutex.unlock(); } void TVScreenAnalog::initializeTextures() { - m_image = std::make_shared(m_cols, m_rows, QImage::Format_ARGB32); - m_lineShifts = std::make_shared(1, m_rows, QImage::Format_ARGB32); - m_image->fill(0); - m_lineShifts->fill(127); + m_imageTexture = std::make_shared(QOpenGLTexture::Target2D); + m_lineShiftsTexture = std::make_shared(QOpenGLTexture::Target2D); + m_imageTexture->setSize(m_data->getWidth(), m_data->getHeight()); + m_lineShiftsTexture->setSize(1, m_data->getHeight()); + m_imageTexture->setFormat(QOpenGLTexture::RGBA8_UNorm); + m_lineShiftsTexture->setFormat(QOpenGLTexture::RGBA8_UNorm); + m_imageTexture->setAutoMipMapGenerationEnabled(false); + m_lineShiftsTexture->setAutoMipMapGenerationEnabled(false); + m_imageTexture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + m_lineShiftsTexture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); - m_imageTexture = std::make_shared(*m_image, QOpenGLTexture::DontGenerateMipMaps); - m_lineShiftsTexture = std::make_shared(*m_lineShifts, QOpenGLTexture::DontGenerateMipMaps); m_imageTexture->setMinificationFilter(QOpenGLTexture::Nearest); m_imageTexture->setMagnificationFilter(QOpenGLTexture::Nearest); m_lineShiftsTexture->setMinificationFilter(QOpenGLTexture::Nearest); @@ -151,69 +152,45 @@ void TVScreenAnalog::renderImage() void TVScreenAnalog::tick() { - if (m_isDataChanged) { + if (m_isDataChanged) + { update(); } } -void TVScreenAnalog::selectRow(int intLine, float shift) -{ - if (!m_image || m_image->height() != m_rows) - return; - - if ((intLine < m_rows) && (intLine >= 0)) - { - m_objCurrentRow = (int*)m_image->scanLine(intLine); - m_lineShifts->setPixel(0, intLine, (1.0f + shift) * 127.5f); - } - else - { - m_objCurrentRow = nullptr; - } -} - -void TVScreenAnalog::setDataColor(int intCol, int objColor) -{ - if ((intCol < m_cols - 2) && - (intCol >= -2) && - (m_objCurrentRow != nullptr)) - { - m_objCurrentRow[intCol + 2] = objColor; - } -} - void TVScreenAnalog::paintGL() { - if (!m_objMutex.tryLock(2)) - return; - m_isDataChanged = false; - if (m_image->width() != m_cols || m_image->height() != m_rows) + if (!m_imageTexture || + m_imageTexture->width() != m_data->getWidth() || + m_imageTexture->height() != m_data->getHeight()) + { initializeTextures(); + } - float imageWidth = m_image->width(); - float imageHeight = m_image->height(); + float imageWidth = m_data->getWidth(); + float imageHeight = m_data->getHeight(); float texelWidth = 1.0f / imageWidth; float texelHeight = 1.0f / imageHeight; m_shader->bind(); - m_shader->setUniformValue(m_objTextureLoc1, 0); - m_shader->setUniformValue(m_objTextureLoc2, 1); - m_shader->setUniformValue(m_objImageWidthLoc, imageWidth); - m_shader->setUniformValue(m_objImageHeightLoc, imageHeight); - m_shader->setUniformValue(m_objTexelWidthLoc, texelWidth); - m_shader->setUniformValue(m_objTexelHeightLoc, texelHeight); + m_shader->setUniformValue(m_textureLoc1, 0); + m_shader->setUniformValue(m_textureLoc2, 1); + m_shader->setUniformValue(m_imageWidthLoc, imageWidth); + m_shader->setUniformValue(m_imageHeightLoc, imageHeight); + m_shader->setUniformValue(m_texelWidthLoc, texelWidth); + m_shader->setUniformValue(m_texelHeightLoc, texelHeight); glActiveTexture(GL_TEXTURE0); m_imageTexture->bind(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - m_image->width(), m_image->height(), GL_RGBA, GL_UNSIGNED_BYTE, m_image->constScanLine(0)); + m_data->getWidth(), m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getImageData()); glActiveTexture(GL_TEXTURE1); m_lineShiftsTexture->bind(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - 1, m_image->height(), GL_RGBA, GL_UNSIGNED_BYTE, m_lineShifts->constScanLine(0)); + 1, m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getLineShiftData()); float rectHalfWidth = 1.0f + 4 * texelWidth; GLfloat vertices[] = @@ -241,6 +218,4 @@ void TVScreenAnalog::paintGL() glDisableVertexAttribArray(m_texCoordAttribIndex); m_shader->release(); - - m_objMutex.unlock(); } \ No newline at end of file diff --git a/sdrgui/gui/tvscreenanalog.h b/sdrgui/gui/tvscreenanalog.h index 70a1cedb8..080eda2f8 100644 --- a/sdrgui/gui/tvscreenanalog.h +++ b/sdrgui/gui/tvscreenanalog.h @@ -26,6 +26,7 @@ #include "export.h" #include +#include #include #include @@ -35,43 +36,110 @@ #include #include +class TVScreenAnalogData +{ +public: + TVScreenAnalogData(int width, int height) + { + m_width = width; + m_height = height; + + m_imageData = new int[width * height]; + m_lineShiftData = new int[height]; + m_outOfBoundsLine = new int[width]; + m_currentLine = m_outOfBoundsLine; + + std::fill(m_imageData, m_imageData + width * height, 0); + std::fill(m_lineShiftData, m_lineShiftData + height, 127); + } + + ~TVScreenAnalogData() + { + delete[] m_imageData; + delete[] m_lineShiftData; + delete[] m_outOfBoundsLine; + } + + int getWidth() + { + return m_width; + } + + int getHeight() + { + return m_height; + } + + const int* getImageData() + { + return m_imageData; + } + + const int* getLineShiftData() + { + return m_lineShiftData; + } + + void selectRow(int line, float shift) + { + if ((line < m_height) && (line >= 0)) + { + m_currentLine = m_imageData + line * m_width; + m_lineShiftData[line] = (1.0f + shift) * 127.5f; + } + else + { + m_currentLine = m_outOfBoundsLine; + } + } + + void setSampleValue(int column, int value) + { + if ((column < m_width - 2) && (column >= -2)) + { + m_currentLine[column + 2] = value; + } + } + +private: + int m_width; + int m_height; + + int* m_imageData; + int* m_lineShiftData; + + int* m_currentLine; + int* m_outOfBoundsLine; +}; + class SDRGUI_API TVScreenAnalog : public QGLWidget, protected QOpenGLFunctions { Q_OBJECT QTimer m_objTimer; - QMutex m_objMutex; bool m_isDataChanged; - int m_objTextureLoc1; - int m_objTextureLoc2; - int m_objImageWidthLoc; - int m_objImageHeightLoc; - int m_objTexelWidthLoc; - int m_objTexelHeightLoc; + int m_textureLoc1; + int m_textureLoc2; + int m_imageWidthLoc; + int m_imageHeightLoc; + int m_texelWidthLoc; + int m_texelHeightLoc; int m_vertexAttribIndex; int m_texCoordAttribIndex; + + std::shared_ptr m_data; + std::shared_ptr m_shader; - - float m_time; - - int m_cols; - int m_rows; - - int* m_objCurrentRow; - - std::shared_ptr m_image; - std::shared_ptr m_lineShifts; std::shared_ptr m_imageTexture; std::shared_ptr m_lineShiftsTexture; public: TVScreenAnalog(QWidget *parent); + std::shared_ptr getData(); void resizeTVScreen(int intCols, int intRows); - void selectRow(int intLine, float shift); - void setDataColor(int intCol, int objColor); void renderImage(); private: From 2e90be0044c9ff8b5697fff9545fe06f0769cb81 Mon Sep 17 00:00:00 2001 From: Vort Date: Wed, 5 Aug 2020 19:36:07 +0300 Subject: [PATCH 3/3] Sample offset is adjusted every line --- plugins/channelrx/demodatv/atvdemodsink.cpp | 14 +-- plugins/channelrx/demodatv/atvdemodsink.h | 116 +++++++------------- sdrgui/gui/tvscreenanalog.cpp | 42 +++++-- 3 files changed, 78 insertions(+), 94 deletions(-) diff --git a/plugins/channelrx/demodatv/atvdemodsink.cpp b/plugins/channelrx/demodatv/atvdemodsink.cpp index 75c73cae8..72b9c7a2f 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.cpp +++ b/plugins/channelrx/demodatv/atvdemodsink.cpp @@ -43,14 +43,12 @@ ATVDemodSink::ATVDemodSink() : m_ampMin(-1.0f), m_ampMax(1.0f), m_ampDelta(2.0f), - m_colIndex(0), - m_sampleIndex(0), - m_sampleIndexDetected(0), - m_hSyncShiftSum(0.0f), - m_hSyncShiftCount(0), + m_sampleOffset(0), + m_sampleOffsetFrac(0.0f), + m_sampleOffsetDetected(0), + m_hSyncShift(0.0f), m_hSyncErrorCount(0), m_amSampleIndex(0), - m_rowIndex(0), m_lineIndex(0), m_ampAverage(4800), m_bfoPLL(200/1000000, 100/1000000, 0.01), @@ -490,8 +488,6 @@ void ATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque } m_fieldIndex = 0; - m_colIndex = 0; - m_rowIndex = 0; m_channelSampleRate = channelSampleRate; m_channelFrequencyOffset = channelFrequencyOffset; @@ -584,8 +580,6 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force) } m_fieldIndex = 0; - m_colIndex = 0; - m_rowIndex = 0; } if ((settings.m_topTimeFactor != m_settings.m_topTimeFactor) || force) { diff --git a/plugins/channelrx/demodatv/atvdemodsink.h b/plugins/channelrx/demodatv/atvdemodsink.h index 8c250cb31..40e48eb02 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.h +++ b/plugins/channelrx/demodatv/atvdemodsink.h @@ -150,15 +150,14 @@ private: float m_fltBufferI[6]; float m_fltBufferQ[6]; - int m_colIndex; - int m_sampleIndex; // assumed (averaged) sample offset from the start of horizontal sync pulse - int m_sampleIndexDetected; // detected sample offset from the start of horizontal sync pulse - int m_amSampleIndex; - int m_rowIndex; + int m_amSampleIndex; + + int m_sampleOffset; // assumed (averaged) sample offset from the start of horizontal sync pulse + float m_sampleOffsetFrac; // sample offset, fractional part + int m_sampleOffsetDetected; // detected sample offset from the start of horizontal sync pulse int m_lineIndex; - float m_hSyncShiftSum; - int m_hSyncShiftCount; + float m_hSyncShift; int m_hSyncErrorCount; float prevSample; @@ -200,67 +199,71 @@ private: inline void processSample(float& sample, int& sampleVideo) { // Filling pixel on the current line - reference index 0 at start of sync pulse - m_tvScreenData->setSampleValue(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo); + m_tvScreenData->setSampleValue(m_sampleOffset - m_numberSamplesPerHSync, sampleVideo); if (m_settings.m_hSync) { // Horizontal Synchro detection if ((prevSample >= m_settings.m_levelSynchroTop && sample < m_settings.m_levelSynchroTop) // horizontal synchro detected - && (m_sampleIndexDetected > m_samplesPerLine - m_numberSamplesPerHTopNom)) + && (m_sampleOffsetDetected > m_samplesPerLine - m_numberSamplesPerHTopNom)) { - double sampleIndexDetectedFrac = + float sampleOffsetDetectedFrac = (sample - m_settings.m_levelSynchroTop) / (prevSample - sample); - double hSyncShift = -m_sampleIndex - sampleIndexDetectedFrac; + float hSyncShift = -m_sampleOffset - m_sampleOffsetFrac - sampleOffsetDetectedFrac; if (hSyncShift > m_samplesPerLine / 2) - hSyncShift -= m_samplesPerLine; + hSyncShift -= m_samplesPerLine; else if (hSyncShift < -m_samplesPerLine / 2) - hSyncShift += m_samplesPerLine; + hSyncShift += m_samplesPerLine; if (fabs(hSyncShift) > m_numberSamplesPerHTopNom) { m_hSyncErrorCount++; - if (m_hSyncErrorCount >= 8) + if (m_hSyncErrorCount >= 4) { // Fast sync: shift is too large, needs to be fixed ASAP - m_sampleIndex = 0; - m_hSyncShiftSum = 0.0; - m_hSyncShiftCount = 0; + m_hSyncShift = hSyncShift; m_hSyncErrorCount = 0; } } else { - m_hSyncShiftSum += hSyncShift; - m_hSyncShiftCount++; + // Slow sync: slight adjustment is needed + m_hSyncShift = hSyncShift * 0.2f; m_hSyncErrorCount = 0; } - m_sampleIndexDetected = 0; + m_sampleOffsetDetected = 0; } else - m_sampleIndexDetected++; + m_sampleOffsetDetected++; } - else - { - m_hSyncShiftSum = 0.0f; - m_hSyncShiftCount = 0; - } - m_sampleIndex++; + m_sampleOffset++; if (m_settings.m_vSync) { - if (m_sampleIndex > m_fieldDetectStartPos && m_sampleIndex < m_fieldDetectEndPos) + if (m_sampleOffset > m_fieldDetectStartPos && m_sampleOffset < m_fieldDetectEndPos) m_fieldDetectSampleCount += sample < m_settings.m_levelSynchroTop; - if (m_sampleIndex > m_vSyncDetectStartPos && m_sampleIndex < m_vSyncDetectEndPos) + if (m_sampleOffset > m_vSyncDetectStartPos && m_sampleOffset < m_vSyncDetectEndPos) m_vSyncDetectSampleCount += sample < m_settings.m_levelSynchroTop; } // end of line - if (m_sampleIndex >= m_samplesPerLine) + if (m_sampleOffset >= m_samplesPerLine) { - m_sampleIndex = 0; + if (m_settings.m_hSync) + { + float sampleOffsetFloat = m_hSyncShift + m_sampleOffsetFrac; + m_sampleOffset = sampleOffsetFloat; + m_sampleOffsetFrac = sampleOffsetFloat - m_sampleOffset; + } + else + { + m_sampleOffset = 0; + } + m_hSyncShift = 0.0f; - if (m_settings.m_atvStd == ATVDemodSettings::ATVStdHSkip) { + m_lineIndex++; + if (m_settings.m_atvStd == ATVDemodSettings::ATVStdHSkip) { processEOLHSkip(); } else { processEOLClassic(); @@ -273,24 +276,9 @@ private: // Standard vertical sync inline void processEOLClassic() { - m_lineIndex++; - if (m_lineIndex == m_numberOfVSyncLines + 3 && m_fieldIndex == 0) { - float shiftSamples = 0.0f; - - // Slow sync: slight adjustment is needed - if (m_hSyncShiftCount != 0 && m_settings.m_hSync) - { - shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; - m_sampleIndex = shiftSamples; - m_hSyncShiftSum = 0.0f; - m_hSyncShiftCount = 0; - m_hSyncErrorCount = 0; - } m_registeredTVScreen->renderImage(); - //m_registeredTVScreen->renderImage(0, - // shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } if (m_vSyncDetectSampleCount > m_vSyncDetectThreshold && @@ -323,47 +311,21 @@ private: if (m_interleaved) rowIndex = rowIndex * 2 - m_fieldIndex; - // TODO: CHANGE - float shiftSamples = 0.0f; - if (m_hSyncShiftCount != 0) - shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; - m_tvScreenData->selectRow(rowIndex, - shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + m_tvScreenData->selectRow(rowIndex, m_sampleOffsetFrac); } // Vertical sync is obtained by skipping horizontal sync on the line that triggers vertical sync (new frame) inline void processEOLHSkip() { - m_lineIndex++; - m_rowIndex++; - - if ((m_sampleIndexDetected > (3*m_samplesPerLine) / 2) // Vertical sync is first horizontal sync after skip (count at least 1.5 line length) + if ((m_sampleOffsetDetected > (3 * m_samplesPerLine) / 2) // Vertical sync is first horizontal sync after skip (count at least 1.5 line length) || (!m_settings.m_vSync && (m_lineIndex >= m_settings.m_nbLines))) // Vsync ignored and reached nominal number of lines per frame { - float shiftSamples = 0.0f; - - // Slow sync: slight adjustment is needed - if (m_hSyncShiftCount != 0 && m_settings.m_hSync) - { - shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; - m_sampleIndex = shiftSamples; - m_hSyncShiftSum = 0.0f; - m_hSyncShiftCount = 0; - m_hSyncErrorCount = 0; - } m_registeredTVScreen->renderImage(); - //m_registeredTVScreen->renderImage(0, - // shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); - m_lineIndex = 0; - m_rowIndex = 0; + m_lineIndex = 0; } - // TODO: CHANGE - float shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; - m_tvScreenData->setSampleValue(m_rowIndex, - shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + m_tvScreenData->selectRow(m_lineIndex, m_sampleOffsetFrac); } }; - #endif // INCLUDE_ATVDEMODSINK_H \ No newline at end of file diff --git a/sdrgui/gui/tvscreenanalog.cpp b/sdrgui/gui/tvscreenanalog.cpp index d61300a9f..5b6766515 100644 --- a/sdrgui/gui/tvscreenanalog.cpp +++ b/sdrgui/gui/tvscreenanalog.cpp @@ -104,10 +104,34 @@ void TVScreenAnalog::initializeGL() { initializeOpenGLFunctions(); + connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, + this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget + m_shader = std::make_shared(this); - m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); - m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); - m_shader->link(); + if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) + { + qWarning() + << "TVScreenAnalog::initializeGL: error in vertex shader:" + << m_shader->log(); + m_shader = nullptr; + return; + } + if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) + { + qWarning() + << "TVScreenAnalog::initializeGL: error in fragment shader:" + << m_shader->log(); + m_shader = nullptr; + return; + } + if (!m_shader->link()) + { + qWarning() + << "TVScreenAnalog::initializeGL: error linking shader:" + << m_shader->log(); + m_shader = nullptr; + return; + } m_vertexAttribIndex = m_shader->attributeLocation("vertex"); m_texCoordAttribIndex = m_shader->attributeLocation("texCoord"); @@ -117,9 +141,6 @@ void TVScreenAnalog::initializeGL() m_imageHeightLoc = m_shader->uniformLocation("imh"); m_texelWidthLoc = m_shader->uniformLocation("tlw"); m_texelHeightLoc = m_shader->uniformLocation("tlh"); - - connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, - this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget } void TVScreenAnalog::initializeTextures() @@ -162,6 +183,13 @@ void TVScreenAnalog::paintGL() { m_isDataChanged = false; + if (!m_shader) + { + glClearColor(0.2f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + return; + } + if (!m_imageTexture || m_imageTexture->width() != m_data->getWidth() || m_imageTexture->height() != m_data->getHeight()) @@ -192,7 +220,7 @@ void TVScreenAnalog::paintGL() glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getLineShiftData()); - float rectHalfWidth = 1.0f + 4 * texelWidth; + float rectHalfWidth = 1.0f + 4.0f / (imageWidth - 4.0f); GLfloat vertices[] = { -rectHalfWidth, -1.0f,