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