[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdebase/workspace
From:       Oswald Buddenhagen <ossi () kde ! org>
Date:       2010-03-20 21:25:24
Message-ID: 20100320212524.80C26AC870 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1105670 by ossi:

add wallpaper attribute to <pixmap> elements

this includes support for plasma wallpaper packages and fuzzy matching
of image sizes.

 M  +16 -4     doc/kdm/theme-ref.docbook  
 M  +107 -16   kdm/kfrontend/themer/kdmpixmap.cpp  
 M  +5 -1      kdm/kfrontend/themer/kdmpixmap.h  


--- trunk/KDE/kdebase/workspace/doc/kdm/theme-ref.docbook #1105669:1105670
@@ -932,15 +932,27 @@
             </para>
 
             <para>
+              <literal>wallpaper</literal> can be used instead of
+              <literal>file</literal> to have &kdm; look for images in the
+              usual locations for &kde; wallpapers. Plasma wallpaper packages
+              are supported.
+            </para>
+
+            <para>
               <literal>element</literal> specifies the element id of a SVG file.
               If empty, the whole SVG image will be rendered.
             </para>
 
             <para>
-              For <literal>pixmap</literal> nodes, &kdm; first tries to open
-              a file that matches \
<replaceable>basename</replaceable><literal>-</literal><replaceable>width</replaceable \
><literal>x</literal><replaceable>height</replaceable><literal>.</literal><replaceable>extension</replaceable>
> 
-              based on the specified file name, so the best-quality image
-              for a given resolution can be used.
+              For <literal>pixmap</literal> nodes, it is possible to provide
+              multiple images, so the best-quality image for a given resolution
+              can be used. Size-tagged file names have the format
+              <replaceable>basename</replaceable><literal>-</literal><replaceable>wid \
th</replaceable><literal>x</literal><replaceable>height</replaceable><literal>.</literal><replaceable>extension</replaceable>.
 +              If the not size-tagged file exists and it is no Plasma
+              wallpaper package, &kdm; will accept only a perfect match
+              for a given size and otherwise fall back to the original file.
+              Otherwise it will try to find an image whose dimensions come
+              closest to the required size if no perfect match is found.
             </para>
 
             <!--
--- trunk/KDE/kdebase/workspace/kdm/kfrontend/themer/kdmpixmap.cpp #1105669:1105670
@@ -22,11 +22,15 @@
 #include "kdmpixmap.h"
 #include "kdmthemer.h"
 
+#include <kstandarddirs.h>
 #include <ksvgrenderer.h>
 
+#include <QDirIterator>
 #include <QPainter>
 #include <QSignalMapper>
 
+#include <math.h>
+
 KdmPixmap::KdmPixmap( QObject *parent, const QDomNode &node )
 	: KdmItem( parent, node )
 	, qsm( 0 )
@@ -65,8 +69,26 @@
 KdmPixmap::definePixmap( const QDomElement &el, PixmapStruct::PixmapClass &pClass )
 {
 	QString fileName = el.attribute( "file" );
-	if (fileName.isEmpty())
-		return;
+	if (!fileName.isEmpty()) {
+		if (fileName.at( 0 ) != '/')
+			fileName.prepend( themer()->baseDir() + '/' );
+		pClass.fullpath = fileName;
+	} else {
+		fileName = el.attribute( "wallpaper" );
+		if (fileName.isEmpty())
+			return;
+		QString xf = KStandardDirs::locate( "wallpaper", fileName + "/contents/images/" );
+		if (!xf.isEmpty()) {
+			pClass.package = true;
+		} else {
+			xf = KStandardDirs::locate( "wallpaper", fileName );
+			if (xf.isEmpty()) {
+				kWarning() << "Cannot find wallpaper" << fileName;
+				return;
+			}
+		}
+		pClass.fullpath = xf;
+	}
 
 	QString aspect = el.attribute( "scalemode", "free" );
 	pClass.aspectMode =
@@ -74,15 +96,61 @@
 			(aspect == "crop") ? Qt::KeepAspectRatioByExpanding :
 			Qt::IgnoreAspectRatio;
 
-	pClass.fullpath = fileName;
-	if (fileName.at( 0 ) != '/')
-		pClass.fullpath = themer()->baseDir() + '/' + fileName;
-
 	pClass.svgImage = fileName.endsWith( ".svg" ) || fileName.endsWith( ".svgz" );
 	if (pClass.svgImage)
 		pClass.svgElement = el.attribute( "element" );
 }
 
+QString
+KdmPixmap::findBestPixmap( const QString &dir, const QString &pat,
+                           const QRect &area, Qt::AspectRatioMode aspectMode )
+{
+	int tw = area.width(), th = area.height();
+	float tar = 1.0 * tw / th;
+	float tpn = tw * th;
+	QString best;
+	float bestPenalty = 0;
+	QRegExp rx( QRegExp::escape( dir ) + pat );
+	QDirIterator dit( dir );
+	while (dit.hasNext()) {
+		QString fn = dit.next();
+		if (rx.exactMatch( fn )) {
+			int w = rx.cap( 1 ).toInt(), h = rx.cap( 2 ).toInt();
+			// This algorithm considers need for zooming and distortion of
+			// aspect ratio / discarded pixels / pixels left free. The weighting
+			// gives good results in the tested cases, but is pretty arbitrary
+			// by all practical means.
+			float ar = 1.0 * w / h;
+			float pn = w * h;
+			float rawAspect = ((tar > ar) ? tar / ar : ar / tar);
+			float penalty = rawAspect - 1;
+			if (aspectMode != Qt::IgnoreAspectRatio) {
+				bool exp = (aspectMode == Qt::KeepAspectRatioByExpanding);
+				// Give an advantage to non-zooming cases.
+				if ((w == tw && (h > th) == exp) || (h == th && (w > tw) == exp))
+					goto skipSize;
+				else
+					penalty *= 5;
+				// Dropped pixels don't contribute to the input area.
+				if (exp)
+					pn /= rawAspect;
+			} else {
+				// This mode does not preserve aspect ratio, so give an
+				// advantage to pics with a better ratio to start with.
+				penalty *= 10;
+			}
+			// Too small is worse than too big - within limits.
+			penalty += sqrt( (tpn > pn) ? (tpn / pn - 1) * 2 : pn / tpn - 1 );
+		    skipSize:
+			if (best.isEmpty() || penalty < bestPenalty) {
+				best = fn;
+				bestPenalty = penalty;
+			}
+		}
+	}
+	return best;
+}
+
 bool
 KdmPixmap::loadPixmap( PixmapStruct::PixmapClass &pClass )
 {
@@ -90,20 +158,43 @@
 		return true;
 	if (pClass.fullpath.isEmpty())
 		return false;
-	if (area.isValid()) {
-		int dot = pClass.fullpath.lastIndexOf( '.' );
-		if (pClass.image.load( pClass.fullpath.left( dot )
-				.append( QString( "-%1x%2" )
-					.arg( area.width() ).arg( area.height() ) )
-				.append( pClass.fullpath.mid( dot ) ) ))
-			goto gotit;
+	QString fn;
+	if (pClass.package) {
+		// Always find best fit from package.
+		fn = findBestPixmap( pClass.fullpath, "(\\d+)x(\\d+)\\.[^.]+",
+		                     area.isValid() ? area : QRect( 0, 0, 1600, 1200 ),
+		                     pClass.aspectMode );
+	} else {
+		if (area.isValid()) {
+			if (QFile::exists( pClass.fullpath )) {
+				// If base file exists, use only a perfect match.
+				int dot = pClass.fullpath.lastIndexOf( '.' );
+				fn = pClass.fullpath.left( dot );
+				fn += QString( "-%1x%2" ).arg( area.width() ).arg( area.height() );
+				fn += pClass.fullpath.mid( dot );
+				if (!QFile::exists( fn ))
+					fn = pClass.fullpath;
+			} else {
+				// Otherwise find best match.
+				int sep = pClass.fullpath.lastIndexOf( '/' );
+				int dot = pClass.fullpath.lastIndexOf( '.' );
+				if (dot < sep)
+					dot = pClass.fullpath.length();
+				QString f = QRegExp::escape( pClass.fullpath.mid( sep + 1, dot - sep - 1 ) );
+				f += "-(\\d+)x(\\d+)";
+				f += QRegExp::escape( pClass.fullpath.mid( dot ) );
+				fn = findBestPixmap( pClass.fullpath.left( sep + 1 ), f, area,
+				                     pClass.aspectMode );
+			}
+		} else {
+			fn = pClass.fullpath;
+		}
 	}
-	if (!pClass.image.load( pClass.fullpath )) {
-		kWarning() << "failed to load " << pClass.fullpath ;
+	if (!pClass.image.load( fn )) {
+		kWarning() << "failed to load" << fn;
 		pClass.fullpath.clear();
 		return false;
 	}
-  gotit:
 	if (pClass.image.format() != QImage::Format_ARGB32)
 		pClass.image = pClass.image.convertToFormat( QImage::Format_ARGB32 );
 	applyTint( pClass, pClass.image );
--- trunk/KDE/kdebase/workspace/kdm/kfrontend/themer/kdmpixmap.h #1105669:1105670
@@ -55,7 +55,8 @@
 	struct PixmapStruct {
 		struct PixmapClass {
 			PixmapClass()
-				: svgRenderer(0), present(false), svgImage(false), \
aspectMode(Qt::IgnoreAspectRatio) {} +				: svgRenderer(0), present(false), \
svgImage(false), package(false), +				  aspectMode(Qt::IgnoreAspectRatio) {}
 			QString fullpath;
 			QImage image;
 			KSvgRenderer *svgRenderer;
@@ -64,6 +65,7 @@
 			QColor tint;
 			bool present;
 			bool svgImage;
+			bool package;
 			QString svgElement;
 			QSize svgSizeHint;
 			Qt::AspectRatioMode aspectMode;
@@ -75,6 +77,8 @@
 private:
 	// Method to load the image given by the theme
 	void definePixmap( const QDomElement &el, PixmapStruct::PixmapClass &pc );
+	QString findBestPixmap( const QString &dir, const QString &pat,
+	                        const QRect &area, Qt::AspectRatioMode aspectMode );
 	bool loadPixmap( PixmapStruct::PixmapClass &pc );
 	bool loadSvg( PixmapStruct::PixmapClass &pc );
 	bool calcTargetArea( PixmapStruct::PixmapClass &pClass, const QSize &sh );


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic