1
2
3 #include "adjustlevelspopup.h"
4
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "cellselection.h"
8 #include "filmstripselection.h"
9 #include "menubarcommandids.h"
10
11 // TnzQt includes
12 #include "toonzqt/histogram.h"
13 #include "toonzqt/marksbar.h"
14 #include "toonzqt/menubarcommand.h"
15 #include "toonzqt/tselectionhandle.h"
16 #include "toonzqt/intfield.h"
17 #include "toonzqt/icongenerator.h"
18
19 // TnzLib includes
20 #include "toonz/txshcell.h"
21 #include "toonz/txshsimplelevel.h"
22 #include "toonz/tframehandle.h"
23 #include "toonz/txshlevelhandle.h"
24 #include "toonz/tcolumnhandle.h"
25 #include "toonz/txsheethandle.h"
26
27 // TnzCore includes
28 #include "trasterimage.h"
29 #include "trop.h"
30 #include "tundo.h"
31 #include "timagecache.h"
32
33 // Qt includes
34 #include <QPushButton>
35 #include <QSplitter>
36 #include <QScrollArea>
37 #include <QMainWindow>
38
39 //**************************************************************************
40 // Local namespace stuff
41 //**************************************************************************
42
43 namespace {
44
resetMarksBar(MarksBar * marksBar)45 void resetMarksBar(MarksBar *marksBar) {
46 QVector<int> &values = marksBar->values();
47 values[0] = 0;
48 values[1] = 255;
49 }
50
51 //--------------------------------------------------------------
52
53 // Get the exact range for specified value
getRange(const QVector<int> & values,int & min,int & max)54 void getRange(const QVector<int> &values, int &min, int &max) {
55 int size = values.size();
56
57 for (min = 0; min < size; ++min) {
58 if (values[min]) break;
59 }
60
61 for (max = size - 1; max >= 0; --max) {
62 if (values[max]) break;
63 }
64 }
65
66 //--------------------------------------------------------------
67
68 // Get the threshold-permissive range for values.
getRange(const QVector<int> & values,int threshold,int & min,int & max)69 void getRange(const QVector<int> &values, int threshold, int &min, int &max) {
70 int val, size = values.size(), count;
71
72 count = values[0]; // Always Consent 1 value cut
73 for (min = 1; min < size; ++min) {
74 val = values[min];
75 count += val;
76 if (val && count > threshold) // Then, stop before a positive value when
77 // thresh is exceeded
78 break;
79 }
80
81 count = values[size - 1];
82 for (max = size - 2; max >= 0; --max) {
83 val = values[max];
84 count += val;
85 if (val && count > threshold) break;
86 }
87 }
88
89 } // namespace
90
91 //**************************************************************************
92 // Adjust Levels Swatch
93 //**************************************************************************
94
95 class AdjustLevelsPopup::Swatch final : public PlaneViewer {
96 TRasterP m_ras;
97
98 public:
Swatch(QWidget * parent=0)99 Swatch(QWidget *parent = 0) : PlaneViewer(parent) {
100 setBgColor(TPixel32::White, TPixel32::White);
101 }
102
raster() const103 TRasterP raster() const { return m_ras; }
raster()104 TRasterP &raster() { return m_ras; }
105
paintGL()106 void paintGL() override {
107 drawBackground();
108
109 if (m_ras) {
110 glEnable(GL_BLEND);
111 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
112
113 // Note GL_ONE instead of GL_SRC_ALPHA: it's needed since the input
114 // image is supposedly premultiplied - and it works because the
115 // viewer's background is opaque.
116 // See tpixelutils.h's overPixT function for comparison.
117
118 pushGLWorldCoordinates();
119 draw(m_ras);
120 popGLCoordinates();
121
122 glDisable(GL_BLEND);
123 }
124 }
125 };
126
127 //**************************************************************************
128 // EditableMarksBar implementation
129 //**************************************************************************
130
EditableMarksBar(QWidget * parent)131 EditableMarksBar::EditableMarksBar(QWidget *parent) : QFrame(parent) {
132 QVBoxLayout *layout = new QVBoxLayout;
133 layout->setMargin(0);
134 layout->setSpacing(2);
135 setLayout(layout);
136
137 // Add MarksBar
138 m_marksBar = new MarksBar;
139 m_marksBar->setContentsMargins(5, 0, 6, 0);
140 layout->addWidget(m_marksBar);
141
142 // Customize it
143 m_marksBar->setRange(0, 256, 2);
144
145 QVector<int> &values = m_marksBar->values();
146 values.push_back(0);
147 values.push_back(256);
148
149 QVector<QColor> &colors = m_marksBar->colors();
150 colors.fill(Qt::black, 2);
151
152 // MarksBar dominates values change notifications
153 bool ret =
154 connect(m_marksBar, SIGNAL(marksUpdated()), this, SLOT(updateFields()));
155 ret = ret && connect(m_marksBar, SIGNAL(marksUpdated()), this,
156 SIGNAL(paramsChanged()));
157
158 // Add fields layout
159 QHBoxLayout *hLayout = new QHBoxLayout;
160 hLayout->setMargin(0);
161 hLayout->setContentsMargins(4, 0, 5, 0);
162 layout->addLayout(hLayout);
163
164 m_fields[0] = new DVGui::IntLineEdit;
165
166 hLayout->addWidget(m_fields[0]);
167 ret = ret && connect(m_fields[0], SIGNAL(editingFinished()), this,
168 SLOT(onFieldEdited()));
169
170 hLayout->addStretch(1);
171
172 m_fields[1] = new DVGui::IntLineEdit;
173 hLayout->addWidget(m_fields[1]);
174 ret = ret && connect(m_fields[1], SIGNAL(editingFinished()), this,
175 SLOT(onFieldEdited()));
176
177 updateFields();
178 }
179
180 //--------------------------------------------------------------
181
~EditableMarksBar()182 EditableMarksBar::~EditableMarksBar() {}
183
184 //--------------------------------------------------------------
185
getValues(int * values) const186 void EditableMarksBar::getValues(int *values) const {
187 const QVector<int> &marks = m_marksBar->values();
188
189 values[0] = marks[0];
190 values[1] = marks[1] - 1;
191 }
192
193 //--------------------------------------------------------------
194
updateFields()195 void EditableMarksBar::updateFields() {
196 const QVector<int> &values = m_marksBar->values();
197
198 m_fields[0]->setValue(values[0]);
199 m_fields[1]->setValue(values[1] - 1);
200
201 // No emission - as it's the marksbar that dominate signal emission
202 }
203
204 //--------------------------------------------------------------
205
onFieldEdited()206 void EditableMarksBar::onFieldEdited() {
207 QVector<int> &values = m_marksBar->values();
208
209 // Copy the values to the marksBar
210 values[0] = m_fields[0]->getValue();
211 values[1] = m_fields[1]->getValue() + 1;
212
213 m_marksBar->conformValues();
214
215 emit paramsChanged();
216 }
217
218 //**************************************************************************
219 // Adjust Levels Popup implementation
220 //**************************************************************************
221
AdjustLevelsPopup()222 AdjustLevelsPopup::AdjustLevelsPopup()
223 : DVGui::Dialog(TApp::instance()->getMainWindow(), true, false,
224 "AdjustLevels")
225 , m_thresholdD(0.005) // 0.5% of the image size
226 {
227 int i, j;
228
229 setWindowTitle(tr("Adjust Levels"));
230 setLabelWidth(0);
231 setModal(false);
232
233 setTopMargin(0);
234 setTopSpacing(0);
235
236 beginVLayout();
237
238 QSplitter *splitter = new QSplitter(Qt::Vertical);
239 splitter->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
240 QSizePolicy::MinimumExpanding));
241 addWidget(splitter);
242
243 endVLayout();
244
245 //------------------------- Top Layout --------------------------
246
247 QScrollArea *scrollArea = new QScrollArea(splitter);
248 scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
249 scrollArea->setWidgetResizable(true);
250 scrollArea->setMinimumWidth(450);
251 splitter->addWidget(scrollArea);
252 splitter->setStretchFactor(0, 1);
253
254 QFrame *topWidget = new QFrame(scrollArea);
255 scrollArea->setWidget(topWidget);
256
257 QVBoxLayout *topVLayout =
258 new QVBoxLayout(topWidget); // Needed to justify at top
259 topWidget->setLayout(topVLayout);
260
261 QHBoxLayout *topLayout = new QHBoxLayout(topWidget);
262 topVLayout->addLayout(topLayout);
263 topVLayout->addStretch(1);
264
265 //------------------------- Histogram ---------------------------
266
267 m_histogram = new Histogram(topWidget);
268 topLayout->addWidget(m_histogram);
269
270 //------------------------- Mark Bars ---------------------------
271
272 QVBoxLayout *histogramViewLayout;
273
274 for (i = 0; i < 5; ++i) {
275 HistogramView *view = m_histogram->getHistograms()->getHistogramView(i);
276 histogramViewLayout = static_cast<QVBoxLayout *>(view->layout());
277
278 // Don't draw channel numbers
279 view->channelBar()->setDrawNumbers(false);
280
281 for (j = 0; j < 2; ++j) {
282 EditableMarksBar *editableMarksBar = m_marksBar[j + (i << 1)] =
283 new EditableMarksBar;
284 MarksBar *marksBar = editableMarksBar->marksBar();
285
286 // Set margins up to cover the histogram
287 editableMarksBar->layout()->setContentsMargins(6, 0, 5, 0);
288 connect(editableMarksBar, SIGNAL(paramsChanged()), this,
289 SLOT(onParamsChanged()));
290
291 histogramViewLayout->insertWidget(1 + (j << 1), editableMarksBar);
292 }
293 }
294
295 //------------------------- View Widget -------------------------
296
297 // NOTE: It's IMPORTANT that parent widget is supplied. It's somewhat
298 // used by QSplitter to decide the initial widget sizes...
299
300 m_viewer = new Swatch(splitter);
301 m_viewer->setMinimumHeight(150);
302 m_viewer->setFocusPolicy(Qt::WheelFocus);
303 splitter->addWidget(m_viewer);
304
305 //--------------------------- Buttons ---------------------------
306
307 QVBoxLayout *buttonsLayout = new QVBoxLayout(topWidget);
308 topLayout->addLayout(buttonsLayout);
309
310 buttonsLayout->addSpacing(50);
311
312 QPushButton *clampRange = new QPushButton(tr("Clamp"), topWidget);
313 clampRange->setMinimumSize(65, 25);
314 buttonsLayout->addWidget(clampRange);
315 connect(clampRange, SIGNAL(clicked(bool)), this, SLOT(clampRange()));
316
317 QPushButton *autoAdjust = new QPushButton(tr("Auto"), topWidget);
318 autoAdjust->setMinimumSize(65, 25);
319 buttonsLayout->addWidget(autoAdjust);
320 connect(autoAdjust, SIGNAL(clicked(bool)), this, SLOT(autoAdjust()));
321
322 QPushButton *resetBtn = new QPushButton(tr("Reset"), topWidget);
323 resetBtn->setMinimumSize(65, 25);
324 buttonsLayout->addWidget(resetBtn);
325 connect(resetBtn, SIGNAL(clicked(bool)), this, SLOT(reset()));
326
327 buttonsLayout->addStretch(1);
328
329 m_okBtn = new QPushButton(tr("Apply"));
330 addButtonBarWidget(m_okBtn);
331
332 connect(m_okBtn, SIGNAL(clicked()), this, SLOT(apply()));
333
334 // Finally, acquire current selection
335 acquireRaster();
336
337 m_viewer->resize(0, 350);
338 resize(600, 700);
339 }
340
341 //--------------------------------------------------------------
342
showEvent(QShowEvent * se)343 void AdjustLevelsPopup::showEvent(QShowEvent *se) {
344 TSelectionHandle *selectionHandle = TApp::instance()->getCurrentSelection();
345 connect(selectionHandle, SIGNAL(selectionChanged(TSelection *)), this,
346 SLOT(onSelectionChanged()));
347
348 acquireRaster();
349 }
350
351 //--------------------------------------------------------------
352
hideEvent(QHideEvent * he)353 void AdjustLevelsPopup::hideEvent(QHideEvent *he) {
354 Dialog::hideEvent(he);
355
356 TSelectionHandle *selectionHandle = TApp::instance()->getCurrentSelection();
357 disconnect(selectionHandle, SIGNAL(selectionChanged(TSelection *)), this,
358 SLOT(onSelectionChanged()));
359
360 m_inputRas = TRasterP();
361 m_viewer->raster() = TRasterP();
362 }
363
364 //--------------------------------------------------------------
365
acquireRaster()366 void AdjustLevelsPopup::acquireRaster() {
367 // Retrieve current selection
368 TApp *app = TApp::instance();
369 TSelection *selection = app->getCurrentSelection()->getSelection();
370 TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(selection);
371 TFilmstripSelection *filmstripSelection =
372 dynamic_cast<TFilmstripSelection *>(selection);
373
374 // Retrieve the input raster
375 m_inputRas = TRasterP();
376 if (cellSelection) {
377 TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
378 TXshCell cell = xsh->getCell(app->getCurrentFrame()->getFrameIndex(),
379 app->getCurrentColumn()->getColumnIndex());
380 TRasterImageP rasImage = cell.getImage(true);
381 if (rasImage && rasImage->getRaster()) m_inputRas = rasImage->getRaster();
382 } else if (filmstripSelection) {
383 TXshSimpleLevel *simpleLevel = app->getCurrentLevel()->getSimpleLevel();
384 if (simpleLevel) {
385 TRasterImageP rasImage = (TRasterImageP)simpleLevel->getFrame(
386 app->getCurrentFrame()->getFid(), true);
387 if (rasImage && rasImage->getRaster()) m_inputRas = rasImage->getRaster();
388 }
389 }
390
391 if (m_inputRas) {
392 m_threshold = m_inputRas->getLx() * m_inputRas->getLy() * m_thresholdD;
393 m_okBtn->setEnabled(true);
394 } else {
395 m_inputRas = TRasterP();
396 m_okBtn->setEnabled(false);
397 }
398
399 // Build histograms
400 m_histogram->setRaster(m_inputRas);
401
402 // Update the corresponding processed image in the viewer
403 updateProcessedImage();
404 }
405
406 //--------------------------------------------------------------
407
setThreshold(double t)408 void AdjustLevelsPopup::setThreshold(double t) {
409 m_thresholdD = t;
410 if (m_inputRas)
411 m_threshold = m_inputRas->getLx() * m_inputRas->getLy() * m_thresholdD;
412 }
413
414 //--------------------------------------------------------------
415
getParameters(int * in0,int * in1,int * out0,int * out1)416 void AdjustLevelsPopup::getParameters(int *in0, int *in1, int *out0,
417 int *out1) {
418 int p[2];
419
420 int i, j;
421 for (i = j = 0; i < 10; i += 2, ++j) {
422 m_marksBar[i]->getValues(p);
423 in0[j] = p[0], in1[j] = p[1];
424
425 m_marksBar[i + 1]->getValues(p);
426 out0[j] = p[0], out1[j] = p[1];
427 }
428 }
429
430 //--------------------------------------------------------------
431
updateProcessedImage()432 void AdjustLevelsPopup::updateProcessedImage() {
433 if (!m_inputRas) {
434 m_viewer->raster() = TRasterP();
435 m_viewer->update();
436 return;
437 }
438
439 // Allocate a conformant output, if necessary
440 TRasterP &outRas = m_viewer->raster();
441 if (!outRas || outRas->getPixelSize() != m_inputRas->getPixelSize() ||
442 outRas->getSize() != m_inputRas->getSize())
443 outRas = m_inputRas->create(m_inputRas->getLx(), m_inputRas->getLy());
444
445 // Perform the operation preview
446 int in0[5], in1[5], out0[5], out1[5];
447 getParameters(in0, in1, out0, out1);
448
449 TRop::rgbmAdjust(outRas, m_inputRas, in0, in1, out0, out1);
450
451 // Update the swatch
452 m_viewer->update();
453 }
454
455 //--------------------------------------------------------------
456
onSelectionChanged()457 void AdjustLevelsPopup::onSelectionChanged() { acquireRaster(); }
458
459 //--------------------------------------------------------------
460
461 // Params were changed. Content must be updated.
onParamsChanged()462 void AdjustLevelsPopup::onParamsChanged() { updateProcessedImage(); }
463
464 //--------------------------------------------------------------
465
466 // Reset ALL channels
reset()467 void AdjustLevelsPopup::reset() {
468 int i;
469 for (i = 0; i < 10; ++i) {
470 EditableMarksBar *editableMarksBar = m_marksBar[i];
471
472 QVector<int> &marks = editableMarksBar->marksBar()->values();
473 marks[0] = 0, marks[1] = 256;
474
475 editableMarksBar->updateFields();
476 }
477
478 onParamsChanged();
479 update();
480 }
481
482 //--------------------------------------------------------------
483
clampRange()484 void AdjustLevelsPopup::clampRange() {
485 if (!m_inputRas) return;
486
487 Histograms *histograms = m_histogram->getHistograms();
488 int channelIdx = histograms->currentIndex();
489 int inputBarIdx = (channelIdx << 1);
490
491 EditableMarksBar *editableMarksBar = m_marksBar[inputBarIdx];
492
493 int min, max;
494
495 // Clamp histogram
496 const QVector<int> &values =
497 histograms->getHistogramView(channelIdx)->values();
498 ::getRange(values, min, max);
499
500 QVector<int> &marks = editableMarksBar->marksBar()->values();
501 if (min < max)
502 marks[0] = min, marks[1] = max + 1;
503 else
504 marks[0] = 0, marks[1] = 256;
505
506 editableMarksBar->updateFields();
507 onParamsChanged();
508 update();
509 }
510
511 //--------------------------------------------------------------
512
autoAdjust()513 void AdjustLevelsPopup::autoAdjust() {
514 if (!m_inputRas) return;
515
516 Histograms *histograms = m_histogram->getHistograms();
517 int channelIdx = histograms->currentIndex();
518 int inputBarIdx = (channelIdx << 1);
519
520 EditableMarksBar *editableMarksBar = m_marksBar[inputBarIdx];
521
522 int min, max;
523
524 // Clamp histogram
525 const QVector<int> &values =
526 histograms->getHistogramView(channelIdx)->values();
527 if (channelIdx == 0) {
528 int minR, maxR, minG, maxG, minB, maxB;
529
530 ::getRange(histograms->getHistogramView(1)->values(), m_threshold, minR,
531 maxR);
532 ::getRange(histograms->getHistogramView(2)->values(), m_threshold, minG,
533 maxG);
534 ::getRange(histograms->getHistogramView(3)->values(), m_threshold, minB,
535 maxB);
536
537 min = std::min({minR, minG, minB});
538 max = std::max({maxR, maxG, maxB});
539 } else
540 ::getRange(values, m_threshold, min, max);
541
542 QVector<int> &marks = editableMarksBar->marksBar()->values();
543 if (min < max)
544 marks[0] = min, marks[1] = max + 1;
545 else
546 marks[0] = 0, marks[1] = 256;
547
548 editableMarksBar->updateFields();
549 onParamsChanged();
550 update();
551 }
552
553 //**************************************************************************
554 // TGBMScale Undo
555 //**************************************************************************
556
557 class AdjustLevelsUndo final : public TUndo {
558 int m_in0[5], m_in1[5], m_out0[5], m_out1[5];
559 int m_r, m_c;
560
561 QString m_rasId;
562 int m_rasSize;
563
564 public:
565 AdjustLevelsUndo(int *in0, int *in1, int *out0, int *out1, int r, int c,
566 TRasterP ras);
567 ~AdjustLevelsUndo();
568
569 void undo() const override;
570 void redo() const override;
571
getSize() const572 int getSize() const override { return sizeof(*this) + m_rasSize; }
573 };
574
575 //--------------------------------------------------------------
576
AdjustLevelsUndo(int * in0,int * in1,int * out0,int * out1,int r,int c,TRasterP ras)577 AdjustLevelsUndo::AdjustLevelsUndo(int *in0, int *in1, int *out0, int *out1,
578 int r, int c, TRasterP ras)
579 : m_r(r)
580 , m_c(c)
581 , m_rasSize(ras->getLx() * ras->getLy() * ras->getPixelSize()) {
582 memcpy(m_in0, in0, sizeof(m_in0));
583 memcpy(m_in1, in1, sizeof(m_in1));
584 memcpy(m_out0, out0, sizeof(m_out0));
585 memcpy(m_out1, out1, sizeof(m_out1));
586
587 static int counter = 0;
588 m_rasId = QString("AdjustLevelsUndo") + QString::number(++counter);
589
590 TImageCache::instance()->add(m_rasId, TRasterImageP(ras));
591 }
592
593 //--------------------------------------------------------------
594
~AdjustLevelsUndo()595 AdjustLevelsUndo::~AdjustLevelsUndo() {
596 TImageCache::instance()->remove(m_rasId);
597 }
598
599 //--------------------------------------------------------------
600
undo() const601 void AdjustLevelsUndo::undo() const {
602 TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
603 TXshCell cell = xsheet->getCell(m_r, m_c);
604
605 TRasterImageP rasImage = (TRasterImageP)cell.getImage(true);
606 if (!rasImage) return; //...Should never happen, though...
607
608 rasImage->setRaster(
609 ((TRasterImageP)TImageCache::instance()->get(m_rasId, true))
610 ->getRaster()
611 ->clone());
612
613 TXshSimpleLevel *simpleLevel = cell.getSimpleLevel();
614 assert(simpleLevel);
615 simpleLevel->touchFrame(cell.getFrameId());
616 simpleLevel->setDirtyFlag(true);
617 IconGenerator::instance()->invalidate(simpleLevel, cell.getFrameId());
618
619 if (m_isLastInBlock)
620 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
621 }
622
623 //--------------------------------------------------------------
624
redo() const625 void AdjustLevelsUndo::redo() const {
626 TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
627 TXshCell cell = xsheet->getCell(m_r, m_c);
628
629 TRasterImageP rasImage = (TRasterImageP)cell.getImage(true);
630 if (!rasImage) return;
631
632 TRasterP ras = rasImage->getRaster();
633 if (!ras) return;
634
635 TRop::rgbmAdjust(ras, ras, m_in0, m_in1, m_out0, m_out1);
636
637 TXshSimpleLevel *simpleLevel = cell.getSimpleLevel();
638 assert(simpleLevel);
639 simpleLevel->touchFrame(cell.getFrameId());
640 simpleLevel->setDirtyFlag(true);
641 IconGenerator::instance()->invalidate(simpleLevel, cell.getFrameId());
642
643 if (m_isLastInBlock)
644 TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
645 }
646
647 //**************************************************************************
648 // Apply stuff
649 //**************************************************************************
650
apply()651 void AdjustLevelsPopup::apply() {
652 // Retrieve parameters
653 int in0[5], in1[5], out0[5], out1[5];
654 getParameters(in0, in1, out0, out1);
655
656 // Operate depending on the selection kind
657 TCellSelection *cellSelection =
658 dynamic_cast<TCellSelection *>(TSelection::getCurrent());
659 if (cellSelection) {
660 std::set<TRasterImage *>
661 images; // Multiple cells may yield the same image...
662
663 int r0, c0, r1, c1;
664 cellSelection->getSelectedCells(r0, c0, r1, c1);
665 TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
666 bool oneImageChanged = false;
667
668 TUndoManager::manager()->beginBlock();
669 {
670 int c, r;
671 for (c = c0; c <= c1; c++) {
672 for (r = r0; r <= r1; r++) {
673 const TXshCell &cell = xsheet->getCell(r, c);
674
675 TRasterImageP rasImage = (TRasterImageP)cell.getImage(true);
676 if (!rasImage) continue;
677
678 if (images.find(rasImage.getPointer()) != images.end()) continue;
679
680 TRasterP ras = rasImage->getRaster();
681 if (!ras) continue;
682
683 images.insert(rasImage.getPointer());
684 oneImageChanged = true;
685
686 TUndoManager::manager()->add(
687 new AdjustLevelsUndo(in0, in1, out0, out1, r, c, ras->clone()));
688 TRop::rgbmAdjust(ras, ras, in0, in1, out0, out1);
689
690 TXshSimpleLevel *simpleLevel = cell.getSimpleLevel();
691 assert(simpleLevel);
692 simpleLevel->touchFrame(cell.getFrameId());
693 simpleLevel->setDirtyFlag(true);
694
695 IconGenerator::instance()->invalidate(simpleLevel, cell.getFrameId());
696 }
697 }
698 }
699 TUndoManager::manager()->endBlock();
700
701 if (oneImageChanged) {
702 close();
703 return;
704 }
705 }
706
707 TFilmstripSelection *filmstripSelection =
708 dynamic_cast<TFilmstripSelection *>(TSelection::getCurrent());
709 if (filmstripSelection) {
710 TXshSimpleLevel *simpleLevel =
711 TApp::instance()->getCurrentLevel()->getSimpleLevel();
712 if (simpleLevel) {
713 std::set<TFrameId> fids = filmstripSelection->getSelectedFids();
714 bool oneImageChanged = false;
715
716 for (auto const &fid : fids) {
717 TRasterImageP rasImage =
718 (TRasterImageP)simpleLevel->getFrame(fid, true);
719 if (!rasImage) continue;
720
721 TRasterP ras = rasImage->getRaster();
722 if (!ras) continue;
723
724 oneImageChanged = true;
725 TRop::rgbmAdjust(ras, ras, in0, in1, out0, out1);
726
727 simpleLevel->touchFrame(fid);
728 simpleLevel->setDirtyFlag(true);
729
730 IconGenerator::instance()->invalidate(simpleLevel, fid);
731 }
732
733 if (oneImageChanged) {
734 close();
735 return;
736 }
737 }
738 }
739
740 DVGui::error(QObject::tr("The current selection is invalid."));
741 return;
742 }
743
744 //**************************************************************************
745 // Open Popup Command Handler instantiation
746 //**************************************************************************
747
748 OpenPopupCommandHandler<AdjustLevelsPopup> openAdjustLevelsPopup(
749 MI_AdjustLevels);
750