summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qppmhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image/qppmhandler.cpp')
-rw-r--r--src/gui/image/qppmhandler.cpp533
1 files changed, 533 insertions, 0 deletions
diff --git a/src/gui/image/qppmhandler.cpp b/src/gui/image/qppmhandler.cpp
new file mode 100644
index 0000000000..72b38222a0
--- /dev/null
+++ b/src/gui/image/qppmhandler.cpp
@@ -0,0 +1,533 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qppmhandler_p.h"
+
+#ifndef QT_NO_IMAGEFORMAT_PPM
+
+#include <qimage.h>
+#include <qvariant.h>
+#include <qvector.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ PBM/PGM/PPM (ASCII and RAW) image read/write functions
+ *****************************************************************************/
+
+static int read_pbm_int(QIODevice *d)
+{
+ char c;
+ int val = -1;
+ bool digit;
+ const int buflen = 100;
+ char buf[buflen];
+ for (;;) {
+ if (!d->getChar(&c)) // end of file
+ break;
+ digit = isdigit((uchar) c);
+ if (val != -1) {
+ if (digit) {
+ val = 10*val + c - '0';
+ continue;
+ } else {
+ if (c == '#') // comment
+ d->readLine(buf, buflen);
+ break;
+ }
+ }
+ if (digit) // first digit
+ val = c - '0';
+ else if (isspace((uchar) c))
+ continue;
+ else if (c == '#')
+ (void)d->readLine(buf, buflen);
+ else
+ break;
+ }
+ return val;
+}
+
+static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
+{
+ char buf[3];
+ if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
+ return false;
+
+ if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2])))
+ return false;
+
+ type = buf[1];
+ if (type < '1' || type > '6')
+ return false;
+
+ w = read_pbm_int(device); // get image width
+ h = read_pbm_int(device); // get image height
+
+ if (type == '1' || type == '4')
+ mcc = 1; // ignore max color component
+ else
+ mcc = read_pbm_int(device); // get max color component
+
+ if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0)
+ return false; // weird P.M image
+
+ return true;
+}
+
+static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
+{
+ int nbits, y;
+ int pbm_bpl;
+ bool raw;
+
+ QImage::Format format;
+ switch (type) {
+ case '1': // ascii PBM
+ case '4': // raw PBM
+ nbits = 1;
+ format = QImage::Format_Mono;
+ break;
+ case '2': // ascii PGM
+ case '5': // raw PGM
+ nbits = 8;
+ format = QImage::Format_Indexed8;
+ break;
+ case '3': // ascii PPM
+ case '6': // raw PPM
+ nbits = 32;
+ format = QImage::Format_RGB32;
+ break;
+ default:
+ return false;
+ }
+ raw = type >= '4';
+
+ int maxc = mcc;
+ if (maxc > 255)
+ maxc = 255;
+ if (outImage->size() != QSize(w, h) || outImage->format() != format) {
+ *outImage = QImage(w, h, format);
+ if (outImage->isNull())
+ return false;
+ }
+
+ pbm_bpl = (nbits*w+7)/8; // bytes per scanline in PBM
+
+ if (raw) { // read raw data
+ if (nbits == 32) { // type 6
+ pbm_bpl = mcc < 256 ? 3*w : 6*w;
+ uchar *buf24 = new uchar[pbm_bpl], *b;
+ QRgb *p;
+ QRgb *end;
+ for (y=0; y<h; y++) {
+ if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
+ delete[] buf24;
+ return false;
+ }
+ p = (QRgb *)outImage->scanLine(y);
+ end = p + w;
+ b = buf24;
+ while (p < end) {
+ if (mcc < 256) {
+ *p++ = qRgb(b[0],b[1],b[2]);
+ b += 3;
+ } else {
+ *p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
+ ((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
+ ((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
+ b += 6;
+ }
+ }
+ }
+ delete[] buf24;
+ } else { // type 4,5
+ for (y=0; y<h; y++) {
+ if (device->read((char *)outImage->scanLine(y), pbm_bpl)
+ != pbm_bpl)
+ return false;
+ }
+ }
+ } else { // read ascii data
+ register uchar *p;
+ int n;
+ for (y=0; y<h; y++) {
+ p = outImage->scanLine(y);
+ n = pbm_bpl;
+ if (nbits == 1) {
+ int b;
+ int bitsLeft = w;
+ while (n--) {
+ b = 0;
+ for (int i=0; i<8; i++) {
+ if (i < bitsLeft)
+ b = (b << 1) | (read_pbm_int(device) & 1);
+ else
+ b = (b << 1) | (0 & 1); // pad it our self if we need to
+ }
+ bitsLeft -= 8;
+ *p++ = b;
+ }
+ } else if (nbits == 8) {
+ if (mcc == maxc) {
+ while (n--) {
+ *p++ = read_pbm_int(device);
+ }
+ } else {
+ while (n--) {
+ *p++ = read_pbm_int(device) * maxc / mcc;
+ }
+ }
+ } else { // 32 bits
+ n /= 4;
+ int r, g, b;
+ if (mcc == maxc) {
+ while (n--) {
+ r = read_pbm_int(device);
+ g = read_pbm_int(device);
+ b = read_pbm_int(device);
+ *((QRgb*)p) = qRgb(r, g, b);
+ p += 4;
+ }
+ } else {
+ while (n--) {
+ r = read_pbm_int(device) * maxc / mcc;
+ g = read_pbm_int(device) * maxc / mcc;
+ b = read_pbm_int(device) * maxc / mcc;
+ *((QRgb*)p) = qRgb(r, g, b);
+ p += 4;
+ }
+ }
+ }
+ }
+ }
+
+ if (nbits == 1) { // bitmap
+ outImage->setColorCount(2);
+ outImage->setColor(0, qRgb(255,255,255)); // white
+ outImage->setColor(1, qRgb(0,0,0)); // black
+ } else if (nbits == 8) { // graymap
+ outImage->setColorCount(maxc+1);
+ for (int i=0; i<=maxc; i++)
+ outImage->setColor(i, qRgb(i*255/maxc,i*255/maxc,i*255/maxc));
+ }
+
+ return true;
+}
+
+static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
+{
+ QByteArray str;
+ QImage image = sourceImage;
+ QByteArray format = sourceFormat;
+
+ format = format.left(3); // ignore RAW part
+ bool gray = format == "pgm";
+
+ if (format == "pbm") {
+ image = image.convertToFormat(QImage::Format_Mono);
+ } else if (image.depth() == 1) {
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ } else {
+ switch (image.format()) {
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB444:
+ image = image.convertToFormat(QImage::Format_RGB32);
+ break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (image.depth() == 1 && image.colorCount() == 2) {
+ if (qGray(image.color(0)) < qGray(image.color(1))) {
+ // 0=dark/black, 1=light/white - invert
+ image.detach();
+ for (int y=0; y<image.height(); y++) {
+ uchar *p = image.scanLine(y);
+ uchar *end = p + image.bytesPerLine();
+ while (p < end)
+ *p++ ^= 0xff;
+ }
+ }
+ }
+
+ uint w = image.width();
+ uint h = image.height();
+
+ str = "P\n";
+ str += QByteArray::number(w);
+ str += ' ';
+ str += QByteArray::number(h);
+ str += '\n';
+
+ switch (image.depth()) {
+ case 1: {
+ str.insert(1, '4');
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ w = (w+7)/8;
+ for (uint y=0; y<h; y++) {
+ uchar* line = image.scanLine(y);
+ if (w != (uint)out->write((char*)line, w))
+ return false;
+ }
+ }
+ break;
+
+ case 8: {
+ str.insert(1, gray ? '5' : '6');
+ str.append("255\n");
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ QVector<QRgb> color = image.colorTable();
+ uint bpl = w*(gray ? 1 : 3);
+ uchar *buf = new uchar[bpl];
+ for (uint y=0; y<h; y++) {
+ uchar *b = image.scanLine(y);
+ uchar *p = buf;
+ uchar *end = buf+bpl;
+ if (gray) {
+ while (p < end) {
+ uchar g = (uchar)qGray(color[*b++]);
+ *p++ = g;
+ }
+ } else {
+ while (p < end) {
+ QRgb rgb = color[*b++];
+ *p++ = qRed(rgb);
+ *p++ = qGreen(rgb);
+ *p++ = qBlue(rgb);
+ }
+ }
+ if (bpl != (uint)out->write((char*)buf, bpl))
+ return false;
+ }
+ delete [] buf;
+ }
+ break;
+
+ case 32: {
+ str.insert(1, gray ? '5' : '6');
+ str.append("255\n");
+ if (out->write(str, str.length()) != str.length())
+ return false;
+ uint bpl = w*(gray ? 1 : 3);
+ uchar *buf = new uchar[bpl];
+ for (uint y=0; y<h; y++) {
+ QRgb *b = (QRgb*)image.scanLine(y);
+ uchar *p = buf;
+ uchar *end = buf+bpl;
+ if (gray) {
+ while (p < end) {
+ uchar g = (uchar)qGray(*b++);
+ *p++ = g;
+ }
+ } else {
+ while (p < end) {
+ QRgb rgb = *b++;
+ *p++ = qRed(rgb);
+ *p++ = qGreen(rgb);
+ *p++ = qBlue(rgb);
+ }
+ }
+ if (bpl != (uint)out->write((char*)buf, bpl))
+ return false;
+ }
+ delete [] buf;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+QPpmHandler::QPpmHandler()
+ : state(Ready)
+{
+}
+
+bool QPpmHandler::readHeader()
+{
+ state = Error;
+ if (!read_pbm_header(device(), type, width, height, mcc))
+ return false;
+ state = ReadHeader;
+ return true;
+}
+
+bool QPpmHandler::canRead() const
+{
+ if (state == Ready && !canRead(device(), &subType))
+ return false;
+
+ if (state != Error) {
+ setFormat(subType);
+ return true;
+ }
+
+ return false;
+}
+
+bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType)
+{
+ if (!device) {
+ qWarning("QPpmHandler::canRead() called with no device");
+ return false;
+ }
+
+ char head[2];
+ if (device->peek(head, sizeof(head)) != sizeof(head))
+ return false;
+
+ if (head[0] != 'P')
+ return false;
+
+ if (head[1] == '1' || head[1] == '4') {
+ if (subType)
+ *subType = "pbm";
+ } else if (head[1] == '2' || head[1] == '5') {
+ if (subType)
+ *subType = "pgm";
+ } else if (head[1] == '3' || head[1] == '6') {
+ if (subType)
+ *subType = "ppm";
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool QPpmHandler::read(QImage *image)
+{
+ if (state == Error)
+ return false;
+
+ if (state == Ready && !readHeader()) {
+ state = Error;
+ return false;
+ }
+
+ if (!read_pbm_body(device(), type, width, height, mcc, image)) {
+ state = Error;
+ return false;
+ }
+
+ state = Ready;
+ return true;
+}
+
+bool QPpmHandler::write(const QImage &image)
+{
+ return write_pbm_image(device(), image, subType);
+}
+
+bool QPpmHandler::supportsOption(ImageOption option) const
+{
+ return option == SubType
+ || option == Size
+ || option == ImageFormat;
+}
+
+QVariant QPpmHandler::option(ImageOption option) const
+{
+ if (option == SubType) {
+ return subType;
+ } else if (option == Size) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
+ return QVariant();
+ return QSize(width, height);
+ } else if (option == ImageFormat) {
+ if (state == Error)
+ return QVariant();
+ if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
+ return QVariant();
+ QImage::Format format = QImage::Format_Invalid;
+ switch (type) {
+ case '1': // ascii PBM
+ case '4': // raw PBM
+ format = QImage::Format_Mono;
+ break;
+ case '2': // ascii PGM
+ case '5': // raw PGM
+ format = QImage::Format_Indexed8;
+ break;
+ case '3': // ascii PPM
+ case '6': // raw PPM
+ format = QImage::Format_RGB32;
+ break;
+ default:
+ break;
+ }
+ return format;
+ }
+ return QVariant();
+}
+
+void QPpmHandler::setOption(ImageOption option, const QVariant &value)
+{
+ if (option == SubType)
+ subType = value.toByteArray().toLower();
+}
+
+QByteArray QPpmHandler::name() const
+{
+ return subType.isEmpty() ? QByteArray("ppm") : subType;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IMAGEFORMAT_PPM