diff options
Diffstat (limited to 'kimgio')
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 |