summaryrefslogtreecommitdiffstats
path: root/kimgio
diff options
context:
space:
mode:
Diffstat (limited to 'kimgio')
-rw-r--r--kimgio/AUTHORS13
-rw-r--r--kimgio/ChangeLog8
-rw-r--r--kimgio/Mainpage.dox29
-rw-r--r--kimgio/Makefile.am94
-rw-r--r--kimgio/README75
-rw-r--r--kimgio/bmp.kimgio85
-rw-r--r--kimgio/configure.in.in24
-rw-r--r--kimgio/dds.cpp1018
-rw-r--r--kimgio/dds.h21
-rw-r--r--kimgio/dds.kimgio64
-rw-r--r--kimgio/eps.cpp294
-rw-r--r--kimgio/eps.h11
-rw-r--r--kimgio/eps.kimgio86
-rw-r--r--kimgio/exr.cpp167
-rw-r--r--kimgio/exr.h21
-rw-r--r--kimgio/exr.kimgio66
-rw-r--r--kimgio/g3.kimgio77
-rw-r--r--kimgio/g3r.cpp53
-rw-r--r--kimgio/g3r.h20
-rw-r--r--kimgio/gif.kimgio85
-rw-r--r--kimgio/gimp.h411
-rw-r--r--kimgio/hdr.cpp265
-rw-r--r--kimgio/hdr.h21
-rw-r--r--kimgio/hdr.kimgio62
-rw-r--r--kimgio/ico.cpp377
-rw-r--r--kimgio/ico.h46
-rw-r--r--kimgio/ico.kimgio84
-rw-r--r--kimgio/jp2.cpp316
-rw-r--r--kimgio/jp2.h13
-rw-r--r--kimgio/jp2.kimgio85
-rw-r--r--kimgio/jpeg.kimgio85
-rw-r--r--kimgio/mng.kimgio79
-rw-r--r--kimgio/pbm.kimgio84
-rw-r--r--kimgio/pcx.cpp534
-rw-r--r--kimgio/pcx.h106
-rw-r--r--kimgio/pcx.kimgio85
-rw-r--r--kimgio/pgm.kimgio84
-rw-r--r--kimgio/png.kimgio85
-rw-r--r--kimgio/ppm.kimgio85
-rw-r--r--kimgio/psd.cpp282
-rw-r--r--kimgio/psd.h23
-rw-r--r--kimgio/psd.kimgio72
-rw-r--r--kimgio/rgb.cpp589
-rw-r--r--kimgio/rgb.h97
-rw-r--r--kimgio/rgb.kimgio80
-rw-r--r--kimgio/tga.cpp390
-rw-r--r--kimgio/tga.h21
-rw-r--r--kimgio/tga.kimgio86
-rw-r--r--kimgio/tiff.kimgio85
-rw-r--r--kimgio/tiffr.cpp151
-rw-r--r--kimgio/tiffr.h20
-rw-r--r--kimgio/xbm.kimgio85
-rw-r--r--kimgio/xcf.cpp2116
-rw-r--r--kimgio/xcf.h231
-rw-r--r--kimgio/xcf.kimgio78
-rw-r--r--kimgio/xpm.kimgio85
-rw-r--r--kimgio/xv.kimgio87
-rw-r--r--kimgio/xview.cpp169
-rw-r--r--kimgio/xview.h12
59 files changed, 9977 insertions, 0 deletions
diff --git a/kimgio/AUTHORS b/kimgio/AUTHORS
new file mode 100644
index 000000000..dabb30c4b
--- /dev/null
+++ b/kimgio/AUTHORS
@@ -0,0 +1,13 @@
+Sirtaj Singh Kang <taj@kde.org> -- kimgio and jpeg, tiff, png, krl readers
+Dirk Schoenberger <> -- eps, netpbm readers
+Torben Weis <weis@kde.org> -- XV format reader/writer
+Thomas Tanghus <tanghus@kde.org> -- PNG writer
+Antonio Larossa <larossa@kde.org> -- initial version of KRL reader
+Sven Wiegand <SWiegand@tfh-berlin.de> -- eps output filter (from KSnapshot)
+Dominik Seichter <domseichter@web.de> -- TGA format read/write support
+Nadeem Hasan <nhasan@kde.org> -- PCX format read/write support
+Melchior Franz <mfranz@kde.org> -- SGI format read/write support, port of XCF qimgio
+Allen Barnett <allen@lignumcomputing.com> -- XCF format read support (qimgio)
+Ignacio Castao <castano@ludicon.com> -- DDS and PDS format reader.
+Christoph Hormann <chris_hormann@gmx.de> -- HDR format read support.
+Michael Ritzert <kde@ritzert.de> -- JPEG 2000 format read/write support
diff --git a/kimgio/ChangeLog b/kimgio/ChangeLog
new file mode 100644
index 000000000..3d9f6dc14
--- /dev/null
+++ b/kimgio/ChangeLog
@@ -0,0 +1,8 @@
+1998-12-08 Alex Zepeda <garbanzo@hooked.net>
+
+ * pngr.cpp (kimgio_png_write): Removed text after #else and #endif. The
+ text should either be commented or made part of an #elif.
+
+1999 Oct 5 Richard Moore <rich@kde.org>
+
+ * Added EPS output filter from ksnapshot
diff --git a/kimgio/Mainpage.dox b/kimgio/Mainpage.dox
new file mode 100644
index 000000000..d63f10e9b
--- /dev/null
+++ b/kimgio/Mainpage.dox
@@ -0,0 +1,29 @@
+/** @mainpage ImageFormat Plugins
+
+Provides imageformat plugins for Qt so that it can read more image file types.
+
+@authors
+Sirtaj Singh Kang \<taj@kde.org><br>
+Dirk Schoenberger<br>
+Torben Weis \<weis@kde.org><br>
+Thomas Tanghus \<tanghus@kde.org><br>
+Antonio Larossa \<larossa@kde.org\><br>
+Sven Wiegand \<SWiegand@tfh-berlin.de><br>
+Dominik Seichter \<domseichter@web.de><br>
+Nadeem Hasan \<nhasan@kde.org><br>
+Melchior Franz \<mfranz@kde.org><br>
+Allen Barnett \<allen@lignumcomputing.com><br>
+Ignacio Castaño \<castano@ludicon.com><br>
+Christoph Hormann \<chris_hormann@gmx.de><br>
+Michael Ritzert \<kde@ritzert.de>
+
+@maintainers
+[Unknown/None]
+
+@licenses
+@lgpl
+
+*/
+
+// DOXYGEN_SET_PROJECT_NAME = KImgIO
+// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/kimgio/Makefile.am b/kimgio/Makefile.am
new file mode 100644
index 000000000..3a28d9eeb
--- /dev/null
+++ b/kimgio/Makefile.am
@@ -0,0 +1,94 @@
+
+if include_TIFF_MODULES
+KIMGIO_TIFF_MODULES=kimg_tiff.la # kimg_g3.la
+KIMGIO_TIFF_DATAFILES=tiff.kimgio # g3.kimgio
+endif
+
+if include_JP2_MODULES
+KIMGIO_JP2_MODULES=kimg_jp2.la
+KIMGIO_JP2_DATAFILES=jp2.kimgio
+endif
+
+if include_EXR_MODULES
+KIMGIO_EXR_MODULES=kimg_exr.la
+KIMGIO_EXR_DATAFILES=exr.kimgio
+endif
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+kde_module_LTLIBRARIES= kimg_eps.la kimg_xview.la \
+ $(KIMGIO_TIFF_MODULES) kimg_ico.la $(KIMGIO_JP2_MODULES) \
+ kimg_pcx.la kimg_tga.la kimg_rgb.la kimg_xcf.la kimg_dds.la $(KIMGIO_EXR_MODULES) \
+ kimg_psd.la kimg_hdr.la
+
+KIMGIO_PLUGIN = -avoid-version -export-symbols-regex 'kimgio_.*_(read|write)'
+
+kimg_tiff_la_SOURCES = tiffr.cpp
+kimg_tiff_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_tiff_la_LIBADD = $(LIBTIFF) $(LIB_QT)
+
+#kimg_g3_la_SOURCES = g3r.cpp
+#kimg_g3_la_LDFLAGS = -module $(USER_LDFLAGS) $(KIMGIO_PLUGIN) -no-undefined
+#kimg_g3_la_LIBADD = $(LIBTIFF)
+
+kimg_xview_la_SOURCES = xview.cpp
+kimg_xview_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_xview_la_LIBADD = $(LIB_QT)
+
+kimg_eps_la_SOURCES = eps.cpp
+kimg_eps_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_eps_la_LIBADD = $(LIB_KDECORE)
+
+kimg_ico_la_SOURCES = ico.cpp
+kimg_ico_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_ico_la_LIBADD = $(LIB_QT)
+
+kimg_jp2_la_SOURCES = jp2.cpp
+kimg_jp2_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_jp2_la_LIBADD = $(LIB_QT) $(LIB_JASPER) ../kdecore/libkdecore.la
+
+kimg_pcx_la_SOURCES = pcx.cpp
+kimg_pcx_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_pcx_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+kimg_tga_la_SOURCES = tga.cpp
+kimg_tga_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_tga_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+kimg_rgb_la_SOURCES = rgb.cpp
+kimg_rgb_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_rgb_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+kimg_xcf_la_SOURCES = xcf.cpp
+kimg_xcf_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_xcf_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+kimg_exr_la_SOURCES = exr.cpp
+kimg_exr_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_exr_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_EXR)
+
+kimg_dds_la_SOURCES = dds.cpp
+kimg_dds_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_dds_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+kimg_psd_la_SOURCES = psd.cpp
+kimg_psd_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_psd_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+kimg_hdr_la_SOURCES = hdr.cpp
+kimg_hdr_la_LDFLAGS = -module $(KIMGIO_PLUGIN) -no-undefined $(all_libraries)
+kimg_hdr_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
+noinst_HEADERS= xview.h eps.h tiffr.h g3r.h ico.h jp2.h pcx.h tga.h rgb.h xcf.h gimp.h exr.h dds.h psd.h hdr.h
+
+INCLUDES = -I$(top_srcdir)/kio -I$(top_srcdir)/dcop -I$(top_srcdir)/libltdl $(all_includes) -Drestrict= $(EXR_FLAGS)
+
+servicedir = $(kde_servicesdir)
+service_DATA = png.kimgio xpm.kimgio bmp.kimgio pbm.kimgio pgm.kimgio \
+ ppm.kimgio xbm.kimgio jpeg.kimgio xv.kimgio eps.kimgio \
+ $(KIMGIO_TIFF_DATAFILES) ico.kimgio $(KIMGIO_JP2_DATAFILES) \
+ gif.kimgio pcx.kimgio tga.kimgio rgb.kimgio xcf.kimgio dds.kimgio \
+ $(KIMGIO_EXR_DATAFILES) mng.kimgio psd.kimgio hdr.kimgio
+
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/kimgio/README b/kimgio/README
new file mode 100644
index 000000000..6c9cec844
--- /dev/null
+++ b/kimgio/README
@@ -0,0 +1,75 @@
+KDE Image I/O library
+---------------------
+This library allows applications that use the Qt library
+(i.e. QImageIO, QImage, QPixmap and friends) to read and
+write images in extra formats. Current formats include:
+
+JPEG <read> <write>
+JPEG2000 <read> <write>
+XV <read> <write>
+EPS <read> <write>
+NETPBM <incomplete>
+PNG <read> <write, only with newer libraries>
+TIFF <read>
+TGA <read> <write>
+PCX <read> <write>
+SGI <read> <write> (images/x-rgb: *.bw, *.rgb, *.rgba, *.sgi)
+DDS <read>
+XCF <read>
+
+(Some example files are in kdenonbeta/kimgio_examples.)
+
+
+To use these formats, you only need to:
+
+1. link the application with the libkio library
+2. Include the <kimageio.h> header
+3. call KImageIO::registerFormats() once, somewhere in your code
+ before you load an image.
+
+Writing handlers
+----------------
+
+0. Please read the documentation for the QImageIO class in the Qt
+documentation.
+
+1. When writing handlers, there is a function naming convention;
+suppose, for example, we were writing PNG read and write handlers,
+we would name them
+
+void kimgio_png_read ( QImageIO * );
+void kimgio_png_write( QImageIO * );
+
+ie
+
+kimgio_<format>_<read/write>
+
+This should reduce the chance of identifier clashes with other code.
+
+2. Remember that a given KDE application may try to load dozens of
+images at once such as when loading icons, or creating thumbnails.
+Also, it may well be loading them over a network connection.
+Therefore,
+
+ - Avoid creating temporary files or allocating too much memory
+ when decoding and encoding. Especially try to avoid firing off
+ external programs.
+
+ - Don't assume that the IODevice which is the source or target
+ of the image data is pointing to a file on the local filesystem.
+ Use the IODevice methods to read and write image data.
+
+ - Check for file corruption or premature end of the image,
+ especially before using values read from the file
+ e.g. for memory allocations.
+
+3. If you only have either a reader or the writer for a particular
+format, don't use NULL in QImageIO::defineIOHandler. Instead, write
+a stub function for the unimplemented handler which displays a message
+on standard output. This prevents kimgio-using programs dumping core
+when attempting to call the unimplemented handler.
+
+
+Yours in good faith and pedantry,
+
+Sirtaj Singh Kang <taj@kde.org>, 23 September 1998.
diff --git a/kimgio/bmp.kimgio b/kimgio/bmp.kimgio
new file mode 100644
index 000000000..e857b8a2c
--- /dev/null
+++ b/kimgio/bmp.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=BMP
+Header=
+Name=BMP Image
+Name[af]=BMP Beeld
+Name[ar]=صورة من نوع BMP
+Name[az]=BMP Rəsmi
+Name[be]=Малюнак BMP
+Name[bg]=BMP изображение
+Name[bn]=বি-এম-পি চিত্র
+Name[br]=Skeudenn BMP
+Name[bs]=BMP slika
+Name[ca]=Imatge BMP
+Name[cs]=Obrázek BMP
+Name[csb]=Òbrôzk BMP
+Name[cy]=Delwedd BMP
+Name[da]=BMP-billede
+Name[de]=BMP-Bild
+Name[el]=Εικόνα BMP
+Name[eo]=BMP bildo
+Name[es]=Imagen BMP
+Name[et]=BMP pildifail
+Name[eu]=BMP irudia
+Name[fa]=تصویر BMP
+Name[fi]=BMP-kuva
+Name[fr]=Image BMP
+Name[fy]=BMP ôfbylding
+Name[ga]=Íomhá BMP
+Name[gl]=Imaxe BMP
+Name[he]=תמונת BMP
+Name[hi]=BMP छवि
+Name[hr]=BMP slika
+Name[hu]=BMP-kép
+Name[id]=Gambar BMP
+Name[is]=BMP myndir
+Name[it]=Immagine BMP
+Name[ja]=BMP 画像
+Name[ka]=BMP ნახატი
+Name[kk]=BMP кескіні
+Name[km]=រូបភាព BMP
+Name[ko]=BMP 그림
+Name[lb]=BMP-Bild
+Name[lt]=BMP paveiksliukas
+Name[lv]=BMP bilde
+Name[mk]=BMP слика
+Name[mn]=BMP Зураг
+Name[ms]=Imej BMP
+Name[nb]=BMP-bilde
+Name[nds]=BMP-Bild
+Name[ne]=BMP छवि
+Name[nl]=BMP-afbeelding
+Name[nn]=BMP-bilete
+Name[pa]=BMP ਚਿੱਤਰ
+Name[pl]=Obrazek BMP
+Name[pt]=Imagem BMP
+Name[pt_BR]=Imagem BMP
+Name[ro]=Imagine BMP
+Name[ru]=Рисунок BMP
+Name[rw]=BMP Ishusho
+Name[se]=BMP-govva
+Name[sk]=BMP obrázok
+Name[sl]=Slika BMP
+Name[sq]=Imazh BMP
+Name[sr]=BMP слика
+Name[sr@Latn]=BMP slika
+Name[sv]=BMP-bild
+Name[ta]=BMP பிம்பம்
+Name[te]=BMP ప్రతిబింబం
+Name[tg]=Тасвири BMP
+Name[th]=แฟ้มภาพ BMP
+Name[tr]=BMP Resim Dosyası
+Name[tt]=BMP Sürät
+Name[uk]=Зображення BMP
+Name[uz]=BMP-rasm
+Name[uz@cyrillic]=BMP-расм
+Name[vi]=Ảnh BMP
+Name[wa]=Imådje BMP
+Name[zh_CN]=BMP 图像
+Name[zh_HK]=點陣圖
+Name[zh_TW]=BMP 影像
+Read=true
+Write=true
+Suffices=bmp,BMP
+Mimetype=image/x-bmp
+Library=
diff --git a/kimgio/configure.in.in b/kimgio/configure.in.in
new file mode 100644
index 000000000..fa17ad3ee
--- /dev/null
+++ b/kimgio/configure.in.in
@@ -0,0 +1,24 @@
+AC_ARG_WITH(tiff,AC_HELP_STRING([--with-tiff],[Enable tiff support [default=check]]),[tiff_test="$withval"],[tiff_test="yes"])
+
+if test "x$tiff_test" = "xyes" ; then
+AC_FIND_TIFF
+fi
+
+AC_FIND_JPEG
+AC_FIND_PNG
+
+AC_ARG_WITH(jasper,AC_HELP_STRING([--with-jasper],[Enable jasper (jpeg2k) support [default=check]]),[jasper_test="$withval"],[jasper_test="yes"])
+
+if test "x$jasper_test" = "xyes" ; then
+AC_FIND_JASPER
+fi
+
+AC_ARG_WITH(openexr,AC_HELP_STRING([--with-openexr],[Enable openexr support [default=check]]),[openexr_test="$withval"],[openexr_test="yes"])
+
+if test "x$openexr_test" = "xyes" ; then
+ KDE_FIND_LIBEXR
+fi
+
+AM_CONDITIONAL(include_TIFF_MODULES, test -n "$LIBTIFF")
+AM_CONDITIONAL(include_JP2_MODULES, test -n "$LIB_JASPER")
+AM_CONDITIONAL(include_EXR_MODULES, test -n "$LIB_EXR")
diff --git a/kimgio/dds.cpp b/kimgio/dds.cpp
new file mode 100644
index 000000000..d08da6936
--- /dev/null
+++ b/kimgio/dds.cpp
@@ -0,0 +1,1018 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Ignacio Castao <castano@ludicon.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ Almost all this code is based on nVidia's DDS-loading example
+ and the DevIl's source code by Denton Woods.
+*/
+
+/* this code supports:
+ * reading:
+ * rgb and dxt dds files
+ * cubemap dds files
+ * volume dds files -- TODO
+ * writing:
+ * rgb dds files only -- TODO
+ */
+
+#include "dds.h"
+
+#include <qimage.h>
+#include <qdatastream.h>
+
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include <math.h> // sqrtf
+
+#ifndef __USE_ISOC99
+#define sqrtf(x) ((float)sqrt(x))
+#endif
+
+typedef Q_UINT32 uint;
+typedef Q_UINT16 ushort;
+typedef Q_UINT8 uchar;
+
+namespace { // Private.
+
+#if !defined(MAKEFOURCC)
+# define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ (uint(uchar(ch0)) | (uint(uchar(ch1)) << 8) | \
+ (uint(uchar(ch2)) << 16) | (uint(uchar(ch3)) << 24 ))
+#endif
+
+#define HORIZONTAL 1
+#define VERTICAL 2
+#define CUBE_LAYOUT HORIZONTAL
+
+ struct Color8888
+ {
+ uchar r, g, b, a;
+ };
+
+ union Color565
+ {
+ struct {
+ ushort b : 5;
+ ushort g : 6;
+ ushort r : 5;
+ } c;
+ ushort u;
+ };
+
+ union Color1555 {
+ struct {
+ ushort b : 5;
+ ushort g : 5;
+ ushort r : 5;
+ ushort a : 1;
+ } c;
+ ushort u;
+ };
+
+ union Color4444 {
+ struct {
+ ushort b : 4;
+ ushort g : 4;
+ ushort r : 4;
+ ushort a : 4;
+ } c;
+ ushort u;
+ };
+
+
+ static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
+ static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
+ static const uint FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2');
+ static const uint FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3');
+ static const uint FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4');
+ static const uint FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5');
+ static const uint FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B');
+ static const uint FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2');
+
+ static const uint DDSD_CAPS = 0x00000001l;
+ static const uint DDSD_PIXELFORMAT = 0x00001000l;
+ static const uint DDSD_WIDTH = 0x00000004l;
+ static const uint DDSD_HEIGHT = 0x00000002l;
+ static const uint DDSD_PITCH = 0x00000008l;
+
+ static const uint DDSCAPS_TEXTURE = 0x00001000l;
+ static const uint DDSCAPS2_VOLUME = 0x00200000l;
+ static const uint DDSCAPS2_CUBEMAP = 0x00000200l;
+
+ static const uint DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400l;
+ static const uint DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800l;
+ static const uint DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000l;
+ static const uint DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000l;
+ static const uint DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000l;
+ static const uint DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000l;
+
+ static const uint DDPF_RGB = 0x00000040l;
+ static const uint DDPF_FOURCC = 0x00000004l;
+ static const uint DDPF_ALPHAPIXELS = 0x00000001l;
+
+ enum DDSType {
+ DDS_A8R8G8B8 = 0,
+ DDS_A1R5G5B5 = 1,
+ DDS_A4R4G4B4 = 2,
+ DDS_R8G8B8 = 3,
+ DDS_R5G6B5 = 4,
+ DDS_DXT1 = 5,
+ DDS_DXT2 = 6,
+ DDS_DXT3 = 7,
+ DDS_DXT4 = 8,
+ DDS_DXT5 = 9,
+ DDS_RXGB = 10,
+ DDS_ATI2 = 11,
+ DDS_UNKNOWN
+ };
+
+
+ struct DDSPixelFormat {
+ uint size;
+ uint flags;
+ uint fourcc;
+ uint bitcount;
+ uint rmask;
+ uint gmask;
+ uint bmask;
+ uint amask;
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, DDSPixelFormat & pf )
+ {
+ s >> pf.size;
+ s >> pf.flags;
+ s >> pf.fourcc;
+ s >> pf.bitcount;
+ s >> pf.rmask;
+ s >> pf.gmask;
+ s >> pf.bmask;
+ s >> pf.amask;
+ return s;
+ }
+
+ struct DDSCaps {
+ uint caps1;
+ uint caps2;
+ uint caps3;
+ uint caps4;
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, DDSCaps & caps )
+ {
+ s >> caps.caps1;
+ s >> caps.caps2;
+ s >> caps.caps3;
+ s >> caps.caps4;
+ return s;
+ }
+
+ struct DDSHeader {
+ uint size;
+ uint flags;
+ uint height;
+ uint width;
+ uint pitch;
+ uint depth;
+ uint mipmapcount;
+ uint reserved[11];
+ DDSPixelFormat pf;
+ DDSCaps caps;
+ uint notused;
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, DDSHeader & header )
+ {
+ s >> header.size;
+ s >> header.flags;
+ s >> header.height;
+ s >> header.width;
+ s >> header.pitch;
+ s >> header.depth;
+ s >> header.mipmapcount;
+ for( int i = 0; i < 11; i++ ) {
+ s >> header.reserved[i];
+ }
+ s >> header.pf;
+ s >> header.caps;
+ s >> header.notused;
+ return s;
+ }
+
+ static bool IsValid( const DDSHeader & header )
+ {
+ if( header.size != 124 ) {
+ return false;
+ }
+ const uint required = (DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT);
+ if( (header.flags & required) != required ) {
+ return false;
+ }
+ if( header.pf.size != 32 ) {
+ return false;
+ }
+ if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) {
+ return false;
+ }
+ return true;
+ }
+
+
+ // Get supported type. We currently support 10 different types.
+ static DDSType GetType( const DDSHeader & header )
+ {
+ if( header.pf.flags & DDPF_RGB ) {
+ if( header.pf.flags & DDPF_ALPHAPIXELS ) {
+ switch( header.pf.bitcount ) {
+ case 16:
+ return (header.pf.amask == 0x8000) ? DDS_A1R5G5B5 : DDS_A4R4G4B4;
+ case 32:
+ return DDS_A8R8G8B8;
+ }
+ }
+ else {
+ switch( header.pf.bitcount ) {
+ case 16:
+ return DDS_R5G6B5;
+ case 24:
+ return DDS_R8G8B8;
+ }
+ }
+ }
+ else if( header.pf.flags & DDPF_FOURCC ) {
+ switch( header.pf.fourcc ) {
+ case FOURCC_DXT1:
+ return DDS_DXT1;
+ case FOURCC_DXT2:
+ return DDS_DXT2;
+ case FOURCC_DXT3:
+ return DDS_DXT3;
+ case FOURCC_DXT4:
+ return DDS_DXT4;
+ case FOURCC_DXT5:
+ return DDS_DXT5;
+ case FOURCC_RXGB:
+ return DDS_RXGB;
+ case FOURCC_ATI2:
+ return DDS_ATI2;
+ }
+ }
+ return DDS_UNKNOWN;
+ }
+
+
+ static bool HasAlpha( const DDSHeader & header )
+ {
+ return header.pf.flags & DDPF_ALPHAPIXELS;
+ }
+
+ static bool IsCubeMap( const DDSHeader & header )
+ {
+ return header.caps.caps2 & DDSCAPS2_CUBEMAP;
+ }
+
+ static bool IsSupported( const DDSHeader & header )
+ {
+ if( header.caps.caps2 & DDSCAPS2_VOLUME ) {
+ return false;
+ }
+ if( GetType(header) == DDS_UNKNOWN ) {
+ return false;
+ }
+ return true;
+ }
+
+
+ static bool LoadA8R8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ for( uint y = 0; y < h; y++ ) {
+ QRgb * scanline = (QRgb *) img.scanLine( y );
+ for( uint x = 0; x < w; x++ ) {
+ uchar r, g, b, a;
+ s >> b >> g >> r >> a;
+ scanline[x] = qRgba(r, g, b, a);
+ }
+ }
+
+ return true;
+ }
+
+ static bool LoadR8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ for( uint y = 0; y < h; y++ ) {
+ QRgb * scanline = (QRgb *) img.scanLine( y );
+ for( uint x = 0; x < w; x++ ) {
+ uchar r, g, b;
+ s >> b >> g >> r;
+ scanline[x] = qRgb(r, g, b);
+ }
+ }
+
+ return true;
+ }
+
+ static bool LoadA1R5G5B5( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ for( uint y = 0; y < h; y++ ) {
+ QRgb * scanline = (QRgb *) img.scanLine( y );
+ for( uint x = 0; x < w; x++ ) {
+ Color1555 color;
+ s >> color.u;
+ uchar a = (color.c.a != 0) ? 0xFF : 0;
+ uchar r = (color.c.r << 3) | (color.c.r >> 2);
+ uchar g = (color.c.g << 3) | (color.c.g >> 2);
+ uchar b = (color.c.b << 3) | (color.c.b >> 2);
+ scanline[x] = qRgba(r, g, b, a);
+ }
+ }
+
+ return true;
+ }
+
+ static bool LoadA4R4G4B4( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ for( uint y = 0; y < h; y++ ) {
+ QRgb * scanline = (QRgb *) img.scanLine( y );
+ for( uint x = 0; x < w; x++ ) {
+ Color4444 color;
+ s >> color.u;
+ uchar a = (color.c.a << 4) | color.c.a;
+ uchar r = (color.c.r << 4) | color.c.r;
+ uchar g = (color.c.g << 4) | color.c.g;
+ uchar b = (color.c.b << 4) | color.c.b;
+ scanline[x] = qRgba(r, g, b, a);
+ }
+ }
+
+ return true;
+ }
+
+ static bool LoadR5G6B5( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ for( uint y = 0; y < h; y++ ) {
+ QRgb * scanline = (QRgb *) img.scanLine( y );
+ for( uint x = 0; x < w; x++ ) {
+ Color565 color;
+ s >> color.u;
+ uchar r = (color.c.r << 3) | (color.c.r >> 2);
+ uchar g = (color.c.g << 2) | (color.c.g >> 4);
+ uchar b = (color.c.b << 3) | (color.c.b >> 2);
+ scanline[x] = qRgb(r, g, b);
+ }
+ }
+
+ return true;
+ }
+
+ static QDataStream & operator>> ( QDataStream & s, Color565 & c )
+ {
+ return s >> c.u;
+ }
+
+
+ struct BlockDXT
+ {
+ Color565 col0;
+ Color565 col1;
+ uchar row[4];
+
+ void GetColors( Color8888 color_array[4] )
+ {
+ color_array[0].r = (col0.c.r << 3) | (col0.c.r >> 2);
+ color_array[0].g = (col0.c.g << 2) | (col0.c.g >> 4);
+ color_array[0].b = (col0.c.b << 3) | (col0.c.b >> 2);
+ color_array[0].a = 0xFF;
+
+ color_array[1].r = (col1.c.r << 3) | (col1.c.r >> 2);
+ color_array[1].g = (col1.c.g << 2) | (col1.c.g >> 4);
+ color_array[1].b = (col1.c.b << 3) | (col1.c.b >> 2);
+ color_array[1].a = 0xFF;
+
+ if( col0.u > col1.u ) {
+ // Four-color block: derive the other two colors.
+ color_array[2].r = (2 * color_array[0].r + color_array[1].r) / 3;
+ color_array[2].g = (2 * color_array[0].g + color_array[1].g) / 3;
+ color_array[2].b = (2 * color_array[0].b + color_array[1].b) / 3;
+ color_array[2].a = 0xFF;
+
+ color_array[3].r = (2 * color_array[1].r + color_array[0].r) / 3;
+ color_array[3].g = (2 * color_array[1].g + color_array[0].g) / 3;
+ color_array[3].b = (2 * color_array[1].b + color_array[0].b) / 3;
+ color_array[3].a = 0xFF;
+ }
+ else {
+ // Three-color block: derive the other color.
+ color_array[2].r = (color_array[0].r + color_array[1].r) / 2;
+ color_array[2].g = (color_array[0].g + color_array[1].g) / 2;
+ color_array[2].b = (color_array[0].b + color_array[1].b) / 2;
+ color_array[2].a = 0xFF;
+
+ // Set all components to 0 to match DXT specs.
+ color_array[3].r = 0x00; // color_array[2].r;
+ color_array[3].g = 0x00; // color_array[2].g;
+ color_array[3].b = 0x00; // color_array[2].b;
+ color_array[3].a = 0x00;
+ }
+ }
+ };
+
+
+ static QDataStream & operator>> ( QDataStream & s, BlockDXT & c )
+ {
+ return s >> c.col0 >> c.col1 >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
+ }
+
+ struct BlockDXTAlphaExplicit {
+ ushort row[4];
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaExplicit & c )
+ {
+ return s >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
+ }
+
+ struct BlockDXTAlphaLinear {
+ uchar alpha0;
+ uchar alpha1;
+ uchar bits[6];
+
+ void GetAlphas( uchar alpha_array[8] )
+ {
+ alpha_array[0] = alpha0;
+ alpha_array[1] = alpha1;
+
+ // 8-alpha or 6-alpha block?
+ if( alpha_array[0] > alpha_array[1] )
+ {
+ // 8-alpha block: derive the other 6 alphas.
+ // 000 = alpha_0, 001 = alpha_1, others are interpolated
+
+ alpha_array[2] = ( 6 * alpha0 + alpha1) / 7; // bit code 010
+ alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7; // Bit code 011
+ alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7; // Bit code 100
+ alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7; // Bit code 101
+ alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7; // Bit code 110
+ alpha_array[7] = ( alpha0 + 6 * alpha1) / 7; // Bit code 111
+ }
+ else
+ {
+ // 6-alpha block: derive the other alphas.
+ // 000 = alpha_0, 001 = alpha_1, others are interpolated
+
+ alpha_array[2] = (4 * alpha0 + alpha1) / 5; // Bit code 010
+ alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5; // Bit code 011
+ alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5; // Bit code 100
+ alpha_array[5] = ( alpha0 + 4 * alpha1) / 5; // Bit code 101
+ alpha_array[6] = 0x00; // Bit code 110
+ alpha_array[7] = 0xFF; // Bit code 111
+ }
+ }
+
+ void GetBits( uchar bit_array[16] )
+ {
+ uint b = (uint &) bits[0];
+ bit_array[0] = uchar(b & 0x07); b >>= 3;
+ bit_array[1] = uchar(b & 0x07); b >>= 3;
+ bit_array[2] = uchar(b & 0x07); b >>= 3;
+ bit_array[3] = uchar(b & 0x07); b >>= 3;
+ bit_array[4] = uchar(b & 0x07); b >>= 3;
+ bit_array[5] = uchar(b & 0x07); b >>= 3;
+ bit_array[6] = uchar(b & 0x07); b >>= 3;
+ bit_array[7] = uchar(b & 0x07); b >>= 3;
+
+ b = (uint &) bits[3];
+ bit_array[8] = uchar(b & 0x07); b >>= 3;
+ bit_array[9] = uchar(b & 0x07); b >>= 3;
+ bit_array[10] = uchar(b & 0x07); b >>= 3;
+ bit_array[11] = uchar(b & 0x07); b >>= 3;
+ bit_array[12] = uchar(b & 0x07); b >>= 3;
+ bit_array[13] = uchar(b & 0x07); b >>= 3;
+ bit_array[14] = uchar(b & 0x07); b >>= 3;
+ bit_array[15] = uchar(b & 0x07); b >>= 3;
+ }
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaLinear & c )
+ {
+ s >> c.alpha0 >> c.alpha1;
+ return s >> c.bits[0] >> c.bits[1] >> c.bits[2] >> c.bits[3] >> c.bits[4] >> c.bits[5];
+ }
+
+ static bool LoadDXT1( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ BlockDXT block;
+ QRgb * scanline[4];
+
+ for( uint y = 0; y < h; y += 4 ) {
+ for( uint j = 0; j < 4; j++ ) {
+ scanline[j] = (QRgb *) img.scanLine( y + j );
+ }
+ for( uint x = 0; x < w; x += 4 ) {
+
+ // Read 64bit color block.
+ s >> block;
+
+ // Decode color block.
+ Color8888 color_array[4];
+ block.GetColors(color_array);
+
+ // bit masks = 00000011, 00001100, 00110000, 11000000
+ const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
+ const int shift[4] = { 0, 2, 4, 6 };
+
+ // Write color block.
+ for( uint j = 0; j < 4; j++ ) {
+ for( uint i = 0; i < 4; i++ ) {
+ if( img.valid( x+i, y+j ) ) {
+ uint idx = (block.row[j] & masks[i]) >> shift[i];
+ scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ static bool LoadDXT3( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ BlockDXT block;
+ BlockDXTAlphaExplicit alpha;
+ QRgb * scanline[4];
+
+ for( uint y = 0; y < h; y += 4 ) {
+ for( uint j = 0; j < 4; j++ ) {
+ scanline[j] = (QRgb *) img.scanLine( y + j );
+ }
+ for( uint x = 0; x < w; x += 4 ) {
+
+ // Read 128bit color block.
+ s >> alpha;
+ s >> block;
+
+ // Decode color block.
+ Color8888 color_array[4];
+ block.GetColors(color_array);
+
+ // bit masks = 00000011, 00001100, 00110000, 11000000
+ const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
+ const int shift[4] = { 0, 2, 4, 6 };
+
+ // Write color block.
+ for( uint j = 0; j < 4; j++ ) {
+ ushort a = alpha.row[j];
+ for( uint i = 0; i < 4; i++ ) {
+ if( img.valid( x+i, y+j ) ) {
+ uint idx = (block.row[j] & masks[i]) >> shift[i];
+ color_array[idx].a = a & 0x0f;
+ color_array[idx].a = color_array[idx].a | (color_array[idx].a << 4);
+ scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
+ }
+ a >>= 4;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ static bool LoadDXT2( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ if( !LoadDXT3(s, header, img) ) return false;
+ //UndoPremultiplyAlpha(img);
+ return true;
+ }
+
+ static bool LoadDXT5( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ BlockDXT block;
+ BlockDXTAlphaLinear alpha;
+ QRgb * scanline[4];
+
+ for( uint y = 0; y < h; y += 4 ) {
+ for( uint j = 0; j < 4; j++ ) {
+ scanline[j] = (QRgb *) img.scanLine( y + j );
+ }
+ for( uint x = 0; x < w; x += 4 ) {
+
+ // Read 128bit color block.
+ s >> alpha;
+ s >> block;
+
+ // Decode color block.
+ Color8888 color_array[4];
+ block.GetColors(color_array);
+
+ uchar alpha_array[8];
+ alpha.GetAlphas(alpha_array);
+
+ uchar bit_array[16];
+ alpha.GetBits(bit_array);
+
+ // bit masks = 00000011, 00001100, 00110000, 11000000
+ const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
+ const int shift[4] = { 0, 2, 4, 6 };
+
+ // Write color block.
+ for( uint j = 0; j < 4; j++ ) {
+ for( uint i = 0; i < 4; i++ ) {
+ if( img.valid( x+i, y+j ) ) {
+ uint idx = (block.row[j] & masks[i]) >> shift[i];
+ color_array[idx].a = alpha_array[bit_array[j*4+i]];
+ scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+ static bool LoadDXT4( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ if( !LoadDXT5(s, header, img) ) return false;
+ //UndoPremultiplyAlpha(img);
+ return true;
+ }
+
+ static bool LoadRXGB( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ BlockDXT block;
+ BlockDXTAlphaLinear alpha;
+ QRgb * scanline[4];
+
+ for( uint y = 0; y < h; y += 4 ) {
+ for( uint j = 0; j < 4; j++ ) {
+ scanline[j] = (QRgb *) img.scanLine( y + j );
+ }
+ for( uint x = 0; x < w; x += 4 ) {
+
+ // Read 128bit color block.
+ s >> alpha;
+ s >> block;
+
+ // Decode color block.
+ Color8888 color_array[4];
+ block.GetColors(color_array);
+
+ uchar alpha_array[8];
+ alpha.GetAlphas(alpha_array);
+
+ uchar bit_array[16];
+ alpha.GetBits(bit_array);
+
+ // bit masks = 00000011, 00001100, 00110000, 11000000
+ const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
+ const int shift[4] = { 0, 2, 4, 6 };
+
+ // Write color block.
+ for( uint j = 0; j < 4; j++ ) {
+ for( uint i = 0; i < 4; i++ ) {
+ if( img.valid( x+i, y+j ) ) {
+ uint idx = (block.row[j] & masks[i]) >> shift[i];
+ color_array[idx].a = alpha_array[bit_array[j*4+i]];
+ scanline[j][x+i] = qRgb(color_array[idx].a, color_array[idx].g, color_array[idx].b);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ static bool LoadATI2( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ const uint w = header.width;
+ const uint h = header.height;
+
+ BlockDXTAlphaLinear xblock;
+ BlockDXTAlphaLinear yblock;
+ QRgb * scanline[4];
+
+ for( uint y = 0; y < h; y += 4 ) {
+ for( uint j = 0; j < 4; j++ ) {
+ scanline[j] = (QRgb *) img.scanLine( y + j );
+ }
+ for( uint x = 0; x < w; x += 4 ) {
+
+ // Read 128bit color block.
+ s >> xblock;
+ s >> yblock;
+
+ // Decode color block.
+ uchar xblock_array[8];
+ xblock.GetAlphas(xblock_array);
+
+ uchar xbit_array[16];
+ xblock.GetBits(xbit_array);
+
+ uchar yblock_array[8];
+ yblock.GetAlphas(yblock_array);
+
+ uchar ybit_array[16];
+ yblock.GetBits(ybit_array);
+
+ // Write color block.
+ for( uint j = 0; j < 4; j++ ) {
+ for( uint i = 0; i < 4; i++ ) {
+ if( img.valid( x+i, y+j ) ) {
+ const uchar nx = xblock_array[xbit_array[j*4+i]];
+ const uchar ny = yblock_array[ybit_array[j*4+i]];
+
+ const float fx = float(nx) / 127.5f - 1.0f;
+ const float fy = float(ny) / 127.5f - 1.0f;
+ const float fz = sqrtf(1.0f - fx*fx - fy*fy);
+ const uchar nz = uchar((fz + 1.0f) * 127.5f);
+
+ scanline[j][x+i] = qRgb(nx, ny, nz);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+
+ typedef bool (* TextureLoader)( QDataStream & s, const DDSHeader & header, QImage & img );
+
+ // Get an appropiate texture loader for the given type.
+ static TextureLoader GetTextureLoader( DDSType type ) {
+ switch( type ) {
+ case DDS_A8R8G8B8:
+ return LoadA8R8G8B8;
+ case DDS_A1R5G5B5:
+ return LoadA1R5G5B5;
+ case DDS_A4R4G4B4:
+ return LoadA4R4G4B4;
+ case DDS_R8G8B8:
+ return LoadR8G8B8;
+ case DDS_R5G6B5:
+ return LoadR5G6B5;
+ case DDS_DXT1:
+ return LoadDXT1;
+ case DDS_DXT2:
+ return LoadDXT2;
+ case DDS_DXT3:
+ return LoadDXT3;
+ case DDS_DXT4:
+ return LoadDXT4;
+ case DDS_DXT5:
+ return LoadDXT5;
+ case DDS_RXGB:
+ return LoadRXGB;
+ case DDS_ATI2:
+ return LoadATI2;
+ default:
+ return NULL;
+ };
+ }
+
+
+ // Load a 2d texture.
+ static bool LoadTexture( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ // Create dst image.
+ if( !img.create( header.width, header.height, 32 )) {
+ return false;
+ }
+
+ // Read image.
+ DDSType type = GetType( header );
+
+ // Enable alpha buffer for transparent or DDS images.
+ if( HasAlpha( header ) || type >= DDS_DXT1 ) {
+ img.setAlphaBuffer( true );
+ }
+
+ TextureLoader loader = GetTextureLoader( type );
+ if( loader == NULL ) {
+ return false;
+ }
+
+ return loader( s, header, img );
+ }
+
+
+ static int FaceOffset( const DDSHeader & header ) {
+
+ DDSType type = GetType( header );
+
+ int mipmap = kMax(int(header.mipmapcount), 1);
+ int size = 0;
+ int w = header.width;
+ int h = header.height;
+
+ if( type >= DDS_DXT1 ) {
+ int multiplier = (type == DDS_DXT1) ? 8 : 16;
+ do {
+ int face_size = kMax(w/4,1) * kMax(h/4,1) * multiplier;
+ size += face_size;
+ w >>= 1;
+ h >>= 1;
+ } while( --mipmap );
+ }
+ else {
+ int multiplier = header.pf.bitcount / 8;
+ do {
+ int face_size = w * h * multiplier;
+ size += face_size;
+ w = kMax( w>>1, 1 );
+ h = kMax( h>>1, 1 );
+ } while( --mipmap );
+ }
+
+ return size;
+ }
+
+#if CUBE_LAYOUT == HORIZONTAL
+ static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} };
+#elif CUBE_LAYOUT == VERTICAL
+ static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {1, 3} };
+#endif
+ static int face_flags[6] = {
+ DDSCAPS2_CUBEMAP_POSITIVEX,
+ DDSCAPS2_CUBEMAP_NEGATIVEX,
+ DDSCAPS2_CUBEMAP_POSITIVEY,
+ DDSCAPS2_CUBEMAP_NEGATIVEY,
+ DDSCAPS2_CUBEMAP_POSITIVEZ,
+ DDSCAPS2_CUBEMAP_NEGATIVEZ
+ };
+
+ // Load unwrapped cube map.
+ static bool LoadCubeMap( QDataStream & s, const DDSHeader & header, QImage & img )
+ {
+ // Create dst image.
+#if CUBE_LAYOUT == HORIZONTAL
+ if( !img.create( 4 * header.width, 3 * header.height, 32 )) {
+ return false; // duplicate code for correct syntax coloring.
+ }
+#elif CUBE_LAYOUT == VERTICAL
+ if( !img.create( 3 * header.width, 4 * header.height, 32 )) {
+ return false;
+ }
+#endif
+
+ DDSType type = GetType( header );
+
+ // Enable alpha buffer for transparent or DDS images.
+ if( HasAlpha( header ) || type >= DDS_DXT1 ) {
+ img.setAlphaBuffer( true );
+ }
+
+ // Select texture loader.
+ TextureLoader loader = GetTextureLoader( type );
+ if( loader == NULL ) {
+ return false;
+ }
+
+ // Clear background.
+ img.fill( 0 );
+
+ // Create face image.
+ QImage face;
+ if( !face.create( header.width, header.height, 32 )) {
+ return false;
+ }
+
+ int offset = s.device()->at();
+ int size = FaceOffset( header );
+
+ for( int i = 0; i < 6; i++ ) {
+
+ if( !(header.caps.caps2 & face_flags[i]) ) {
+ // Skip face.
+ continue;
+ }
+
+ // Seek device.
+ s.device()->at( offset );
+ offset += size;
+
+ // Load face from stream.
+ if( !loader( s, header, face ) ) {
+ return false;
+ }
+
+#if CUBE_LAYOUT == VERTICAL
+ if( i == 5 ) {
+ face = face.mirror(true, true);
+ }
+#endif
+
+ // Compute face offsets.
+ int offset_x = face_offset[i][0] * header.width;
+ int offset_y = face_offset[i][1] * header.height;
+
+ // Copy face on the image.
+ for( uint y = 0; y < header.height; y++ ) {
+ QRgb * src = (QRgb *) face.scanLine( y );
+ QRgb * dst = (QRgb *) img.scanLine( y + offset_y ) + offset_x;
+ memcpy( dst, src, sizeof(QRgb) * header.width );
+ }
+ }
+
+ return true;
+ }
+
+}
+
+
+KDE_EXPORT void kimgio_dds_read( QImageIO *io )
+{
+ QDataStream s( io->ioDevice() );
+ s.setByteOrder( QDataStream::LittleEndian );
+
+ // Validate header.
+ uint fourcc;
+ s >> fourcc;
+ if( fourcc != FOURCC_DDS ) {
+ kdDebug(399) << "This is not a DDS file." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ // Read image header.
+ DDSHeader header;
+ s >> header;
+
+ // Check image file format.
+ if( s.atEnd() || !IsValid( header ) ) {
+ kdDebug(399) << "This DDS file is not valid." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ // Determine image type, by now, we only support 2d textures.
+ if( !IsSupported( header ) ) {
+ kdDebug(399) << "This DDS file is not supported." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+
+ QImage img;
+ bool result;
+
+ if( IsCubeMap( header ) ) {
+ result = LoadCubeMap( s, header, img );
+ }
+ else {
+ result = LoadTexture( s, header, img );
+ }
+
+ if( result == false ) {
+ kdDebug(399) << "Error loading DDS file." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ io->setImage( img );
+ io->setStatus( 0 );
+}
+
+
+KDE_EXPORT void kimgio_dds_write( QImageIO * )
+{
+ // TODO Stub!
+}
+
diff --git a/kimgio/dds.h b/kimgio/dds.h
new file mode 100644
index 000000000..0f79a5ad9
--- /dev/null
+++ b/kimgio/dds.h
@@ -0,0 +1,21 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Ignacio Castao <castano@ludicon.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#ifndef KIMG_DDS_H
+#define KIMG_DDS_H
+
+class QImageIO;
+
+extern "C" {
+void kimgio_dds_read( QImageIO * );
+void kimgio_dds_write( QImageIO * );
+}
+
+#endif
+
diff --git a/kimgio/dds.kimgio b/kimgio/dds.kimgio
new file mode 100644
index 000000000..2bd5f33a9
--- /dev/null
+++ b/kimgio/dds.kimgio
@@ -0,0 +1,64 @@
+[Image Format]
+Type=DDS
+Header=^DDS
+Name=Direct Draw Surface
+Name[af]=Direct Draw Oppervlakte
+Name[be]=Паверхня Direct Draw
+Name[bg]=Област с директно изчертаване
+Name[bn]=ডাইরেক্ট ড্র সারফেস
+Name[bs]=Površina za direktno crtanje
+Name[ca]=Superfície de dibuix directe
+Name[csb]=Wiéchrzëzna Direct Draw
+Name[da]=Overflade til direkte tegning
+Name[de]=Oberfläche für Direct Draw
+Name[eo]=Rekta desegnosurfaco
+Name[eu]=Zuzeneko marrazketarako gainazala
+Name[fa]=سطح ترسیم مستقیم
+Name[fi]=Suora piirtotaso
+Name[fr]=Surface Direct Draw
+Name[gl]=Debuxar Superfície Directamente
+Name[he]=משטח של Direct Draw
+Name[hi]=डायरेक्ट ड्रा सरफेस
+Name[hr]=Direct Draw površina
+Name[hu]=DirectDraw-felület
+Name[is]=Direct Draw yfirborð
+Name[it]=Superficie DirectDraw
+Name[ka]=Direct Draw ზედაპირი
+Name[kk]=Direct Draw беті
+Name[lb]=Direct-Draw-Uewerfläch
+Name[lv]=Direct Draw virsma
+Name[mk]=Direct Draw-површина
+Name[ms]=Permukaan Direct Draw
+Name[nb]=Overflate for direktetegning
+Name[nds]=Böversiet för "Direct Draw"
+Name[ne]=प्रत्यक्ष कोर्ने सतह
+Name[nl]=Direct beeldgeheugen
+Name[nn]=Overflate for direkteteikning
+Name[pa]=ਸਿੱਧਾ ਖਿੱਚਿਆ ਤਲ
+Name[pl]=Powierzchnia Direct Draw
+Name[pt]=Superfície Direct Draw
+Name[pt_BR]=DirectDraw
+Name[ro]=Suprafaţă de desenare directă
+Name[ru]=Поверхность Direct Draw
+Name[rw]=Gushushanya Ubuso Akokanya
+Name[se]=Guovlu masa sáhttá sárgut dakkaviđe
+Name[sk]=Povrch Direct Draw
+Name[sl]=Površina Direct Draw
+Name[sr]=Површина за директно цртање
+Name[sr@Latn]=Površina za direktno crtanje
+Name[sv]=Direktrityta
+Name[ta]=நேரடியாக வரையும் சூழல்
+Name[te]=డైరక్ట్ డ్రా ఉపరితలం
+Name[tg]=Кашидани сатҳи мустақим
+Name[th]=พื้นผิว Direct Draw
+Name[tr]=Doğrudan Çizim Yüzeyi
+Name[tt]=Direct Draw Öslege
+Name[uk]=Поверхня Direct Draw
+Name[vi]=Mặt vẽ trực tiếp
+Name[zh_CN]=DirectDraw 表面
+Read=true
+Write=false
+Suffices=dds,DDS
+Mimetype=image/x-dds
+Library=kimg_dds.la
+
diff --git a/kimgio/eps.cpp b/kimgio/eps.cpp
new file mode 100644
index 000000000..b1b3e28f5
--- /dev/null
+++ b/kimgio/eps.cpp
@@ -0,0 +1,294 @@
+// This library is distributed under the conditions of the GNU LGPL.
+#include <unistd.h>
+#include <stdio.h>
+#include <qimage.h>
+#include <qfile.h>
+#include <qpainter.h>
+#include <qprinter.h>
+#include <kapplication.h>
+#include <ktempfile.h>
+#include <kdebug.h>
+#include "eps.h"
+
+#define BUFLEN 200
+
+#define BBOX "%%BoundingBox:"
+#define BBOX_LEN strlen(BBOX)
+
+static bool seekToCodeStart( QIODevice * io, Q_UINT32 & ps_offset, Q_UINT32 & ps_size )
+{
+ char buf[4]; // We at most need to read 4 bytes at a time
+ ps_offset=0L;
+ ps_size=0L;
+
+ if ( io->readBlock(buf, 2)!=2 ) // Read first two bytes
+ {
+ kdError(399) << "kimgio EPS: EPS file has less than 2 bytes." << endl;
+ return false;
+ }
+
+ if ( buf[0]=='%' && buf[1]=='!' ) // Check %! magic
+ {
+ kdDebug(399) << "kimgio EPS: normal EPS file" << endl;
+ }
+ else if ( buf[0]==char(0xc5) && buf[1]==char(0xd0) ) // Check start of MS-DOS EPS magic
+ { // May be a MS-DOS EPS file
+ if ( io->readBlock(buf+2, 2)!=2 ) // Read further bytes of MS-DOS EPS magic
+ {
+ kdError(399) << "kimgio EPS: potential MS-DOS EPS file has less than 4 bytes." << endl;
+ return false;
+ }
+ if ( buf[2]==char(0xd3) && buf[3]==char(0xc6) ) // Check last bytes of MS-DOS EPS magic
+ {
+ if (io->readBlock(buf, 4)!=4) // Get offset of PostScript code in the MS-DOS EPS file.
+ {
+ kdError(399) << "kimgio EPS: cannot read offset of MS-DOS EPS file" << endl;
+ return false;
+ }
+ ps_offset // Offset is in little endian
+ = ((unsigned char) buf[0])
+ + ((unsigned char) buf[1] << 8)
+ + ((unsigned char) buf[2] << 16)
+ + ((unsigned char) buf[3] << 24);
+ if (io->readBlock(buf, 4)!=4) // Get size of PostScript code in the MS-DOS EPS file.
+ {
+ kdError(399) << "kimgio EPS: cannot read size of MS-DOS EPS file" << endl;
+ return false;
+ }
+ ps_size // Size is in little endian
+ = ((unsigned char) buf[0])
+ + ((unsigned char) buf[1] << 8)
+ + ((unsigned char) buf[2] << 16)
+ + ((unsigned char) buf[3] << 24);
+ kdDebug(399) << "kimgio EPS: Offset: " << ps_offset <<" Size: " << ps_size << endl;
+ if ( !io->at(ps_offset) ) // Get offset of PostScript code in the MS-DOS EPS file.
+ {
+ kdError(399) << "kimgio EPS: cannot seek in MS-DOS EPS file" << endl;
+ return false;
+ }
+ if ( io->readBlock(buf, 2)!=2 ) // Read first two bytes of what should be the Postscript code
+ {
+ kdError(399) << "kimgio EPS: PostScript code has less than 2 bytes." << endl;
+ return false;
+ }
+ if ( buf[0]=='%' && buf[1]=='!' ) // Check %! magic
+ {
+ kdDebug(399) << "kimgio EPS: MS-DOS EPS file" << endl;
+ }
+ else
+ {
+ kdError(399) << "kimgio EPS: supposed Postscript code of a MS-DOS EPS file doe not start with %!." << endl;
+ return false;
+ }
+ }
+ else
+ {
+ kdError(399) << "kimgio EPS: wrong magic for potential MS-DOS EPS file!" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ kdError(399) << "kimgio EPS: not an EPS file!" << endl;
+ return false;
+ }
+ return true;
+}
+
+static bool bbox ( QIODevice *io, int *x1, int *y1, int *x2, int *y2)
+{
+ char buf[BUFLEN+1];
+
+ bool ret = false;
+
+ while (io->readLine(buf, BUFLEN) > 0)
+ {
+ if (strncmp (buf, BBOX, BBOX_LEN) == 0)
+ {
+ // Some EPS files have non-integer values for the bbox
+ // We don't support that currently, but at least we parse it
+ float _x1, _y1, _x2, _y2;
+ if ( sscanf (buf, "%*s %f %f %f %f",
+ &_x1, &_y1, &_x2, &_y2) == 4) {
+ kdDebug(399) << "kimgio EPS BBOX: " << _x1 << " " << _y1 << " " << _x2 << " " << _y2 << endl;
+ *x1=(int)_x1; *y1=(int)_y1; *x2=(int)_x2; *y2=(int)_y2;
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+KDE_EXPORT void kimgio_eps_read (QImageIO *image)
+{
+ kdDebug(399) << "kimgio EPS: starting..." << endl;
+
+ FILE * ghostfd;
+ int x1, y1, x2, y2;
+ //QTime dt;
+ //dt.start();
+
+ QString cmdBuf;
+ QString tmp;
+
+ QIODevice* io = image->ioDevice();
+ Q_UINT32 ps_offset, ps_size;
+
+ // find start of PostScript code
+ if ( !seekToCodeStart(io, ps_offset, ps_size) )
+ return;
+
+ // find bounding box
+ if ( !bbox (io, &x1, &y1, &x2, &y2)) {
+ kdError(399) << "kimgio EPS: no bounding box found!" << endl;
+ return;
+ }
+
+ KTempFile tmpFile;
+ tmpFile.setAutoDelete(true);
+
+ if( tmpFile.status() != 0 ) {
+ kdError(399) << "kimgio EPS: no temp file!" << endl;
+ return;
+ }
+ tmpFile.close(); // Close the file, we just want the filename
+
+ // x1, y1 -> translation
+ // x2, y2 -> new size
+
+ x2 -= x1;
+ y2 -= y1;
+ //kdDebug(399) << "origin point: " << x1 << "," << y1 << " size:" << x2 << "," << y2 << endl;
+ double xScale = 1.0;
+ double yScale = 1.0;
+ bool needsScaling = false;
+ int wantedWidth = x2;
+ int wantedHeight = y2;
+
+ if (image->parameters())
+ {
+ // Size forced by the caller
+ QStringList params = QStringList::split(':', image->parameters());
+ if (params.count() >= 2 && x2 != 0.0 && y2 != 0.0)
+ {
+ wantedWidth = params[0].toInt();
+ xScale = (double)wantedWidth / (double)x2;
+ wantedHeight = params[1].toInt();
+ yScale = (double)wantedHeight / (double)y2;
+ //kdDebug(399) << "wanted size:" << wantedWidth << "x" << wantedHeight << endl;
+ //kdDebug(399) << "scaling:" << xScale << "," << yScale << endl;
+ needsScaling = true;
+ }
+ }
+
+ // create GS command line
+
+ cmdBuf = "gs -sOutputFile=";
+ cmdBuf += tmpFile.name();
+ cmdBuf += " -q -g";
+ tmp.setNum( wantedWidth );
+ cmdBuf += tmp;
+ tmp.setNum( wantedHeight );
+ cmdBuf += "x";
+ cmdBuf += tmp;
+ cmdBuf += " -dSAFER -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ppm -c "
+ "0 0 moveto "
+ "1000 0 lineto "
+ "1000 1000 lineto "
+ "0 1000 lineto "
+ "1 1 254 255 div setrgbcolor fill "
+ "0 0 0 setrgbcolor - -c showpage quit";
+
+ // run ghostview
+
+ ghostfd = popen (QFile::encodeName(cmdBuf), "w");
+
+ if ( ghostfd == 0 ) {
+ kdError(399) << "kimgio EPS: no GhostScript?" << endl;
+ return;
+ }
+
+ fprintf (ghostfd, "\n%d %d translate\n", -qRound(x1*xScale), -qRound(y1*yScale));
+ if ( needsScaling )
+ fprintf (ghostfd, "%g %g scale\n", xScale, yScale);
+
+ // write image to gs
+
+ io->reset(); // Go back to start of file to give all the file to GhostScript
+ if (ps_offset>0L) // We have an offset
+ io->at(ps_offset);
+ QByteArray buffer ( io->readAll() );
+
+ // If we have no MS-DOS EPS file or if the size seems wrong, then choose the buffer size
+ if (ps_size<=0L || ps_size>buffer.size())
+ ps_size=buffer.size();
+
+ fwrite(buffer.data(), sizeof(char), ps_size, ghostfd);
+ buffer.resize(0);
+
+ pclose ( ghostfd );
+
+ // load image
+ QImage myimage;
+ if( myimage.load (tmpFile.name()) ) {
+ image->setImage (myimage);
+ image->setStatus (0);
+ kdDebug(399) << "kimgio EPS: success!" << endl;
+ }
+ else
+ kdError(399) << "kimgio EPS: no image!" << endl;
+
+ //kdDebug(399) << "Loading EPS took " << (float)(dt.elapsed()) / 1000 << " seconds" << endl;
+ return;
+}
+
+// Sven Wiegand <SWiegand@tfh-berlin.de> -- eps output filter (from KSnapshot)
+KDE_EXPORT void kimgio_eps_write( QImageIO *imageio )
+{
+ QPrinter psOut(QPrinter::PrinterResolution);
+ QPainter p;
+
+ // making some definitions (papersize, output to file, filename):
+ psOut.setCreator( "KDE " KDE_VERSION_STRING );
+ psOut.setOutputToFile( true );
+
+ // Extension must be .eps so that Qt generates EPS file
+ KTempFile tmpFile(QString::null, ".eps");
+ tmpFile.setAutoDelete(true);
+ if ( tmpFile.status() != 0)
+ return;
+ tmpFile.close(); // Close the file, we just want the filename
+
+ psOut.setOutputFileName(tmpFile.name());
+ psOut.setFullPage(true);
+
+ // painting the pixmap to the "printer" which is a file
+ p.begin( &psOut );
+ // Qt uses the clip rect for the bounding box
+ p.setClipRect( 0, 0, imageio->image().width(), imageio->image().height(), QPainter::CoordPainter);
+ p.drawImage( QPoint( 0, 0 ), imageio->image() );
+ p.end();
+
+ // Copy file to imageio struct
+ QFile inFile(tmpFile.name());
+ inFile.open( IO_ReadOnly );
+
+ QTextStream in( &inFile );
+ in.setEncoding( QTextStream::Latin1 );
+ QTextStream out( imageio->ioDevice() );
+ out.setEncoding( QTextStream::Latin1 );
+
+ QString szInLine = in.readLine();
+ out << szInLine << '\n';
+
+ while( !in.atEnd() ){
+ szInLine = in.readLine();
+ out << szInLine << '\n';
+ }
+
+ inFile.close();
+
+ imageio->setStatus(0);
+}
diff --git a/kimgio/eps.h b/kimgio/eps.h
new file mode 100644
index 000000000..ecdd82787
--- /dev/null
+++ b/kimgio/eps.h
@@ -0,0 +1,11 @@
+// This library is distributed under the conditions of the GNU LGPL.
+#ifndef _EPS_H
+#define _EPS_H
+
+extern "C" {
+void kimgio_eps_read (QImageIO *image);
+void kimgio_eps_write (QImageIO *image);
+}
+
+#endif
+
diff --git a/kimgio/eps.kimgio b/kimgio/eps.kimgio
new file mode 100644
index 000000000..0a89d5766
--- /dev/null
+++ b/kimgio/eps.kimgio
@@ -0,0 +1,86 @@
+[Image Format]
+Type=EPS
+Header=^(?:%c5%d0%d3%c6|%!PS-Adobe)
+Name=Encapsulated PostScript Image
+Name[af]=Geënkapsuleerde Postscript Beeld
+Name[ar]=Encapsulated PostScript صورة
+Name[az]=Encapsulated PostScript Rəsmi
+Name[be]=Відарыс EPS
+Name[bg]=Postscript (encapsulated) изображение
+Name[bn]=এনক্যাপসুলেটেড পোস্টস্ক্রিপ্ট চিত্র
+Name[br]=Skeudenn PostScript Enklozet
+Name[bs]=Enkapsulirana PostScript slika
+Name[ca]=Imatge encapsulada PostScript
+Name[cs]=Obrázek ve formátu Encapsulated PostScript
+Name[csb]=Òbrôzk EPS
+Name[cy]=Delwedd PostScript Mewn-amgaeëdig
+Name[da]=Encapsulated PostScript-billede
+Name[de]=EPS-Bild (Encapsulated PostScript)
+Name[el]=Εικόνα Encapsulated PostScript
+Name[eo]=EPS-bildo
+Name[es]=Imagen PostScript encapsulada
+Name[et]=Kapseldatud PostScript pildifail
+Name[eu]=Kapsulatutako PostScript irudia
+Name[fa]=تصویر پست‌اسکریپت کپسوله‌شده
+Name[fi]=Encapsulated PostScript (EPS) -kuva
+Name[fr]=Image PostScript encapsulée
+Name[fy]=Ynsletten PostSkript ôfbylding
+Name[ga]=Íomhá "Encapsulated PostScript"
+Name[gl]=Imaxes en Postscript Encapsulado
+Name[he]=תמונת PostScript מכומסת
+Name[hi]=एनकेप्सुलेटेड पोस्टस्क्रिप्ट छवि
+Name[hr]=Enkapsulirana PostScript slika
+Name[hu]=Encapsulated PostScript-kép
+Name[id]=Gambar Encapsulated Postscript
+Name[is]=Postscript-myndir (encapsulated)
+Name[it]=Immagine Encapsulated PostScript
+Name[ja]=Encapsulated PostScript 画像
+Name[ka]=EPS ნახატი
+Name[kk]=Encapsulated PostScript кескіні
+Name[km]=រូបភាព Encapsulated PostScript
+Name[ko]=암호화된 포스트스크립트 그림
+Name[lb]=Gekapselt PostScript-Bild
+Name[lt]=Įsiūtas Postscript paveiksliukas
+Name[lv]=Iekapsulēts PostScript attēls
+Name[mk]=Encapsulated PostScript слика
+Name[mn]=Хайрцагласан PostScript зураг
+Name[ms]=Imej PostScript Terenkapsul
+Name[nb]=Innkapslede PostScript-bilder
+Name[nds]=EPS-Bild
+Name[ne]=इनक्याप्सुलेट गरिएको पोष्टस्क्रिप्ट छवि
+Name[nl]=Encapsulated PostScript-afbeelding
+Name[nn]=Encapsulated PostScript-bilete
+Name[pa]=ਸਮੇਟਿਆ ਪੋਸਟ-ਸਕ੍ਰਿਪਟ ਚਿੱਤਰ
+Name[pl]=Obrazek EPS
+Name[pt]=Imagem em PostScript Encapsulado
+Name[pt_BR]=Imagens PostScript Encapsuladas
+Name[ro]=Imagine EPS
+Name[ru]=Рисунок EPS
+Name[rw]=Ishusho IyandikaNyuma Ryakusanyijwe
+Name[se]=Encapsulated PostScript-govva
+Name[sk]=Zapúzdrený PostScript obrázok
+Name[sl]=Slika v enkapsuliranem postscriptu
+Name[sq]=Imazh PostScript i Mbyllur
+Name[sr]=Учаурена PostScript слика
+Name[sr@Latn]=Učaurena PostScript slika
+Name[sv]=Inkapslad Postscript-bild
+Name[ta]=பாதுகாக்கப்பட்ட போஸ்ட்ஸ்கிரிப்ட் பிம்பம்
+Name[te]=పొదగబడిన పోస్ట్ స్క్రిప్ట్ ప్రతిబింబం
+Name[tg]=Capsula-шудаи PostScriptted-и тасвир
+Name[th]=ข้อมูลโพสต์สคริปต์เข้ารหัส
+Name[tr]=Encapsulated PostScript Görüntüsü
+Name[tt]=Quşılğan PostScript-Sürät
+Name[uk]=Вбудоване зображення PostScript
+Name[uz]=EPS-rasm
+Name[uz@cyrillic]=EPS-расм
+Name[vi]=Ảnh PostScript đã bao bọc (EPS)
+Name[wa]=Imådje PostScript ecapsulêye
+Name[zh_CN]=封装后的 Postscript 图像
+Name[zh_HK]=Encapsulated Postscript 圖檔
+Name[zh_TW]=Ensapsulated Postscript 影像
+Read=true
+Write=true
+Suffices=eps,EPS,epsi,EPSI,epsf,EPSF
+Mimetype=image/x-eps
+Library=kimg_eps.la
+
diff --git a/kimgio/exr.cpp b/kimgio/exr.cpp
new file mode 100644
index 000000000..e3efe5a6a
--- /dev/null
+++ b/kimgio/exr.cpp
@@ -0,0 +1,167 @@
+// -*- C++;indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*-
+
+/**
+* KImageIO Routines to read (and perhaps in the future, write) images
+* in the high dynamic range EXR format.
+* Copyright (c) 2003, Brad Hards <bradh@frogmouth.net>
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*
+* $Id$
+*/
+
+#include "config.h"
+
+#ifdef HAVE_EXR
+
+#include <ImfRgbaFile.h>
+#include <ImfStandardAttributes.h>
+#include <ImathBox.h>
+#include <ImfInputFile.h>
+#include <ImfBoxAttribute.h>
+#include <ImfChannelListAttribute.h>
+#include <ImfCompressionAttribute.h>
+#include <ImfFloatAttribute.h>
+#include <ImfIntAttribute.h>
+#include <ImfLineOrderAttribute.h>
+#include <ImfStringAttribute.h>
+#include <ImfVecAttribute.h>
+#include <ImfArray.h>
+#include <ImfConvert.h>
+
+#include <iostream>
+
+#include <stdlib.h>
+
+#include <kurl.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kdebug.h>
+
+#include <qimage.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qdict.h>
+#include <qvalidator.h>
+#include <qcolor.h>
+
+#include "exr.h"
+
+using namespace Imf;
+
+/* this does a conversion from the ILM Half (equal to Nvidia Half)
+ * format into the normal 32 bit pixel format. Process is from the
+ * ILM code.
+ */
+QRgb RgbaToQrgba(struct Rgba imagePixel)
+{
+ float r,g,b,a;
+
+ // 1) Compensate for fogging by subtracting defog
+ // from the raw pixel values.
+ // Response: We work with defog of 0.0, so this is a no-op
+
+ // 2) Multiply the defogged pixel values by
+ // 2^(exposure + 2.47393).
+ // Response: We work with exposure of 0.0.
+ // (2^2.47393) is 5.55555
+ r = imagePixel.r * 5.55555;
+ g = imagePixel.g * 5.55555;
+ b = imagePixel.b * 5.55555;
+ a = imagePixel.a * 5.55555;
+
+ // 3) Values, which are now 1.0, are called "middle gray".
+ // If defog and exposure are both set to 0.0, then
+ // middle gray corresponds to a raw pixel value of 0.18.
+ // In step 6, middle gray values will be mapped to an
+ // intensity 3.5 f-stops below the display's maximum
+ // intensity.
+ // Response: no apparent content.
+
+ // 4) Apply a knee function. The knee function has two
+ // parameters, kneeLow and kneeHigh. Pixel values
+ // below 2^kneeLow are not changed by the knee
+ // function. Pixel values above kneeLow are lowered
+ // according to a logarithmic curve, such that the
+ // value 2^kneeHigh is mapped to 2^3.5 (in step 6,
+ // this value will be mapped to the the display's
+ // maximum intensity).
+ // Response: kneeLow = 0.0 (2^0.0 => 1); kneeHigh = 5.0 (2^5 =>32)
+ if (r > 1.0)
+ r = 1.0 + Imath::Math<float>::log ((r-1.0) * 0.184874 + 1) / 0.184874;
+ if (g > 1.0)
+ g = 1.0 + Imath::Math<float>::log ((g-1.0) * 0.184874 + 1) / 0.184874;
+ if (b > 1.0)
+ b = 1.0 + Imath::Math<float>::log ((b-1.0) * 0.184874 + 1) / 0.184874;
+ if (a > 1.0)
+ a = 1.0 + Imath::Math<float>::log ((a-1.0) * 0.184874 + 1) / 0.184874;
+//
+// 5) Gamma-correct the pixel values, assuming that the
+// screen's gamma is 0.4545 (or 1/2.2).
+ r = Imath::Math<float>::pow (r, 0.4545);
+ g = Imath::Math<float>::pow (g, 0.4545);
+ b = Imath::Math<float>::pow (b, 0.4545);
+ a = Imath::Math<float>::pow (a, 0.4545);
+
+// 6) Scale the values such that pixels middle gray
+// pixels are mapped to 84.66 (or 3.5 f-stops below
+// the display's maximum intensity).
+//
+// 7) Clamp the values to [0, 255].
+ return qRgba( char (Imath::clamp ( r * 84.66f, 0.f, 255.f ) ),
+ char (Imath::clamp ( g * 84.66f, 0.f, 255.f ) ),
+ char (Imath::clamp ( b * 84.66f, 0.f, 255.f ) ),
+ char (Imath::clamp ( a * 84.66f, 0.f, 255.f ) ) );
+}
+
+KDE_EXPORT void kimgio_exr_read( QImageIO *io )
+{
+ try
+ {
+ int width, height;
+
+ // This won't work if io is not QFile !
+ RgbaInputFile file (QFile::encodeName(io->fileName()));
+ Imath::Box2i dw = file.dataWindow();
+
+ width = dw.max.x - dw.min.x + 1;
+ height = dw.max.y - dw.min.y + 1;
+
+ Array2D<Rgba> pixels;
+ pixels.resizeErase (height, width);
+
+ file.setFrameBuffer (&pixels[0][0] - dw.min.x - dw.min.y * width, 1, width);
+ file.readPixels (dw.min.y, dw.max.y);
+
+ QImage image(width, height, 32, 0, QImage::BigEndian);
+ if( image.isNull())
+ return;
+
+ // somehow copy pixels into image
+ for ( int y=0; y < height; y++ ) {
+ for ( int x=0; x < width; x++ ) {
+ // copy pixels(x,y) into image(x,y)
+ image.setPixel( x, y, RgbaToQrgba( pixels[y][x] ) );
+ }
+ }
+
+ io->setImage( image );
+ io->setStatus( 0 );
+ }
+ catch (const std::exception &exc)
+ {
+ kdDebug(399) << exc.what() << endl;
+ return;
+ }
+}
+
+
+KDE_EXPORT void kimgio_exr_write(QImageIO *)
+{
+ // TODO: stub
+}
+
+
+#endif
diff --git a/kimgio/exr.h b/kimgio/exr.h
new file mode 100644
index 000000000..9a039c6c6
--- /dev/null
+++ b/kimgio/exr.h
@@ -0,0 +1,21 @@
+/**
+* QImageIO Routines to read (and perhaps in the future, write) images
+* in the high definition EXR format.
+*
+* Copyright (c) 2003, Brad Hards <bradh@frogmouth.net>
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*
+*/
+
+#ifndef KIMG_EXR_H
+#define KIMG_EXR_H
+
+class QImageIO;
+
+extern "C" {
+ void kimgio_exr_read (QImageIO *io);
+ void kimgio_exr_write (QImageIO *io);
+}
+
+#endif
diff --git a/kimgio/exr.kimgio b/kimgio/exr.kimgio
new file mode 100644
index 000000000..618cde574
--- /dev/null
+++ b/kimgio/exr.kimgio
@@ -0,0 +1,66 @@
+[Image Format]
+Type=EXR
+Header=^\x76\x2f\x31\01
+Name=ILM EXR Image Kimgio
+Name[af]=ILM EXR Beeld Kimgio
+Name[be]=Відарыс ILM EXR Kimgio
+Name[bg]=ILM EXR Kimgio изображение
+Name[br]=Skeudenn Kimgio ILM EXR
+Name[ca]=Imatge ILM EXR pel Kimgio
+Name[csb]=Òbrôzk ILM EXR
+Name[da]=ILM EXR billede Kimgio
+Name[de]=ILM-EXR-Bild
+Name[el]=Εικόνα ILM EXR Kimgio
+Name[eo]=ILM-EXR-bildo
+Name[es]=Imagen Kimgio ILM EXR
+Name[eu]=ILM EXR Kimgio irudia
+Name[fi]=ILM EXR -kuva Kimgio
+Name[fr]=Image Kimgio ILM EXR
+Name[fy]=ILM EXR ôfbylding Kimgio
+Name[gl]=Imaxe ILM EXR Kimgio
+Name[he]=רכיב Kimgio של תמונת ILM EXR
+Name[hi]=आईएलएम ईएक्सआर छवि के-इमिजियो
+Name[hr]=ILM EXR Kimgio slika
+Name[hu]=ILM EXR-kép Kimgio
+Name[id]=Kimgio Gambar ILM EXR
+Name[is]=ILM EXR mynd Kimgio
+Name[it]=Immagine Kimgio ILM EXR
+Name[ja]=ILM EXR 画像 Kimgio
+Name[ka]=ILM EXR ნახატი
+Name[kk]=ILM EXR кескіні
+Name[lb]=ILM-EXR-Bild Kimgio
+Name[lt]=ILM EXR paveiksliukas Kimgio
+Name[mk]=ILM EXR слика Kimgio
+Name[ms]=Imej Kimgio ILM EXR
+Name[nb]=ILM EXR-bilde Kimgio
+Name[nds]="ILM EXR Image"-Kimgio
+Name[ne]=ILM EXR छवि किमगिओ
+Name[nl]=ILM EXR Kimgio-afbeelding
+Name[nn]=ILM EXR-bilete Kimgio
+Name[pa]=ILM EXR ਚਿੱਤਰ ਕੀਮਗੀਓ
+Name[pl]=Obrazek ILM EXR
+Name[pt]=Imagem ILM EXR Kimgio
+Name[pt_BR]=Imagem ILM EXR do Kimgio
+Name[ro]=Imagine ILM EXR
+Name[ru]=Рисунок ILM EXR
+Name[rw]=Kimgio Ishusho ILM EXR
+Name[se]=ILM EXR-govva Kimgio
+Name[sk]=Obrázok ILM EXR KImgio
+Name[sl]=Slika ILM EXR Kimgio
+Name[sr]=ILM EXR Kimgio слика
+Name[sr@Latn]=ILM EXR Kimgio slika
+Name[sv]=ILM EXR-bild Kimgio
+Name[ta]=ILM EXR பிம்ப கிம்ஜியோ
+Name[te]=ILM EXR ప్రతిబింబం కేఐఎమ్జిఐఓ
+Name[tg]=Тасвири ILM EXR Kimgio
+Name[tr]=ILM EXR Resim Kimgio
+Name[tt]=ILM EXR Süräte
+Name[uk]=Ввід/Вивід для зображень ILM EXR
+Name[vi]=Kimgio ảnh EXR ILM
+Name[zh_CN]=ILM EXR 图像 Kimgio
+Name[zh_TW]=ILM EXR 影像 Kimgio
+Read=true
+Write=false
+Suffices=.exr,.EXR
+Mimetype=image/x-exr
+Library=kimg_exr.la
diff --git a/kimgio/g3.kimgio b/kimgio/g3.kimgio
new file mode 100644
index 000000000..02219e9b3
--- /dev/null
+++ b/kimgio/g3.kimgio
@@ -0,0 +1,77 @@
+[Image Format]
+Type=G3
+Header=II
+Name=CCITT G3 Fax
+Name[af]=CCITT G3 Faks
+Name[ar]=فاكس CCITT G3
+Name[az]=CCITT G3 Faks
+Name[be]=Факс-файл CCITT G3
+Name[bg]=Факс CCITT G3
+Name[bn]=CCITT G3 ফ্যাক্স
+Name[br]=Faks CCITT G3
+Name[ca]=Fax G3 CCITT
+Name[csb]=Faks CCITT G3
+Name[cy]=Ffacs G3 CCITT
+Name[de]=CCITT-G3-Fax
+Name[el]=CCITT G3 φαξ
+Name[eo]=CCITT-G3-fakso
+Name[es]=Fax CCITT G3
+Name[et]=CCITT g3 fax
+Name[eu]=CCITT G3 faxa
+Name[fa]=دورنگار CCITT G3
+Name[fi]=CCITT G3 -faksi
+Name[fr]=Fax CCITT G3
+Name[fy]=CCITT G3 Faks
+Name[ga]=Facs CCITT G3
+Name[gl]=Fax G3 CCITT
+Name[he]=פקס CCITT G3
+Name[hi]=CCITT G3 फ़ैक्स
+Name[hr]=CCITT G3 faks
+Name[hu]=CCITT G3-faxfájl
+Name[id]=Faks CCITT G3
+Name[is]=CCITT G3 fax
+Name[it]=Fax CCITT G3
+Name[ja]=CCITT G3 ファクス
+Name[ka]=ფაქსის ფაილი CCITT G3 ფორმატში
+Name[kk]=CCITT G3 пішімдегі факс файлы
+Name[km]=ទូរសារ CCITT G3
+Name[ko]=CCITT G3 팩스
+Name[lb]=CCITT-G3-Fax
+Name[lt]=CCITT G3 faksas
+Name[lv]=CCITT G3 fakss
+Name[mk]=CCITT G3 факс
+Name[mn]=CCITT G3 Факс
+Name[ms]=Faks CCITT G3
+Name[nb]=CCITT G3-Faks
+Name[nds]=CCITT-G3-Fax
+Name[ne]=CCITT G3 फ्याक्स
+Name[nl]=CCITT G3-fax
+Name[nn]=CCITT G3-Faks
+Name[pa]=CCITT G3 ਫੈਕਸ
+Name[pl]=Faks CCITT G3
+Name[pt]=Fax CCITT G3
+Name[pt_BR]=CCITT G3 fax
+Name[ro]=Fax CCITT G3
+Name[ru]=Файл факса в формате CCITT G3
+Name[se]=CCITT G3-fáksa
+Name[sl]=Faks CCITT G3
+Name[sq]=CCITT GR Faks
+Name[sr]=CCITT G3 факс
+Name[sr@Latn]=CCITT G3 faks
+Name[sv]=CCITT grupp 3-fax
+Name[ta]=CCITT g3 தொலைநகலி
+Name[te]=CCITT G3 ఫేక్స్
+Name[tg]=Факси CCITT G3
+Name[tr]=CCITT G3 Faks
+Name[uk]=Факс CCITT G3
+Name[uz]=CCITT G3 faks-fayli
+Name[uz@cyrillic]=CCITT G3 факс-файли
+Name[wa]=Facs CCITT G3
+Name[zh_CN]=CCITT G3 传真
+Name[zh_HK]=CCITT G3 傳真
+Name[zh_TW]=CCITT G3 傳真
+Read=true
+Write=false
+Suffices=g3,G3,
+Mimetype=image/fax-g3
+Library=kimg_g3.la
diff --git a/kimgio/g3r.cpp b/kimgio/g3r.cpp
new file mode 100644
index 000000000..d67aa2da6
--- /dev/null
+++ b/kimgio/g3r.cpp
@@ -0,0 +1,53 @@
+// This library is distributed under the conditions of the GNU LGPL.
+
+#include "config.h"
+
+#ifdef HAVE_LIBTIFF
+
+#include <tiffio.h>
+
+#include <qimage.h>
+#include <qfile.h>
+
+#include "g3r.h"
+
+KDE_EXPORT void kimgio_g3_read( QImageIO *io )
+{
+ // This won't work if io is not a QFile !
+ TIFF *tiff = TIFFOpen(QFile::encodeName(io->fileName()), "r");
+ if (!tiff)
+ return;
+
+ uint32 width, height;
+ tsize_t scanlength;
+
+ if( TIFFGetField( tiff, TIFFTAG_IMAGEWIDTH, &width ) != 1
+ || TIFFGetField( tiff, TIFFTAG_IMAGELENGTH, &height ) != 1 )
+ return;
+ scanlength = TIFFScanlineSize(tiff);
+
+ QImage image(width, height, 1, 0, QImage::BigEndian);
+
+ if (image.isNull() || scanlength != image.bytesPerLine())
+ {
+ TIFFClose(tiff);
+ return;
+ }
+
+ for (uint32 y=0; y < height; y++)
+ TIFFReadScanline(tiff, image.scanLine(y), y);
+
+ TIFFClose(tiff);
+
+ io->setImage(image);
+ io->setStatus(0);
+}
+
+
+KDE_EXPORT void kimgio_g3_write(QImageIO *)
+{
+ // TODO: stub
+}
+
+
+#endif
diff --git a/kimgio/g3r.h b/kimgio/g3r.h
new file mode 100644
index 000000000..df90c5dee
--- /dev/null
+++ b/kimgio/g3r.h
@@ -0,0 +1,20 @@
+/**
+* QImageIO Routines to read/write g3 (fax) images.
+* (c) 2000, Matthias Hlzer-Klpfel
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*
+* $Id$
+*/
+
+#ifndef KIMG_G3R_H
+#define KIMG_G3R_H
+
+class QImageIO;
+
+extern "C" {
+ void kimgio_g3_read( QImageIO *io );
+ void kimgio_g3_write( QImageIO *io );
+}
+
+#endif
diff --git a/kimgio/gif.kimgio b/kimgio/gif.kimgio
new file mode 100644
index 000000000..4131d9ca3
--- /dev/null
+++ b/kimgio/gif.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=GIF
+Header=
+Name=GIF Image
+Name[af]=GIF Beeld
+Name[ar]=صورة من نوع GIF
+Name[az]=GIF Rəsmi
+Name[be]=Відарыс GIF
+Name[bg]=GIF изображение
+Name[bn]=জি-আই-এফ চিত্র
+Name[br]=Skeudenn GIF
+Name[bs]=GIF slika
+Name[ca]=Imatge GIF
+Name[cs]=Obrázek GIF
+Name[csb]=Òbrôzk GIF
+Name[cy]=Delwedd GIF
+Name[da]=GIF-billede
+Name[de]=GIF-Bild
+Name[el]=Εικόνα GIF
+Name[eo]=GIF bildo
+Name[es]=Imagen GIF
+Name[et]=GIF pildifail
+Name[eu]=GIF irudia
+Name[fa]=تصویر GIF
+Name[fi]=GIF-kuva
+Name[fr]=Image GIF
+Name[fy]=GIF ôfbylding
+Name[ga]=Íomhá GIF
+Name[gl]=Imaxe GIF
+Name[he]=תמונת GIF
+Name[hi]=GIF छवि
+Name[hr]=GIF slika
+Name[hu]=GIF-kép
+Name[id]=Gambar GIF
+Name[is]=GIF mynd
+Name[it]=Immagine GIF
+Name[ja]=GIF 画像
+Name[ka]=GIF ნახატი
+Name[kk]=GIF кескіні
+Name[km]=រូបភាព GIF
+Name[ko]=GIF 그림
+Name[lb]=GIF-Bild
+Name[lt]=GIF paveiksliukas
+Name[lv]=GIF attēls
+Name[mk]=GIF слика
+Name[mn]=GIF-Зураг
+Name[ms]=Imej GIF
+Name[nb]=GIF-bilde
+Name[nds]=GIF-Bild
+Name[ne]=GIF छवि
+Name[nl]=GIF-afbeelding
+Name[nn]=GIF-bilete
+Name[pa]=GIF ਚਿੱਤਰ
+Name[pl]=Obrazek GIF
+Name[pt]=Imagem GIF
+Name[pt_BR]=Imagem GIF
+Name[ro]=Imagine GIF
+Name[ru]=Рисунок GIF
+Name[rw]=GIF Ishusho
+Name[se]=GIF-govva
+Name[sk]=GIF obrázok
+Name[sl]=Slika GIF
+Name[sq]=Imazh GIF
+Name[sr]=GIF слика
+Name[sr@Latn]=GIF slika
+Name[sv]=Gif-bild
+Name[ta]=GIF பிம்பம்
+Name[te]=GIF ప్రతిబింబం
+Name[tg]=Тасвири GIF
+Name[th]=แฟ้มภาพ GIF
+Name[tr]=GIF Resim Dosyası
+Name[tt]=GIF Süräte
+Name[uk]=Зображення GIF
+Name[uz]=GIF-rasm
+Name[uz@cyrillic]=GIF-расм
+Name[vi]=Ảnh GIF
+Name[wa]=Imådje GIF
+Name[zh_CN]=GIF 图像
+Name[zh_HK]=GIF 圖檔
+Name[zh_TW]=GIF 影像
+Read=true
+Write=false
+Suffices=gif,GIF
+Mimetype=image/gif
+Library=
diff --git a/kimgio/gimp.h b/kimgio/gimp.h
new file mode 100644
index 000000000..94a6086d4
--- /dev/null
+++ b/kimgio/gimp.h
@@ -0,0 +1,411 @@
+#ifndef GIMP_H
+#define GIMP_H
+/* -*- c++ -*-
+ * gimp.h: Header for a Qt 3 plug-in for reading GIMP XCF image files
+ * Copyright (C) 2001 lignum Computing, Inc. <allen@lignumcomputing.com>
+ * Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
+ *
+ * This plug-in is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <kglobal.h>
+
+/*
+ * These are the constants and functions I extracted from The GIMP source
+ * code. If the reader fails to work, this is probably the place to start
+ * looking for discontinuities.
+ */
+
+// From GIMP "tile.h" v1.2
+
+const uint TILE_WIDTH = 64; //!< Width of a tile in the XCF file.
+const uint TILE_HEIGHT = 64; //!< Height of a tile in the XCF file.
+
+// From GIMP "paint_funcs.c" v1.2
+
+const int RANDOM_TABLE_SIZE = 4096; //!< Size of dissolve random number table.
+const int RANDOM_SEED = 314159265; //!< Seed for dissolve random number table.
+const double EPSILON = 0.0001; //!< Roundup in alpha blending.
+
+// From GIMP "paint_funcs.h" v1.2
+
+const uchar OPAQUE_OPACITY = 255; //!< Opaque value for 8-bit alpha component.
+
+// From GIMP "apptypes.h" v1.2
+
+//! Basic GIMP image type. QImage converter may produce a deeper image
+//! than is specified here. For example, a grayscale image with an
+//! alpha channel must (currently) use a 32-bit Qt image.
+
+typedef enum
+{
+ RGB,
+ GRAY,
+ INDEXED
+} GimpImageBaseType;
+
+//! Type of individual layers in an XCF file.
+
+typedef enum
+{
+ RGB_GIMAGE,
+ RGBA_GIMAGE,
+ GRAY_GIMAGE,
+ GRAYA_GIMAGE,
+ INDEXED_GIMAGE,
+ INDEXEDA_GIMAGE
+} GimpImageType;
+
+//! Effect to apply when layers are merged together.
+
+typedef enum
+{
+ NORMAL_MODE,
+ DISSOLVE_MODE,
+ BEHIND_MODE,
+ MULTIPLY_MODE,
+ SCREEN_MODE,
+ OVERLAY_MODE,
+ DIFFERENCE_MODE,
+ ADDITION_MODE,
+ SUBTRACT_MODE,
+ DARKEN_ONLY_MODE,
+ LIGHTEN_ONLY_MODE,
+ HUE_MODE,
+ SATURATION_MODE,
+ COLOR_MODE,
+ VALUE_MODE,
+ DIVIDE_MODE,
+ ERASE_MODE,
+ REPLACE_MODE,
+ ANTI_ERASE_MODE
+} LayerModeEffects;
+
+// From GIMP "xcf.c" v1.2
+
+//! Properties which can be stored in an XCF file.
+
+typedef enum
+{
+ PROP_END = 0,
+ PROP_COLORMAP = 1,
+ PROP_ACTIVE_LAYER = 2,
+ PROP_ACTIVE_CHANNEL = 3,
+ PROP_SELECTION = 4,
+ PROP_FLOATING_SELECTION = 5,
+ PROP_OPACITY = 6,
+ PROP_MODE = 7,
+ PROP_VISIBLE = 8,
+ PROP_LINKED = 9,
+ PROP_PRESERVE_TRANSPARENCY = 10,
+ PROP_APPLY_MASK = 11,
+ PROP_EDIT_MASK = 12,
+ PROP_SHOW_MASK = 13,
+ PROP_SHOW_MASKED = 14,
+ PROP_OFFSETS = 15,
+ PROP_COLOR = 16,
+ PROP_COMPRESSION = 17,
+ PROP_GUIDES = 18,
+ PROP_RESOLUTION = 19,
+ PROP_TATTOO = 20,
+ PROP_PARASITES = 21,
+ PROP_UNIT = 22,
+ PROP_PATHS = 23,
+ PROP_USER_UNIT = 24
+} PropType;
+
+// From GIMP "xcf.c" v1.2
+
+//! Compression type used in layer tiles.
+
+typedef enum
+{
+ COMPRESS_NONE = 0,
+ COMPRESS_RLE = 1,
+ COMPRESS_ZLIB = 2,
+ COMPRESS_FRACTAL = 3 /* Unused. */
+} CompressionType;
+
+// From GIMP "paint_funcs.c" v1.2
+
+/*!
+ * Multiply two color components. Really expects the arguments to be
+ * 8-bit quantities.
+ * \param a first minuend.
+ * \param b second minuend.
+ * \return product of arguments.
+ */
+inline int INT_MULT ( int a, int b )
+{
+ int c = a * b + 0x80;
+ return ( ( c >> 8 ) + c ) >> 8;
+}
+
+/*!
+ * Blend the two color components in the proportion alpha:
+ *
+ * result = alpha a + ( 1 - alpha b)
+ *
+ * \param a first component.
+ * \param b second component.
+ * \param alpha blend proportion.
+ * \return blended color components.
+ */
+
+inline int INT_BLEND ( int a, int b, int alpha )
+{
+ return INT_MULT( a - b, alpha ) + b;
+}
+
+// From GIMP "gimpcolorspace.c" v1.2
+
+/*!
+ * Convert a color in RGB space to HSV space (Hue, Saturation, Value).
+ * \param red the red component (modified in place).
+ * \param green the green component (modified in place).
+ * \param blue the blue component (modified in place).
+ */
+void RGBTOHSV ( uchar& red, uchar& green, uchar& blue )
+{
+ int r, g, b;
+ double h, s, v;
+ int min, max;
+
+ h = 0.;
+
+ r = red;
+ g = green;
+ b = blue;
+
+ if ( r > g ) {
+ max = KMAX( r, b );
+ min = KMIN( g, b );
+ }
+ else {
+ max = KMAX( g, b );
+ min = KMIN( r, b );
+ }
+
+ v = max;
+
+ if ( max != 0 )
+ s = ( ( max - min ) * 255 ) / (double)max;
+ else
+ s = 0;
+
+ if ( s == 0 )
+ h = 0;
+ else {
+ int delta = max - min;
+ if ( r == max )
+ h = ( g - b ) / (double)delta;
+ else if ( g == max )
+ h = 2 + ( b - r ) / (double)delta;
+ else if ( b == max )
+ h = 4 + ( r - g ) / (double)delta;
+ h *= 42.5;
+
+ if ( h < 0 )
+ h += 255;
+ if ( h > 255 )
+ h -= 255;
+ }
+
+ red = (uchar)h;
+ green = (uchar)s;
+ blue = (uchar)v;
+}
+
+/*!
+ * Convert a color in HSV space to RGB space.
+ * \param hue the hue component (modified in place).
+ * \param saturation the saturation component (modified in place).
+ * \param value the value component (modified in place).
+ */
+void HSVTORGB ( uchar& hue, uchar& saturation, uchar& value )
+{
+ if ( saturation == 0 ) {
+ hue = value;
+ saturation = value;
+ value = value;
+ }
+ else {
+ double h = hue * 6. / 255.;
+ double s = saturation / 255.;
+ double v = value / 255.;
+
+ double f = h - (int)h;
+ double p = v * ( 1. - s );
+ double q = v * ( 1. - ( s * f ) );
+ double t = v * ( 1. - ( s * ( 1. - f ) ) );
+
+ // Worth a note here that gcc 2.96 will generate different results
+ // depending on optimization mode on i386.
+
+ switch ((int)h) {
+ case 0:
+ hue = (uchar)( v * 255 );
+ saturation = (uchar)( t * 255 );
+ value = (uchar)( p * 255 );
+ break;
+ case 1:
+ hue = (uchar)( q * 255 );
+ saturation = (uchar)( v * 255 );
+ value = (uchar)( p * 255 );
+ break;
+ case 2:
+ hue = (uchar)( p * 255 );
+ saturation = (uchar)( v * 255 );
+ value = (uchar)( t * 255 );
+ break;
+ case 3:
+ hue = (uchar)( p * 255 );
+ saturation = (uchar)( q * 255 );
+ value = (uchar)( v * 255 );
+ break;
+ case 4:
+ hue = (uchar)( t * 255 );
+ saturation = (uchar)( p * 255 );
+ value = (uchar)( v * 255 );
+ break;
+ case 5:
+ hue = (uchar)( v * 255 );
+ saturation = (uchar)( p * 255 );
+ value = (uchar)( q * 255 );
+ }
+ }
+}
+
+/*!
+ * Convert a color in RGB space to HLS space (Hue, Lightness, Saturation).
+ * \param red the red component (modified in place).
+ * \param green the green component (modified in place).
+ * \param blue the blue component (modified in place).
+ */
+void RGBTOHLS ( uchar& red, uchar& green, uchar& blue )
+{
+ int r = red;
+ int g = green;
+ int b = blue;
+
+ int min, max;
+
+ if ( r > g ) {
+ max = KMAX( r, b );
+ min = KMIN( g, b );
+ }
+ else {
+ max = KMAX( g, b );
+ min = KMIN( r, b );
+ }
+
+ double h;
+ double l = ( max + min ) / 2.;
+ double s;
+
+ if ( max == min ) {
+ s = 0.;
+ h = 0.;
+ }
+ else {
+ int delta = max - min;
+
+ if ( l < 128 )
+ s = 255 * (double)delta / (double)( max + min );
+ else
+ s = 255 * (double)delta / (double)( 511 - max - min );
+
+ if ( r == max )
+ h = ( g - b ) / (double)delta;
+ else if ( g == max )
+ h = 2 + ( b - r ) / (double)delta;
+ else
+ h = 4 + ( r - g ) / (double)delta;
+
+ h *= 42.5;
+
+ if ( h < 0 )
+ h += 255;
+ else if ( h > 255 )
+ h -= 255;
+ }
+
+ red = (uchar)h;
+ green = (uchar)l;
+ blue = (uchar)s;
+}
+
+/*!
+ * Implement the HLS "double hex-cone".
+ * \param n1 lightness fraction (?)
+ * \param n2 saturation fraction (?)
+ * \param hue hue "angle".
+ * \return HLS value.
+ */
+int HLSVALUE ( double n1, double n2, double hue )
+{
+ double value;
+
+ if ( hue > 255 )
+ hue -= 255;
+ else if ( hue < 0 )
+ hue += 255;
+
+ if ( hue < 42.5 )
+ value = n1 + ( n2 - n1 ) * ( hue / 42.5 );
+ else if ( hue < 127.5 )
+ value = n2;
+ else if ( hue < 170 )
+ value = n1 + ( n2 - n1 ) * ( ( 170 - hue ) / 42.5 );
+ else
+ value = n1;
+
+ return (int)( value * 255 );
+}
+
+/*!
+ * Convert a color in HLS space to RGB space.
+ * \param hue the hue component (modified in place).
+ * \param lightness the lightness component (modified in place).
+ * \param saturation the saturation component (modified in place).
+ */
+void HLSTORGB ( uchar& hue, uchar& lightness, uchar& saturation )
+{
+ double h = hue;
+ double l = lightness;
+ double s = saturation;
+
+ if ( s == 0 ) {
+ hue = (uchar)l;
+ lightness = (uchar)l;
+ saturation = (uchar)l;
+ }
+ else {
+ double m1, m2;
+
+ if ( l < 128 )
+ m2 = ( l * ( 255 + s ) ) / 65025.;
+ else
+ m2 = ( l + s - ( l * s ) / 255. ) / 255.;
+
+ m1 = ( l / 127.5 ) - m2;
+
+ hue = HLSVALUE( m1, m2, h + 85 );
+ lightness = HLSVALUE( m1, m2, h );
+ saturation = HLSVALUE( m1, m2, h - 85 );
+ }
+}
+#endif
diff --git a/kimgio/hdr.cpp b/kimgio/hdr.cpp
new file mode 100644
index 000000000..edc49f91b
--- /dev/null
+++ b/kimgio/hdr.cpp
@@ -0,0 +1,265 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Christoph Hormann <chris_hormann@gmx.de>
+ Copyright (C) 2005 Ignacio Castao <castanyo@yahoo.es>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#include "hdr.h"
+
+#include <qimage.h>
+#include <qdatastream.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+
+typedef Q_UINT8 uchar;
+
+namespace { // Private.
+
+#define MAXLINE 1024
+#define MINELEN 8 // minimum scanline length for encoding
+#define MAXELEN 0x7fff // maximum scanline length for encoding
+
+ static inline uchar ClipToByte(float value)
+ {
+ if (value > 255.0f) return 255;
+ //else if (value < 0.0f) return 0; // we know value is positive.
+ return uchar(value);
+ }
+
+ // read an old style line from the hdr image file
+ // if 'first' is true the first byte is already read
+ static bool Read_Old_Line (uchar * image, int width, QDataStream & s)
+ {
+ int rshift = 0;
+ int i;
+
+ while (width > 0)
+ {
+ s >> image[0];
+ s >> image[1];
+ s >> image[2];
+ s >> image[3];
+
+ if (s.atEnd()) return false;
+
+ if ((image[0] == 1) && (image[1] == 1) && (image[2] == 1))
+ {
+ for (i = image[3] << rshift; i > 0; i--)
+ {
+ //memcpy(image, image-4, 4);
+ (uint &)image[0] = (uint &)image[0-4];
+ image += 4;
+ width--;
+ }
+ rshift += 8;
+ }
+ else
+ {
+ image += 4;
+ width--;
+ rshift = 0;
+ }
+ }
+ return true;
+ }
+
+
+ static void RGBE_To_QRgbLine(uchar * image, QRgb * scanline, int width)
+ {
+ for (int j = 0; j < width; j++)
+ {
+ // v = ldexp(1.0, int(image[3]) - 128);
+ float v;
+ int e = int(image[3]) - 128;
+ if( e > 0 )
+ {
+ v = float(1 << e);
+ }
+ else
+ {
+ v = 1.0f / float(1 << -e);
+ }
+
+ scanline[j] = qRgb( ClipToByte(float(image[0]) * v),
+ ClipToByte(float(image[1]) * v),
+ ClipToByte(float(image[2]) * v) );
+
+ image += 4;
+ }
+ }
+
+ // Load the HDR image.
+ static bool LoadHDR( QDataStream & s, const int width, const int height, QImage & img )
+ {
+ uchar val, code;
+
+ // Create dst image.
+ if( !img.create( width, height, 32 ) )
+ {
+ return false;
+ }
+
+ QMemArray<uchar> image( width * 4 );
+
+ for (int cline = 0; cline < height; cline++)
+ {
+ QRgb * scanline = (QRgb *) img.scanLine( cline );
+
+ // determine scanline type
+ if ((width < MINELEN) || (MAXELEN < width))
+ {
+ Read_Old_Line(image.data(), width, s);
+ RGBE_To_QRgbLine(image.data(), scanline, width);
+ continue;
+ }
+
+ s >> val;
+
+ if (s.atEnd())
+ {
+ return true;
+ }
+
+ if (val != 2)
+ {
+ s.device()->at( s.device()->at() - 1 );
+ Read_Old_Line(image.data(), width, s);
+ RGBE_To_QRgbLine(image.data(), scanline, width);
+ continue;
+ }
+
+ s >> image[1];
+ s >> image[2];
+ s >> image[3];
+
+ if (s.atEnd())
+ {
+ return true;
+ }
+
+ if ((image[1] != 2) || (image[2] & 128))
+ {
+ image[0] = 2;
+ Read_Old_Line(image.data()+4, width-1, s);
+ RGBE_To_QRgbLine(image.data(), scanline, width);
+ continue;
+ }
+
+ if ((image[2] << 8 | image[3]) != width)
+ {
+ return false;
+ }
+
+ // read each component
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < width; )
+ {
+ s >> code;
+ if (s.atEnd())
+ {
+ return false;
+ }
+ if (code > 128)
+ {
+ // run
+ code &= 127;
+ s >> val;
+ while( code != 0 )
+ {
+ image[i + j * 4] = val;
+ j++;
+ code--;
+ }
+ }
+ else
+ {
+ // non-run
+ while( code != 0 )
+ {
+ s >> image[i + j * 4];
+ j++;
+ code--;
+ }
+ }
+ }
+ }
+
+ RGBE_To_QRgbLine(image.data(), scanline, width);
+ }
+
+ return true;
+ }
+
+} // namespace
+
+
+KDE_EXPORT void kimgio_hdr_read( QImageIO * io )
+{
+ int len;
+ char line[MAXLINE];
+ //bool validHeader = false;
+ bool validFormat = false;
+
+ // Parse header
+ do {
+ len = io->ioDevice()->readLine(line, MAXLINE);
+
+ /*if (strcmp(line, "#?RADIANCE\n") == 0 || strcmp(line, "#?RGBE\n") == 0)
+ {
+ validHeader = true;
+ }*/
+ if (strcmp(line, "FORMAT=32-bit_rle_rgbe\n") == 0)
+ {
+ validFormat = true;
+ }
+
+ } while((len > 0) && (line[0] != '\n'));
+
+ if( /*!validHeader ||*/ !validFormat )
+ {
+ kdDebug(399) << "Unknown HDR format." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ io->ioDevice()->readLine(line, MAXLINE);
+
+ char s1[3], s2[3];
+ int width, height;
+ if (sscanf(line, "%2[+-XY] %d %2[+-XY] %d\n", s1, &height, s2, &width) != 4)
+ //if( sscanf(line, "-Y %d +X %d", &height, &width) < 2 )
+ {
+ kdDebug(399) << "Invalid HDR file." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ QDataStream s( io->ioDevice() );
+
+ QImage img;
+ if( !LoadHDR(s, width, height, img) )
+ {
+ kdDebug(399) << "Error loading HDR file." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ io->setImage( img );
+ io->setStatus( 0 );
+}
+
+
+KDE_EXPORT void kimgio_hdr_write( QImageIO * )
+{
+ // intentionally not implemented (since writing low dynamic range data to a HDR file is nonsense.)
+}
+
diff --git a/kimgio/hdr.h b/kimgio/hdr.h
new file mode 100644
index 000000000..34c833f73
--- /dev/null
+++ b/kimgio/hdr.h
@@ -0,0 +1,21 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Christoph Hormann <chris_hormann@gmx.de>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#ifndef KIMG_HDR_H
+#define KIMG_HDR_H
+
+class QImageIO;
+
+extern "C" {
+void kimgio_hdr_read( QImageIO * );
+void kimgio_hdr_write( QImageIO * );
+}
+
+#endif
+
diff --git a/kimgio/hdr.kimgio b/kimgio/hdr.kimgio
new file mode 100644
index 000000000..3f1e1f9ac
--- /dev/null
+++ b/kimgio/hdr.kimgio
@@ -0,0 +1,62 @@
+[Image Format]
+Type=HDR
+Header=^\x23\x3f(RGBE|RADIANCE)
+Name=High Dynamic Range Image
+Name[af]=Hoe Dinamiese Reeks Beeld
+Name[be]=Відарыс HDR
+Name[bg]=Изображение с висока динамичност (High Dynamic Range)
+Name[bs]=High Dynamic Range slika
+Name[ca]=Imatge d'abast altament dinàmic
+Name[cs]=Obrázek ve formátu HDRI
+Name[csb]=Òbrôzk HDR (High Dynamic Range)
+Name[da]=Billede med højt dynamisk område
+Name[el]=Εικόνα υψηλής δυναμικής περιοχής
+Name[es]=Imagen de gran rango dinámico
+Name[et]=High Dynamic Range pildifail
+Name[eu]=Dinamika Altuko Barruti irudia
+Name[fa]=گسترۀ پویایی بالای تصویر
+Name[fi]=Korkeatasoinen muuttuvien arvoalueiden kuva
+Name[fr]=Image à définition dynamique haute
+Name[fy]=High Dynamic Range-ôfbylding
+Name[gl]=Imaxe de Alto Rango Dinámico (HDR)
+Name[hu]=HDR-kép
+Name[is]=High Dynamic Range mynd
+Name[it]=Immagine High Dynamic Range
+Name[ja]=High Dynamic Range 画像
+Name[ka]=HDR ნახატი
+Name[kk]=High Dynamic Range кескіні
+Name[km]=រូបភាព​ជួរ​ថាមវណ្ដ​ខ្ពស់
+Name[lb]=High-Dynamic-Range-Bild
+Name[lt]=HDR paveikslėlis
+Name[mk]=Слика со висок динамички опсег
+Name[ms]=Imej Jarak DinamikTinggi
+Name[nb]=Bilde med høyt dynamisk område
+Name[nds]="High Dynamic Range"-Bild
+Name[ne]=उच्च गतिशील दायरा छवि
+Name[nl]=High Dynamic Range-afbeelding
+Name[nn]=Bilete med stort dynamikkområde
+Name[pl]=Obrazek HDR (High Dynamic Range)
+Name[pt]=Imagem de Intervalo Dinâmico Elevado
+Name[pt_BR]=Imagem de Intervalo Dinâmico Alto
+Name[ro]=Imagine cu domeniu dinamic larg
+Name[ru]=Рисунок HDR
+Name[se]=Govva mas lea stuorra dynámalaš gaskkadat
+Name[sk]=High Dynamic Range obrázok
+Name[sl]=Slika z visokim dinamičnim razponom
+Name[sr]=Слика високог динамичког опсега
+Name[sr@Latn]=Slika visokog dinamičkog opsega
+Name[sv]=Bild med stort dynamiskt område
+Name[te]=ఎక్కువ పరిణామశీల వ్యాప్తి కలిగిన ప్రతిబింబం
+Name[tg]=Тасвири динамикии баландқатор
+Name[th]=แฟ้มภาพแบบช่วงไดนามิคสูง
+Name[tr]=HDR Resmi
+Name[tt]=Bik Tere Aralı Sürät
+Name[uk]=Зображення HDR (High Dynamic Range)
+Name[vi]=Ảnh phạm vị động cao
+Name[zh_CN]=高动态范围图像
+Read=true
+Write=false
+Suffices=.hdr,.HDR,.pic,.PIC
+Mimetype=image/x-hdr
+Library=kimg_hdr.la
+
diff --git a/kimgio/ico.cpp b/kimgio/ico.cpp
new file mode 100644
index 000000000..bc1ba388d
--- /dev/null
+++ b/kimgio/ico.cpp
@@ -0,0 +1,377 @@
+
+/*
+ * $Id$
+ * kimgio import filter for MS Windows .ico files
+ *
+ * Distributed under the terms of the LGPL
+ * Copyright (c) 2000 Malte Starostik <malte@kde.org>
+ *
+ */
+
+#include <cstring>
+#include <cstdlib>
+#include <algorithm>
+#include <vector>
+
+#include <qimage.h>
+#include <qbitmap.h>
+#include <qapplication.h>
+#include <qmemarray.h>
+#include <qpaintdevicemetrics.h>
+
+#include <kdelibs_export.h>
+
+#include "ico.h"
+
+namespace
+{
+ // Global header
+ struct IcoHeader
+ {
+ enum Type { Icon = 1, Cursor };
+ Q_UINT16 reserved;
+ Q_UINT16 type;
+ Q_UINT16 count;
+ };
+
+ inline QDataStream& operator >>( QDataStream& s, IcoHeader& h )
+ {
+ return s >> h.reserved >> h.type >> h.count;
+ }
+
+ // Based on qt_read_dib et al. from qimage.cpp
+ // (c) 1992-2002 Trolltech AS.
+ struct BMP_INFOHDR
+ {
+ static const Q_UINT32 Size = 40;
+ Q_UINT32 biSize; // size of this struct
+ Q_UINT32 biWidth; // pixmap width
+ Q_UINT32 biHeight; // pixmap height
+ Q_UINT16 biPlanes; // should be 1
+ Q_UINT16 biBitCount; // number of bits per pixel
+ enum Compression { RGB = 0 };
+ Q_UINT32 biCompression; // compression method
+ Q_UINT32 biSizeImage; // size of image
+ Q_UINT32 biXPelsPerMeter; // horizontal resolution
+ Q_UINT32 biYPelsPerMeter; // vertical resolution
+ Q_UINT32 biClrUsed; // number of colors used
+ Q_UINT32 biClrImportant; // number of important colors
+ };
+ const Q_UINT32 BMP_INFOHDR::Size;
+
+ QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi )
+ {
+ s >> bi.biSize;
+ if ( bi.biSize == BMP_INFOHDR::Size )
+ {
+ s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
+ s >> bi.biCompression >> bi.biSizeImage;
+ s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
+ s >> bi.biClrUsed >> bi.biClrImportant;
+ }
+ return s;
+ }
+
+#if 0
+ 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;
+ }
+#endif
+
+ // Header for every icon in the file
+ struct IconRec
+ {
+ unsigned char width;
+ unsigned char height;
+ Q_UINT16 colors;
+ Q_UINT16 hotspotX;
+ Q_UINT16 hotspotY;
+ Q_UINT32 size;
+ Q_UINT32 offset;
+ };
+
+ inline QDataStream& operator >>( QDataStream& s, IconRec& r )
+ {
+ return s >> r.width >> r.height >> r.colors
+ >> r.hotspotX >> r.hotspotY >> r.size >> r.offset;
+ }
+
+ struct LessDifference
+ {
+ LessDifference( unsigned s, unsigned c )
+ : size( s ), colors( c ) {}
+
+ bool operator ()( const IconRec& lhs, const IconRec& rhs ) const
+ {
+ // closest size match precedes everything else
+ if ( std::abs( int( lhs.width - size ) ) <
+ std::abs( int( rhs.width - size ) ) ) return true;
+ else if ( std::abs( int( lhs.width - size ) ) >
+ std::abs( int( rhs.width - size ) ) ) return false;
+ else if ( colors == 0 )
+ {
+ // high/true color requested
+ if ( lhs.colors == 0 ) return true;
+ else if ( rhs.colors == 0 ) return false;
+ else return lhs.colors > rhs.colors;
+ }
+ else
+ {
+ // indexed icon requested
+ if ( lhs.colors == 0 && rhs.colors == 0 ) return false;
+ else if ( lhs.colors == 0 ) return false;
+ else return std::abs( int( lhs.colors - colors ) ) <
+ std::abs( int( rhs.colors - colors ) );
+ }
+ }
+ unsigned size;
+ unsigned colors;
+ };
+
+ bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon )
+ {
+ BMP_INFOHDR header;
+ stream >> header;
+ if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size ||
+ header.biSize > rec.size ||
+ header.biCompression != BMP_INFOHDR::RGB ||
+ ( header.biBitCount != 1 && header.biBitCount != 4 &&
+ header.biBitCount != 8 && header.biBitCount != 24 &&
+ header.biBitCount != 32 ) ) return false;
+
+ unsigned paletteSize, paletteEntries;
+
+ if (header.biBitCount > 8)
+ {
+ paletteEntries = 0;
+ paletteSize = 0;
+ }
+ else
+ {
+ paletteSize = (1 << header.biBitCount);
+ paletteEntries = paletteSize;
+ if (header.biClrUsed && header.biClrUsed < paletteSize)
+ paletteEntries = header.biClrUsed;
+ }
+
+ // Always create a 32-bit image to get the mask right
+ // Note: this is safe as rec.width, rec.height are bytes
+ icon.create( rec.width, rec.height, 32 );
+ if ( icon.isNull() ) return false;
+ icon.setAlphaBuffer( true );
+
+ QMemArray< QRgb > colorTable( paletteSize );
+
+ colorTable.fill( QRgb( 0 ) );
+ for ( unsigned i = 0; i < paletteEntries; ++i )
+ {
+ unsigned char rgb[ 4 ];
+ stream.readRawBytes( reinterpret_cast< char* >( &rgb ),
+ sizeof( rgb ) );
+ colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] );
+ }
+
+ unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4;
+
+ unsigned char* buf = new unsigned char[ bpl ];
+ unsigned char** lines = icon.jumpTable();
+ for ( unsigned y = rec.height; !stream.atEnd() && y--; )
+ {
+ stream.readRawBytes( reinterpret_cast< char* >( buf ), bpl );
+ unsigned char* pixel = buf;
+ QRgb* p = reinterpret_cast< QRgb* >( lines[ y ] );
+ switch ( header.biBitCount )
+ {
+ case 1:
+ for ( unsigned x = 0; x < rec.width; ++x )
+ *p++ = colorTable[
+ ( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ];
+ break;
+ case 4:
+ for ( unsigned x = 0; x < rec.width; ++x )
+ if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ];
+ else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ];
+ break;
+ case 8:
+ for ( unsigned x = 0; x < rec.width; ++x )
+ *p++ = colorTable[ pixel[ x ] ];
+ break;
+ case 24:
+ for ( unsigned x = 0; x < rec.width; ++x )
+ *p++ = qRgb( pixel[ 3 * x + 2 ],
+ pixel[ 3 * x + 1 ],
+ pixel[ 3 * x ] );
+ break;
+ case 32:
+ for ( unsigned x = 0; x < rec.width; ++x )
+ *p++ = qRgba( pixel[ 4 * x + 2 ],
+ pixel[ 4 * x + 1 ],
+ pixel[ 4 * x ],
+ pixel[ 4 * x + 3] );
+ break;
+ }
+ }
+ delete[] buf;
+
+ if ( header.biBitCount < 32 )
+ {
+ // Traditional 1-bit mask
+ bpl = ( rec.width + 31 ) / 32 * 4;
+ buf = new unsigned char[ bpl ];
+ for ( unsigned y = rec.height; y--; )
+ {
+ stream.readRawBytes( reinterpret_cast< char* >( buf ), bpl );
+ QRgb* p = reinterpret_cast< QRgb* >( lines[ y ] );
+ for ( unsigned x = 0; x < rec.width; ++x, ++p )
+ if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) )
+ *p &= RGB_MASK;
+ }
+ delete[] buf;
+ }
+ return true;
+ }
+}
+
+extern "C" KDE_EXPORT void kimgio_ico_read( QImageIO* io )
+{
+ QIODevice::Offset offset = io->ioDevice()->at();
+
+ QDataStream stream( io->ioDevice() );
+ stream.setByteOrder( QDataStream::LittleEndian );
+ IcoHeader header;
+ stream >> header;
+ if ( stream.atEnd() || !header.count ||
+ ( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) )
+ return;
+
+ QPaintDeviceMetrics metrics( QApplication::desktop() );
+ unsigned requestedSize = 32;
+ unsigned requestedColors = metrics.depth() > 8 ? 0 : metrics.depth();
+ int requestedIndex = -1;
+ if ( io->parameters() )
+ {
+ QStringList params = QStringList::split( ';', io->parameters() );
+ QMap< QString, QString > options;
+ for ( QStringList::ConstIterator it = params.begin();
+ it != params.end(); ++it )
+ {
+ QStringList tmp = QStringList::split( '=', *it );
+ if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ];
+ }
+ if ( options[ "index" ].toUInt() )
+ requestedIndex = options[ "index" ].toUInt();
+ if ( options[ "size" ].toUInt() )
+ requestedSize = options[ "size" ].toUInt();
+ if ( options[ "colors" ].toUInt() )
+ requestedColors = options[ "colors" ].toUInt();
+ }
+
+ typedef std::vector< IconRec > IconList;
+ IconList icons;
+ for ( unsigned i = 0; i < header.count; ++i )
+ {
+ if ( stream.atEnd() ) return;
+ IconRec rec;
+ stream >> rec;
+ icons.push_back( rec );
+ }
+ IconList::const_iterator selected;
+ if (requestedIndex >= 0) {
+ selected = std::min( icons.begin() + requestedIndex, icons.end() );
+ } else {
+ selected = std::min_element( icons.begin(), icons.end(),
+ LessDifference( requestedSize, requestedColors ) );
+ }
+ if ( stream.atEnd() || selected == icons.end() ||
+ offset + selected->offset > io->ioDevice()->size() )
+ return;
+
+ io->ioDevice()->at( offset + selected->offset );
+ QImage icon;
+ if ( loadFromDIB( stream, *selected, icon ) )
+ {
+ icon.setText( "X-Index", 0, QString::number( selected - icons.begin() ) );
+ if ( header.type == IcoHeader::Cursor )
+ {
+ icon.setText( "X-HotspotX", 0, QString::number( selected->hotspotX ) );
+ icon.setText( "X-HotspotY", 0, QString::number( selected->hotspotY ) );
+ }
+ io->setImage(icon);
+ io->setStatus(0);
+ }
+}
+
+#if 0
+void kimgio_ico_write(QImageIO *io)
+{
+ if (io->image().isNull())
+ return;
+
+ QByteArray dibData;
+ QDataStream dib(dibData, IO_ReadWrite);
+ dib.setByteOrder(QDataStream::LittleEndian);
+
+ QImage pixels = io->image();
+ QImage mask;
+ if (io->image().hasAlphaBuffer())
+ mask = io->image().createAlphaMask();
+ else
+ mask = io->image().createHeuristicMask();
+ mask.invertPixels();
+ for ( int y = 0; y < pixels.height(); ++y )
+ for ( int x = 0; x < pixels.width(); ++x )
+ if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 );
+
+ if (!qt_write_dib(dib, pixels))
+ return;
+
+ uint hdrPos = dib.device()->at();
+ if (!qt_write_dib(dib, mask))
+ return;
+ memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8);
+ dibData.resize(dibData.size() - BMP_WIN - 8);
+
+ QDataStream ico(io->ioDevice());
+ ico.setByteOrder(QDataStream::LittleEndian);
+ IcoHeader hdr;
+ hdr.reserved = 0;
+ hdr.type = Icon;
+ hdr.count = 1;
+ ico << hdr.reserved << hdr.type << hdr.count;
+ IconRec rec;
+ rec.width = io->image().width();
+ rec.height = io->image().height();
+ if (io->image().numColors() <= 16)
+ rec.colors = 16;
+ else if (io->image().depth() <= 8)
+ rec.colors = 256;
+ else
+ rec.colors = 0;
+ rec.hotspotX = 0;
+ rec.hotspotY = 0;
+ rec.dibSize = dibData.size();
+ ico << rec.width << rec.height << rec.colors
+ << rec.hotspotX << rec.hotspotY << rec.dibSize;
+ rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset);
+ ico << rec.dibOffset;
+
+ BMP_INFOHDR dibHeader;
+ dib.device()->at(0);
+ dib >> dibHeader;
+ dibHeader.biHeight = io->image().height() << 1;
+ dib.device()->at(0);
+ dib << dibHeader;
+
+ ico.writeRawBytes(dibData.data(), dibData.size());
+ io->setStatus(0);
+}
+#endif
diff --git a/kimgio/ico.h b/kimgio/ico.h
new file mode 100644
index 000000000..4492d4d9d
--- /dev/null
+++ b/kimgio/ico.h
@@ -0,0 +1,46 @@
+
+/*
+ * $Id$
+ * ico.h - kimgio import filter for MS Windows .ico files
+ *
+ * Distributed under the terms of the LGPL
+ * Copyright (c) 2000 Malte Starostik <malte@kde.org>
+ *
+ */
+
+// You can use QImageIO::setParameters() to request a specific
+// Icon out of an .ico file:
+//
+// Options consist of a name=value pair and are separated by a semicolon.
+// Available options are:
+// size=<size> select the icon that most closely matches <size> (pixels)
+// default: 32
+// colors=<num> select the icon that has <num> colors (or comes closest)
+// default: 1 << display depth or 0 (RGB) if display depth > 8
+// index=<index> select the indexth icon from the file. If this option
+// is present, the size and colors options will be ignored.
+// default: none
+// If both size and colors are given, size takes precedence.
+//
+// The old format is still supported:
+// the parameters consist of a single string in the form
+// "<size>[:<colors>]" which correspond to the options above
+//
+// If an icon was returned (i.e. the file is valid and the index option
+// if present was not out of range), the icon's index within the .ico
+// file is returned in the text tag "X-Index" of the image.
+// If the icon is in fact a cursor, its hotspot coordinates are returned
+// in the text tags "X-HotspotX" and "X-HotspotY".
+
+#ifndef _ICO_H_
+#define _ICO_H_
+
+class QImageIO;
+
+extern "C"
+{
+ void kimgio_ico_read(QImageIO *);
+// void kimgio_ico_write(QImageIO *);
+}
+
+#endif
diff --git a/kimgio/ico.kimgio b/kimgio/ico.kimgio
new file mode 100644
index 000000000..c2ea6bcd4
--- /dev/null
+++ b/kimgio/ico.kimgio
@@ -0,0 +1,84 @@
+[Image Format]
+Type=ICO
+Header=^\001\001[\001|\002]\001
+Name=Windows Icon
+Name[af]=Windows Ikoon
+Name[ar]=أيقونة ويندوز
+Name[az]=Windows Timsalı
+Name[bg]=Икони на Windows
+Name[bn]=উইন্ডোস আইকন
+Name[br]=Arlun Windows
+Name[bs]=Windows ikona
+Name[ca]=Icona de Windows
+Name[cs]=Ikona MS Windows
+Name[csb]=Ikòna Windows
+Name[cy]=Eicon Windows
+Name[da]=Windows-ikon
+Name[de]=Windows-Symbol
+Name[el]=Εικονίδιο Windows
+Name[eo]=Vindoza piktogramo
+Name[es]=Icono de Windows
+Name[et]=Windowsi ikoon
+Name[eu]=Windows ikonoa
+Name[fa]=شمایل پنجره‌ها
+Name[fi]=Windows-kuvake
+Name[fr]=Icône Windows
+Name[fy]=Windows Ikoan
+Name[ga]=Deilbhín MS Windows
+Name[gl]=Ícone de Windows
+Name[he]=סמלים של Windows
+Name[hi]=विंडोज़ आइकन
+Name[hr]=Windows ikona
+Name[hu]=Windows-ikon
+Name[id]=Icon Windows
+Name[is]=Windows táknmynd
+Name[it]=Icona Windows
+Name[ja]=Windows アイコン
+Name[ka]=Windows-ის პიქტოგრამა
+Name[kk]=Windows таңбашасы
+Name[km]=រូបតំណាង​បង្អួច
+Name[ko]=윈도우즈 아이콘
+Name[lb]=Windows-Symbol
+Name[lt]=Windows ženkliukas
+Name[lv]=Windows Ikona
+Name[mk]=MSWindows икона
+Name[mn]=Windows-Тэмдэг
+Name[ms]=Ikon Tetingkap
+Name[nb]=Windows-ikoner
+Name[nds]=Windows-Lüttbild
+Name[ne]=विन्डोज प्रतिमा
+Name[nl]=Windows-pictogram
+Name[nn]=Windows-ikon
+Name[pa]=ਝਰੋਖਾ ਆਈਕਾਨ
+Name[pl]=Ikona Windows
+Name[pt]=Ícone do Windows
+Name[pt_BR]=Ícone do Windows
+Name[ro]=Iconiţă MS Windows
+Name[ru]=Значок Windows
+Name[rw]=Agashushondanga Windows
+Name[se]=MSWindows-govažat
+Name[sk]=Windows ikony
+Name[sl]=Ikona iz Windows
+Name[sq]=Ikon i Windows-it
+Name[sr]=Windows-ова икона
+Name[sr@Latn]=Windows-ova ikona
+Name[sv]=Windows-ikon
+Name[ta]=சாளரங்கள் குறும்படங்கள்
+Name[te]=విండొస్ ప్రతిమ
+Name[tg]=Ишораҳои Windows
+Name[th]=ไอคอนของวินโดว์ส
+Name[tr]=Windows Simgesi
+Name[tt]=Windows İkonı
+Name[uk]=Піктограма Windows
+Name[uz]=Windows nishonchasi
+Name[uz@cyrillic]=Windows нишончаси
+Name[vi]=Biểu tượng Windows
+Name[wa]=Imådjete MS-Windows
+Name[zh_CN]=Windows 图标
+Name[zh_HK]=Windows 圖示
+Name[zh_TW]=Windows 圖示
+Read=true
+Write=false
+Suffices=ico,ICO
+Mimetype=image/x-ico
+Library=kimg_ico.la
diff --git a/kimgio/jp2.cpp b/kimgio/jp2.cpp
new file mode 100644
index 000000000..3e44275d8
--- /dev/null
+++ b/kimgio/jp2.cpp
@@ -0,0 +1,316 @@
+// This library is distributed under the conditions of the GNU LGPL.
+#include "config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_JASPER
+
+#include "jp2.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <ktempfile.h>
+#include <qcolor.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qimage.h>
+
+// dirty, but avoids a warning because jasper.h includes jas_config.h.
+#undef PACKAGE
+#undef VERSION
+#include <jasper/jasper.h>
+
+// code taken in parts from JasPer's jiv.c
+
+#define DEFAULT_RATE 0.10
+#define MAXCMPTS 256
+
+
+typedef struct {
+ jas_image_t* image;
+
+ int cmptlut[MAXCMPTS];
+
+ jas_image_t* altimage;
+} gs_t;
+
+
+jas_image_t*
+read_image( const QImageIO* io )
+{
+ jas_stream_t* in = 0;
+ // for QIODevice's other than QFile, a temp. file is used.
+ KTempFile* tempf = 0;
+
+ QFile* qf = 0;
+ if( ( qf = dynamic_cast<QFile*>( io->ioDevice() ) ) ) {
+ // great, it's a QFile. Let's just take the filename.
+ in = jas_stream_fopen( QFile::encodeName( qf->name() ), "rb" );
+ } else {
+ // not a QFile. Copy the whole data to a temp. file.
+ tempf = new KTempFile();
+ if( tempf->status() != 0 ) {
+ delete tempf;
+ return 0;
+ } // if
+ tempf->setAutoDelete( true );
+ QFile* out = tempf->file();
+ // 4096 (=4k) is a common page size.
+ QByteArray b( 4096 );
+ Q_LONG size;
+ // 0 or -1 is EOF / error
+ while( ( size = io->ioDevice()->readBlock( b.data(), 4096 ) ) > 0 ) {
+ // in case of a write error, still give the decoder a try
+ if( ( out->writeBlock( b.data(), size ) ) == -1 ) break;
+ } // while
+ // flush everything out to disk
+ out->flush();
+
+ in = jas_stream_fopen( QFile::encodeName( tempf->name() ), "rb" );
+ } // else
+ if( !in ) {
+ delete tempf;
+ return 0;
+ } // if
+
+ jas_image_t* image = jas_image_decode( in, -1, 0 );
+ jas_stream_close( in );
+ delete tempf;
+
+ // image may be 0, but that's Ok
+ return image;
+} // read_image
+
+static bool
+convert_colorspace( gs_t& gs )
+{
+ jas_cmprof_t *outprof = jas_cmprof_createfromclrspc( JAS_CLRSPC_SRGB );
+ if( !outprof ) return false;
+
+ gs.altimage = jas_image_chclrspc( gs.image, outprof,
+ JAS_CMXFORM_INTENT_PER );
+ if( !gs.altimage ) return false;
+
+ return true;
+} // convert_colorspace
+
+static bool
+render_view( gs_t& gs, QImage& qti )
+{
+ if((gs.cmptlut[0] = jas_image_getcmptbytype(gs.altimage,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 ||
+ (gs.cmptlut[1] = jas_image_getcmptbytype(gs.altimage,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 ||
+ (gs.cmptlut[2] = jas_image_getcmptbytype(gs.altimage,
+ JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) {
+ return false;
+ } // if
+
+ const int* cmptlut = gs.cmptlut;
+ int v[3];
+
+ // check that all components have the same size.
+ const int width = jas_image_cmptwidth( gs.altimage, cmptlut[0] );
+ const int height = jas_image_cmptheight( gs.altimage, cmptlut[0] );
+ for( int i = 1; i < 3; ++i ) {
+ if (jas_image_cmptwidth( gs.altimage, cmptlut[i] ) != width ||
+ jas_image_cmptheight( gs.altimage, cmptlut[i] ) != height)
+ return false;
+ } // for
+
+ if( !qti.create( jas_image_width( gs.altimage ),
+ jas_image_height( gs.altimage ), 32 ) )
+ return false;
+
+ uint32_t* data = (uint32_t*)qti.bits();
+
+ for( int y = 0; y < height; ++y ) {
+ for( int x = 0; x < width; ++x ) {
+ for( int k = 0; k < 3; ++k ) {
+ v[k] = jas_image_readcmptsample( gs.altimage, cmptlut[k], x, y );
+ // if the precision of the component is too small, increase
+ // it to use the complete value range.
+ v[k] <<= 8 - jas_image_cmptprec( gs.altimage, cmptlut[k] );
+
+ if( v[k] < 0 ) v[k] = 0;
+ else if( v[k] > 255 ) v[k] = 255;
+ } // for k
+
+ *data++ = qRgb( v[0], v[1], v[2] );
+ } // for x
+ } // for y
+ return true;
+} // render_view
+
+
+KDE_EXPORT void
+kimgio_jp2_read( QImageIO* io )
+{
+ if( jas_init() ) return;
+
+ gs_t gs;
+ if( !(gs.image = read_image( io )) ) return;
+
+ if( !convert_colorspace( gs ) ) return;
+
+ QImage image;
+ render_view( gs, image );
+
+ if( gs.image ) jas_image_destroy( gs.image );
+ if( gs.altimage ) jas_image_destroy( gs.altimage );
+
+ io->setImage( image );
+ io->setStatus( 0 );
+} // kimgio_jp2_read
+
+
+static jas_image_t*
+create_image( const QImage& qi )
+{
+ // prepare the component parameters
+ jas_image_cmptparm_t* cmptparms = new jas_image_cmptparm_t[ 3 ];
+
+ for ( int i = 0; i < 3; ++i ) {
+ // x and y offset
+ cmptparms[i].tlx = 0;
+ cmptparms[i].tly = 0;
+
+ // the resulting image will be hstep*width x vstep*height !
+ cmptparms[i].hstep = 1;
+ cmptparms[i].vstep = 1;
+ cmptparms[i].width = qi.width();
+ cmptparms[i].height = qi.height();
+
+ // we write everything as 24bit truecolor ATM
+ cmptparms[i].prec = 8;
+ cmptparms[i].sgnd = false;
+ }
+
+ jas_image_t* ji = jas_image_create( 3 /* number components */, cmptparms, JAS_CLRSPC_UNKNOWN );
+ delete[] cmptparms;
+
+ // returning 0 is ok
+ return ji;
+} // create_image
+
+
+static bool
+write_components( jas_image_t* ji, const QImage& qi )
+{
+ const unsigned height = qi.height();
+ const unsigned width = qi.width();
+
+ jas_matrix_t* m = jas_matrix_create( height, width );
+ if( !m ) return false;
+
+ jas_image_setclrspc( ji, JAS_CLRSPC_SRGB );
+
+ jas_image_setcmpttype( ji, 0, JAS_IMAGE_CT_RGB_R );
+ for( uint y = 0; y < height; ++y )
+ for( uint x = 0; x < width; ++x )
+ jas_matrix_set( m, y, x, qRed( qi.pixel( x, y ) ) );
+ jas_image_writecmpt( ji, 0, 0, 0, width, height, m );
+
+ jas_image_setcmpttype( ji, 1, JAS_IMAGE_CT_RGB_G );
+ for( uint y = 0; y < height; ++y )
+ for( uint x = 0; x < width; ++x )
+ jas_matrix_set( m, y, x, qGreen( qi.pixel( x, y ) ) );
+ jas_image_writecmpt( ji, 1, 0, 0, width, height, m );
+
+ jas_image_setcmpttype( ji, 2, JAS_IMAGE_CT_RGB_B );
+ for( uint y = 0; y < height; ++y )
+ for( uint x = 0; x < width; ++x )
+ jas_matrix_set( m, y, x, qBlue( qi.pixel( x, y ) ) );
+ jas_image_writecmpt( ji, 2, 0, 0, width, height, m );
+ jas_matrix_destroy( m );
+
+ return true;
+} // write_components
+
+KDE_EXPORT void
+kimgio_jp2_write( QImageIO* io )
+{
+ if( jas_init() ) return;
+
+ // open the stream. we write directly to the file if possible, to a
+ // temporary file otherwise.
+ jas_stream_t* stream = 0;
+
+ QFile* qf = 0;
+ KTempFile* ktempf = 0;
+ if( ( qf = dynamic_cast<QFile*>( io->ioDevice() ) ) ) {
+ // jas_stream_fdopen works here, but not when reading...
+ stream = jas_stream_fdopen( dup( qf->handle() ), "w" );
+ } else {
+ ktempf = new KTempFile;
+ ktempf->setAutoDelete( true );
+ stream = jas_stream_fdopen( dup( ktempf->handle()), "w" );
+ } // else
+
+
+ // by here, a jas_stream_t is open
+ if( !stream ) return;
+
+ jas_image_t* ji = create_image( io->image() );
+ if( !ji ) {
+ delete ktempf;
+ jas_stream_close( stream );
+ return;
+ } // if
+
+ if( !write_components( ji, io->image() ) ) {
+ delete ktempf;
+ jas_stream_close( stream );
+ jas_image_destroy( ji );
+ return;
+ } // if
+
+ // optstr:
+ // - rate=#B => the resulting file size is about # bytes
+ // - rate=0.0 .. 1.0 => the resulting file size is about the factor times
+ // the uncompressed size
+ QString rate;
+ QTextStream ts( &rate, IO_WriteOnly );
+ ts << "rate="
+ << ( (io->quality() < 0) ? DEFAULT_RATE : io->quality() / 100.0F );
+ int i = jp2_encode( ji, stream, rate.utf8().data() );
+
+ jas_image_destroy( ji );
+ jas_stream_close( stream );
+
+ if( i != 0 ) { delete ktempf; return; }
+
+ if( ktempf ) {
+ // We've written to a tempfile. Copy the data to the final destination.
+ QFile* in = ktempf->file();
+
+ QByteArray b( 4096 );
+ Q_LONG size;
+
+ // seek to the beginning of the file.
+ if( !in->at( 0 ) ) { delete ktempf; return; }
+
+ // 0 or -1 is EOF / error
+ while( ( size = in->readBlock( b.data(), 4096 ) ) > 0 ) {
+ if( ( io->ioDevice()->writeBlock( b.data(), size ) ) == -1 ) {
+ delete ktempf;
+ return;
+ } // if
+ } // while
+ io->ioDevice()->flush();
+ delete ktempf;
+
+ // see if we've left the while loop due to an error.
+ if( size == -1 ) return;
+ } // if
+
+
+ // everything went fine
+ io->setStatus( IO_Ok );
+} // kimgio_jp2_write
+
+#endif // HAVE_JASPER
+
diff --git a/kimgio/jp2.h b/kimgio/jp2.h
new file mode 100644
index 000000000..d137ac0a3
--- /dev/null
+++ b/kimgio/jp2.h
@@ -0,0 +1,13 @@
+// This library is distributed under the conditions of the GNU LGPL.
+#ifndef KIMG_JP2_H
+#define KIMG_JP2_H
+
+class QImageIO;
+
+extern "C" {
+ void kimgio_jp2_read( QImageIO* io );
+ void kimgio_jp2_write( QImageIO* io );
+} // extern "C"
+
+#endif
+
diff --git a/kimgio/jp2.kimgio b/kimgio/jp2.kimgio
new file mode 100644
index 000000000..75980cbff
--- /dev/null
+++ b/kimgio/jp2.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=jp2
+Header=^\x01\x01\x01\x0C\x6A\x50
+Name=JPEG 2000 Image
+Name[af]=JPEG 2000 Beeld
+Name[ar]=صورة من نوع JPEG
+Name[az]=JPEG 2000 Rəsmi
+Name[be]=Малюнак JPEG 2000
+Name[bg]=JPEG 2000 изображение
+Name[bn]=জেপেগ ২০০০ চিত্র
+Name[br]=Skeudenn JPEG 2000
+Name[bs]=JPEG 2000 slika
+Name[ca]=Imatge JPEG 2000
+Name[cs]=Obrázek ve formátu JPEG 2000
+Name[csb]=Òbrôzk JPEG 2000
+Name[cy]=Delwedd JPEG 2000
+Name[da]=JPEG 2000-billede
+Name[de]=JPEG-2000-Bild
+Name[el]=Εικόνα JPEG 2000
+Name[eo]=JPEG-2000 bildo
+Name[es]=Imagen JPEG 2000
+Name[et]=JPEG 2000 pildifail
+Name[eu]=JPEG 2000 irudia
+Name[fa]=تصویر JPEG 2000
+Name[fi]=JPEG 2000 -kuva
+Name[fr]=Image JPEG 2000
+Name[fy]=JPEG 2000 ôfbylding
+Name[ga]=Íomhá JPEG 2000
+Name[gl]=Imaxe JPEG 2000
+Name[he]=תמונת JPEG 2000
+Name[hi]=JPEG 2000 छवि
+Name[hr]=JPEG 2000 slika
+Name[hu]=JPEG 2000-kép
+Name[id]=Gambar JPEG 2000
+Name[is]=JPEG 2000 mynd
+Name[it]=Immagine JPEG 2000
+Name[ja]=JPEG 2000 画像
+Name[ka]=JPEG 2000 ნახატი
+Name[kk]=JPEG 2000 кескіні
+Name[km]=រូបភាព JPEG 2000
+Name[ko]=JPEG 2000 그림
+Name[lb]=JPEG-2000-Bild
+Name[lt]=JPEG 2000 paveiksliukas
+Name[lv]=JPEG 2000 attēls
+Name[mk]=JPEG 2000 слика
+Name[mn]=JPEG 2000 Зураг
+Name[ms]=Imej JPEG 2000
+Name[nb]=JPEG 2000 bilde
+Name[nds]=JPEG2000-Bild
+Name[ne]=JPEG 2000 छवि
+Name[nl]=JPEG 2000-afbeelding
+Name[nn]=JPEG 2000-bilete
+Name[pa]=JPEG 2000 ਚਿੱਤਰ
+Name[pl]=Obrazek JPEG 2000
+Name[pt]=Imagem JPEG 2000
+Name[pt_BR]=Imagem JPEG 2000
+Name[ro]=Imagine JPEG 2000
+Name[ru]=Рисунок JPEG 2000
+Name[rw]=JPEG 2000 Ishusho
+Name[se]=JPEG 2000-govva
+Name[sk]=JPEG 2000 obrázok
+Name[sl]=Slika JPEG 2000
+Name[sq]=Imazh JPEG 2000
+Name[sr]=JPEG 2000 слика
+Name[sr@Latn]=JPEG 2000 slika
+Name[sv]=Jpeg 2000-bild
+Name[ta]=JPEG2000 பிம்பம்
+Name[te]=JPEG 2000 ప్రతిబింబం
+Name[tg]=Тасвири JPEG 2000
+Name[th]=แฟ้มภาพ JPEG 2000
+Name[tr]=JPEG 2000 Resim Dosyası
+Name[tt]=JPEG 2000 Süräte
+Name[uk]=Зображення JPEG 2000
+Name[uz]=JPEG 2000-rasm
+Name[uz@cyrillic]=JPEG 2000-расм
+Name[vi]=Ảnh JPEG 2000
+Name[wa]=Imådje JPEG 2000
+Name[zh_CN]=JPEG 2000 图像
+Name[zh_HK]=JPEG 2000 圖檔
+Name[zh_TW]=JPEG 2000 影像
+Read=true
+Write=true
+Suffices=jp2,JP2
+Mimetype=image/jp2
+Library=kimg_jp2.la
diff --git a/kimgio/jpeg.kimgio b/kimgio/jpeg.kimgio
new file mode 100644
index 000000000..238d25e75
--- /dev/null
+++ b/kimgio/jpeg.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=JPEG
+Header=
+Name=JPEG Image
+Name[af]=JPEG Beeld
+Name[ar]=صورة من نوع JPEG
+Name[az]=JPEG Rəsmi
+Name[be]=Малюнак JPEG
+Name[bg]=JPEG изображение
+Name[bn]=জেপেগ চিত্র
+Name[br]=Skeudenn JPEG
+Name[bs]=JPEG slika
+Name[ca]=Imatge JPEG
+Name[cs]=Obrázek ve formátu JPEG
+Name[csb]=Òbrôzk JPEG
+Name[cy]=Delwedd JPEG
+Name[da]=JPEG-billede
+Name[de]=JPEG-Bild
+Name[el]=Εικόνα JPEG
+Name[eo]=JPEG-bildo
+Name[es]=Imagen JPEG
+Name[et]=JPEG pildifail
+Name[eu]=JPEG irudia
+Name[fa]=تصویر JPEG
+Name[fi]=JPEG-kuva
+Name[fr]=Image JPEG
+Name[fy]=JPEG ôfbylding
+Name[ga]=Íomhá JPEG
+Name[gl]=Imaxe JPEG
+Name[he]=תמונת JPEG
+Name[hi]=JPEG छवि
+Name[hr]=JPEG slika
+Name[hu]=JPEG-kép
+Name[id]=Gambar JPEG
+Name[is]=JPEG mynd
+Name[it]=Immagine JPEG
+Name[ja]=JPEG 画像
+Name[ka]=JPEG ნახატი
+Name[kk]=JPEG кескіні
+Name[km]=រូបភាព JPEG
+Name[ko]=JPEG 그림
+Name[lb]=JPEG-Bild
+Name[lt]=JPEG paveiksliukas
+Name[lv]=JPEG attēls
+Name[mk]=JPEG слика
+Name[mn]=JPEG-Зураг
+Name[ms]=Imej JPEG
+Name[nb]=JPEG-bilde
+Name[nds]=JPEG-Bild
+Name[ne]=JPEG छवि
+Name[nl]=JPEG-afbeelding
+Name[nn]=JPEG-bilete
+Name[pa]=JPEG ਚਿੱਤਰ
+Name[pl]=Obrazek JPEG
+Name[pt]=Imagem JPEG
+Name[pt_BR]=Imagem JPEG
+Name[ro]=Imagine JPEG
+Name[ru]=Рисунок JPEG
+Name[rw]=JPEG Ishusho
+Name[se]=JPEG-govva
+Name[sk]=JPEG obrázok
+Name[sl]=Slika JPEG
+Name[sq]=Imazh JPEG
+Name[sr]=JPEG слика
+Name[sr@Latn]=JPEG slika
+Name[sv]=Jpeg-bild
+Name[ta]=JPEG பிம்பம்
+Name[te]=JPEG ప్రతిబింబం
+Name[tg]=Тасвири JPEG
+Name[th]=แฟ้มภาพ JPEG
+Name[tr]=JPEG Resim Dosyası
+Name[tt]=JPEG Süräte
+Name[uk]=Зображення JPEG
+Name[uz]=JPEG-rasm
+Name[uz@cyrillic]=JPEG-расм
+Name[vi]=Ảnh JPEG
+Name[wa]=Imådje JPEG
+Name[zh_CN]=JPEG 图像
+Name[zh_HK]=JPEG 圖檔
+Name[zh_TW]=JPEG 影像
+Read=true
+Write=true
+Suffices=jpg,JPG,jpeg,JPEG
+Mimetype=image/jpeg
+Library=
diff --git a/kimgio/mng.kimgio b/kimgio/mng.kimgio
new file mode 100644
index 000000000..5fdb64bc0
--- /dev/null
+++ b/kimgio/mng.kimgio
@@ -0,0 +1,79 @@
+[Image Format]
+Type=MNG
+Header=
+Name=MNG Image
+Name[af]=Png Beeld
+Name[be]=Малюнак MNG
+Name[bg]=MNG изображение
+Name[bn]=এম-এন-জি চিত্র
+Name[br]=Skeudenn MNG
+Name[bs]=MNG slika
+Name[ca]=Imatge MNG
+Name[cs]=Obrázek ve formátu MNG
+Name[csb]=Òbrôzk MNG
+Name[cy]=Delwedd MNG
+Name[da]=MNG-billede
+Name[de]=MNG-Bild
+Name[el]=Εικόνα MNG
+Name[eo]=MNG bildo
+Name[es]=Imagen MNG
+Name[et]=MNG pildifail
+Name[eu]=MNG irudia
+Name[fa]=تصویر MNG
+Name[fi]=MNG-kuva
+Name[fr]=Image MNG
+Name[fy]=MNG-ôfbylding
+Name[ga]=Íomhá MNG
+Name[gl]=Imaxe MNG
+Name[he]=תמונת MNG
+Name[hi]=एमएनजी छवि
+Name[hr]=MNG slika
+Name[hu]=MNG-kép
+Name[id]=Gambar MNG
+Name[is]=MNG mynd
+Name[it]=Immagine MNG
+Name[ja]=MNG 画像
+Name[ka]=MNG ნახატი
+Name[kk]=MNG кескіні
+Name[km]=រូបភាព MNG
+Name[lb]=MNG-Bild
+Name[lt]=MNG paveiksliukas
+Name[lv]=MNG attēls
+Name[mk]=MNG слика
+Name[ms]=Imej MNG
+Name[nb]=MNG-bilde
+Name[nds]=MNG-Bild
+Name[ne]=MNG छवि
+Name[nl]=MNG-afbeelding
+Name[nn]=MNG-bilete
+Name[pa]=MNG ਚਿੱਤਰ
+Name[pl]=Obrazek MNG
+Name[pt]=Imagem MNG
+Name[pt_BR]=Imagem MNG
+Name[ro]=Imagine MNG
+Name[ru]=Рисунок MNG
+Name[rw]=MNG Ishusho
+Name[se]=MNG-govva
+Name[sk]=MNG obrázok
+Name[sl]=Slika MNG
+Name[sr]=MNG слика
+Name[sr@Latn]=MNG slika
+Name[sv]=MNG-bild
+Name[ta]=MNG பிம்பம்
+Name[te]=MNG ప్రతిబింబం
+Name[tg]=Тасвири MNG
+Name[th]=แฟ้มภาพ MNG
+Name[tr]=MNG Resmi
+Name[tt]=MNG Sürät
+Name[uk]=Зображення MNG
+Name[uz]=MNG-rasm
+Name[uz@cyrillic]=MNG-расм
+Name[vi]=Ảnh MNG
+Name[zh_CN]=MNG 图像
+Name[zh_HK]=MNG 圖檔
+Name[zh_TW]=MNG 影像
+Read=true
+Write=false
+Suffices=mng,MNG
+Mimetype=video/x-mng
+Library=
diff --git a/kimgio/pbm.kimgio b/kimgio/pbm.kimgio
new file mode 100644
index 000000000..6f37f7a57
--- /dev/null
+++ b/kimgio/pbm.kimgio
@@ -0,0 +1,84 @@
+[Image Format]
+Type=PBM
+Header=
+Name=Portable Bitmap Image
+Name[af]='Portable Bitmap' Beeld
+Name[ar]=صورة قابلة للنقل PNG
+Name[az]=Portable Bitmap Rəsmi
+Name[be]=Малюнак Portable Bitmap
+Name[bg]=Portable Bitmap Image изображение
+Name[bn]=পোর্টেবল বিটম্যাপ চিত্র
+Name[br]=Skeudenn Portable Bitmap
+Name[bs]=Portable Bitmap slika
+Name[ca]=Imatge portable Bitmap
+Name[csb]=Przenosnô bitmapa
+Name[cy]=Delwedd Ddidfap Gludadwy
+Name[da]=Portable Bitmap-billede
+Name[de]=Portierbares Bitmap-Bild
+Name[el]=Εικόνα Portable Bitmap
+Name[eo]=Portebla punktbildo
+Name[es]=Imagen Bitmap portable
+Name[et]=Portable bittraster-pildifail
+Name[eu]=Bitmap irudi eramangarria
+Name[fa]=تصویر نگاشت بیت حمل‌پذیر
+Name[fi]=Portable Bitmap -kuva
+Name[fr]=Image Bitmap Portable
+Name[fy]=Portable Bitmap ôfbylding
+Name[ga]=Íomhá PBM (Portable Bitmap)
+Name[gl]=Imaxe de Mapa de Bits Portábel
+Name[he]=תמונת מפת סיביות ניידת
+Name[hi]=पोर्टेबल बिटमेप छवि
+Name[hr]=Portabilna bitmap slika
+Name[hu]=PBM-kép
+Name[id]=Gambar Portable Bitmap
+Name[is]=Skráarsnið GIMP forritsins (PBI)
+Name[it]=Immagine Portable Bitmap
+Name[ja]=ポータブルビットマップ画像
+Name[ka]=Portable Bitmap ნახატი
+Name[kk]=Portable Bitmap кескіні
+Name[km]=រូបភាព Portable Bitmap
+Name[ko]=포터블 비트맵 그림
+Name[lb]=Portéierbart Bitmap-Bild
+Name[lt]=Perkeliamas taškinės grafikos (bitmap) bylų formatas
+Name[lv]=Portable Bitmap attēls
+Name[mk]=Портабилна Bitmap слика
+Name[mn]=Шилжүүлж болох Bitmap-Зураг
+Name[ms]=Imej Bitmap Mudah Alih
+Name[nb]=Portable Bitmap-bilde
+Name[nds]=Porteerbor Bitmap-Bild
+Name[ne]=चलायमान बिटम्याप छवि
+Name[nl]=Portable Bitmap-afbeelding
+Name[nn]=Portable Bitmap-bilete
+Name[pa]=ਪੋਰਟਬਲ ਬਿੱਟਮੈਪ ਚਿੱਤਰ
+Name[pl]=Przenośna mapa bitowa
+Name[pt]=Formato Portável de Imagem
+Name[pt_BR]=Imagem Bitmap Portável
+Name[ro]=Imagine bitmap în format portabil
+Name[ru]=Рисунок Portable Bitmap
+Name[rw]=Portable Bitmap Ishusho
+Name[se]=Portable Bitmap-govva
+Name[sk]=Portable Bitmap obrázok
+Name[sl]=Prenosljiva bitna slika
+Name[sq]=Imazh Portable Bitmap
+Name[sr]=Портабл битмапирана слика
+Name[sr@Latn]=Portabl bitmapirana slika
+Name[sv]=Flyttbart bitmappsformat
+Name[ta]=எடுத்துச்செல்லக்கூடிய பிட்வரைபடம் பிம்பம்
+Name[te]=పోర్టబుల్ బిట్ మేప్ ప్రతిబింబం
+Name[tg]=Тасвири нақшбайти қобили ҳамла
+Name[th]=แฟ้มภาพ Portable Bitmap
+Name[tr]=Taşınabilir Bit Eşlem Resim Dosyası
+Name[tt]=Portable Bitmap Süräte
+Name[uk]=Двокольорове зображення у портативному форматі
+Name[uz]=PBMP-rasm
+Name[uz@cyrillic]=PBMP-расм
+Name[vi]=Ảnh sơ đồ bit di động
+Name[wa]=Imådje PBM
+Name[zh_CN]=便携位图图像
+Name[zh_HK]=PNG 圖檔
+Name[zh_TW]=可攜式點陣影像 (PNG)
+Read=true
+Write=true
+Suffices=pbm,PBM,pbmraw,PBMRAW
+Mimetype=image/x-portable-bitmap
+Library=
diff --git a/kimgio/pcx.cpp b/kimgio/pcx.cpp
new file mode 100644
index 000000000..867829eb6
--- /dev/null
+++ b/kimgio/pcx.cpp
@@ -0,0 +1,534 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2005 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License (LGPL) as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#include "pcx.h"
+
+#include <qimage.h>
+
+#include <kdebug.h>
+
+static QDataStream &operator>>( QDataStream &s, RGB &rgb )
+{
+ s >> rgb.r >> rgb.g >> rgb.b;
+
+ return s;
+}
+
+static QDataStream &operator>>( QDataStream &s, Palette &pal )
+{
+ for ( int i=0; i<16; ++i )
+ s >> pal.rgb[ i ];
+
+ return s;
+}
+
+static QDataStream &operator>>( QDataStream &s, PCXHEADER &ph )
+{
+ s >> ph.Manufacturer;
+ s >> ph.Version;
+ s >> ph.Encoding;
+ s >> ph.Bpp;
+ s >> ph.XMin >> ph.YMin >> ph.XMax >> ph.YMax;
+ s >> ph.HDpi >> ph.YDpi;
+ s >> ph.ColorMap;
+ s >> ph.Reserved;
+ s >> ph.NPlanes;
+ s >> ph.BytesPerLine;
+ s >> ph.PaletteInfo;
+ s >> ph.HScreenSize;
+ s >> ph.VScreenSize;
+
+ // Skip the rest of the header
+ Q_UINT8 byte;
+ while ( s.device()->at() < 128 )
+ s >> byte;
+
+ return s;
+}
+
+static QDataStream &operator<<( QDataStream &s, const RGB &rgb )
+{
+ s << rgb.r << rgb.g << rgb.b;
+
+ return s;
+}
+
+static QDataStream &operator<<( QDataStream &s, const Palette &pal )
+{
+ for ( int i=0; i<16; ++i )
+ s << pal.rgb[ i ];
+
+ return s;
+}
+
+static QDataStream &operator<<( QDataStream &s, const PCXHEADER &ph )
+{
+ s << ph.Manufacturer;
+ s << ph.Version;
+ s << ph.Encoding;
+ s << ph.Bpp;
+ s << ph.XMin << ph.YMin << ph.XMax << ph.YMax;
+ s << ph.HDpi << ph.YDpi;
+ s << ph.ColorMap;
+ s << ph.Reserved;
+ s << ph.NPlanes;
+ s << ph.BytesPerLine;
+ s << ph.PaletteInfo;
+ s << ph.HScreenSize;
+ s << ph.VScreenSize;
+
+ Q_UINT8 byte = 0;
+ for ( int i=0; i<54; ++i )
+ s << byte;
+
+ return s;
+}
+
+PCXHEADER::PCXHEADER()
+{
+ // Initialize all data to zero
+ QByteArray dummy( 128 );
+ dummy.fill( 0 );
+ QDataStream s( dummy, IO_ReadOnly );
+ s >> *this;
+}
+
+static void readLine( QDataStream &s, QByteArray &buf, const PCXHEADER &header )
+{
+ Q_UINT32 i=0;
+ Q_UINT32 size = buf.size();
+ Q_UINT8 byte, count;
+
+ if ( header.isCompressed() )
+ {
+ // Uncompress the image data
+ while ( i < size )
+ {
+ count = 1;
+ s >> byte;
+ if ( byte > 0xc0 )
+ {
+ count = byte - 0xc0;
+ s >> byte;
+ }
+ while ( count-- && i < size )
+ buf[ i++ ] = byte;
+ }
+ }
+ else
+ {
+ // Image is not compressed (possible?)
+ while ( i < size )
+ {
+ s >> byte;
+ buf[ i++ ] = byte;
+ }
+ }
+}
+
+static void readImage1( QImage &img, QDataStream &s, const PCXHEADER &header )
+{
+ QByteArray buf( header.BytesPerLine );
+
+ if(!img.create( header.width(), header.height(), 1, 2, QImage::BigEndian ))
+ return;
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ if ( s.atEnd() )
+ {
+ img.reset();
+ return;
+ }
+
+ readLine( s, buf, header );
+ uchar *p = img.scanLine( y );
+ unsigned int bpl = QMIN((header.width()+7)/8, header.BytesPerLine);
+ for ( unsigned int x=0; x< bpl; ++x )
+ p[ x ] = buf[x];
+ }
+
+ // Set the color palette
+ img.setColor( 0, qRgb( 0, 0, 0 ) );
+ img.setColor( 1, qRgb( 255, 255, 255 ) );
+}
+
+static void readImage4( QImage &img, QDataStream &s, const PCXHEADER &header )
+{
+ QByteArray buf( header.BytesPerLine*4 );
+ QByteArray pixbuf( header.width() );
+
+ if(!img.create( header.width(), header.height(), 8, 16 ))
+ return;
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ if ( s.atEnd() )
+ {
+ img.reset();
+ return;
+ }
+
+ pixbuf.fill( 0 );
+ readLine( s, buf, header );
+
+ for ( int i=0; i<4; i++ )
+ {
+ Q_UINT32 offset = i*header.BytesPerLine;
+ for ( unsigned int x=0; x<header.width(); ++x )
+ if ( buf[ offset + ( x/8 ) ] & ( 128 >> ( x%8 ) ) )
+ pixbuf[ x ] += ( 1 << i );
+ }
+
+ uchar *p = img.scanLine( y );
+ for ( unsigned int x=0; x<header.width(); ++x )
+ p[ x ] = pixbuf[ x ];
+ }
+
+ // Read the palette
+ for ( int i=0; i<16; ++i )
+ img.setColor( i, header.ColorMap.color( i ) );
+}
+
+static void readImage8( QImage &img, QDataStream &s, const PCXHEADER &header )
+{
+ QByteArray buf( header.BytesPerLine );
+
+ if(!img.create( header.width(), header.height(), 8, 256 ))
+ return;
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ if ( s.atEnd() )
+ {
+ img.reset();
+ return;
+ }
+
+ readLine( s, buf, header );
+
+ uchar *p = img.scanLine( y );
+ unsigned int bpl = QMIN(header.BytesPerLine, header.width());
+ for ( unsigned int x=0; x<bpl; ++x )
+ p[ x ] = buf[ x ];
+ }
+
+ Q_UINT8 flag;
+ s >> flag;
+ kdDebug( 399 ) << "Palette Flag: " << flag << endl;
+
+ if ( flag == 12 && ( header.Version == 5 || header.Version == 2 ) )
+ {
+ // Read the palette
+ Q_UINT8 r, g, b;
+ for ( int i=0; i<256; ++i )
+ {
+ s >> r >> g >> b;
+ img.setColor( i, qRgb( r, g, b ) );
+ }
+ }
+}
+
+static void readImage24( QImage &img, QDataStream &s, const PCXHEADER &header )
+{
+ QByteArray r_buf( header.BytesPerLine );
+ QByteArray g_buf( header.BytesPerLine );
+ QByteArray b_buf( header.BytesPerLine );
+
+ if(!img.create( header.width(), header.height(), 32 ))
+ return;
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ if ( s.atEnd() )
+ {
+ img.reset();
+ return;
+ }
+
+ readLine( s, r_buf, header );
+ readLine( s, g_buf, header );
+ readLine( s, b_buf, header );
+
+ uint *p = ( uint * )img.scanLine( y );
+ for ( unsigned int x=0; x<header.width(); ++x )
+ p[ x ] = qRgb( r_buf[ x ], g_buf[ x ], b_buf[ x ] );
+ }
+}
+
+KDE_EXPORT void kimgio_pcx_read( QImageIO *io )
+{
+ QDataStream s( io->ioDevice() );
+ s.setByteOrder( QDataStream::LittleEndian );
+
+ if ( s.device()->size() < 128 )
+ {
+ io->setStatus( -1 );
+ return;
+ }
+
+ PCXHEADER header;
+
+ s >> header;
+
+ if ( header.Manufacturer != 10 || s.atEnd())
+ {
+ io->setStatus( -1 );
+ return;
+ }
+
+ int w = header.width();
+ int h = header.height();
+
+ kdDebug( 399 ) << "Manufacturer: " << header.Manufacturer << endl;
+ kdDebug( 399 ) << "Version: " << header.Version << endl;
+ kdDebug( 399 ) << "Encoding: " << header.Encoding << endl;
+ kdDebug( 399 ) << "Bpp: " << header.Bpp << endl;
+ kdDebug( 399 ) << "Width: " << w << endl;
+ kdDebug( 399 ) << "Height: " << h << endl;
+ kdDebug( 399 ) << "Window: " << header.XMin << "," << header.XMax << ","
+ << header.YMin << "," << header.YMax << endl;
+ kdDebug( 399 ) << "BytesPerLine: " << header.BytesPerLine << endl;
+ kdDebug( 399 ) << "NPlanes: " << header.NPlanes << endl;
+
+ QImage img;
+
+ if ( header.Bpp == 1 && header.NPlanes == 1 )
+ {
+ readImage1( img, s, header );
+ }
+ else if ( header.Bpp == 1 && header.NPlanes == 4 )
+ {
+ readImage4( img, s, header );
+ }
+ else if ( header.Bpp == 8 && header.NPlanes == 1 )
+ {
+ readImage8( img, s, header );
+ }
+ else if ( header.Bpp == 8 && header.NPlanes == 3 )
+ {
+ readImage24( img, s, header );
+ }
+
+ kdDebug( 399 ) << "Image Bytes: " << img.numBytes() << endl;
+ kdDebug( 399 ) << "Image Bytes Per Line: " << img.bytesPerLine() << endl;
+ kdDebug( 399 ) << "Image Depth: " << img.depth() << endl;
+
+ if ( !img.isNull() )
+ {
+ io->setImage( img );
+ io->setStatus( 0 );
+ }
+ else
+ {
+ io->setStatus( -1 );
+ }
+}
+
+static void writeLine( QDataStream &s, QByteArray &buf )
+{
+ Q_UINT32 i = 0;
+ Q_UINT32 size = buf.size();
+ Q_UINT8 count, data;
+ char byte;
+
+ while ( i < size )
+ {
+ count = 1;
+ byte = buf[ i++ ];
+
+ while ( ( i < size ) && ( byte == buf[ i ] ) && ( count < 63 ) )
+ {
+ ++i;
+ ++count;
+ }
+
+ data = byte;
+
+ if ( count > 1 || data >= 0xc0 )
+ {
+ count |= 0xc0;
+ s << count;
+ }
+
+ s << data;
+ }
+}
+
+static void writeImage1( QImage &img, QDataStream &s, PCXHEADER &header )
+{
+ img = img.convertBitOrder( QImage::BigEndian );
+
+ header.Bpp = 1;
+ header.NPlanes = 1;
+ header.BytesPerLine = img.bytesPerLine();
+
+ s << header;
+
+ QByteArray buf( header.BytesPerLine );
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ Q_UINT8 *p = img.scanLine( y );
+
+ // Invert as QImage uses reverse palette for monochrome images?
+ for ( int i=0; i<header.BytesPerLine; ++i )
+ buf[ i ] = ~p[ i ];
+
+ writeLine( s, buf );
+ }
+}
+
+static void writeImage4( QImage &img, QDataStream &s, PCXHEADER &header )
+{
+ header.Bpp = 1;
+ header.NPlanes = 4;
+ header.BytesPerLine = header.width()/8;
+
+ for ( int i=0; i<16; ++i )
+ header.ColorMap.setColor( i, img.color( i ) );
+
+ s << header;
+
+ QByteArray buf[ 4 ];
+
+ for ( int i=0; i<4; ++i )
+ buf[ i ].resize( header.BytesPerLine );
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ Q_UINT8 *p = img.scanLine( y );
+
+ for ( int i=0; i<4; ++i )
+ buf[ i ].fill( 0 );
+
+ for ( unsigned int x=0; x<header.width(); ++x )
+ {
+ for ( int i=0; i<4; ++i )
+ if ( *( p+x ) & ( 1 << i ) )
+ buf[ i ][ x/8 ] |= 1 << ( 7-x%8 );
+ }
+
+ for ( int i=0; i<4; ++i )
+ writeLine( s, buf[ i ] );
+ }
+}
+
+static void writeImage8( QImage &img, QDataStream &s, PCXHEADER &header )
+{
+ header.Bpp = 8;
+ header.NPlanes = 1;
+ header.BytesPerLine = img.bytesPerLine();
+
+ s << header;
+
+ QByteArray buf( header.BytesPerLine );
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ Q_UINT8 *p = img.scanLine( y );
+
+ for ( int i=0; i<header.BytesPerLine; ++i )
+ buf[ i ] = p[ i ];
+
+ writeLine( s, buf );
+ }
+
+ // Write palette flag
+ Q_UINT8 byte = 12;
+ s << byte;
+
+ // Write palette
+ for ( int i=0; i<256; ++i )
+ s << RGB( img.color( i ) );
+}
+
+static void writeImage24( QImage &img, QDataStream &s, PCXHEADER &header )
+{
+ header.Bpp = 8;
+ header.NPlanes = 3;
+ header.BytesPerLine = header.width();
+
+ s << header;
+
+ QByteArray r_buf( header.width() );
+ QByteArray g_buf( header.width() );
+ QByteArray b_buf( header.width() );
+
+ for ( int y=0; y<header.height(); ++y )
+ {
+ uint *p = ( uint * )img.scanLine( y );
+
+ for ( unsigned int x=0; x<header.width(); ++x )
+ {
+ QRgb rgb = *p++;
+ r_buf[ x ] = qRed( rgb );
+ g_buf[ x ] = qGreen( rgb );
+ b_buf[ x ] = qBlue( rgb );
+ }
+
+ writeLine( s, r_buf );
+ writeLine( s, g_buf );
+ writeLine( s, b_buf );
+ }
+}
+
+KDE_EXPORT void kimgio_pcx_write( QImageIO *io )
+{
+ QDataStream s( io->ioDevice() );
+ s.setByteOrder( QDataStream::LittleEndian );
+
+ QImage img = io->image();
+
+ int w = img.width();
+ int h = img.height();
+
+ kdDebug( 399 ) << "Width: " << w << endl;
+ kdDebug( 399 ) << "Height: " << h << endl;
+ kdDebug( 399 ) << "Depth: " << img.depth() << endl;
+ kdDebug( 399 ) << "BytesPerLine: " << img.bytesPerLine() << endl;
+ kdDebug( 399 ) << "Num Colors: " << img.numColors() << endl;
+
+ PCXHEADER header;
+
+ header.Manufacturer = 10;
+ header.Version = 5;
+ header.Encoding = 1;
+ header.XMin = 0;
+ header.YMin = 0;
+ header.XMax = w-1;
+ header.YMax = h-1;
+ header.HDpi = 300;
+ header.YDpi = 300;
+ header.Reserved = 0;
+ header.PaletteInfo =1;
+
+ if ( img.depth() == 1 )
+ {
+ writeImage1( img, s, header );
+ }
+ else if ( img.depth() == 8 && img.numColors() <= 16 )
+ {
+ writeImage4( img, s, header );
+ }
+ else if ( img.depth() == 8 )
+ {
+ writeImage8( img, s, header );
+ }
+ else if ( img.depth() == 32 )
+ {
+ writeImage24( img, s, header );
+ }
+
+ io->setStatus( 0 );
+}
+
+/* vim: et sw=2 ts=2
+*/
+
diff --git a/kimgio/pcx.h b/kimgio/pcx.h
new file mode 100644
index 000000000..991cf6acb
--- /dev/null
+++ b/kimgio/pcx.h
@@ -0,0 +1,106 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#ifndef PCX_H
+#define PCX_H
+
+#include <qglobal.h>
+#include <qdatastream.h>
+#include <qcolor.h>
+
+class QImageIO;
+
+extern "C"
+{
+ void kimgio_pcx_read( QImageIO * );
+ void kimgio_pcx_write( QImageIO * );
+}
+
+class RGB
+{
+ public:
+ RGB() { }
+
+ RGB( const QRgb color )
+ {
+ r = qRed( color );
+ g = qGreen( color );
+ b = qBlue( color );
+ }
+
+ Q_UINT8 r;
+ Q_UINT8 g;
+ Q_UINT8 b;
+};
+
+class Palette
+{
+ public:
+ Palette() { }
+
+ void setColor( int i, const QRgb color )
+ {
+ rgb[ i ] = RGB( color );
+ }
+
+ QRgb color( int i ) const
+ {
+ return qRgb( rgb[ i ].r, rgb[ i ].g, rgb[ i ].b );
+ }
+
+ struct RGB rgb[ 16 ];
+};
+
+class PCXHEADER
+{
+ public:
+ PCXHEADER();
+
+ inline int width() const { return ( XMax-XMin ) + 1; }
+ inline int height() const { return ( YMax-YMin ) + 1; }
+ inline bool isCompressed() const { return ( Encoding==1 ); }
+
+ Q_UINT8 Manufacturer; // Constant Flag, 10 = ZSoft .pcx
+ Q_UINT8 Version; // Version information
+ // 0 = Version 2.5 of PC Paintbrush
+ // 2 = Version 2.8 w/palette information
+ // 3 = Version 2.8 w/o palette information
+ // 4 = PC Paintbrush for Windows(Plus for
+ // Windows uses Ver 5)
+ // 5 = Version 3.0 and > of PC Paintbrush
+ // and PC Paintbrush +, includes
+ // Publisher's Paintbrush . Includes
+ // 24-bit .PCX files
+ Q_UINT8 Encoding; // 1 = .PCX run length encoding
+ Q_UINT8 Bpp; // Number of bits to represent a pixel
+ // (per Plane) - 1, 2, 4, or 8
+ Q_UINT16 XMin;
+ Q_UINT16 YMin;
+ Q_UINT16 XMax;
+ Q_UINT16 YMax;
+ Q_UINT16 HDpi;
+ Q_UINT16 YDpi;
+ Palette ColorMap;
+ Q_UINT8 Reserved; // Should be set to 0.
+ Q_UINT8 NPlanes; // Number of color planes
+ Q_UINT16 BytesPerLine; // Number of bytes to allocate for a scanline
+ // plane. MUST be an EVEN number. Do NOT
+ // calculate from Xmax-Xmin.
+ Q_UINT16 PaletteInfo; // How to interpret palette- 1 = Color/BW,
+ // 2 = Grayscale ( ignored in PB IV/ IV + )
+ Q_UINT16 HScreenSize; // Horizontal screen size in pixels. New field
+ // found only in PB IV/IV Plus
+ Q_UINT16 VScreenSize; // Vertical screen size in pixels. New field
+ // found only in PB IV/IV Plus
+} KDE_PACKED;
+
+#endif // PCX_H
+
+/* vim: et sw=2 ts=2
+*/
diff --git a/kimgio/pcx.kimgio b/kimgio/pcx.kimgio
new file mode 100644
index 000000000..c040d9c64
--- /dev/null
+++ b/kimgio/pcx.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=PCX
+Header=^\012
+Name=PCX Image
+Name[af]='PCX' Beeld
+Name[ar]=صورة من نوع PCX
+Name[az]=PCX Rəsmi
+Name[be]=Відарыс PCX
+Name[bg]=PCX изображение
+Name[bn]=পি-সি-এক্স (PCX) চিত্র
+Name[br]=Skeudenn PCX
+Name[bs]=PCX slika
+Name[ca]=Imatge PCX
+Name[cs]=Obrázek ve formátu PCX
+Name[csb]=Òbrôzk PCX
+Name[cy]=Delwedd PCX
+Name[da]=PCX-billede
+Name[de]=PCX-Bild
+Name[el]=Εικόνα PCX
+Name[eo]=PCX bildo
+Name[es]=Imagen PCX
+Name[et]=PCX pildifail
+Name[eu]=PCX irudia
+Name[fa]=تصویر PCX
+Name[fi]=PCX-kuva
+Name[fr]=Image PCX
+Name[fy]=PCX ôfbylding
+Name[ga]=Íomhá PCX
+Name[gl]=Imaxe PCX
+Name[he]=תמונת PCX
+Name[hi]=PCX छवि
+Name[hr]=PCX slika
+Name[hu]=PCX-kép
+Name[id]=Gambar PCX
+Name[is]=PCX mynd
+Name[it]=Immagine PCX
+Name[ja]=PCX 画像
+Name[ka]=PCX ნახატი
+Name[kk]=PCX кескіні
+Name[km]=រូបភាព PCX
+Name[ko]=PCX 그림
+Name[lb]=PCX-Bild
+Name[lt]=PCX paveiksliukas
+Name[lv]=PCX attēls
+Name[mk]=PCX слика
+Name[mn]=PCX-Зураг
+Name[ms]=Imej PCX
+Name[nb]=PCX-bilde
+Name[nds]=PCX-Bild
+Name[ne]=PCX छवि
+Name[nl]=PCX-afbeelding
+Name[nn]=PCX-bilete
+Name[pa]=PCX ਚਿੱਤਰ
+Name[pl]=Obrazek PCX
+Name[pt]=Imagem PCX
+Name[pt_BR]=Imagem PCX
+Name[ro]=Imagine PCX
+Name[ru]=Рисунок PCX
+Name[rw]=PCX Ishusho
+Name[se]=PCX-govva
+Name[sk]=PCX obrázok
+Name[sl]=Slika PCX
+Name[sq]=Imazh PCX
+Name[sr]=PCX слика
+Name[sr@Latn]=PCX slika
+Name[sv]=PCX-bild
+Name[ta]=PCX பிம்பம்
+Name[te]=PCX ప్రతిబింబం
+Name[tg]=Тасвири PCX
+Name[th]=แฟ้มภาพ PCX
+Name[tr]=PCX Resim Dosyası
+Name[tt]=PCX Sürät
+Name[uk]=Зображення PCX
+Name[uz]=PCX-rasm
+Name[uz@cyrillic]=PCX-расм
+Name[vi]=Ảnh PCX
+Name[wa]=Imådje PCX
+Name[zh_CN]=PCX 图像
+Name[zh_HK]=PCX 圖檔
+Name[zh_TW]=PCX 影像
+Read=true
+Write=true
+Suffices=pcx,PCX
+Mimetype=image/x-pcx
+Library=kimg_pcx.la
diff --git a/kimgio/pgm.kimgio b/kimgio/pgm.kimgio
new file mode 100644
index 000000000..517691c7e
--- /dev/null
+++ b/kimgio/pgm.kimgio
@@ -0,0 +1,84 @@
+[Image Format]
+Type=PGM
+Header=
+Name=Portable Graymap Image
+Name[af]='Portable Graymap' Beeld
+Name[ar]=صورة ثنائية اللون قابلة للنقل
+Name[az]=Portable Graymap Rəsmi
+Name[be]=Малюнак Portable Graymap
+Name[bg]=Portable Graymap изображение
+Name[bn]=পোর্টেবল গ্রেম্যাপ চিত্র
+Name[bs]=Portable Greymap slika
+Name[ca]=Imatge portable Graymap
+Name[cs]=Obrázek ve formátu "Portable Graymap"
+Name[csb]=Przenosnô bitmapa (ceniowatosc szaroscë)
+Name[cy]=Delwedd Ddidfap Llwydfap
+Name[da]=Portabelt Graymap-billede
+Name[de]=Portierbares Graymap-Bild
+Name[el]=Εικόνα Portable Graymap
+Name[en_GB]=Portable Greymap Image
+Name[eo]=Portebla griza grafiko
+Name[es]=Imagen portable Graymap
+Name[et]=Portable Graymap pildifail
+Name[eu]=Graymap irudi eramangarria
+Name[fa]=تصویر نگاشت خاکستری حمل‌پذیر
+Name[fi]=Portable Graymap -kuva
+Name[fr]=Image Graymap Portable
+Name[fy]=Portable Graymap ôfbylding
+Name[ga]=Íomhá "Portable Graymap"
+Name[gl]=Imaxe de Mapa de Grises Portábel
+Name[he]=תמונת מפת גווני אפור ניידת
+Name[hi]=पोर्टेबल ग्रे-मेप छवि
+Name[hr]=Portabilna slika u sivim tonovima
+Name[hu]=PGM-kép
+Name[id]=Gambar Portable Graymap
+Name[is]=Skráarsnið GIMP forritsins (PGI)
+Name[it]=Immagine Portable Graymap
+Name[ja]=ポータブルグレイマップ画像
+Name[ka]=Portable Graymap ნახატი
+Name[kk]=Portable Graymap кескіні
+Name[km]=រូបភាព Portable Graymap
+Name[lb]=Portéierbart Graymap-Bild
+Name[lt]=Perkeliamas pilkų pustonių (greymap) paveiksliukas
+Name[lv]=Portable Graymap attēls
+Name[mk]=Портабилна Graymap слика
+Name[mn]=Шилжүүлж болох Greymap-Зураг
+Name[ms]=Imej Graymap Mudah Alih
+Name[nb]=Portable Graymap-bilde
+Name[nds]=Porteerbor Graymap-Bild
+Name[ne]=चलायमान ग्रेम्याप छवि
+Name[nl]=Portable Graymap-afbeelding
+Name[nn]=Portable Graymap-bilete
+Name[pa]=ਪੋਰਟਬਲ ਗਰੇਮੇਪ ਚਿੱਤਰ
+Name[pl]=Przenośna mapa bitowa (w odcieniach szarości)
+Name[pt]=Formato Portável de Mapas de Cinzentos
+Name[pt_BR]=Imagem de Mapas de Escalas de Cinza Portável
+Name[ro]=Imagine în nuanţe de gri în format portabil
+Name[ru]=Рисунок Portable Graymap
+Name[rw]=Portable Graymap Ishusho
+Name[se]=Portable Graymap-govva
+Name[sk]=Portable Graymap obrázok
+Name[sl]=Prenosljiva sivinska slika
+Name[sq]=Imazh Portable Grazmap
+Name[sr]=Портабл слика у нијансама сиве
+Name[sr@Latn]=Portabl slika u nijansama sive
+Name[sv]=Flyttbart gråskalefilformat
+Name[ta]=எடுத்துச்செல்லக்கூடிய கருப்பு-வெள்ளை பிம்பம்
+Name[te]=పొర్టబుల్ గ్రేమేప్ ప్రతిబింబం
+Name[tg]=Тасвири Graymap
+Name[th]=แฟ้มภาพ Portable Graymap
+Name[tr]=Taşınabilir Gri Resim Dosyası
+Name[tt]=Portable Graymap Süräte
+Name[uk]=Чорно-біле зображення у портативному форматі
+Name[uz]=PGM-rasm
+Name[uz@cyrillic]=PGM-расм
+Name[vi]=Ảnh sơ đồ xám di động
+Name[wa]=Imådje PGM
+Name[zh_CN]=便携灰度图像
+Name[zh_HK]=PGM 圖檔
+Name[zh_TW]=可攜式灰階檔案格式
+Read=true
+Write=true
+Suffices=pgm,PGM,pgmraw,PGMRAW
+Mimetype=image/x-portable-greymap
+Library=
diff --git a/kimgio/png.kimgio b/kimgio/png.kimgio
new file mode 100644
index 000000000..c39b23d7f
--- /dev/null
+++ b/kimgio/png.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=PNG
+Header=
+Name=PNG Image
+Name[af]=Png Beeld
+Name[ar]=صورة من نوع PNG
+Name[az]=PNG Rəsmi
+Name[be]=Малюнак PNG
+Name[bg]=PNG изображение
+Name[bn]=পি-এন-জি চিত্র
+Name[br]=Skeudenn PNG
+Name[bs]=PNG slika
+Name[ca]=Imatge PNG
+Name[cs]=Obrázek ve formátu PNG
+Name[csb]=Òbrôzk PNG
+Name[cy]=Delwedd PNG
+Name[da]=PNG-billede
+Name[de]=PNG-Bild
+Name[el]=Εικόνα PNG
+Name[eo]=PNG bildo
+Name[es]=Imagen PNG
+Name[et]=PNG pildifail
+Name[eu]=PNG irudia
+Name[fa]=تصویر PNG
+Name[fi]=PNG-kuva
+Name[fr]=Image PNG
+Name[fy]=PNG ôfbylding
+Name[ga]=Íomhá PNG
+Name[gl]=Imaxe PNG
+Name[he]=תמונת PNG
+Name[hi]=PNG वहनीय
+Name[hr]=PNG slika
+Name[hu]=PNG-kép
+Name[id]=Gambar PNG
+Name[is]=PNG mynd
+Name[it]=Immagine PNG
+Name[ja]=PNG 画像
+Name[ka]=PNG ნახატი
+Name[kk]=PNG кескіні
+Name[km]=រូបភាព PNG
+Name[ko]=PNG 그림
+Name[lb]=PNG-Bild
+Name[lt]=PNG paveiksliukas
+Name[lv]=PNG attēls
+Name[mk]=PNG слика
+Name[mn]=PNG-Зураг
+Name[ms]=Imej PNG
+Name[nb]=PNG-bilde
+Name[nds]=PNG-Bild
+Name[ne]=PNG छवि
+Name[nl]=PNG-afbeelding
+Name[nn]=PNG-bilete
+Name[pa]=PNG ਚਿੱਤਰ
+Name[pl]=Obrazek PNG
+Name[pt]=Imagem PNG
+Name[pt_BR]=Imagem PNG
+Name[ro]=Imagine PNG
+Name[ru]=Рисунок PNG
+Name[rw]=PNG Ishusho
+Name[se]=PNG-govva
+Name[sk]=PNG obrázok
+Name[sl]=Slika PNG
+Name[sq]=Imazh PNG
+Name[sr]=PNG слика
+Name[sr@Latn]=PNG slika
+Name[sv]=PNG-bild
+Name[ta]=PNG பிம்பம்
+Name[te]=PNG ప్రతిబింబం
+Name[tg]=Тасвири PNG
+Name[th]=แฟ้มภาพ PNG
+Name[tr]=PNG Resim Dosyası
+Name[tt]=PNG Sürät
+Name[uk]=Зображення PNG
+Name[uz]=PNG-rasm
+Name[uz@cyrillic]=PNG-расм
+Name[vi]=Ảnh PNG
+Name[wa]=Imådje PNG
+Name[zh_CN]=PNG 图像
+Name[zh_HK]=PNG 圖檔
+Name[zh_TW]=PNG 影像
+Read=true
+Write=true
+Suffices=png,PNG
+Mimetype=image/png
+Library=
diff --git a/kimgio/ppm.kimgio b/kimgio/ppm.kimgio
new file mode 100644
index 000000000..27993d376
--- /dev/null
+++ b/kimgio/ppm.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=PPM
+Header=
+Name=Portable Pixmap Image
+Name[af]='Portable Pixmap' Beeld
+Name[ar]=صورة Pixmap قابلة للنقل
+Name[az]=Portable Pixmap Rəsmi
+Name[be]=Малюнак Portable Pixmap
+Name[bg]=Portable Pixmap изображение
+Name[bn]=পোর্টেবল পিক্সম্যাপ চিত্র
+Name[br]=Skeudenn Portable Pixmap
+Name[bs]=Portable Pixmap slika
+Name[ca]=Imatge portable mapa de píxels
+Name[cs]=Obrázek ve formátu "Portable Pixmap"
+Name[csb]=Przenosnô mapa pikselów
+Name[cy]=Delwedd Bicsfap Llwydfap
+Name[da]=Portabelt Pixmap-billede
+Name[de]=Portierbares Pixmap-Bild
+Name[el]=Εικόνα Portable Pixmap
+Name[eo]=Portebla grafiko (PPM)
+Name[es]=Imagen portable Pixmap
+Name[et]=Portable pikselrster-pildifail
+Name[eu]=Pixmap irudi eramangarria
+Name[fa]=تصویر نگاشت تصویردانه‌ای حمل‌پذیر
+Name[fi]=Portable Pixmap -kuva
+Name[fr]=Image Pixmap Portable
+Name[fy]=Portable Pixmap ôfbylding
+Name[ga]=Íomhá "Portable Pixmap"
+Name[gl]=Imaxe Pixmap Portábel
+Name[he]=תמונת מפת פיקסלים ניידת
+Name[hi]=पोर्टेबल पिक्समेप छवि
+Name[hr]=Portabilna PixMap slika
+Name[hu]=PPM-kép
+Name[id]=Gambar Portable Pixmap
+Name[is]=Skráarsnið GIMP forritsins (PPI)
+Name[it]=Immagine Portable Pixmap
+Name[ja]=ポータブルピックスマップ画像
+Name[ka]=Portable Pixmap ნახატი
+Name[kk]=Portable Pixmap кескіні
+Name[km]=រូបភាព Portable Pixmap
+Name[ko]=포터블 픽스맵 그림
+Name[lb]=Portéierbart Pixmap-Bild
+Name[lt]=Perkeliamas taškinės grafikos (pixmap) paveiksliukas
+Name[lv]=Portable Pixmap attēls
+Name[mk]=Портабилна Pixmap слика
+Name[mn]=Шилжүүлж болох Pixmap-Зураг
+Name[ms]=Imej Pixmap Mudah Alih
+Name[nb]=Portable Pixmap-bilde
+Name[nds]=Porteerbor Pixmap-Bild
+Name[ne]=चलायमान पिक्सम्याप छवि
+Name[nl]=Portable Pixmap-afbeelding
+Name[nn]=Portable Pixmap-bilete
+Name[pa]=ਪੋਰਟਬਲ ਪਿਕਬਲ ਚਿੱਤਰ
+Name[pl]=Przenośna mapa pikseli
+Name[pt]=Formato Portável de 'Pixmaps'
+Name[pt_BR]=Imagem Pixmap Portável
+Name[ro]=Imagine pixmap în format portabil
+Name[ru]=Рисунок Portable Pixmap
+Name[rw]=Portable Pixmap Ishusho
+Name[se]=Portable Pixmap-govva
+Name[sk]=Portable Pixmap obrázok
+Name[sl]=Prenosljiva rastrska slika
+Name[sq]=Imazh Portable Pixmap
+Name[sr]=Портабл пикселмапирана слика
+Name[sr@Latn]=Portabl pikselmapirana slika
+Name[sv]=Flyttbart pixmappfilformat
+Name[ta]=எடுத்துச்செல்லக்கூடிய பிக்ஸ்வரைபட பிம்பம்
+Name[te]=పోర్టబుల్ పిక్స్ మేప్ ప్రతిబింబం
+Name[tg]=Тасвири Pixmap
+Name[th]=แฟ้มภาพ Portable Pixmap
+Name[tr]=Taşınabilir Bit Eşlem Resim Dosyası
+Name[tt]=Portable Pixmap Süräte
+Name[uk]=Растрове зображення у портативному форматі
+Name[uz]=PPMP-rasm
+Name[uz@cyrillic]=PPMP-расм
+Name[vi]=Ảnh sơ đồ điểm ảnh di động
+Name[wa]=Imådje PPM
+Name[zh_CN]=便携像素图图像
+Name[zh_HK]=PPM 圖檔
+Name[zh_TW]=可攜式像素影像
+Read=true
+Write=true
+Suffices=ppm,PPM,pnm,PNM,ppmraw,PPMRAW
+Mimetype=image/x-portable-pixmap
+Library=
diff --git a/kimgio/psd.cpp b/kimgio/psd.cpp
new file mode 100644
index 000000000..202fbacb4
--- /dev/null
+++ b/kimgio/psd.cpp
@@ -0,0 +1,282 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Ignacio Castao <castano@ludicon.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This code is based on Thacher Ulrich PSD loading code released
+ on public domain. See: http://tulrich.com/geekstuff/
+*/
+
+/* this code supports:
+ * reading:
+ * rle and raw psd files
+ * writing:
+ * not supported
+ */
+
+#include "psd.h"
+
+#include <qimage.h>
+#include <qdatastream.h>
+
+#include <kdebug.h>
+
+typedef Q_UINT32 uint;
+typedef Q_UINT16 ushort;
+typedef Q_UINT8 uchar;
+
+namespace { // Private.
+
+ enum ColorMode {
+ CM_BITMAP = 0,
+ CM_GRAYSCALE = 1,
+ CM_INDEXED = 2,
+ CM_RGB = 3,
+ CM_CMYK = 4,
+ CM_MULTICHANNEL = 7,
+ CM_DUOTONE = 8,
+ CM_LABCOLOR = 9
+ };
+
+ struct PSDHeader {
+ uint signature;
+ ushort version;
+ uchar reserved[6];
+ ushort channel_count;
+ uint height;
+ uint width;
+ ushort depth;
+ ushort color_mode;
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, PSDHeader & header )
+ {
+ s >> header.signature;
+ s >> header.version;
+ for( int i = 0; i < 6; i++ ) {
+ s >> header.reserved[i];
+ }
+ s >> header.channel_count;
+ s >> header.height;
+ s >> header.width;
+ s >> header.depth;
+ s >> header.color_mode;
+ return s;
+ }
+ static bool seekBy(QDataStream& s, unsigned int bytes)
+ {
+ char buf[4096];
+ while (bytes) {
+ unsigned int num= QMIN(bytes,sizeof(buf));
+ unsigned int l = num;
+ s.readRawBytes(buf, l);
+ if(l != num)
+ return false;
+ bytes -= num;
+ }
+ return true;
+ }
+
+ // Check that the header is a valid PSD.
+ static bool IsValid( const PSDHeader & header )
+ {
+ if( header.signature != 0x38425053 ) { // '8BPS'
+ return false;
+ }
+ return true;
+ }
+
+ // Check that the header is supported.
+ static bool IsSupported( const PSDHeader & header )
+ {
+ if( header.version != 1 ) {
+ return false;
+ }
+ if( header.channel_count > 16 ) {
+ return false;
+ }
+ if( header.depth != 8 ) {
+ return false;
+ }
+ if( header.color_mode != CM_RGB ) {
+ return false;
+ }
+ return true;
+ }
+
+ // Load the PSD image.
+ static bool LoadPSD( QDataStream & s, const PSDHeader & header, QImage & img )
+ {
+ // Create dst image.
+ if( !img.create( header.width, header.height, 32 )) {
+ return false;
+ }
+
+ uint tmp;
+
+ // Skip mode data.
+ s >> tmp;
+ s.device()->at( s.device()->at() + tmp );
+
+ // Skip image resources.
+ s >> tmp;
+ s.device()->at( s.device()->at() + tmp );
+
+ // Skip the reserved data.
+ s >> tmp;
+ s.device()->at( s.device()->at() + tmp );
+
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ ushort compression;
+ s >> compression;
+
+ if( compression > 1 ) {
+ // Unknown compression type.
+ return false;
+ }
+
+ uint channel_num = header.channel_count;
+
+ // Clear the image.
+ if( channel_num < 4 ) {
+ img.fill(qRgba(0, 0, 0, 0xFF));
+ }
+ else {
+ // Enable alpha.
+ img.setAlphaBuffer( true );
+
+ // Ignore the other channels.
+ channel_num = 4;
+ }
+
+ const uint pixel_count = header.height * header.width;
+
+ static const uint components[4] = {2, 1, 0, 3}; // @@ Is this endian dependant?
+
+ if( compression ) {
+
+ // Skip row lengths.
+ if(!seekBy(s, header.height*header.channel_count*sizeof(ushort)))
+ return false;
+
+ // Read RLE data.
+ for(uint channel = 0; channel < channel_num; channel++) {
+
+ uchar * ptr = img.bits() + components[channel];
+
+ uint count = 0;
+ while( count < pixel_count ) {
+ uchar c;
+ if(s.atEnd())
+ return false;
+ s >> c;
+ uint len = c;
+
+ if( len < 128 ) {
+ // Copy next len+1 bytes literally.
+ len++;
+ count += len;
+ if ( count > pixel_count )
+ return false;
+
+ while( len != 0 ) {
+ s >> *ptr;
+ ptr += 4;
+ len--;
+ }
+ }
+ else if( len > 128 ) {
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len ^= 0xFF;
+ len += 2;
+ count += len;
+ if(s.atEnd() || count > pixel_count)
+ return false;
+ uchar val;
+ s >> val;
+ while( len != 0 ) {
+ *ptr = val;
+ ptr += 4;
+ len--;
+ }
+ }
+ else if( len == 128 ) {
+ // No-op.
+ }
+ }
+ }
+ }
+ else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit value for each pixel in the image.
+
+ // Read the data by channel.
+ for(uint channel = 0; channel < channel_num; channel++) {
+
+ uchar * ptr = img.bits() + components[channel];
+
+ // Read the data.
+ uint count = pixel_count;
+ while( count != 0 ) {
+ s >> *ptr;
+ ptr += 4;
+ count--;
+ }
+ }
+ }
+
+ return true;
+ }
+
+} // Private
+
+
+void kimgio_psd_read( QImageIO *io )
+{
+ QDataStream s( io->ioDevice() );
+ s.setByteOrder( QDataStream::BigEndian );
+
+ PSDHeader header;
+ s >> header;
+
+ // Check image file format.
+ if( s.atEnd() || !IsValid( header ) ) {
+ kdDebug(399) << "This PSD file is not valid." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ // Check if it's a supported format.
+ if( !IsSupported( header ) ) {
+ kdDebug(399) << "This PSD file is not supported." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ QImage img;
+ if( !LoadPSD(s, header, img) ) {
+ kdDebug(399) << "Error loading PSD file." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ io->setImage( img );
+ io->setStatus( 0 );
+}
+
+
+void kimgio_psd_write( QImageIO * )
+{
+ // TODO Stub!
+}
+
diff --git a/kimgio/psd.h b/kimgio/psd.h
new file mode 100644
index 000000000..0b99ef75e
--- /dev/null
+++ b/kimgio/psd.h
@@ -0,0 +1,23 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Ignacio Castao <castano@ludicon.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#ifndef KIMG_PSD_H
+#define KIMG_PSD_H
+
+#include <kdemacros.h>
+
+class QImageIO;
+
+extern "C" {
+KDE_EXPORT void kimgio_psd_read( QImageIO * );
+KDE_EXPORT void kimgio_psd_write( QImageIO * );
+}
+
+#endif
+
diff --git a/kimgio/psd.kimgio b/kimgio/psd.kimgio
new file mode 100644
index 000000000..853dd8229
--- /dev/null
+++ b/kimgio/psd.kimgio
@@ -0,0 +1,72 @@
+[Image Format]
+Type=PSD
+Header=^8BPS
+Name=Adobe Photoshop Image
+Name[ar]=صورة Adobe Photoshop
+Name[be]=Малюнак Adobe Photoshop
+Name[bg]=Adobe Photoshop изображение
+Name[bn]=অ্যাডোব ফোটোশপ চিত্র
+Name[br]=Skeudenn Adobe Photoshop
+Name[bs]=Adobe Photoshop slika
+Name[ca]=Imatge Adobe Photoshop
+Name[cs]=Obrázek Adobe Photoshop
+Name[csb]=Òbrôzk Adobe Photoshop
+Name[cy]=Delwedd Adobe Photoshop
+Name[da]=Adobe Photoshop-billede
+Name[de]=Adobe-Photoshop-Bild
+Name[el]=Εικόνα Adobe Photoshop
+Name[es]=Imagen Photoshop de Adobe
+Name[et]=Adobe Photoshop pildifail
+Name[eu]=Adobe Photoshop irudia
+Name[fa]=تصویر Adobe Photoshop
+Name[fi]=Adobe Photoshop -kuva
+Name[fr]=Image Adobe Photoshop
+Name[fy]=Adobe Photoshop-ôfbylding
+Name[ga]=Íomhá Adobe Photoshop
+Name[gl]=Imaxe de Adobe Photoshop
+Name[hr]=Adobe Photoshop slika
+Name[hu]=Adobe Photoshop-kép
+Name[is]=Adobe Photoshop mynd
+Name[it]=Immagine Adobe Photoshop
+Name[ja]=Adobe Photoshop 画像
+Name[ka]=Adobe Photoshop ნახატი
+Name[kk]=Adobe Photoshop кескіні
+Name[km]= រូបភាព​របស់ Adobe Photoshop
+Name[lb]=Adobe-Photoshop-Bild
+Name[lv]=Adobe Photoshop attēls
+Name[mk]=Adobe Photoshop-слика
+Name[ms]=Imej Adobe Photoshop
+Name[nb]=Adobe Photoshop-bilde
+Name[nds]=Adobe-Photoshop-Bild
+Name[ne]=एडब फोटोसप छवि
+Name[nl]=Adobe Photoshop-afbeelding
+Name[nn]=Adobe Photoshop-bilete
+Name[pa]=Adobe Photoshop ਚਿੱਤਰ
+Name[pl]=Obrazek Adobe Photoshop
+Name[pt]=Imagem do Adobe Photoshop
+Name[pt_BR]=Imagem do Adobe Photoshop
+Name[ro]=Imagine Adobe Photoshop
+Name[ru]=Рисунок Adobe Photoshop
+Name[se]=Adobe Photoshop-govva
+Name[sk]=Adobe Photoshop obrázok
+Name[sl]=Slika Adobe Photoshop
+Name[sr]=Слика Adobe-овог Photoshop-а
+Name[sr@Latn]=Slika Adobe-ovog Photoshop-a
+Name[sv]=Adobe Photoshop-bild
+Name[te]=ఎడోబ్ ఫొటోషాప్ ప్రతిబింబం
+Name[tg]=Тасвири Adobe Photoshop
+Name[th]=แฟ้มภาพอโดบีโฟโต้ช้อป
+Name[tr]=Adobe Photoshop Resmi
+Name[uk]=Зображення Adobe Photoshop
+Name[uz]=Adobe Photoshop-rasm
+Name[uz@cyrillic]=Adobe Photoshop-расм
+Name[vi]=Ảnh Photoshop của Adobe
+Name[zh_CN]=Adobe Photoshop 图像
+Name[zh_HK]=Adobe Photoshop 圖檔
+Name[zh_TW]=Adobe Photoshop 影像
+Read=true
+Write=false
+Suffices=psd,PSD
+Mimetype=image/x-vnd.adobe.photoshop
+Library=kimg_psd.la
+
diff --git a/kimgio/rgb.cpp b/kimgio/rgb.cpp
new file mode 100644
index 000000000..2e62f4f6f
--- /dev/null
+++ b/kimgio/rgb.cpp
@@ -0,0 +1,589 @@
+// kimgio module for SGI images
+//
+// Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the Lesser GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+
+
+/* this code supports:
+ * reading:
+ * everything, except images with 1 dimension or images with
+ * mapmode != NORMAL (e.g. dithered); Images with 16 bit
+ * precision or more than 4 layers are stripped down.
+ * writing:
+ * Run Length Encoded (RLE) or Verbatim (uncompressed)
+ * (whichever is smaller)
+ *
+ * Please report if you come across rgb/rgba/sgi/bw files that aren't
+ * recognized. Also report applications that can't deal with images
+ * saved by this filter.
+ */
+
+
+#include "rgb.h"
+#include <qimage.h>
+#include <kdebug.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+KDE_EXPORT void kimgio_rgb_read(QImageIO *io)
+{
+ SGIImage sgi(io);
+ QImage img;
+
+ if (!sgi.readImage(img)) {
+ io->setImage(0);
+ io->setStatus(-1);
+ return;
+ }
+
+ io->setImage(img);
+ io->setStatus(0);
+}
+
+
+KDE_EXPORT void kimgio_rgb_write(QImageIO *io)
+{
+ SGIImage sgi(io);
+ QImage img = io->image();
+
+ if (!sgi.writeImage(img))
+ io->setStatus(-1);
+
+ io->setStatus(0);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+SGIImage::SGIImage(QImageIO *io) :
+ m_io(io),
+ m_starttab(0),
+ m_lengthtab(0)
+{
+ m_dev = io->ioDevice();
+ m_stream.setDevice(m_dev);
+}
+
+
+SGIImage::~SGIImage()
+{
+ delete[] m_starttab;
+ delete[] m_lengthtab;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+bool SGIImage::getRow(uchar *dest)
+{
+ int n, i;
+ if (!m_rle) {
+ for (i = 0; i < m_xsize; i++) {
+ if (m_pos >= m_data.end())
+ return false;
+ dest[i] = uchar(*m_pos);
+ m_pos += m_bpc;
+ }
+ return true;
+ }
+
+ for (i = 0; i < m_xsize;) {
+ if (m_bpc == 2)
+ m_pos++;
+ n = *m_pos & 0x7f;
+ if (!n)
+ break;
+
+ if (*m_pos++ & 0x80) {
+ for (; i < m_xsize && n--; i++) {
+ *dest++ = *m_pos;
+ m_pos += m_bpc;
+ }
+ } else {
+ for (; i < m_xsize && n--; i++)
+ *dest++ = *m_pos;
+
+ m_pos += m_bpc;
+ }
+ }
+ return i == m_xsize;
+}
+
+
+bool SGIImage::readData(QImage& img)
+{
+ QRgb *c;
+ Q_UINT32 *start = m_starttab;
+ QByteArray lguard(m_xsize);
+ uchar *line = (uchar *)lguard.data();
+ unsigned x, y;
+
+ if (!m_rle)
+ m_pos = m_data.begin();
+
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgb(line[x], line[x], line[x]);
+ }
+
+ if (m_zsize == 1)
+ return true;
+
+ if (m_zsize != 2) {
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgb(qRed(*c), line[x], line[x]);
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgb(qRed(*c), qGreen(*c), line[x]);
+ }
+
+ if (m_zsize == 3)
+ return true;
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgba(qRed(*c), qGreen(*c), qBlue(*c), line[x]);
+ }
+
+ return true;
+}
+
+
+bool SGIImage::readImage(QImage& img)
+{
+ Q_INT8 u8;
+ Q_INT16 u16;
+ Q_INT32 u32;
+
+ kdDebug(399) << "reading '" << m_io->fileName() << '\'' << endl;
+
+ // magic
+ m_stream >> u16;
+ if (u16 != 0x01da)
+ return false;
+
+ // verbatim/rle
+ m_stream >> m_rle;
+ kdDebug(399) << (m_rle ? "RLE" : "verbatim") << endl;
+ if (m_rle > 1)
+ return false;
+
+ // bytes per channel
+ m_stream >> m_bpc;
+ kdDebug(399) << "bytes per channel: " << int(m_bpc) << endl;
+ if (m_bpc == 1)
+ ;
+ else if (m_bpc == 2)
+ kdDebug(399) << "dropping least significant byte" << endl;
+ else
+ return false;
+
+ // number of dimensions
+ m_stream >> m_dim;
+ kdDebug(399) << "dimensions: " << m_dim << endl;
+ if (m_dim < 1 || m_dim > 3)
+ return false;
+
+ m_stream >> m_xsize >> m_ysize >> m_zsize >> m_pixmin >> m_pixmax >> u32;
+ kdDebug(399) << "x: " << m_xsize << endl;
+ kdDebug(399) << "y: " << m_ysize << endl;
+ kdDebug(399) << "z: " << m_zsize << endl;
+
+ // name
+ m_stream.readRawBytes(m_imagename, 80);
+ m_imagename[79] = '\0';
+ m_io->setDescription(m_imagename);
+
+ m_stream >> m_colormap;
+ kdDebug(399) << "colormap: " << m_colormap << endl;
+ if (m_colormap != NORMAL)
+ return false; // only NORMAL supported
+
+ for (int i = 0; i < 404; i++)
+ m_stream >> u8;
+
+ if (m_dim == 1) {
+ kdDebug(399) << "1-dimensional images aren't supported yet" << endl;
+ return false;
+ }
+
+ if( m_stream.atEnd())
+ return false;
+
+ m_numrows = m_ysize * m_zsize;
+
+ if (!img.create(m_xsize, m_ysize, 32)) {
+ kdDebug(399) << "cannot create image" << endl;
+ return false;
+ }
+
+ if (m_zsize == 2 || m_zsize == 4)
+ img.setAlphaBuffer(true);
+ else if (m_zsize > 4)
+ kdDebug(399) << "using first 4 of " << m_zsize << " channels" << endl;
+
+ if (m_rle) {
+ uint l;
+ m_starttab = new Q_UINT32[m_numrows];
+ for (l = 0; !m_stream.atEnd() && l < m_numrows; l++) {
+ m_stream >> m_starttab[l];
+ m_starttab[l] -= 512 + m_numrows * 2 * sizeof(Q_UINT32);
+ }
+
+ m_lengthtab = new Q_UINT32[m_numrows];
+ for (l = 0; l < m_numrows; l++)
+ m_stream >> m_lengthtab[l];
+ }
+
+ m_data = m_dev->readAll();
+
+ // sanity check
+ if (m_rle)
+ for (uint o = 0; o < m_numrows; o++)
+ // don't change to greater-or-equal!
+ if (m_starttab[o] + m_lengthtab[o] > m_data.size()) {
+ kdDebug(399) << "image corrupt (sanity check failed)" << endl;
+ return false;
+ }
+
+ if (!readData(img)) {
+ kdDebug(399) << "image corrupt (incomplete scanline)" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+// TODO remove; for debugging purposes only
+void RLEData::print(QString desc) const
+{
+ QString s = desc + ": ";
+ for (uint i = 0; i < size(); i++)
+ s += QString::number(at(i)) + ",";
+ kdDebug() << "--- " << s << endl;
+}
+
+
+void RLEData::write(QDataStream& s)
+{
+ for (unsigned i = 0; i < size(); i++)
+ s << at(i);
+}
+
+
+bool RLEData::operator<(const RLEData& b) const
+{
+ uchar ac, bc;
+ for (unsigned i = 0; i < QMIN(size(), b.size()); i++) {
+ ac = at(i);
+ bc = b[i];
+ if (ac != bc)
+ return ac < bc;
+ }
+ return size() < b.size();
+}
+
+
+uint RLEMap::insert(const uchar *d, uint l)
+{
+ RLEData data = RLEData(d, l, m_offset);
+ Iterator it = find(data);
+ if (it != end())
+ return it.data();
+
+ m_offset += l;
+ return QMap<RLEData, uint>::insert(data, m_counter++).data();
+}
+
+
+QPtrVector<RLEData> RLEMap::vector()
+{
+ QPtrVector<RLEData> v(size());
+ for (Iterator it = begin(); it != end(); ++it)
+ v.insert(it.data(), &it.key());
+
+ return v;
+}
+
+
+uchar SGIImage::intensity(uchar c)
+{
+ if (c < m_pixmin)
+ m_pixmin = c;
+ if (c > m_pixmax)
+ m_pixmax = c;
+ return c;
+}
+
+
+uint SGIImage::compact(uchar *d, uchar *s)
+{
+ uchar *dest = d, *src = s, patt, *t, *end = s + m_xsize;
+ int i, n;
+ while (src < end) {
+ for (n = 0, t = src; t + 2 < end && !(*t == t[1] && *t == t[2]); t++)
+ n++;
+
+ while (n) {
+ i = n > 126 ? 126 : n;
+ n -= i;
+ *dest++ = 0x80 | i;
+ while (i--)
+ *dest++ = *src++;
+ }
+
+ if (src == end)
+ break;
+
+ patt = *src++;
+ for (n = 1; src < end && *src == patt; src++)
+ n++;
+
+ while (n) {
+ i = n > 126 ? 126 : n;
+ n -= i;
+ *dest++ = i;
+ *dest++ = patt;
+ }
+ }
+ *dest++ = 0;
+ return dest - d;
+}
+
+
+bool SGIImage::scanData(const QImage& img)
+{
+ Q_UINT32 *start = m_starttab;
+ QCString lineguard(m_xsize * 2);
+ QCString bufguard(m_xsize);
+ uchar *line = (uchar *)lineguard.data();
+ uchar *buf = (uchar *)bufguard.data();
+ QRgb *c;
+ unsigned x, y;
+ uint len;
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qRed(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ if (m_zsize == 1)
+ return true;
+
+ if (m_zsize != 2) {
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qGreen(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qBlue(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ if (m_zsize == 3)
+ return true;
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qAlpha(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ return true;
+}
+
+
+void SGIImage::writeHeader()
+{
+ m_stream << Q_UINT16(0x01da);
+ m_stream << m_rle << m_bpc << m_dim;
+ m_stream << m_xsize << m_ysize << m_zsize;
+ m_stream << m_pixmin << m_pixmax;
+ m_stream << Q_UINT32(0);
+
+ uint i;
+ QString desc = m_io->description();
+ kdDebug(399) << "Description: " << desc << endl;
+ desc.truncate(79);
+
+ for (i = 0; i < desc.length(); i++)
+ m_imagename[i] = desc.latin1()[i];
+ for (; i < 80; i++)
+ m_imagename[i] = '\0';
+ m_stream.writeRawBytes(m_imagename, 80);
+
+ m_stream << m_colormap;
+ for (i = 0; i < 404; i++)
+ m_stream << Q_UINT8(0);
+}
+
+
+void SGIImage::writeRle()
+{
+ m_rle = 1;
+ kdDebug(399) << "writing RLE data" << endl;
+ writeHeader();
+ uint i;
+
+ // write start table
+ for (i = 0; i < m_numrows; i++)
+ m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->offset());
+
+ // write length table
+ for (i = 0; i < m_numrows; i++)
+ m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->size());
+
+ // write data
+ for (i = 0; i < m_rlevector.size(); i++)
+ m_rlevector[i]->write(m_stream);
+}
+
+
+void SGIImage::writeVerbatim(const QImage& img)
+{
+ m_rle = 0;
+ kdDebug(399) << "writing verbatim data" << endl;
+ writeHeader();
+
+ QRgb *c;
+ unsigned x, y;
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qRed(*c++));
+ }
+
+ if (m_zsize == 1)
+ return;
+
+ if (m_zsize != 2) {
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qGreen(*c++));
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qBlue(*c++));
+ }
+
+ if (m_zsize == 3)
+ return;
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qAlpha(*c++));
+ }
+}
+
+
+bool SGIImage::writeImage(QImage& img)
+{
+ kdDebug(399) << "writing '" << m_io->fileName() << '\'' << endl;
+
+ if (img.allGray())
+ m_dim = 2, m_zsize = 1;
+ else
+ m_dim = 3, m_zsize = 3;
+
+ if (img.hasAlphaBuffer())
+ m_dim = 3, m_zsize++;
+
+ img = img.convertDepth(32);
+ if (img.isNull()) {
+ kdDebug(399) << "can't convert image to depth 32" << endl;
+ return false;
+ }
+
+ m_bpc = 1;
+ m_xsize = img.width();
+ m_ysize = img.height();
+ m_pixmin = ~0;
+ m_pixmax = 0;
+ m_colormap = NORMAL;
+
+ m_numrows = m_ysize * m_zsize;
+
+ m_starttab = new Q_UINT32[m_numrows];
+ m_rlemap.setBaseOffset(512 + m_numrows * 2 * sizeof(Q_UINT32));
+
+ if (!scanData(img)) {
+ kdDebug(399) << "this can't happen" << endl;
+ return false;
+ }
+
+ m_rlevector = m_rlemap.vector();
+
+ long verbatim_size = m_numrows * m_xsize;
+ long rle_size = m_numrows * 2 * sizeof(Q_UINT32);
+ for (uint i = 0; i < m_rlevector.size(); i++)
+ rle_size += m_rlevector[i]->size();
+
+ kdDebug(399) << "minimum intensity: " << m_pixmin << endl;
+ kdDebug(399) << "maximum intensity: " << m_pixmax << endl;
+ kdDebug(399) << "saved scanlines: " << m_numrows - m_rlemap.size() << endl;
+ kdDebug(399) << "total savings: " << (verbatim_size - rle_size) << " bytes" << endl;
+ kdDebug(399) << "compression: " << (rle_size * 100.0 / verbatim_size) << '%' << endl;
+
+ if (verbatim_size <= rle_size || m_io->quality() > 50)
+ writeVerbatim(img);
+ else
+ writeRle();
+ return true;
+}
+
+
diff --git a/kimgio/rgb.h b/kimgio/rgb.h
new file mode 100644
index 000000000..224d22cc8
--- /dev/null
+++ b/kimgio/rgb.h
@@ -0,0 +1,97 @@
+// kimgio module for SGI images
+//
+// Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the Lesser GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+
+
+#ifndef KIMG_RGB_H
+#define KIMG_RGB_H
+
+#include <qmap.h>
+#include <qptrvector.h>
+
+
+class QImage;
+class QImageIO;
+
+extern "C" {
+void kimgio_rgb_read(QImageIO *);
+void kimgio_rgb_write(QImageIO *);
+}
+
+
+class RLEData : public QMemArray<uchar> {
+public:
+ RLEData() {}
+ RLEData(const uchar *d, uint l, uint o) : m_offset(o) { duplicate(d, l); }
+ bool operator<(const RLEData&) const;
+ void write(QDataStream& s);
+ void print(QString) const; // TODO remove
+ uint offset() { return m_offset; }
+private:
+ uint m_offset;
+};
+
+
+class RLEMap : public QMap<RLEData, uint> {
+public:
+ RLEMap() : m_counter(0), m_offset(0) {}
+ uint insert(const uchar *d, uint l);
+ QPtrVector<RLEData> vector();
+ void setBaseOffset(uint o) { m_offset = o; }
+private:
+ uint m_counter;
+ uint m_offset;
+};
+
+
+class SGIImage {
+public:
+ SGIImage(QImageIO *);
+ ~SGIImage();
+
+ bool readImage(QImage&);
+ bool writeImage(QImage&);
+
+private:
+ enum { NORMAL, DITHERED, SCREEN, COLORMAP }; // colormap
+ QImageIO *m_io;
+ QIODevice *m_dev;
+ QDataStream m_stream;
+
+ Q_UINT8 m_rle;
+ Q_UINT8 m_bpc;
+ Q_UINT16 m_dim;
+ Q_UINT16 m_xsize;
+ Q_UINT16 m_ysize;
+ Q_UINT16 m_zsize;
+ Q_UINT32 m_pixmin;
+ Q_UINT32 m_pixmax;
+ char m_imagename[80];
+ Q_UINT32 m_colormap;
+
+ Q_UINT32 *m_starttab;
+ Q_UINT32 *m_lengthtab;
+ QByteArray m_data;
+ QByteArray::Iterator m_pos;
+ RLEMap m_rlemap;
+ QPtrVector<RLEData> m_rlevector;
+ uint m_numrows;
+
+ bool readData(QImage&);
+ bool getRow(uchar *dest);
+
+ void writeHeader();
+ void writeRle();
+ void writeVerbatim(const QImage&);
+ bool scanData(const QImage&);
+ uint compact(uchar *, uchar *);
+ uchar intensity(uchar);
+};
+
+#endif
+
diff --git a/kimgio/rgb.kimgio b/kimgio/rgb.kimgio
new file mode 100644
index 000000000..ebe7fe36f
--- /dev/null
+++ b/kimgio/rgb.kimgio
@@ -0,0 +1,80 @@
+[Image Format]
+Type=RGB
+Header=^\x01\xda\x01[\x01\x02]
+Name=SGI Image (RGB)
+Name[af]=SGI Beeld (RGB)
+Name[be]=Відарыс SGI (RGB)
+Name[bg]=SGI (RGB) изображение
+Name[bn]=এস-জি-আই চিত্র (RGB)
+Name[br]=Skeudenn SGI (RGB)
+Name[bs]=SGI slika (RGB)
+Name[ca]=Imatge SGI (RGB)
+Name[cs]=SGI obrázek (RGB)
+Name[csb]=Òbrôzk SGI (RGB)
+Name[cy]=Delwedd SGI (RGB)
+Name[da]=SGI-billede (RGB)
+Name[de]=SGI-Bild (RGB)
+Name[el]=Εικόνα SGI (RGB)
+Name[eo]=SGIa bildo (rvb)
+Name[es]=Imagen SGI (RGB)
+Name[et]=SGI pildifail (RGB)
+Name[eu]=SGI irudia (RGB)
+Name[fa]=تصویر) SGI (ًRGB
+Name[fi]=SGI-kuva (RGB)
+Name[fr]=Image SGI (RVB)
+Name[fy]=SGI ôfbylding (RBG)
+Name[ga]=Íomhá SGI (RGB)
+Name[gl]=Imaxe SGI (RGB)
+Name[he]=תמונת SGI (RGB)
+Name[hi]=एसजीआई छवि (आरजीबी)
+Name[hr]=SGI slika (RGB)
+Name[hu]=SGI-kép (RGB)
+Name[id]=Gambar SGI (RGB)
+Name[is]=SGI mynd (RGB)
+Name[it]=Immagine SGI (RGB)
+Name[ja]=SGI 画像 (RGB)
+Name[ka]=SGI (RGB) ნახატი
+Name[kk]=SGI кескіні (RGB)
+Name[km]=រូបភាព SGI (RGB)
+Name[ko]=CGI 그림 (RGB)
+Name[lb]=SGI-Bild (RGB)
+Name[lt]=SGI paveiksliukas (RGB)
+Name[lv]=SGI attēls (RGB)
+Name[mk]=SGI слика (RGB)
+Name[ms]=Imej SGI(RGB)
+Name[nb]=SGI-bilde (RGB)
+Name[nds]=SGI-Bild (RGB)
+Name[ne]=SGI छवि (RGB)
+Name[nl]=SGI-afbeelding (RGB)
+Name[nn]=SGI-bilete (RGB)
+Name[pa]=SGI ਚਿੱਤਰ (RGB)
+Name[pl]=Obrazek SGI (RGB)
+Name[pt]=Imagem SGI (RGB)
+Name[pt_BR]=Imagem SGI (RGB)
+Name[ro]=Imagine SGI (RGB)
+Name[ru]=Рисунок SGI (RGB)
+Name[rw]=SGI Ishusho (RGB)
+Name[se]=SGI-govva (RGB)
+Name[sk]=SGI obrázok (RGB)
+Name[sl]=Slika SGI (RGB)
+Name[sr]=SGI слика (RGB)
+Name[sr@Latn]=SGI slika (RGB)
+Name[sv]=SGI-bild (RGB)
+Name[ta]=GIF பிம்பம்
+Name[te]=SGI ప్రతిబింబం (RGB)
+Name[tg]=Тасвири SGI (RGB)
+Name[th]=แฟ้มภาพ SGI (RGB)
+Name[tr]=SGI Resmi (KYM)
+Name[tt]=SGI Sürät (RGB)
+Name[uk]=Зображення SGI (RGB)
+Name[uz]=SGI-rasm (RGB)
+Name[uz@cyrillic]=SGI-расм (RGB)
+Name[vi]=Ảnh SGI (RGB)
+Name[zh_CN]=SGI 图像(RGB)
+Name[zh_HK]=SGI 圖檔 (RGB)
+Name[zh_TW]=SGI 影像(RGB)
+Read=true
+Write=true
+Suffices=rgb,RGB,rgba,RGBA,bw,BW,sgi,SGI
+Mimetype=image/x-rgb
+Library=kimg_rgb.la
diff --git a/kimgio/tga.cpp b/kimgio/tga.cpp
new file mode 100644
index 000000000..2a227d077
--- /dev/null
+++ b/kimgio/tga.cpp
@@ -0,0 +1,390 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Dominik Seichter <domseichter@web.de>
+ Copyright (C) 2004 Ignacio Castao <castano@ludicon.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+/* this code supports:
+ * reading:
+ * uncompressed and run length encoded indexed, grey and color tga files.
+ * image types 1, 2, 3, 9, 10 and 11.
+ * only RGB color maps with no more than 256 colors.
+ * pixel formats 8, 15, 24 and 32.
+ * writing:
+ * uncompressed true color tga files
+ */
+
+#include "tga.h"
+
+#include <assert.h>
+
+#include <qimage.h>
+#include <qdatastream.h>
+
+#include <kdebug.h>
+
+typedef Q_UINT32 uint;
+typedef Q_UINT16 ushort;
+typedef Q_UINT8 uchar;
+
+namespace { // Private.
+
+ // Header format of saved files.
+ uchar targaMagic[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ enum TGAType {
+ TGA_TYPE_INDEXED = 1,
+ TGA_TYPE_RGB = 2,
+ TGA_TYPE_GREY = 3,
+ TGA_TYPE_RLE_INDEXED = 9,
+ TGA_TYPE_RLE_RGB = 10,
+ TGA_TYPE_RLE_GREY = 11
+ };
+
+#define TGA_INTERLEAVE_MASK 0xc0
+#define TGA_INTERLEAVE_NONE 0x00
+#define TGA_INTERLEAVE_2WAY 0x40
+#define TGA_INTERLEAVE_4WAY 0x80
+
+#define TGA_ORIGIN_MASK 0x30
+#define TGA_ORIGIN_LEFT 0x00
+#define TGA_ORIGIN_RIGHT 0x10
+#define TGA_ORIGIN_LOWER 0x00
+#define TGA_ORIGIN_UPPER 0x20
+
+ /** Tga Header. */
+ struct TgaHeader {
+ uchar id_length;
+ uchar colormap_type;
+ uchar image_type;
+ ushort colormap_index;
+ ushort colormap_length;
+ uchar colormap_size;
+ ushort x_origin;
+ ushort y_origin;
+ ushort width;
+ ushort height;
+ uchar pixel_size;
+ uchar flags;
+
+ enum { SIZE = 18 }; // const static int SIZE = 18;
+ };
+
+ static QDataStream & operator>> ( QDataStream & s, TgaHeader & head )
+ {
+ s >> head.id_length;
+ s >> head.colormap_type;
+ s >> head.image_type;
+ s >> head.colormap_index;
+ s >> head.colormap_length;
+ s >> head.colormap_size;
+ s >> head.x_origin;
+ s >> head.y_origin;
+ s >> head.width;
+ s >> head.height;
+ s >> head.pixel_size;
+ s >> head.flags;
+ return s;
+ }
+
+ static bool IsSupported( const TgaHeader & head )
+ {
+ if( head.image_type != TGA_TYPE_INDEXED &&
+ head.image_type != TGA_TYPE_RGB &&
+ head.image_type != TGA_TYPE_GREY &&
+ head.image_type != TGA_TYPE_RLE_INDEXED &&
+ head.image_type != TGA_TYPE_RLE_RGB &&
+ head.image_type != TGA_TYPE_RLE_GREY )
+ {
+ return false;
+ }
+ if( head.image_type == TGA_TYPE_INDEXED ||
+ head.image_type == TGA_TYPE_RLE_INDEXED )
+ {
+ if( head.colormap_length > 256 || head.colormap_size != 24 )
+ {
+ return false;
+ }
+ }
+ if( head.width == 0 || head.height == 0 )
+ {
+ return false;
+ }
+ if( head.pixel_size != 8 && head.pixel_size != 16 &&
+ head.pixel_size != 24 && head.pixel_size != 32 )
+ {
+ return false;
+ }
+ return true;
+ }
+
+ struct Color555 {
+ ushort b : 5;
+ ushort g : 5;
+ ushort r : 5;
+ };
+
+ struct TgaHeaderInfo {
+ bool rle;
+ bool pal;
+ bool rgb;
+ bool grey;
+ bool supported;
+
+ TgaHeaderInfo( const TgaHeader & tga ) : rle(false), pal(false), rgb(false), grey(false), supported(true)
+ {
+ switch( tga.image_type ) {
+ case TGA_TYPE_RLE_INDEXED:
+ rle = true;
+ // no break is intended!
+ case TGA_TYPE_INDEXED:
+ if( tga.colormap_type!=1 || tga.colormap_size!=24 || tga.colormap_length>256 ) {
+ supported = false;
+ }
+ pal = true;
+ break;
+
+ case TGA_TYPE_RLE_RGB:
+ rle = true;
+ // no break is intended!
+ case TGA_TYPE_RGB:
+ rgb = true;
+ break;
+
+ case TGA_TYPE_RLE_GREY:
+ rle = true;
+ // no break is intended!
+ case TGA_TYPE_GREY:
+ grey = true;
+ break;
+
+ default:
+ // Error, unknown image type.
+ supported = false;
+ }
+ }
+ };
+
+ static bool LoadTGA( QDataStream & s, const TgaHeader & tga, QImage &img )
+ {
+ // Create image.
+ if( !img.create( tga.width, tga.height, 32 )) {
+ return false;
+ }
+
+ TgaHeaderInfo info(tga);
+ if( !info.supported ) {
+ // File not supported.
+ kdDebug(399) << "This TGA file is not supported." << endl;
+ return false;
+ }
+
+ // Bits 0-3 are the numbers of alpha bits (can be zero!)
+ const int numAlphaBits = tga.flags & 0xf;
+ // However alpha exists only in the 32 bit format.
+ if( ( tga.pixel_size == 32 ) && ( tga.flags & 0xf ) ) {
+ img.setAlphaBuffer( true );
+ }
+
+ uint pixel_size = (tga.pixel_size/8);
+ uint size = tga.width * tga.height * pixel_size;
+
+ if (size < 1)
+ {
+ kdDebug(399) << "This TGA file is broken with size " << size << endl;
+ return false;
+ }
+
+
+ // Read palette.
+ char palette[768];
+ if( info.pal ) {
+ // @todo Support palettes in other formats!
+ s.readRawBytes( palette, 3 * tga.colormap_length );
+ }
+
+ // Allocate image.
+ uchar * const image = new uchar[size];
+
+ if( info.rle ) {
+ // Decode image.
+ char * dst = (char *)image;
+ int num = size;
+
+ while (num > 0) {
+ // Get packet header.
+ uchar c;
+ s >> c;
+
+ uint count = (c & 0x7f) + 1;
+ num -= count * pixel_size;
+
+ if (c & 0x80) {
+ // RLE pixels.
+ assert(pixel_size <= 8);
+ char pixel[8];
+ s.readRawBytes( pixel, pixel_size );
+ do {
+ memcpy(dst, pixel, pixel_size);
+ dst += pixel_size;
+ } while (--count);
+ }
+ else {
+ // Raw pixels.
+ count *= pixel_size;
+ s.readRawBytes( dst, count );
+ dst += count;
+ }
+ }
+ }
+ else {
+ // Read raw image.
+ s.readRawBytes( (char *)image, size );
+ }
+
+ // Convert image to internal format.
+ int y_start, y_step, y_end;
+ if( tga.flags & TGA_ORIGIN_UPPER ) {
+ y_start = 0;
+ y_step = 1;
+ y_end = tga.height;
+ }
+ else {
+ y_start = tga.height - 1;
+ y_step = -1;
+ y_end = -1;
+ }
+
+ uchar * src = image;
+
+ for( int y = y_start; y != y_end; y += y_step ) {
+ QRgb * scanline = (QRgb *) img.scanLine( y );
+
+ if( info.pal ) {
+ // Paletted.
+ for( int x = 0; x < tga.width; x++ ) {
+ uchar idx = *src++;
+ scanline[x] = qRgb( palette[3*idx+2], palette[3*idx+1], palette[3*idx+0] );
+ }
+ }
+ else if( info.grey ) {
+ // Greyscale.
+ for( int x = 0; x < tga.width; x++ ) {
+ scanline[x] = qRgb( *src, *src, *src );
+ src++;
+ }
+ }
+ else {
+ // True Color.
+ if( tga.pixel_size == 16 ) {
+ for( int x = 0; x < tga.width; x++ ) {
+ Color555 c = *reinterpret_cast<Color555 *>(src);
+ scanline[x] = qRgb( (c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2) );
+ src += 2;
+ }
+ }
+ else if( tga.pixel_size == 24 ) {
+ for( int x = 0; x < tga.width; x++ ) {
+ scanline[x] = qRgb( src[2], src[1], src[0] );
+ src += 3;
+ }
+ }
+ else if( tga.pixel_size == 32 ) {
+ for( int x = 0; x < tga.width; x++ ) {
+ // ### TODO: verify with images having really some alpha data
+ const uchar alpha = ( src[3] << ( 8 - numAlphaBits ) );
+ scanline[x] = qRgba( src[2], src[1], src[0], alpha );
+ src += 4;
+ }
+ }
+ }
+ }
+
+ // Free image.
+ delete [] image;
+
+ return true;
+ }
+
+} // namespace
+
+
+KDE_EXPORT void kimgio_tga_read( QImageIO *io )
+{
+ //kdDebug(399) << "Loading TGA file!" << endl;
+
+ QDataStream s( io->ioDevice() );
+ s.setByteOrder( QDataStream::LittleEndian );
+
+
+ // Read image header.
+ TgaHeader tga;
+ s >> tga;
+ s.device()->at( TgaHeader::SIZE + tga.id_length );
+
+ // Check image file format.
+ if( s.atEnd() ) {
+ kdDebug(399) << "This TGA file is not valid." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+ // Check supported file types.
+ if( !IsSupported(tga) ) {
+ kdDebug(399) << "This TGA file is not supported." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+
+ QImage img;
+ bool result = LoadTGA(s, tga, img);
+
+ if( result == false ) {
+ kdDebug(399) << "Error loading TGA file." << endl;
+ io->setImage( 0 );
+ io->setStatus( -1 );
+ return;
+ }
+
+
+ io->setImage( img );
+ io->setStatus( 0 );
+}
+
+
+KDE_EXPORT void kimgio_tga_write( QImageIO *io )
+{
+ QDataStream s( io->ioDevice() );
+ s.setByteOrder( QDataStream::LittleEndian );
+
+ const QImage img = io->image();
+ const bool hasAlpha = img.hasAlphaBuffer();
+ for( int i = 0; i < 12; i++ )
+ s << targaMagic[i];
+
+ // write header
+ s << Q_UINT16( img.width() ); // width
+ s << Q_UINT16( img.height() ); // height
+ s << Q_UINT8( hasAlpha ? 32 : 24 ); // depth (24 bit RGB + 8 bit alpha)
+ s << Q_UINT8( hasAlpha ? 0x24 : 0x20 ); // top left image (0x20) + 8 bit alpha (0x4)
+
+ for( int y = 0; y < img.height(); y++ )
+ for( int x = 0; x < img.width(); x++ ) {
+ const QRgb color = img.pixel( x, y );
+ s << Q_UINT8( qBlue( color ) );
+ s << Q_UINT8( qGreen( color ) );
+ s << Q_UINT8( qRed( color ) );
+ if( hasAlpha )
+ s << Q_UINT8( qAlpha( color ) );
+ }
+
+ io->setStatus( 0 );
+}
+
diff --git a/kimgio/tga.h b/kimgio/tga.h
new file mode 100644
index 000000000..85ce1f69f
--- /dev/null
+++ b/kimgio/tga.h
@@ -0,0 +1,21 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Dominik Seichter <domseichter@web.de>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Lesser GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+*/
+
+#ifndef KIMG_TGA_H
+#define KIMG_TGA_H
+
+class QImageIO;
+
+extern "C" {
+void kimgio_tga_read( QImageIO * );
+void kimgio_tga_write( QImageIO * );
+}
+
+#endif
+
diff --git a/kimgio/tga.kimgio b/kimgio/tga.kimgio
new file mode 100644
index 000000000..5f6c23b30
--- /dev/null
+++ b/kimgio/tga.kimgio
@@ -0,0 +1,86 @@
+[Image Format]
+Type=TGA
+Header=^.\x01[\x01-\x03\x09-\x0b]\x01{3}.[\x01\x18]
+Name=Truevision Targa Image
+Name[af]='Truevision Targa' Beeld
+Name[ar]=صورة Truevision Targa
+Name[az]=Truevision Targa Rəsmi
+Name[be]=Відарыс Truevision Targa
+Name[bg]=Truevision Targa изображение
+Name[bn]=ট্রুভিশন টার্গা চিত্র
+Name[br]=Skeudenn Truevision Targa
+Name[bs]=Truevision Targa slika
+Name[ca]=Imatge Truevision de Targa
+Name[cs]=Obrázek ve formátu Truevision Targa
+Name[csb]=Òbrôzk Truevision Targa
+Name[cy]=Delwedd Truevision Targa
+Name[da]=Truevision Targa billede
+Name[de]=Truevision-Targa-Bild
+Name[el]=Εικόνα Truevision Targa
+Name[eo]=TARGA-bildo
+Name[es]=Imagen de visión verdadera Targa
+Name[et]=Truevision Targa pildifail
+Name[eu]=Truevision Targa irudia
+Name[fa]=تصویر پندار صحیح Targa
+Name[fi]=Truevision Targa -kuva
+Name[fr]=Image Targa Truevision
+Name[fy]=Truevision Targa ôfbylding
+Name[ga]=Íomhá Truevision Targa
+Name[gl]=Imaxe Targa Truevisión
+Name[he]=תמונת Truevision Targa
+Name[hi]=ट्रूविजन टाग्रा छवि
+Name[hr]=Truevision Targa slika
+Name[hu]=Truevision Targa-kép
+Name[id]=Gambar Truevision Targa
+Name[is]=Truevision Targa mynd
+Name[it]=Immagine Truevision Targa
+Name[ja]=トゥルービジョン Targa 画像
+Name[ka]=Truevision Targa ნახატი
+Name[kk]=Truevision Targa кескіні
+Name[km]=រូបភាព Truevision Targa
+Name[ko]=트루비전 타가 그림
+Name[lb]=Truevision-Targa-Bild
+Name[lt]=Truevision Targa paveiksliukas
+Name[lv]=Truevision Targa attēls
+Name[mk]=Truevision Targa слика
+Name[mn]=Truevision Targa Зураг
+Name[ms]=Imej Truevision Targa
+Name[nb]=Truevision Targa-bilde
+Name[nds]="Truevision Targa"-Bild
+Name[ne]=ट्रुभिजन टार्गा छवि
+Name[nl]=Truevision Targa-afbeelding
+Name[nn]=Truevision Targa-bilete
+Name[pa]=Truevision Targa ਚਿੱਤਰ
+Name[pl]=Obrazek Truevision Targa
+Name[pt]=Imagem Truevision Targa
+Name[pt_BR]=Imagem Truevision Targa
+Name[ro]=Imagine Targa Truevision
+Name[ru]=Рисунок Truevision Targa
+Name[rw]=Truevision Targa Ishusho
+Name[se]=Truevision Targa-govva
+Name[sk]=Truevision Targa obrázok
+Name[sl]=Slika Truevision Targa
+Name[sq]=Imazh Truevision Targa
+Name[sr]=Truevision Targa слика
+Name[sr@Latn]=Truevision Targa slika
+Name[sv]=Truevision Targa-bild
+Name[ta]=சரியானபார்வை டார்கா பிம்பம்
+Name[te]=ట్రూవిజన్ టార్గా ప్రతిబింబం
+Name[tg]=Тасвири Truevision Targa
+Name[th]=แฟ้มภาพทรุวิชัน Targa
+Name[tr]=Truevision Targa Resim Dosyası
+Name[tt]=Truevision Targa Süräte
+Name[uk]=Зображення Truevision Targa
+Name[uz]=TGA-rasm
+Name[uz@cyrillic]=TGA-расм
+Name[vi]=Ảnh Truevision Targa
+Name[wa]=Imådje Targa
+Name[zh_CN]=Truevision Targa 图像
+Name[zh_HK]=Truevision Targa 圖形
+Name[zh_TW]=Truevision Targa 圖形
+Read=true
+Write=true
+Suffices=tga,TGA
+Mimetype=image/x-targa
+Library=kimg_tga.la
+
diff --git a/kimgio/tiff.kimgio b/kimgio/tiff.kimgio
new file mode 100644
index 000000000..ec9b18d5f
--- /dev/null
+++ b/kimgio/tiff.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=TIFF
+Header=[MI][MI]
+Name=TIFF Image
+Name[af]=TIFF Beeld
+Name[ar]=صورة من نوع TIFF
+Name[az]=TIFF Rəsmi
+Name[be]=Малюнак TIFF
+Name[bg]=TIFF изображение
+Name[bn]=টিফ চিত্র
+Name[br]=Skeudenn TIFF
+Name[bs]=TIFF slika
+Name[ca]=Imatge TIFF
+Name[cs]=Obrázek ve formátu TIFF
+Name[csb]=Òbrôzk TIFF
+Name[cy]=Delwedd TIFF
+Name[da]=TIFF-billede
+Name[de]=TIFF-Bild
+Name[el]=Εικόνα TIFF
+Name[eo]=TIFF bildo
+Name[es]=Imagen TIFF
+Name[et]=TIFF pildifail
+Name[eu]=TIFF irudia
+Name[fa]=تصویر TIFF
+Name[fi]=TIFF-kuva
+Name[fr]=Image TIFF
+Name[fy]=TIFF ôfbylding
+Name[ga]=Íomhá TIFF
+Name[gl]=Imaxe TIFF
+Name[he]=תמונת TIFF
+Name[hi]=TIFF छवि
+Name[hr]=TIFF slika
+Name[hu]=TIFF-kép
+Name[id]=Gambar TIFF
+Name[is]=TIFF mynd
+Name[it]=Immagine TIFF
+Name[ja]=TIFF 画像
+Name[ka]=TIFF ნახატი
+Name[kk]=TIFF кескіні
+Name[km]=រូបភាព TIFF
+Name[ko]=TIFF 그림
+Name[lb]=TIFF-Bild
+Name[lt]=TIFF paveiksliukas
+Name[lv]=TIFF attēls
+Name[mk]=TIFF слика
+Name[mn]=TIFF-Зураг
+Name[ms]=Imej TIFF
+Name[nb]=TIFF-bilde
+Name[nds]=TIFF-Bild
+Name[ne]=TIFF छवि
+Name[nl]=TIFF-afbeelding
+Name[nn]=TIFF-bilete
+Name[pa]=TIFF ਚਿੱਤਰ
+Name[pl]=Obrazek TIFF
+Name[pt]=Imagem TIFF
+Name[pt_BR]=Imagem TIFF
+Name[ro]=Imagine TIFF
+Name[ru]=Рисунок TIFF
+Name[rw]=TIFF Ishusho
+Name[se]=TIFF-govva
+Name[sk]=TIFF obrázok
+Name[sl]=Slika TIFF
+Name[sq]=Imazh TIFF
+Name[sr]=TIFF слика
+Name[sr@Latn]=TIFF slika
+Name[sv]=Tiff-bild
+Name[ta]=TIFF பிம்பம்
+Name[te]=TIFF ప్రతిబింబం
+Name[tg]=Тасвири TIFF
+Name[th]=แฟ้มภาพ TIFF
+Name[tr]=TIFF Resim Dosyası
+Name[tt]=TIFF Sürät
+Name[uk]=Зображення TIFF
+Name[uz]=TIFF-rasm
+Name[uz@cyrillic]=TIFF-расм
+Name[vi]=Ảnh TIFF
+Name[wa]=Imådje TIFF
+Name[zh_CN]=TIFF 图像
+Name[zh_HK]=TIFF 圖檔
+Name[zh_TW]=TIFF 影像
+Read=true
+Write=false
+Suffices=tiff,TIFF,tif,TIF
+Mimetype=image/tiff
+Library=kimg_tiff.la
diff --git a/kimgio/tiffr.cpp b/kimgio/tiffr.cpp
new file mode 100644
index 000000000..bda4ce354
--- /dev/null
+++ b/kimgio/tiffr.cpp
@@ -0,0 +1,151 @@
+// This library is distributed under the conditions of the GNU LGPL.
+
+#include "config.h"
+
+#ifdef HAVE_LIBTIFF
+
+#include <tiffio.h>
+
+#include <qimage.h>
+#include <qfile.h>
+#include <kdelibs_export.h>
+
+#include <assert.h>
+
+#include "tiffr.h"
+
+static tsize_t tiff_read( thandle_t handle, tdata_t buf, tsize_t size )
+{
+ QIODevice *dev = reinterpret_cast<QIODevice *>( handle );
+ return dev->readBlock( reinterpret_cast<char *>( buf ), size );
+}
+
+static tsize_t tiff_write( thandle_t, tdata_t, tsize_t )
+{
+ return 0;
+}
+
+static toff_t tiff_seek( thandle_t handle, toff_t off, int whence )
+{
+ QIODevice *dev = reinterpret_cast<QIODevice *>( handle );
+
+ if ( whence == SEEK_CUR )
+ off += dev->at();
+ else if ( whence == SEEK_END )
+ off += dev->size();
+
+ if ( !dev->at( off ) )
+ return ( toff_t )-1;
+
+ return dev->at();
+}
+
+static toff_t tiff_size( thandle_t handle )
+{
+ QIODevice *dev = reinterpret_cast<QIODevice *>( handle );
+ return dev->size();
+}
+
+static int tiff_close( thandle_t )
+{
+ return 0;
+}
+
+static int tiff_map( thandle_t, tdata_t *, toff_t * )
+{
+ return 0;
+}
+
+static void tiff_unmap( thandle_t, tdata_t, toff_t )
+{
+}
+
+KDE_EXPORT void kimgio_tiff_read( QImageIO *io )
+{
+ TIFF *tiff;
+ uint32 width, height;
+ uint32 *data;
+
+ // FIXME: use qdatastream
+
+ // open file
+ tiff = TIFFClientOpen( QFile::encodeName( io->fileName() ), "r",
+ ( thandle_t )io->ioDevice(),
+ tiff_read, tiff_write, tiff_seek, tiff_close,
+ tiff_size, tiff_map, tiff_unmap );
+
+ if( tiff == 0 ) {
+ return;
+ }
+
+ // create image with loaded dimensions
+ if( TIFFGetField( tiff, TIFFTAG_IMAGEWIDTH, &width ) != 1
+ || TIFFGetField( tiff, TIFFTAG_IMAGELENGTH, &height ) != 1 )
+ return;
+
+ QImage image( width, height, 32 );
+ if( image.isNull()) {
+ TIFFClose( tiff );
+ return;
+ }
+ data = (uint32 *)image.bits();
+
+ //Sven: changed to %ld for 64bit machines
+ //debug( "unsigned size: %ld, uint32 size: %ld",
+ // (long)sizeof(unsigned), (long)sizeof(uint32) );
+
+ // read data
+ bool stat =TIFFReadRGBAImage( tiff, width, height, data );
+
+ if( stat == 0 ) {
+ TIFFClose( tiff );
+ return;
+ }
+
+ // reverse red and blue
+ for( unsigned i = 0; i < width * height; ++i )
+ {
+ uint32 red = ( 0x00FF0000 & data[i] ) >> 16;
+ uint32 blue = ( 0x000000FF & data[i] ) << 16;
+ data[i] &= 0xFF00FF00;
+ data[i] += red + blue;
+ }
+
+ // reverse image (it's upside down)
+ for( unsigned ctr = 0; ctr < (height>>1); ) {
+ unsigned *line1 = (unsigned *)image.scanLine( ctr );
+ unsigned *line2 = (unsigned *)image.scanLine( height
+ - ( ++ctr ) );
+
+ for( unsigned x = 0; x < width; x++ ) {
+ int temp = *line1;
+ *line1 = *line2;
+ *line2 = temp;
+ line1++;
+ line2++;
+ }
+
+ // swap rows
+ }
+
+ // set channel order to Qt order
+ // FIXME: Right now they are the same, but will it change?
+
+// for( int ctr = (image.numBytes() / sizeof(uint32))+1; ctr ; ctr-- ) {
+// // TODO: manage alpha with TIFFGetA
+// *data = qRgb( TIFFGetR( *data ),
+// TIFFGetG( *data ), TIFFGetB( *data ) );
+// data++;
+// }
+ TIFFClose( tiff );
+
+ io->setImage( image );
+ io->setStatus ( 0 );
+}
+
+KDE_EXPORT void kimgio_tiff_write( QImageIO * )
+{
+ // TODO: stub
+}
+
+#endif
diff --git a/kimgio/tiffr.h b/kimgio/tiffr.h
new file mode 100644
index 000000000..18d39f9bb
--- /dev/null
+++ b/kimgio/tiffr.h
@@ -0,0 +1,20 @@
+/**
+* QImageIO Routines to read/write TIFF images.
+* Sirtaj Singh Kang, Oct 1998.
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*
+* $Id$
+*/
+
+#ifndef KIMG_TIFFR_H
+#define KIMG_TIFFR_H
+
+class QImageIO;
+
+extern "C" {
+void kimgio_tiff_read( QImageIO *io );
+void kimgio_tiff_write( QImageIO *io );
+}
+
+#endif
diff --git a/kimgio/xbm.kimgio b/kimgio/xbm.kimgio
new file mode 100644
index 000000000..17f3e75d4
--- /dev/null
+++ b/kimgio/xbm.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=XBM
+Header=
+Name=X BitMap Image
+Name[af]='X BitMap' Beeld
+Name[ar]=صورة من نوع XBitMap
+Name[az]=X BitMap Rəsmi
+Name[be]=Малюнак "X BitMap"
+Name[bg]=X BitMap изображение
+Name[bn]=এক্স বিটম্যাপ চিত্র
+Name[br]=Skeudenn X BitMap
+Name[bs]=X BitMap slika
+Name[ca]=Imatge mapa de bits de X
+Name[cs]=Obrázek ve formátu X Bitmap
+Name[csb]=Òbrôzk X Bitmap
+Name[cy]=Delwedd Didfap X
+Name[da]=X BitMap-billede
+Name[de]=X-BitMap-Bild
+Name[el]=Εικόνα X BitMap
+Name[eo]=Xa grafiko
+Name[es]=Imagen mapa de bits de X
+Name[et]=X bitmap pildifail
+Name[eu]=X BitMap irudia
+Name[fa]=تصویر نگاشت بیتی X
+Name[fi]=X-bittikarttakuva
+Name[fr]=Image X Bitmap
+Name[fy]=X BitMap ôfbylding
+Name[ga]=Íomhá "X BitMap"
+Name[gl]=Imaxe X BitMap
+Name[he]=תמונת מפת סיביות של X
+Name[hi]=X BitMap छवि
+Name[hr]=X BitMap slika
+Name[hu]=XBM-kép
+Name[id]=Gambar X BitMap
+Name[is]=X BitMap mynd
+Name[it]=Immagine X BitMap
+Name[ja]=X ビットマップ画像
+Name[ka]=X BitMap ნახატი
+Name[kk]=X BitMap кескіні
+Name[km]=រូបភាព X BitMap
+Name[ko]=X용 비트맵 그림
+Name[lb]=X-BitMap-Bild
+Name[lt]=X BitMap paveiksliukas
+Name[lv]=X BitMap attēls
+Name[mk]=X BitMap слика
+Name[mn]=X Bitmap зураг
+Name[ms]=Imej X BitMap
+Name[nb]=X BitMap-bilde
+Name[nds]=X-Bitmap-Bild
+Name[ne]=X बिटम्याप छवि
+Name[nl]=X Bitmap-afbeelding
+Name[nn]=X BitMap-bilete
+Name[pa]=X BitMap ਚਿੱਤਰ
+Name[pl]=Obrazek X Bitmap
+Name[pt]=Imagem X BitMap
+Name[pt_BR]=Imagem Bitmap X
+Name[ro]=Imagine XBM
+Name[ru]=Рисунок X BitMap
+Name[rw]=X BitMap Ishusho
+Name[se]=X BitMap-govva
+Name[sk]=X BitMap obrázok
+Name[sl]=Bitna slika za X
+Name[sq]=Imazh X BitMap
+Name[sr]=X битмапирана слика
+Name[sr@Latn]=X bitmapirana slika
+Name[sv]=X bitmapp-bild
+Name[ta]=X பிட்வரைப்பட பிம்பம்
+Name[te]=X బిట్ మేప్ ప్రతిబింబం
+Name[tg]=Тасвири X BitMap
+Name[th]=แฟ้มภาพบิตแมพ X
+Name[tr]=X BitMap Resim Dosyası
+Name[tt]=X BitMap Sürät
+Name[uk]=Двокольорове зображення для X
+Name[uz]=X BMP-rasm
+Name[uz@cyrillic]=X BMP-расм
+Name[vi]=Ảnh sơ đồ bit X
+Name[wa]=Imådje XBM
+Name[zh_CN]=X 位图图像
+Name[zh_HK]=X BitMap 圖檔
+Name[zh_TW]=X 點陣影像
+Read=true
+Write=true
+Suffices=xbm,XBM
+Mimetype=image/x-xbm
+Library=
diff --git a/kimgio/xcf.cpp b/kimgio/xcf.cpp
new file mode 100644
index 000000000..c7164d7ba
--- /dev/null
+++ b/kimgio/xcf.cpp
@@ -0,0 +1,2116 @@
+/*
+ * qxcfi.cpp: A Qt 3 plug-in for reading GIMP XCF image files
+ * Copyright (C) 2001 lignum Computing, Inc. <allen@lignumcomputing.com>
+ * Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
+ *
+ * This plug-in is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <qimage.h>
+#include <qiodevice.h>
+#include <qvaluestack.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+#include "xcf.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+KDE_EXPORT void kimgio_xcf_read(QImageIO *io)
+{
+ XCFImageFormat xcfif;
+ xcfif.readXCF(io);
+}
+
+
+KDE_EXPORT void kimgio_xcf_write(QImageIO *io)
+{
+ kdDebug(399) << "XCF: write support not implemented" << endl;
+ io->setStatus(-1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+int XCFImageFormat::random_table[RANDOM_TABLE_SIZE];
+
+//int XCFImageFormat::add_lut[256][256];
+
+
+const XCFImageFormat::LayerModes XCFImageFormat::layer_modes[] = {
+ {true}, // NORMAL_MODE
+ {true}, // DISSOLVE_MODE
+ {true}, // BEHIND_MODE
+ {false}, // MULTIPLY_MODE
+ {false}, // SCREEN_MODE
+ {false}, // OVERLAY_MODE
+ {false}, // DIFFERENCE_MODE
+ {false}, // ADDITION_MODE
+ {false}, // SUBTRACT_MODE
+ {false}, // DARKEN_ONLY_MODE
+ {false}, // LIGHTEN_ONLY_MODE
+ {false}, // HUE_MODE
+ {false}, // SATURATION_MODE
+ {false}, // COLOR_MODE
+ {false}, // VALUE_MODE
+ {false}, // DIVIDE_MODE
+ {true}, // ERASE_MODE
+ {true}, // REPLACE_MODE
+ {true}, // ANTI_ERASE_MODE
+};
+
+
+//! Change a QRgb value's alpha only.
+inline QRgb qRgba ( QRgb rgb, int a )
+{
+ return ((a & 0xff) << 24 | (rgb & RGB_MASK));
+}
+
+
+/*!
+ * The constructor for the XCF image loader. This initializes the
+ * tables used in the layer merging routines.
+ */
+XCFImageFormat::XCFImageFormat()
+{
+ // From GIMP "paint_funcs.c" v1.2
+ srand(RANDOM_SEED);
+
+ for (int i = 0; i < RANDOM_TABLE_SIZE; i++)
+ random_table[i] = rand();
+
+ for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
+ int tmp;
+ int swap = i + rand() % (RANDOM_TABLE_SIZE - i);
+ tmp = random_table[i];
+ random_table[i] = random_table[swap];
+ random_table[swap] = tmp;
+ }
+
+// for (int j = 0; j < 256; j++) {
+// for (int k = 0; k < 256; k++) {
+// int tmp_sum = j + k;
+// if (tmp_sum > 255)
+// tmp_sum = 255;
+// add_lut[j][k] = tmp_sum;
+// }
+// }
+}
+
+inline
+int XCFImageFormat::add_lut( int a, int b ) {
+ return QMIN( a + b, 255 );
+}
+
+void XCFImageFormat::readXCF(QImageIO *io)
+{
+ XCFImage xcf_image;
+ QDataStream xcf_io(io->ioDevice());
+
+ char tag[14];
+ xcf_io.readRawBytes(tag, sizeof(tag));
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on header tag" << endl;
+ return;
+ }
+
+ xcf_io >> xcf_image.width >> xcf_image.height >> xcf_image.type;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on image info" << endl;
+ return;
+ }
+
+kdDebug() << tag << " " << xcf_image.width << " " << xcf_image.height << " " << xcf_image.type << endl;
+ if (!loadImageProperties(xcf_io, xcf_image))
+ return;
+
+ // The layers appear to be stored in top-to-bottom order. This is
+ // the reverse of how a merged image must be computed. So, the layer
+ // offsets are pushed onto a LIFO stack (thus, we don't have to load
+ // all the data of all layers before beginning to construct the
+ // merged image).
+
+ QValueStack<Q_INT32> layer_offsets;
+
+ while (true) {
+ Q_INT32 layer_offset;
+
+ xcf_io >> layer_offset;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer offsets" << endl;
+ return;
+ }
+
+ if (layer_offset == 0)
+ break;
+
+ layer_offsets.push(layer_offset);
+ }
+
+ xcf_image.num_layers = layer_offsets.size();
+
+ if (layer_offsets.size() == 0) {
+ kdDebug(399) << "XCF: no layers!" << endl;
+ return;
+ }
+
+ // Load each layer and add it to the image
+ while (!layer_offsets.isEmpty()) {
+ Q_INT32 layer_offset = layer_offsets.pop();
+
+ xcf_io.device()->at(layer_offset);
+
+ if (!loadLayer(xcf_io, xcf_image))
+ return;
+ }
+
+ if (!xcf_image.initialized) {
+ kdDebug(399) << "XCF: no visible layers!" << endl;
+ return;
+ }
+
+ io->setImage(xcf_image.image);
+ io->setStatus(0);
+}
+
+
+/*!
+ * An XCF file can contain an arbitrary number of properties associated
+ * with the image (and layer and mask).
+ * \param xcf_io the data stream connected to the XCF image
+ * \param xcf_image XCF image data.
+ * \return true if there were no I/O errors.
+ */
+bool XCFImageFormat::loadImageProperties(QDataStream& xcf_io, XCFImage& xcf_image)
+{
+ while (true) {
+ PropType type;
+ QByteArray bytes;
+
+ if (!loadProperty(xcf_io, type, bytes)) {
+ kdDebug(399) << "XCF: error loading global image properties" << endl;
+ return false;
+ }
+
+ QDataStream property(bytes, IO_ReadOnly);
+
+ switch (type) {
+ case PROP_END:
+ return true;
+
+ case PROP_COMPRESSION:
+ property >> xcf_image.compression;
+ break;
+
+ case PROP_RESOLUTION:
+ property >> xcf_image.x_resolution >> xcf_image.y_resolution;
+ break;
+
+ case PROP_TATTOO:
+ property >> xcf_image.tattoo;
+ break;
+
+ case PROP_PARASITES:
+ while (!property.atEnd()) {
+ char* tag;
+ Q_UINT32 size;
+
+ property.readBytes(tag, size);
+
+ Q_UINT32 flags;
+ char* data=0;
+ property >> flags >> data;
+
+ if (tag && strncmp(tag, "gimp-comment", strlen("gimp-comment")) == 0)
+ xcf_image.image.setText("Comment", 0, data);
+
+ delete[] tag;
+ delete[] data;
+ }
+ break;
+
+ case PROP_UNIT:
+ property >> xcf_image.unit;
+ break;
+
+ case PROP_PATHS: // This property is ignored.
+ break;
+
+ case PROP_USER_UNIT: // This property is ignored.
+ break;
+
+ case PROP_COLORMAP:
+ property >> xcf_image.num_colors;
+ if(xcf_image.num_colors < 0 || xcf_image.num_colors > 65535)
+ return false;
+
+ xcf_image.palette.reserve(xcf_image.num_colors);
+
+ for (int i = 0; i < xcf_image.num_colors; i++) {
+ uchar r, g, b;
+ property >> r >> g >> b;
+ xcf_image.palette.push_back( qRgb(r,g,b) );
+ }
+ break;
+
+ default:
+ kdDebug(399) << "XCF: unimplemented image property" << type
+ << ", size " << bytes.size() << endl;
+ }
+ }
+}
+
+
+/*!
+ * Read a single property from the image file. The property type is returned
+ * in type and the data is returned in bytes.
+ * \param xcf the image file data stream.
+ * \param type returns with the property type.
+ * \param bytes returns with the property data.
+ * \return true if there were no IO errors. */
+bool XCFImageFormat::loadProperty(QDataStream& xcf_io, PropType& type, QByteArray& bytes)
+{
+ Q_UINT32 foo;
+ xcf_io >> foo;
+ type=PropType(foo); // TODO urks
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on property type" << type << endl;
+ return false;
+ }
+
+ char* data;
+ Q_UINT32 size;
+
+ // The colormap property size is not the correct number of bytes:
+ // The GIMP source xcf.c has size = 4 + ncolors, but it should be
+ // 4 + 3 * ncolors
+
+ if (type == PROP_COLORMAP) {
+ xcf_io >> size;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on property " << type << " size" << endl;
+ return false;
+ }
+
+ if(size > 65535 || size < 4)
+ return false;
+
+ size = 3 * (size - 4) + 4;
+ data = new char[size];
+
+ xcf_io.readRawBytes(data, size);
+ } else if (type == PROP_USER_UNIT) {
+ // The USER UNIT property size is not correct. I'm not sure why, though.
+ float factor;
+ Q_INT32 digits;
+ char* unit_strings;
+
+ xcf_io >> size >> factor >> digits;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on property " << type << endl;
+ return false;
+ }
+
+ for (int i = 0; i < 5; i++) {
+ xcf_io >> unit_strings;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on property " << type << endl;
+ return false;
+ }
+
+ delete[] unit_strings;
+ }
+
+ size = 0;
+ } else {
+ xcf_io >> size;
+ if(size >256000)
+ return false;
+ data = new char[size];
+ xcf_io.readRawBytes(data, size);
+ }
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on property " << type << " data, size " << size << endl;
+ return false;
+ }
+
+ if (size != 0 && data) {
+ bytes.assign(data,size);
+ }
+
+ return true;
+}
+
+
+/*!
+ * Load a layer from the XCF file. The data stream must be positioned at
+ * the beginning of the layer data.
+ * \param xcf_io the image file data stream.
+ * \param xcf_image contains the layer and the color table
+ * (if the image is indexed).
+ * \return true if there were no I/O errors.
+ */
+bool XCFImageFormat::loadLayer(QDataStream& xcf_io, XCFImage& xcf_image)
+{
+ Layer& layer(xcf_image.layer);
+ delete[] layer.name;
+
+ xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer" << endl;
+ return false;
+ }
+
+ if (!loadLayerProperties(xcf_io, layer))
+ return false;
+#if 0
+ cout << "layer: \"" << layer.name << "\", size: " << layer.width << " x "
+ << layer.height << ", type: " << layer.type << ", mode: " << layer.mode
+ << ", opacity: " << layer.opacity << ", visible: " << layer.visible
+ << ", offset: " << layer.x_offset << ", " << layer.y_offset << endl;
+#endif
+ // Skip reading the rest of it if it is not visible. Typically, when
+ // you export an image from the The GIMP it flattens (or merges) only
+ // the visible layers into the output image.
+
+ if (layer.visible == 0)
+ return true;
+
+ // If there are any more layers, merge them into the final QImage.
+
+ xcf_io >> layer.hierarchy_offset >> layer.mask_offset;
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer image offsets" << endl;
+ return false;
+ }
+
+ // Allocate the individual tile QImages based on the size and type
+ // of this layer.
+
+ if( !composeTiles(xcf_image))
+ return false;
+ xcf_io.device()->at(layer.hierarchy_offset);
+
+ // As tiles are loaded, they are copied into the layers tiles by
+ // this routine. (loadMask(), below, uses a slightly different
+ // version of assignBytes().)
+
+ layer.assignBytes = assignImageBytes;
+
+ if (!loadHierarchy(xcf_io, layer))
+ return false;
+
+ if (layer.mask_offset != 0) {
+ xcf_io.device()->at(layer.mask_offset);
+
+ if (!loadMask(xcf_io, layer))
+ return false;
+ }
+
+ // Now we should have enough information to initialize the final
+ // QImage. The first visible layer determines the attributes
+ // of the QImage.
+
+ if (!xcf_image.initialized) {
+ if( !initializeImage(xcf_image))
+ return false;
+ copyLayerToImage(xcf_image);
+ xcf_image.initialized = true;
+ } else
+ mergeLayerIntoImage(xcf_image);
+
+ return true;
+}
+
+
+/*!
+ * An XCF file can contain an arbitrary number of properties associated
+ * with a layer.
+ * \param xcf_io the data stream connected to the XCF image.
+ * \param layer layer to collect the properties.
+ * \return true if there were no I/O errors.
+ */
+bool XCFImageFormat::loadLayerProperties(QDataStream& xcf_io, Layer& layer)
+{
+ while (true) {
+ PropType type;
+ QByteArray bytes;
+
+ if (!loadProperty(xcf_io, type, bytes)) {
+ kdDebug(399) << "XCF: error loading layer properties" << endl;
+ return false;
+ }
+
+ QDataStream property(bytes, IO_ReadOnly);
+
+ switch (type) {
+ case PROP_END:
+ return true;
+
+ case PROP_ACTIVE_LAYER:
+ layer.active = true;
+ break;
+
+ case PROP_OPACITY:
+ property >> layer.opacity;
+ break;
+
+ case PROP_VISIBLE:
+ property >> layer.visible;
+ break;
+
+ case PROP_LINKED:
+ property >> layer.linked;
+ break;
+
+ case PROP_PRESERVE_TRANSPARENCY:
+ property >> layer.preserve_transparency;
+ break;
+
+ case PROP_APPLY_MASK:
+ property >> layer.apply_mask;
+ break;
+
+ case PROP_EDIT_MASK:
+ property >> layer.edit_mask;
+ break;
+
+ case PROP_SHOW_MASK:
+ property >> layer.show_mask;
+ break;
+
+ case PROP_OFFSETS:
+ property >> layer.x_offset >> layer.y_offset;
+ break;
+
+ case PROP_MODE:
+ property >> layer.mode;
+ break;
+
+ case PROP_TATTOO:
+ property >> layer.tattoo;
+ break;
+
+ default:
+ kdDebug(399) << "XCF: unimplemented layer property " << type
+ << ", size " << bytes.size() << endl;
+ }
+ }
+}
+
+
+/*!
+ * Compute the number of tiles in the current layer and allocate
+ * QImage structures for each of them.
+ * \param xcf_image contains the current layer.
+ */
+bool XCFImageFormat::composeTiles(XCFImage& xcf_image)
+{
+ Layer& layer(xcf_image.layer);
+
+ layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
+ layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
+
+ layer.image_tiles.resize(layer.nrows);
+
+ if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
+ layer.alpha_tiles.resize(layer.nrows);
+
+ if (layer.mask_offset != 0)
+ layer.mask_tiles.resize(layer.nrows);
+
+ for (uint j = 0; j < layer.nrows; j++) {
+ layer.image_tiles[j].resize(layer.ncols);
+
+ if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
+ layer.alpha_tiles[j].resize(layer.ncols);
+
+ if (layer.mask_offset != 0)
+ layer.mask_tiles[j].resize(layer.ncols);
+ }
+
+ for (uint j = 0; j < layer.nrows; j++) {
+ for (uint i = 0; i < layer.ncols; i++) {
+
+ uint tile_width = (i + 1) * TILE_WIDTH <= layer.width
+ ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
+
+ uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height
+ ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
+
+ // Try to create the most appropriate QImage (each GIMP layer
+ // type is treated slightly differently)
+
+ switch (layer.type) {
+ case RGB_GIMAGE:
+ layer.image_tiles[j][i] = QImage(tile_width, tile_height, 32, 0);
+ if( layer.image_tiles[j][i].isNull())
+ return false;
+ layer.image_tiles[j][i].setAlphaBuffer(false);
+ break;
+
+ case RGBA_GIMAGE:
+ layer.image_tiles[j][i] = QImage(tile_width, tile_height, 32, 0);
+ if( layer.image_tiles[j][i].isNull())
+ return false;
+ layer.image_tiles[j][i].setAlphaBuffer(true);
+ break;
+
+ case GRAY_GIMAGE:
+ layer.image_tiles[j][i] = QImage(tile_width, tile_height, 8, 256);
+ if( layer.image_tiles[j][i].isNull())
+ return false;
+ setGrayPalette(layer.image_tiles[j][i]);
+ break;
+
+ case GRAYA_GIMAGE:
+ layer.image_tiles[j][i] = QImage(tile_width, tile_height, 8, 256);
+ if( layer.image_tiles[j][i].isNull())
+ return false;
+ setGrayPalette(layer.image_tiles[j][i]);
+
+ layer.alpha_tiles[j][i] = QImage( tile_width, tile_height, 8, 256);
+ if( layer.alpha_tiles[j][i].isNull())
+ return false;
+ setGrayPalette(layer.alpha_tiles[j][i]);
+ break;
+
+ case INDEXED_GIMAGE:
+ layer.image_tiles[j][i] = QImage(tile_width, tile_height, 8,
+ xcf_image.num_colors);
+ if( layer.image_tiles[j][i].isNull())
+ return false;
+ setPalette(xcf_image, layer.image_tiles[j][i]);
+ break;
+
+ case INDEXEDA_GIMAGE:
+ layer.image_tiles[j][i] = QImage(tile_width, tile_height,8,
+ xcf_image.num_colors);
+ if( layer.image_tiles[j][i].isNull())
+ return false;
+ setPalette(xcf_image, layer.image_tiles[j][i]);
+
+ layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, 8, 256);
+ if( layer.alpha_tiles[j][i].isNull())
+ return false;
+ setGrayPalette(layer.alpha_tiles[j][i]);
+ }
+
+ if (layer.mask_offset != 0) {
+ layer.mask_tiles[j][i] = QImage(tile_width, tile_height, 8, 256);
+ if( layer.mask_tiles[j][i].isNull())
+ return false;
+ setGrayPalette(layer.mask_tiles[j][i]);
+ }
+ }
+ }
+ return true;
+}
+
+
+/*!
+ * Apply a grayscale palette to the QImage. Note that Qt does not distinguish
+ * between grayscale and indexed images. A grayscale image is just
+ * an indexed image with a 256-color, grayscale palette.
+ * \param image image to set to a grayscale palette.
+ */
+void XCFImageFormat::setGrayPalette(QImage& image)
+{
+ for (int i = 0; i < 256; i++)
+ image.setColor(i, qRgb(i, i, i));
+}
+
+
+/*!
+ * Copy the indexed palette from the XCF image into the QImage.
+ * \param xcf_image XCF image containing the palette read from the data stream.
+ * \param image image to apply the palette to.
+ */
+void XCFImageFormat::setPalette(XCFImage& xcf_image, QImage& image)
+{
+ for (int i = 0; i < xcf_image.num_colors; i++)
+ image.setColor(i, xcf_image.palette[i]);
+}
+
+
+/*!
+ * Copy the bytes from the tile buffer into the image tile QImage, taking into
+ * account all the myriad different modes.
+ * \param layer layer containing the tile buffer and the image tile matrix.
+ * \param i column index of current tile.
+ * \param j row index of current tile.
+ */
+void XCFImageFormat::assignImageBytes(Layer& layer, uint i, uint j)
+{
+ uchar* tile = layer.tile;
+
+ switch (layer.type) {
+ case RGB_GIMAGE:
+ for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
+ for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
+ layer.image_tiles[j][i].setPixel(k, l,
+ qRgb(tile[0], tile[1], tile[2]));
+ tile += sizeof(QRgb);
+ }
+ }
+ break;
+
+ case RGBA_GIMAGE:
+ for ( int l = 0; l < layer.image_tiles[j][i].height(); l++ ) {
+ for ( int k = 0; k < layer.image_tiles[j][i].width(); k++ ) {
+ layer.image_tiles[j][i].setPixel(k, l,
+ qRgba(tile[0], tile[1], tile[2], tile[3]));
+ tile += sizeof(QRgb);
+ }
+ }
+ break;
+
+ case GRAY_GIMAGE:
+ case INDEXED_GIMAGE:
+ for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
+ for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
+ layer.image_tiles[j][i].setPixel(k, l, tile[0]);
+ tile += sizeof(QRgb);
+ }
+ }
+ break;
+
+ case GRAYA_GIMAGE:
+ case INDEXEDA_GIMAGE:
+ for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
+ for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
+
+ // The "if" here should not be necessary, but apparently there
+ // are some cases where the image can contain larger indices
+ // than there are colors in the palette. (A bug in The GIMP?)
+
+ if (tile[0] < layer.image_tiles[j][i].numColors())
+ layer.image_tiles[j][i].setPixel(k, l, tile[0]);
+
+ layer.alpha_tiles[j][i].setPixel(k, l, tile[1]);
+ tile += sizeof(QRgb);
+ }
+ }
+ break;
+ }
+}
+
+
+/*!
+ * The GIMP stores images in a "mipmap"-like hierarchy. As far as the QImage
+ * is concerned, however, only the top level (i.e., the full resolution image)
+ * is used.
+ * \param xcf_io the data stream connected to the XCF image.
+ * \param layer the layer to collect the image.
+ * \return true if there were no I/O errors.
+ */
+bool XCFImageFormat::loadHierarchy(QDataStream& xcf_io, Layer& layer)
+{
+ Q_INT32 width;
+ Q_INT32 height;
+ Q_INT32 bpp;
+ Q_UINT32 offset;
+
+ xcf_io >> width >> height >> bpp >> offset;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer " << layer.name << " image header" << endl;
+ return false;
+ }
+
+ // GIMP stores images in a "mipmap"-like format (multiple levels of
+ // increasingly lower resolution). Only the top level is used here,
+ // however.
+
+ Q_UINT32 junk;
+ do {
+ xcf_io >> junk;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer " << layer.name << " level offsets" << endl;
+ return false;
+ }
+ } while (junk != 0);
+
+ QIODevice::Offset saved_pos = xcf_io.device()->at();
+
+ xcf_io.device()->at(offset);
+ if (!loadLevel(xcf_io, layer, bpp))
+ return false;
+
+ xcf_io.device()->at(saved_pos);
+ return true;
+}
+
+
+/*!
+ * Load one level of the image hierarchy (but only the top level is ever used).
+ * \param xcf_io the data stream connected to the XCF image.
+ * \param layer the layer to collect the image.
+ * \param bpp the number of bytes in a pixel.
+ * \return true if there were no I/O errors.
+ * \sa loadTileRLE().
+ */
+bool XCFImageFormat::loadLevel(QDataStream& xcf_io, Layer& layer, Q_INT32 bpp)
+{
+ Q_INT32 width;
+ Q_INT32 height;
+ Q_UINT32 offset;
+
+ xcf_io >> width >> height >> offset;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer " << layer.name << " level info" << endl;
+ return false;
+ }
+
+ if (offset == 0)
+ return true;
+
+ for (uint j = 0; j < layer.nrows; j++) {
+ for (uint i = 0; i < layer.ncols; i++) {
+
+ if (offset == 0) {
+ kdDebug(399) << "XCF: incorrect number of tiles in layer " << layer.name << endl;
+ return false;
+ }
+
+ QIODevice::Offset saved_pos = xcf_io.device()->at();
+ Q_UINT32 offset2;
+ xcf_io >> offset2;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer " << layer.name << " level offset look-ahead" << endl;
+ return false;
+ }
+
+ // Evidently, RLE can occasionally expand a tile instead of compressing it!
+
+ if (offset2 == 0)
+ offset2 = offset + (uint)(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5);
+
+ xcf_io.device()->at(offset);
+ int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
+
+ if (!loadTileRLE(xcf_io, layer.tile, size, offset2 - offset, bpp))
+ return false;
+
+ // The bytes in the layer tile are juggled differently depending on
+ // the target QImage. The caller has set layer.assignBytes to the
+ // appropriate routine.
+
+ layer.assignBytes(layer, i, j);
+
+ xcf_io.device()->at(saved_pos);
+ xcf_io >> offset;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on layer " << layer.name << " level offset" << endl;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/*!
+ * A layer can have a one channel image which is used as a mask.
+ * \param xcf_io the data stream connected to the XCF image.
+ * \param layer the layer to collect the mask image.
+ * \return true if there were no I/O errors.
+ */
+bool XCFImageFormat::loadMask(QDataStream& xcf_io, Layer& layer)
+{
+ Q_INT32 width;
+ Q_INT32 height;
+ char* name;
+
+ xcf_io >> width >> height >> name;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on mask info" << endl;
+ return false;
+ }
+
+ delete name;
+
+ if (!loadChannelProperties(xcf_io, layer))
+ return false;
+
+ Q_UINT32 hierarchy_offset;
+ xcf_io >> hierarchy_offset;
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ kdDebug(399) << "XCF: read failure on mask image offset" << endl;
+ return false;
+ }
+
+ xcf_io.device()->at(hierarchy_offset);
+ layer.assignBytes = assignMaskBytes;
+
+ if (!loadHierarchy(xcf_io, layer))
+ return false;
+
+ return true;
+}
+
+
+/*!
+ * This is the routine for which all the other code is simply
+ * infrastructure. Read the image bytes out of the file and
+ * store them in the tile buffer. This is passed a full 32-bit deep
+ * buffer, even if bpp is smaller. The caller can figure out what to
+ * do with the bytes.
+ *
+ * The tile is stored in "channels", i.e. the red component of all
+ * pixels, then the green component of all pixels, then blue then
+ * alpha, or, for indexed images, the color indices of all pixels then
+ * the alpha of all pixels.
+ *
+ * The data is compressed with "run length encoding". Some simple data
+ * integrity checks are made.
+ *
+ * \param xcf_io the data stream connected to the XCF image.
+ * \param tile the buffer to expand the RLE into.
+ * \param image_size number of bytes expected to be in the image tile.
+ * \param data_length number of bytes expected in the RLE.
+ * \param bpp number of bytes per pixel.
+ * \return true if there were no I/O errors and no obvious corruption of
+ * the RLE data.
+ */
+bool XCFImageFormat::loadTileRLE(QDataStream& xcf_io, uchar* tile, int image_size,
+ int data_length, Q_INT32 bpp)
+{
+ uchar* data;
+
+ uchar* xcfdata;
+ uchar* xcfodata;
+ uchar* xcfdatalimit;
+
+ xcfdata = xcfodata = new uchar[data_length];
+
+ xcf_io.readRawBytes((char*)xcfdata, data_length);
+
+ if (xcf_io.device()->status() != IO_Ok) {
+ delete[] xcfodata;
+ kdDebug(399) << "XCF: read failure on tile" << endl;
+ return false;
+ }
+
+ xcfdatalimit = &xcfodata[data_length - 1];
+
+ for (int i = 0; i < bpp; ++i) {
+
+ data = tile + i;
+
+ int count = 0;
+ int size = image_size;
+
+ while (size > 0) {
+ if (xcfdata > xcfdatalimit)
+ goto bogus_rle;
+
+ uchar val = *xcfdata++;
+ uint length = val;
+
+ if (length >= 128) {
+ length = 255 - (length - 1);
+ if (length == 128) {
+ if (xcfdata >= xcfdatalimit)
+ goto bogus_rle;
+
+ length = (*xcfdata << 8) + xcfdata[1];
+
+ xcfdata += 2;
+ }
+
+ count += length;
+ size -= length;
+
+ if (size < 0)
+ goto bogus_rle;
+
+ if (&xcfdata[length - 1] > xcfdatalimit)
+ goto bogus_rle;
+
+ while (length-- > 0) {
+ *data = *xcfdata++;
+ data += sizeof(QRgb);
+ }
+ } else {
+ length += 1;
+ if (length == 128) {
+ if (xcfdata >= xcfdatalimit)
+ goto bogus_rle;
+
+ length = (*xcfdata << 8) + xcfdata[1];
+ xcfdata += 2;
+ }
+
+ count += length;
+ size -= length;
+
+ if (size < 0)
+ goto bogus_rle;
+
+ if (xcfdata > xcfdatalimit)
+ goto bogus_rle;
+
+ val = *xcfdata++;
+
+ while (length-- > 0) {
+ *data = val;
+ data += sizeof(QRgb);
+ }
+ }
+ }
+ }
+
+ delete[] xcfodata;
+ return true;
+
+bogus_rle:
+
+ kdDebug(399) << "The run length encoding could not be decoded properly" << endl;
+ delete[] xcfodata;
+ return false;
+}
+
+
+/*!
+ * An XCF file can contain an arbitrary number of properties associated
+ * with a channel. Note that this routine only reads mask channel properties.
+ * \param xcf_io the data stream connected to the XCF image.
+ * \param layer layer containing the mask channel to collect the properties.
+ * \return true if there were no I/O errors.
+ */
+bool XCFImageFormat::loadChannelProperties(QDataStream& xcf_io, Layer& layer)
+{
+ while (true) {
+ PropType type;
+ QByteArray bytes;
+
+ if (!loadProperty(xcf_io, type, bytes)) {
+ kdDebug(399) << "XCF: error loading channel properties" << endl;
+ return false;
+ }
+
+ QDataStream property(bytes, IO_ReadOnly);
+
+ switch (type) {
+ case PROP_END:
+ return true;
+
+ case PROP_OPACITY:
+ property >> layer.mask_channel.opacity;
+ break;
+
+ case PROP_VISIBLE:
+ property >> layer.mask_channel.visible;
+ break;
+
+ case PROP_SHOW_MASKED:
+ property >> layer.mask_channel.show_masked;
+ break;
+
+ case PROP_COLOR:
+ property >> layer.mask_channel.red >> layer.mask_channel.green
+ >> layer.mask_channel.blue;
+ break;
+
+ case PROP_TATTOO:
+ property >> layer.mask_channel.tattoo;
+ break;
+
+ default:
+ kdDebug(399) << "XCF: unimplemented channel property " << type
+ << ", size " << bytes.size() << endl;
+ }
+ }
+}
+
+
+/*!
+ * Copy the bytes from the tile buffer into the mask tile QImage.
+ * \param layer layer containing the tile buffer and the mask tile matrix.
+ * \param i column index of current tile.
+ * \param j row index of current tile.
+ */
+void XCFImageFormat::assignMaskBytes(Layer& layer, uint i, uint j)
+{
+ uchar* tile = layer.tile;
+
+ for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
+ for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
+ layer.mask_tiles[j][i].setPixel(k, l, tile[0]);
+ tile += sizeof(QRgb);
+ }
+ }
+}
+
+
+/*!
+ * Construct the QImage which will eventually be returned to the QImage
+ * loader.
+ *
+ * There are a couple of situations which require that the QImage is not
+ * exactly the same as The GIMP's representation. The full table is:
+ * \verbatim
+ * Grayscale opaque : 8 bpp indexed
+ * Grayscale translucent : 32 bpp + alpha
+ * Indexed opaque : 1 bpp if num_colors <= 2
+ * : 8 bpp indexed otherwise
+ * Indexed translucent : 8 bpp indexed + alpha if num_colors < 256
+ * : 32 bpp + alpha otherwise
+ * RGB opaque : 32 bpp
+ * RGBA translucent : 32 bpp + alpha
+ * \endverbatim
+ * Whether the image is translucent or not is determined by the bottom layer's
+ * alpha channel. However, even if the bottom layer lacks an alpha channel,
+ * it can still have an opacity < 1. In this case, the QImage is promoted
+ * to 32-bit. (Note this is different from the output from the GIMP image
+ * exporter, which seems to ignore this attribute.)
+ *
+ * Independently, higher layers can be translucent, but the background of
+ * the image will not show through if the bottom layer is opaque.
+ *
+ * For indexed images, translucency is an all or nothing effect.
+ * \param xcf_image contains image info and bottom-most layer.
+ */
+bool XCFImageFormat::initializeImage(XCFImage& xcf_image)
+{
+ // (Aliases to make the code look a little better.)
+ Layer& layer(xcf_image.layer);
+ QImage& image(xcf_image.image);
+
+ switch (layer.type) {
+ case RGB_GIMAGE:
+ if (layer.opacity == OPAQUE_OPACITY) {
+ image.create( xcf_image.width, xcf_image.height, 32);
+ if( image.isNull())
+ return false;
+ image.fill(qRgb(255, 255, 255));
+ break;
+ } // else, fall through to 32-bit representation
+
+ case RGBA_GIMAGE:
+ image.create(xcf_image.width, xcf_image.height, 32);
+ if( image.isNull())
+ return false;
+ image.fill(qRgba(255, 255, 255, 0));
+ // Turning this on prevents fill() from affecting the alpha channel,
+ // by the way.
+ image.setAlphaBuffer(true);
+ break;
+
+ case GRAY_GIMAGE:
+ if (layer.opacity == OPAQUE_OPACITY) {
+ image.create(xcf_image.width, xcf_image.height, 8, 256);
+ if( image.isNull())
+ return false;
+ setGrayPalette(image);
+ image.fill(255);
+ break;
+ } // else, fall through to 32-bit representation
+
+ case GRAYA_GIMAGE:
+ image.create(xcf_image.width, xcf_image.height, 32);
+ if( image.isNull())
+ return false;
+ image.fill(qRgba(255, 255, 255, 0));
+ image.setAlphaBuffer(true);
+ break;
+
+ case INDEXED_GIMAGE:
+ // As noted in the table above, there are quite a few combinations
+ // which are possible with indexed images, depending on the
+ // presense of transparency (note: not translucency, which is not
+ // supported by The GIMP for indexed images) and the number of
+ // individual colors.
+
+ // Note: Qt treats a bitmap with a Black and White color palette
+ // as a mask, so only the "on" bits are drawn, regardless of the
+ // order color table entries. Otherwise (i.e., at least one of the
+ // color table entries is not black or white), it obeys the one-
+ // or two-color palette. Have to ask about this...
+
+ if (xcf_image.num_colors <= 2) {
+ image.create(xcf_image.width, xcf_image.height,
+ 1, xcf_image.num_colors,
+ QImage::LittleEndian);
+ if( image.isNull())
+ return false;
+ image.fill(0);
+ setPalette(xcf_image, image);
+ } else if (xcf_image.num_colors <= 256) {
+ image.create(xcf_image.width, xcf_image.height,
+ 8, xcf_image.num_colors,
+ QImage::LittleEndian);
+ if( image.isNull())
+ return false;
+ image.fill(0);
+ setPalette(xcf_image, image);
+ }
+ break;
+
+ case INDEXEDA_GIMAGE:
+ if (xcf_image.num_colors == 1) {
+ // Plenty(!) of room to add a transparent color
+ xcf_image.num_colors++;
+ xcf_image.palette.resize(xcf_image.num_colors);
+ xcf_image.palette[1] = xcf_image.palette[0];
+ xcf_image.palette[0] = qRgba(255, 255, 255, 0);
+
+ image.create(xcf_image.width, xcf_image.height,
+ 1, xcf_image.num_colors,
+ QImage::LittleEndian);
+ if( image.isNull())
+ return false;
+ image.fill(0);
+ setPalette(xcf_image, image);
+ image.setAlphaBuffer(true);
+ } else if (xcf_image.num_colors < 256) {
+ // Plenty of room to add a transparent color
+ xcf_image.num_colors++;
+ xcf_image.palette.resize(xcf_image.num_colors);
+ for (int c = xcf_image.num_colors - 1; c >= 1; c--)
+ xcf_image.palette[c] = xcf_image.palette[c - 1];
+
+ xcf_image.palette[0] = qRgba(255, 255, 255, 0);
+ image.create( xcf_image.width, xcf_image.height,
+ 8, xcf_image.num_colors);
+ if( image.isNull())
+ return false;
+ image.fill(0);
+ setPalette(xcf_image, image);
+ image.setAlphaBuffer(true);
+ } else {
+ // No room for a transparent color, so this has to be promoted to
+ // true color. (There is no equivalent PNG representation output
+ // from The GIMP as of v1.2.)
+ image.create(xcf_image.width, xcf_image.height, 32);
+ if( image.isNull())
+ return false;
+ image.fill(qRgba(255, 255, 255, 0));
+ image.setAlphaBuffer(true);
+ }
+ break;
+ }
+
+ image.setDotsPerMeterX((int)(xcf_image.x_resolution * INCHESPERMETER));
+ image.setDotsPerMeterY((int)(xcf_image.y_resolution * INCHESPERMETER));
+ return true;
+}
+
+
+/*!
+ * Copy a layer into an image, taking account of the manifold modes. The
+ * contents of the image are replaced.
+ * \param xcf_image contains the layer and image to be replaced.
+ */
+void XCFImageFormat::copyLayerToImage(XCFImage& xcf_image)
+{
+ Layer& layer(xcf_image.layer);
+ QImage& image(xcf_image.image);
+ PixelCopyOperation copy = 0;
+
+ switch (layer.type) {
+ case RGB_GIMAGE:
+ case RGBA_GIMAGE:
+ copy = copyRGBToRGB;
+ break;
+ case GRAY_GIMAGE:
+ if (layer.opacity == OPAQUE_OPACITY)
+ copy = copyGrayToGray;
+ else
+ copy = copyGrayToRGB;
+ break;
+ case GRAYA_GIMAGE:
+ copy = copyGrayAToRGB;
+ break;
+ case INDEXED_GIMAGE:
+ copy = copyIndexedToIndexed;
+ break;
+ case INDEXEDA_GIMAGE:
+ if (xcf_image.image.depth() <= 8)
+ copy = copyIndexedAToIndexed;
+ else
+ copy = copyIndexedAToRGB;
+ }
+
+ // For each tile...
+
+ for (uint j = 0; j < layer.nrows; j++) {
+ uint y = j * TILE_HEIGHT;
+
+ for (uint i = 0; i < layer.ncols; i++) {
+ uint x = i * TILE_WIDTH;
+
+ // This seems the best place to apply the dissolve because it
+ // depends on the global position of each tile's
+ // pixels. Apparently it's the only mode which can apply to a
+ // single layer.
+
+ if (layer.mode == DISSOLVE_MODE) {
+ if (layer.type == RGBA_GIMAGE)
+ dissolveRGBPixels(layer.image_tiles[j][i], x, y);
+
+ else if (layer.type == GRAYA_GIMAGE)
+ dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
+ }
+
+ for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
+ for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
+
+ int m = x + k + layer.x_offset;
+ int n = y + l + layer.y_offset;
+
+ if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
+ continue;
+
+ (*copy)(layer, i, j, k, l, image, m, n);
+ }
+ }
+ }
+ }
+}
+
+
+/*!
+ * Copy an RGB pixel from the layer to the RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ uchar src_a = layer.opacity;
+
+ if (layer.type == RGBA_GIMAGE)
+ src_a = INT_MULT(src_a, qAlpha(src));
+
+ // Apply the mask (if any)
+
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ image.setPixel(m, n, qRgba(src, src_a));
+}
+
+
+/*!
+ * Copy a Gray pixel from the layer to the Gray image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyGrayToGray(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ int src = layer.image_tiles[j][i].pixelIndex(k, l);
+ image.setPixel(m, n, src);
+}
+
+
+/*!
+ * Copy a Gray pixel from the layer to an RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ uchar src_a = layer.opacity;
+ image.setPixel(m, n, qRgba(src, src_a));
+}
+
+
+/*!
+ * Copy a GrayA pixel from the layer to an RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ // Apply the mask (if any)
+
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ image.setPixel(m, n, qRgba(src, src_a));
+}
+
+
+/*!
+ * Copy an Indexed pixel from the layer to the Indexed image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ int src = layer.image_tiles[j][i].pixelIndex(k, l);
+ image.setPixel(m, n, src);
+}
+
+
+/*!
+ * Copy an IndexedA pixel from the layer to the Indexed image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ if (layer.apply_mask == 1 &&
+ layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ if (src_a > 127)
+ src++;
+ else
+ src = 0;
+
+image.setPixel(m, n, src);
+}
+
+
+/*!
+ * Copy an IndexedA pixel from the layer to an RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ // Apply the mask (if any)
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ // This is what appears in the GIMP window
+ if (src_a <= 127)
+ src_a = 0;
+ else
+ src_a = OPAQUE_OPACITY;
+
+ image.setPixel(m, n, qRgba(src, src_a));
+}
+
+
+/*!
+ * Merge a layer into an image, taking account of the manifold modes.
+ * \param xcf_image contains the layer and image to merge.
+ */
+void XCFImageFormat::mergeLayerIntoImage(XCFImage& xcf_image)
+{
+ Layer& layer(xcf_image.layer);
+ QImage& image(xcf_image.image);
+
+ PixelMergeOperation merge = 0;
+
+ switch (layer.type) {
+ case RGB_GIMAGE:
+ case RGBA_GIMAGE:
+ merge = mergeRGBToRGB;
+ break;
+ case GRAY_GIMAGE:
+ if (layer.opacity == OPAQUE_OPACITY)
+ merge = mergeGrayToGray;
+ else
+ merge = mergeGrayToRGB;
+ break;
+ case GRAYA_GIMAGE:
+ if (xcf_image.image.depth() <= 8)
+ merge = mergeGrayAToGray;
+ else
+ merge = mergeGrayAToRGB;
+ break;
+ case INDEXED_GIMAGE:
+ merge = mergeIndexedToIndexed;
+ break;
+ case INDEXEDA_GIMAGE:
+ if (xcf_image.image.depth() <= 8)
+ merge = mergeIndexedAToIndexed;
+ else
+ merge = mergeIndexedAToRGB;
+ }
+
+ for (uint j = 0; j < layer.nrows; j++) {
+ uint y = j * TILE_HEIGHT;
+
+ for (uint i = 0; i < layer.ncols; i++) {
+ uint x = i * TILE_WIDTH;
+
+ // This seems the best place to apply the dissolve because it
+ // depends on the global position of each tile's
+ // pixels. Apparently it's the only mode which can apply to a
+ // single layer.
+
+ if (layer.mode == DISSOLVE_MODE) {
+ if (layer.type == RGBA_GIMAGE)
+ dissolveRGBPixels(layer.image_tiles[j][i], x, y);
+
+ else if (layer.type == GRAYA_GIMAGE)
+ dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
+ }
+
+ for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
+ for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
+
+ int m = x + k + layer.x_offset;
+ int n = y + l + layer.y_offset;
+
+ if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
+ continue;
+
+ (*merge)(layer, i, j, k, l, image, m, n);
+ }
+ }
+ }
+ }
+}
+
+
+/*!
+ * Merge an RGB pixel from the layer to the RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ QRgb dst = image.pixel(m, n);
+
+ uchar src_r = qRed(src);
+ uchar src_g = qGreen(src);
+ uchar src_b = qBlue(src);
+ uchar src_a = qAlpha(src);
+
+ uchar dst_r = qRed(dst);
+ uchar dst_g = qGreen(dst);
+ uchar dst_b = qBlue(dst);
+ uchar dst_a = qAlpha(dst);
+
+ switch (layer.mode) {
+ case MULTIPLY_MODE: {
+ src_r = INT_MULT(src_r, dst_r);
+ src_g = INT_MULT(src_g, dst_g);
+ src_b = INT_MULT(src_b, dst_b);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case DIVIDE_MODE: {
+ src_r = KMIN((dst_r * 256) / (1 + src_r), 255);
+ src_g = KMIN((dst_g * 256) / (1 + src_g), 255);
+ src_b = KMIN((dst_b * 256) / (1 + src_b), 255);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case SCREEN_MODE: {
+ src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
+ src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
+ src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case OVERLAY_MODE: {
+ src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
+ src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
+ src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case DIFFERENCE_MODE: {
+ src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
+ src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
+ src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case ADDITION_MODE: {
+ src_r = add_lut(dst_r,src_r);
+ src_g = add_lut(dst_g,src_g);
+ src_b = add_lut(dst_b,src_b);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case SUBTRACT_MODE: {
+ src_r = dst_r > src_r ? dst_r - src_r : 0;
+ src_g = dst_g > src_g ? dst_g - src_g : 0;
+ src_b = dst_b > src_b ? dst_b - src_b : 0;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case DARKEN_ONLY_MODE: {
+ src_r = dst_r < src_r ? dst_r : src_r;
+ src_g = dst_g < src_g ? dst_g : src_g;
+ src_b = dst_b < src_b ? dst_b : src_b;
+ src_a = KMIN( src_a, dst_a );
+ }
+ break;
+ case LIGHTEN_ONLY_MODE: {
+ src_r = dst_r < src_r ? src_r : dst_r;
+ src_g = dst_g < src_g ? src_g : dst_g;
+ src_b = dst_b < src_b ? src_b : dst_b;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case HUE_MODE: {
+ uchar new_r = dst_r;
+ uchar new_g = dst_g;
+ uchar new_b = dst_b;
+
+ RGBTOHSV(src_r, src_g, src_b);
+ RGBTOHSV(new_r, new_g, new_b);
+
+ new_r = src_r;
+
+ HSVTORGB(new_r, new_g, new_b);
+
+ src_r = new_r;
+ src_g = new_g;
+ src_b = new_b;
+ src_a = KMIN( src_a, dst_a );
+ }
+ break;
+ case SATURATION_MODE: {
+ uchar new_r = dst_r;
+ uchar new_g = dst_g;
+ uchar new_b = dst_b;
+
+ RGBTOHSV(src_r, src_g, src_b);
+ RGBTOHSV(new_r, new_g, new_b);
+
+ new_g = src_g;
+
+ HSVTORGB(new_r, new_g, new_b);
+
+ src_r = new_r;
+ src_g = new_g;
+ src_b = new_b;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case VALUE_MODE: {
+ uchar new_r = dst_r;
+ uchar new_g = dst_g;
+ uchar new_b = dst_b;
+
+ RGBTOHSV(src_r, src_g, src_b);
+ RGBTOHSV(new_r, new_g, new_b);
+
+ new_b = src_b;
+
+ HSVTORGB(new_r, new_g, new_b);
+
+ src_r = new_r;
+ src_g = new_g;
+ src_b = new_b;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case COLOR_MODE: {
+ uchar new_r = dst_r;
+ uchar new_g = dst_g;
+ uchar new_b = dst_b;
+
+ RGBTOHLS(src_r, src_g, src_b);
+ RGBTOHLS(new_r, new_g, new_b);
+
+ new_r = src_r;
+ new_b = src_b;
+
+ HLSTORGB(new_r, new_g, new_b);
+
+ src_r = new_r;
+ src_g = new_g;
+ src_b = new_b;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ }
+
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ // Apply the mask (if any)
+
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ uchar new_r, new_g, new_b, new_a;
+ new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
+
+ float src_ratio = (float)src_a / new_a;
+ float dst_ratio = 1.0 - src_ratio;
+
+ new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
+ new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
+ new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
+
+ if (!layer_modes[layer.mode].affect_alpha)
+ new_a = dst_a;
+
+ image.setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
+}
+
+
+/*!
+ * Merge a Gray pixel from the layer to the Gray image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ int src = layer.image_tiles[j][i].pixelIndex(k, l);
+ image.setPixel(m, n, src);
+}
+
+
+/*!
+ * Merge a GrayA pixel from the layer to the Gray image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ int src = qGray(layer.image_tiles[j][i].pixel(k, l));
+ int dst = image.pixelIndex(m, n);
+
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+
+ switch (layer.mode) {
+ case MULTIPLY_MODE: {
+ src = INT_MULT( src, dst );
+ }
+ break;
+ case DIVIDE_MODE: {
+ src = KMIN((dst * 256) / (1 + src), 255);
+ }
+ break;
+ case SCREEN_MODE: {
+ src = 255 - INT_MULT(255 - dst, 255 - src);
+ }
+ break;
+ case OVERLAY_MODE: {
+ src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
+ }
+ break;
+ case DIFFERENCE_MODE: {
+ src = dst > src ? dst - src : src - dst;
+ }
+ break;
+ case ADDITION_MODE: {
+ src = add_lut(dst,src);
+ }
+ break;
+ case SUBTRACT_MODE: {
+ src = dst > src ? dst - src : 0;
+ }
+ break;
+ case DARKEN_ONLY_MODE: {
+ src = dst < src ? dst : src;
+ }
+ break;
+ case LIGHTEN_ONLY_MODE: {
+ src = dst < src ? src : dst;
+ }
+ break;
+ }
+
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ // Apply the mask (if any)
+
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ uchar new_a = OPAQUE_OPACITY;
+
+ float src_ratio = (float)src_a / new_a;
+ float dst_ratio = 1.0 - src_ratio;
+
+ uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
+
+ image.setPixel(m, n, new_g);
+}
+
+
+/*!
+ * Merge a Gray pixel from the layer to an RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ uchar src_a = layer.opacity;
+ image.setPixel(m, n, qRgba(src, src_a));
+}
+
+
+/*!
+ * Merge a GrayA pixel from the layer to an RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ int src = qGray(layer.image_tiles[j][i].pixel(k, l));
+ int dst = qGray(image.pixel(m, n));
+
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+ uchar dst_a = qAlpha(image.pixel(m, n));
+
+ switch (layer.mode) {
+ case MULTIPLY_MODE: {
+ src = INT_MULT(src, dst);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case DIVIDE_MODE: {
+ src = KMIN((dst * 256) / (1 + src), 255);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case SCREEN_MODE: {
+ src = 255 - INT_MULT(255 - dst, 255 - src);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case OVERLAY_MODE: {
+ src = INT_MULT( dst, dst + INT_MULT(2 * src, 255 - dst));
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case DIFFERENCE_MODE: {
+ src = dst > src ? dst - src : src - dst;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case ADDITION_MODE: {
+ src = add_lut(dst,src);
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case SUBTRACT_MODE: {
+ src = dst > src ? dst - src : 0;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case DARKEN_ONLY_MODE: {
+ src = dst < src ? dst : src;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ case LIGHTEN_ONLY_MODE: {
+ src = dst < src ? src : dst;
+ src_a = KMIN(src_a, dst_a);
+ }
+ break;
+ }
+
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ // Apply the mask (if any)
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
+
+ float src_ratio = (float)src_a / new_a;
+ float dst_ratio = 1.0 - src_ratio;
+
+ uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
+
+ if (!layer_modes[layer.mode].affect_alpha)
+ new_a = dst_a;
+
+ image.setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
+}
+
+
+/*!
+ * Merge an Indexed pixel from the layer to the Indexed image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ int src = layer.image_tiles[j][i].pixelIndex(k, l);
+ image.setPixel(m, n, src);
+}
+
+
+/*!
+ * Merge an IndexedA pixel from the layer to the Indexed image. Straight-forward.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+ src_a = INT_MULT( src_a, layer.opacity );
+
+ if ( layer.apply_mask == 1 &&
+ layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ if (src_a > 127) {
+ src++;
+ image.setPixel(m, n, src);
+ }
+}
+
+
+/*!
+ * Merge an IndexedA pixel from the layer to an RGB image. Straight-forward.
+ * The only thing this has to take account of is the opacity of the
+ * layer. Evidently, the GIMP exporter itself does not actually do this.
+ * \param layer source layer.
+ * \param i x tile index.
+ * \param j y tile index.
+ * \param k x pixel index of tile i,j.
+ * \param l y pixel index of tile i,j.
+ * \param image destination image.
+ * \param m x pixel of destination image.
+ * \param n y pixel of destination image.
+ */
+void XCFImageFormat::mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n)
+{
+ QRgb src = layer.image_tiles[j][i].pixel(k, l);
+ uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
+ src_a = INT_MULT(src_a, layer.opacity);
+
+ // Apply the mask (if any)
+ if (layer.apply_mask == 1 && layer.mask_tiles.size() > j &&
+ layer.mask_tiles[j].size() > i)
+ src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
+
+ // This is what appears in the GIMP window
+ if (src_a <= 127)
+ src_a = 0;
+ else
+ src_a = OPAQUE_OPACITY;
+
+ image.setPixel(m, n, qRgba(src, src_a));
+}
+
+
+/*!
+ * Dissolving pixels: pick a random number between 0 and 255. If the pixel's
+ * alpha is less than that, make it transparent.
+ * \param image the image tile to dissolve.
+ * \param x the global x position of the tile.
+ * \param y the global y position of the tile.
+ */
+void XCFImageFormat::dissolveRGBPixels ( QImage& image, int x, int y )
+{
+ // The apparently spurious rand() calls are to wind the random
+ // numbers up to the same point for each tile.
+
+ for (int l = 0; l < image.height(); l++) {
+ srand(random_table[( l + y ) % RANDOM_TABLE_SIZE]);
+
+ for (int k = 0; k < x; k++)
+ rand();
+
+ for (int k = 0; k < image.width(); k++) {
+ int rand_val = rand() & 0xff;
+ QRgb pixel = image.pixel(k, l);
+
+ if (rand_val > qAlpha(pixel)) {
+ image.setPixel(k, l, qRgba(pixel, 0));
+ }
+ }
+ }
+}
+
+
+/*!
+ * Dissolving pixels: pick a random number between 0 and 255. If the pixel's
+ * alpha is less than that, make it transparent. This routine works for
+ * the GRAYA and INDEXEDA image types where the pixel alpha's are stored
+ * separately from the pixel themselves.
+ * \param image the alpha tile to dissolve.
+ * \param x the global x position of the tile.
+ * \param y the global y position of the tile.
+ */
+void XCFImageFormat::dissolveAlphaPixels ( QImage& image, int x, int y )
+{
+ // The apparently spurious rand() calls are to wind the random
+ // numbers up to the same point for each tile.
+
+ for (int l = 0; l < image.height(); l++) {
+ srand( random_table[(l + y) % RANDOM_TABLE_SIZE]);
+
+ for (int k = 0; k < x; k++)
+ rand();
+
+ for (int k = 0; k < image.width(); k++) {
+ int rand_val = rand() & 0xff;
+ uchar alpha = image.pixelIndex(k, l);
+
+ if (rand_val > alpha) {
+ image.setPixel(k, l, 0);
+ }
+ }
+ }
+}
+
diff --git a/kimgio/xcf.h b/kimgio/xcf.h
new file mode 100644
index 000000000..69b434bd3
--- /dev/null
+++ b/kimgio/xcf.h
@@ -0,0 +1,231 @@
+#ifndef XCF_H
+#define XCF_H
+/*
+ * qxcfi.cpp: A Qt 3 plug-in for reading GIMP XCF image files
+ * Copyright (C) 2001 lignum Computing, Inc. <allen@lignumcomputing.com>
+ * Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
+ *
+ * This plug-in is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <qimage.h>
+#include <qiodevice.h>
+#include <qvaluestack.h>
+#include <qvaluevector.h>
+
+#include "gimp.h"
+
+
+extern "C" {
+void kimgio_xcf_read(QImageIO *);
+void kimgio_xcf_write(QImageIO *);
+}
+
+const float INCHESPERMETER = (100.0 / 2.54);
+
+/*!
+ * Each layer in an XCF file is stored as a matrix of
+ * 64-pixel by 64-pixel images. The GIMP has a sophisticated
+ * method of handling very large images as well as implementing
+ * parallel processing on a tile-by-tile basis. Here, though,
+ * we just read them in en-masse and store them in a matrix.
+ */
+typedef QValueVector<QValueVector<QImage> > Tiles;
+
+
+
+class XCFImageFormat {
+public:
+ XCFImageFormat();
+ void readXCF(QImageIO* image_io);
+
+
+private:
+ /*!
+ * Each GIMP image is composed of one or more layers. A layer can
+ * be one of any three basic types: RGB, grayscale or indexed. With an
+ * optional alpha channel, there are six possible types altogether.
+ *
+ * Note: there is only ever one instance of this structure. The
+ * layer info is discarded after it is merged into the final QImage.
+ */
+ class Layer {
+ public:
+ Q_UINT32 width; //!< Width of the layer
+ Q_UINT32 height; //!< Height of the layer
+ Q_INT32 type; //!< Type of the layer (GimpImageType)
+ char* name; //!< Name of the layer
+ Q_UINT32 hierarchy_offset; //!< File position of Tile hierarchy
+ Q_UINT32 mask_offset; //!< File position of mask image
+
+ uint nrows; //!< Number of rows of tiles (y direction)
+ uint ncols; //!< Number of columns of tiles (x direction)
+
+ Tiles image_tiles; //!< The basic image
+ //! For Grayscale and Indexed images, the alpha channel is stored
+ //! separately (in this data structure, anyway).
+ Tiles alpha_tiles;
+ Tiles mask_tiles; //!< The layer mask (optional)
+
+ //! Additional information about a layer mask.
+ struct {
+ Q_UINT32 opacity;
+ Q_UINT32 visible;
+ Q_UINT32 show_masked;
+ uchar red, green, blue;
+ Q_UINT32 tattoo;
+ } mask_channel;
+
+ bool active; //!< Is this layer the active layer?
+ Q_UINT32 opacity; //!< The opacity of the layer
+ Q_UINT32 visible; //!< Is the layer visible?
+ Q_UINT32 linked; //!< Is this layer linked (geometrically)
+ Q_UINT32 preserve_transparency; //!< Preserve alpha when drawing on layer?
+ Q_UINT32 apply_mask; //!< Apply the layer mask?
+ Q_UINT32 edit_mask; //!< Is the layer mask the being edited?
+ Q_UINT32 show_mask; //!< Show the layer mask rather than the image?
+ Q_INT32 x_offset; //!< x offset of the layer relative to the image
+ Q_INT32 y_offset; //!< y offset of the layer relative to the image
+ Q_UINT32 mode; //!< Combining mode of layer (LayerModeEffects)
+ Q_UINT32 tattoo; //!< (unique identifier?)
+
+ //! As each tile is read from the file, it is buffered here.
+ uchar tile[TILE_WIDTH * TILE_HEIGHT * sizeof(QRgb)];
+
+ //! The data from tile buffer is copied to the Tile by this
+ //! method. Depending on the type of the tile (RGB, Grayscale,
+ //! Indexed) and use (image or mask), the bytes in the buffer are
+ //! copied in different ways.
+ void (*assignBytes)(Layer& layer, uint i, uint j);
+
+ Layer(void) : name(0) {}
+ ~Layer(void) { delete[] name; }
+ };
+
+
+ /*!
+ * The in-memory representation of the XCF Image. It contains a few
+ * metadata items, but is mostly a container for the layer information.
+ */
+ class XCFImage {
+ public:
+ Q_UINT32 width; //!< width of the XCF image
+ Q_UINT32 height; //!< height of the XCF image
+ Q_INT32 type; //!< type of the XCF image (GimpImageBaseType)
+
+ Q_UINT8 compression; //!< tile compression method (CompressionType)
+ float x_resolution; //!< x resolution in dots per inch
+ float y_resolution; //!< y resolution in dots per inch
+ Q_INT32 tattoo; //!< (unique identifier?)
+ Q_UINT32 unit; //!< Units of The GIMP (inch, mm, pica, etc...)
+ Q_INT32 num_colors; //!< number of colors in an indexed image
+ QValueVector<QRgb> palette; //!< indexed image color palette
+
+ int num_layers; //!< number of layers
+ Layer layer; //!< most recently read layer
+
+ bool initialized; //!< Is the QImage initialized?
+ QImage image; //!< final QImage
+
+ XCFImage(void) : initialized(false) {}
+ };
+
+
+ //! In layer DISSOLVE mode, a random number is chosen to compare to a
+ //! pixel's alpha. If the alpha is greater than the random number, the
+ //! pixel is drawn. This table merely contains the random number seeds
+ //! for each ROW of an image. Therefore, the random numbers chosen
+ //! are consistent from run to run.
+ static int random_table[RANDOM_TABLE_SIZE];
+
+ //! This table provides the add_pixel saturation values (i.e. 250 + 250 = 255).
+ //static int add_lut[256][256]; - this is so lame waste of 256k of memory
+ static int add_lut( int, int );
+
+ //! The bottom-most layer is copied into the final QImage by this
+ //! routine.
+ typedef void (*PixelCopyOperation)(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+
+ //! Higher layers are merged into the the final QImage by this routine.
+ typedef void (*PixelMergeOperation)(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+
+ //! Layer mode static data.
+ typedef struct {
+ bool affect_alpha; //!< Does this mode affect the source alpha?
+ } LayerModes;
+
+ //! Array of layer mode structures for the modes described by
+ //! LayerModeEffects.
+ static const LayerModes layer_modes[];
+
+ bool loadImageProperties(QDataStream& xcf_io, XCFImage& image);
+ bool loadProperty(QDataStream& xcf_io, PropType& type, QByteArray& bytes);
+ bool loadLayer(QDataStream& xcf_io, XCFImage& xcf_image);
+ bool loadLayerProperties(QDataStream& xcf_io, Layer& layer);
+ bool composeTiles(XCFImage& xcf_image);
+ void setGrayPalette(QImage& image);
+ void setPalette(XCFImage& xcf_image, QImage& image);
+ static void assignImageBytes(Layer& layer, uint i, uint j);
+ bool loadHierarchy(QDataStream& xcf_io, Layer& layer);
+ bool loadLevel(QDataStream& xcf_io, Layer& layer, Q_INT32 bpp);
+ static void assignMaskBytes(Layer& layer, uint i, uint j);
+ bool loadMask(QDataStream& xcf_io, Layer& layer);
+ bool loadChannelProperties(QDataStream& xcf_io, Layer& layer);
+ bool initializeImage(XCFImage& xcf_image);
+ bool loadTileRLE(QDataStream& xcf_io, uchar* tile, int size,
+ int data_length, Q_INT32 bpp);
+ static void copyLayerToImage(XCFImage& xcf_image);
+ static void copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+
+ static void copyGrayToGray(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+
+ static void mergeLayerIntoImage(XCFImage& xcf_image);
+ static void mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+ static void mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
+ QImage& image, int m, int n);
+
+ static void dissolveRGBPixels(QImage& image, int x, int y);
+ static void dissolveAlphaPixels(QImage& image, int x, int y);
+};
+
+#endif
diff --git a/kimgio/xcf.kimgio b/kimgio/xcf.kimgio
new file mode 100644
index 000000000..64211af4c
--- /dev/null
+++ b/kimgio/xcf.kimgio
@@ -0,0 +1,78 @@
+[Image Format]
+Type=XCF
+Header=^gimp xcf
+Name=GIMP Image
+Name[af]=GIMP Beeld
+Name[be]=Малюнак GIMP
+Name[bg]=GIMP изображение
+Name[bn]=জিম্প চিত্র
+Name[br]=Skeudenn GIMP
+Name[bs]=GIMP slika
+Name[ca]=Imatge GIMP
+Name[cs]=Obrázek GIMP
+Name[csb]=Òbrôzk GIMPa
+Name[da]=GIMP-billede
+Name[de]=GIMP-Bild
+Name[el]=Εικόνα GIMP
+Name[eo]=Gimp bildo
+Name[es]=Imagen GIMP
+Name[et]=GIMP-i pildifail
+Name[eu]=GIMP irudia
+Name[fa]=تصویر GIMP
+Name[fi]=GIMP-kuva
+Name[fr]=Image GIMP
+Name[fy]=GIMP-ôfbylding
+Name[ga]=Íomhá GIMP
+Name[gl]=Imaxe GIMP
+Name[he]=תמונת GIMP
+Name[hi]=गिम्प छवि
+Name[hr]=GIMP slika
+Name[hu]=GIMP-kép
+Name[id]=Gambar GIMP
+Name[is]=GIMP mynd
+Name[it]=Immagine GIMP
+Name[ja]=GIMP 画像
+Name[ka]=GIMP ნახატი
+Name[kk]=GIMP кескіні
+Name[km]=រូបភាព GIMP
+Name[lb]=GIMP-Bild
+Name[lt]=GIMP paveiksliukas
+Name[lv]=GIMP attēls
+Name[mk]=GIMP слика
+Name[ms]=Imej GIMP
+Name[nb]=GIMP-bilde
+Name[nds]=GIMP-Bild
+Name[ne]=GIMP छवि
+Name[nl]=GIMP-afbeelding
+Name[nn]=GIMP-bilete
+Name[pa]=GIMP ਚਿੱਤਰ
+Name[pl]=Obrazek programu GIMP
+Name[pt]=Imagem do GIMP
+Name[pt_BR]=Imagem do GIMP
+Name[ro]=Imagine GIMP
+Name[ru]=Рисунок GIMP
+Name[rw]=GIMP Ishusho
+Name[se]=GIMP-govva
+Name[sk]=GIMP obrázok
+Name[sl]=Slika GIMP
+Name[sr]=GIMP-ова слика
+Name[sr@Latn]=GIMP-ova slika
+Name[sv]=GIMP-bild
+Name[ta]=GIMP பிம்பம்
+Name[te]=జింప్ ప్రతిబింబం
+Name[tg]=Тасвири GIMP
+Name[th]=แฟ้มภาพ GIMP
+Name[tr]=GIMP Resmi
+Name[tt]=GIMP Sürät
+Name[uk]=Зображення GIMP
+Name[uz]=GIMP-rasm
+Name[uz@cyrillic]=GIMP-расм
+Name[vi]=Ảnh GIMP
+Name[zh_CN]=GIMP 图像
+Name[zh_HK]=GIMP 圖檔
+Name[zh_TW]=GIMP 影像
+Read=true
+Write=false
+Suffices=xcf,XCF
+Mimetype=image/x-xcf-gimp
+Library=kimg_xcf.la
diff --git a/kimgio/xpm.kimgio b/kimgio/xpm.kimgio
new file mode 100644
index 000000000..f146a921e
--- /dev/null
+++ b/kimgio/xpm.kimgio
@@ -0,0 +1,85 @@
+[Image Format]
+Type=XPM
+Header=
+Name=X PixMap Image
+Name[af]='X PixMap' Beeld
+Name[ar]=صورة من نوع X PixMap
+Name[az]=X PixMap Rəsmi
+Name[be]=Малюнак "X PixMap"
+Name[bg]=X PixMap изображение
+Name[bn]=এক্স পিক্সম্যাপ চিত্র
+Name[br]=Skeudenn X PixMap
+Name[bs]=X PixMap slika
+Name[ca]=Imatge mapa de píxels de X
+Name[cs]=Obrázek ve formátu X Pixmap
+Name[csb]=Òbrôzk X PixMap
+Name[cy]=Delwedd Picsfap X
+Name[da]=X-PixMap-billede
+Name[de]=X-PixMap-Bild
+Name[el]=Εικόνα X PixMap
+Name[eo]=Xa grafiko
+Name[es]=Imagen mapa de pixels de X
+Name[et]=X pixmap pildifail
+Name[eu]=X PixMap irudia
+Name[fa]=تصویر نگاشت تصویردانه‌ای X
+Name[fi]=X-kuvakartta
+Name[fr]=Image X Pixmap
+Name[fy]=X PixMap ôfbylding
+Name[ga]=Íomhá "X PixMap"
+Name[gl]=Imaxe X PixMap
+Name[he]=תמונת מפת פיקסלים של X
+Name[hi]=X PixMap छवि
+Name[hr]=X PixMap slika
+Name[hu]=XPM-kép
+Name[id]=Gambar X PixMap
+Name[is]=X PixMap mynd
+Name[it]=Immagine X PixMap
+Name[ja]=X ピックスマップ画像
+Name[ka]=X PixMap ნახატი
+Name[kk]=X PixMap кескіні
+Name[km]=រូបភាព X PixMap
+Name[ko]=X용 픽스맵 그림
+Name[lb]=X-PixMap-Bild
+Name[lt]=X PixMap paveiksliukas
+Name[lv]=X PixMap attēls
+Name[mk]=X PixMap слика
+Name[mn]=X-Bitmap зураг
+Name[ms]=Imej X PixMap
+Name[nb]=X PixMap-bilde
+Name[nds]=X-PixMap-Bild
+Name[ne]=X पिक्सम्याप छवि
+Name[nl]=X Pixmap-afbeelding
+Name[nn]=X PixMap-bilete
+Name[pa]=X PixMap ਚਿੱਤਰ
+Name[pl]=Obrazek X PixMap
+Name[pt]=Imagem X PixMap
+Name[pt_BR]=Imagem X Pixmap
+Name[ro]=Imagine XPM
+Name[ru]=Рисунок X PixMap
+Name[rw]=X PixMap Ishusho
+Name[se]=X PixMap-govva
+Name[sk]=X PixMap obrázok
+Name[sl]=Rastrska slika za X
+Name[sq]=Imazh PixMap
+Name[sr]=X пикселмапирана слика
+Name[sr@Latn]=X pikselmapirana slika
+Name[sv]=X pixmapp-bild
+Name[ta]=XPixMap வரைப்பட பிம்பம்
+Name[te]=X పిక్స్ మేప్ ప్రతిబింబం
+Name[tg]=Тасвири X PixMap
+Name[th]=แฟ้มภาพพิกซ์แมพ X
+Name[tr]=X Resim Dosyası
+Name[tt]=X PixMap Süräte
+Name[uk]=Растрове зображення для X
+Name[uz]=X PMP-rasm
+Name[uz@cyrillic]=X PMP-расм
+Name[vi]=Ảnh sơ đồ điểm ảnh X
+Name[wa]=Imådje XPM
+Name[zh_CN]=X 像素图图像
+Name[zh_HK]=X PixMap 圖檔
+Name[zh_TW]=X 像素影像
+Read=true
+Write=true
+Suffices=xpm,XPM
+Mimetype=image/x-xpm
+Library=
diff --git a/kimgio/xv.kimgio b/kimgio/xv.kimgio
new file mode 100644
index 000000000..a5432e837
--- /dev/null
+++ b/kimgio/xv.kimgio
@@ -0,0 +1,87 @@
+[Image Format]
+Type=XV
+Header=^P7 332
+Flags=T
+Name=XView Image
+Name[af]=XView Beeld
+Name[ar]=صورة من نوع XView
+Name[az]=XView Rəsmi
+Name[be]=Відарыс XView
+Name[bg]=XView изображение
+Name[bn]=এক্স-ভিউ চিত্র
+Name[br]=Skeudenn XView
+Name[bs]=XView slika
+Name[ca]=Imatge XView
+Name[cs]=XView obrázek
+Name[csb]=Òbrôzk XView
+Name[cy]=Delwedd XView
+Name[da]=XView-billede
+Name[de]=XView-Bild
+Name[el]=Εικόνα XView
+Name[eo]=XView bildo
+Name[es]=Imagen XView
+Name[et]=XView pildifail
+Name[eu]=XView irudia
+Name[fa]=تصویر XView
+Name[fi]=XView-kuva
+Name[fr]=Image XView
+Name[fy]=XView ôfbylding
+Name[ga]=Íomhá XView
+Name[gl]=Imaxe XView
+Name[he]=תמונת XView
+Name[hi]=XView छवि
+Name[hr]=XView slika
+Name[hu]=XView-kép
+Name[id]=Gambar XView
+Name[is]=XView mynd
+Name[it]=Immagine XView
+Name[ja]=XView 画像
+Name[ka]=XView ნახატი
+Name[kk]=XView кескіні
+Name[km]=រូបភាព XView
+Name[ko]=XView 그림
+Name[lb]=XView-Bild
+Name[lt]=XView paveiksliukas
+Name[lv]=XView attēls
+Name[mk]=XView слика
+Name[mn]=XView-Format
+Name[ms]=Imej XView
+Name[nb]=XView-format
+Name[nds]=XView-Bild
+Name[ne]=X दृश्य छवि
+Name[nl]=XView-afbeelding
+Name[nn]=XView-bilete
+Name[pa]=XView ਚਿੱਤਰ
+Name[pl]=Obrazek XView
+Name[pt]=Imagem do XView
+Name[pt_BR]=Imagem XView
+Name[ro]=Imagine XView
+Name[ru]=Рисунок XView
+Name[rw]=XView Ishusho
+Name[se]=XView-govva
+Name[sk]=XView obrázok
+Name[sl]=Slika XView
+Name[sq]=Imazh XView
+Name[sr]=XView слика
+Name[sr@Latn]=XView slika
+Name[sv]=Xview-format
+Name[ta]=XView பிம்பம்
+Name[te]=ఎక్స్ వ్యు ప్రతిబింబం
+Name[tg]=Тасвири XНамуд
+Name[th]=แฟ้มภาพ XView
+Name[tr]=XView Resim Dosyası
+Name[tt]=XView Sürät
+Name[uk]=Зображення XView
+Name[uz]=XView-rasm
+Name[uz@cyrillic]=XView-расм
+Name[vi]=Ảnh XView
+Name[wa]=Imådje XView
+Name[zh_CN]=XView 图像
+Name[zh_HK]=XView 圖檔
+Name[zh_TW]=XView 影像
+Read=true
+Write=true
+Suffices=
+Mimetype=
+Library=kimg_xview.la
+
diff --git a/kimgio/xview.cpp b/kimgio/xview.cpp
new file mode 100644
index 000000000..d61a60fde
--- /dev/null
+++ b/kimgio/xview.cpp
@@ -0,0 +1,169 @@
+// Oliver Eiden <o.eiden@pop.ruhr.de>
+// 23.3.99
+// changed the mapping from 3-3-2 decoded pixels to 8-8-8 decoded true-color pixels
+// now it uses the same mapping as xv, this leads to better visual results
+// Patch merged in HEAD by Chris Spiegel <matrix@xirtam.org>
+// This library is distributed under the conditions of the GNU LGPL.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <qimage.h>
+
+#include <kdelibs_export.h>
+
+#include "xview.h"
+
+#define BUFSIZE 1024
+
+static const int b_255_3[]= {0,85,170,255}, // index*255/3
+ rg_255_7[]={0,36,72,109,145,182,218,255}; // index *255/7
+
+KDE_EXPORT void kimgio_xv_read( QImageIO *_imageio )
+{
+ int x=-1;
+ int y=-1;
+ int maxval=-1;
+ QIODevice *iodev = _imageio->ioDevice();
+
+ char str[ BUFSIZE ];
+
+ // magic number must be "P7 332"
+ iodev->readLine( str, BUFSIZE );
+ if (strncmp(str,"P7 332",6)) return;
+
+ // next line #XVVERSION
+ iodev->readLine( str, BUFSIZE );
+ if (strncmp(str, "#XVVERSION", 10))
+ return;
+
+ // now it gets interesting, #BUILTIN means we are out.
+ // if IMGINFO comes, we are happy!
+ iodev->readLine( str, BUFSIZE );
+ if (strncmp(str, "#IMGINFO:", 9))
+ return;
+
+ // after this an #END_OF_COMMENTS signals everything to be ok!
+ iodev->readLine( str, BUFSIZE );
+ if (strncmp(str, "#END_OF", 7))
+ return;
+
+ // now a last line with width, height, maxval which is
+ // supposed to be 255
+ iodev->readLine( str, BUFSIZE );
+ sscanf(str, "%d %d %d", &x, &y, &maxval);
+
+ if (maxval != 255) return;
+ int blocksize = x*y;
+ if(x < 0 || y < 0 || blocksize < x || blocksize < y)
+ return;
+
+ // now follows a binary block of x*y bytes.
+ char *block = (char*) malloc(blocksize);
+ if(!block)
+ return;
+
+ if (iodev->readBlock(block, blocksize) != blocksize )
+ {
+ return;
+ }
+
+ // Create the image
+ QImage image( x, y, 8, maxval + 1, QImage::BigEndian );
+ if( image.isNull()) {
+ free(block);
+ return;
+ }
+
+ // how do the color handling? they are absolute 24bpp
+ // or at least can be calculated as such.
+ int r,g,b;
+
+ for ( int j = 0; j < 256; j++ )
+ {
+ r = rg_255_7[((j >> 5) & 0x07)];
+ g = rg_255_7[((j >> 2) & 0x07)];
+ b = b_255_3[((j >> 0) & 0x03)];
+ image.setColor( j, qRgb( r, g, b ) );
+ }
+
+ for ( int py = 0; py < y; py++ )
+ {
+ uchar *data = image.scanLine( py );
+ memcpy( data, block + py * x, x );
+ }
+
+ _imageio->setImage( image );
+ _imageio->setStatus( 0 );
+
+ free(block);
+ return;
+}
+
+KDE_EXPORT void kimgio_xv_write( QImageIO *imageio )
+{
+ QIODevice& f = *( imageio->ioDevice() );
+
+ // Removed "f.open(...)" and "f.close()" (tanghus)
+
+ const QImage& image = imageio->image();
+ int w = image.width(), h = image.height();
+
+ char str[ 1024 ];
+
+ // magic number must be "P7 332"
+ f.writeBlock( "P7 332\n", 7 );
+
+ // next line #XVVERSION
+ f.writeBlock( "#XVVERSION:\n", 12 );
+
+ // now it gets interesting, #BUILTIN means we are out.
+ // if IMGINFO comes, we are happy!
+ f.writeBlock( "#IMGINFO:\n", 10 );
+
+ // after this an #END_OF_COMMENTS signals everything to be ok!
+ f.writeBlock( "#END_OF_COMMENTS:\n", 18 );
+
+ // now a last line with width, height, maxval which is supposed to be 255
+ sprintf( str, "%i %i 255\n", w, h );
+ f.writeBlock( str, strlen( str ) );
+
+
+ if ( image.depth() == 1 )
+ {
+ image.convertDepth( 8 );
+ }
+
+ uchar* buffer = new uchar[ w ];
+
+ for ( int py = 0; py < h; py++ )
+ {
+ uchar *data = image.scanLine( py );
+ for ( int px = 0; px < w; px++ )
+ {
+ int r, g, b;
+ if ( image.depth() == 32 )
+ {
+ QRgb *data32 = (QRgb*) data;
+ r = qRed( *data32 ) >> 5;
+ g = qGreen( *data32 ) >> 5;
+ b = qBlue( *data32 ) >> 6;
+ data += sizeof( QRgb );
+ }
+ else
+ {
+ QRgb color = image.color( *data );
+ r = qRed( color ) >> 5;
+ g = qGreen( color ) >> 5;
+ b = qBlue( color ) >> 6;
+ data++;
+ }
+ buffer[ px ] = ( r << 5 ) | ( g << 2 ) | b;
+ }
+ f.writeBlock( (const char*)buffer, w );
+ }
+ delete[] buffer;
+
+ imageio->setStatus( 0 );
+}
+
diff --git a/kimgio/xview.h b/kimgio/xview.h
new file mode 100644
index 000000000..4d9aa9945
--- /dev/null
+++ b/kimgio/xview.h
@@ -0,0 +1,12 @@
+// This library is distributed under the conditions of the GNU LGPL.
+#ifndef XVIEW_H
+#define XVIEW_H
+
+class QImageIO;
+
+extern "C" {
+void kimgio_xv_read( QImageIO * );
+void kimgio_xv_write( QImageIO * );
+}
+
+#endif