source: trunk/src/plugins/imageformats/tiff/qtiffhandler.cpp@ 318

Last change on this file since 318 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 10.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qtiffhandler.h"
43#include <qvariant.h>
44#include <qdebug.h>
45#include <qimage.h>
46#include <qglobal.h>
47extern "C" {
48#include "tiffio.h"
49}
50
51QT_BEGIN_NAMESPACE
52
53tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
54{
55 QIODevice* device = static_cast<QTiffHandler*>(fd)->device();
56 return device->isReadable() ? device->read(static_cast<char *>(buf), size) : -1;
57}
58
59tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
60{
61 return static_cast<QTiffHandler*>(fd)->device()->write(static_cast<char *>(buf), size);
62}
63
64toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence)
65{
66 QIODevice *device = static_cast<QTiffHandler*>(fd)->device();
67 switch (whence) {
68 case SEEK_SET:
69 device->seek(off);
70 break;
71 case SEEK_CUR:
72 device->seek(device->pos() + off);
73 break;
74 case SEEK_END:
75 device->seek(device->size() + off);
76 break;
77 }
78
79 return device->pos();
80}
81
82int qtiffCloseProc(thandle_t /*fd*/)
83{
84 return 0;
85}
86
87toff_t qtiffSizeProc(thandle_t fd)
88{
89 return static_cast<QTiffHandler*>(fd)->device()->size();
90}
91
92int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/)
93{
94 return 0;
95}
96
97void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/)
98{
99}
100
101QTiffHandler::QTiffHandler() : QImageIOHandler()
102{
103 compression = NoCompression;
104}
105
106bool QTiffHandler::canRead() const
107{
108 if (canRead(device())) {
109 setFormat("tiff");
110 return true;
111 }
112 return false;
113}
114
115bool QTiffHandler::canRead(QIODevice *device)
116{
117 if (!device) {
118 qWarning("QTiffHandler::canRead() called with no device");
119 return false;
120 }
121
122 // current implementation uses TIFFClientOpen which needs to be
123 // able to seek, so sequential devices are not supported
124 QByteArray header = device->peek(4);
125 return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4)
126 || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4);
127}
128
129bool QTiffHandler::read(QImage *image)
130{
131 if (!canRead())
132 return false;
133
134 TIFF *tiff = TIFFClientOpen("foo",
135 "r",
136 this,
137 qtiffReadProc,
138 qtiffWriteProc,
139 qtiffSeekProc,
140 qtiffCloseProc,
141 qtiffSizeProc,
142 qtiffMapProc,
143 qtiffUnmapProc);
144
145 if (tiff) {
146 uint32 width = 0;
147 uint32 height = 0;
148 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
149 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
150 if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32)
151 *image = QImage(width, height, QImage::Format_ARGB32);
152 if (!image->isNull()) {
153 if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, 0)) {
154 uint16 resUnit = RESUNIT_NONE;
155 float resX = 0;
156 float resY = 0;
157 TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit);
158 TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &resX);
159 TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &resY);
160 switch(resUnit) {
161 case RESUNIT_CENTIMETER:
162 image->setDotsPerMeterX(qRound(resX * 100));
163 image->setDotsPerMeterY(qRound(resY * 100));
164 break;
165 case RESUNIT_INCH:
166 image->setDotsPerMeterX(qRound(resX * (100 / 2.54)));
167 image->setDotsPerMeterY(qRound(resY * (100 / 2.54)));
168 break;
169 default:
170 // do nothing as defaults have already
171 // been set within the QImage class
172 break;
173 }
174 for (uint32 y=0; y<height; ++y)
175 convert32BitOrder(image->scanLine(y), width);
176 } else {
177 *image = QImage();
178 }
179 }
180 TIFFClose(tiff);
181 }
182
183 if (image->isNull())
184 return false;
185
186 return true;
187}
188
189bool QTiffHandler::write(const QImage &image)
190{
191 if (!device()->isWritable())
192 return false;
193
194 TIFF *tiff = TIFFClientOpen("foo",
195 "w",
196 this,
197 qtiffReadProc,
198 qtiffWriteProc,
199 qtiffSeekProc,
200 qtiffCloseProc,
201 qtiffSizeProc,
202 qtiffMapProc,
203 qtiffUnmapProc);
204
205 if (tiff) {
206 int width = image.width();
207 int height = image.height();
208 int depth = 32;
209
210 if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)
211 || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)
212 || !TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
213 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
214 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, depth/8)
215 || !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)
216 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
217 TIFFClose(tiff);
218 return false;
219 }
220
221 // try to do the ARGB32 conversion in chunks no greater than 16 MB
222 int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1;
223 int chunkHeight = qMax(height / chunks, 1);
224
225 int y = 0;
226 while (y < height) {
227 QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_ARGB32);
228
229 int chunkStart = y;
230 int chunkEnd = y + chunk.height();
231 while (y < chunkEnd) {
232 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
233 convert32BitOrder(chunk.scanLine(y - chunkStart), width);
234 else
235 convert32BitOrderBigEndian(chunk.scanLine(y - chunkStart), width);
236
237 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
238 TIFFClose(tiff);
239 return false;
240 }
241 ++y;
242 }
243 }
244 TIFFClose(tiff);
245 } else {
246 return false;
247 }
248 return true;
249}
250
251QByteArray QTiffHandler::name() const
252{
253 return "tiff";
254}
255
256QVariant QTiffHandler::option(ImageOption option) const
257{
258 if (option == Size && canRead()) {
259 QSize imageSize;
260 qint64 pos = device()->pos();
261 TIFF *tiff = TIFFClientOpen("foo",
262 "r",
263 const_cast<QTiffHandler*>(this),
264 qtiffReadProc,
265 qtiffWriteProc,
266 qtiffSeekProc,
267 qtiffCloseProc,
268 qtiffSizeProc,
269 qtiffMapProc,
270 qtiffUnmapProc);
271
272 if (tiff) {
273 uint32 width = 0;
274 uint32 height = 0;
275 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
276 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
277 imageSize = QSize(width, height);
278 }
279 device()->seek(pos);
280 if (imageSize.isValid())
281 return imageSize;
282 } else if (option == CompressionRatio) {
283 return compression;
284 } else if (option == ImageFormat) {
285 return QImage::Format_ARGB32;
286 }
287 return QVariant();
288}
289
290void QTiffHandler::setOption(ImageOption option, const QVariant &value)
291{
292 if (option == CompressionRatio && value.type() == QVariant::Int)
293 compression = value.toInt();
294}
295
296bool QTiffHandler::supportsOption(ImageOption option) const
297{
298 return option == CompressionRatio
299 || option == Size
300 || option == ImageFormat;
301}
302
303void QTiffHandler::convert32BitOrder(void *buffer, int width)
304{
305 uint32 *target = reinterpret_cast<uint32 *>(buffer);
306 for (int32 x=0; x<width; ++x) {
307 uint32 p = target[x];
308 // convert between ARGB and ABGR
309 target[x] = (p & 0xff000000)
310 | ((p & 0x00ff0000) >> 16)
311 | (p & 0x0000ff00)
312 | ((p & 0x000000ff) << 16);
313 }
314}
315
316void QTiffHandler::convert32BitOrderBigEndian(void *buffer, int width)
317{
318 uint32 *target = reinterpret_cast<uint32 *>(buffer);
319 for (int32 x=0; x<width; ++x) {
320 uint32 p = target[x];
321 target[x] = (p & 0xff000000) >> 24
322 | (p & 0x00ff0000) << 8
323 | (p & 0x0000ff00) << 8
324 | (p & 0x000000ff) << 8;
325 }
326}
327
328QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.