@@ -39,6 +39,8 @@ OscilloscopeDialog::OscilloscopeDialog(QWidget *parent) :
3939 lastScopeX = 0 ;
4040 lastScopeY = 0 ;
4141 scopeWidth = 0 ;
42+ scopeHeight = 512 ; // Initial default height
43+ devicePixelRatio = 1.0 ;
4244
4345 // Configure the GUI
4446 ui->xCoordSpinBox ->setMinimum (0 );
@@ -71,14 +73,25 @@ void OscilloscopeDialog::showTraceImage(TbcSource::ScanLineData scanLineData, qi
7173 lastScopeX = xCoord;
7274 lastScopeY = yCoord;
7375
76+ // Calculate optimal scope height based on widget size and DPI
77+ scopeHeight = getOptimalScopeHeight ();
78+ devicePixelRatio = ui->scopeLabel ->devicePixelRatioF ();
79+
80+ qDebug () << " OscilloscopeDialog::showTraceImage(): Widget height =" << ui->scopeLabel ->height ()
81+ << " Calculated scope height =" << scopeHeight << " DPI ratio =" << devicePixelRatio;
82+
7483 // Get the raw field data for the selected line
7584 QImage traceImage = getFieldLineTraceImage (scanLineData, lastScopeX);
7685
7786 // Add the QImage to the QLabel in the dialogue
7887 ui->scopeLabel ->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
7988 ui->scopeLabel ->setAlignment (Qt::AlignCenter);
8089 ui->scopeLabel ->setScaledContents (true );
81- ui->scopeLabel ->setPixmap (QPixmap::fromImage (traceImage));
90+
91+ // Set pixmap with proper high-DPI support
92+ QPixmap pixmap = QPixmap::fromImage (traceImage);
93+ pixmap.setDevicePixelRatio (devicePixelRatio);
94+ ui->scopeLabel ->setPixmap (pixmap);
8295
8396 // Update the X coordinate spinbox
8497 ui->xCoordSpinBox ->setMaximum (maximumX - 1 );
@@ -110,14 +123,24 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
110123 bool showC = ui->CcheckBox ->isChecked ();
111124 bool showDropouts = ui->dropoutsCheckBox ->isChecked ();
112125
113- // These are fixed, but may be changed to options later
114- qint32 scopeHeight = 2048 ;
126+ // Use dynamic scope height - ensure it's been calculated
127+ qint32 imageHeight = scopeHeight;
128+ if (imageHeight <= 0 ) {
129+ qWarning () << " OscilloscopeDialog::getFieldLineTraceImage(): scopeHeight not set, using default 512" ;
130+ imageHeight = 512 ;
131+ }
132+
133+ // Clamp to reasonable bounds for image generation
134+ if (imageHeight < 256 ) imageHeight = 256 ;
135+ if (imageHeight > 4096 ) imageHeight = 4096 ;
136+
115137 scopeWidth = scanLineData.fieldWidth ;
138+ qint32 scopeScale = 65536 / imageHeight;
116139
117- qint32 scopeScale = 65536 / scopeHeight ;
140+ qDebug () << " OscilloscopeDialog::getFieldLineTraceImage(): Using imageHeight = " << imageHeight << " scopeScale = " << scopeScale ;
118141
119142 // Define image with width, height and format
120- QImage scopeImage (scopeWidth, scopeHeight , QImage::Format_RGB888);
143+ QImage scopeImage (scopeWidth, imageHeight , QImage::Format_RGB888);
121144 QPainter scopePainter;
122145
123146 // Ensure we have valid data
@@ -138,11 +161,11 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
138161 // Add the black and white levels
139162 // Note: For PAL this should be black at 64 and white at 211
140163
141- // Scale to 512 pixel height
142- qint32 blackIre = scopeHeight - (scanLineData.blackIre / scopeScale);
143- qint32 whiteIre = scopeHeight - (scanLineData.whiteIre / scopeScale);
164+ // Scale to dynamic pixel height
165+ qint32 blackIre = imageHeight - (scanLineData.blackIre / scopeScale);
166+ qint32 whiteIre = imageHeight - (scanLineData.whiteIre / scopeScale);
144167 qint32 midPointIre = scanLineData.blackIre + ((scanLineData.whiteIre - scanLineData.blackIre ) / 2 );
145- midPointIre = scopeHeight - (midPointIre / scopeScale);
168+ midPointIre = imageHeight - (midPointIre / scopeScale);
146169
147170 scopePainter.setPen (Qt::white);
148171 scopePainter.drawLine (0 , blackIre, scanLineData.fieldWidth , blackIre);
@@ -156,11 +179,11 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
156179
157180 // Draw the indicator lines
158181 scopePainter.setPen (Qt::blue);
159- scopePainter.drawLine (scanLineData.colourBurstStart , 0 , scanLineData.colourBurstStart , scopeHeight );
160- scopePainter.drawLine (scanLineData.colourBurstEnd , 0 , scanLineData.colourBurstEnd , scopeHeight );
182+ scopePainter.drawLine (scanLineData.colourBurstStart , 0 , scanLineData.colourBurstStart , imageHeight );
183+ scopePainter.drawLine (scanLineData.colourBurstEnd , 0 , scanLineData.colourBurstEnd , imageHeight );
161184 scopePainter.setPen (Qt::cyan);
162- scopePainter.drawLine (scanLineData.activeVideoStart , 0 , scanLineData.activeVideoStart , scopeHeight );
163- scopePainter.drawLine (scanLineData.activeVideoEnd , 0 , scanLineData.activeVideoEnd , scopeHeight );
185+ scopePainter.drawLine (scanLineData.activeVideoStart , 0 , scanLineData.activeVideoStart , imageHeight );
186+ scopePainter.drawLine (scanLineData.activeVideoEnd , 0 , scanLineData.activeVideoEnd , imageHeight );
164187
165188 // Get the signal data
166189 const QVector<qint32> &signalDataYC = scanLineData.composite ; // Composite signal
@@ -179,8 +202,8 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
179202 qint32 lastSignalLevelYC = 0 ;
180203 for (qint32 xPosition = 0 ; xPosition < scanLineData.fieldWidth ; xPosition++) {
181204 if (showYC) {
182- // Scale (to 0-512 ) and invert
183- qint32 signalLevelYC = scopeHeight - (signalDataYC[xPosition] / scopeScale);
205+ // Scale (to dynamic height ) and invert
206+ qint32 signalLevelYC = imageHeight - (signalDataYC[xPosition] / scopeScale);
184207
185208 if (xPosition != 0 ) {
186209 // Non-active video area YC is yellow, active is white
@@ -204,8 +227,8 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
204227 qint32 lastSignalLevelC = 0 ;
205228 for (qint32 xPosition = scanLineData.activeVideoStart ; xPosition < scanLineData.activeVideoEnd ; xPosition++) {
206229 if (showC && scanLineData.isActiveLine ) {
207- // Scale (to 0-512 ) and invert
208- qint32 signalLevelC = (scopeHeight - (signalDataC[xPosition] / scopeScale)) - (scopeHeight - midPointIre);
230+ // Scale (to dynamic height ) and invert
231+ qint32 signalLevelC = (imageHeight - (signalDataC[xPosition] / scopeScale)) - (imageHeight - midPointIre);
209232
210233 if (xPosition != scanLineData.activeVideoStart ) {
211234 // Draw a line from the last Y signal to the current one (signal green, out of range in yellow)
@@ -219,8 +242,8 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
219242 }
220243
221244 if (showY && scanLineData.isActiveLine ) {
222- // Scale (to 0-512 ) and invert
223- qint32 signalLevelY = scopeHeight - (signalDataY[xPosition] / scopeScale);
245+ // Scale (to dynamic height ) and invert
246+ qint32 signalLevelY = imageHeight - (signalDataY[xPosition] / scopeScale);
224247
225248 if (xPosition != scanLineData.activeVideoStart ) {
226249 // Draw a line from the last Y signal to the current one (signal white, out of range in red)
@@ -236,13 +259,36 @@ QImage OscilloscopeDialog::getFieldLineTraceImage(TbcSource::ScanLineData scanLi
236259
237260 // Draw the picture dot position line
238261 scopePainter.setPen (QColor (0 , 255 , 0 , 127 ));
239- scopePainter.drawLine (pictureDot, 0 , pictureDot, scopeHeight );
262+ scopePainter.drawLine (pictureDot, 0 , pictureDot, imageHeight );
240263
241264 // Return the QImage
242265 scopePainter.end ();
243266 return scopeImage;
244267}
245268
269+ // Calculate optimal scope height based on widget size and DPI
270+ qint32 OscilloscopeDialog::getOptimalScopeHeight ()
271+ {
272+ // Get the widget's physical size
273+ qint32 widgetHeight = ui->scopeLabel ->height ();
274+
275+ qDebug () << " OscilloscopeDialog::getOptimalScopeHeight(): Widget height =" << widgetHeight;
276+
277+ // If widget hasn't been sized yet, use a reasonable default
278+ if (widgetHeight < 50 ) {
279+ qDebug () << " OscilloscopeDialog::getOptimalScopeHeight(): Using default height 512 (widget too small)" ;
280+ return 512 ;
281+ }
282+
283+ // Clamp to reasonable bounds (between 256 and 4096 pixels)
284+ if (widgetHeight > 2048 ) {
285+ return 4096 ; // Maximum quality to avoid memory waste
286+ } else {
287+ // Scale height proportionally: each display pixel gets ~2 image pixels for quality
288+ return qMax (512 , widgetHeight * 2 );
289+ }
290+ }
291+
246292// GUI signal handlers ------------------------------------------------------------------------------------------------
247293
248294void OscilloscopeDialog::on_previousPushButton_clicked ()
@@ -328,17 +374,20 @@ void OscilloscopeDialog::mouseMoveEvent(QMouseEvent *event)
328374// Handle a click on the scope with shift held down, to select a level
329375void OscilloscopeDialog::mouseLevelSelect (qint32 oY)
330376{
331- double unscaledYR = (65536.0 / static_cast <double >(ui->scopeLabel ->height ())) * static_cast <double >(oY);
377+ // Convert widget pixel coordinate to image pixel coordinate, accounting for DPI
378+ double scaleFactor = static_cast <double >(scopeHeight) / static_cast <double >(ui->scopeLabel ->height ());
379+ double unscaledYR = scaleFactor * static_cast <double >(oY);
332380
333- qint32 level = qBound (0 , 65535 - static_cast <qint32>(unscaledYR), 65535 );
381+ qint32 level = qBound (0 , 65535 - static_cast <qint32>(unscaledYR * ( 65536.0 / static_cast < double >(scopeHeight)) ), 65535 );
334382 emit scopeLevelSelect (level);
335383}
336384
337385// Handle a click on the scope without shift held down, to select a sample
338386void OscilloscopeDialog::mousePictureDotSelect (qint32 oX)
339387{
340- double unscaledXR = (static_cast <double >(scopeWidth) /
341- static_cast <double >(ui->scopeLabel ->width ())) * static_cast <double >(oX);
388+ // Convert widget pixel coordinate to image pixel coordinate, accounting for DPI
389+ double scaleFactor = static_cast <double >(scopeWidth) / static_cast <double >(ui->scopeLabel ->width ());
390+ double unscaledXR = scaleFactor * static_cast <double >(oX);
342391
343392 qint32 unscaledX = static_cast <qint32>(unscaledXR);
344393 if (unscaledX > scopeWidth - 1 ) unscaledX = scopeWidth - 1 ;
0 commit comments