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