src/gui/image/qimagewriter.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/image/qimagewriter.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,690 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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$
+**
+****************************************************************************/
+
+/*!
+    \class QImageWriter
+    \brief The QImageWriter class provides a format independent interface
+    for writing images to files or other devices.
+
+    \reentrant
+    \ingroup painting
+    \ingroup io
+
+    QImageWriter supports setting format specific options, such as the
+    gamma level, compression level and quality, prior to storing the
+    image. If you do not need such options, you can use QImage::save()
+    or QPixmap::save() instead.
+
+    To store an image, you start by constructing a QImageWriter
+    object.  Pass either a file name or a device pointer, and the
+    image format to QImageWriter's constructor. You can then set
+    several options, such as the gamma level (by calling setGamma())
+    and quality (by calling setQuality()). canWrite() returns true if
+    QImageWriter can write the image (i.e., the image format is
+    supported and the device is open for writing). Call write() to
+    write the image to the device.
+
+    If any error occurs when writing the image, write() will return
+    false. You can then call error() to find the type of error that
+    occurred, or errorString() to get a human readable description of
+    what went wrong.
+
+    Call supportedImageFormats() for a list of formats that
+    QImageWriter can write. QImageWriter supports all built-in image
+    formats, in addition to any image format plugins that support
+    writing.
+
+    \sa QImageReader, QImageIOHandler, QImageIOPlugin
+*/
+
+/*!
+    \enum QImageWriter::ImageWriterError
+
+    This enum describes errors that can occur when writing images with
+    QImageWriter.
+
+    \value DeviceError QImageWriter encountered a device error when
+    writing the image data. Consult your device for more details on
+    what went wrong.
+
+    \value UnsupportedFormatError Qt does not support the requested
+    image format.
+
+    \value UnknownError An unknown error occurred. If you get this
+    value after calling write(), it is most likely caused by a bug in
+    QImageWriter.
+*/
+
+#include "qimagewriter.h"
+
+#include <qbytearray.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qimageiohandler.h>
+#include <qset.h>
+#include <qvariant.h>
+
+// factory loader
+#include <qcoreapplication.h>
+#include <private/qfactoryloader_p.h>
+
+// image handlers
+#include <private/qbmphandler_p.h>
+#include <private/qppmhandler_p.h>
+#include <private/qxbmhandler_p.h>
+#include <private/qxpmhandler_p.h>
+#ifndef QT_NO_IMAGEFORMAT_PNG
+#include <private/qpnghandler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+                          (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))
+#endif
+
+static QImageIOHandler *createWriteHandlerHelper(QIODevice *device,
+    const QByteArray &format)
+{
+    QByteArray form = format.toLower();
+    QByteArray suffix;
+    QImageIOHandler *handler = 0;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+    // check if any plugins can write the image
+    QFactoryLoader *l = loader();
+    QStringList keys = l->keys();
+    int suffixPluginIndex = -1;
+#endif
+
+    if (device && format.isEmpty()) {
+        // if there's no format, see if \a device is a file, and if so, find
+        // the file suffix and find support for that format among our plugins.
+        // this allows plugins to override our built-in handlers.
+        if (QFile *file = qobject_cast<QFile *>(device)) {
+            if (!(suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1()).isEmpty()) {
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+                int index = keys.indexOf(QString::fromLatin1(suffix));
+                if (index != -1)
+                    suffixPluginIndex = index;
+#endif
+            }
+        }
+    }
+
+    QByteArray testFormat = !form.isEmpty() ? form : suffix;
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+    if (suffixPluginIndex != -1) {
+        // when format is missing, check if we can find a plugin for the
+        // suffix.
+        QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(QString::fromLatin1(suffix)));
+        if (plugin && (plugin->capabilities(device, suffix) & QImageIOPlugin::CanWrite))
+            handler = plugin->create(device, suffix);
+    }
+#endif // Q_NO_LIBRARY
+
+    // check if any built-in handlers can write the image
+    if (!handler && !testFormat.isEmpty()) {
+        if (false) {
+#ifndef QT_NO_IMAGEFORMAT_PNG
+        } else if (testFormat == "png") {
+            handler = new QPngHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_BMP
+        } else if (testFormat == "bmp") {
+            handler = new QBmpHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+        } else if (testFormat == "xpm") {
+            handler = new QXpmHandler;
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+        } else if (testFormat == "xbm") {
+            handler = new QXbmHandler;
+            handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PPM
+        } else if (testFormat == "pbm" || testFormat == "pbmraw" || testFormat == "pgm"
+                 || testFormat == "pgmraw" || testFormat == "ppm" || testFormat == "ppmraw") {
+            handler = new QPpmHandler;
+            handler->setOption(QImageIOHandler::SubType, testFormat);
+#endif
+        }
+    }
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+    if (!testFormat.isEmpty()) {
+        for (int i = 0; i < keys.size(); ++i) {
+            QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+            if (plugin && (plugin->capabilities(device, testFormat) & QImageIOPlugin::CanWrite)) {
+                handler = plugin->create(device, testFormat);
+                break;
+            }
+        }
+    }
+#endif
+
+    if (!handler)
+        return 0;
+
+    handler->setDevice(device);
+    if (!testFormat.isEmpty())
+        handler->setFormat(testFormat);
+    return handler;
+}
+
+class QImageWriterPrivate
+{
+public:
+    QImageWriterPrivate(QImageWriter *qq);
+
+    // device
+    QByteArray format;
+    QIODevice *device;
+    bool deleteDevice;
+    QImageIOHandler *handler;
+
+    // image options
+    int quality;
+    int compression;
+    float gamma;
+    QString description;
+    QString text;
+
+    // error
+    QImageWriter::ImageWriterError imageWriterError;
+    QString errorString;
+
+    QImageWriter *q;
+};
+
+/*!
+    \internal
+*/
+QImageWriterPrivate::QImageWriterPrivate(QImageWriter *qq)
+{
+    device = 0;
+    deleteDevice = false;
+    handler = 0;
+    quality = -1;
+    compression = 0;
+    gamma = 0.0;
+    imageWriterError = QImageWriter::UnknownError;
+    errorString = QT_TRANSLATE_NOOP(QImageWriter, QLatin1String("Unknown error"));
+
+    q = qq;
+}
+
+/*!
+    Constructs an empty QImageWriter object. Before writing, you must
+    call setFormat() to set an image format, then setDevice() or
+    setFileName().
+*/
+QImageWriter::QImageWriter()
+    : d(new QImageWriterPrivate(this))
+{
+}
+
+/*!
+    Constructs a QImageWriter object using the device \a device and
+    image format \a format.
+*/
+QImageWriter::QImageWriter(QIODevice *device, const QByteArray &format)
+    : d(new QImageWriterPrivate(this))
+{
+    d->device = device;
+    d->format = format;
+}
+
+/*!
+    Constructs a QImageWriter objects that will write to a file with
+    the name \a fileName, using the image format \a format. If \a
+    format is not provided, QImageWriter will detect the image format
+    by inspecting the extension of \a fileName.
+*/
+QImageWriter::QImageWriter(const QString &fileName, const QByteArray &format)
+    : d(new QImageWriterPrivate(this))
+{
+    QFile *file = new QFile(fileName);
+    d->device = file;
+    d->deleteDevice = true;
+    d->format = format;
+}
+
+/*!
+    Destructs the QImageWriter object.
+*/
+QImageWriter::~QImageWriter()
+{
+    if (d->deleteDevice)
+        delete d->device;
+    delete d->handler;
+    delete d;
+}
+
+/*!
+    Sets the format QImageWriter will use when writing images, to \a
+    format. \a format is a case insensitive text string. Example:
+
+    \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 0
+
+    You can call supportedImageFormats() for the full list of formats
+    QImageWriter supports.
+
+    \sa format()
+*/
+void QImageWriter::setFormat(const QByteArray &format)
+{
+    d->format = format;
+}
+
+/*!
+    Returns the format QImageWriter uses for writing images.
+
+    \sa setFormat()
+*/
+QByteArray QImageWriter::format() const
+{
+    return d->format;
+}
+
+/*!
+    Sets QImageWriter's device to \a device. If a device has already
+    been set, the old device is removed from QImageWriter and is
+    otherwise left unchanged.
+
+    If the device is not already open, QImageWriter will attempt to
+    open the device in \l QIODevice::WriteOnly mode by calling
+    open(). Note that this does not work for certain devices, such as
+    QProcess, QTcpSocket and QUdpSocket, where more logic is required
+    to open the device.
+
+    \sa device(), setFileName()
+*/
+void QImageWriter::setDevice(QIODevice *device)
+{
+    if (d->device && d->deleteDevice)
+        delete d->device;
+
+    d->device = device;
+    d->deleteDevice = false;
+    delete d->handler;
+    d->handler = 0;
+}
+
+/*!
+    Returns the device currently assigned to QImageWriter, or 0 if no
+    device has been assigned.
+*/
+QIODevice *QImageWriter::device() const
+{
+    return d->device;
+}
+
+/*!
+    Sets the file name of QImageWriter to \a fileName. Internally,
+    QImageWriter will create a QFile and open it in \l
+    QIODevice::WriteOnly mode, and use this file when writing images.
+
+    \sa fileName(), setDevice()
+*/
+void QImageWriter::setFileName(const QString &fileName)
+{
+    setDevice(new QFile(fileName));
+    d->deleteDevice = true;
+}
+
+/*!
+    If the currently assigned device is a QFile, or if setFileName()
+    has been called, this function returns the name of the file
+    QImageWriter writes to. Otherwise (i.e., if no device has been
+    assigned or the device is not a QFile), an empty QString is
+    returned.
+
+    \sa setFileName(), setDevice()
+*/
+QString QImageWriter::fileName() const
+{
+    QFile *file = qobject_cast<QFile *>(d->device);
+    return file ? file->fileName() : QString();
+}
+
+/*!
+    This is an image format specific function that sets the quality
+    level of the image to \a quality. For image formats that do not
+    support setting the quality, this value is ignored.
+
+    The value range of \a quality depends on the image format. For
+    example, the "jpeg" format supports a quality range from 0 (low
+    quality, high compression) to 100 (high quality, low compression).
+
+    \sa quality()
+*/
+void QImageWriter::setQuality(int quality)
+{
+    d->quality = quality;
+}
+
+/*!
+    Returns the quality level of the image.
+
+    \sa setQuality()
+*/
+int QImageWriter::quality() const
+{
+    return d->quality;
+}
+
+/*!
+    This is an image format specific function that set the compression
+    of an image. For image formats that do not support setting the
+    compression, this value is ignored.
+
+    The value range of \a compression depends on the image format. For
+    example, the "tiff" format supports two values, 0(no compression) and
+    1(LZW-compression).
+
+    \sa compression()
+*/
+void QImageWriter::setCompression(int compression)
+{
+    d->compression = compression;
+}
+
+/*!
+    Returns the compression of the image.
+
+    \sa setCompression()
+*/
+int QImageWriter::compression() const
+{
+    return d->compression;
+}
+
+/*!
+    This is an image format specific function that sets the gamma
+    level of the image to \a gamma. For image formats that do not
+    support setting the gamma level, this value is ignored.
+
+    The value range of \a gamma depends on the image format. For
+    example, the "png" format supports a gamma range from 0.0 to 1.0.
+
+    \sa quality()
+*/
+void QImageWriter::setGamma(float gamma)
+{
+    d->gamma = gamma;
+}
+
+/*!
+    Returns the gamma level of the image.
+
+    \sa setGamma()
+*/
+float QImageWriter::gamma() const
+{
+    return d->gamma;
+}
+
+/*!
+    \obsolete
+
+    Use setText() instead.
+
+    This is an image format specific function that sets the
+    description of the image to \a description. For image formats that
+    do not support setting the description, this value is ignored.
+
+    The contents of \a description depends on the image format.
+
+    \sa description()
+*/
+void QImageWriter::setDescription(const QString &description)
+{
+    d->description = description;
+}
+
+/*!
+    \obsolete
+
+    Use QImageReader::text() instead.
+
+    Returns the description of the image.
+
+    \sa setDescription()
+*/
+QString QImageWriter::description() const
+{
+    return d->description;
+}
+
+/*!
+    \since 4.1
+
+    Sets the image text associated with the key \a key to
+    \a text. This is useful for storing copyright information
+    or other information about the image. Example:
+
+    \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 1
+
+    If you want to store a single block of data
+    (e.g., a comment), you can pass an empty key, or use
+    a generic key like "Description".
+
+    The key and text will be embedded into the
+    image data after calling write().
+
+    Support for this option is implemented through
+    QImageIOHandler::Description.
+
+    \sa QImage::setText(), QImageReader::text()
+*/
+void QImageWriter::setText(const QString &key, const QString &text)
+{
+    if (!d->description.isEmpty())
+        d->description += QLatin1String("\n\n");
+    d->description += key.simplified() + QLatin1String(": ") + text.simplified();
+}
+
+/*!
+    Returns true if QImageWriter can write the image; i.e., the image
+    format is supported and the assigned device is open for reading.
+
+    \sa write(), setDevice(), setFormat()
+*/
+bool QImageWriter::canWrite() const
+{
+    if (d->device && !d->handler && (d->handler = createWriteHandlerHelper(d->device, d->format)) == 0) {
+        d->imageWriterError = QImageWriter::UnsupportedFormatError;
+        d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+                                           QLatin1String("Unsupported image format"));
+        return false;
+    }
+    if (d->device && !d->device->isOpen())
+        d->device->open(QIODevice::WriteOnly);
+    if (!d->device || !d->device->isWritable()) {
+        d->imageWriterError = QImageWriter::DeviceError;
+        d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+                                           QLatin1String("Device not writable"));
+        return false;
+    }
+    return true;
+}
+
+/*!
+    Writes the image \a image to the assigned device or file
+    name. Returns true on success; otherwise returns false. If the
+    operation fails, you can call error() to find the type of error
+    that occurred, or errorString() to get a human readable
+    description of the error.
+
+    \sa canWrite(), error(), errorString()
+*/
+bool QImageWriter::write(const QImage &image)
+{
+    if (!canWrite())
+        return false;
+
+    if (d->handler->supportsOption(QImageIOHandler::Quality))
+        d->handler->setOption(QImageIOHandler::Quality, d->quality);
+    if (d->handler->supportsOption(QImageIOHandler::CompressionRatio))
+        d->handler->setOption(QImageIOHandler::CompressionRatio, d->compression);
+    if (d->handler->supportsOption(QImageIOHandler::Gamma))
+        d->handler->setOption(QImageIOHandler::Gamma, d->gamma);
+    if (!d->description.isEmpty() && d->handler->supportsOption(QImageIOHandler::Description))
+        d->handler->setOption(QImageIOHandler::Description, d->description);
+
+    if (!d->handler->write(image))
+        return false;
+    if (QFile *file = qobject_cast<QFile *>(d->device))
+        file->flush();
+    return true;
+}
+
+/*!
+    Returns the type of error that last occurred.
+
+    \sa ImageWriterError, errorString()
+*/
+QImageWriter::ImageWriterError QImageWriter::error() const
+{
+    return d->imageWriterError;
+}
+
+/*!
+    Returns a human readable description of the last error that occurred.
+
+    \sa error()
+*/
+QString QImageWriter::errorString() const
+{
+    return d->errorString;
+}
+
+/*!
+    \since 4.2
+
+    Returns true if the writer supports \a option; otherwise returns
+    false.
+
+    Different image formats support different options. Call this function to
+    determine whether a certain option is supported by the current format. For
+    example, the PNG format allows you to embed text into the image's metadata
+    (see text()).
+
+    \snippet doc/src/snippets/code/src_gui_image_qimagewriter.cpp 2
+
+    Options can be tested after the writer has been associated with a format.
+
+    \sa QImageReader::supportsOption(), setFormat()
+*/
+bool QImageWriter::supportsOption(QImageIOHandler::ImageOption option) const
+{
+    if (!d->handler && (d->handler = createWriteHandlerHelper(d->device, d->format)) == 0) {
+        d->imageWriterError = QImageWriter::UnsupportedFormatError;
+        d->errorString = QT_TRANSLATE_NOOP(QImageWriter,
+                                           QLatin1String("Unsupported image format"));
+        return false;
+    }
+
+    return d->handler->supportsOption(option);
+}
+
+/*!
+    Returns the list of image formats supported by QImageWriter.
+
+    By default, Qt can write the following formats:
+
+    \table
+    \header \o Format \o Description
+    \row    \o BMP    \o Windows Bitmap
+    \row    \o JPG    \o Joint Photographic Experts Group
+    \row    \o JPEG   \o Joint Photographic Experts Group
+    \row    \o PNG    \o Portable Network Graphics
+    \row    \o PPM    \o Portable Pixmap
+    \row    \o TIFF   \o Tagged Image File Format
+    \row    \o XBM    \o X11 Bitmap
+    \row    \o XPM    \o X11 Pixmap
+    \endtable
+
+    Reading and writing SVG files is supported through Qt's
+    \l{QtSvg Module}{SVG Module}.
+
+    \sa setFormat(), QImageReader::supportedImageFormats(), QImageIOPlugin
+*/
+QList<QByteArray> QImageWriter::supportedImageFormats()
+{
+    QSet<QByteArray> formats;
+    formats << "bmp";
+#ifndef QT_NO_IMAGEFORMAT_PPM
+    formats << "ppm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XBM
+    formats << "xbm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_XPM
+    formats << "xpm";
+#endif
+#ifndef QT_NO_IMAGEFORMAT_PNG
+    formats << "png";
+#endif
+
+#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
+    QFactoryLoader *l = loader();
+    QStringList keys = l->keys();
+    for (int i = 0; i < keys.count(); ++i) {
+        QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(keys.at(i)));
+        if (plugin && (plugin->capabilities(0, keys.at(i).toLatin1()) & QImageIOPlugin::CanWrite) != 0)
+            formats << keys.at(i).toLatin1();
+    }
+#endif // QT_NO_LIBRARY
+
+    QList<QByteArray> sortedFormats;
+    for (QSet<QByteArray>::ConstIterator it = formats.constBegin(); it != formats.constEnd(); ++it)
+        sortedFormats << *it;
+
+    qSort(sortedFormats);
+    return sortedFormats;
+}
+
+QT_END_NAMESPACE