drop support for bmp, ico, tga, jpeg and tiff
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq libpcre3-dev libssl-dev zlib1g-dev libzstd-dev libjansson-dev libc6-dev libpng-dev libjpeg-dev libtiff-dev libcups2-dev libfreetype6-dev libfontconfig1-dev libdbus-1-dev libicu-dev unixodbc-dev libpq-dev libsqlite3-dev xorg-dev
|
||||
sudo apt-get install -qq libpcre3-dev libssl-dev zlib1g-dev libzstd-dev libjansson-dev libc6-dev libpng-dev libcups2-dev libfreetype6-dev libfontconfig1-dev libdbus-1-dev libicu-dev xorg-dev
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
|
|
@ -146,21 +146,12 @@ set(KATIE_TOOLS_SUFFIX "" CACHE STRING "Tools (moc, uic, rcc, etc.) suffix")
|
|||
option(WITH_CUPS "Build CUPS support" ON)
|
||||
add_feature_info(cups WITH_CUPS "build CUPS support")
|
||||
|
||||
option(WITH_JPEG "Build JPEG support" ON)
|
||||
add_feature_info(jpeg WITH_JPEG "build JPEG support")
|
||||
|
||||
option(WITH_TIFF "Build TIFF support" ON)
|
||||
add_feature_info(tiff WITH_TIFF "build TIFF support")
|
||||
|
||||
option(WITH_DBUS "Build D-Bus support" ON)
|
||||
add_feature_info(dbus WITH_DBUS "build D-Bus support")
|
||||
|
||||
option(WITH_FONTCONFIG "Build Fontconfig support" ON)
|
||||
add_feature_info(fontconfig WITH_FONTCONFIG "build Fontconfig support")
|
||||
|
||||
option(WITH_ODBC "Build ODBC database plugin" ON)
|
||||
add_feature_info(odbc WITH_ODBC "build ODBC support")
|
||||
|
||||
option(WITH_INTL "Build Intl support" ON)
|
||||
add_feature_info(intl WITH_INTL "build Intl support")
|
||||
|
||||
|
@ -269,23 +260,6 @@ set_package_properties(PNG PROPERTIES
|
|||
TYPE REQUIRED
|
||||
)
|
||||
|
||||
find_package(JPEG)
|
||||
set_package_properties(JPEG PROPERTIES
|
||||
PURPOSE "JPEG format handler"
|
||||
DESCRIPTION "JPEG image codec with accelerated baseline compression and decompression"
|
||||
URL "https://libjpeg-turbo.virtualgl.org/"
|
||||
TYPE RECOMMENDED
|
||||
)
|
||||
|
||||
# v3.6.0+ required for TIFFReadRGBAImageOriented()
|
||||
find_package(TIFF 3.6.0)
|
||||
set_package_properties(TIFF PROPERTIES
|
||||
PURPOSE "TIFF format handler"
|
||||
DESCRIPTION "Library for manipulation of TIFF images"
|
||||
URL "http://www.libtiff.org/"
|
||||
TYPE RECOMMENDED
|
||||
)
|
||||
|
||||
find_package(Cups)
|
||||
set_package_properties(Cups PROPERTIES
|
||||
PURPOSE "Required for printing support"
|
||||
|
|
|
@ -12,9 +12,9 @@ build_script:
|
|||
|
||||
sudo apt-get update -qq
|
||||
|
||||
sudo apt-get install -qq libpcre3-dev libssl-dev zlib1g-dev libzstd-dev libc6-dev libpng-dev \
|
||||
libjpeg-dev libtiff-dev libcups2-dev libfreetype6-dev libfontconfig1-dev libdbus-1-dev \
|
||||
libicu-dev xorg-dev dbus-x11 libjansson-dev ccache
|
||||
sudo apt-get install -qq libpcre3-dev libssl-dev zlib1g-dev libzstd-dev \
|
||||
libc6-dev libpng-dev libcups2-dev libfreetype6-dev libfontconfig1-dev \
|
||||
libdbus-1-dev libicu-dev xorg-dev dbus-x11 libjansson-dev ccache
|
||||
|
||||
export PATH="/usr/lib/ccache/:$PATH"
|
||||
|
||||
|
|
|
@ -6,17 +6,16 @@ Vcs-Git: git://github.com/fluxer/katie.git
|
|||
Vcs-browser: https://github.com/fluxer/katie
|
||||
Standards-Version: 4.12.0
|
||||
Build-Depends: debhelper (>= 9~), libssl-dev, zlib1g-dev, libzstd-dev,
|
||||
libc6-dev, libjansson-dev, libpng-dev, libjpeg-dev, libtiff5-dev,
|
||||
libcups2-dev, libfreetype6-dev, libfontconfig1-dev, libpcre3-dev,
|
||||
libdbus-1-dev, libicu-dev, cmake, git, xserver-xorg-dev,
|
||||
libxinerama-dev, libxrandr-dev, libxrender-dev, libxcursor-dev,
|
||||
libsm-dev, unifdef | dpkg
|
||||
libc6-dev, libjansson-dev, libpng-dev, libcups2-dev, libfreetype6-dev,
|
||||
libfontconfig1-dev, libpcre3-dev, libdbus-1-dev, libicu-dev, cmake,
|
||||
git, xserver-xorg-dev, libxinerama-dev, libxrandr-dev, libxrender-dev,
|
||||
libxcursor-dev, libsm-dev, unifdef | dpkg
|
||||
|
||||
Package: katie-runtime
|
||||
Architecture: amd64 arm64 armel armhf i386 mips mips64el mipsel ppc64el s390x hurd-i386
|
||||
Section: x11
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, xdg-utils
|
||||
Recommends: unixodbc, xserver-xorg-core
|
||||
Recommends: xserver-xorg-core
|
||||
Description: C++ toolkit derived from the Qt 4.8 framework
|
||||
Katie is continuation of the Qt4 C++ toolkit with the goal to keep it alive,
|
||||
clean it up, fix some bugs and backport some features from Qt5. It is based
|
||||
|
|
|
@ -7,7 +7,7 @@ Summary: C++ toolkit derived from the Qt 4.8 framework
|
|||
License: BSD and LGPLv2+
|
||||
URL: https://github.com/fluxer/katie
|
||||
|
||||
BuildRequires: gcc-c++ cmake libicu-devel libzstd-devel jansson-devel zlib-devel libpng-devel freetype-devel pcre-devel openssl-devel libX11-devel libXinerama-devel libXrandr-devel libXrender-devel libXfixes-devel libXcursor-devel libSM-devel libICE-devel dbus-devel libtiff-devel libjpeg-turbo-devel fontconfig-devel cups-devel unifdef
|
||||
BuildRequires: gcc-c++ cmake libicu-devel libzstd-devel jansson-devel zlib-devel libpng-devel freetype-devel pcre-devel openssl-devel libX11-devel libXinerama-devel libXrandr-devel libXrender-devel libXfixes-devel libXcursor-devel libSM-devel libICE-devel dbus-devel fontconfig-devel cups-devel unifdef
|
||||
Requires: xdg-utils
|
||||
Requires(post): /sbin/ldconfig
|
||||
Requires(postun): /sbin/ldconfig
|
||||
|
|
|
@ -13,16 +13,15 @@ DISTFILES = master.tar.gz
|
|||
DIST_SUBDIR = ${PORTNAME}
|
||||
WRKSRC = ${WRKDIR}/katie-master
|
||||
|
||||
USES = compiler:c++11-lang pkgconfig cmake ssl xorg jpeg desktop-file-utils
|
||||
USES = compiler:c++11-lang pkgconfig cmake ssl xorg desktop-file-utils
|
||||
USE_XORG = x11 xinerama xrandr xrender xfixes xcursor xext sm ice
|
||||
USE_LDCONFIG = yes
|
||||
RUN_DEPENDS = xdg-open:devel/xdg-utils
|
||||
LIB_DEPENDS = libzstd.so:archivers/zstd libicuuc.so:devel/icu \
|
||||
libicui18n.so:devel/icu libjansson.so:devel/jansson \
|
||||
libpcre.so:devel/pcre libpng.so:graphics/png \
|
||||
libtiff.so:graphics/tiff libfreetype.so:print/freetype2 \
|
||||
libfontconfig.so:x11-fonts/fontconfig libdbus-1.so:devel/dbus \
|
||||
libcups.so:print/cups
|
||||
libfreetype.so:print/freetype2 libfontconfig.so:x11-fonts/fontconfig \
|
||||
libdbus-1.so:devel/dbus libcups.so:print/cups
|
||||
CMAKE_ARGS = -DKATIE_TOOLS_SUFFIX="-katie" -Wno-dev
|
||||
|
||||
OPTIONS_DEFINE = NLS
|
||||
|
|
|
@ -1406,11 +1406,7 @@ lib/katie/plugins/accessible/libqtaccessiblewidgets.so
|
|||
lib/katie/plugins/designer/libqdeclarativeview.so
|
||||
lib/katie/plugins/iconengines/libqsvgicon.so
|
||||
lib/katie/plugins/imageformats/libqgif.so
|
||||
lib/katie/plugins/imageformats/libqico.so
|
||||
lib/katie/plugins/imageformats/libqjpeg.so
|
||||
lib/katie/plugins/imageformats/libqsvg.so
|
||||
lib/katie/plugins/imageformats/libqtga.so
|
||||
lib/katie/plugins/imageformats/libqtiff.so
|
||||
lib/katie/plugins/script/libqtscriptdbus.so
|
||||
lib/libKtCore.so
|
||||
lib/libKtCore.so.4.12
|
||||
|
|
|
@ -43,10 +43,8 @@ BUILD_DEPENDS = unifdef-[0-9]*:../../devel/unifdef
|
|||
.include "../../fonts/fontconfig/buildlink3.mk"
|
||||
.include "../../graphics/freetype2/buildlink3.mk"
|
||||
.include "../../graphics/png/buildlink3.mk"
|
||||
.include "../../graphics/tiff/buildlink3.mk"
|
||||
.include "../../print/cups-base/buildlink3.mk"
|
||||
.include "../../sysutils/dbus/buildlink3.mk"
|
||||
.include "../../mk/pthread.buildlink3.mk"
|
||||
.include "../../mk/jpeg.buildlink3.mk"
|
||||
.include "../../mk/dlopen.buildlink3.mk"
|
||||
.include "../../mk/bsd.pkg.mk"
|
||||
|
|
|
@ -1409,11 +1409,7 @@ lib/katie/plugins/accessible/libqtaccessiblewidgets.so
|
|||
lib/katie/plugins/designer/libqdeclarativeview.so
|
||||
lib/katie/plugins/iconengines/libqsvgicon.so
|
||||
lib/katie/plugins/imageformats/libqgif.so
|
||||
lib/katie/plugins/imageformats/libqico.so
|
||||
lib/katie/plugins/imageformats/libqjpeg.so
|
||||
lib/katie/plugins/imageformats/libqsvg.so
|
||||
lib/katie/plugins/imageformats/libqtga.so
|
||||
lib/katie/plugins/imageformats/libqtiff.so
|
||||
lib/katie/plugins/script/libqtscriptdbus.so
|
||||
lib/libKtCore.so
|
||||
lib/libKtCore.so.4.12
|
||||
|
|
|
@ -22,10 +22,9 @@ MODULES = devel/cmake
|
|||
BUILD_DEPENDS = devel/gettext,-tools
|
||||
RUN_DEPENDS = devel/desktop-file-utils devel/xdg-utils
|
||||
LIB_DEPENDS = archivers/zstd textproc/icu4c devel/jansson devel/pcre \
|
||||
graphics/png graphics/jpeg graphics/tiff x11/dbus print/cups,-libs \
|
||||
devel/gettext,-runtime
|
||||
graphics/png x11/dbus print/cups,-libs devel/gettext,-runtime
|
||||
WANTLIB = ${COMPILER_LIBCXX} ICE SM X11 Xcursor Xext Xfixes Xinerama Xrandr \
|
||||
Xrender fontconfig freetype zstd icui18n icuuc pcre png jpeg tiff \
|
||||
Xrender fontconfig freetype zstd icui18n icuuc pcre png \
|
||||
dbus-1 cups intl ssl z c crypto m
|
||||
SEPARATE_BUILD = Yes
|
||||
CONFIGURE_ARGS = -DKATIE_TOOLS_SUFFIX="-katie" -Wno-dev
|
||||
|
|
|
@ -1433,11 +1433,7 @@ lib/katie/plugins/iconengines/
|
|||
@so lib/katie/plugins/iconengines/libqsvgicon.so
|
||||
lib/katie/plugins/imageformats/
|
||||
@so lib/katie/plugins/imageformats/libqgif.so
|
||||
@so lib/katie/plugins/imageformats/libqico.so
|
||||
@so lib/katie/plugins/imageformats/libqjpeg.so
|
||||
@so lib/katie/plugins/imageformats/libqsvg.so
|
||||
@so lib/katie/plugins/imageformats/libqtga.so
|
||||
@so lib/katie/plugins/imageformats/libqtiff.so
|
||||
lib/katie/plugins/script/
|
||||
@so lib/katie/plugins/script/libqtscriptdbus.so
|
||||
@lib lib/libKtCore.so.${LIBKtCore_VERSION}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import os, glob, subprocess
|
||||
|
||||
components = ('core', 'dbus', 'declarative', 'gui', 'network', 'plugins',
|
||||
'script', 'scripttools', 'sql', 'svg', 'test', 'uitools', 'xml')
|
||||
'script', 'scripttools', 'svg', 'test', 'uitools', 'xml')
|
||||
cfiles = []
|
||||
tfiles = []
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
#define QT_NO_TABLETEVENT
|
||||
#define QT_NO_RAWFONT
|
||||
#define QT_NO_IMAGE_TEXT
|
||||
#define QT_NO_IMAGEFORMAT_BMP
|
||||
#define QT_NO_IMAGEFORMAT_MNG
|
||||
#define QT_NO_TEXTODFWRITER
|
||||
#define QT_NO_TEXTCODECPLUGIN
|
||||
|
@ -200,7 +201,6 @@
|
|||
#cmakedefine QT_NO_HTTP
|
||||
#cmakedefine QT_NO_ICON
|
||||
#cmakedefine QT_NO_IDENTITYPROXYMODEL
|
||||
#cmakedefine QT_NO_IMAGEFORMAT_BMP
|
||||
#cmakedefine QT_NO_IMAGEFORMAT_PPM
|
||||
#cmakedefine QT_NO_IMAGEFORMAT_XBM
|
||||
#cmakedefine QT_NO_IMAGEFORMAT_XPM
|
||||
|
|
|
@ -38,10 +38,9 @@ QT_BEGIN_NAMESPACE
|
|||
The Image element is used to display images in a declarative user interface.
|
||||
|
||||
The source of the image is specified as a URL using the \l source property.
|
||||
Images can be supplied in any of the standard image formats supported by Qt,
|
||||
including bitmap formats such as PNG and JPEG, and vector graphics formats
|
||||
such as SVG. If you need to display animated images, use the \l AnimatedImage
|
||||
element.
|
||||
Images can be supplied in any of the standard image formats supported by Katie,
|
||||
including bitmap formats such as PNG and vector graphics formats such as SVG.
|
||||
If you need to display animated images, use the \l AnimatedImage element.
|
||||
|
||||
If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
|
||||
specified, the Image element automatically uses the size of the loaded image.
|
||||
|
@ -335,7 +334,7 @@ qreal QDeclarativeImage::paintedHeight() const
|
|||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: "reallyBigImage.jpg"
|
||||
source: "reallyBigImage.png"
|
||||
sourceSize.width: 1024
|
||||
sourceSize.height: 1024
|
||||
}
|
||||
|
@ -352,9 +351,8 @@ qreal QDeclarativeImage::paintedHeight() const
|
|||
Avoid changing this property dynamically; rendering an SVG is \e slow compared
|
||||
to an image.
|
||||
|
||||
If the source is a non-scalable image (eg. JPEG), the loaded image will
|
||||
be no greater than this property specifies. For some formats (currently only JPEG),
|
||||
the whole image will never actually be loaded into memory.
|
||||
If the source is a non-scalable image (eg. PNG), the loaded image will
|
||||
be no greater than this property specifies.
|
||||
|
||||
Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
|
||||
by setting sourceSize to \c undefined.
|
||||
|
|
|
@ -341,17 +341,13 @@ static QString imageFilter()
|
|||
{
|
||||
QString filter = QApplication::translate("IconSelector", "All Pixmaps (");
|
||||
const QList<QByteArray> supportedImageFormats = QImageReader::supportedImageFormats();
|
||||
const QString jpeg = QLatin1String("JPEG");
|
||||
const int count = supportedImageFormats.count();
|
||||
for (int i = 0; i< count; ++i) {
|
||||
if (i)
|
||||
filter += QLatin1Char(' ');
|
||||
filter += QLatin1String("*.");
|
||||
const QString outputFormat = QString::fromUtf8(supportedImageFormats.at(i));
|
||||
if (outputFormat != jpeg)
|
||||
filter += outputFormat.toLower();
|
||||
else
|
||||
filter += QLatin1String("jpg *.jpeg");
|
||||
}
|
||||
filter += QLatin1Char(')');
|
||||
return filter;
|
||||
|
|
|
@ -314,7 +314,6 @@ set(GUI_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/image/qpixmapcache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qpixmapdata_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qpixmapfilter_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qbmphandler_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qppmhandler_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qxbmhandler_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qxpmhandler_p.h
|
||||
|
@ -684,7 +683,6 @@ set(GUI_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/image/qpixmap_raster.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qpixmap_x11.cpp
|
||||
# Built-in image format support
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qbmphandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qppmhandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qxbmhandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/image/qxpmhandler.cpp
|
||||
|
|
|
@ -84,7 +84,7 @@ Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook
|
|||
In the above example, a modal QFileDialog is created using a static
|
||||
function. The dialog initially displays the contents of the "/home/jana"
|
||||
directory, and displays files matching the patterns given in the
|
||||
string "Image Files (*.png *.jpg *.bmp)". The parent of the file dialog
|
||||
string "Image Files (*.png *.xpm)". The parent of the file dialog
|
||||
is set to \e this, and the window title is set to "Open Image".
|
||||
|
||||
If you want to use multiple filters, separate each one with
|
||||
|
@ -111,11 +111,11 @@ Q_GUI_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_hook
|
|||
|
||||
\snippet doc/src/snippets/code/src_gui_dialogs_qfiledialog.cpp 3
|
||||
|
||||
In the above example, the filter is set to \c{"Images (*.png *.xpm *.jpg)"},
|
||||
this means that only files with the extension \c png, \c xpm,
|
||||
or \c jpg will be shown in the QFileDialog. You can apply
|
||||
several filters by using setNameFilters(). Use selectNameFilter() to select
|
||||
one of the filters you've given as the file dialog's default filter.
|
||||
In the above example, the filter is set to \c{"Images (*.png *.xpm)"},
|
||||
this means that only files with the extension \c png or \c xpm,
|
||||
will be shown in the QFileDialog. You can apply several filters by
|
||||
using setNameFilters(). Use selectNameFilter() to select one of the
|
||||
filters you've given as the file dialog's default filter.
|
||||
|
||||
The file dialog has two view modes: \l{QFileDialog::}{List} and
|
||||
\l{QFileDialog::}{Detail}.
|
||||
|
@ -1524,7 +1524,7 @@ QString QFileDialog::labelText(DialogLabel label) const
|
|||
example:
|
||||
|
||||
\code
|
||||
"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
|
||||
"Images (*.png *.xpm);;Text files (*.txt);;XML files (*.xml)"
|
||||
\endcode
|
||||
|
||||
The \a options argument holds various options about how to run the dialog,
|
||||
|
@ -1603,7 +1603,7 @@ QString QFileDialog::getOpenFileName(QWidget *parent,
|
|||
filters, separate them with ';;', for instance:
|
||||
|
||||
\code
|
||||
"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
|
||||
"Images (*.png *.xpm);;Text files (*.txt);;XML files (*.xml)"
|
||||
\endcode
|
||||
|
||||
The dialog's caption is set to \a caption. If \a caption is not specified
|
||||
|
@ -1685,7 +1685,7 @@ QStringList QFileDialog::getOpenFileNames(QWidget *parent,
|
|||
Multiple filters are separated with ';;'. For instance:
|
||||
|
||||
\code
|
||||
"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"
|
||||
"Images (*.png *.xpm);;Text files (*.txt);;XML files (*.xml)"
|
||||
\endcode
|
||||
|
||||
The \a options argument holds various options about how to run the dialog,
|
||||
|
@ -2766,7 +2766,7 @@ void QFileDialogPrivate::_q_goToDirectory(const QString &path)
|
|||
}
|
||||
}
|
||||
|
||||
// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
|
||||
// Makes a list of filters from a normal filter string "Image Files (*.png *.xpm)"
|
||||
QStringList qt_clean_filter_list(const QString &filter)
|
||||
{
|
||||
QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
|
||||
|
|
|
@ -417,7 +417,7 @@ qint64 QFileSystemModel::size(const QModelIndex &index) const
|
|||
}
|
||||
|
||||
/*!
|
||||
Returns the type of file \a index such as "Directory" or "JPEG file".
|
||||
Returns the type of file \a index such as "Directory" or "PNG file".
|
||||
*/
|
||||
QString QFileSystemModel::type(const QModelIndex &index) const
|
||||
{
|
||||
|
@ -858,7 +858,7 @@ static inline QChar getNextChar(const QString &s, int location)
|
|||
|
||||
Examples:
|
||||
1, 2, 10, 55, 100
|
||||
01.jpg, 2.jpg, 10.jpg
|
||||
01.png, 2.png, 10.png
|
||||
|
||||
Note on the algorithm:
|
||||
Only as many characters as necessary are looked at and at most they all
|
||||
|
|
|
@ -1,827 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtGui module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qbmphandler_p.h"
|
||||
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
|
||||
#include "qimage.h"
|
||||
#include "qvariant.h"
|
||||
#include "qvector.h"
|
||||
#include "qcorecommon_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels
|
||||
{
|
||||
int i;
|
||||
if (image->depth() == 1 && image->colorCount() == 2) {
|
||||
uint *p = (uint *)image->bits();
|
||||
int nbytes = image->byteCount();
|
||||
for (i=0; i<nbytes/4; i++) {
|
||||
*p = ~*p;
|
||||
p++;
|
||||
}
|
||||
uchar *p2 = (uchar *)p;
|
||||
for (i=0; i<(nbytes&3); i++) {
|
||||
*p2 = ~*p2;
|
||||
p2++;
|
||||
}
|
||||
QRgb t = image->color(0); // swap color 0 and 1
|
||||
image->setColor(0, image->color(1));
|
||||
image->setColor(1, t);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
QImageIO::defineIOHandler("BMP", "^BM", 0,
|
||||
read_bmp_image, write_bmp_image);
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
BMP (DIB) image read/write functions
|
||||
*****************************************************************************/
|
||||
|
||||
const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data
|
||||
|
||||
static QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf)
|
||||
{ // read file header
|
||||
s.readRawData(bf.bfType, 2);
|
||||
s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
|
||||
return s;
|
||||
}
|
||||
|
||||
static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf)
|
||||
{ // write file header
|
||||
s.writeRawData(bf.bfType, 2);
|
||||
s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
const int BMP_OLD = 12; // old Windows/OS2 BMP size
|
||||
const int BMP_WIN = 40; // Windows BMP v3 size
|
||||
const int BMP_OS2 = 64; // new OS/2 BMP size
|
||||
const int BMP_WIN4 = 108; // Windows BMP v4 size
|
||||
const int BMP_WIN5 = 124; // Windows BMP v5 size
|
||||
|
||||
const int BMP_RGB = 0; // no compression
|
||||
const int BMP_RLE8 = 1; // run-length encoded, 8 bits
|
||||
const int BMP_RLE4 = 2; // run-length encoded, 4 bits
|
||||
const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields
|
||||
|
||||
|
||||
static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi)
|
||||
{
|
||||
s >> bi.biSize;
|
||||
if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2 || bi.biSize == BMP_WIN4 || bi.biSize == BMP_WIN5) {
|
||||
s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
|
||||
s >> bi.biCompression >> bi.biSizeImage;
|
||||
s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
|
||||
s >> bi.biClrUsed >> bi.biClrImportant;
|
||||
}
|
||||
else { // probably old Windows format
|
||||
qint16 w, h;
|
||||
s >> w >> h >> bi.biPlanes >> bi.biBitCount;
|
||||
bi.biWidth = w;
|
||||
bi.biHeight = h;
|
||||
bi.biCompression = BMP_RGB; // no compression
|
||||
bi.biSizeImage = 0;
|
||||
bi.biXPelsPerMeter = bi.biYPelsPerMeter = 0;
|
||||
bi.biClrUsed = bi.biClrImportant = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
|
||||
{
|
||||
s << bi.biSize;
|
||||
s << bi.biWidth << bi.biHeight;
|
||||
s << bi.biPlanes;
|
||||
s << bi.biBitCount;
|
||||
s << bi.biCompression;
|
||||
s << bi.biSizeImage;
|
||||
s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
|
||||
s << bi.biClrUsed << bi.biClrImportant;
|
||||
return s;
|
||||
}
|
||||
|
||||
static int calc_shift(uint mask)
|
||||
{
|
||||
int result = 0;
|
||||
while (mask && !(mask & 1)) {
|
||||
result++;
|
||||
mask >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
|
||||
{
|
||||
// read BMP file header
|
||||
s >> bf;
|
||||
if (s.status() != QDataStream::Ok)
|
||||
return false;
|
||||
|
||||
// check header
|
||||
if (qstrncmp(bf.bfType,"BM",2) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
|
||||
{
|
||||
s >> bi; // read BMP info header
|
||||
if (s.status() != QDataStream::Ok)
|
||||
return false;
|
||||
|
||||
int nbits = bi.biBitCount;
|
||||
int comp = bi.biCompression;
|
||||
if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
|
||||
bi.biPlanes != 1 || comp > BMP_BITFIELDS)
|
||||
return false; // weird BMP image
|
||||
if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
|
||||
(nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
|
||||
return false; // weird compression type
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int startpos, QImage &image)
|
||||
{
|
||||
QIODevice* d = s.device();
|
||||
if (d->atEnd()) // end of stream/file
|
||||
return false;
|
||||
#if 0
|
||||
qDebug("offset...........%d", offset);
|
||||
qDebug("startpos.........%d", startpos);
|
||||
qDebug("biSize...........%d", bi.biSize);
|
||||
qDebug("biWidth..........%d", bi.biWidth);
|
||||
qDebug("biHeight.........%d", bi.biHeight);
|
||||
qDebug("biPlanes.........%d", bi.biPlanes);
|
||||
qDebug("biBitCount.......%d", bi.biBitCount);
|
||||
qDebug("biCompression....%d", bi.biCompression);
|
||||
qDebug("biSizeImage......%d", bi.biSizeImage);
|
||||
qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
|
||||
qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
|
||||
qDebug("biClrUsed........%d", bi.biClrUsed);
|
||||
qDebug("biClrImportant...%d", bi.biClrImportant);
|
||||
#endif
|
||||
int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
|
||||
int t = bi.biSize, comp = bi.biCompression;
|
||||
uint red_mask = 0;
|
||||
uint green_mask = 0;
|
||||
uint blue_mask = 0;
|
||||
int red_shift = 0;
|
||||
int green_shift = 0;
|
||||
int blue_shift = 0;
|
||||
int red_scale = 0;
|
||||
int green_scale = 0;
|
||||
int blue_scale = 0;
|
||||
|
||||
int ncols = 0;
|
||||
int depth = 0;
|
||||
QImage::Format format;
|
||||
switch (nbits) {
|
||||
case 32:
|
||||
case 24:
|
||||
case 16:
|
||||
depth = 32;
|
||||
format = QImage::Format_RGB32;
|
||||
break;
|
||||
case 8:
|
||||
case 4:
|
||||
depth = 8;
|
||||
format = QImage::Format_Indexed8;
|
||||
break;
|
||||
default:
|
||||
depth = 1;
|
||||
format = QImage::Format_Mono;
|
||||
}
|
||||
|
||||
if (bi.biHeight < 0)
|
||||
h = -h; // support images with negative height
|
||||
|
||||
if (image.size() != QSize(w, h) || image.format() != format) {
|
||||
image = QImage(w, h, format);
|
||||
if (image.isNull()) // could not create image
|
||||
return false;
|
||||
}
|
||||
|
||||
if (depth != 32) {
|
||||
ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
|
||||
if (ncols > 256) // sanity check - don't run out of mem if color table is broken
|
||||
return false;
|
||||
image.setColorCount(ncols);
|
||||
}
|
||||
|
||||
image.setDotsPerMeterX(bi.biXPelsPerMeter);
|
||||
image.setDotsPerMeterY(bi.biYPelsPerMeter);
|
||||
|
||||
if (!d->isSequential())
|
||||
d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4? BMP_WIN : bi.biSize)); // goto start of colormap
|
||||
|
||||
if (bi.biSize >= BMP_WIN4 || (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32))) {
|
||||
if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
|
||||
return false;
|
||||
if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
|
||||
return false;
|
||||
if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
|
||||
return false;
|
||||
|
||||
// Read BMP v4+ header
|
||||
if (bi.biSize >= BMP_WIN4) {
|
||||
int alpha_mask = 0;
|
||||
int CSType = 0;
|
||||
int gamma_red = 0;
|
||||
int gamma_green = 0;
|
||||
int gamma_blue = 0;
|
||||
int endpoints[9];
|
||||
|
||||
if (d->read((char *)&alpha_mask, sizeof(alpha_mask)) != sizeof(alpha_mask))
|
||||
return false;
|
||||
if (d->read((char *)&CSType, sizeof(CSType)) != sizeof(CSType))
|
||||
return false;
|
||||
if (d->read((char *)&endpoints, sizeof(endpoints)) != sizeof(endpoints))
|
||||
return false;
|
||||
if (d->read((char *)&gamma_red, sizeof(gamma_red)) != sizeof(gamma_red))
|
||||
return false;
|
||||
if (d->read((char *)&gamma_green, sizeof(gamma_green)) != sizeof(gamma_green))
|
||||
return false;
|
||||
if (d->read((char *)&gamma_blue, sizeof(gamma_blue)) != sizeof(gamma_blue))
|
||||
return false;
|
||||
|
||||
if (bi.biSize == BMP_WIN5) {
|
||||
qint32 intent = 0;
|
||||
qint32 profileData = 0;
|
||||
qint32 profileSize = 0;
|
||||
qint32 reserved = 0;
|
||||
|
||||
if (d->read((char *)&intent, sizeof(intent)) != sizeof(intent))
|
||||
return false;
|
||||
if (d->read((char *)&profileData, sizeof(profileData)) != sizeof(profileData))
|
||||
return false;
|
||||
if (d->read((char *)&profileSize, sizeof(profileSize)) != sizeof(profileSize))
|
||||
return false;
|
||||
if (d->read((char *)&reserved, sizeof(reserved)) != sizeof(reserved) || reserved != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ncols > 0) { // read color table
|
||||
QSTACKARRAY(uchar, rgb, 4);
|
||||
int rgb_len = t == BMP_OLD ? 3 : 4;
|
||||
for (int i=0; i<ncols; i++) {
|
||||
if (d->read((char *)rgb, rgb_len) != rgb_len)
|
||||
return false;
|
||||
image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
|
||||
if (d->atEnd()) // truncated file
|
||||
return false;
|
||||
}
|
||||
} else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
|
||||
red_shift = calc_shift(red_mask);
|
||||
if (((red_mask >> red_shift) + 1) == 0)
|
||||
return false;
|
||||
red_scale = 256 / ((red_mask >> red_shift) + 1);
|
||||
green_shift = calc_shift(green_mask);
|
||||
if (((green_mask >> green_shift) + 1) == 0)
|
||||
return false;
|
||||
green_scale = 256 / ((green_mask >> green_shift) + 1);
|
||||
blue_shift = calc_shift(blue_mask);
|
||||
if (((blue_mask >> blue_shift) + 1) == 0)
|
||||
return false;
|
||||
blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
|
||||
} else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
|
||||
blue_mask = 0x000000ff;
|
||||
green_mask = 0x0000ff00;
|
||||
red_mask = 0x00ff0000;
|
||||
blue_shift = 0;
|
||||
green_shift = 8;
|
||||
red_shift = 16;
|
||||
blue_scale = green_scale = red_scale = 1;
|
||||
} else if (comp == BMP_RGB && nbits == 16) {
|
||||
blue_mask = 0x001f;
|
||||
green_mask = 0x03e0;
|
||||
red_mask = 0x7c00;
|
||||
blue_shift = 0;
|
||||
green_shift = 2;
|
||||
red_shift = 7;
|
||||
red_scale = 1;
|
||||
green_scale = 1;
|
||||
blue_scale = 8;
|
||||
}
|
||||
|
||||
// offset can be bogus, be careful
|
||||
if (offset>=0 && startpos + offset > d->pos()) {
|
||||
if (!d->isSequential())
|
||||
d->seek(startpos + offset); // start of image data
|
||||
}
|
||||
|
||||
int bpl = image.bytesPerLine();
|
||||
uchar *data = image.bits();
|
||||
|
||||
if (nbits == 1) { // 1 bit BMP image
|
||||
while (--h >= 0) {
|
||||
if (d->read((char*)(data + h*bpl), bpl) != bpl)
|
||||
break;
|
||||
}
|
||||
if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
|
||||
swapPixel01(&image); // pixel 0 is white!
|
||||
}
|
||||
|
||||
else if (nbits == 4) { // 4 bit BMP image
|
||||
int buflen = ((w+7)/8)*4;
|
||||
QSTACKARRAY(uchar, buf, buflen);
|
||||
if (comp == BMP_RLE4) { // run length compression
|
||||
int x=0, y=0, c, i;
|
||||
quint8 b;
|
||||
uchar *p = data + (h-1)*bpl;
|
||||
const uchar *endp = p + w;
|
||||
while (y < h) {
|
||||
if (!d->getChar((char *)&b))
|
||||
break;
|
||||
if (b == 0) { // escape code
|
||||
if (!d->getChar((char *)&b) || b == 1) {
|
||||
y = h; // exit loop
|
||||
} else switch (b) {
|
||||
case 0: // end of line
|
||||
x = 0;
|
||||
y++;
|
||||
p = data + (h-y-1)*bpl;
|
||||
break;
|
||||
case 2: // delta (jump)
|
||||
{
|
||||
quint8 tmp;
|
||||
d->getChar((char *)&tmp);
|
||||
x += tmp;
|
||||
d->getChar((char *)&tmp);
|
||||
y += tmp;
|
||||
}
|
||||
|
||||
// Protection
|
||||
if ((uint)x >= (uint)w)
|
||||
x = w-1;
|
||||
if ((uint)y >= (uint)h)
|
||||
y = h-1;
|
||||
|
||||
p = data + (h-y-1)*bpl + x;
|
||||
break;
|
||||
default: // absolute mode
|
||||
// Protection
|
||||
if (p + b > endp)
|
||||
b = endp-p;
|
||||
|
||||
i = (c = b)/2;
|
||||
while (i--) {
|
||||
d->getChar((char *)&b);
|
||||
*p++ = b >> 4;
|
||||
*p++ = b & 0x0f;
|
||||
}
|
||||
if (c & 1) {
|
||||
unsigned char tmp;
|
||||
d->getChar((char *)&tmp);
|
||||
*p++ = tmp >> 4;
|
||||
}
|
||||
if ((((c & 3) + 1) & 2) == 2)
|
||||
d->getChar(0); // align on word boundary
|
||||
x += c;
|
||||
}
|
||||
} else { // encoded mode
|
||||
// Protection
|
||||
if (p + b > endp)
|
||||
b = endp-p;
|
||||
|
||||
i = (c = b)/2;
|
||||
d->getChar((char *)&b); // 2 pixels to be repeated
|
||||
while (i--) {
|
||||
*p++ = b >> 4;
|
||||
*p++ = b & 0x0f;
|
||||
}
|
||||
if (c & 1)
|
||||
*p++ = b >> 4;
|
||||
x += c;
|
||||
}
|
||||
}
|
||||
} else if (comp == BMP_RGB) { // no compression
|
||||
::memset(data, 0, size_t(h) * bpl);
|
||||
while (--h >= 0) {
|
||||
if (d->read((char*)buf,buflen) != buflen)
|
||||
break;
|
||||
uchar *p = data + h*bpl;
|
||||
uchar *b = buf;
|
||||
for (int i=0; i<w/2; i++) { // convert nibbles to bytes
|
||||
*p++ = *b >> 4;
|
||||
*p++ = *b++ & 0x0f;
|
||||
}
|
||||
if (w & 1) // the last nibble
|
||||
*p = *b >> 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (nbits == 8) { // 8 bit BMP image
|
||||
if (comp == BMP_RLE8) { // run length compression
|
||||
int x=0, y=0;
|
||||
quint8 b;
|
||||
uchar *p = data + (h-1)*bpl;
|
||||
const uchar *endp = p + w;
|
||||
while (y < h) {
|
||||
if (!d->getChar((char *)&b))
|
||||
break;
|
||||
if (b == 0) { // escape code
|
||||
if (!d->getChar((char *)&b) || b == 1) {
|
||||
y = h; // exit loop
|
||||
} else switch (b) {
|
||||
case 0: // end of line
|
||||
x = 0;
|
||||
y++;
|
||||
p = data + (h-y-1)*bpl;
|
||||
break;
|
||||
case 2: // delta (jump)
|
||||
{
|
||||
quint8 tmp;
|
||||
d->getChar((char *)&tmp);
|
||||
x += tmp;
|
||||
d->getChar((char *)&tmp);
|
||||
y += tmp;
|
||||
}
|
||||
|
||||
// Protection
|
||||
if ((uint)x >= (uint)w)
|
||||
x = w-1;
|
||||
if ((uint)y >= (uint)h)
|
||||
y = h-1;
|
||||
|
||||
p = data + (h-y-1)*bpl + x;
|
||||
break;
|
||||
default: // absolute mode
|
||||
// Protection
|
||||
if (p + b > endp)
|
||||
b = endp-p;
|
||||
|
||||
if (d->read((char *)p, b) != b)
|
||||
return false;
|
||||
if ((b & 1) == 1)
|
||||
d->getChar(0); // align on word boundary
|
||||
x += b;
|
||||
p += b;
|
||||
}
|
||||
} else { // encoded mode
|
||||
// Protection
|
||||
if (p + b > endp)
|
||||
b = endp-p;
|
||||
|
||||
char tmp;
|
||||
d->getChar(&tmp);
|
||||
memset(p, tmp, b); // repeat pixel
|
||||
x += b;
|
||||
p += b;
|
||||
}
|
||||
}
|
||||
} else if (comp == BMP_RGB) { // uncompressed
|
||||
while (--h >= 0) {
|
||||
if (d->read((char *)data + h*bpl, bpl) != bpl)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
|
||||
QRgb *p;
|
||||
QRgb *end;
|
||||
QSTACKARRAY(uchar, buf24, bpl);
|
||||
int bpl24 = ((w*nbits+31)/32)*4;
|
||||
uchar *b;
|
||||
int c;
|
||||
|
||||
while (--h >= 0) {
|
||||
p = (QRgb *)(data + h*bpl);
|
||||
end = p + w;
|
||||
if (d->read((char *)buf24,bpl24) != bpl24)
|
||||
break;
|
||||
b = buf24;
|
||||
while (p < end) {
|
||||
c = *(uchar*)b | (*(uchar*)(b+1)<<8);
|
||||
if (nbits != 16)
|
||||
c |= *(uchar*)(b+2)<<16;
|
||||
*p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
|
||||
((c & green_mask) >> green_shift) * green_scale,
|
||||
((c & blue_mask) >> blue_shift) * blue_scale);
|
||||
b += nbits/8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bi.biHeight < 0) {
|
||||
// Flip the image
|
||||
QSTACKARRAY(uchar, buf, bpl);
|
||||
h = -bi.biHeight;
|
||||
for (int y = 0; y < h/2; ++y) {
|
||||
memcpy(buf, data + y*bpl, bpl);
|
||||
memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
|
||||
memcpy(data + (h-y-1)*bpl, buf, bpl);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is also used in qmime_win.cpp
|
||||
bool qt_write_dib(QDataStream &s, QImage image)
|
||||
{
|
||||
int nbits;
|
||||
int bpl_bmp;
|
||||
int bpl = image.bytesPerLine();
|
||||
|
||||
QIODevice* d = s.device();
|
||||
if (!d->isWritable())
|
||||
return false;
|
||||
|
||||
if (image.depth() == 8 && image.colorCount() <= 16) {
|
||||
bpl_bmp = (((bpl+1)/2+3)/4)*4;
|
||||
nbits = 4;
|
||||
} else if (image.depth() == 32) {
|
||||
bpl_bmp = ((image.width()*24+31)/32)*4;
|
||||
nbits = 24;
|
||||
} else {
|
||||
bpl_bmp = bpl;
|
||||
nbits = image.depth();
|
||||
}
|
||||
|
||||
BMP_INFOHDR bi;
|
||||
bi.biSize = BMP_WIN; // build info header
|
||||
bi.biWidth = image.width();
|
||||
bi.biHeight = image.height();
|
||||
bi.biPlanes = 1;
|
||||
bi.biBitCount = nbits;
|
||||
bi.biCompression = BMP_RGB;
|
||||
bi.biSizeImage = bpl_bmp*image.height();
|
||||
bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
|
||||
: 2834; // 72 dpi default
|
||||
bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
|
||||
bi.biClrUsed = image.colorCount();
|
||||
bi.biClrImportant = image.colorCount();
|
||||
s << bi; // write info header
|
||||
if (s.status() != QDataStream::Ok)
|
||||
return false;
|
||||
|
||||
if (image.depth() != 32) { // write color table
|
||||
QSTACKARRAY(uchar, color_table, 4*image.colorCount());
|
||||
uchar *rgb = color_table;
|
||||
QVector<QRgb> c = image.colorTable();
|
||||
for (int i=0; i<image.colorCount(); i++) {
|
||||
*rgb++ = qBlue (c[i]);
|
||||
*rgb++ = qGreen(c[i]);
|
||||
*rgb++ = qRed (c[i]);
|
||||
*rgb++ = 0;
|
||||
}
|
||||
if (d->write((char *)color_table, 4*image.colorCount()) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (image.format() == QImage::Format_MonoLSB)
|
||||
image = image.convertToFormat(QImage::Format_Mono);
|
||||
|
||||
int y;
|
||||
|
||||
if (nbits == 1 || nbits == 8) { // direct output
|
||||
for (y=image.height()-1; y>=0; y--) {
|
||||
if (d->write((char*)image.scanLine(y), bpl) == -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QSTACKARRAY(uchar, buf, bpl_bmp);
|
||||
uchar *b, *end;
|
||||
uchar *p;
|
||||
|
||||
for (y=image.height()-1; y>=0; y--) { // write the image bits
|
||||
if (nbits == 4) { // convert 8 -> 4 bits
|
||||
p = image.scanLine(y);
|
||||
b = buf;
|
||||
end = b + image.width()/2;
|
||||
while (b < end) {
|
||||
*b++ = (*p << 4) | (*(p+1) & 0x0f);
|
||||
p += 2;
|
||||
}
|
||||
if (image.width() & 1)
|
||||
*b = *p << 4;
|
||||
} else { // 32 bits
|
||||
QRgb *p = (QRgb *)image.scanLine(y);
|
||||
QRgb *end = p + image.width();
|
||||
b = buf;
|
||||
while (p < end) {
|
||||
*b++ = qBlue(*p);
|
||||
*b++ = qGreen(*p);
|
||||
*b++ = qRed(*p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QBmpHandler::QBmpHandler()
|
||||
: state(Ready)
|
||||
{
|
||||
}
|
||||
|
||||
bool QBmpHandler::readHeader()
|
||||
{
|
||||
state = Error;
|
||||
|
||||
QIODevice *d = device();
|
||||
QDataStream s(d);
|
||||
startpos = d->pos();
|
||||
|
||||
// Intel byte order
|
||||
s.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// read BMP file header
|
||||
if (!read_dib_fileheader(s, fileHeader))
|
||||
return false;
|
||||
|
||||
// read BMP info header
|
||||
if (!read_dib_infoheader(s, infoHeader))
|
||||
return false;
|
||||
|
||||
state = ReadHeader;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QBmpHandler::canRead() const
|
||||
{
|
||||
if (state == Ready && !canRead(device()))
|
||||
return false;
|
||||
|
||||
if (state != Error) {
|
||||
setFormat("bmp");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QBmpHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (Q_UNLIKELY(!device)) {
|
||||
qWarning("QBmpHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
QSTACKARRAY(char, head, 2);
|
||||
if (device->peek(head, sizeof(head)) != sizeof(head))
|
||||
return false;
|
||||
|
||||
return (qstrncmp(head, "BM", 2) == 0);
|
||||
}
|
||||
|
||||
bool QBmpHandler::read(QImage *image)
|
||||
{
|
||||
if (state == Error)
|
||||
return false;
|
||||
|
||||
if (!image) {
|
||||
qWarning("QBmpHandler::read: cannot read into null pointer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state == Ready && !readHeader()) {
|
||||
state = Error;
|
||||
return false;
|
||||
}
|
||||
|
||||
QIODevice *d = device();
|
||||
QDataStream s(d);
|
||||
|
||||
// Intel byte order
|
||||
s.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// read image
|
||||
if (!read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image))
|
||||
return false;
|
||||
|
||||
state = Ready;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QBmpHandler::write(const QImage &img)
|
||||
{
|
||||
QImage image(img);
|
||||
if (image.format() == QImage::Format_RGB16) {
|
||||
image = img.convertToFormat(QImage::Format_RGB32);
|
||||
}
|
||||
|
||||
QIODevice *d = device();
|
||||
QDataStream s(d);
|
||||
BMP_FILEHDR bf;
|
||||
int bpl_bmp;
|
||||
int bpl = image.bytesPerLine();
|
||||
|
||||
// Code partially repeated in qt_write_dib
|
||||
if (image.depth() == 8 && image.colorCount() <= 16) {
|
||||
bpl_bmp = (((bpl+1)/2+3)/4)*4;
|
||||
} else if (image.depth() == 32) {
|
||||
bpl_bmp = ((image.width()*24+31)/32)*4;
|
||||
} else {
|
||||
bpl_bmp = bpl;
|
||||
}
|
||||
|
||||
// Intel byte order
|
||||
s.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// build file header
|
||||
memcpy(bf.bfType, "BM", 2);
|
||||
|
||||
// write file header
|
||||
bf.bfReserved1 = 0;
|
||||
bf.bfReserved2 = 0;
|
||||
bf.bfOffBits = BMP_FILEHDR_SIZE + BMP_WIN + image.colorCount() * 4;
|
||||
bf.bfSize = bf.bfOffBits + bpl_bmp*image.height();
|
||||
s << bf;
|
||||
|
||||
// write image
|
||||
return qt_write_dib(s, image);
|
||||
}
|
||||
|
||||
bool QBmpHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
return option == Size
|
||||
|| option == ImageFormat;
|
||||
}
|
||||
|
||||
QVariant QBmpHandler::option(ImageOption option) const
|
||||
{
|
||||
if (option == Size) {
|
||||
if (state == Error)
|
||||
return QVariant();
|
||||
if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
|
||||
return QVariant();
|
||||
return QSize(infoHeader.biWidth, infoHeader.biHeight);
|
||||
} else if (option == ImageFormat) {
|
||||
if (state == Error)
|
||||
return QVariant();
|
||||
if (state == Ready && !const_cast<QBmpHandler*>(this)->readHeader())
|
||||
return QVariant();
|
||||
QImage::Format format;
|
||||
switch (infoHeader.biBitCount) {
|
||||
case 32:
|
||||
case 24:
|
||||
case 16:
|
||||
format = QImage::Format_RGB32;
|
||||
break;
|
||||
case 8:
|
||||
case 4:
|
||||
format = QImage::Format_Indexed8;
|
||||
break;
|
||||
default:
|
||||
format = QImage::Format_Mono;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void QBmpHandler::setOption(ImageOption option, const QVariant &value)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(value);
|
||||
}
|
||||
|
||||
QByteArray QBmpHandler::name() const
|
||||
{
|
||||
return "bmp";
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_IMAGEFORMAT_BMP
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtGui module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QBMPHANDLER_P_H
|
||||
#define QBMPHANDLER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "QtGui/qimageiohandler.h"
|
||||
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct BMP_FILEHDR { // BMP file header
|
||||
char bfType[2]; // "BM"
|
||||
qint32 bfSize; // size of file
|
||||
qint16 bfReserved1;
|
||||
qint16 bfReserved2;
|
||||
qint32 bfOffBits; // pointer to the pixmap bits
|
||||
};
|
||||
|
||||
struct BMP_INFOHDR { // BMP information header
|
||||
qint32 biSize; // size of this struct
|
||||
qint32 biWidth; // pixmap width
|
||||
qint32 biHeight; // pixmap height
|
||||
qint16 biPlanes; // should be 1
|
||||
qint16 biBitCount; // number of bits per pixel
|
||||
qint32 biCompression; // compression method
|
||||
qint32 biSizeImage; // size of image
|
||||
qint32 biXPelsPerMeter; // horizontal resolution
|
||||
qint32 biYPelsPerMeter; // vertical resolution
|
||||
qint32 biClrUsed; // number of colors used
|
||||
qint32 biClrImportant; // number of important colors
|
||||
};
|
||||
|
||||
class QBmpHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
QBmpHandler();
|
||||
bool canRead() const;
|
||||
bool read(QImage *image);
|
||||
bool write(const QImage &image);
|
||||
|
||||
QByteArray name() const;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
QVariant option(ImageOption option) const;
|
||||
void setOption(ImageOption option, const QVariant &value);
|
||||
bool supportsOption(ImageOption option) const;
|
||||
|
||||
private:
|
||||
bool readHeader();
|
||||
enum State {
|
||||
Ready,
|
||||
ReadHeader,
|
||||
Error
|
||||
};
|
||||
State state;
|
||||
BMP_FILEHDR fileHeader;
|
||||
BMP_INFOHDR infoHeader;
|
||||
int startpos;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_IMAGEFORMAT_BMP
|
||||
|
||||
#endif // QBMPHANDLER_P_H
|
|
@ -313,16 +313,12 @@ bool QImageData::checkForAlphaPixels() const
|
|||
formats:
|
||||
|
||||
\table
|
||||
\header \o Format \o Description \o Qt's support
|
||||
\row \o BMP \o Windows Bitmap \o Read/write
|
||||
\header \o Format \o Description \o Katie's support
|
||||
\row \o GIF \o Graphic Interchange Format (optional) \o Read
|
||||
\row \o JPG \o Joint Photographic Experts Group \o Read/write
|
||||
\row \o JPEG \o Joint Photographic Experts Group \o Read/write
|
||||
\row \o PNG \o Portable Network Graphics \o Read/write
|
||||
\row \o PBM \o Portable Bitmap \o Read
|
||||
\row \o PGM \o Portable Graymap \o Read
|
||||
\row \o PPM \o Portable Pixmap \o Read/write
|
||||
\row \o TIFF \o Tagged Image File Format \o Read/write
|
||||
\row \o XBM \o X11 Bitmap \o Read/write
|
||||
\row \o XPM \o X11 Pixmap \o Read/write
|
||||
\endtable
|
||||
|
@ -3571,7 +3567,7 @@ QImage QImage::rgbSwapped() const
|
|||
the image was successfully loaded; otherwise returns false.
|
||||
|
||||
The loader attempts to read the image using the specified \a format, e.g.,
|
||||
PNG or JPG. If \a format is not specified (which is the default), the
|
||||
PNG or XPM. If \a format is not specified (which is the default), the
|
||||
loader probes the file for a header to guess the file format.
|
||||
|
||||
The file name can either refer to an actual file on disk or to one
|
||||
|
@ -3621,7 +3617,7 @@ bool QImage::load(QIODevice* device, const char* format)
|
|||
returns false.
|
||||
|
||||
The loader attempts to read the image using the specified \a format, e.g.,
|
||||
PNG or JPG. If \a format is not specified (which is the default), the
|
||||
PNG or XPM. If \a format is not specified (which is the default), the
|
||||
loader probes the file for a header to guess the file format.
|
||||
|
||||
\sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
|
||||
|
@ -3742,9 +3738,9 @@ bool QImageData::doImageIO(const QImage *image, QImageWriter *writer, int qualit
|
|||
\fn QDataStream &operator<<(QDataStream &stream, const QImage &image)
|
||||
\relates QImage
|
||||
|
||||
Writes the given \a image to the given \a stream as a PNG image,
|
||||
or as a BMP image if the stream's version is 1. Note that writing
|
||||
the stream to a file will not produce a valid image file.
|
||||
Writes the given \a image to the given \a stream as a PNG image.
|
||||
Note that writing the stream to a file will not produce a valid
|
||||
image file.
|
||||
|
||||
\sa QImage::save(), {Serializing Qt Data Types}
|
||||
*/
|
||||
|
|
|
@ -490,11 +490,11 @@ QImageIOPlugin::~QImageIOPlugin()
|
|||
|
||||
Returns the capabilities on the plugin, based on the data in \a
|
||||
device and the format \a format. For example, if the
|
||||
QImageIOHandler supports the BMP format, and the data in the
|
||||
device starts with the characters "BM", this function should
|
||||
return \l CanRead. If \a format is "bmp" and the handler supports
|
||||
both reading and writing, this function should return \l CanRead |
|
||||
\l CanWrite.
|
||||
QImageIOHandler supports the PNG format, and the data in the
|
||||
device starts with the magick bits for that format, this function
|
||||
should return \l CanRead. If \a format is "png" and the handler
|
||||
supports both reading and writing, this function should return
|
||||
\l CanRead | \l CanWrite.
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -503,7 +503,7 @@ QImageIOPlugin::~QImageIOPlugin()
|
|||
Returns the list of image keys this plugin supports.
|
||||
|
||||
These keys are usually the names of the image formats that are implemented
|
||||
in the plugin (e.g., "jpg" or "gif").
|
||||
in the plugin (e.g., "svg" or "gif").
|
||||
|
||||
\sa capabilities()
|
||||
*/
|
||||
|
|
|
@ -114,7 +114,6 @@
|
|||
#include "qguicommon_p.h"
|
||||
|
||||
// image handlers
|
||||
#include "qbmphandler_p.h"
|
||||
#include "qppmhandler_p.h"
|
||||
#include "qxbmhandler_p.h"
|
||||
#include "qxpmhandler_p.h"
|
||||
|
@ -135,10 +134,6 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
|
|||
// check if we have built-in support for the format first
|
||||
if (form == "png") {
|
||||
handler = new QPngHandler;
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
} else if (form == "bmp") {
|
||||
handler = new QBmpHandler;
|
||||
#endif
|
||||
#ifndef QT_NO_IMAGEFORMAT_XPM
|
||||
} else if (form == "xpm") {
|
||||
handler = new QXpmHandler;
|
||||
|
@ -172,11 +167,6 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
|
|||
if (QPngHandler::canRead(device)) {
|
||||
handler = new QPngHandler;
|
||||
}
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
if (!handler && QBmpHandler::canRead(device)) {
|
||||
handler = new QBmpHandler;
|
||||
}
|
||||
#endif
|
||||
#ifndef QT_NO_IMAGEFORMAT_XPM
|
||||
if (!handler && QXpmHandler::canRead(device)) {
|
||||
handler = new QXpmHandler;
|
||||
|
@ -506,7 +496,7 @@ QIODevice *QImageReader::device() const
|
|||
QImageReader will create a QFile object and open it in \l
|
||||
QIODevice::ReadOnly mode, and use this when reading images.
|
||||
|
||||
If \a fileName does not include a file extension (e.g., .png or .bmp),
|
||||
If \a fileName does not include a file extension (e.g., .png or .xpm),
|
||||
QImageReader will cycle through all supported extensions until it finds
|
||||
a matching file.
|
||||
|
||||
|
@ -541,7 +531,7 @@ QString QImageReader::fileName() const
|
|||
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
|
||||
example, the "png" format supports a quality range from 0 (low
|
||||
quality, high compression) to 100 (high quality, low compression).
|
||||
|
||||
\sa quality()
|
||||
|
@ -1024,10 +1014,7 @@ QString QImageReader::errorString() const
|
|||
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()), and the BMP format allows you to determine the image's size
|
||||
without loading the whole image into memory (see size()).
|
||||
determine whether a certain option is supported by the current format.
|
||||
|
||||
\snippet doc/src/snippets/code/src_gui_image_qimagereader.cpp 3
|
||||
|
||||
|
@ -1078,32 +1065,19 @@ QByteArray QImageReader::imageFormat(QIODevice *device)
|
|||
|
||||
\table
|
||||
\header \o Format \o Description
|
||||
\row \o BMP \o Windows Bitmap
|
||||
\row \o GIF \o Graphic Interchange Format (optional)
|
||||
\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 PBM \o Portable Bitmap
|
||||
\row \o PGM \o Portable Graymap
|
||||
\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
|
||||
\row \o SVG \o Scalable Vector Graphics
|
||||
\row \o TGA \o Targa Image Format
|
||||
\endtable
|
||||
|
||||
Reading and writing SVG files is supported through Qt's
|
||||
\l{QtSvg Module}{SVG Module}.
|
||||
|
||||
TGA support only extends to reading non-RLE compressed files. In particular
|
||||
calls to \l{http://doc.qt.io/qt-4.8/qimageioplugin.html#capabilities}{capabilities}
|
||||
for the tga plugin returns only QImageIOPlugin::CanRead, not QImageIOPlugin::CanWrite.
|
||||
|
||||
To configure Qt with GIF support, pass \c -qt-gif to the \c
|
||||
configure script or check the appropriate option in the graphical
|
||||
installer.
|
||||
|
||||
Note that the QApplication instance must be created before this function is
|
||||
called.
|
||||
|
||||
|
@ -1113,9 +1087,6 @@ QList<QByteArray> QImageReader::supportedImageFormats()
|
|||
{
|
||||
QList<QByteArray> formats = QList<QByteArray>()
|
||||
<< "png"
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
<< "bmp"
|
||||
#endif
|
||||
#ifndef QT_NO_IMAGEFORMAT_PPM
|
||||
<< "ppm" << "pgm" << "pbm"
|
||||
#endif
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
#include "qguicommon_p.h"
|
||||
|
||||
// image handlers
|
||||
#include "qbmphandler_p.h"
|
||||
#include "qppmhandler_p.h"
|
||||
#include "qxbmhandler_p.h"
|
||||
#include "qxpmhandler_p.h"
|
||||
|
@ -104,10 +103,6 @@ static QImageIOHandler *createWriteHandlerHelper(QIODevice *device,
|
|||
// check if any built-in handlers can write the image
|
||||
if (form == "png") {
|
||||
handler = new QPngHandler;
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
} else if (form == "bmp") {
|
||||
handler = new QBmpHandler;
|
||||
#endif
|
||||
#ifndef QT_NO_IMAGEFORMAT_XPM
|
||||
} else if (form == "xpm") {
|
||||
handler = new QXpmHandler;
|
||||
|
@ -328,7 +323,7 @@ QString QImageWriter::fileName() const
|
|||
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
|
||||
example, the "png" format supports a quality range from 0 (low
|
||||
quality, high compression) to 100 (high quality, low compression).
|
||||
|
||||
\sa quality()
|
||||
|
@ -353,9 +348,7 @@ int QImageWriter::quality() const
|
|||
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).
|
||||
The value range of \a compression depends on the image format.
|
||||
|
||||
\sa compression()
|
||||
*/
|
||||
|
@ -508,12 +501,8 @@ bool QImageWriter::supportsOption(QImageIOHandler::ImageOption option) const
|
|||
|
||||
\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
|
||||
|
@ -530,9 +519,6 @@ QList<QByteArray> QImageWriter::supportedImageFormats()
|
|||
{
|
||||
QList<QByteArray> formats;
|
||||
formats << "png";
|
||||
#ifndef QT_NO_IMAGEFORMAT_BMP
|
||||
formats << "bmp";
|
||||
#endif
|
||||
#ifndef QT_NO_IMAGEFORMAT_PPM
|
||||
formats << "ppm";
|
||||
#endif
|
||||
|
|
|
@ -1356,10 +1356,7 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode)
|
|||
file, optionally manipulating the image data, before the QImage
|
||||
object is converted into a QPixmap to be shown on
|
||||
screen. Alternatively, if no manipulation is desired, the image
|
||||
file can be loaded directly into a QPixmap. On Windows, the
|
||||
QPixmap class also supports conversion between \c HBITMAP and
|
||||
QPixmap. On Symbian, the QPixmap class also supports conversion
|
||||
between CFbsBitmap and QPixmap.
|
||||
file can be loaded directly into a QPixmap.
|
||||
|
||||
QPixmap provides a collection of functions that can be used to
|
||||
obtain a variety of information about the pixmap. In addition,
|
||||
|
@ -1388,10 +1385,7 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode)
|
|||
|
||||
\table
|
||||
\header \o Format \o Description \o Qt's support
|
||||
\row \o BMP \o Windows Bitmap \o Read/write
|
||||
\row \o GIF \o Graphic Interchange Format (optional) \o Read
|
||||
\row \o JPG \o Joint Photographic Experts Group \o Read/write
|
||||
\row \o JPEG \o Joint Photographic Experts Group \o Read/write
|
||||
\row \o PNG \o Portable Network Graphics \o Read/write
|
||||
\row \o PBM \o Portable Bitmap \o Read
|
||||
\row \o PGM \o Portable Graymap \o Read
|
||||
|
@ -1457,16 +1451,6 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode)
|
|||
QPixmap using the fromImage(). If this is too expensive an
|
||||
operation, you can use QBitmap::fromImage() instead.
|
||||
|
||||
In addition, on Windows, the QPixmap class supports conversion to
|
||||
and from HBITMAP: the toWinHBITMAP() function creates a HBITMAP
|
||||
equivalent to the QPixmap, based on the given HBitmapFormat, and
|
||||
returns the HBITMAP handle. The fromWinHBITMAP() function returns
|
||||
a QPixmap that is equivalent to the given bitmap which has the
|
||||
specified format. The QPixmap class also supports conversion to
|
||||
and from HICON: the toWinHICON() function creates a HICON equivalent
|
||||
to the QPixmap, and returns the HICON handle. The fromWinHICON()
|
||||
function returns a QPixmap that is equivalent to the given icon.
|
||||
|
||||
In addition, on Symbian, the QPixmap class supports conversion to
|
||||
and from CFbsBitmap: the toSymbianCFbsBitmap() function creates
|
||||
CFbsBitmap equivalent to the QPixmap, based on given mode and returns
|
||||
|
@ -1587,7 +1571,7 @@ void QPixmap::setAlphaChannel(const QPixmap &alphaChannel)
|
|||
Returns the alpha channel of the pixmap as a new grayscale QPixmap in which
|
||||
each pixel's red, green, and blue values are given the alpha value of the
|
||||
original pixmap. The color depth of the returned pixmap is the system depth
|
||||
on X11 and 8-bit on Windows and Mac OS X.
|
||||
on X11 and 8-bit.
|
||||
|
||||
You can use this function while debugging
|
||||
to get a visible image of the alpha channel. If the pixmap doesn't have an
|
||||
|
@ -1640,8 +1624,7 @@ QBitmap QPixmap::mask() const
|
|||
/*!
|
||||
Returns the default pixmap depth used by the application.
|
||||
|
||||
On Windows and Mac, the default depth is always 32. On X11 and
|
||||
embedded, the depth of the screen will be returned by this
|
||||
On X11 and the depth of the screen will be returned by this
|
||||
function.
|
||||
|
||||
\sa depth(), QColormap::depth(), {QPixmap#Pixmap Information}{Pixmap Information}
|
||||
|
|
|
@ -530,28 +530,6 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_n
|
|||
bool hasAlpha = false;
|
||||
bool hasMask = false;
|
||||
|
||||
if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
|
||||
QBuffer buffer(&imageData);
|
||||
QImageWriter writer(&buffer, "jpeg");
|
||||
writer.setQuality(94);
|
||||
writer.write(image);
|
||||
dct = true;
|
||||
|
||||
if (format != QImage::Format_RGB32) {
|
||||
softMaskData.resize(w * h);
|
||||
uchar *sdata = (uchar *)softMaskData.data();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
const QRgb *rgb = (const QRgb *)image.constScanLine(y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
uchar alpha = qAlpha(*rgb);
|
||||
*sdata++ = alpha;
|
||||
hasMask |= (alpha < 255);
|
||||
hasAlpha |= (alpha != 0 && alpha != 255);
|
||||
++rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
|
||||
uchar *data = (uchar *)imageData.data();
|
||||
softMaskData.resize(w * h);
|
||||
|
@ -582,7 +560,6 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_n
|
|||
}
|
||||
if (format == QImage::Format_RGB32)
|
||||
hasAlpha = hasMask = false;
|
||||
}
|
||||
int maskObject = 0;
|
||||
int softMaskObject = 0;
|
||||
if (hasAlpha) {
|
||||
|
|
|
@ -272,13 +272,7 @@ static QByteArray compressHelper(const QImage &image, bool gray, int *format)
|
|||
|
||||
Q_ASSERT(image.format() != QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
if (depth != 1 && !gray && QImageWriter::supportedImageFormats().contains("jpeg")) {
|
||||
QBuffer buffer(&pixelData);
|
||||
QImageWriter writer(&buffer, "jpeg");
|
||||
writer.setQuality(94);
|
||||
writer.write(image);
|
||||
*format = DCT;
|
||||
} else {
|
||||
|
||||
int width = image.width();
|
||||
int height = image.height();
|
||||
int size = width*height;
|
||||
|
@ -346,7 +340,6 @@ static QByteArray compressHelper(const QImage &image, bool gray, int *format)
|
|||
pixelData = runlengthEncode(pixelData);
|
||||
*format = Runlength;
|
||||
}
|
||||
}
|
||||
return QPdf::ascii85Encode(pixelData);
|
||||
}
|
||||
|
||||
|
|
|
@ -2917,8 +2917,7 @@ QTextTableFormat::QTextTableFormat(const QTextFormat &fmt)
|
|||
occupy is specified using setWidth() and setHeight().
|
||||
|
||||
Images can be supplied in any format for which Qt has an image
|
||||
reader, so SVG drawings can be included alongside PNG, TIFF and
|
||||
other bitmap formats.
|
||||
reader, so SVG drawings can be included alongside PNG.
|
||||
|
||||
\sa QImage, QImageReader
|
||||
*/
|
||||
|
|
|
@ -238,11 +238,11 @@ QUrl QDeclarativeFolderListModel::parentFolder() const
|
|||
The \a nameFilters property contains a list of file name filters.
|
||||
The filters may include the ? and * wildcards.
|
||||
|
||||
The example below filters on PNG and JPEG files:
|
||||
The example below filters on PNG and XPM files:
|
||||
|
||||
\qml
|
||||
FolderListModel {
|
||||
nameFilters: [ "*.png", "*.jpg" ]
|
||||
nameFilters: [ "*.png", "*.xpm" ]
|
||||
}
|
||||
\endqml
|
||||
|
||||
|
|
|
@ -28,16 +28,7 @@ add_subdirectory(iconengines/svgiconengine)
|
|||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/imageformats")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/imageformats")
|
||||
add_subdirectory(imageformats/gif)
|
||||
add_subdirectory(imageformats/ico)
|
||||
add_subdirectory(imageformats/svg)
|
||||
add_subdirectory(imageformats/tga)
|
||||
if(WITH_JPEG AND JPEG_FOUND)
|
||||
add_subdirectory(imageformats/jpeg)
|
||||
endif()
|
||||
if(WITH_TIFF AND TIFF_FOUND)
|
||||
add_subdirectory(imageformats/tiff)
|
||||
endif()
|
||||
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/script")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins/script")
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
set(QICOPLUGIN_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qicohandler.h
|
||||
)
|
||||
|
||||
set(QICOPLUGIN_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qicoplugin.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qicohandler.cpp
|
||||
)
|
||||
|
||||
katie_setup_target(qicoplugin ${QICOPLUGIN_SOURCES} ${QICOPLUGIN_HEADERS})
|
||||
|
||||
add_library(qicoplugin MODULE ${qicoplugin_SOURCES})
|
||||
target_link_libraries(qicoplugin KtCore KtGui)
|
||||
set_target_properties(qicoplugin PROPERTIES OUTPUT_NAME qico)
|
||||
|
||||
katie_setup_plugin(qicoplugin)
|
||||
|
||||
install(
|
||||
TARGETS qicoplugin
|
||||
DESTINATION ${KATIE_PLUGINS_PATH}/imageformats
|
||||
COMPONENT Runtime
|
||||
)
|
|
@ -1,876 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/*!
|
||||
\class QtIcoHandler
|
||||
\since 4.4
|
||||
\brief The QtIcoHandler class provides support for the ICO image format.
|
||||
\internal
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "qicohandler.h"
|
||||
#include <QtCore/qendian.h>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QBuffer>
|
||||
#include "qvariant.h"
|
||||
#include "qcorecommon_p.h"
|
||||
#include "qguicommon_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// These next two structs represent how the icon information is stored
|
||||
// in an ICO file.
|
||||
typedef struct
|
||||
{
|
||||
quint8 bWidth; // Width of the image
|
||||
quint8 bHeight; // Height of the image (times 2)
|
||||
quint8 bColorCount; // Number of colors in image (0 if >=8bpp) [ not ture ]
|
||||
quint8 bReserved; // Reserved
|
||||
quint16 wPlanes; // Color Planes
|
||||
quint16 wBitCount; // Bits per pixel
|
||||
quint32 dwBytesInRes; // how many bytes in this resource?
|
||||
quint32 dwImageOffset; // where in the file is this image
|
||||
} ICONDIRENTRY, *LPICONDIRENTRY;
|
||||
#define ICONDIRENTRY_SIZE 16
|
||||
|
||||
typedef struct
|
||||
{
|
||||
quint16 idReserved; // Reserved
|
||||
quint16 idType; // resource type (1 for icons)
|
||||
quint16 idCount; // how many images?
|
||||
ICONDIRENTRY idEntries[1]; // the entries for each image
|
||||
} ICONDIR, *LPICONDIR;
|
||||
#define ICONDIR_SIZE 6 // Exclude the idEntries field
|
||||
|
||||
typedef struct { // BMP information header
|
||||
quint32 biSize; // size of this struct
|
||||
quint32 biWidth; // pixmap width
|
||||
quint32 biHeight; // pixmap height (specifies the combined height of the XOR and AND masks)
|
||||
quint16 biPlanes; // should be 1
|
||||
quint16 biBitCount; // number of bits per pixel
|
||||
quint32 biCompression; // compression method
|
||||
quint32 biSizeImage; // size of image
|
||||
quint32 biXPelsPerMeter; // horizontal resolution
|
||||
quint32 biYPelsPerMeter; // vertical resolution
|
||||
quint32 biClrUsed; // number of colors used
|
||||
quint32 biClrImportant; // number of important colors
|
||||
} BMP_INFOHDR ,*LPBMP_INFOHDR;
|
||||
#define BMP_INFOHDR_SIZE 40
|
||||
|
||||
class ICOReader
|
||||
{
|
||||
public:
|
||||
ICOReader(QIODevice * iodevice);
|
||||
int count();
|
||||
QImage iconAt(int index);
|
||||
static bool canRead(QIODevice *iodev);
|
||||
|
||||
static QList<QImage> read(QIODevice * device);
|
||||
|
||||
static bool write(QIODevice * device, const QList<QImage> & images);
|
||||
|
||||
private:
|
||||
bool readHeader();
|
||||
bool readIconEntry(int index, ICONDIRENTRY * iconEntry);
|
||||
|
||||
bool readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header);
|
||||
void findColorInfo(QImage & image);
|
||||
void readColorTable(QImage & image);
|
||||
|
||||
void readBMP(QImage & image);
|
||||
void read1BitBMP(QImage & image);
|
||||
void read4BitBMP(QImage & image);
|
||||
void read8BitBMP(QImage & image);
|
||||
void read16_24_32BMP(QImage & image);
|
||||
|
||||
struct IcoAttrib
|
||||
{
|
||||
int nbits;
|
||||
int ncolors;
|
||||
int h;
|
||||
int w;
|
||||
int depth;
|
||||
} icoAttrib;
|
||||
|
||||
QIODevice * iod;
|
||||
qint64 startpos;
|
||||
bool headerRead;
|
||||
ICONDIR iconDir;
|
||||
|
||||
};
|
||||
|
||||
// Data readers and writers that takes care of alignment and endian stuff.
|
||||
static bool readIconDirEntry(QIODevice *iodev, ICONDIRENTRY *iconDirEntry)
|
||||
{
|
||||
if (iodev) {
|
||||
QSTACKARRAY(uchar, tmp, ICONDIRENTRY_SIZE);
|
||||
if (iodev->read((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE) {
|
||||
iconDirEntry->bWidth = tmp[0];
|
||||
iconDirEntry->bHeight = tmp[1];
|
||||
iconDirEntry->bColorCount = tmp[2];
|
||||
iconDirEntry->bReserved = tmp[3];
|
||||
|
||||
iconDirEntry->wPlanes = qFromLittleEndian<quint16>(&tmp[4]);
|
||||
iconDirEntry->wBitCount = qFromLittleEndian<quint16>(&tmp[6]);
|
||||
iconDirEntry->dwBytesInRes = qFromLittleEndian<quint32>(&tmp[8]);
|
||||
iconDirEntry->dwImageOffset = qFromLittleEndian<quint32>(&tmp[12]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool writeIconDirEntry(QIODevice *iodev, const ICONDIRENTRY &iconEntry)
|
||||
{
|
||||
if (iodev) {
|
||||
QSTACKARRAY(uchar, tmp, ICONDIRENTRY_SIZE);
|
||||
tmp[0] = iconEntry.bWidth;
|
||||
tmp[1] = iconEntry.bHeight;
|
||||
tmp[2] = iconEntry.bColorCount;
|
||||
tmp[3] = iconEntry.bReserved;
|
||||
qToLittleEndian<quint16>(iconEntry.wPlanes, &tmp[4]);
|
||||
qToLittleEndian<quint16>(iconEntry.wBitCount, &tmp[6]);
|
||||
qToLittleEndian<quint32>(iconEntry.dwBytesInRes, &tmp[8]);
|
||||
qToLittleEndian<quint32>(iconEntry.dwImageOffset, &tmp[12]);
|
||||
return (iodev->write((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool readIconDir(QIODevice *iodev, ICONDIR *iconDir)
|
||||
{
|
||||
if (iodev) {
|
||||
QSTACKARRAY(uchar, tmp, ICONDIR_SIZE);
|
||||
if (iodev->read((char*)tmp, ICONDIR_SIZE) == ICONDIR_SIZE) {
|
||||
iconDir->idReserved = qFromLittleEndian<quint16>(&tmp[0]);
|
||||
iconDir->idType = qFromLittleEndian<quint16>(&tmp[2]);
|
||||
iconDir->idCount = qFromLittleEndian<quint16>(&tmp[4]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool writeIconDir(QIODevice *iodev, const ICONDIR &iconDir)
|
||||
{
|
||||
if (iodev) {
|
||||
QSTACKARRAY(uchar, tmp, 6);
|
||||
qToLittleEndian(iconDir.idReserved, tmp);
|
||||
qToLittleEndian(iconDir.idType, &tmp[2]);
|
||||
qToLittleEndian(iconDir.idCount, &tmp[4]);
|
||||
return (iodev->write((char*)tmp, 6) == 6);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader)
|
||||
{
|
||||
if (iodev) {
|
||||
QSTACKARRAY(uchar, header, BMP_INFOHDR_SIZE);
|
||||
if (iodev->read((char*)header, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE) {
|
||||
pHeader->biSize = qFromLittleEndian<quint32>(&header[0]);
|
||||
pHeader->biWidth = qFromLittleEndian<quint32>(&header[4]);
|
||||
pHeader->biHeight = qFromLittleEndian<quint32>(&header[8]);
|
||||
pHeader->biPlanes = qFromLittleEndian<quint16>(&header[12]);
|
||||
pHeader->biBitCount = qFromLittleEndian<quint16>(&header[14]);
|
||||
pHeader->biCompression = qFromLittleEndian<quint32>(&header[16]);
|
||||
pHeader->biSizeImage = qFromLittleEndian<quint32>(&header[20]);
|
||||
pHeader->biXPelsPerMeter = qFromLittleEndian<quint32>(&header[24]);
|
||||
pHeader->biYPelsPerMeter = qFromLittleEndian<quint32>(&header[28]);
|
||||
pHeader->biClrUsed = qFromLittleEndian<quint32>(&header[32]);
|
||||
pHeader->biClrImportant = qFromLittleEndian<quint32>(&header[36]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool writeBMPInfoHeader(QIODevice *iodev, const BMP_INFOHDR &header)
|
||||
{
|
||||
if (iodev) {
|
||||
QSTACKARRAY(uchar, tmp, BMP_INFOHDR_SIZE);
|
||||
qToLittleEndian<quint32>(header.biSize, &tmp[0]);
|
||||
qToLittleEndian<quint32>(header.biWidth, &tmp[4]);
|
||||
qToLittleEndian<quint32>(header.biHeight, &tmp[8]);
|
||||
qToLittleEndian<quint16>(header.biPlanes, &tmp[12]);
|
||||
qToLittleEndian<quint16>(header.biBitCount, &tmp[14]);
|
||||
qToLittleEndian<quint32>(header.biCompression, &tmp[16]);
|
||||
qToLittleEndian<quint32>(header.biSizeImage, &tmp[20]);
|
||||
qToLittleEndian<quint32>(header.biXPelsPerMeter, &tmp[24]);
|
||||
qToLittleEndian<quint32>(header.biYPelsPerMeter, &tmp[28]);
|
||||
qToLittleEndian<quint32>(header.biClrUsed, &tmp[32]);
|
||||
qToLittleEndian<quint32>(header.biClrImportant, &tmp[36]);
|
||||
|
||||
return (iodev->write((char*)tmp, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ICOReader::ICOReader(QIODevice * iodevice)
|
||||
: iod(iodevice)
|
||||
, startpos(0)
|
||||
, headerRead(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int ICOReader::count()
|
||||
{
|
||||
if (readHeader()) {
|
||||
return iconDir.idCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ICOReader::canRead(QIODevice *iodev)
|
||||
{
|
||||
Q_ASSERT(iodev);
|
||||
|
||||
bool isProbablyICO = false;
|
||||
qint64 oldPos = iodev->pos();
|
||||
|
||||
ICONDIR ikonDir;
|
||||
if (readIconDir(iodev, &ikonDir)) {
|
||||
qint64 readBytes = ICONDIR_SIZE;
|
||||
if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) {
|
||||
readBytes += ICONDIRENTRY_SIZE;
|
||||
// ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file.
|
||||
if ( ikonDir.idReserved == 0
|
||||
&& ikonDir.idType == 1
|
||||
&& ikonDir.idEntries[0].bReserved == 0
|
||||
&& ikonDir.idEntries[0].wPlanes <= 1
|
||||
&& ikonDir.idEntries[0].wBitCount <= 32 // Bits per pixel
|
||||
&& ikonDir.idEntries[0].dwBytesInRes >= 40 // Must be over 40, since sizeof (infoheader) == 40
|
||||
) {
|
||||
isProbablyICO = true;
|
||||
}
|
||||
|
||||
if (iodev->isSequential()) {
|
||||
// Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() !
|
||||
quint32 tmp = ikonDir.idEntries[0].dwImageOffset;
|
||||
iodev->ungetChar((tmp >> 24) & 0xff);
|
||||
iodev->ungetChar((tmp >> 16) & 0xff);
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
|
||||
tmp = ikonDir.idEntries[0].dwBytesInRes;
|
||||
iodev->ungetChar((tmp >> 24) & 0xff);
|
||||
iodev->ungetChar((tmp >> 16) & 0xff);
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
|
||||
tmp = ikonDir.idEntries[0].wBitCount;
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
|
||||
tmp = ikonDir.idEntries[0].wPlanes;
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
|
||||
iodev->ungetChar(ikonDir.idEntries[0].bReserved);
|
||||
iodev->ungetChar(ikonDir.idEntries[0].bColorCount);
|
||||
iodev->ungetChar(ikonDir.idEntries[0].bHeight);
|
||||
iodev->ungetChar(ikonDir.idEntries[0].bWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (iodev->isSequential()) {
|
||||
// Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() !
|
||||
quint32 tmp = ikonDir.idCount;
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
|
||||
tmp = ikonDir.idType;
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
|
||||
tmp = ikonDir.idReserved;
|
||||
iodev->ungetChar((tmp >> 8) & 0xff);
|
||||
iodev->ungetChar(tmp & 0xff);
|
||||
}
|
||||
}
|
||||
if (!iodev->isSequential()) {
|
||||
iodev->seek(oldPos);
|
||||
}
|
||||
|
||||
return isProbablyICO;
|
||||
}
|
||||
|
||||
bool ICOReader::readHeader()
|
||||
{
|
||||
if (iod && !headerRead) {
|
||||
startpos = iod->pos();
|
||||
if (readIconDir(iod, &iconDir)) {
|
||||
if (iconDir.idReserved == 0 || iconDir.idType == 1)
|
||||
headerRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
return headerRead;
|
||||
}
|
||||
|
||||
bool ICOReader::readIconEntry(int index, ICONDIRENTRY *iconEntry)
|
||||
{
|
||||
if (iod) {
|
||||
if (iod->seek(startpos + ICONDIR_SIZE + (index * ICONDIRENTRY_SIZE))) {
|
||||
return readIconDirEntry(iod, iconEntry);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ICOReader::readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header)
|
||||
{
|
||||
if (iod) {
|
||||
if (iod->seek(startpos + imageOffset)) {
|
||||
if (readBMPInfoHeader(iod, header)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ICOReader::findColorInfo(QImage & image)
|
||||
{
|
||||
if (icoAttrib.ncolors > 0) { // set color table
|
||||
readColorTable(image);
|
||||
} else if (icoAttrib.nbits == 16) { // don't support RGB values for 15/16 bpp
|
||||
image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ICOReader::readColorTable(QImage & image)
|
||||
{
|
||||
if (iod) {
|
||||
image.setColorCount(icoAttrib.ncolors);
|
||||
QSTACKARRAY(uchar, rgb, 4);
|
||||
for (int i=0; i<icoAttrib.ncolors; i++) {
|
||||
if (iod->read((char*)rgb, 4) != 4) {
|
||||
image = QImage();
|
||||
break;
|
||||
}
|
||||
image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
|
||||
}
|
||||
} else {
|
||||
image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ICOReader::readBMP(QImage & image)
|
||||
{
|
||||
if (icoAttrib.nbits == 1) { // 1 bit BMP image
|
||||
read1BitBMP(image);
|
||||
} else if (icoAttrib.nbits == 4) { // 4 bit BMP image
|
||||
read4BitBMP(image);
|
||||
} else if (icoAttrib.nbits == 8) {
|
||||
read8BitBMP(image);
|
||||
} else if (icoAttrib.nbits == 16 || icoAttrib.nbits == 24 || icoAttrib.nbits == 32 ) { // 16,24,32 bit BMP image
|
||||
read16_24_32BMP(image);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NOTE: A 1 bit BMP is only flipped vertically, and not horizontally like all other color depths!
|
||||
* (This is the same with the bitmask)
|
||||
*
|
||||
*/
|
||||
void ICOReader::read1BitBMP(QImage & image)
|
||||
{
|
||||
if (iod) {
|
||||
|
||||
int h = image.height();
|
||||
int bpl = image.bytesPerLine();
|
||||
|
||||
while (--h >= 0) {
|
||||
if (iod->read((char*)image.scanLine(h),bpl) != bpl) {
|
||||
image = QImage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ICOReader::read4BitBMP(QImage & image)
|
||||
{
|
||||
if (iod) {
|
||||
|
||||
int h = icoAttrib.h;
|
||||
int buflen = ((icoAttrib.w+7)/8)*4;
|
||||
QSTACKARRAY(uchar, buf, buflen);
|
||||
|
||||
while (--h >= 0) {
|
||||
if (iod->read((char*)buf,buflen) != buflen) {
|
||||
image = QImage();
|
||||
break;
|
||||
}
|
||||
uchar *p = image.scanLine(h);
|
||||
uchar *b = buf;
|
||||
for (int i=0; i<icoAttrib.w/2; i++) { // convert nibbles to bytes
|
||||
*p++ = *b >> 4;
|
||||
*p++ = *b++ & 0x0f;
|
||||
}
|
||||
if (icoAttrib.w & 1) // the last nibble
|
||||
*p = *b >> 4;
|
||||
}
|
||||
|
||||
} else {
|
||||
image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ICOReader::read8BitBMP(QImage & image)
|
||||
{
|
||||
if (iod) {
|
||||
|
||||
int h = icoAttrib.h;
|
||||
int bpl = image.bytesPerLine();
|
||||
|
||||
while (--h >= 0) {
|
||||
if (iod->read((char *)image.scanLine(h), bpl) != bpl) {
|
||||
image = QImage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ICOReader::read16_24_32BMP(QImage & image)
|
||||
{
|
||||
if (iod) {
|
||||
int h = icoAttrib.h;
|
||||
QRgb *p;
|
||||
QRgb *end;
|
||||
QSTACKARRAY(uchar, buf, image.bytesPerLine());
|
||||
int bpl = ((icoAttrib.w*icoAttrib.nbits+31)/32)*4;
|
||||
uchar *b;
|
||||
|
||||
while (--h >= 0) {
|
||||
p = (QRgb *)image.scanLine(h);
|
||||
end = p + icoAttrib.w;
|
||||
if (iod->read((char *)buf, bpl) != bpl) {
|
||||
image = QImage();
|
||||
break;
|
||||
}
|
||||
b = buf;
|
||||
while (p < end) {
|
||||
if (icoAttrib.nbits == 24)
|
||||
*p++ = qRgb(*(b+2), *(b+1), *b);
|
||||
else if (icoAttrib.nbits == 32)
|
||||
*p++ = qRgba(*(b+2), *(b+1), *b, *(b+3));
|
||||
b += icoAttrib.nbits/8;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
QImage ICOReader::iconAt(int index)
|
||||
{
|
||||
QImage img;
|
||||
|
||||
if (count() > index) { // forces header to be read
|
||||
|
||||
ICONDIRENTRY iconEntry;
|
||||
if (readIconEntry(index, &iconEntry)) {
|
||||
|
||||
static const uchar pngMagicData[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
|
||||
|
||||
iod->seek(iconEntry.dwImageOffset);
|
||||
|
||||
const QByteArray pngMagic = QByteArray::fromRawData((char*)pngMagicData, sizeof(pngMagicData));
|
||||
const bool isPngImage = (iod->read(pngMagic.size()) == pngMagic);
|
||||
|
||||
if (isPngImage) {
|
||||
iod->seek(iconEntry.dwImageOffset);
|
||||
return QImage::fromData(iod->read(iconEntry.dwBytesInRes), "png");
|
||||
}
|
||||
|
||||
BMP_INFOHDR header;
|
||||
if (readBMPHeader(iconEntry.dwImageOffset, &header)) {
|
||||
icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount;
|
||||
|
||||
switch (icoAttrib.nbits) {
|
||||
case 32:
|
||||
case 24:
|
||||
case 16:
|
||||
icoAttrib.depth = 32;
|
||||
break;
|
||||
case 8:
|
||||
case 4:
|
||||
icoAttrib.depth = 8;
|
||||
break;
|
||||
default:
|
||||
icoAttrib.depth = 1;
|
||||
}
|
||||
if (icoAttrib.depth == 32) // there's no colormap
|
||||
icoAttrib.ncolors = 0;
|
||||
else // # colors used
|
||||
icoAttrib.ncolors = header.biClrUsed ? header.biClrUsed : 1 << icoAttrib.nbits;
|
||||
if (icoAttrib.ncolors > 256) //color table can't be more than 256
|
||||
return img;
|
||||
icoAttrib.w = iconEntry.bWidth;
|
||||
if (icoAttrib.w == 0)
|
||||
icoAttrib.w = header.biWidth;
|
||||
icoAttrib.h = iconEntry.bHeight;
|
||||
if (icoAttrib.h == 0)
|
||||
icoAttrib.h = header.biHeight/2;
|
||||
|
||||
QImage::Format format = QImage::Format_ARGB32;
|
||||
if (icoAttrib.nbits == 24)
|
||||
format = QImage::Format_RGB32;
|
||||
else if (icoAttrib.ncolors == 2 && icoAttrib.depth == 1)
|
||||
format = QImage::Format_Mono;
|
||||
else if (icoAttrib.ncolors > 0)
|
||||
format = QImage::Format_Indexed8;
|
||||
|
||||
QImage image(icoAttrib.w, icoAttrib.h, format);
|
||||
if (!image.isNull()) {
|
||||
findColorInfo(image);
|
||||
if (!image.isNull()) {
|
||||
readBMP(image);
|
||||
if (!image.isNull()) {
|
||||
QImage mask(image.width(), image.height(), QImage::Format_Mono);
|
||||
if (!mask.isNull()) {
|
||||
mask.setColorTable(monoColorTable());
|
||||
read1BitBMP(mask);
|
||||
if (!mask.isNull()) {
|
||||
img = QImage(image.width(), image.height(), QImage::Format_ARGB32 );
|
||||
img = image;
|
||||
img.setAlphaChannel(mask);
|
||||
// (Luckily, it seems that setAlphaChannel() does not ruin the alpha values
|
||||
// of partially transparent pixels in those icons that have that)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Reads all the icons from the given \a device, and returns them as
|
||||
a list of QImage objects.
|
||||
|
||||
Each image has an alpha channel that represents the mask from the
|
||||
corresponding icon.
|
||||
|
||||
\sa write()
|
||||
*/
|
||||
QList<QImage> ICOReader::read(QIODevice * device)
|
||||
{
|
||||
QList<QImage> images;
|
||||
|
||||
ICOReader reader(device);
|
||||
for (int i = 0; i < reader.count(); i++)
|
||||
images += reader.iconAt(i);
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Writes all the QImages in the \a images list to the given \a
|
||||
device. Returns true if the images are written successfully;
|
||||
otherwise returns false.
|
||||
|
||||
The first image in the list is stored as the first icon in the
|
||||
device, and is therefore used as the default icon by applications.
|
||||
The alpha channel of each image is converted to a mask for each
|
||||
corresponding icon.
|
||||
|
||||
\sa read()
|
||||
*/
|
||||
bool ICOReader::write(QIODevice * device, const QList<QImage> & images)
|
||||
{
|
||||
bool retValue = false;
|
||||
|
||||
if (images.count()) {
|
||||
|
||||
qint64 origOffset = device->pos();
|
||||
|
||||
ICONDIR id;
|
||||
id.idReserved = 0;
|
||||
id.idType = 1;
|
||||
id.idCount = images.count();
|
||||
|
||||
ICONDIRENTRY * entries = new ICONDIRENTRY[id.idCount];
|
||||
BMP_INFOHDR * bmpHeaders = new BMP_INFOHDR[id.idCount];
|
||||
QByteArray * imageData = new QByteArray[id.idCount];
|
||||
|
||||
for (int i=0; i<id.idCount; i++) {
|
||||
|
||||
QImage image = images[i];
|
||||
// Scale down the image if it is larger than 128 pixels in either width or height
|
||||
if (image.width() > 128 || image.height() > 128)
|
||||
{
|
||||
image = image.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
QImage maskImage(image.width(), image.height(), QImage::Format_Mono);
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
if (image.hasAlphaChannel()) {
|
||||
maskImage = image.createAlphaMask();
|
||||
} else {
|
||||
maskImage.fill(0xff);
|
||||
}
|
||||
maskImage = maskImage.convertToFormat(QImage::Format_Mono);
|
||||
|
||||
int nbits = 32;
|
||||
int bpl_bmp = ((image.width()*nbits+31)/32)*4;
|
||||
|
||||
entries[i].bColorCount = 0;
|
||||
entries[i].bReserved = 0;
|
||||
entries[i].wBitCount = nbits;
|
||||
entries[i].bHeight = image.height();
|
||||
entries[i].bWidth = image.width();
|
||||
entries[i].dwBytesInRes = BMP_INFOHDR_SIZE + (bpl_bmp * image.height())
|
||||
+ (maskImage.bytesPerLine() * maskImage.height());
|
||||
entries[i].wPlanes = 1;
|
||||
if (i == 0)
|
||||
entries[i].dwImageOffset = origOffset + ICONDIR_SIZE
|
||||
+ (id.idCount * ICONDIRENTRY_SIZE);
|
||||
else
|
||||
entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes;
|
||||
|
||||
bmpHeaders[i].biBitCount = entries[i].wBitCount;
|
||||
bmpHeaders[i].biClrImportant = 0;
|
||||
bmpHeaders[i].biClrUsed = entries[i].bColorCount;
|
||||
bmpHeaders[i].biCompression = 0;
|
||||
bmpHeaders[i].biHeight = entries[i].bHeight * 2; // 2 is for the mask
|
||||
bmpHeaders[i].biPlanes = entries[i].wPlanes;
|
||||
bmpHeaders[i].biSize = BMP_INFOHDR_SIZE;
|
||||
bmpHeaders[i].biSizeImage = entries[i].dwBytesInRes - BMP_INFOHDR_SIZE;
|
||||
bmpHeaders[i].biWidth = entries[i].bWidth;
|
||||
bmpHeaders[i].biXPelsPerMeter = 0;
|
||||
bmpHeaders[i].biYPelsPerMeter = 0;
|
||||
|
||||
QBuffer buffer(&imageData[i]);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
QSTACKARRAY(uchar, buf, bpl_bmp);
|
||||
uchar *b;
|
||||
int y;
|
||||
for (y = image.height() - 1; y >= 0; y--) { // write the image bits
|
||||
// 32 bits
|
||||
QRgb *p = (QRgb *)image.scanLine(y);
|
||||
QRgb *end = p + image.width();
|
||||
b = buf;
|
||||
int x = 0;
|
||||
while (p < end) {
|
||||
*b++ = qBlue(*p);
|
||||
*b++ = qGreen(*p);
|
||||
*b++ = qRed(*p);
|
||||
*b++ = qAlpha(*p);
|
||||
if (qAlpha(*p) > 0) // Even mostly transparent pixels must not be masked away
|
||||
maskImage.setPixel(x, y, Qt::color1); // (i.e. createAlphaMask() takes away too much)
|
||||
p++;
|
||||
x++;
|
||||
}
|
||||
buffer.write((char*)buf, bpl_bmp);
|
||||
}
|
||||
|
||||
maskImage.invertPixels(); // seems as though it needs this
|
||||
// NOTE! !! The mask is only flipped vertically - not horizontally !!
|
||||
for (y = maskImage.height() - 1; y >= 0; y--)
|
||||
buffer.write((char*)maskImage.scanLine(y), maskImage.bytesPerLine());
|
||||
}
|
||||
|
||||
if (writeIconDir(device, id)) {
|
||||
int i;
|
||||
bool bOK = true;
|
||||
for (i = 0; i < id.idCount && bOK; i++) {
|
||||
bOK = writeIconDirEntry(device, entries[i]);
|
||||
}
|
||||
if (bOK) {
|
||||
for (i = 0; i < id.idCount && bOK; i++) {
|
||||
bOK = writeBMPInfoHeader(device, bmpHeaders[i]);
|
||||
bOK &= (device->write(imageData[i]) == (int) imageData[i].size());
|
||||
}
|
||||
retValue = bOK;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] entries;
|
||||
delete [] bmpHeaders;
|
||||
delete [] imageData;
|
||||
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs an instance of QtIcoHandler initialized to use \a device.
|
||||
*/
|
||||
QtIcoHandler::QtIcoHandler(QIODevice *device)
|
||||
{
|
||||
m_currentIconIndex = 0;
|
||||
setDevice(device);
|
||||
m_pICOReader = new ICOReader(device);
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructor for QtIcoHandler.
|
||||
*/
|
||||
QtIcoHandler::~QtIcoHandler()
|
||||
{
|
||||
delete m_pICOReader;
|
||||
}
|
||||
|
||||
QVariant QtIcoHandler::option(ImageOption option) const
|
||||
{
|
||||
if (option == Size) {
|
||||
QIODevice *device = QImageIOHandler::device();
|
||||
qint64 oldPos = device->pos();
|
||||
ICONDIRENTRY iconEntry;
|
||||
if (device->seek(oldPos + ICONDIR_SIZE + (m_currentIconIndex * ICONDIRENTRY_SIZE))) {
|
||||
if (readIconDirEntry(device, &iconEntry)) {
|
||||
device->seek(oldPos);
|
||||
return QSize(iconEntry.bWidth, iconEntry.bHeight);
|
||||
}
|
||||
}
|
||||
if (!device->isSequential()) {
|
||||
device->seek(oldPos);
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool QtIcoHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
return option == Size;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Verifies if some values (magic bytes) are set as expected in the header of the file.
|
||||
* If the magic bytes were found, it is assumed that the QtIcoHandler can read the file.
|
||||
*
|
||||
*/
|
||||
bool QtIcoHandler::canRead() const
|
||||
{
|
||||
if (QtIcoHandler::canRead(device())) {
|
||||
setFormat("ico");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! This static function is used by the plugin code, and is provided for convenience only.
|
||||
\a device must be an opened device with pointing to the start of the header data of the ICO file.
|
||||
*/
|
||||
bool QtIcoHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (Q_UNLIKELY(!device)) {
|
||||
qWarning("QtIcoHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ICOReader::canRead(device);
|
||||
}
|
||||
|
||||
/*! \reimp
|
||||
|
||||
*/
|
||||
bool QtIcoHandler::read(QImage *image)
|
||||
{
|
||||
bool bSuccess = false;
|
||||
QImage img = m_pICOReader->iconAt(m_currentIconIndex);
|
||||
|
||||
// Make sure we only write to \a image when we succeed.
|
||||
if (!img.isNull()) {
|
||||
bSuccess = true;
|
||||
*image = img;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
|
||||
/*! \reimp
|
||||
|
||||
*/
|
||||
bool QtIcoHandler::write(const QImage &image)
|
||||
{
|
||||
QIODevice *device = QImageIOHandler::device();
|
||||
QList<QImage> imgs;
|
||||
imgs.append(image);
|
||||
return ICOReader::write(device, imgs);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Return the common identifier of the format.
|
||||
* For ICO format this will return "ico".
|
||||
*/
|
||||
QByteArray QtIcoHandler::name() const
|
||||
{
|
||||
return "ico";
|
||||
}
|
||||
|
||||
|
||||
/*! \reimp
|
||||
|
||||
*/
|
||||
int QtIcoHandler::imageCount() const
|
||||
{
|
||||
return m_pICOReader->count();
|
||||
}
|
||||
|
||||
/*! \reimp
|
||||
|
||||
*/
|
||||
bool QtIcoHandler::jumpToImage(int imageNumber)
|
||||
{
|
||||
if (imageNumber < imageCount()) {
|
||||
m_currentIconIndex = imageNumber;
|
||||
}
|
||||
|
||||
return (imageNumber < imageCount());
|
||||
}
|
||||
|
||||
/*! \reimp
|
||||
|
||||
*/
|
||||
bool QtIcoHandler::jumpToNextImage()
|
||||
{
|
||||
return jumpToImage(m_currentIconIndex + 1);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,59 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef QTICOHANDLER_H
|
||||
#define QTICOHANDLER_H
|
||||
|
||||
#include <QtGui/QImageIOHandler>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class ICOReader;
|
||||
class QtIcoHandler: public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
QtIcoHandler(QIODevice *device);
|
||||
virtual ~QtIcoHandler();
|
||||
|
||||
bool canRead() const;
|
||||
bool read(QImage *image);
|
||||
bool write(const QImage &image);
|
||||
|
||||
QByteArray name() const;
|
||||
|
||||
int imageCount() const;
|
||||
bool jumpToImage(int imageNumber);
|
||||
bool jumpToNextImage();
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
bool supportsOption(ImageOption option) const;
|
||||
QVariant option(ImageOption option) const;
|
||||
|
||||
private:
|
||||
int m_currentIconIndex;
|
||||
ICOReader *m_pICOReader;
|
||||
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif /* QTICOHANDLER_H */
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qimageiohandler.h"
|
||||
#include "qdebug.h"
|
||||
#include "qicohandler.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QICOPlugin : public QImageIOPlugin
|
||||
{
|
||||
public:
|
||||
QStringList keys() const;
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
|
||||
};
|
||||
|
||||
QStringList QICOPlugin::keys() const
|
||||
{
|
||||
static const QStringList list = QStringList()
|
||||
<< QLatin1String("ico");
|
||||
return list;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities QICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "ico")
|
||||
return Capabilities(CanRead | CanWrite);
|
||||
if (!format.isEmpty())
|
||||
return 0;
|
||||
if (!device->isOpen())
|
||||
return 0;
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && QtIcoHandler::canRead(device))
|
||||
cap |= CanRead;
|
||||
if (device->isWritable())
|
||||
cap |= CanWrite;
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *QICOPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new QtIcoHandler(device);
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2(qico, QICOPlugin)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,24 +0,0 @@
|
|||
include_directories(${JPEG_INCLUDE_DIR})
|
||||
|
||||
set(QJPEGPLUGIN_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qjpeghandler_p.h
|
||||
)
|
||||
|
||||
set(QJPEGPLUGIN_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qjpegplugin.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qjpeghandler.cpp
|
||||
)
|
||||
|
||||
katie_setup_target(qjpegplugin ${QJPEGPLUGIN_SOURCES} ${QJPEGPLUGIN_HEADERS})
|
||||
|
||||
add_library(qjpegplugin MODULE ${qjpegplugin_SOURCES})
|
||||
target_link_libraries(qjpegplugin KtCore KtGui ${JPEG_LIBRARIES})
|
||||
set_target_properties(qjpegplugin PROPERTIES OUTPUT_NAME qjpeg)
|
||||
|
||||
katie_setup_plugin(qjpegplugin)
|
||||
|
||||
install(
|
||||
TARGETS qjpegplugin
|
||||
DESTINATION ${KATIE_PLUGINS_PATH}/imageformats
|
||||
COMPONENT Runtime
|
||||
)
|
|
@ -1,845 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qjpeghandler_p.h"
|
||||
|
||||
#include "qimage.h"
|
||||
#include "qvariant.h"
|
||||
#include "qvector.h"
|
||||
#include "qbuffer.h"
|
||||
#include "qplatformdefs.h"
|
||||
#include "qcorecommon_p.h"
|
||||
#include "qguicommon_p.h"
|
||||
|
||||
#include <stdio.h> // jpeglib needs this to be pre-included
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef FAR
|
||||
#undef FAR
|
||||
#endif
|
||||
|
||||
// including jpeglib.h seems to be a little messy
|
||||
extern "C" {
|
||||
|
||||
#define XMD_H // shut JPEGlib up
|
||||
#include <jpeglib.h>
|
||||
#ifdef const
|
||||
# undef const // remove crazy C hackery in jconfig.h
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(JPEG_TRUE)
|
||||
// this jpeglib.h uses JPEG_boolean
|
||||
typedef JPEG_boolean boolean;
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
void QT_FASTCALL convert_rgb888_to_rgb32_C(quint32 *dst, const uchar *src, int len)
|
||||
{
|
||||
// Expand 24->32 bpp.
|
||||
for (int i = 0; i < len; ++i) {
|
||||
*dst++ = qRgb(src[0], src[1], src[2]);
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
|
||||
struct my_error_mgr : public jpeg_error_mgr {
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
#if defined(Q_C_CALLBACKS)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static void my_error_exit (j_common_ptr cinfo)
|
||||
{
|
||||
my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
|
||||
QSTACKARRAY(char, buffer, JMSG_LENGTH_MAX);
|
||||
(*cinfo->err->format_message)(cinfo, buffer);
|
||||
qWarning("%s", buffer);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
#if defined(Q_C_CALLBACKS)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct my_jpeg_source_mgr : public jpeg_source_mgr {
|
||||
// Nothing dynamic - cannot rely on destruction over longjump
|
||||
QIODevice *device;
|
||||
JOCTET buffer[QT_BUFFSIZE];
|
||||
const QBuffer *memDevice;
|
||||
|
||||
public:
|
||||
my_jpeg_source_mgr(QIODevice *device);
|
||||
};
|
||||
|
||||
#if defined(Q_C_CALLBACKS)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static void qt_init_source(j_decompress_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
|
||||
{
|
||||
my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
|
||||
qint64 num_read = 0;
|
||||
if (src->memDevice) {
|
||||
src->next_input_byte = (const JOCTET *)(src->memDevice->data().constData() + src->memDevice->pos());
|
||||
num_read = src->memDevice->data().size() - src->memDevice->pos();
|
||||
src->device->seek(src->memDevice->data().size());
|
||||
} else {
|
||||
src->next_input_byte = src->buffer;
|
||||
num_read = src->device->read((char*)src->buffer, QT_BUFFSIZE);
|
||||
}
|
||||
if (num_read <= 0) {
|
||||
// Insert a fake EOI marker - as per jpeglib recommendation
|
||||
src->next_input_byte = src->buffer;
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
src->bytes_in_buffer = 2;
|
||||
} else {
|
||||
src->bytes_in_buffer = num_read;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
|
||||
|
||||
// `dumb' implementation from jpeglib
|
||||
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if (num_bytes > 0) {
|
||||
while (num_bytes > (long) src->bytes_in_buffer) { // Should not happen in case of memDevice
|
||||
num_bytes -= (long) src->bytes_in_buffer;
|
||||
(void) qt_fill_input_buffer(cinfo);
|
||||
/* note we assume that qt_fill_input_buffer will never return false,
|
||||
* so suspension need not be handled.
|
||||
*/
|
||||
}
|
||||
src->next_input_byte += (size_t) num_bytes;
|
||||
src->bytes_in_buffer -= (size_t) num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static void qt_term_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
|
||||
if (!src->device->isSequential())
|
||||
src->device->seek(src->device->pos() - src->bytes_in_buffer);
|
||||
}
|
||||
|
||||
#if defined(Q_C_CALLBACKS)
|
||||
}
|
||||
#endif
|
||||
|
||||
inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device)
|
||||
{
|
||||
jpeg_source_mgr::init_source = qt_init_source;
|
||||
jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
|
||||
jpeg_source_mgr::skip_input_data = qt_skip_input_data;
|
||||
jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
|
||||
jpeg_source_mgr::term_source = qt_term_source;
|
||||
this->device = device;
|
||||
memDevice = qobject_cast<QBuffer *>(device);
|
||||
bytes_in_buffer = 0;
|
||||
next_input_byte = buffer;
|
||||
}
|
||||
|
||||
|
||||
inline static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
|
||||
{
|
||||
(void) jpeg_calc_output_dimensions(cinfo);
|
||||
|
||||
w = cinfo->output_width;
|
||||
h = cinfo->output_height;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define HIGH_QUALITY_THRESHOLD 50
|
||||
|
||||
inline static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
|
||||
{
|
||||
|
||||
bool result = true;
|
||||
switch (cinfo->output_components) {
|
||||
case 1:
|
||||
format = QImage::Format_Indexed8;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
format = QImage::Format_RGB32;
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
cinfo->output_scanline = cinfo->output_height;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
|
||||
const QSize& size)
|
||||
{
|
||||
QImage::Format format;
|
||||
switch (info->output_components) {
|
||||
case 1:
|
||||
format = QImage::Format_Indexed8;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
format = QImage::Format_RGB32;
|
||||
break;
|
||||
default:
|
||||
return false; // unsupported format
|
||||
}
|
||||
|
||||
if (dest->size() != size || dest->format() != format) {
|
||||
*dest = QImage(size, format);
|
||||
|
||||
if (format == QImage::Format_Indexed8) {
|
||||
dest->setColorTable(grayColorTable());
|
||||
}
|
||||
}
|
||||
|
||||
return !dest->isNull();
|
||||
}
|
||||
|
||||
static bool read_jpeg_image(QImage *outImage,
|
||||
QSize scaledSize, QRect scaledClipRect,
|
||||
QRect clipRect, int quality, j_decompress_ptr info, struct my_error_mgr* err )
|
||||
{
|
||||
if (!setjmp(err->setjmp_buffer)) {
|
||||
// If possible, merge the scaledClipRect into either scaledSize
|
||||
// or clipRect to avoid doing a separate scaled clipping pass.
|
||||
// Best results are achieved by clipping before scaling, not after.
|
||||
if (!scaledClipRect.isEmpty()) {
|
||||
if (scaledSize.isEmpty() && clipRect.isEmpty()) {
|
||||
// No clipping or scaling before final clip.
|
||||
clipRect = scaledClipRect;
|
||||
scaledClipRect = QRect();
|
||||
} else if (scaledSize.isEmpty()) {
|
||||
// Clipping, but no scaling: combine the clip regions.
|
||||
scaledClipRect.translate(clipRect.topLeft());
|
||||
clipRect = scaledClipRect.intersected(clipRect);
|
||||
scaledClipRect = QRect();
|
||||
} else if (clipRect.isEmpty()) {
|
||||
// No clipping, but scaling: if we can map back to an
|
||||
// integer pixel boundary, then clip before scaling.
|
||||
if ((info->image_width % scaledSize.width()) == 0 &&
|
||||
(info->image_height % scaledSize.height()) == 0) {
|
||||
int x = scaledClipRect.x() * info->image_width /
|
||||
scaledSize.width();
|
||||
int y = scaledClipRect.y() * info->image_height /
|
||||
scaledSize.height();
|
||||
int width = (scaledClipRect.right() + 1) *
|
||||
info->image_width / scaledSize.width() - x;
|
||||
int height = (scaledClipRect.bottom() + 1) *
|
||||
info->image_height / scaledSize.height() - y;
|
||||
clipRect = QRect(x, y, width, height);
|
||||
scaledSize = scaledClipRect.size();
|
||||
scaledClipRect = QRect();
|
||||
}
|
||||
} else {
|
||||
// Clipping and scaling: too difficult to figure out,
|
||||
// and not a likely use case, so do it the long way.
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the scale factor to pass to libjpeg for quick downscaling.
|
||||
if (!scaledSize.isEmpty()) {
|
||||
if (clipRect.isEmpty()) {
|
||||
info->scale_denom =
|
||||
qMin(info->image_width / scaledSize.width(),
|
||||
info->image_height / scaledSize.height());
|
||||
} else {
|
||||
info->scale_denom =
|
||||
qMin(clipRect.width() / scaledSize.width(),
|
||||
clipRect.height() / scaledSize.height());
|
||||
}
|
||||
if (info->scale_denom < 2) {
|
||||
info->scale_denom = 1;
|
||||
} else if (info->scale_denom < 4) {
|
||||
info->scale_denom = 2;
|
||||
} else if (info->scale_denom < 8) {
|
||||
info->scale_denom = 4;
|
||||
} else {
|
||||
info->scale_denom = 8;
|
||||
}
|
||||
info->scale_num = 1;
|
||||
if (!clipRect.isEmpty()) {
|
||||
// Correct the scale factor so that we clip accurately.
|
||||
// It is recommended that the clip rectangle be aligned
|
||||
// on an 8-pixel boundary for best performance.
|
||||
while (info->scale_denom > 1 &&
|
||||
((clipRect.x() % info->scale_denom) != 0 ||
|
||||
(clipRect.y() % info->scale_denom) != 0 ||
|
||||
(clipRect.width() % info->scale_denom) != 0 ||
|
||||
(clipRect.height() % info->scale_denom) != 0)) {
|
||||
info->scale_denom /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If high quality not required, use fast decompression
|
||||
if( quality < HIGH_QUALITY_THRESHOLD ) {
|
||||
info->dct_method = JDCT_IFAST;
|
||||
info->do_fancy_upsampling = FALSE;
|
||||
}
|
||||
|
||||
(void) jpeg_calc_output_dimensions(info);
|
||||
|
||||
// Determine the clip region to extract.
|
||||
QRect imageRect(0, 0, info->output_width, info->output_height);
|
||||
QRect clip;
|
||||
if (clipRect.isEmpty()) {
|
||||
clip = imageRect;
|
||||
} else if (info->scale_denom == info->scale_num) {
|
||||
clip = clipRect.intersected(imageRect);
|
||||
} else {
|
||||
// The scale factor was corrected above to ensure that
|
||||
// we don't miss pixels when we scale the clip rectangle.
|
||||
clip = QRect(clipRect.x() / int(info->scale_denom),
|
||||
clipRect.y() / int(info->scale_denom),
|
||||
clipRect.width() / int(info->scale_denom),
|
||||
clipRect.height() / int(info->scale_denom));
|
||||
clip = clip.intersected(imageRect);
|
||||
}
|
||||
|
||||
// Allocate memory for the clipped QImage.
|
||||
if (!ensureValidImage(outImage, info, clip.size()))
|
||||
longjmp(err->setjmp_buffer, 1);
|
||||
|
||||
// Avoid memcpy() overhead if grayscale with no clipping.
|
||||
bool quickGray = (info->output_components == 1 &&
|
||||
clip == imageRect);
|
||||
if (!quickGray) {
|
||||
// Ask the jpeg library to allocate a temporary row.
|
||||
// The library will automatically delete it for us later.
|
||||
// The libjpeg docs say we should do this before calling
|
||||
// jpeg_start_decompress(). We can't use "new" here
|
||||
// because we are inside the setjmp() block and an error
|
||||
// in the jpeg input stream would cause a memory leak.
|
||||
JSAMPARRAY rows = (info->mem->alloc_sarray)
|
||||
((j_common_ptr)info, JPOOL_IMAGE,
|
||||
info->output_width * info->output_components, 1);
|
||||
|
||||
(void) jpeg_start_decompress(info);
|
||||
|
||||
while (info->output_scanline < info->output_height) {
|
||||
int y = int(info->output_scanline) - clip.y();
|
||||
if (y >= clip.height())
|
||||
break; // We've read the entire clip region, so abort.
|
||||
|
||||
(void) jpeg_read_scanlines(info, rows, 1);
|
||||
|
||||
if (y < 0)
|
||||
continue; // Haven't reached the starting line yet.
|
||||
|
||||
if (info->output_components == 3) {
|
||||
uchar *in = rows[0] + clip.x() * 3;
|
||||
QRgb *out = (QRgb*)outImage->scanLine(y);
|
||||
convert_rgb888_to_rgb32_C(out, in, clip.width());
|
||||
} else if (info->out_color_space == JCS_CMYK) {
|
||||
// Convert CMYK->RGB.
|
||||
uchar *in = rows[0] + clip.x() * 4;
|
||||
QRgb *out = (QRgb*)outImage->scanLine(y);
|
||||
for (int i = 0; i < clip.width(); ++i) {
|
||||
int k = in[3];
|
||||
*out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
|
||||
k * in[2] / 255);
|
||||
in += 4;
|
||||
}
|
||||
} else if (info->output_components == 1) {
|
||||
// Grayscale.
|
||||
memcpy(outImage->scanLine(y),
|
||||
rows[0] + clip.x(), clip.width());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Load unclipped grayscale data directly into the QImage.
|
||||
(void) jpeg_start_decompress(info);
|
||||
while (info->output_scanline < info->output_height) {
|
||||
uchar *row = outImage->scanLine(info->output_scanline);
|
||||
(void) jpeg_read_scanlines(info, &row, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->output_scanline == info->output_height)
|
||||
(void) jpeg_finish_decompress(info);
|
||||
|
||||
if (info->density_unit == 1) {
|
||||
outImage->setDotsPerMeterX(int(100. * info->X_density / 2.54));
|
||||
outImage->setDotsPerMeterY(int(100. * info->Y_density / 2.54));
|
||||
} else if (info->density_unit == 2) {
|
||||
outImage->setDotsPerMeterX(int(100. * info->X_density));
|
||||
outImage->setDotsPerMeterY(int(100. * info->Y_density));
|
||||
}
|
||||
|
||||
if (scaledSize.isValid() && scaledSize != clip.size()) {
|
||||
*outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >= HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
|
||||
}
|
||||
|
||||
if (!scaledClipRect.isEmpty())
|
||||
*outImage = outImage->copy(scaledClipRect);
|
||||
return !outImage->isNull();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
|
||||
// Nothing dynamic - cannot rely on destruction over longjump
|
||||
QIODevice *device;
|
||||
JOCTET buffer[QT_BUFFSIZE];
|
||||
|
||||
public:
|
||||
my_jpeg_destination_mgr(QIODevice *);
|
||||
};
|
||||
|
||||
|
||||
#if defined(Q_C_CALLBACKS)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static void qt_init_destination(j_compress_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
|
||||
{
|
||||
my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
|
||||
|
||||
int written = dest->device->write((char*)dest->buffer, QT_BUFFSIZE);
|
||||
if (written == -1)
|
||||
(*cinfo->err->error_exit)((j_common_ptr)cinfo);
|
||||
|
||||
dest->next_output_byte = dest->buffer;
|
||||
dest->free_in_buffer = QT_BUFFSIZE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void qt_term_destination(j_compress_ptr cinfo)
|
||||
{
|
||||
my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
|
||||
qint64 n = QT_BUFFSIZE - dest->free_in_buffer;
|
||||
|
||||
qint64 written = dest->device->write((char*)dest->buffer, n);
|
||||
if (written == -1)
|
||||
(*cinfo->err->error_exit)((j_common_ptr)cinfo);
|
||||
}
|
||||
|
||||
#if defined(Q_C_CALLBACKS)
|
||||
}
|
||||
#endif
|
||||
|
||||
inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device)
|
||||
{
|
||||
jpeg_destination_mgr::init_destination = qt_init_destination;
|
||||
jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
|
||||
jpeg_destination_mgr::term_destination = qt_term_destination;
|
||||
this->device = device;
|
||||
next_output_byte = buffer;
|
||||
free_in_buffer = QT_BUFFSIZE;
|
||||
}
|
||||
|
||||
|
||||
static bool write_jpeg_image(const QImage &image, QIODevice *device, int quality)
|
||||
{
|
||||
bool success = false;
|
||||
const QVector<QRgb> cmap = image.colorTable();
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
JSAMPROW row_pointer[1];
|
||||
row_pointer[0] = 0;
|
||||
|
||||
struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device);
|
||||
struct my_error_mgr jerr;
|
||||
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jerr.error_exit = my_error_exit;
|
||||
|
||||
if (!setjmp(jerr.setjmp_buffer)) {
|
||||
// WARNING:
|
||||
// this if loop is inside a setjmp/longjmp branch
|
||||
// do not create C++ temporaries here because the destructor may never be called
|
||||
// if you allocate memory, make sure that you can free it (row_pointer[0])
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
cinfo.dest = iod_dest;
|
||||
|
||||
cinfo.image_width = image.width();
|
||||
cinfo.image_height = image.height();
|
||||
|
||||
bool gray=false;
|
||||
switch (image.format()) {
|
||||
case QImage::Format_Mono:
|
||||
case QImage::Format_MonoLSB:
|
||||
case QImage::Format_Indexed8:
|
||||
gray = true;
|
||||
for (int i = image.colorCount(); gray && i--;) {
|
||||
gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) &&
|
||||
qRed(cmap[i]) == qBlue(cmap[i]));
|
||||
}
|
||||
cinfo.input_components = gray ? 1 : 3;
|
||||
cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
|
||||
break;
|
||||
default:
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
}
|
||||
|
||||
jpeg_set_defaults(&cinfo);
|
||||
|
||||
qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
|
||||
+ qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
|
||||
qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
|
||||
+ qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
|
||||
if (diffInch < diffCm) {
|
||||
cinfo.density_unit = 1; // dots/inch
|
||||
cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
|
||||
cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
|
||||
} else {
|
||||
cinfo.density_unit = 2; // dots/cm
|
||||
cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
|
||||
cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
|
||||
}
|
||||
|
||||
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
|
||||
row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
|
||||
int w = cinfo.image_width;
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
uchar *row = row_pointer[0];
|
||||
switch (image.format()) {
|
||||
case QImage::Format_Mono:
|
||||
case QImage::Format_MonoLSB:
|
||||
if (gray) {
|
||||
const uchar* data = image.constScanLine(cinfo.next_scanline);
|
||||
if (image.format() == QImage::Format_MonoLSB) {
|
||||
for (int i=0; i<w; i++) {
|
||||
bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
|
||||
row[i] = qRed(cmap[bit]);
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<w; i++) {
|
||||
bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
|
||||
row[i] = qRed(cmap[bit]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const uchar* data = image.constScanLine(cinfo.next_scanline);
|
||||
if (image.format() == QImage::Format_MonoLSB) {
|
||||
for (int i=0; i<w; i++) {
|
||||
bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
|
||||
*row++ = qRed(cmap[bit]);
|
||||
*row++ = qGreen(cmap[bit]);
|
||||
*row++ = qBlue(cmap[bit]);
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<w; i++) {
|
||||
bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
|
||||
*row++ = qRed(cmap[bit]);
|
||||
*row++ = qGreen(cmap[bit]);
|
||||
*row++ = qBlue(cmap[bit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QImage::Format_Indexed8:
|
||||
if (gray) {
|
||||
const uchar* pix = image.constScanLine(cinfo.next_scanline);
|
||||
for (int i=0; i<w; i++) {
|
||||
*row = qRed(cmap[*pix]);
|
||||
++row; ++pix;
|
||||
}
|
||||
} else {
|
||||
const uchar* pix = image.constScanLine(cinfo.next_scanline);
|
||||
for (int i=0; i<w; i++) {
|
||||
*row++ = qRed(cmap[*pix]);
|
||||
*row++ = qGreen(cmap[*pix]);
|
||||
*row++ = qBlue(cmap[*pix]);
|
||||
++pix;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QImage::Format_RGB32:
|
||||
case QImage::Format_ARGB32:
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
{
|
||||
const QRgb* rgb = (const QRgb*)image.constScanLine(cinfo.next_scanline);
|
||||
for (int i=0; i<w; i++) {
|
||||
*row++ = qRed(*rgb);
|
||||
*row++ = qGreen(*rgb);
|
||||
*row++ = qBlue(*rgb);
|
||||
++rgb;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// (Testing shows that this way is actually faster than converting to RGB888 + memcpy)
|
||||
QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32);
|
||||
const QRgb* rgb = (const QRgb*)rowImg.constScanLine(0);
|
||||
for (int i=0; i<w; i++) {
|
||||
*row++ = qRed(*rgb);
|
||||
*row++ = qGreen(*rgb);
|
||||
*row++ = qBlue(*rgb);
|
||||
++rgb;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
success = true;
|
||||
} else {
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
success = false;
|
||||
}
|
||||
|
||||
delete iod_dest;
|
||||
delete [] row_pointer[0];
|
||||
return success;
|
||||
}
|
||||
|
||||
class QJpegHandlerPrivate
|
||||
{
|
||||
public:
|
||||
enum State {
|
||||
Ready,
|
||||
ReadHeader,
|
||||
Error
|
||||
};
|
||||
|
||||
QJpegHandlerPrivate(QJpegHandler *qq)
|
||||
: quality(75), iod_src(0), state(Ready), q(qq)
|
||||
{}
|
||||
|
||||
~QJpegHandlerPrivate()
|
||||
{
|
||||
if(iod_src)
|
||||
{
|
||||
jpeg_destroy_decompress(&info);
|
||||
delete iod_src;
|
||||
iod_src = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool readJpegHeader(QIODevice*);
|
||||
bool read(QImage *image);
|
||||
|
||||
int quality;
|
||||
QVariant size;
|
||||
QImage::Format format;
|
||||
QSize scaledSize;
|
||||
QRect scaledClipRect;
|
||||
QRect clipRect;
|
||||
struct jpeg_decompress_struct info;
|
||||
struct my_jpeg_source_mgr * iod_src;
|
||||
struct my_error_mgr err;
|
||||
|
||||
State state;
|
||||
|
||||
QJpegHandler *q;
|
||||
};
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device)
|
||||
{
|
||||
if(state == Ready)
|
||||
{
|
||||
state = Error;
|
||||
iod_src = new my_jpeg_source_mgr(device);
|
||||
|
||||
jpeg_create_decompress(&info);
|
||||
info.src = iod_src;
|
||||
info.err = jpeg_std_error(&err);
|
||||
err.error_exit = my_error_exit;
|
||||
|
||||
if (!setjmp(err.setjmp_buffer)) {
|
||||
(void) jpeg_read_header(&info, TRUE);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
read_jpeg_size(width, height, &info);
|
||||
size = QSize(width, height);
|
||||
|
||||
format = QImage::Format_Invalid;
|
||||
read_jpeg_format(format, &info);
|
||||
state = ReadHeader;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(state == Error)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QJpegHandlerPrivate::read(QImage *image)
|
||||
{
|
||||
if(state == Ready)
|
||||
readJpegHeader(q->device());
|
||||
|
||||
if(state == ReadHeader)
|
||||
{
|
||||
bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality, &info, &err);
|
||||
state = success ? Ready : Error;
|
||||
return success;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
QJpegHandler::QJpegHandler()
|
||||
: d(new QJpegHandlerPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
QJpegHandler::~QJpegHandler()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool QJpegHandler::canRead() const
|
||||
{
|
||||
if(d->state == QJpegHandlerPrivate::Ready && !canRead(device()))
|
||||
return false;
|
||||
|
||||
if (d->state != QJpegHandlerPrivate::Error) {
|
||||
setFormat("jpeg");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QJpegHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (Q_UNLIKELY(!device)) {
|
||||
qWarning("QJpegHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
QSTACKARRAY(char, buffer, 2);
|
||||
if (device->peek(buffer, 2) != 2)
|
||||
return false;
|
||||
return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8;
|
||||
}
|
||||
|
||||
bool QJpegHandler::read(QImage *image)
|
||||
{
|
||||
if (!canRead())
|
||||
return false;
|
||||
return d->read(image);
|
||||
}
|
||||
|
||||
bool QJpegHandler::write(const QImage &image)
|
||||
{
|
||||
return write_jpeg_image(image, device(), d->quality);
|
||||
}
|
||||
|
||||
bool QJpegHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
return option == Quality
|
||||
|| option == ScaledSize
|
||||
|| option == ScaledClipRect
|
||||
|| option == ClipRect
|
||||
|| option == Size
|
||||
|| option == ImageFormat;
|
||||
}
|
||||
|
||||
QVariant QJpegHandler::option(ImageOption option) const
|
||||
{
|
||||
switch(option) {
|
||||
case Quality:
|
||||
return d->quality;
|
||||
case ScaledSize:
|
||||
return d->scaledSize;
|
||||
case ScaledClipRect:
|
||||
return d->scaledClipRect;
|
||||
case ClipRect:
|
||||
return d->clipRect;
|
||||
case Size:
|
||||
d->readJpegHeader(device());
|
||||
return d->size;
|
||||
case ImageFormat:
|
||||
d->readJpegHeader(device());
|
||||
return d->format;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
void QJpegHandler::setOption(ImageOption option, const QVariant &value)
|
||||
{
|
||||
switch(option) {
|
||||
case Quality: {
|
||||
const int newquality = value.toInt();
|
||||
// -1 means default quality.
|
||||
d->quality = (newquality >= 0 ? qMin(newquality, 100) : 75);
|
||||
break;
|
||||
}
|
||||
case ScaledSize:
|
||||
d->scaledSize = value.toSize();
|
||||
break;
|
||||
case ScaledClipRect:
|
||||
d->scaledClipRect = value.toRect();
|
||||
break;
|
||||
case ClipRect:
|
||||
d->clipRect = value.toRect();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray QJpegHandler::name() const
|
||||
{
|
||||
return "jpeg";
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QJPEGHANDLER_P_H
|
||||
#define QJPEGHANDLER_P_H
|
||||
|
||||
#include <QtGui/qimageiohandler.h>
|
||||
#include <QtCore/QSize>
|
||||
#include <QtCore/QRect>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QJpegHandlerPrivate;
|
||||
class QJpegHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
QJpegHandler();
|
||||
~QJpegHandler();
|
||||
|
||||
bool canRead() const;
|
||||
bool read(QImage *image);
|
||||
bool write(const QImage &image);
|
||||
|
||||
QByteArray name() const;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
QVariant option(ImageOption option) const;
|
||||
void setOption(ImageOption option, const QVariant &value);
|
||||
bool supportsOption(ImageOption option) const;
|
||||
|
||||
private:
|
||||
QJpegHandlerPrivate *d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QJPEGHANDLER_P_H
|
|
@ -1,82 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qimageiohandler.h"
|
||||
#include "qstringlist.h"
|
||||
#include "qjpeghandler_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QJpegPlugin : public QImageIOPlugin
|
||||
{
|
||||
public:
|
||||
QJpegPlugin();
|
||||
~QJpegPlugin();
|
||||
|
||||
QStringList keys() const;
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
|
||||
};
|
||||
|
||||
QJpegPlugin::QJpegPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
QJpegPlugin::~QJpegPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList QJpegPlugin::keys() const
|
||||
{
|
||||
static const QStringList list = QStringList()
|
||||
<< QLatin1String("jpg")
|
||||
<< QLatin1String("jpeg");
|
||||
return list;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities QJpegPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "jpg" || format == "jpeg")
|
||||
return Capabilities(CanRead | CanWrite);
|
||||
if (!format.isEmpty())
|
||||
return 0;
|
||||
if (!device->isOpen())
|
||||
return 0;
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && QJpegHandler::canRead(device))
|
||||
cap |= CanRead;
|
||||
if (device->isWritable())
|
||||
cap |= CanWrite;
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *QJpegPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new QJpegHandler;
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2(qjpeg, QJpegPlugin)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,24 +0,0 @@
|
|||
set(QTGAPLUGIN_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtgahandler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtgafile.h
|
||||
)
|
||||
|
||||
set(QTGAPLUGIN_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtgaplugin.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtgahandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtgafile.cpp
|
||||
)
|
||||
|
||||
katie_setup_target(qtgaplugin ${QTGAPLUGIN_SOURCES} ${QTGAPLUGIN_HEADERS})
|
||||
|
||||
add_library(qtgaplugin MODULE ${qtgaplugin_SOURCES})
|
||||
target_link_libraries(qtgaplugin KtCore KtGui)
|
||||
set_target_properties(qtgaplugin PROPERTIES OUTPUT_NAME qtga)
|
||||
|
||||
katie_setup_plugin(qtgaplugin)
|
||||
|
||||
install(
|
||||
TARGETS qtgaplugin
|
||||
DESTINATION ${KATIE_PLUGINS_PATH}/imageformats
|
||||
COMPONENT Runtime
|
||||
)
|
|
@ -1,254 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtQuick3D module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtgafile.h"
|
||||
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDateTime>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct TgaReader
|
||||
{
|
||||
virtual ~TgaReader() {}
|
||||
virtual QRgb operator()(QIODevice *s) const = 0;
|
||||
};
|
||||
|
||||
struct Tga16Reader : public TgaReader
|
||||
{
|
||||
~Tga16Reader() {}
|
||||
QRgb operator()(QIODevice *s) const
|
||||
{
|
||||
char ch1, ch2;
|
||||
if (s->getChar(&ch1) && s->getChar(&ch2)) {
|
||||
quint16 d = (int(ch1) & 0xFF) | ((int(ch2) & 0xFF) << 8);
|
||||
QRgb result = (d & 0x8000) ? 0xFF000000 : 0x00000000;
|
||||
result |= (d & 0x7C00 << 6) | (d & 0x03E0 << 3) | (d & 0x001F);
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Tga24Reader : public TgaReader
|
||||
{
|
||||
QRgb operator()(QIODevice *s) const
|
||||
{
|
||||
char r, g, b;
|
||||
if (s->getChar(&b) && s->getChar(&g) && s->getChar(&r))
|
||||
return qRgb(uchar(r), uchar(g), uchar(b));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Tga32Reader : public TgaReader
|
||||
{
|
||||
QRgb operator()(QIODevice *s) const
|
||||
{
|
||||
char r, g, b, a;
|
||||
if (s->getChar(&b) && s->getChar(&g) && s->getChar(&r) && s->getChar(&a))
|
||||
return qRgba(uchar(r), uchar(g), uchar(b), uchar(a));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
\class QTgaFile
|
||||
\since 4.8
|
||||
\internal
|
||||
|
||||
File data container for a TrueVision Graphics format file.
|
||||
|
||||
Format is as described here:
|
||||
http://local.wasp.uwa.edu.au/~pbourke/dataformats/tga/
|
||||
http://netghost.narod.ru/gff2/graphics/summary/tga.htm
|
||||
|
||||
Usage is:
|
||||
\code
|
||||
QTgaFile tga(myFile);
|
||||
QImage tgaImage;
|
||||
if (tga.isValid())
|
||||
tgaImage = tga.readImage();
|
||||
\endcode
|
||||
|
||||
The class is designed to handle sequential and non-sequential
|
||||
sources, so during construction the mHeader is read. Then during
|
||||
the readImage() call the rest of the data is read.
|
||||
|
||||
After passing myFile to the constructor, if the QIODevice *myFile
|
||||
is read, or has seek() called, the results are undefined - so don't
|
||||
do that.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Construct a new QTgaFile object getting data from \a device.
|
||||
|
||||
The object does not take ownership of the \a device, but until the
|
||||
object is destroyed do not do any non-const operations, eg seek or
|
||||
read on the device.
|
||||
*/
|
||||
QTgaFile::QTgaFile(QIODevice *device)
|
||||
: mDevice(device)
|
||||
{
|
||||
::memset(mHeader, 0, HeaderSize);
|
||||
if (!mDevice->isReadable())
|
||||
{
|
||||
mErrorMessage = QObject::tr("Could not read image data");
|
||||
return;
|
||||
}
|
||||
if (mDevice->isSequential())
|
||||
{
|
||||
mErrorMessage = QObject::tr("Sequential device (eg socket) for image read not supported");
|
||||
return;
|
||||
}
|
||||
if (!mDevice->seek(0))
|
||||
{
|
||||
mErrorMessage = QObject::tr("Seek file/device for image read failed");
|
||||
return;
|
||||
}
|
||||
int bytes = device->read((char*)mHeader, HeaderSize);
|
||||
if (bytes != HeaderSize)
|
||||
{
|
||||
mErrorMessage = QObject::tr("Image mHeader read failed");
|
||||
device->seek(0);
|
||||
return;
|
||||
}
|
||||
if (mHeader[ImageType] != 2)
|
||||
{
|
||||
// TODO: should support other image types
|
||||
mErrorMessage = QObject::tr("Image type not supported");
|
||||
device->seek(0);
|
||||
return;
|
||||
}
|
||||
int bitsPerPixel = mHeader[PixelDepth];
|
||||
bool validDepth = (bitsPerPixel == 16 || bitsPerPixel == 24 || bitsPerPixel == 32);
|
||||
if (!validDepth)
|
||||
{
|
||||
mErrorMessage = QObject::tr("Image depth not valid");
|
||||
}
|
||||
int fileBytes = mDevice->size();
|
||||
if (!mDevice->seek(fileBytes - FooterSize))
|
||||
{
|
||||
mErrorMessage = QObject::tr("Could not seek to image read footer");
|
||||
device->seek(0);
|
||||
return;
|
||||
}
|
||||
char footer[FooterSize];
|
||||
bytes = mDevice->read((char*)footer, FooterSize);
|
||||
if (bytes != FooterSize)
|
||||
{
|
||||
mErrorMessage = QObject::tr("Could not read footer");
|
||||
}
|
||||
if (qstrncmp(&footer[SignatureOffset], "TRUEVISION-XFILE", 16) != 0)
|
||||
{
|
||||
mErrorMessage = QObject::tr("Image type (non-TrueVision 2.0) not supported");
|
||||
}
|
||||
if (!mDevice->seek(0))
|
||||
{
|
||||
mErrorMessage = QObject::tr("Could not reset to start position");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Destroy the device, recovering any resources.
|
||||
*/
|
||||
QTgaFile::~QTgaFile()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Reads an image file from the QTgaFile's device, and returns it.
|
||||
|
||||
This method seeks to the absolute position of the image data in the file,
|
||||
so no assumptions are made about where the devices read pointer is when this
|
||||
method is called. For this reason only random access devices are supported.
|
||||
|
||||
If the constructor completed successfully, such that isValid() returns true,
|
||||
then this method is likely to succeed, unless the file is somehow corrupted.
|
||||
|
||||
In the case that the read fails, the QImage returned will be null, such that
|
||||
QImage::isNull() will be true.
|
||||
*/
|
||||
QImage QTgaFile::readImage()
|
||||
{
|
||||
if (!isValid())
|
||||
return QImage();
|
||||
|
||||
int offset = mHeader[IdLength]; // Mostly always zero
|
||||
|
||||
// Even in TrueColor files a color pallette may be present
|
||||
if (mHeader[ColorMapType] == 1)
|
||||
offset += littleEndianInt(&mHeader[CMapLength]) * littleEndianInt(&mHeader[CMapDepth]);
|
||||
|
||||
mDevice->seek(HeaderSize + offset);
|
||||
|
||||
char dummy;
|
||||
for (int i = 0; i < offset; ++i)
|
||||
mDevice->getChar(&dummy);
|
||||
|
||||
int bitsPerPixel = mHeader[PixelDepth];
|
||||
int imageWidth = width();
|
||||
int imageHeight = height();
|
||||
|
||||
unsigned char desc = mHeader[ImageDescriptor];
|
||||
//unsigned char xCorner = desc & 0x10; // 0 = left, 1 = right
|
||||
unsigned char yCorner = desc & 0x20; // 0 = lower, 1 = upper
|
||||
|
||||
QImage im(imageWidth, imageHeight, QImage::Format_ARGB32);
|
||||
TgaReader *reader = 0;
|
||||
if (bitsPerPixel == 16)
|
||||
reader = new Tga16Reader();
|
||||
else if (bitsPerPixel == 24)
|
||||
reader = new Tga24Reader();
|
||||
else if (bitsPerPixel == 32)
|
||||
reader = new Tga32Reader();
|
||||
else
|
||||
return QImage();
|
||||
TgaReader &read = *reader;
|
||||
|
||||
// For now only deal with yCorner, since no one uses xCorner == 1
|
||||
// Also this is upside down, since Qt has the origin flipped
|
||||
if (yCorner)
|
||||
{
|
||||
for (int y = 0; y < imageHeight; ++y)
|
||||
for (int x = 0; x < imageWidth; ++x)
|
||||
im.setPixel(x, y, read(mDevice));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = imageHeight - 1; y >= 0; --y)
|
||||
for (int x = 0; x < imageWidth; ++x)
|
||||
im.setPixel(x, y, read(mDevice));
|
||||
}
|
||||
|
||||
delete reader;
|
||||
|
||||
// TODO: add processing of TGA extension information - ie TGA 2.0 files
|
||||
return im;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,131 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtQuick3D module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTGAFILE_H
|
||||
#define QTGAFILE_H
|
||||
|
||||
#include <QtGui/QColor>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class QTgaFile
|
||||
{
|
||||
public:
|
||||
enum Compression {
|
||||
NoCompression = 0,
|
||||
RleCompression = 1
|
||||
};
|
||||
|
||||
enum HeaderOffset {
|
||||
IdLength = 0, /* 00h Size of Image ID field */
|
||||
ColorMapType = 1, /* 01h Color map type */
|
||||
ImageType = 2, /* 02h Image type code */
|
||||
CMapStart = 3, /* 03h Color map origin */
|
||||
CMapLength = 5, /* 05h Color map length */
|
||||
CMapDepth = 7, /* 07h Depth of color map entries */
|
||||
XOffset = 8, /* 08h X origin of image */
|
||||
YOffset = 10, /* 0Ah Y origin of image */
|
||||
Width = 12, /* 0Ch Width of image */
|
||||
Height = 14, /* 0Eh Height of image */
|
||||
PixelDepth = 16, /* 10h Image pixel size */
|
||||
ImageDescriptor = 17, /* 11h Image descriptor byte */
|
||||
HeaderSize = 18
|
||||
};
|
||||
|
||||
enum FooterOffset {
|
||||
ExtensionOffset = 0,
|
||||
DeveloperOffset = 4,
|
||||
SignatureOffset = 8,
|
||||
FooterSize = 26
|
||||
};
|
||||
|
||||
QTgaFile(QIODevice *);
|
||||
~QTgaFile();
|
||||
|
||||
inline bool isValid() const;
|
||||
QImage readImage();
|
||||
inline int xOffset() const;
|
||||
inline int yOffset() const;
|
||||
inline int width() const;
|
||||
inline int height() const;
|
||||
inline QSize size() const;
|
||||
inline Compression compression() const;
|
||||
|
||||
private:
|
||||
static inline quint16 littleEndianInt(const unsigned char *d);
|
||||
|
||||
QString mErrorMessage;
|
||||
unsigned char mHeader[HeaderSize];
|
||||
QIODevice *mDevice;
|
||||
};
|
||||
|
||||
inline bool QTgaFile::isValid() const
|
||||
{
|
||||
return mErrorMessage.isEmpty();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns the integer encoded in the two little endian bytes at \a d.
|
||||
*/
|
||||
inline quint16 QTgaFile::littleEndianInt(const unsigned char *d)
|
||||
{
|
||||
return d[0] + d[1] * 256;
|
||||
}
|
||||
|
||||
inline int QTgaFile::xOffset() const
|
||||
{
|
||||
return littleEndianInt(&mHeader[XOffset]);
|
||||
}
|
||||
|
||||
inline int QTgaFile::yOffset() const
|
||||
{
|
||||
return littleEndianInt(&mHeader[YOffset]);
|
||||
}
|
||||
|
||||
inline int QTgaFile::width() const
|
||||
{
|
||||
return littleEndianInt(&mHeader[Width]);
|
||||
}
|
||||
|
||||
inline int QTgaFile::height() const
|
||||
{
|
||||
return littleEndianInt(&mHeader[Height]);
|
||||
}
|
||||
|
||||
inline QSize QTgaFile::size() const
|
||||
{
|
||||
return QSize(width(), height());
|
||||
}
|
||||
|
||||
inline QTgaFile::Compression QTgaFile::compression() const
|
||||
{
|
||||
// TODO: for now, only handle type 2 files, with no color table
|
||||
// and no compression
|
||||
return NoCompression;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTGAFILE_H
|
|
@ -1,106 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtQuick3D module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtgahandler.h"
|
||||
#include "qtgafile.h"
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QTgaHandler::QTgaHandler()
|
||||
: QImageIOHandler()
|
||||
, tga(0)
|
||||
{
|
||||
}
|
||||
|
||||
QTgaHandler::~QTgaHandler()
|
||||
{
|
||||
delete tga;
|
||||
}
|
||||
|
||||
bool QTgaHandler::canRead() const
|
||||
{
|
||||
if (!tga)
|
||||
tga = new QTgaFile(device());
|
||||
if (tga->isValid())
|
||||
{
|
||||
setFormat("tga");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QTgaHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (Q_UNLIKELY(!device)) {
|
||||
qWarning("QTgaHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
const qint64 oldpos = device->pos();
|
||||
QTgaFile tga(device);
|
||||
const bool isvalid = tga.isValid();
|
||||
device->seek(oldpos);
|
||||
return isvalid;
|
||||
}
|
||||
|
||||
bool QTgaHandler::read(QImage *image)
|
||||
{
|
||||
if (!canRead())
|
||||
return false;
|
||||
*image = tga->readImage();
|
||||
return !image->isNull();
|
||||
}
|
||||
|
||||
QByteArray QTgaHandler::name() const
|
||||
{
|
||||
return "tga";
|
||||
}
|
||||
|
||||
QVariant QTgaHandler::option(ImageOption option) const
|
||||
{
|
||||
if (option == Size && canRead()) {
|
||||
return tga->size();
|
||||
} else if (option == CompressionRatio) {
|
||||
return tga->compression();
|
||||
} else if (option == ImageFormat) {
|
||||
return QImage::Format_ARGB32;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void QTgaHandler::setOption(ImageOption option, const QVariant &value)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(value);
|
||||
}
|
||||
|
||||
bool QTgaHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
return option == CompressionRatio
|
||||
|| option == Size
|
||||
|| option == ImageFormat;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,54 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtQuick3D module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTGAHANDLER_H
|
||||
#define QTGAHANDLER_H
|
||||
|
||||
#include <QtGui/QImageIOHandler>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTgaFile;
|
||||
|
||||
class QTgaHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
QTgaHandler();
|
||||
~QTgaHandler();
|
||||
|
||||
bool canRead() const;
|
||||
bool read(QImage *image);
|
||||
|
||||
QByteArray name() const;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
QVariant option(ImageOption option) const;
|
||||
void setOption(ImageOption option, const QVariant &value);
|
||||
bool supportsOption(ImageOption option) const;
|
||||
|
||||
private:
|
||||
mutable QTgaFile *tga;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTGAHANDLER_H
|
|
@ -1,68 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtQuick3D module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtGui/QImageIOHandler>
|
||||
#include <QtCore/QDebug>
|
||||
#include "qtgahandler.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTgaPlugin : public QImageIOPlugin
|
||||
{
|
||||
public:
|
||||
Capabilities capabilities(QIODevice * device, const QByteArray & format) const;
|
||||
QImageIOHandler * create(QIODevice * device, const QByteArray & format = QByteArray()) const;
|
||||
QStringList keys() const;
|
||||
};
|
||||
|
||||
QImageIOPlugin::Capabilities QTgaPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "tga")
|
||||
return Capabilities(CanRead);
|
||||
if (!format.isEmpty())
|
||||
return 0;
|
||||
if (!device->isOpen())
|
||||
return 0;
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && QTgaHandler::canRead(device))
|
||||
cap |= CanRead;
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler* QTgaPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *tgaHandler = new QTgaHandler();
|
||||
tgaHandler->setDevice(device);
|
||||
tgaHandler->setFormat(format);
|
||||
return tgaHandler;
|
||||
}
|
||||
|
||||
QStringList QTgaPlugin::keys() const
|
||||
{
|
||||
static const QStringList list = QStringList()
|
||||
<< QLatin1String("tga");
|
||||
return list;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2(qtga, QTgaPlugin)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,24 +0,0 @@
|
|||
include_directories(${TIFF_INCLUDE_DIR})
|
||||
|
||||
set(QTIFFPLUGIN_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtiffhandler_p.h
|
||||
)
|
||||
|
||||
set(QTIFFPLUGIN_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtiffplugin.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qtiffhandler.cpp
|
||||
)
|
||||
|
||||
katie_setup_target(qtiffplugin ${QTIFFPLUGIN_SOURCES} ${QTIFFPLUGIN_HEADERS})
|
||||
|
||||
add_library(qtiffplugin MODULE ${qtiffplugin_SOURCES})
|
||||
target_link_libraries(qtiffplugin KtCore KtGui ${TIFF_LIBRARIES})
|
||||
set_target_properties(qtiffplugin PROPERTIES OUTPUT_NAME qtiff)
|
||||
|
||||
katie_setup_plugin(qtiffplugin)
|
||||
|
||||
install(
|
||||
TARGETS qtiffplugin
|
||||
DESTINATION ${KATIE_PLUGINS_PATH}/imageformats
|
||||
COMPONENT Runtime
|
||||
)
|
|
@ -1,642 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtiffhandler_p.h"
|
||||
#include "qvariant.h"
|
||||
#include "qdebug.h"
|
||||
#include "qimage.h"
|
||||
|
||||
extern "C" {
|
||||
#include "tiffio.h"
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
|
||||
{
|
||||
QIODevice* device = static_cast<QTiffHandler*>(fd)->device();
|
||||
return device->isReadable() ? device->read(static_cast<char *>(buf), size) : -1;
|
||||
}
|
||||
|
||||
tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
|
||||
{
|
||||
return static_cast<QTiffHandler*>(fd)->device()->write(static_cast<char *>(buf), size);
|
||||
}
|
||||
|
||||
toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence)
|
||||
{
|
||||
QIODevice *device = static_cast<QTiffHandler*>(fd)->device();
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
device->seek(off);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
device->seek(device->pos() + off);
|
||||
break;
|
||||
case SEEK_END:
|
||||
device->seek(device->size() + off);
|
||||
break;
|
||||
}
|
||||
|
||||
return device->pos();
|
||||
}
|
||||
|
||||
int qtiffCloseProc(thandle_t /*fd*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
toff_t qtiffSizeProc(thandle_t fd)
|
||||
{
|
||||
return static_cast<QTiffHandler*>(fd)->device()->size();
|
||||
}
|
||||
|
||||
int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/)
|
||||
{
|
||||
}
|
||||
|
||||
// for 32 bits images
|
||||
inline void rotate_right_mirror_horizontal(QImage *const image)// rotate right->mirrored horizontal
|
||||
{
|
||||
const int height = image->height();
|
||||
const int width = image->width();
|
||||
QImage generated(/* width = */ height, /* height = */ width, image->format());
|
||||
const uint32 *originalPixel = reinterpret_cast<const uint32*>(image->bits());
|
||||
uint32 *const generatedPixels = reinterpret_cast<uint32*>(generated.bits());
|
||||
for (int row=0; row < height; ++row) {
|
||||
for (int col=0; col < width; ++col) {
|
||||
int idx = col * height + row;
|
||||
generatedPixels[idx] = *originalPixel;
|
||||
++originalPixel;
|
||||
}
|
||||
}
|
||||
*image = generated;
|
||||
}
|
||||
|
||||
inline void rotate_right_mirror_vertical(QImage *const image) // rotate right->mirrored vertical
|
||||
{
|
||||
const int height = image->height();
|
||||
const int width = image->width();
|
||||
QImage generated(/* width = */ height, /* height = */ width, image->format());
|
||||
const int lastCol = width - 1;
|
||||
const int lastRow = height - 1;
|
||||
const uint32 *pixel = reinterpret_cast<const uint32*>(image->bits());
|
||||
uint32 *const generatedBits = reinterpret_cast<uint32*>(generated.bits());
|
||||
for (int row=0; row < height; ++row) {
|
||||
for (int col=0; col < width; ++col) {
|
||||
int idx = (lastCol - col) * height + (lastRow - row);
|
||||
generatedBits[idx] = *pixel;
|
||||
++pixel;
|
||||
}
|
||||
}
|
||||
*image = generated;
|
||||
}
|
||||
|
||||
|
||||
static void qConvert32BitOrder(void *buffer, int width)
|
||||
{
|
||||
uint32 *target = reinterpret_cast<uint32 *>(buffer);
|
||||
for (int32 x=0; x<width; ++x) {
|
||||
uint32 p = target[x];
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
target[x] = (p & 0xff000000) >> 24
|
||||
| (p & 0x00ff0000) << 8
|
||||
| (p & 0x0000ff00) << 8
|
||||
| (p & 0x000000ff) << 8;
|
||||
#else
|
||||
// convert between ARGB and ABGR
|
||||
target[x] = (p & 0xff000000)
|
||||
| ((p & 0x00ff0000) >> 16)
|
||||
| (p & 0x0000ff00)
|
||||
| ((p & 0x000000ff) << 16);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
QTiffHandler::QTiffHandler() : QImageIOHandler()
|
||||
{
|
||||
compression = NoCompression;
|
||||
}
|
||||
|
||||
bool QTiffHandler::canRead() const
|
||||
{
|
||||
if (canRead(device())) {
|
||||
setFormat("tiff");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QTiffHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (Q_UNLIKELY(!device)) {
|
||||
qWarning("QTiffHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
// need the magic from the beginning
|
||||
Q_ASSERT(device->pos() == 0);
|
||||
const QByteArray header = device->peek(4);
|
||||
return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4)
|
||||
|| header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4);
|
||||
}
|
||||
|
||||
bool QTiffHandler::read(QImage *image)
|
||||
{
|
||||
if (!canRead())
|
||||
return false;
|
||||
|
||||
TIFF *const tiff = TIFFClientOpen("foo",
|
||||
"r",
|
||||
this,
|
||||
qtiffReadProc,
|
||||
qtiffWriteProc,
|
||||
qtiffSeekProc,
|
||||
qtiffCloseProc,
|
||||
qtiffSizeProc,
|
||||
qtiffMapProc,
|
||||
qtiffUnmapProc);
|
||||
|
||||
if (!tiff) {
|
||||
return false;
|
||||
}
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
uint16 photometric;
|
||||
if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)
|
||||
|| !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height)
|
||||
|| !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
// BitsPerSample defaults to 1 according to the TIFF spec.
|
||||
uint16 bitPerSample;
|
||||
if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample))
|
||||
bitPerSample = 1;
|
||||
uint16 samplesPerPixel; // they may be e.g. grayscale with 2 samples per pixel
|
||||
if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel))
|
||||
samplesPerPixel = 1;
|
||||
|
||||
bool grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE;
|
||||
if (grayscale && bitPerSample == 1 && samplesPerPixel == 1) {
|
||||
if (image->size() != QSize(width, height) || image->format() != QImage::Format_Mono)
|
||||
*image = QImage(width, height, QImage::Format_Mono);
|
||||
QVector<QRgb> colortable(2);
|
||||
if (photometric == PHOTOMETRIC_MINISBLACK) {
|
||||
colortable[0] = 0xff000000;
|
||||
colortable[1] = 0xffffffff;
|
||||
} else {
|
||||
colortable[0] = 0xffffffff;
|
||||
colortable[1] = 0xff000000;
|
||||
}
|
||||
image->setColorTable(colortable);
|
||||
|
||||
if (!image->isNull()) {
|
||||
for (uint32 y=0; y<height; ++y) {
|
||||
if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1) {
|
||||
if (image->size() != QSize(width, height) || image->format() != QImage::Format_Indexed8)
|
||||
*image = QImage(width, height, QImage::Format_Indexed8);
|
||||
if (!image->isNull()) {
|
||||
const uint16 tableSize = 256;
|
||||
QVector<QRgb> qtColorTable(tableSize);
|
||||
if (grayscale) {
|
||||
for (int i = 0; i<tableSize; ++i) {
|
||||
const int c = (photometric == PHOTOMETRIC_MINISBLACK) ? i : (255 - i);
|
||||
qtColorTable[i] = qRgb(c, c, c);
|
||||
}
|
||||
} else {
|
||||
// create the color table
|
||||
uint16 *redTable = 0;
|
||||
uint16 *greenTable = 0;
|
||||
uint16 *blueTable = 0;
|
||||
if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
if (!redTable || !greenTable || !blueTable) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i<tableSize ;++i) {
|
||||
const int red = redTable[i] / 257;
|
||||
const int green = greenTable[i] / 257;
|
||||
const int blue = blueTable[i] / 257;
|
||||
qtColorTable[i] = qRgb(red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
image->setColorTable(qtColorTable);
|
||||
for (uint32 y=0; y<height; ++y) {
|
||||
if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// free redTable, greenTable and greenTable done by libtiff
|
||||
}
|
||||
} else {
|
||||
if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32)
|
||||
*image = QImage(width, height, QImage::Format_ARGB32);
|
||||
if (!image->isNull()) {
|
||||
const int stopOnError = 1;
|
||||
if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, stopOnError)) {
|
||||
for (uint32 y=0; y<height; ++y)
|
||||
qConvert32BitOrder(image->scanLine(y), width);
|
||||
} else {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (image->isNull()) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
float resX = 0;
|
||||
float resY = 0;
|
||||
uint16 resUnit;
|
||||
if (!TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit))
|
||||
resUnit = RESUNIT_INCH;
|
||||
|
||||
if (TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &resX)
|
||||
&& TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &resY)) {
|
||||
switch(resUnit) {
|
||||
case RESUNIT_CENTIMETER:
|
||||
image->setDotsPerMeterX(qRound(resX * 100));
|
||||
image->setDotsPerMeterY(qRound(resY * 100));
|
||||
break;
|
||||
case RESUNIT_INCH:
|
||||
image->setDotsPerMeterX(qRound(resX * (100 / 2.54)));
|
||||
image->setDotsPerMeterY(qRound(resY * (100 / 2.54)));
|
||||
break;
|
||||
default:
|
||||
// do nothing as defaults have already
|
||||
// been set within the QImage class
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rotate the image if the orientation is defined in the file
|
||||
uint16 orientationTag;
|
||||
if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag)) {
|
||||
if (image->format() == QImage::Format_ARGB32) {
|
||||
// TIFFReadRGBAImageOriented() flip the image but does not rotate them
|
||||
switch (orientationTag) {
|
||||
case 5:
|
||||
rotate_right_mirror_horizontal(image);
|
||||
break;
|
||||
case 6:
|
||||
rotate_right_mirror_vertical(image);
|
||||
break;
|
||||
case 7:
|
||||
rotate_right_mirror_horizontal(image);
|
||||
break;
|
||||
case 8:
|
||||
rotate_right_mirror_vertical(image);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (orientationTag) {
|
||||
case 1: // default orientation
|
||||
break;
|
||||
case 2: // mirror horizontal
|
||||
*image = image->mirrored(true, false);
|
||||
break;
|
||||
case 3: // mirror both
|
||||
*image = image->mirrored(true, true);
|
||||
break;
|
||||
case 4: // mirror vertical
|
||||
*image = image->mirrored(false, true);
|
||||
break;
|
||||
case 5: // rotate right mirror horizontal
|
||||
{
|
||||
QMatrix transformation;
|
||||
transformation.rotate(90);
|
||||
*image = image->transformed(transformation);
|
||||
*image = image->mirrored(true, false);
|
||||
break;
|
||||
}
|
||||
case 6: // rotate right
|
||||
{
|
||||
QMatrix transformation;
|
||||
transformation.rotate(90);
|
||||
*image = image->transformed(transformation);
|
||||
break;
|
||||
}
|
||||
case 7: // rotate right, mirror vertical
|
||||
{
|
||||
QMatrix transformation;
|
||||
transformation.rotate(90);
|
||||
*image = image->transformed(transformation);
|
||||
*image = image->mirrored(false, true);
|
||||
break;
|
||||
}
|
||||
case 8: // rotate left
|
||||
{
|
||||
QMatrix transformation;
|
||||
transformation.rotate(270);
|
||||
*image = image->transformed(transformation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TIFFClose(tiff);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool checkGrayscale(const QVector<QRgb> &colorTable)
|
||||
{
|
||||
if (colorTable.size() != 256)
|
||||
return false;
|
||||
|
||||
const bool increasing = (colorTable.at(0) == 0xff000000);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if ((increasing && colorTable.at(i) != qRgb(i, i, i))
|
||||
|| (!increasing && colorTable.at(i) != qRgb(255 - i, 255 - i, 255 - i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QTiffHandler::write(const QImage &image)
|
||||
{
|
||||
if (!device()->isWritable())
|
||||
return false;
|
||||
|
||||
TIFF *const tiff = TIFFClientOpen("foo",
|
||||
"w",
|
||||
this,
|
||||
qtiffReadProc,
|
||||
qtiffWriteProc,
|
||||
qtiffSeekProc,
|
||||
qtiffCloseProc,
|
||||
qtiffSizeProc,
|
||||
qtiffMapProc,
|
||||
qtiffUnmapProc);
|
||||
if (!tiff)
|
||||
return false;
|
||||
|
||||
const int width = image.width();
|
||||
const int height = image.height();
|
||||
|
||||
if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the resolution
|
||||
bool resolutionSet = false;
|
||||
const int dotPerMeterX = image.dotsPerMeterX();
|
||||
const int dotPerMeterY = image.dotsPerMeterY();
|
||||
if ((dotPerMeterX % 100) == 0
|
||||
&& (dotPerMeterY % 100) == 0) {
|
||||
resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER)
|
||||
&& TIFFSetField(tiff, TIFFTAG_XRESOLUTION, dotPerMeterX/100.0)
|
||||
&& TIFFSetField(tiff, TIFFTAG_YRESOLUTION, dotPerMeterY/100.0);
|
||||
} else {
|
||||
resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)
|
||||
&& TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(image.logicalDpiX()))
|
||||
&& TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(image.logicalDpiY()));
|
||||
}
|
||||
if (!resolutionSet) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
// configure image depth
|
||||
const QImage::Format format = image.format();
|
||||
if (format == QImage::Format_Mono || format == QImage::Format_MonoLSB) {
|
||||
uint16 photometric = PHOTOMETRIC_MINISBLACK;
|
||||
if (image.colorTable().at(0) == 0xffffffff)
|
||||
photometric = PHOTOMETRIC_MINISWHITE;
|
||||
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_CCITTRLE)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 1)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to do the conversion in chunks no greater than 16 MB
|
||||
int chunks = (width * height / (1024 * 1024 * 16)) + 1;
|
||||
int chunkHeight = qMax(height / chunks, 1);
|
||||
|
||||
int y = 0;
|
||||
while (y < height) {
|
||||
QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_Mono);
|
||||
|
||||
int chunkStart = y;
|
||||
int chunkEnd = y + chunk.height();
|
||||
while (y < chunkEnd) {
|
||||
if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
++y;
|
||||
}
|
||||
}
|
||||
TIFFClose(tiff);
|
||||
} else if (format == QImage::Format_Indexed8) {
|
||||
const QVector<QRgb> colorTable = image.colorTable();
|
||||
bool isGrayscale = checkGrayscale(colorTable);
|
||||
if (isGrayscale) {
|
||||
uint16 photometric = PHOTOMETRIC_MINISBLACK;
|
||||
if (image.colorTable().at(0) == 0xffffffff)
|
||||
photometric = PHOTOMETRIC_MINISWHITE;
|
||||
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_PACKBITS)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_PACKBITS)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
//// write the color table
|
||||
// allocate the color tables
|
||||
uint16 *redTable = static_cast<uint16 *>(malloc(256 * sizeof(uint16)));
|
||||
uint16 *greenTable = static_cast<uint16 *>(malloc(256 * sizeof(uint16)));
|
||||
uint16 *blueTable = static_cast<uint16 *>(malloc(256 * sizeof(uint16)));
|
||||
if (!redTable || !greenTable || !blueTable) {
|
||||
free(redTable);
|
||||
free(greenTable);
|
||||
free(blueTable);
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the color table
|
||||
const int tableSize = colorTable.size();
|
||||
Q_ASSERT(tableSize <= 256);
|
||||
for (int i = 0; i<tableSize; ++i) {
|
||||
const QRgb color = colorTable.at(i);
|
||||
redTable[i] = qRed(color) * 257;
|
||||
greenTable[i] = qGreen(color) * 257;
|
||||
blueTable[i] = qBlue(color) * 257;
|
||||
}
|
||||
|
||||
const bool setColorTableSuccess = TIFFSetField(tiff, TIFFTAG_COLORMAP, redTable, greenTable, blueTable);
|
||||
|
||||
free(redTable);
|
||||
free(greenTable);
|
||||
free(blueTable);
|
||||
|
||||
if (!setColorTableSuccess) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//// write the data
|
||||
// try to do the conversion in chunks no greater than 16 MB
|
||||
int chunks = (width * height/ (1024 * 1024 * 16)) + 1;
|
||||
int chunkHeight = qMax(height / chunks, 1);
|
||||
|
||||
int y = 0;
|
||||
while (y < height) {
|
||||
QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y));
|
||||
|
||||
int chunkStart = y;
|
||||
int chunkEnd = y + chunk.height();
|
||||
while (y < chunkEnd) {
|
||||
if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
++y;
|
||||
}
|
||||
}
|
||||
TIFFClose(tiff);
|
||||
|
||||
} else {
|
||||
if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
|
||||
|| !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
// try to do the ARGB32 conversion in chunks no greater than 16 MB
|
||||
int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1;
|
||||
int chunkHeight = qMax(height / chunks, 1);
|
||||
|
||||
int y = 0;
|
||||
while (y < height) {
|
||||
QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_ARGB32);
|
||||
|
||||
int chunkStart = y;
|
||||
int chunkEnd = y + chunk.height();
|
||||
while (y < chunkEnd) {
|
||||
qConvert32BitOrder(chunk.scanLine(y - chunkStart), width);
|
||||
|
||||
if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
|
||||
TIFFClose(tiff);
|
||||
return false;
|
||||
}
|
||||
++y;
|
||||
}
|
||||
}
|
||||
TIFFClose(tiff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray QTiffHandler::name() const
|
||||
{
|
||||
return "tiff";
|
||||
}
|
||||
|
||||
QVariant QTiffHandler::option(ImageOption option) const
|
||||
{
|
||||
if (option == Size && canRead()) {
|
||||
QSize imageSize;
|
||||
qint64 pos = device()->pos();
|
||||
TIFF *tiff = TIFFClientOpen("foo",
|
||||
"r",
|
||||
const_cast<QTiffHandler*>(this),
|
||||
qtiffReadProc,
|
||||
qtiffWriteProc,
|
||||
qtiffSeekProc,
|
||||
qtiffCloseProc,
|
||||
qtiffSizeProc,
|
||||
qtiffMapProc,
|
||||
qtiffUnmapProc);
|
||||
|
||||
if (tiff) {
|
||||
uint32 width = 0;
|
||||
uint32 height = 0;
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
|
||||
imageSize = QSize(width, height);
|
||||
TIFFClose(tiff);
|
||||
}
|
||||
device()->seek(pos);
|
||||
if (imageSize.isValid())
|
||||
return imageSize;
|
||||
} else if (option == CompressionRatio) {
|
||||
return compression;
|
||||
} else if (option == ImageFormat) {
|
||||
return QImage::Format_ARGB32;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void QTiffHandler::setOption(ImageOption option, const QVariant &value)
|
||||
{
|
||||
if (option == CompressionRatio && value.type() == QVariant::Int)
|
||||
compression = value.toInt();
|
||||
}
|
||||
|
||||
bool QTiffHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
return option == CompressionRatio
|
||||
|| option == Size
|
||||
|| option == ImageFormat;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1,56 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTIFFHANDLER_P_H
|
||||
#define QTIFFHANDLER_P_H
|
||||
|
||||
#include <QtGui/qimageiohandler.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTiffHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
QTiffHandler();
|
||||
|
||||
bool canRead() const;
|
||||
bool read(QImage *image);
|
||||
bool write(const QImage &image);
|
||||
|
||||
QByteArray name() const;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
QVariant option(ImageOption option) const;
|
||||
void setOption(ImageOption option, const QVariant &value);
|
||||
bool supportsOption(ImageOption option) const;
|
||||
|
||||
enum Compression {
|
||||
NoCompression = 0,
|
||||
LzwCompression = 1
|
||||
};
|
||||
private:
|
||||
int compression;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTIFFHANDLER_P_H
|
|
@ -1,83 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the plugins of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qimageiohandler.h"
|
||||
#include "qstringlist.h"
|
||||
|
||||
#include "qtiffhandler_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTiffPlugin : public QImageIOPlugin
|
||||
{
|
||||
public:
|
||||
QTiffPlugin();
|
||||
~QTiffPlugin();
|
||||
|
||||
QStringList keys() const;
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
|
||||
};
|
||||
|
||||
QTiffPlugin::QTiffPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
QTiffPlugin::~QTiffPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList QTiffPlugin::keys() const
|
||||
{
|
||||
static const QStringList list = QStringList()
|
||||
<< QLatin1String("tif")
|
||||
<< QLatin1String("tiff");
|
||||
return list;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities QTiffPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "tif" || format == "tiff")
|
||||
return Capabilities(CanRead | CanWrite);
|
||||
if (!format.isEmpty())
|
||||
return 0;
|
||||
if (!device->isOpen())
|
||||
return 0;
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && QTiffHandler::canRead(device))
|
||||
cap |= CanRead;
|
||||
if (device->isWritable())
|
||||
cap |= CanWrite;
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *QTiffPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new QTiffHandler;
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2(qtiff, QTiffPlugin)
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -1455,7 +1455,7 @@ void tst_QDir::equalityOperator_data()
|
|||
<< false;
|
||||
|
||||
QTest::newRow("diff-namefilters") << SRCDIR << "*.cpp" << int(QDir::Name) << int(QDir::Files)
|
||||
<< SRCDIR << "*.jpg" << int(QDir::Name) << int(QDir::Files)
|
||||
<< SRCDIR << "*.png" << int(QDir::Name) << int(QDir::Files)
|
||||
<< false;
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 2.2 KiB |
BIN
tests/benchmarks/gui/graphicsview/qgraphicsview/images/wine.png
Normal file
After Width: | Height: | Size: 11 KiB |
|
@ -1,8 +1,8 @@
|
|||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>images/designer.png</file>
|
||||
<file>images/wine.jpeg</file>
|
||||
<file>images/wine-big.jpeg</file>
|
||||
<file>images/wine.png</file>
|
||||
<file>images/wine-big.png</file>
|
||||
<file>random.data</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -742,7 +742,7 @@ void tst_QGraphicsView::moveItemCache()
|
|||
|
||||
QGraphicsScene scene(0, 0, 300, 300);
|
||||
|
||||
QPixmap pix(QLatin1String(":/images/wine.jpeg"));
|
||||
QPixmap pix(QLatin1String(":/images/wine.png"));
|
||||
QVERIFY(!pix.isNull());
|
||||
|
||||
QList<QGraphicsItem *> items;
|
||||
|
@ -839,7 +839,7 @@ void tst_QGraphicsView::paintItemCache()
|
|||
|
||||
QGraphicsScene scene(0, 0, 300, 300);
|
||||
|
||||
QPixmap pix(QLatin1String(":/images/wine.jpeg"));
|
||||
QPixmap pix(QLatin1String(":/images/wine.png"));
|
||||
QVERIFY(!pix.isNull());
|
||||
|
||||
QList<QGraphicsItem *> items;
|
||||
|
@ -854,7 +854,7 @@ void tst_QGraphicsView::paintItemCache()
|
|||
item->setPos(-100, -100);
|
||||
scene.addItem(item);
|
||||
|
||||
QPixmap pix2(QLatin1String(":/images/wine-big.jpeg"));
|
||||
QPixmap pix2(QLatin1String(":/images/wine-big.png"));
|
||||
item = new UpdatedPixmapCacheItem(updatePartial);
|
||||
item->setPixmap(pix2);
|
||||
item->setCacheMode((QGraphicsItem::CacheMode)cacheMode);
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
katie_gui_test(tst_bench_qimagereader
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_qimagereader.cpp
|
||||
)
|
||||
|
||||
if(WITH_JPEG AND JPEG_FOUND)
|
||||
target_compile_definitions(tst_bench_qimagereader PRIVATE -DQTEST_HAVE_JPEG)
|
||||
endif()
|
||||
if(WITH_TIFF AND TIFF_FOUND)
|
||||
target_compile_definitions(tst_bench_qimagereader PRIVATE -DQTEST_HAVE_TIFF)
|
||||
endif()
|
||||
|
|
Before Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 18 B |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 326 B |
Before Width: | Height: | Size: 953 KiB |
Before Width: | Height: | Size: 582 B |
|
@ -74,25 +74,12 @@ private:
|
|||
|
||||
tst_QImageReader::tst_QImageReader()
|
||||
{
|
||||
images << QPair<QString, QByteArray>(QLatin1String("colorful.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("font.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("crash-signed-char.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("4bpp-rle.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("tst7.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("16bpp.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("negativeheight.bmp"), QByteArray("bmp"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("marble.xpm"), QByteArray("xpm"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("kollada.png"), QByteArray("png"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("teapot.ppm"), QByteArray("ppm"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("runners.ppm"), QByteArray("ppm"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("test.ppm"), QByteArray("ppm"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("gnus.xbm"), QByteArray("xbm"));
|
||||
#if defined QTEST_HAVE_JPEG
|
||||
images << QPair<QString, QByteArray>(QLatin1String("beavis.jpg"), QByteArray("jpeg"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("YCbCr_cmyk.jpg"), QByteArray("jpeg"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("YCbCr_rgb.jpg"), QByteArray("jpeg"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("task210380.jpg"), QByteArray("jpeg"));
|
||||
#endif
|
||||
images << QPair<QString, QByteArray>(QLatin1String("earth.gif"), QByteArray("gif"));
|
||||
images << QPair<QString, QByteArray>(QLatin1String("trolltech.gif"), QByteArray("gif"));
|
||||
}
|
||||
|
|
|
@ -255,7 +255,7 @@ void tst_QPixmap::fromImageReader_data()
|
|||
image.save(QString::fromLatin1("test.png"));
|
||||
|
||||
// RGB32
|
||||
const QString rgb32Path = tempDir + QString::fromLatin1("/rgb32.jpg");
|
||||
const QString rgb32Path = tempDir + QString::fromLatin1("/rgb32.png");
|
||||
image.save(rgb32Path);
|
||||
QTest::newRow("gradient RGB32") << rgb32Path;
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
if(WITH_JPEG AND JPEG_FOUND)
|
||||
katie_test(tst_bench_jpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/jpeg.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_bench_jpeg KtGui)
|
||||
endif()
|
|
@ -1,68 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the test suite of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QDebug>
|
||||
#include <qtest.h>
|
||||
#include <QtTest/QtTest>
|
||||
#include <QFile>
|
||||
#include <QByteArray>
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QSize>
|
||||
|
||||
class tst_jpeg : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void jpegDecodingQtWebkitStyle();
|
||||
};
|
||||
|
||||
void tst_jpeg::jpegDecodingQtWebkitStyle()
|
||||
{
|
||||
// QtWebkit currently calls size() to get the image size for layouting purposes.
|
||||
// Then when it is in the viewport (we assume that here) it actually gets decoded.
|
||||
QFile inputJpeg(QLatin1String(SRCDIR "n900.jpeg"));
|
||||
QVERIFY(inputJpeg.exists());
|
||||
inputJpeg.open(QIODevice::ReadOnly);
|
||||
QByteArray imageData = inputJpeg.readAll();
|
||||
QBuffer buffer;
|
||||
buffer.setData(imageData);
|
||||
buffer.open(QBuffer::ReadOnly);
|
||||
QCOMPARE(buffer.size(), qint64(19016));
|
||||
|
||||
|
||||
QBENCHMARK{
|
||||
for (int i = 0; i < 50; i++) {
|
||||
QImageReader reader(&buffer, "jpeg");
|
||||
QSize size = reader.size();
|
||||
QVERIFY(!size.isNull());
|
||||
QByteArray format = reader.format();
|
||||
QVERIFY(!format.isEmpty());
|
||||
QImage img = reader.read();
|
||||
QVERIFY(!img.isNull());
|
||||
buffer.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_jpeg)
|
||||
|
||||
#include "moc_jpeg.cpp"
|
Before Width: | Height: | Size: 19 KiB |