--Boundary-00=_YsP69y6XC/46Q4v Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Hello, the attached patch adds some more features to the PNG file plugin: =2D support for compressed textual information =2D gamma value =2D date/time of last modification of the image data =2D number of palette entries =2D background color of transparent images Also, the whole plugin now uses libpng, which makes things a lot easier (no= =20 self-made file parser, no magic numbers, etc.). Of course, it now depends o= n=20 libpng, but since there are other file plugins which have dependencies (e.g= =2E=20 pdf), I assume, that this is not a problem. I've testet the plugin with the offical PNG test suite, which also contains= =20 corrupt files, everythings seems to work. I plan to add the remaining png meta information as well, as soon as I=20 found/create some useable test images (the PNG test suite doesn't contain=20 examples for everything). regards Volker Krause --Boundary-00=_YsP69y6XC/46Q4v Content-Type: text/x-diff; charset="us-ascii"; name="kfile_png.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="kfile_png.diff" ? png.kdevelop Index: kfile_png.cpp =================================================================== RCS file: /home/kde/kdegraphics/kfile-plugins/png/kfile_png.cpp,v retrieving revision 1.19 diff -u -r1.19 kfile_png.cpp --- kfile_png.cpp 9 Oct 2002 15:10:05 -0000 1.19 +++ kfile_png.cpp 30 Nov 2002 17:23:19 -0000 @@ -18,28 +18,15 @@ * $Id: kfile_png.cpp,v 1.19 2002/10/09 15:10:05 pfeiffer Exp $ */ -#include #include "kfile_png.h" +#include #include -#include -#include #include #include -#include -#include #include -#include -#include - -// some defines to make it easier -// don't tell me anything about preprocessor usage :) -#define CHUNK_SIZE(data, index) ((data[index ]<<24) + (data[index+1]<<16) + \ - (data[index+2]<< 8) + data[index+3]) -#define CHUNK_TYPE(data, index) &data[index+4] -#define CHUNK_HEADER_SIZE 12 -#define CHUNK_DATA(data, index, offset) data[8+index+offset] +#include // known translations for common png keys static const char* knownTranslations[] @@ -115,135 +102,137 @@ addItemInfo(group, "ColorMode", i18n("Color mode"), QVariant::String); addItemInfo(group, "Compression", i18n("Compression"), QVariant::String); addItemInfo(group, "InterlaceMode", i18n("Interlace mode"),QVariant::String); + addItemInfo(group, "Gamma", i18n("Gamma"), QVariant::Double); + addItemInfo(group, "LastModified", i18n("Last modified"), QVariant::DateTime); + addItemInfo(group, "PaletteEntries", i18n("Number of palette entries"), QVariant::Int); + addItemInfo(group, "BackgroundColor", i18n("Background color"), QVariant::Color); } -bool KPngPlugin::readInfo( KFileMetaInfo& info, uint what) -{ - QFile f(info.path()); - f.open(IO_ReadOnly); - - if (f.size() < 26) return false; - // the technical group will be read from the first 26 bytes. If the file - // is smaller, we can't even read this. - - bool readComments = false; - if (what & (KFileMetaInfo::Fastest | - KFileMetaInfo::DontCare | - KFileMetaInfo::ContentInfo)) readComments = true; - - uchar *data = new uchar[f.size()+1]; - f.readBlock(reinterpret_cast(data), f.size()); - data[f.size()]='\n'; - - // find the start - if (data[0] == 137 && data[1] == 80 && data[2] == 78 && data[3] == 71 && - data[4] == 13 && data[5] == 10 && data[6] == 26 && data[7] == 10 ) - { - // ok - // the IHDR chunk should be the first - if (!strncmp((char*)&data[12], "IHDR", 4)) - { - // we found it, get the dimensions - ulong x,y; - x = (data[16]<<24) + (data[17]<<16) + (data[18]<<8) + data[19]; - y = (data[20]<<24) + (data[21]<<16) + (data[22]<<8) + data[23]; - - uint type = data[25]; - uint bpp = data[24]; - kdDebug(7034) << "dimensions " << x << "*" << y << endl; - - // the bpp are only per channel, so we need to multiply the with - // the channel count - switch (type) - { - case 0: break; // Grayscale - case 2: bpp *= 3; break; // RGB - case 3: break; // palette - case 4: bpp *= 2; break; // grayscale w. alpha - case 6: bpp *= 4; break; // RGBA - - default: // we don't get any sensible value here - bpp = 0; - } - - KFileMetaInfoGroup techgroup = appendGroup(info, "Technical"); - - appendItem(techgroup, "Dimensions", QSize(x, y)); - appendItem(techgroup, "BitDepth", bpp); - appendItem(techgroup, "ColorMode", - (type < sizeof(colors)/sizeof(colors[0])) - ? i18n(colors[data[25]]) : i18n("Unknown")); - - appendItem(techgroup, "Compression", - (data[26] < sizeof(compressions)/sizeof(compressions[0])) - ? i18n(compressions[data[26]]) : i18n("Unknown")); - - appendItem(techgroup, "InterlaceMode", - (data[28] < sizeof(interlaceModes)/sizeof(interlaceModes[0])) - ? i18n(interlaceModes[data[28]]) : i18n("Unknown")); - } - - // look for a tEXt chunk - if (readComments) - { - uint index = 8; - index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; - KFileMetaInfoGroup commentGroup = appendGroup(info, "Comment"); - - while(index=f.size()) - goto end; - - // the text comes after the key, but isn't null terminated - uchar* text = &CHUNK_DATA(data,index, keysize+1); - int textsize = CHUNK_SIZE(data, index)-keysize-1; - - // security check - if ( (uint)(text - data) + textsize > f.size()) - goto end; - - QByteArray arr(textsize); - arr = QByteArray(textsize).duplicate((const char*)text, - textsize); - - appendItem(commentGroup, - QString(reinterpret_cast(key)), - QString(arr)); - - kdDebug(7034) << "adding " << key << " / " - << QString(arr) << endl; - - index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; - } - } - } - } -end: - delete[] data; - return true; +bool KPngPlugin::readInfo( KFileMetaInfo& info, uint) { + // open file + FILE *fp = fopen(info.path().local8Bit(), "rb"); + if(!fp) + return false; + + // create png structs + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL ,NULL); + if (!png_ptr) + return false; + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return false; + } + + // error handling for png I/O + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + return false; + } + + // read the file + png_init_io(png_ptr, fp); + png_read_info(png_ptr, info_ptr); + + // IHDR Chunk + png_uint_32 height, width; + int bit_depth, color_type, interlace_type, compression_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, &compression_type, NULL); + + // the bpp are only per channel, so we need to multiply the with the channel count + int bpp = bit_depth; + switch (color_type) { + case PNG_COLOR_TYPE_GRAY : break; + case PNG_COLOR_TYPE_RGB : bpp *= 3; break; + case PNG_COLOR_TYPE_PALETTE : break; + case PNG_COLOR_TYPE_GRAY_ALPHA: bpp *= 2; break; + case PNG_COLOR_TYPE_RGB_ALPHA : bpp *= 4; break; + default: + // the file has an unknown color type, we can't handle it + return false; + } + + KFileMetaInfoGroup techgroup = appendGroup(info, "Technical"); + appendItem(techgroup, "Dimensions", QSize(height, width)); + appendItem(techgroup, "BitDepth", bpp); + appendItem(techgroup, "ColorMode", + ((uint)color_type < sizeof(colors)/sizeof(colors[0])) + ? i18n(colors[color_type]) : i18n("Unknown")); + appendItem(techgroup, "Compression", + ((uint)compression_type < sizeof(compressions)/sizeof(compressions[0])) + ? i18n(compressions[compression_type]) : i18n("Unknown")); + appendItem(techgroup, "InterlaceMode", + ((uint)interlace_type < sizeof(interlaceModes)/sizeof(interlaceModes[0])) + ? i18n(interlaceModes[interlace_type]) : i18n("Unknown")); + + // textual information + KFileMetaInfoGroup commentGroup = appendGroup(info, "Comment"); + png_text* text_chunks; + int count = 0; + count = png_get_text(png_ptr, info_ptr, &text_chunks, NULL); + for(int i = 0; i < count; i++) { + appendItem(commentGroup, + QString(reinterpret_cast(text_chunks[i].key)), + QString(reinterpret_cast(text_chunks[i].text))); + } + + // gamma value + if(png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { + double gamma = 0.0; + png_get_gAMA(png_ptr, info_ptr, &gamma); + appendItem(techgroup, "Gamma", gamma); + } + + // last modification time + if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tIME)) { + png_timep modified; + png_get_tIME(png_ptr, info_ptr, &modified); + QDate date(modified->year, modified->month, modified->day); + QTime time(modified->hour, modified->minute, modified->second); + appendItem(techgroup, "LastModified", QDateTime(date,time)); + } + + // number of palette entries + png_colorp palette; + if(png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) { + int count = 0; + png_get_PLTE(png_ptr, info_ptr, &palette, &count); + appendItem(techgroup, "PaletteEntries", count); + } + + // background color + if(png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { + png_color_16p bkgd; + png_get_bKGD(png_ptr, info_ptr, &bkgd); + int r = 0, g = 0, b = 0; + if(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { + r = bkgd->red; + g = bkgd->green; + b = bkgd->blue; + } + if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + r = g = b = bkgd->gray; + } + if(color_type == PNG_COLOR_TYPE_PALETTE) { + r = palette[bkgd->index].red; + g = palette[bkgd->index].green; + b = palette[bkgd->index].blue; + } + // scale down to 8 bit color since QColor doesn't support 16 bit colors + if(bit_depth > 8) { + r >>= 8; + g >>= 8; + b >>= 8; + } + appendItem(techgroup, "BackgroundColor", QColor(r,g,b)); + } + + // destroy png structs and close file + png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + return true; } #include "kfile_png.moc" --Boundary-00=_YsP69y6XC/46Q4v-- >> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<