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 test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45
46 #include <QDebug>
47 #include <QFile>
48 #include <QImage>
49 #include <QImageReader>
50 #include <QImageWriter>
51 #include <QLabel>
52 #include <QPainter>
53 #include <QSet>
54
55 #if defined(Q_OS_SYMBIAN)
56 # define SRCDIR ""
57 #endif
58 typedef QMap<QString, QString> QStringMap;
59 typedef QList<int> QIntList;
60 Q_DECLARE_METATYPE(QImage)
61 Q_DECLARE_METATYPE(QStringMap)
62 Q_DECLARE_METATYPE(QIntList)
63 Q_DECLARE_METATYPE(QImageWriter::ImageWriterError)
64 Q_DECLARE_METATYPE(QIODevice *)
65 Q_DECLARE_METATYPE(QImage::Format)
66
67 //TESTED_FILES=
68
69 class tst_QImageWriter : public QObject
70 {
71 Q_OBJECT
72
73 public:
74 tst_QImageWriter();
75 virtual ~tst_QImageWriter();
76
77 public slots:
78 void init();
79 void cleanup();
80
81 private slots:
82 void getSetCheck();
83 void writeImage_data();
84 void writeImage();
85 void writeImage2_data();
86 void writeImage2();
87 void supportedFormats();
88
89 void readWriteNonDestructive_data();
90 void readWriteNonDestructive();
91
92 #if defined QTEST_HAVE_TIFF
93 void largeTiff();
94 #endif
95
96 void writeToInvalidDevice();
97
98 void supportsOption_data();
99 void supportsOption();
100
101 void saveWithNoFormat_data();
102 void saveWithNoFormat();
103
104 void resolution_data();
105 void resolution();
106
107 void saveToTemporaryFile();
108 };
109 #ifdef Q_OS_SYMBIAN
110 static const QLatin1String prefix(SRCDIR "images/");
111 #else
112 static const QLatin1String prefix(SRCDIR "/images/");
113 #endif
initializePadding(QImage * image)114 static void initializePadding(QImage *image)
115 {
116 int effectiveBytesPerLine = (image->width() * image->depth() + 7) / 8;
117 int paddingBytes = image->bytesPerLine() - effectiveBytesPerLine;
118 if (paddingBytes == 0)
119 return;
120 for (int y = 0; y < image->height(); ++y) {
121 qMemSet(image->scanLine(y) + effectiveBytesPerLine, 0, paddingBytes);
122 }
123 }
124
125 // Testing get/set functions
getSetCheck()126 void tst_QImageWriter::getSetCheck()
127 {
128 QImageWriter obj1;
129 // QIODevice * QImageWriter::device()
130 // void QImageWriter::setDevice(QIODevice *)
131 QFile *var1 = new QFile;
132 obj1.setDevice(var1);
133
134 QCOMPARE((QIODevice *) var1, obj1.device());
135 // The class should possibly handle a 0-pointer as a device, since
136 // there is a default contructor, so it's "handling" a 0 device by default.
137 // For example: QMovie::setDevice(0) works just fine
138 obj1.setDevice((QIODevice *)0);
139 QCOMPARE((QIODevice *) 0, obj1.device());
140 delete var1;
141
142 // int QImageWriter::quality()
143 // void QImageWriter::setQuality(int)
144 obj1.setQuality(0);
145 QCOMPARE(0, obj1.quality());
146 obj1.setQuality(INT_MIN);
147 QCOMPARE(INT_MIN, obj1.quality());
148 obj1.setQuality(INT_MAX);
149 QCOMPARE(INT_MAX, obj1.quality());
150
151 // int QImageWriter::compression()
152 // void QImageWriter::setCompression(int)
153 obj1.setCompression(0);
154 QCOMPARE(0, obj1.compression());
155 obj1.setCompression(INT_MIN);
156 QCOMPARE(INT_MIN, obj1.compression());
157 obj1.setCompression(INT_MAX);
158 QCOMPARE(INT_MAX, obj1.compression());
159
160 // float QImageWriter::gamma()
161 // void QImageWriter::setGamma(float)
162 obj1.setGamma(0.0f);
163 QCOMPARE(0.0f, obj1.gamma());
164 obj1.setGamma(1.1f);
165 QCOMPARE(1.1f, obj1.gamma());
166 }
167
tst_QImageWriter()168 tst_QImageWriter::tst_QImageWriter()
169 {
170 }
171
~tst_QImageWriter()172 tst_QImageWriter::~tst_QImageWriter()
173 {
174 QDir dir(prefix);
175 QStringList filesToDelete = dir.entryList(QStringList() << "gen-*" , QDir::NoDotAndDotDot | QDir::Files);
176 foreach( QString file, filesToDelete) {
177 QFile::remove(dir.absoluteFilePath(file));
178 }
179
180 }
181
init()182 void tst_QImageWriter::init()
183 {
184 }
185
cleanup()186 void tst_QImageWriter::cleanup()
187 {
188 }
189
writeImage_data()190 void tst_QImageWriter::writeImage_data()
191 {
192 QTest::addColumn<QString>("fileName");
193 QTest::addColumn<bool>("lossy");
194 QTest::addColumn<QByteArray>("format");
195
196 QTest::newRow("BMP: colorful") << QString("colorful.bmp") << false << QByteArray("bmp");
197 QTest::newRow("BMP: font") << QString("font.bmp") << false << QByteArray("bmp");
198 QTest::newRow("XPM: marble") << QString("marble.xpm") << false << QByteArray("xpm");
199 QTest::newRow("PNG: kollada") << QString("kollada.png") << false << QByteArray("png");
200 QTest::newRow("PPM: teapot") << QString("teapot.ppm") << false << QByteArray("ppm");
201 QTest::newRow("PBM: ship63") << QString("ship63.pbm") << true << QByteArray("pbm");
202 QTest::newRow("XBM: gnus") << QString("gnus.xbm") << false << QByteArray("xbm");
203 QTest::newRow("JPEG: beavis") << QString("beavis.jpg") << true << QByteArray("jpeg");
204 #if defined QTEST_HAVE_TIFF
205 QTest::newRow("TIFF: teapot") << QString("teapot.tiff") << false << QByteArray("tiff");
206 #endif
207 }
208
writeImage()209 void tst_QImageWriter::writeImage()
210 {
211 QFETCH(QString, fileName);
212 QFETCH(bool, lossy);
213 QFETCH(QByteArray, format);
214
215 QImage image;
216 {
217 QImageReader reader(prefix + fileName);
218 image = reader.read();
219 QVERIFY2(!image.isNull(), qPrintable(reader.errorString()));
220 }
221 {
222 QImageWriter writer(prefix + "gen-" + fileName, format);
223 QVERIFY(writer.write(image));
224 }
225
226 {
227 // Shouldn't be able to write to read-only file
228 QFile sourceFile(prefix + "gen-" + fileName);
229 QFile::Permissions permissions = sourceFile.permissions();
230 QVERIFY(sourceFile.setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther));
231
232 QImageWriter writer(prefix + "gen-" + fileName, format);
233 QVERIFY(!writer.write(image));
234
235 QVERIFY(sourceFile.setPermissions(permissions));
236 }
237
238 QImage image2;
239 {
240 QImageReader reader(prefix + "gen-" + fileName);
241 image2 = reader.read();
242 QVERIFY(!image2.isNull());
243 }
244 if (!lossy) {
245 QCOMPARE(image, image2);
246 } else {
247 QCOMPARE(image.format(), image2.format());
248 QCOMPARE(image.depth(), image2.depth());
249 }
250 }
251
writeImage2_data()252 void tst_QImageWriter::writeImage2_data()
253 {
254 QTest::addColumn<QString>("fileName");
255 QTest::addColumn<QByteArray>("format");
256 QTest::addColumn<QImage>("image");
257
258 const QStringList formats = QStringList() << "bmp" << "xpm" << "png"
259 << "ppm"; //<< "jpeg";
260 QImage image0(70, 70, QImage::Format_ARGB32);
261 image0.fill(QColor(Qt::red).rgb());
262
263 QImage::Format imgFormat = QImage::Format_Mono;
264 while (imgFormat != QImage::NImageFormats) {
265 QImage image = image0.convertToFormat(imgFormat);
266 initializePadding(&image);
267 foreach (const QString format, formats) {
268 const QString fileName = QString("solidcolor_%1.%2").arg(imgFormat)
269 .arg(format);
270 QTest::newRow(fileName.toLatin1()) << fileName
271 << format.toLatin1()
272 << image;
273 }
274 imgFormat = QImage::Format(int(imgFormat) + 1);
275 }
276 }
277
278 #if defined QTEST_HAVE_TIFF
largeTiff()279 void tst_QImageWriter::largeTiff()
280 {
281 #if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
282 QImage img(4096, 2048, QImage::Format_ARGB32);
283
284 QPainter p(&img);
285 img.fill(0x0);
286 p.fillRect(0, 0, 4096, 2048, QBrush(Qt::CrossPattern));
287 p.end();
288
289 QByteArray array;
290 QBuffer writeBuffer(&array);
291 writeBuffer.open(QIODevice::WriteOnly);
292
293 QImageWriter writer(&writeBuffer, "tiff");
294 QVERIFY(writer.write(img));
295
296 writeBuffer.close();
297
298 QBuffer readBuffer(&array);
299 readBuffer.open(QIODevice::ReadOnly);
300
301 QImageReader reader(&readBuffer, "tiff");
302
303 QImage img2 = reader.read();
304 QVERIFY(!img2.isNull());
305
306 QCOMPARE(img, img2);
307 #else
308 QWARN("not tested on Symbian/WinCE");
309 #endif
310 }
311 #endif
312
313 /*
314 Workaround for the equality operator for indexed formats
315 (which fails if the colortables are different).
316
317 Images must have the same format and size.
318 */
equalImageContents(const QImage & image1,const QImage & image2)319 static bool equalImageContents(const QImage &image1, const QImage &image2)
320 {
321 switch (image1.format()) {
322 case QImage::Format_Mono:
323 case QImage::Format_Indexed8:
324 for (int y = 0; y < image1.height(); ++y)
325 for (int x = 0; x < image1.width(); ++x)
326 if (image1.pixel(x, y) != image2.pixel(x, y))
327 return false;
328 return true;
329 default:
330 return (image1 == image2);
331 }
332 }
333
writeImage2()334 void tst_QImageWriter::writeImage2()
335 {
336 QFETCH(QString, fileName);
337 QFETCH(QByteArray, format);
338 QFETCH(QImage, image);
339
340 //we reduce the scope of writer so that it closes the associated file
341 // and QFile::remove can actually work
342 {
343 QImageWriter writer(fileName, format);
344 QVERIFY(writer.write(image));
345 }
346
347 QImage written;
348
349 //we reduce the scope of reader so that it closes the associated file
350 // and QFile::remove can actually work
351 {
352 QImageReader reader(fileName, format);
353 QVERIFY(reader.read(&written));
354 }
355
356 written = written.convertToFormat(image.format());
357 if (!equalImageContents(written, image)) {
358 qDebug() << "image" << image.format() << image.width()
359 << image.height() << image.depth()
360 << hex << image.pixel(0, 0);
361 qDebug() << "written" << written.format() << written.width()
362 << written.height() << written.depth()
363 << hex << written.pixel(0, 0);
364 }
365 QVERIFY(equalImageContents(written, image));
366
367 QVERIFY(QFile::remove(fileName));
368 }
369
supportedFormats()370 void tst_QImageWriter::supportedFormats()
371 {
372 QList<QByteArray> formats = QImageWriter::supportedImageFormats();
373 QList<QByteArray> sortedFormats = formats;
374 qSort(sortedFormats);
375
376 // check that the list is sorted
377 QCOMPARE(formats, sortedFormats);
378
379 QSet<QByteArray> formatSet;
380 foreach (QByteArray format, formats)
381 formatSet << format;
382
383 // check that the list does not contain duplicates
384 QCOMPARE(formatSet.size(), formats.size());
385 }
386
readWriteNonDestructive_data()387 void tst_QImageWriter::readWriteNonDestructive_data()
388 {
389 QTest::addColumn<QImage::Format>("format");
390 QTest::addColumn<QImage::Format>("expectedFormat");
391 QTest::addColumn<bool>("grayscale");
392 QTest::newRow("tiff mono") << QImage::Format_Mono << QImage::Format_Mono << false;
393 QTest::newRow("tiff indexed") << QImage::Format_Indexed8 << QImage::Format_Indexed8 << false;
394 QTest::newRow("tiff rgb32") << QImage::Format_ARGB32 << QImage::Format_ARGB32 << false;
395 QTest::newRow("tiff grayscale") << QImage::Format_Indexed8 << QImage::Format_Indexed8 << true;
396 }
397
readWriteNonDestructive()398 void tst_QImageWriter::readWriteNonDestructive()
399 {
400 QFETCH(QImage::Format, format);
401 QFETCH(QImage::Format, expectedFormat);
402 QFETCH(bool, grayscale);
403 QImage image = QImage(prefix + "colorful.bmp").convertToFormat(format);
404
405 if (grayscale) {
406 QVector<QRgb> colors;
407 for (int i = 0; i < 256; ++i)
408 colors << qRgb(i, i, i);
409 image.setColorTable(colors);
410 }
411
412 QVERIFY(image.save(prefix + "gen-readWriteNonDestructive.tiff"));
413
414 QImage image2 = QImage(prefix + "gen-readWriteNonDestructive.tiff");
415 QImage::Format readFormat = image2.format();
416 QCOMPARE(readFormat, expectedFormat);
417 QCOMPARE(image, image2);
418 }
419
writeToInvalidDevice()420 void tst_QImageWriter::writeToInvalidDevice()
421 {
422 QLatin1String fileName("/these/directories/do/not/exist/001.png");
423 {
424 QImageWriter writer(fileName);
425 QVERIFY(!writer.canWrite());
426 QCOMPARE(writer.error(), QImageWriter::DeviceError);
427 }
428 {
429 QImageWriter writer(fileName);
430 writer.setFormat("png");
431 QVERIFY(!writer.canWrite());
432 QCOMPARE(writer.error(), QImageWriter::DeviceError);
433 }
434 {
435 QImageWriter writer(fileName);
436 QImage im(10, 10, QImage::Format_ARGB32);
437 QVERIFY(!writer.write(im));
438 QCOMPARE(writer.error(), QImageWriter::DeviceError);
439 }
440 {
441 QImageWriter writer(fileName);
442 writer.setFormat("png");
443 QImage im(10, 10, QImage::Format_ARGB32);
444 QVERIFY(!writer.write(im));
445 QCOMPARE(writer.error(), QImageWriter::DeviceError);
446 }
447 }
448
supportsOption_data()449 void tst_QImageWriter::supportsOption_data()
450 {
451 QTest::addColumn<QString>("fileName");
452 QTest::addColumn<QIntList>("options");
453
454 QTest::newRow("png") << QString("gen-black.png")
455 << (QIntList() << QImageIOHandler::Gamma
456 << QImageIOHandler::Description
457 << QImageIOHandler::Quality
458 << QImageIOHandler::Size);
459 #if defined QTEST_HAVE_TIFF
460 QTest::newRow("tiff") << QString("gen-black.tiff")
461 << (QIntList() << QImageIOHandler::Size
462 << QImageIOHandler::CompressionRatio);
463 #endif
464 }
465
supportsOption()466 void tst_QImageWriter::supportsOption()
467 {
468 QFETCH(QString, fileName);
469 QFETCH(QIntList, options);
470
471 QSet<QImageIOHandler::ImageOption> allOptions;
472 allOptions << QImageIOHandler::Size
473 << QImageIOHandler::ClipRect
474 << QImageIOHandler::Description
475 << QImageIOHandler::ScaledClipRect
476 << QImageIOHandler::ScaledSize
477 << QImageIOHandler::CompressionRatio
478 << QImageIOHandler::Gamma
479 << QImageIOHandler::Quality
480 << QImageIOHandler::Name
481 << QImageIOHandler::SubType
482 << QImageIOHandler::IncrementalReading
483 << QImageIOHandler::Endianness
484 << QImageIOHandler::Animation
485 << QImageIOHandler::BackgroundColor;
486
487 QImageWriter writer(prefix + fileName);
488 for (int i = 0; i < options.size(); ++i) {
489 QVERIFY(writer.supportsOption(QImageIOHandler::ImageOption(options.at(i))));
490 allOptions.remove(QImageIOHandler::ImageOption(options.at(i)));
491 }
492
493 foreach (QImageIOHandler::ImageOption option, allOptions)
494 QVERIFY(!writer.supportsOption(option));
495 }
496
saveWithNoFormat_data()497 void tst_QImageWriter::saveWithNoFormat_data()
498 {
499 QTest::addColumn<QString>("fileName");
500 QTest::addColumn<QByteArray>("format");
501 QTest::addColumn<QImageWriter::ImageWriterError>("error");
502
503 QTest::newRow("garble") << prefix + QString("gen-out.garble") << QByteArray("jpeg") << QImageWriter::UnsupportedFormatError;
504 QTest::newRow("bmp") << prefix + QString("gen-out.bmp") << QByteArray("bmp") << QImageWriter::ImageWriterError(0);
505 QTest::newRow("xbm") << prefix + QString("gen-out.xbm") << QByteArray("xbm") << QImageWriter::ImageWriterError(0);
506 QTest::newRow("xpm") << prefix + QString("gen-out.xpm") << QByteArray("xpm") << QImageWriter::ImageWriterError(0);
507 QTest::newRow("png") << prefix + QString("gen-out.png") << QByteArray("png") << QImageWriter::ImageWriterError(0);
508 QTest::newRow("ppm") << prefix + QString("gen-out.ppm") << QByteArray("ppm") << QImageWriter::ImageWriterError(0);
509 QTest::newRow("pbm") << prefix + QString("gen-out.pbm") << QByteArray("pbm") << QImageWriter::ImageWriterError(0);
510 #if defined QTEST_HAVE_TIFF
511 QTest::newRow("tiff") << prefix + QString("gen-out.tiff") << QByteArray("tiff") << QImageWriter::ImageWriterError(0);
512 #endif
513 }
514
saveWithNoFormat()515 void tst_QImageWriter::saveWithNoFormat()
516 {
517 QFETCH(QString, fileName);
518 QFETCH(QByteArray, format);
519 QFETCH(QImageWriter::ImageWriterError, error);
520
521 QImage niceImage(64, 64, QImage::Format_ARGB32);
522 qMemSet(niceImage.bits(), 0, niceImage.byteCount());
523
524 QImageWriter writer(fileName /* , 0 - no format! */);
525 if (error != 0) {
526 QVERIFY(!writer.write(niceImage));
527 QCOMPARE(writer.error(), error);
528 return;
529 }
530
531 QVERIFY2(writer.write(niceImage), qPrintable(writer.errorString()));
532
533 QImageReader reader(fileName);
534 QCOMPARE(reader.format(), format);
535
536 QVERIFY(reader.canRead());
537
538 QImage outImage = reader.read();
539 QVERIFY2(!outImage.isNull(), qPrintable(reader.errorString()));
540 }
541
resolution_data()542 void tst_QImageWriter::resolution_data()
543 {
544 QTest::addColumn<QString>("filename");
545 QTest::addColumn<int>("expectedDotsPerMeterX");
546 QTest::addColumn<int>("expectedDotsPerMeterY");
547 #if defined QTEST_HAVE_TIFF
548 QTest::newRow("TIFF: 100 dpi") << ("image_100dpi.tif") << qRound(100 * (100 / 2.54)) << qRound(100 * (100 / 2.54));
549 QTest::newRow("TIFF: 50 dpi") << ("image_50dpi.tif") << qRound(50 * (100 / 2.54)) << qRound(50 * (100 / 2.54));
550 QTest::newRow("TIFF: 300 dot per meter") << ("image_300dpm.tif") << 300 << 300;
551 #endif
552 }
553
resolution()554 void tst_QImageWriter::resolution()
555 {
556 QFETCH(QString, filename);
557 QFETCH(int, expectedDotsPerMeterX);
558 QFETCH(int, expectedDotsPerMeterY);
559
560 QImage image(prefix + QLatin1String("colorful.bmp"));
561 image.setDotsPerMeterX(expectedDotsPerMeterX);
562 image.setDotsPerMeterY(expectedDotsPerMeterY);
563 const QString generatedFilepath = prefix + "gen-" + filename;
564 {
565 QImageWriter writer(generatedFilepath);
566 QVERIFY(writer.write(image));
567 }
568 QImageReader reader(generatedFilepath);
569 const QImage generatedImage = reader.read();
570
571 QCOMPARE(expectedDotsPerMeterX, generatedImage.dotsPerMeterX());
572 QCOMPARE(expectedDotsPerMeterY, generatedImage.dotsPerMeterY());
573 }
574
saveToTemporaryFile()575 void tst_QImageWriter::saveToTemporaryFile()
576 {
577 QImage image(prefix + "kollada.png");
578 QVERIFY(!image.isNull());
579
580 {
581 // 1) Via QImageWriter's API, with a standard temp file name
582 QTemporaryFile file;
583 QVERIFY(file.open());
584 QImageWriter writer(&file, "PNG");
585 if (writer.canWrite())
586 QVERIFY(writer.write(image));
587 else
588 qWarning() << file.errorString();
589 #if defined(Q_OS_WINCE)
590 file.reset();
591 #endif
592 QCOMPARE(QImage(writer.fileName()), image);
593 }
594 {
595 // 2) Via QImage's API, with a standard temp file name
596 QTemporaryFile file;
597 QVERIFY(file.open());
598 QVERIFY(image.save(&file, "PNG"));
599 file.reset();
600 QImage tmp;
601 QVERIFY(tmp.load(&file, "PNG"));
602 QCOMPARE(tmp, image);
603 }
604 {
605 // 3) Via QImageWriter's API, with a named temp file
606 QTemporaryFile file("tempXXXXXX");
607 QVERIFY(file.open());
608 QImageWriter writer(&file, "PNG");
609 QVERIFY(writer.write(image));
610 #if defined(Q_OS_WINCE)
611 file.reset();
612 #endif
613 QCOMPARE(QImage(writer.fileName()), image);
614 }
615 {
616 // 4) Via QImage's API, with a named temp file
617 QTemporaryFile file("tempXXXXXX");
618 QVERIFY(file.open());
619 QVERIFY(image.save(&file, "PNG"));
620 file.reset();
621 QImage tmp;
622 QVERIFY(tmp.load(&file, "PNG"));
623 QCOMPARE(tmp, image);
624 }
625 }
626
627 QTEST_MAIN(tst_QImageWriter)
628 #include "tst_qimagewriter.moc"
629