source: trunk/src/gui/image/qppmhandler.cpp@ 855

Last change on this file since 855 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** 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 have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qppmhandler_p.h"
43
44#ifndef QT_NO_IMAGEFORMAT_PPM
45
46#include <qimage.h>
47#include <qvariant.h>
48#include <qvector.h>
49#include <ctype.h>
50
51QT_BEGIN_NAMESPACE
52
53/*****************************************************************************
54 PBM/PGM/PPM (ASCII and RAW) image read/write functions
55 *****************************************************************************/
56
57static int read_pbm_int(QIODevice *d)
58{
59 char c;
60 int val = -1;
61 bool digit;
62 const int buflen = 100;
63 char buf[buflen];
64 for (;;) {
65 if (!d->getChar(&c)) // end of file
66 break;
67 digit = isdigit((uchar) c);
68 if (val != -1) {
69 if (digit) {
70 val = 10*val + c - '0';
71 continue;
72 } else {
73 if (c == '#') // comment
74 d->readLine(buf, buflen);
75 break;
76 }
77 }
78 if (digit) // first digit
79 val = c - '0';
80 else if (isspace((uchar) c))
81 continue;
82 else if (c == '#')
83 (void)d->readLine(buf, buflen);
84 else
85 break;
86 }
87 return val;
88}
89
90static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
91{
92 char buf[3];
93 if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
94 return false;
95
96 if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2])))
97 return false;
98
99 type = buf[1];
100 if (type < '1' || type > '6')
101 return false;
102
103 w = read_pbm_int(device); // get image width
104 h = read_pbm_int(device); // get image height
105
106 if (type == '1' || type == '4')
107 mcc = 1; // ignore max color component
108 else
109 mcc = read_pbm_int(device); // get max color component
110
111 if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0)
112 return false; // weird P.M image
113
114 return true;
115}
116
117static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
118{
119 int nbits, y;
120 int pbm_bpl;
121 bool raw;
122
123 QImage::Format format;
124 switch (type) {
125 case '1': // ascii PBM
126 case '4': // raw PBM
127 nbits = 1;
128 format = QImage::Format_Mono;
129 break;
130 case '2': // ascii PGM
131 case '5': // raw PGM
132 nbits = 8;
133 format = QImage::Format_Indexed8;
134 break;
135 case '3': // ascii PPM
136 case '6': // raw PPM
137 nbits = 32;
138 format = QImage::Format_RGB32;
139 break;
140 default:
141 return false;
142 }
143 raw = type >= '4';
144
145 int maxc = mcc;
146 if (maxc > 255)
147 maxc = 255;
148 if (outImage->size() != QSize(w, h) || outImage->format() != format) {
149 *outImage = QImage(w, h, format);
150 if (outImage->isNull())
151 return false;
152 }
153
154 pbm_bpl = (nbits*w+7)/8; // bytes per scanline in PBM
155
156 if (raw) { // read raw data
157 if (nbits == 32) { // type 6
158 pbm_bpl = mcc < 256 ? 3*w : 6*w;
159 uchar *buf24 = new uchar[pbm_bpl], *b;
160 QRgb *p;
161 QRgb *end;
162 for (y=0; y<h; y++) {
163 if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
164 delete[] buf24;
165 return false;
166 }
167 p = (QRgb *)outImage->scanLine(y);
168 end = p + w;
169 b = buf24;
170 while (p < end) {
171 if (mcc < 256) {
172 *p++ = qRgb(b[0],b[1],b[2]);
173 b += 3;
174 } else {
175 *p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
176 ((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
177 ((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
178 b += 6;
179 }
180 }
181 }
182 delete[] buf24;
183 } else { // type 4,5
184 for (y=0; y<h; y++) {
185 if (device->read((char *)outImage->scanLine(y), pbm_bpl)
186 != pbm_bpl)
187 return false;
188 }
189 }
190 } else { // read ascii data
191 register uchar *p;
192 int n;
193 for (y=0; y<h; y++) {
194 p = outImage->scanLine(y);
195 n = pbm_bpl;
196 if (nbits == 1) {
197 int b;
198 int bitsLeft = w;
199 while (n--) {
200 b = 0;
201 for (int i=0; i<8; i++) {
202 if (i < bitsLeft)
203 b = (b << 1) | (read_pbm_int(device) & 1);
204 else
205 b = (b << 1) | (0 & 1); // pad it our self if we need to
206 }
207 bitsLeft -= 8;
208 *p++ = b;
209 }
210 } else if (nbits == 8) {
211 if (mcc == maxc) {
212 while (n--) {
213 *p++ = read_pbm_int(device);
214 }
215 } else {
216 while (n--) {
217 *p++ = read_pbm_int(device) * maxc / mcc;
218 }
219 }
220 } else { // 32 bits
221 n /= 4;
222 int r, g, b;
223 if (mcc == maxc) {
224 while (n--) {
225 r = read_pbm_int(device);
226 g = read_pbm_int(device);
227 b = read_pbm_int(device);
228 *((QRgb*)p) = qRgb(r, g, b);
229 p += 4;
230 }
231 } else {
232 while (n--) {
233 r = read_pbm_int(device) * maxc / mcc;
234 g = read_pbm_int(device) * maxc / mcc;
235 b = read_pbm_int(device) * maxc / mcc;
236 *((QRgb*)p) = qRgb(r, g, b);
237 p += 4;
238 }
239 }
240 }
241 }
242 }
243
244 if (nbits == 1) { // bitmap
245 outImage->setColorCount(2);
246 outImage->setColor(0, qRgb(255,255,255)); // white
247 outImage->setColor(1, qRgb(0,0,0)); // black
248 } else if (nbits == 8) { // graymap
249 outImage->setColorCount(maxc+1);
250 for (int i=0; i<=maxc; i++)
251 outImage->setColor(i, qRgb(i*255/maxc,i*255/maxc,i*255/maxc));
252 }
253
254 return true;
255}
256
257static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
258{
259 QByteArray str;
260 QImage image = sourceImage;
261 QByteArray format = sourceFormat;
262
263 format = format.left(3); // ignore RAW part
264 bool gray = format == "pgm";
265
266 if (format == "pbm") {
267 image = image.convertToFormat(QImage::Format_Mono);
268 } else if (image.depth() == 1) {
269 image = image.convertToFormat(QImage::Format_Indexed8);
270 } else {
271 switch (image.format()) {
272 case QImage::Format_RGB16:
273 case QImage::Format_RGB666:
274 case QImage::Format_RGB555:
275 case QImage::Format_RGB888:
276 case QImage::Format_RGB444:
277 image = image.convertToFormat(QImage::Format_RGB32);
278 break;
279 case QImage::Format_ARGB8565_Premultiplied:
280 case QImage::Format_ARGB6666_Premultiplied:
281 case QImage::Format_ARGB8555_Premultiplied:
282 case QImage::Format_ARGB4444_Premultiplied:
283 image = image.convertToFormat(QImage::Format_ARGB32);
284 break;
285 default:
286 break;
287 }
288 }
289
290 if (image.depth() == 1 && image.colorCount() == 2) {
291 if (qGray(image.color(0)) < qGray(image.color(1))) {
292 // 0=dark/black, 1=light/white - invert
293 image.detach();
294 for (int y=0; y<image.height(); y++) {
295 uchar *p = image.scanLine(y);
296 uchar *end = p + image.bytesPerLine();
297 while (p < end)
298 *p++ ^= 0xff;
299 }
300 }
301 }
302
303 uint w = image.width();
304 uint h = image.height();
305
306 str = "P\n";
307 str += QByteArray::number(w);
308 str += ' ';
309 str += QByteArray::number(h);
310 str += '\n';
311
312 switch (image.depth()) {
313 case 1: {
314 str.insert(1, '4');
315 if (out->write(str, str.length()) != str.length())
316 return false;
317 w = (w+7)/8;
318 for (uint y=0; y<h; y++) {
319 uchar* line = image.scanLine(y);
320 if (w != (uint)out->write((char*)line, w))
321 return false;
322 }
323 }
324 break;
325
326 case 8: {
327 str.insert(1, gray ? '5' : '6');
328 str.append("255\n");
329 if (out->write(str, str.length()) != str.length())
330 return false;
331 QVector<QRgb> color = image.colorTable();
332 uint bpl = w*(gray ? 1 : 3);
333 uchar *buf = new uchar[bpl];
334 for (uint y=0; y<h; y++) {
335 uchar *b = image.scanLine(y);
336 uchar *p = buf;
337 uchar *end = buf+bpl;
338 if (gray) {
339 while (p < end) {
340 uchar g = (uchar)qGray(color[*b++]);
341 *p++ = g;
342 }
343 } else {
344 while (p < end) {
345 QRgb rgb = color[*b++];
346 *p++ = qRed(rgb);
347 *p++ = qGreen(rgb);
348 *p++ = qBlue(rgb);
349 }
350 }
351 if (bpl != (uint)out->write((char*)buf, bpl))
352 return false;
353 }
354 delete [] buf;
355 }
356 break;
357
358 case 32: {
359 str.insert(1, gray ? '5' : '6');
360 str.append("255\n");
361 if (out->write(str, str.length()) != str.length())
362 return false;
363 uint bpl = w*(gray ? 1 : 3);
364 uchar *buf = new uchar[bpl];
365 for (uint y=0; y<h; y++) {
366 QRgb *b = (QRgb*)image.scanLine(y);
367 uchar *p = buf;
368 uchar *end = buf+bpl;
369 if (gray) {
370 while (p < end) {
371 uchar g = (uchar)qGray(*b++);
372 *p++ = g;
373 }
374 } else {
375 while (p < end) {
376 QRgb rgb = *b++;
377 *p++ = qRed(rgb);
378 *p++ = qGreen(rgb);
379 *p++ = qBlue(rgb);
380 }
381 }
382 if (bpl != (uint)out->write((char*)buf, bpl))
383 return false;
384 }
385 delete [] buf;
386 }
387 break;
388
389 default:
390 return false;
391 }
392
393 return true;
394}
395
396QPpmHandler::QPpmHandler()
397 : state(Ready)
398{
399}
400
401bool QPpmHandler::readHeader()
402{
403 state = Error;
404 if (!read_pbm_header(device(), type, width, height, mcc))
405 return false;
406 state = ReadHeader;
407 return true;
408}
409
410bool QPpmHandler::canRead() const
411{
412 if (state == Ready && !canRead(device(), &subType))
413 return false;
414
415 if (state != Error) {
416 setFormat(subType);
417 return true;
418 }
419
420 return false;
421}
422
423bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType)
424{
425 if (!device) {
426 qWarning("QPpmHandler::canRead() called with no device");
427 return false;
428 }
429
430 char head[2];
431 if (device->peek(head, sizeof(head)) != sizeof(head))
432 return false;
433
434 if (head[0] != 'P')
435 return false;
436
437 if (head[1] == '1' || head[1] == '4') {
438 if (subType)
439 *subType = "pbm";
440 } else if (head[1] == '2' || head[1] == '5') {
441 if (subType)
442 *subType = "pgm";
443 } else if (head[1] == '3' || head[1] == '6') {
444 if (subType)
445 *subType = "ppm";
446 } else {
447 return false;
448 }
449 return true;
450}
451
452bool QPpmHandler::read(QImage *image)
453{
454 if (state == Error)
455 return false;
456
457 if (state == Ready && !readHeader()) {
458 state = Error;
459 return false;
460 }
461
462 if (!read_pbm_body(device(), type, width, height, mcc, image)) {
463 state = Error;
464 return false;
465 }
466
467 state = Ready;
468 return true;
469}
470
471bool QPpmHandler::write(const QImage &image)
472{
473 return write_pbm_image(device(), image, subType);
474}
475
476bool QPpmHandler::supportsOption(ImageOption option) const
477{
478 return option == SubType
479 || option == Size
480 || option == ImageFormat;
481}
482
483QVariant QPpmHandler::option(ImageOption option) const
484{
485 if (option == SubType) {
486 return subType;
487 } else if (option == Size) {
488 if (state == Error)
489 return QVariant();
490 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
491 return QVariant();
492 return QSize(width, height);
493 } else if (option == ImageFormat) {
494 if (state == Error)
495 return QVariant();
496 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
497 return QVariant();
498 QImage::Format format = QImage::Format_Invalid;
499 switch (type) {
500 case '1': // ascii PBM
501 case '4': // raw PBM
502 format = QImage::Format_Mono;
503 break;
504 case '2': // ascii PGM
505 case '5': // raw PGM
506 format = QImage::Format_Indexed8;
507 break;
508 case '3': // ascii PPM
509 case '6': // raw PPM
510 format = QImage::Format_RGB32;
511 break;
512 default:
513 break;
514 }
515 return format;
516 }
517 return QVariant();
518}
519
520void QPpmHandler::setOption(ImageOption option, const QVariant &value)
521{
522 if (option == SubType)
523 subType = value.toByteArray().toLower();
524}
525
526QByteArray QPpmHandler::name() const
527{
528 return subType.isEmpty() ? QByteArray("ppm") : subType;
529}
530
531QT_END_NAMESPACE
532
533#endif // QT_NO_IMAGEFORMAT_PPM
Note: See TracBrowser for help on using the repository browser.