Changeset View
Changeset View
Standalone View
Standalone View
kstars/fitsviewer/fitsview.cpp
Show All 13 Lines | |||||
14 | #include "fitsdata.h" | 14 | #include "fitsdata.h" | ||
15 | #include "fitslabel.h" | 15 | #include "fitslabel.h" | ||
16 | #include "kspopupmenu.h" | 16 | #include "kspopupmenu.h" | ||
17 | #include "kstarsdata.h" | 17 | #include "kstarsdata.h" | ||
18 | #include "ksutils.h" | 18 | #include "ksutils.h" | ||
19 | #include "Options.h" | 19 | #include "Options.h" | ||
20 | #include "skymap.h" | 20 | #include "skymap.h" | ||
21 | #include "fits_debug.h" | 21 | #include "fits_debug.h" | ||
22 | #include "stretch.h" | ||||
22 | 23 | | |||
23 | #ifdef HAVE_INDI | 24 | #ifdef HAVE_INDI | ||
24 | #include "basedevice.h" | 25 | #include "basedevice.h" | ||
25 | #include "indi/indilistener.h" | 26 | #include "indi/indilistener.h" | ||
26 | #endif | 27 | #endif | ||
27 | 28 | | |||
28 | #include <KActionCollection> | 29 | #include <KActionCollection> | ||
29 | 30 | | |||
30 | #include <QtConcurrent> | 31 | #include <QtConcurrent> | ||
31 | #include <QScrollBar> | 32 | #include <QScrollBar> | ||
32 | #include <QToolBar> | 33 | #include <QToolBar> | ||
33 | #include <QGraphicsOpacityEffect> | 34 | #include <QGraphicsOpacityEffect> | ||
34 | #include <QApplication> | 35 | #include <QApplication> | ||
35 | #include <QGestureEvent> | 36 | #include <QGestureEvent> | ||
36 | 37 | | |||
37 | #define BASE_OFFSET 50 | 38 | #define BASE_OFFSET 50 | ||
38 | #define ZOOM_DEFAULT 100.0 | 39 | #define ZOOM_DEFAULT 100.0 | ||
39 | #define ZOOM_MIN 10 | 40 | #define ZOOM_MIN 10 | ||
40 | #define ZOOM_MAX 400 | 41 | #define ZOOM_MAX 400 | ||
41 | #define ZOOM_LOW_INCR 10 | 42 | #define ZOOM_LOW_INCR 10 | ||
42 | #define ZOOM_HIGH_INCR 50 | 43 | #define ZOOM_HIGH_INCR 50 | ||
43 | 44 | | |||
44 | FITSView::FITSView(QWidget * parent, FITSMode fitsMode, FITSScale filterType) : QScrollArea(parent), zoomFactor(1.2) | 45 | FITSView::FITSView(QWidget * parent, FITSMode fitsMode, FITSScale filterType) : QScrollArea(parent), zoomFactor(1.2) | ||
45 | { | 46 | { | ||
47 | stretchImage = Options::autoStretch(); | ||||
48 | | ||||
46 | grabGesture(Qt::PinchGesture); | 49 | grabGesture(Qt::PinchGesture); | ||
47 | 50 | | |||
48 | image_frame.reset(new FITSLabel(this)); | 51 | image_frame.reset(new FITSLabel(this)); | ||
49 | filter = filterType; | 52 | filter = filterType; | ||
50 | mode = fitsMode; | 53 | mode = fitsMode; | ||
51 | 54 | | |||
52 | setBackgroundRole(QPalette::Dark); | 55 | setBackgroundRole(QPalette::Dark); | ||
53 | 56 | | |||
▲ Show 20 Lines • Show All 326 Lines • ▼ Show 20 Line(s) | |||||
380 | } | 383 | } | ||
381 | 384 | | |||
382 | template <typename T> | 385 | template <typename T> | ||
383 | bool FITSView::rescale(FITSZoom type) | 386 | bool FITSView::rescale(FITSZoom type) | ||
384 | { | 387 | { | ||
385 | if (rawImage.isNull()) | 388 | if (rawImage.isNull()) | ||
386 | return false; | 389 | return false; | ||
387 | 390 | | |||
388 | uint8_t * imageBuffer = imageData->getImageBuffer(); | | |||
389 | uint8_t * displayBuffer = nullptr; | | |||
390 | uint32_t size = imageData->width() * imageData->height(); | | |||
391 | | ||||
392 | QVector<double> min(3), max(3); | | |||
393 | | ||||
394 | if (Options::autoStretch()) | | |||
395 | { | | |||
396 | displayBuffer = new uint8_t[size * imageData->channels() * imageData->getBytesPerPixel()]; | | |||
397 | memcpy(displayBuffer, imageBuffer, size * imageData->channels() * imageData->getBytesPerPixel()); | | |||
398 | imageData->applyFilter(FITS_AUTO_STRETCH, displayBuffer, &min, &max); | | |||
399 | } | | |||
400 | else | | |||
401 | { | | |||
402 | displayBuffer = imageBuffer; | | |||
403 | for (int i = 0; i < 3; i++) | | |||
404 | { | | |||
405 | min[i] = imageData->getMin(i); | | |||
406 | max[i] = imageData->getMax(i); | | |||
407 | } | | |||
408 | } | | |||
409 | | ||||
410 | scaledImage = QImage(); | | |||
411 | | ||||
412 | auto * buffer = reinterpret_cast<T *>(displayBuffer); | | |||
413 | | ||||
414 | if (min[0] == max[0]) | | |||
415 | { | | |||
416 | rawImage.fill(Qt::white); | | |||
417 | emit newStatus(i18n("Image is saturated."), FITS_MESSAGE); | | |||
418 | } | | |||
419 | else | | |||
420 | { | | |||
421 | if (image_height != imageData->height() || image_width != imageData->width()) | 391 | if (image_height != imageData->height() || image_width != imageData->width()) | ||
422 | { | 392 | { | ||
423 | image_width = imageData->width(); | 393 | image_width = imageData->width(); | ||
424 | image_height = imageData->height(); | 394 | image_height = imageData->height(); | ||
425 | 395 | | |||
426 | initDisplayImage(); | 396 | initDisplayImage(); | ||
427 | 397 | | |||
428 | if (isVisible()) | 398 | if (isVisible()) | ||
429 | emit newStatus(QString("%1x%2").arg(image_width).arg(image_height), FITS_RESOLUTION); | 399 | emit newStatus(QString("%1x%2").arg(image_width).arg(image_height), FITS_RESOLUTION); | ||
430 | } | 400 | } | ||
431 | 401 | | |||
432 | image_frame->setScaledContents(true); | 402 | image_frame->setScaledContents(true); | ||
433 | currentWidth = rawImage.width(); | 403 | currentWidth = rawImage.width(); | ||
434 | currentHeight = rawImage.height(); | 404 | currentHeight = rawImage.height(); | ||
435 | 405 | | |||
436 | if (imageData->channels() == 1) | 406 | Stretch<T> stretch(reinterpret_cast<T*>(imageData->getImageBuffer()), | ||
437 | { | 407 | static_cast<int>(imageData->width()), | ||
438 | double range = max[0] - min[0]; | 408 | static_cast<int>(imageData->height()), | ||
439 | double bscale = 255. / range; | 409 | imageData->channels()); | ||
440 | double bzero = (-min[0]) * (255. / range); | | |||
441 | | ||||
442 | QVector<QFuture<void>> futures; | | |||
443 | | ||||
444 | /* Fill in pixel values using indexed map, linear scale */ | | |||
445 | for (uint32_t j = 0; j < image_height; j++) | | |||
446 | { | | |||
447 | futures.append(QtConcurrent::run([ = ]() | | |||
448 | { | | |||
449 | T * runningBuffer = buffer + j * image_width; | | |||
450 | uint8_t * scanLine = rawImage.scanLine(j); | | |||
451 | for (uint32_t i = 0; i < image_width; i++) | | |||
452 | { | | |||
453 | //scanLine[i] = qBound(0, static_cast<uint8_t>(runningBuffer[i] * bscale + bzero), 255); | | |||
454 | scanLine[i] = qBound(0.0, runningBuffer[i] * bscale + bzero, 255.0); | | |||
455 | } | | |||
456 | })); | | |||
457 | } | | |||
458 | | ||||
459 | for(QFuture<void> future : futures) | | |||
460 | future.waitForFinished(); | | |||
461 | } | | |||
462 | else | | |||
463 | { | | |||
464 | QVector<QFuture<void>> futures; | | |||
465 | double bscale[3], bzero[3]; | | |||
466 | for (int i = 0; i < 3; i++) | | |||
467 | { | | |||
468 | bscale[i] = 255. / (max[i] - min[i]); | | |||
469 | bzero[i] = (-min[i]) * (255. / (max[i] - min[i])); | | |||
470 | } | | |||
471 | | ||||
472 | /* Fill in pixel values using indexed map, linear scale */ | | |||
473 | for (uint32_t j = 0; j < image_height; j++) | | |||
474 | { | | |||
475 | futures.append(QtConcurrent::run([ = ]() | | |||
476 | { | | |||
477 | auto * scanLine = reinterpret_cast<QRgb *>((rawImage.scanLine(j))); | | |||
478 | T * runningBufferR = buffer + j * image_width; | | |||
479 | T * runningBufferG = buffer + j * image_width + size; | | |||
480 | T * runningBufferB = buffer + j * image_width + size * 2; | | |||
481 | | ||||
482 | for (uint32_t i = 0; i < image_width; i++) | | |||
483 | { | | |||
484 | scanLine[i] = qRgb(runningBufferR[i] * bscale[0] + bzero[0], | | |||
485 | runningBufferG[i] * bscale[1] + bzero[1], | | |||
486 | runningBufferB[i] * bscale[2] + bzero[2]); | | |||
487 | } | | |||
488 | })); | | |||
489 | } | | |||
490 | 410 | | |||
491 | for(QFuture<void> future : futures) | 411 | if (stretchImage) | ||
492 | future.waitForFinished(); | 412 | stretch.setParams(stretch.computeParams()); | ||
493 | } | | |||
494 | 413 | | |||
495 | } | 414 | stretch.run(&rawImage); | ||
496 | 415 | | |||
497 | // Clear memory if it was allocated. | 416 | scaledImage = QImage(); | ||
498 | if (displayBuffer != imageBuffer) | | |||
499 | delete [] displayBuffer; | | |||
500 | 417 | | |||
501 | switch (type) | 418 | switch (type) | ||
502 | { | 419 | { | ||
503 | case ZOOM_FIT_WINDOW: | 420 | case ZOOM_FIT_WINDOW: | ||
504 | if ((rawImage.width() > width() || rawImage.height() > height())) | 421 | if ((rawImage.width() > width() || rawImage.height() > height())) | ||
505 | { | 422 | { | ||
506 | double w = baseSize().width() - BASE_OFFSET; | 423 | double w = baseSize().width() - BASE_OFFSET; | ||
507 | double h = baseSize().height() - BASE_OFFSET; | 424 | double h = baseSize().height() - BASE_OFFSET; | ||
▲ Show 20 Lines • Show All 765 Lines • ▼ Show 20 Line(s) | |||||
1273 | 1190 | | |||
1274 | void FITSView::toggleStars() | 1191 | void FITSView::toggleStars() | ||
1275 | { | 1192 | { | ||
1276 | toggleStars(!markStars); | 1193 | toggleStars(!markStars); | ||
1277 | if (image_frame != nullptr) | 1194 | if (image_frame != nullptr) | ||
1278 | updateFrame(); | 1195 | updateFrame(); | ||
1279 | } | 1196 | } | ||
1280 | 1197 | | |||
1198 | void FITSView::toggleStretch() | ||||
1199 | { | ||||
1200 | stretchImage = !stretchImage; | ||||
1201 | if (image_frame != nullptr && rescale(ZOOM_KEEP_LEVEL)) | ||||
1202 | updateFrame(); | ||||
1203 | } | ||||
1204 | | ||||
1281 | void FITSView::toggleStarProfile() | 1205 | void FITSView::toggleStarProfile() | ||
1282 | { | 1206 | { | ||
1283 | #ifdef HAVE_DATAVISUALIZATION | 1207 | #ifdef HAVE_DATAVISUALIZATION | ||
1284 | showStarProfile = !showStarProfile; | 1208 | showStarProfile = !showStarProfile; | ||
1285 | if(showStarProfile && trackingBoxEnabled) | 1209 | if(showStarProfile && trackingBoxEnabled) | ||
1286 | viewStarProfile(); | 1210 | viewStarProfile(); | ||
1287 | if(toggleProfileAction) | 1211 | if(toggleProfileAction) | ||
1288 | toggleProfileAction->setChecked(showStarProfile); | 1212 | toggleProfileAction->setChecked(showStarProfile); | ||
▲ Show 20 Lines • Show All 341 Lines • ▼ Show 20 Line(s) | 1553 | floatingToolBar->addAction(QIcon::fromTheme("zoom-out"), | |||
1630 | i18n("Zoom Out"), this, SLOT(ZoomOut())); | 1554 | i18n("Zoom Out"), this, SLOT(ZoomOut())); | ||
1631 | 1555 | | |||
1632 | floatingToolBar->addAction(QIcon::fromTheme("zoom-fit-best"), | 1556 | floatingToolBar->addAction(QIcon::fromTheme("zoom-fit-best"), | ||
1633 | i18n("Default Zoom"), this, SLOT(ZoomDefault())); | 1557 | i18n("Default Zoom"), this, SLOT(ZoomDefault())); | ||
1634 | 1558 | | |||
1635 | floatingToolBar->addAction(QIcon::fromTheme("zoom-fit-width"), | 1559 | floatingToolBar->addAction(QIcon::fromTheme("zoom-fit-width"), | ||
1636 | i18n("Zoom to Fit"), this, SLOT(ZoomToFit())); | 1560 | i18n("Zoom to Fit"), this, SLOT(ZoomToFit())); | ||
1637 | 1561 | | |||
1562 | floatingToolBar->addAction(QIcon::fromTheme("transform-move"), | ||||
1563 | i18n("Toggle Stretch"), this, SLOT(toggleStretch())); | ||||
1564 | | ||||
1638 | floatingToolBar->addSeparator(); | 1565 | floatingToolBar->addSeparator(); | ||
1639 | 1566 | | |||
1640 | action = floatingToolBar->addAction(QIcon::fromTheme("crosshairs"), | 1567 | action = floatingToolBar->addAction(QIcon::fromTheme("crosshairs"), | ||
1641 | i18n("Show Cross Hairs"), this, SLOT(toggleCrosshair())); | 1568 | i18n("Show Cross Hairs"), this, SLOT(toggleCrosshair())); | ||
1642 | action->setCheckable(true); | 1569 | action->setCheckable(true); | ||
1643 | 1570 | | |||
1644 | action = floatingToolBar->addAction(QIcon::fromTheme("map-flat"), | 1571 | action = floatingToolBar->addAction(QIcon::fromTheme("map-flat"), | ||
1645 | i18n("Show Pixel Gridlines"), this, SLOT(togglePixelGrid())); | 1572 | i18n("Show Pixel Gridlines"), this, SLOT(togglePixelGrid())); | ||
▲ Show 20 Lines • Show All 126 Lines • Show Last 20 Lines |