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