1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the documentation of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 **   * Redistributions of source code must retain the above copyright
25 **     notice, this list of conditions and the following disclaimer.
26 **   * Redistributions in binary form must reproduce the above copyright
27 **     notice, this list of conditions and the following disclaimer in
28 **     the documentation and/or other materials provided with the
29 **     distribution.
30 **   * Neither the name of The Qt Company Ltd nor the names of its
31 **     contributors may be used to endorse or promote products derived
32 **     from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 /*
52 viewer.cpp
53 
54 Provides a main window for displaying a user-specified original image
55 with three color separations in a grid layout.
56 
57 A main menu provides entries for selecting files, and adjusting the
58 brightness of the separations.
59 */
60 
61 #include <QtWidgets>
62 
63 #include "finalwidget.h"
64 #include "screenwidget.h"
65 #include "viewer.h"
66 
67 /*
68     Constructor: initializes a default value for the brightness, creates
69     the main menu entries, and constructs a central widget that contains
70     enough space for images to be displayed.
71 */
72 
Viewer()73 Viewer::Viewer()
74 {
75     setWindowTitle(tr("QImage Color Separations"));
76 
77     brightness = 255;
78 
79     createMenus();
80     setCentralWidget(createCentralWidget());
81 }
82 
83 /*
84     Creates a main menu with two entries: a File menu, to allow the image
85     to be selected, and a Brightness menu to allow the brightness of the
86     separations to be changed.
87 
88     Initially, the Brightness menu items are disabled, but the first entry in
89     the menu is checked to reflect the default brightness.
90 */
91 
createMenus()92 void Viewer::createMenus()
93 {
94     fileMenu = new QMenu(tr("&File"), this);
95     brightnessMenu = new QMenu(tr("&Brightness"), this);
96 
97     QAction *openAction = fileMenu->addAction(tr("&Open..."));
98     openAction->setShortcut(QKeySequence("Ctrl+O"));
99     saveAction = fileMenu->addAction(tr("&Save..."));
100     saveAction->setShortcut(QKeySequence("Ctrl+S"));
101     saveAction->setEnabled(false);
102     QAction *quitAction = fileMenu->addAction(tr("E&xit"));
103     quitAction->setShortcut(QKeySequence("Ctrl+Q"));
104 
105     QAction *noBrightness = brightnessMenu->addAction(tr("&0%"));
106     noBrightness->setCheckable(true);
107     QAction *quarterBrightness = brightnessMenu->addAction(tr("&25%"));
108     quarterBrightness->setCheckable(true);
109     QAction *halfBrightness = brightnessMenu->addAction(tr("&50%"));
110     halfBrightness->setCheckable(true);
111     QAction *threeQuartersBrightness = brightnessMenu->addAction(tr("&75%"));
112     threeQuartersBrightness->setCheckable(true);
113     QAction *fullBrightness = brightnessMenu->addAction(tr("&100%"));
114     fullBrightness->setCheckable(true);
115 
116     menuMap[noBrightness] = None;
117     menuMap[quarterBrightness] = Quarter;
118     menuMap[halfBrightness] = Half;
119     menuMap[threeQuartersBrightness] = ThreeQuarters;
120     menuMap[fullBrightness] = Full;
121 
122     currentBrightness = fullBrightness;
123     currentBrightness->setChecked(true);
124     brightnessMenu->setEnabled(false);
125 
126     menuBar()->addMenu(fileMenu);
127     menuBar()->addMenu(brightnessMenu);
128 
129     connect(openAction, &QAction::triggered, this, &Viewer::chooseFile);
130     connect(saveAction, &QAction::triggered, this, &Viewer::saveImage);
131     connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
132     connect(brightnessMenu, &QMenu::triggered,
133             this, &Viewer::setBrightness);
134 }
135 
136 /*
137     Constructs a central widget for the window consisting of a two-by-two
138     grid of labels, each of which will contain an image. We restrict the
139     size of the labels to 256 pixels, and ensure that the window cannot
140     be resized.
141 */
142 
createCentralWidget()143 QFrame* Viewer::createCentralWidget()
144 {
145     QFrame* frame = new QFrame(this);
146     grid = new QGridLayout(frame);
147     grid->setSpacing(8);
148     grid->setMargin(4);
149 
150     layout()->setSizeConstraint(QLayout::SetFixedSize);
151 
152     QSize labelSize(256, 256);
153 
154     finalWidget = new FinalWidget(frame, tr("Final image"), labelSize);
155 
156     cyanWidget = new ScreenWidget(frame, Qt::cyan, tr("Cyan"),
157                                   ScreenWidget::Cyan, labelSize);
158     magentaWidget = new ScreenWidget(frame, Qt::magenta, tr("Magenta"),
159                                      ScreenWidget::Magenta, labelSize);
160     yellowWidget = new ScreenWidget(frame, Qt::yellow, tr("Yellow"),
161                                     ScreenWidget::Yellow, labelSize);
162 
163     connect(cyanWidget, &ScreenWidget::imageChanged, this, &Viewer::createImage);
164     connect(magentaWidget, &ScreenWidget::imageChanged, this, &Viewer::createImage);
165     connect(yellowWidget, &ScreenWidget::imageChanged, this, &Viewer::createImage);
166 
167     grid->addWidget(finalWidget, 0, 0, Qt::AlignTop | Qt::AlignHCenter);
168     grid->addWidget(cyanWidget, 0, 1, Qt::AlignTop | Qt::AlignHCenter);
169     grid->addWidget(magentaWidget, 1, 0, Qt::AlignTop | Qt::AlignHCenter);
170     grid->addWidget(yellowWidget, 1, 1, Qt::AlignTop | Qt::AlignHCenter);
171 
172     return frame;
173 }
174 
175 /*
176     Provides a dialog window to allow the user to specify an image file.
177     If a file is selected, the appropriate function is called to process
178     and display it.
179 */
180 
chooseFile()181 void Viewer::chooseFile()
182 {
183     QString imageFile = QFileDialog::getOpenFileName(this,
184         tr("Choose an image file to open"), path, tr("Images (*.*)"));
185 
186     if (!imageFile.isEmpty()) {
187         openImageFile(imageFile);
188         path = imageFile;
189     }
190 }
191 
192 /*
193     Changes the value of the brightness according to the entry selected in the
194     Brightness menu. The selected entry is checked, and the previously selected
195     entry is unchecked.
196 
197     The color separations are updated to use the new value for the brightness.
198 */
199 
setBrightness(QAction * action)200 void Viewer::setBrightness(QAction *action)
201 {
202     if (!menuMap.contains(action) || scaledImage.isNull())
203         return;
204 
205     Brightness amount = menuMap[action];
206 
207     switch (amount) {
208         case None:
209             brightness = 0; break;
210         case Quarter:
211             brightness = 64; break;
212         case Half:
213             brightness = 128; break;
214         case ThreeQuarters:
215             brightness = 191; break;
216         case Full:
217             brightness = 255; break;
218         default: return;
219     }
220 
221     currentBrightness->setChecked(false);
222     currentBrightness = action;
223     currentBrightness->setChecked(true);
224 
225     createImage();
226 }
227 
228 /*
229     Load the image from the file given, and create four pixmaps based
230     on the original image.
231 
232     The window caption is set, and the Brightness menu enabled if the image file
233     can be loaded.
234 */
235 
openImageFile(QString & imageFile)236 void Viewer::openImageFile(QString &imageFile)
237 {
238     QImage originalImage;
239 
240     if (originalImage.load(imageFile)) {
241         setWindowTitle(imageFile);
242         //menuBar()->setItemEnabled(brightnessMenuId, true);
243         saveAction->setEnabled(true);
244         brightnessMenu->setEnabled(true);
245 
246         /* Note: the ScaleMin value may be different for Qt 4. */
247         scaledImage = originalImage.scaled(256, 256, Qt::KeepAspectRatio);
248 
249         cyanWidget->setImage(scaledImage);
250         magentaWidget->setImage(scaledImage);
251         yellowWidget->setImage(scaledImage);
252         createImage();
253     }
254     else
255         (void) QMessageBox::warning(this, tr("Cannot open file"),
256             tr("The selected file could not be opened."),
257             QMessageBox::Cancel, QMessageBox::NoButton, QMessageBox::NoButton);
258 }
259 
260 /*
261     Creates an image by combining the contents of the three screens
262     to present a page preview.
263 
264     The image associated with each screen is separated into cyan,
265     magenta, and yellow components. We add up the values for each
266     component from the three screen images, and subtract the totals
267     from the maximum value for each corresponding primary color.
268 */
269 
createImage()270 void Viewer::createImage()
271 {
272     QImage newImage = scaledImage.copy();
273 
274     QImage *image1 = cyanWidget->image();
275     QImage *image2 = magentaWidget->image();
276     QImage *image3 = yellowWidget->image();
277     int darkness = 255 - brightness;
278 
279     for (int y = 0; y < newImage.height(); ++y) {
280         for (int x = 0; x < newImage.width(); ++x) {
281 
282             // Create three screens, using the quantities of the source
283             // CMY components to determine how much of each of the
284             // inks are to be put on each screen.
285             QRgb p1(image1->pixel(x, y));
286             float cyan1 = 255 - qRed(p1);
287             float magenta1 = 255 - qGreen(p1);
288             float yellow1 = 255 - qBlue(p1);
289 
290             QRgb p2(image2->pixel(x, y));
291             float cyan2 = 255 - qRed(p2);
292             float magenta2 = 255 - qGreen(p2);
293             float yellow2 = 255 - qBlue(p2);
294 
295             QRgb p3(image3->pixel(x, y));
296             float cyan3 = 255 - qRed(p3);
297             float magenta3 = 255 - qGreen(p3);
298             float yellow3 = 255 - qBlue(p3);
299 
300             QColor newColor(
301                 qMax(255 - int(cyan1+cyan2+cyan3) - darkness, 0),
302                 qMax(255 - int(magenta1+magenta2+magenta3) - darkness, 0),
303                 qMax(255 - int(yellow1+yellow2+yellow3) - darkness, 0));
304 
305             newImage.setPixel(x, y, newColor.rgb());
306         }
307     }
308 
309     finalWidget->setPixmap(QPixmap::fromImage(newImage));
310 }
311 
312 /*
313     Provides a dialog window to allow the user to save the image file.
314 */
315 
saveImage()316 void Viewer::saveImage()
317 {
318     QString imageFile = QFileDialog::getSaveFileName(this,
319         tr("Choose a filename to save the image"), "", tr("Images (*.png)"));
320 
321     QFileInfo info(imageFile);
322 
323     if (!info.baseName().isEmpty()) {
324         QString newImageFile = QFileInfo(info.absoluteDir(),
325             info.baseName() + ".png").absoluteFilePath();
326 
327         if (!finalWidget->pixmap()->save(newImageFile, "PNG"))
328             (void) QMessageBox::warning(this, tr("Cannot save file"),
329                 tr("The file could not be saved."),
330                 QMessageBox::Cancel, QMessageBox::NoButton,
331                 QMessageBox::NoButton);
332     }
333     else
334         (void) QMessageBox::warning(this, tr("Cannot save file"),
335             tr("Please enter a valid filename."),
336             QMessageBox::Cancel, QMessageBox::NoButton,
337             QMessageBox::NoButton);
338 }
339