JPG Wasserzeichen aber EXIF beibehalten

Alles rund um die Programmierung mit Qt
Antworten
lx2
Beiträge: 75
Registriert: 5. Mai 2009 03:13

JPG Wasserzeichen aber EXIF beibehalten

Beitrag von lx2 »

Hallo Forum,

ich möchte gerne ein Wasserzeichen über JPGs zeichnen (muss nicht transparent sein) und danach in eine neue Datei abspeichern. Dabei soll die original JPG so wenig wie möglich verändert werden. Vor allem die EXIF-Metadaten sollen erhalten bleiben.

Momentan gehe ich so vor:
  • JPG laden in ein QImage
  • Wasserzeichen laden in ein QImage
  • Beide mit einem QPainter kombinieren
  • QImage abspeichern
Dabei wird natürlich die komplette JPG nochmal neu geschieben und hat mit den Ausgangsdaten sog gut wie gar nichts mehr zu tun.

Ich hab mir mal qexifimageheader.h und qexifimageheader.cpp von einem alten QTExtend besorgt.
Nur erstens generiert das JPG-Dateien die nicht lesbar sind (zumindest mit meinem QT 4.8.3) - und zweitens bin ich zum kombinieren immer noch auf die obere methode angewiesen (JPG wird neugeschrieben...)

Perfekt wäre eine lösung in der ich die Original JPG in einen Datenstrom einlese und irgendwo mittig im Bild mein Wasserzeichen (ebenfalls als z.B. QByteArray) über die Originaldaten drüberschreibe. Leider ist nur die JPG Kompression so komplex, dass diese Lösung nicht so einfach durchführbar ist, wenn hinten nocht ein einigermaßen vernünftiges Bild rauskommen soll.

Habt ihr eine Lösungsidee (am besten QT-intern ohne verwendung zusätzlicher Libaries)
Beste Grüße aus München,
Alex
veeman
Beiträge: 280
Registriert: 3. Oktober 2012 01:43
Kontaktdaten:

Re: JPG Wasserzeichen aber EXIF beibehalten

Beitrag von veeman »

Anscheinend hat die Speicher-Routine der Klasse einen Fehler, wo genau der ist hab ich nicht geschaut.

Wenn du EXIF-Daten im Original behalten willst, dann kannst du diese einfach in die neue Datei rüber kopieren.

Beispiel:

Code: Alles auswählen

int CopyExif(QString from, QString to) 
{
	const char *JPG_ID = "\xFF\xD8";
	const char *JPG_EXIF_CHUNK = "\xFF\xE1";
	const char *JPG_EXIF_ID = "Exif";

	QFile fileFrom(from);
	QFile fileTo(to);
	quint8 len[2];
	quint16 length = 0;

	if (!fileFrom.open(QIODevice::ReadWrite) || 
		!fileTo.open(QIODevice::ReadWrite))
		return -1;

	if ((fileFrom.read(2) != JPG_ID) || (fileTo.read(2) != JPG_ID)) // invalid jpg file
		return -2;

	// search exif data in src file
	while (true)
    {
		QByteArray chunkID = fileFrom.read(2);

        if( fileFrom.atEnd() )
            return -3;

		fileFrom.read((char*)len, 2);
		length = (quint16(len[0]) << 8) | quint16(len[1]);

		if (chunkID != JPG_EXIF_CHUNK)
			fileFrom.seek(fileFrom.pos() + length - 2);
		else
			if (fileFrom.read(strlen(JPG_EXIF_ID)) == JPG_EXIF_ID)
			break;
		else
			fileFrom.seek(fileFrom.pos() + length - 2 - 4);
    }

	QByteArray exif = fileFrom.read(length-4-2);
	fileFrom.close();

	// build new jpeg image
	QByteArray newContent;
	newContent.reserve(fileTo.size() + exif.size() + 2 + 4);
	newContent.append(JPG_ID);
	newContent.append(JPG_EXIF_CHUNK);
	newContent.append((char*)len, 2);
	newContent.append(JPG_EXIF_ID);
	newContent.append(exif);

	// copy data
	while (true)
    {
		QByteArray chunkID = fileTo.read(2);

        if( fileTo.atEnd() )
		{
			newContent.append(chunkID);
            break;
		}

		fileTo.read((char*)len, 2);
		length = (quint16(len[0]) << 8) | quint16(len[1]);

		QByteArray chunkData = fileTo.read(length);
		if (!((chunkID == JPG_EXIF_CHUNK) && (chunkData.left(strlen(JPG_EXIF_ID)) == JPG_EXIF_ID)))
		{
			newContent.append(chunkID);
			newContent.append((char*)len, 2);
			newContent.append(chunkData);
		}
	}

	// write new data
	fileTo.seek(0);
	fileTo.resize(newContent.size());
	fileTo.write(newContent);
	fileTo.close();

	return 0;
}
Mfg veeman
Antworten