|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the plugins of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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> |
|
47 extern "C" { |
|
48 #include "tiffio.h" |
|
49 } |
|
50 |
|
51 QT_BEGIN_NAMESPACE |
|
52 |
|
53 tsize_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 |
|
59 tsize_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 |
|
64 toff_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 |
|
82 int qtiffCloseProc(thandle_t /*fd*/) |
|
83 { |
|
84 return 0; |
|
85 } |
|
86 |
|
87 toff_t qtiffSizeProc(thandle_t fd) |
|
88 { |
|
89 return static_cast<QTiffHandler*>(fd)->device()->size(); |
|
90 } |
|
91 |
|
92 int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/) |
|
93 { |
|
94 return 0; |
|
95 } |
|
96 |
|
97 void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/) |
|
98 { |
|
99 } |
|
100 |
|
101 // for 32 bits images |
|
102 inline void rotate_right_mirror_horizontal(QImage *const image)// rotate right->mirrored horizontal |
|
103 { |
|
104 const int height = image->height(); |
|
105 const int width = image->width(); |
|
106 QImage generated(/* width = */ height, /* height = */ width, image->format()); |
|
107 const uint32 *originalPixel = reinterpret_cast<const uint32*>(image->bits()); |
|
108 uint32 *const generatedPixels = reinterpret_cast<uint32*>(generated.bits()); |
|
109 for (int row=0; row < height; ++row) { |
|
110 for (int col=0; col < width; ++col) { |
|
111 int idx = col * height + row; |
|
112 generatedPixels[idx] = *originalPixel; |
|
113 ++originalPixel; |
|
114 } |
|
115 } |
|
116 *image = generated; |
|
117 } |
|
118 |
|
119 inline void rotate_right_mirror_vertical(QImage *const image) // rotate right->mirrored vertical |
|
120 { |
|
121 const int height = image->height(); |
|
122 const int width = image->width(); |
|
123 QImage generated(/* width = */ height, /* height = */ width, image->format()); |
|
124 const int lastCol = width - 1; |
|
125 const int lastRow = height - 1; |
|
126 const uint32 *pixel = reinterpret_cast<const uint32*>(image->bits()); |
|
127 uint32 *const generatedBits = reinterpret_cast<uint32*>(generated.bits()); |
|
128 for (int row=0; row < height; ++row) { |
|
129 for (int col=0; col < width; ++col) { |
|
130 int idx = (lastCol - col) * height + (lastRow - row); |
|
131 generatedBits[idx] = *pixel; |
|
132 ++pixel; |
|
133 } |
|
134 } |
|
135 *image = generated; |
|
136 } |
|
137 |
|
138 QTiffHandler::QTiffHandler() : QImageIOHandler() |
|
139 { |
|
140 compression = NoCompression; |
|
141 } |
|
142 |
|
143 bool QTiffHandler::canRead() const |
|
144 { |
|
145 if (canRead(device())) { |
|
146 setFormat("tiff"); |
|
147 return true; |
|
148 } |
|
149 return false; |
|
150 } |
|
151 |
|
152 bool QTiffHandler::canRead(QIODevice *device) |
|
153 { |
|
154 if (!device) { |
|
155 qWarning("QTiffHandler::canRead() called with no device"); |
|
156 return false; |
|
157 } |
|
158 |
|
159 // current implementation uses TIFFClientOpen which needs to be |
|
160 // able to seek, so sequential devices are not supported |
|
161 QByteArray header = device->peek(4); |
|
162 return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4) |
|
163 || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4); |
|
164 } |
|
165 |
|
166 bool QTiffHandler::read(QImage *image) |
|
167 { |
|
168 if (!canRead()) |
|
169 return false; |
|
170 |
|
171 TIFF *const tiff = TIFFClientOpen("foo", |
|
172 "r", |
|
173 this, |
|
174 qtiffReadProc, |
|
175 qtiffWriteProc, |
|
176 qtiffSeekProc, |
|
177 qtiffCloseProc, |
|
178 qtiffSizeProc, |
|
179 qtiffMapProc, |
|
180 qtiffUnmapProc); |
|
181 |
|
182 if (!tiff) { |
|
183 return false; |
|
184 } |
|
185 uint32 width; |
|
186 uint32 height; |
|
187 uint16 photometric; |
|
188 if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width) |
|
189 || !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height) |
|
190 || !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) { |
|
191 TIFFClose(tiff); |
|
192 return false; |
|
193 } |
|
194 |
|
195 if (photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE) { |
|
196 if (image->size() != QSize(width, height) || image->format() != QImage::Format_Mono) |
|
197 *image = QImage(width, height, QImage::Format_Mono); |
|
198 QVector<QRgb> colortable(2); |
|
199 if (photometric == PHOTOMETRIC_MINISBLACK) { |
|
200 colortable[0] = 0xff000000; |
|
201 colortable[1] = 0xffffffff; |
|
202 } else { |
|
203 colortable[0] = 0xffffffff; |
|
204 colortable[1] = 0xff000000; |
|
205 } |
|
206 image->setColorTable(colortable); |
|
207 |
|
208 if (!image->isNull()) { |
|
209 for (uint32 y=0; y<height; ++y) { |
|
210 if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) { |
|
211 TIFFClose(tiff); |
|
212 return false; |
|
213 } |
|
214 } |
|
215 } |
|
216 } else { |
|
217 uint16 bitPerSample; |
|
218 if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample)) { |
|
219 TIFFClose(tiff); |
|
220 return false; |
|
221 } |
|
222 if (photometric == PHOTOMETRIC_PALETTE && bitPerSample == 8) { |
|
223 if (image->size() != QSize(width, height) || image->format() != QImage::Format_Indexed8) |
|
224 *image = QImage(width, height, QImage::Format_Indexed8); |
|
225 if (!image->isNull()) { |
|
226 // create the color table |
|
227 const uint16 tableSize = 256; |
|
228 uint16 *redTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16))); |
|
229 uint16 *greenTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16))); |
|
230 uint16 *blueTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16))); |
|
231 if (!redTable || !greenTable || !blueTable) { |
|
232 TIFFClose(tiff); |
|
233 return false; |
|
234 } |
|
235 if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) { |
|
236 TIFFClose(tiff); |
|
237 return false; |
|
238 } |
|
239 |
|
240 QVector<QRgb> qtColorTable(tableSize); |
|
241 for (int i = 0; i<tableSize ;++i) { |
|
242 const int red = redTable[i] / 257; |
|
243 const int green = greenTable[i] / 257; |
|
244 const int blue = blueTable[i] / 257; |
|
245 qtColorTable[i] = qRgb(red, green, blue); |
|
246 |
|
247 } |
|
248 |
|
249 image->setColorTable(qtColorTable); |
|
250 for (uint32 y=0; y<height; ++y) { |
|
251 if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) { |
|
252 TIFFClose(tiff); |
|
253 return false; |
|
254 } |
|
255 } |
|
256 |
|
257 // free redTable, greenTable and greenTable done by libtiff |
|
258 } |
|
259 } else { |
|
260 if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32) |
|
261 *image = QImage(width, height, QImage::Format_ARGB32); |
|
262 if (!image->isNull()) { |
|
263 const int stopOnError = 1; |
|
264 if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, stopOnError)) { |
|
265 for (uint32 y=0; y<height; ++y) |
|
266 convert32BitOrder(image->scanLine(y), width); |
|
267 } else { |
|
268 TIFFClose(tiff); |
|
269 return false; |
|
270 } |
|
271 } |
|
272 } |
|
273 } |
|
274 |
|
275 if (image->isNull()) { |
|
276 TIFFClose(tiff); |
|
277 return false; |
|
278 } |
|
279 |
|
280 float resX = 0; |
|
281 float resY = 0; |
|
282 uint16 resUnit = RESUNIT_NONE; |
|
283 if (TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit) |
|
284 && TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &resX) |
|
285 && TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &resY)) { |
|
286 |
|
287 switch(resUnit) { |
|
288 case RESUNIT_CENTIMETER: |
|
289 image->setDotsPerMeterX(qRound(resX * 100)); |
|
290 image->setDotsPerMeterY(qRound(resY * 100)); |
|
291 break; |
|
292 case RESUNIT_INCH: |
|
293 image->setDotsPerMeterX(qRound(resX * (100 / 2.54))); |
|
294 image->setDotsPerMeterY(qRound(resY * (100 / 2.54))); |
|
295 break; |
|
296 default: |
|
297 // do nothing as defaults have already |
|
298 // been set within the QImage class |
|
299 break; |
|
300 } |
|
301 } |
|
302 |
|
303 // rotate the image if the orientation is defined in the file |
|
304 uint16 orientationTag; |
|
305 if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag)) { |
|
306 if (image->format() == QImage::Format_ARGB32) { |
|
307 // TIFFReadRGBAImageOriented() flip the image but does not rotate them |
|
308 switch (orientationTag) { |
|
309 case 5: |
|
310 rotate_right_mirror_horizontal(image); |
|
311 break; |
|
312 case 6: |
|
313 rotate_right_mirror_vertical(image); |
|
314 break; |
|
315 case 7: |
|
316 rotate_right_mirror_horizontal(image); |
|
317 break; |
|
318 case 8: |
|
319 rotate_right_mirror_vertical(image); |
|
320 break; |
|
321 } |
|
322 } else { |
|
323 switch (orientationTag) { |
|
324 case 1: // default orientation |
|
325 break; |
|
326 case 2: // mirror horizontal |
|
327 *image = image->mirrored(true, false); |
|
328 break; |
|
329 case 3: // mirror both |
|
330 *image = image->mirrored(true, true); |
|
331 break; |
|
332 case 4: // mirror vertical |
|
333 *image = image->mirrored(false, true); |
|
334 break; |
|
335 case 5: // rotate right mirror horizontal |
|
336 { |
|
337 QMatrix transformation; |
|
338 transformation.rotate(90); |
|
339 *image = image->transformed(transformation); |
|
340 *image = image->mirrored(true, false); |
|
341 break; |
|
342 } |
|
343 case 6: // rotate right |
|
344 { |
|
345 QMatrix transformation; |
|
346 transformation.rotate(90); |
|
347 *image = image->transformed(transformation); |
|
348 break; |
|
349 } |
|
350 case 7: // rotate right, mirror vertical |
|
351 { |
|
352 QMatrix transformation; |
|
353 transformation.rotate(90); |
|
354 *image = image->transformed(transformation); |
|
355 *image = image->mirrored(false, true); |
|
356 break; |
|
357 } |
|
358 case 8: // rotate left |
|
359 { |
|
360 QMatrix transformation; |
|
361 transformation.rotate(270); |
|
362 *image = image->transformed(transformation); |
|
363 break; |
|
364 } |
|
365 } |
|
366 } |
|
367 } |
|
368 |
|
369 |
|
370 TIFFClose(tiff); |
|
371 return true; |
|
372 } |
|
373 |
|
374 bool QTiffHandler::write(const QImage &image) |
|
375 { |
|
376 if (!device()->isWritable()) |
|
377 return false; |
|
378 |
|
379 TIFF *const tiff = TIFFClientOpen("foo", |
|
380 "w", |
|
381 this, |
|
382 qtiffReadProc, |
|
383 qtiffWriteProc, |
|
384 qtiffSeekProc, |
|
385 qtiffCloseProc, |
|
386 qtiffSizeProc, |
|
387 qtiffMapProc, |
|
388 qtiffUnmapProc); |
|
389 if (!tiff) |
|
390 return false; |
|
391 |
|
392 const int width = image.width(); |
|
393 const int height = image.height(); |
|
394 |
|
395 if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width) |
|
396 || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height) |
|
397 || !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { |
|
398 TIFFClose(tiff); |
|
399 return false; |
|
400 } |
|
401 |
|
402 // set the resolution |
|
403 bool resolutionSet = false; |
|
404 const int dotPerMeterX = image.dotsPerMeterX(); |
|
405 const int dotPerMeterY = image.dotsPerMeterY(); |
|
406 if ((dotPerMeterX % 100) == 0 |
|
407 && (dotPerMeterY % 100) == 0) { |
|
408 resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER) |
|
409 && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, dotPerMeterX/100.0) |
|
410 && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, dotPerMeterY/100.0); |
|
411 } else { |
|
412 resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH) |
|
413 && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(image.logicalDpiX())) |
|
414 && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(image.logicalDpiY())); |
|
415 } |
|
416 if (!resolutionSet) { |
|
417 TIFFClose(tiff); |
|
418 return false; |
|
419 } |
|
420 |
|
421 // configure image depth |
|
422 const QImage::Format format = image.format(); |
|
423 if (format == QImage::Format_Mono || format == QImage::Format_MonoLSB) { |
|
424 uint16 photometric = PHOTOMETRIC_MINISBLACK; |
|
425 if (image.colorTable().at(0) == 0xffffffff) |
|
426 photometric = PHOTOMETRIC_MINISWHITE; |
|
427 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric) |
|
428 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_CCITTRLE)) { |
|
429 TIFFClose(tiff); |
|
430 return false; |
|
431 } |
|
432 |
|
433 // try to do the conversion in chunks no greater than 16 MB |
|
434 int chunks = (width * height / (1024 * 1024 * 16)) + 1; |
|
435 int chunkHeight = qMax(height / chunks, 1); |
|
436 |
|
437 int y = 0; |
|
438 while (y < height) { |
|
439 QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_Mono); |
|
440 |
|
441 int chunkStart = y; |
|
442 int chunkEnd = y + chunk.height(); |
|
443 while (y < chunkEnd) { |
|
444 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) { |
|
445 TIFFClose(tiff); |
|
446 return false; |
|
447 } |
|
448 ++y; |
|
449 } |
|
450 } |
|
451 TIFFClose(tiff); |
|
452 } else if (format == QImage::Format_Indexed8) { |
|
453 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE) |
|
454 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_PACKBITS) |
|
455 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) { |
|
456 TIFFClose(tiff); |
|
457 return false; |
|
458 } |
|
459 //// write the color table |
|
460 // allocate the color tables |
|
461 uint16 *redTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16))); |
|
462 uint16 *greenTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16))); |
|
463 uint16 *blueTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16))); |
|
464 if (!redTable || !greenTable || !blueTable) { |
|
465 TIFFClose(tiff); |
|
466 return false; |
|
467 } |
|
468 |
|
469 // set the color table |
|
470 const QVector<QRgb> colorTable = image.colorTable(); |
|
471 |
|
472 const int tableSize = colorTable.size(); |
|
473 Q_ASSERT(tableSize <= 256); |
|
474 for (int i = 0; i<tableSize; ++i) { |
|
475 const QRgb color = colorTable.at(i); |
|
476 redTable[i] = qRed(color) * 257; |
|
477 greenTable[i] = qGreen(color) * 257; |
|
478 blueTable[i] = qBlue(color) * 257; |
|
479 } |
|
480 |
|
481 const bool setColorTableSuccess = TIFFSetField(tiff, TIFFTAG_COLORMAP, redTable, greenTable, blueTable); |
|
482 |
|
483 qFree(redTable); |
|
484 qFree(greenTable); |
|
485 qFree(blueTable); |
|
486 |
|
487 if (!setColorTableSuccess) { |
|
488 TIFFClose(tiff); |
|
489 return false; |
|
490 } |
|
491 |
|
492 //// write the data |
|
493 // try to do the conversion in chunks no greater than 16 MB |
|
494 int chunks = (width * height/ (1024 * 1024 * 16)) + 1; |
|
495 int chunkHeight = qMax(height / chunks, 1); |
|
496 |
|
497 int y = 0; |
|
498 while (y < height) { |
|
499 QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)); |
|
500 |
|
501 int chunkStart = y; |
|
502 int chunkEnd = y + chunk.height(); |
|
503 while (y < chunkEnd) { |
|
504 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) { |
|
505 TIFFClose(tiff); |
|
506 return false; |
|
507 } |
|
508 ++y; |
|
509 } |
|
510 } |
|
511 TIFFClose(tiff); |
|
512 |
|
513 } else { |
|
514 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB) |
|
515 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW) |
|
516 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4) |
|
517 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) { |
|
518 TIFFClose(tiff); |
|
519 return false; |
|
520 } |
|
521 // try to do the ARGB32 conversion in chunks no greater than 16 MB |
|
522 int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1; |
|
523 int chunkHeight = qMax(height / chunks, 1); |
|
524 |
|
525 int y = 0; |
|
526 while (y < height) { |
|
527 QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_ARGB32); |
|
528 |
|
529 int chunkStart = y; |
|
530 int chunkEnd = y + chunk.height(); |
|
531 while (y < chunkEnd) { |
|
532 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) |
|
533 convert32BitOrder(chunk.scanLine(y - chunkStart), width); |
|
534 else |
|
535 convert32BitOrderBigEndian(chunk.scanLine(y - chunkStart), width); |
|
536 |
|
537 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) { |
|
538 TIFFClose(tiff); |
|
539 return false; |
|
540 } |
|
541 ++y; |
|
542 } |
|
543 } |
|
544 TIFFClose(tiff); |
|
545 } |
|
546 |
|
547 return true; |
|
548 } |
|
549 |
|
550 QByteArray QTiffHandler::name() const |
|
551 { |
|
552 return "tiff"; |
|
553 } |
|
554 |
|
555 QVariant QTiffHandler::option(ImageOption option) const |
|
556 { |
|
557 if (option == Size && canRead()) { |
|
558 QSize imageSize; |
|
559 qint64 pos = device()->pos(); |
|
560 TIFF *tiff = TIFFClientOpen("foo", |
|
561 "r", |
|
562 const_cast<QTiffHandler*>(this), |
|
563 qtiffReadProc, |
|
564 qtiffWriteProc, |
|
565 qtiffSeekProc, |
|
566 qtiffCloseProc, |
|
567 qtiffSizeProc, |
|
568 qtiffMapProc, |
|
569 qtiffUnmapProc); |
|
570 |
|
571 if (tiff) { |
|
572 uint32 width = 0; |
|
573 uint32 height = 0; |
|
574 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width); |
|
575 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height); |
|
576 imageSize = QSize(width, height); |
|
577 } |
|
578 device()->seek(pos); |
|
579 if (imageSize.isValid()) |
|
580 return imageSize; |
|
581 } else if (option == CompressionRatio) { |
|
582 return compression; |
|
583 } else if (option == ImageFormat) { |
|
584 return QImage::Format_ARGB32; |
|
585 } |
|
586 return QVariant(); |
|
587 } |
|
588 |
|
589 void QTiffHandler::setOption(ImageOption option, const QVariant &value) |
|
590 { |
|
591 if (option == CompressionRatio && value.type() == QVariant::Int) |
|
592 compression = value.toInt(); |
|
593 } |
|
594 |
|
595 bool QTiffHandler::supportsOption(ImageOption option) const |
|
596 { |
|
597 return option == CompressionRatio |
|
598 || option == Size |
|
599 || option == ImageFormat; |
|
600 } |
|
601 |
|
602 void QTiffHandler::convert32BitOrder(void *buffer, int width) |
|
603 { |
|
604 uint32 *target = reinterpret_cast<uint32 *>(buffer); |
|
605 for (int32 x=0; x<width; ++x) { |
|
606 uint32 p = target[x]; |
|
607 // convert between ARGB and ABGR |
|
608 target[x] = (p & 0xff000000) |
|
609 | ((p & 0x00ff0000) >> 16) |
|
610 | (p & 0x0000ff00) |
|
611 | ((p & 0x000000ff) << 16); |
|
612 } |
|
613 } |
|
614 |
|
615 void QTiffHandler::convert32BitOrderBigEndian(void *buffer, int width) |
|
616 { |
|
617 uint32 *target = reinterpret_cast<uint32 *>(buffer); |
|
618 for (int32 x=0; x<width; ++x) { |
|
619 uint32 p = target[x]; |
|
620 target[x] = (p & 0xff000000) >> 24 |
|
621 | (p & 0x00ff0000) << 8 |
|
622 | (p & 0x0000ff00) << 8 |
|
623 | (p & 0x000000ff) << 8; |
|
624 } |
|
625 } |
|
626 |
|
627 QT_END_NAMESPACE |