[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-devel
Subject: [Patch] Some more features for PNG file plugin
From: Volker Krause <volker.krause () post ! rwth-aachen ! de>
Date: 2002-11-30 17:53:28
[Download RAW message or body]
Hello,
the attached patch adds some more features to the PNG file plugin:
- support for compressed textual information
- gamma value
- date/time of last modification of the image data
- number of palette entries
- background color of transparent images
Also, the whole plugin now uses libpng, which makes things a lot easier (no
self-made file parser, no magic numbers, etc.). Of course, it now depends on
libpng, but since there are other file plugins which have dependencies (e.g.
pdf), I assume, that this is not a problem.
I've testet the plugin with the offical PNG test suite, which also contains
corrupt files, everythings seems to work.
I plan to add the remaining png meta information as well, as soon as I
found/create some useable test images (the PNG test suite doesn't contain
examples for everything).
regards
Volker Krause
["kfile_png.diff" (text/x-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 <stdlib.h>
#include "kfile_png.h"
+#include <png.h>
#include <kurl.h>
-#include <kprocess.h>
-#include <klocale.h>
#include <kgenericfactory.h>
#include <kdebug.h>
-#include <qcstring.h>
-#include <qfile.h>
#include <qdatetime.h>
-#include <qdict.h>
-#include <qvalidator.h>
-
-// 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 <qcolor.h>
// 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<char*>(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()-12)
- {
- while (strncmp((char*)CHUNK_TYPE(data,index), "tEXt", 4) &&
- index < f.size() - 12)
- {
- if (!strncmp((char*)CHUNK_TYPE(data,index), "IEND", 4))
- goto end;
-
- index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE;
- }
-
- if (index<f.size())
- {
- // we found a tEXt field
- kdDebug(7034) << "We found a tEXt field\n";
- // get the key, it's a null terminated string at the
- // chunk start
-
- uchar* key = &CHUNK_DATA(data,index,0);
-
- int keysize=0;
- for (;key[keysize]!=0; keysize++)
- // look if we reached the end of the file
- // (it might be corrupted)
- if (8+index+keysize>=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<char*>(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<char*>(text_chunks[i].key)),
+ QString(reinterpret_cast<char*>(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"
>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic