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

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

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

File size: 16.5 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 QtGui module 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 "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 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->setNumColors(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->setNumColors(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_MonoLSB);
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.numColors() == 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) {
413 if (!canRead(device(), &subType))
414 return false;
415 setFormat(subType);
416 return true;
417 }
418 return state != Error;
419}
420
421bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType)
422{
423 if (!device) {
424 qWarning("QPpmHandler::canRead() called with no device");
425 return false;
426 }
427
428 char head[2];
429 if (device->peek(head, sizeof(head)) != sizeof(head))
430 return false;
431
432 if (head[0] != 'P')
433 return false;
434
435 if (head[1] == '1' || head[1] == '4') {
436 if (subType)
437 *subType = "pbm";
438 } else if (head[1] == '2' || head[1] == '5') {
439 if (subType)
440 *subType = "pgm";
441 } else if (head[1] == '3' || head[1] == '6') {
442 if (subType)
443 *subType = "ppm";
444 } else {
445 return false;
446 }
447 return true;
448}
449
450bool QPpmHandler::read(QImage *image)
451{
452 if (state == Error)
453 return false;
454
455 if (state == Ready && !readHeader()) {
456 state = Error;
457 return false;
458 }
459
460 if (!read_pbm_body(device(), type, width, height, mcc, image)) {
461 state = Error;
462 return false;
463 }
464
465 state = Ready;
466 return true;
467}
468
469bool QPpmHandler::write(const QImage &image)
470{
471 return write_pbm_image(device(), image, subType);
472}
473
474bool QPpmHandler::supportsOption(ImageOption option) const
475{
476 return option == SubType
477 || option == Size
478 || option == ImageFormat;
479}
480
481QVariant QPpmHandler::option(ImageOption option) const
482{
483 if (option == SubType) {
484 return subType;
485 } else if (option == Size) {
486 if (state == Error)
487 return QVariant();
488 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
489 return QVariant();
490 return QSize(width, height);
491 } else if (option == ImageFormat) {
492 if (state == Error)
493 return QVariant();
494 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
495 return QVariant();
496 QImage::Format format = QImage::Format_Invalid;
497 switch (type) {
498 case '1': // ascii PBM
499 case '4': // raw PBM
500 format = QImage::Format_Mono;
501 break;
502 case '2': // ascii PGM
503 case '5': // raw PGM
504 format = QImage::Format_Indexed8;
505 break;
506 case '3': // ascii PPM
507 case '6': // raw PPM
508 format = QImage::Format_RGB32;
509 break;
510 default:
511 break;
512 }
513 return format;
514 }
515 return QVariant();
516}
517
518void QPpmHandler::setOption(ImageOption option, const QVariant &value)
519{
520 if (option == SubType)
521 subType = value.toByteArray().toLower();
522}
523
524QByteArray QPpmHandler::name() const
525{
526 return subType.isEmpty() ? QByteArray("ppm") : subType;
527}
528
529QT_END_NAMESPACE
530
531#endif // QT_NO_IMAGEFORMAT_PPM
Note: See TracBrowser for help on using the repository browser.