[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