Index: includes/DefaultSettings.php
===================================================================
--- includes/DefaultSettings.php	(revision 48720)
+++ includes/DefaultSettings.php	(working copy)
@@ -2007,6 +2007,7 @@
 	'image/vnd.djvu' => 'DjVuHandler', // official
 	'image/x.djvu' => 'DjVuHandler', // compat
 	'image/x-djvu' => 'DjVuHandler', // compat
+	'image/x-xcf' => 'XcfHandler',
 );
 
 
@@ -2064,6 +2065,17 @@
 $wgSVGConverterPath = '';
 /** Don't scale a SVG larger than this */
 $wgSVGMaxSize = 2048;
+
+# An external program is required to perform this conversion:
+$wgXCFConverters = array(
+	'pyxcf' => '$path/pyxcf -i $input -o $output -w $width -h $height',
+	);
+/** Pick one of the above */
+$wgXCFConverter = 'pyxcf';
+/** If not in the executable PATH, specify */
+$wgXCFConverterPath = '';
+/** Don't scale a XCF larger than this */
+$wgXCFMaxSize = 2048;
 /**
  * Don't thumbnail an image if it will use too much working memory
  * Default is 50 MB if decompressed to RGBA form, which corresponds to
Index: includes/AutoLoader.php
===================================================================
--- includes/AutoLoader.php	(revision 48720)
+++ includes/AutoLoader.php	(working copy)
@@ -401,6 +401,7 @@
 	'ImageHandler' => 'includes/media/Generic.php',
 	'MediaHandler' => 'includes/media/Generic.php',
 	'SvgHandler' => 'includes/media/SVG.php',
+	'XcfHandler' => 'includes/media/XCF.php',
 
 	# includes/normal
 	'UtfNormal' => 'includes/normal/UtfNormal.php',
Index: includes/media/XCF.php
===================================================================
--- includes/media/XCF.php	(revision 0)
+++ includes/media/XCF.php	(revision 0)
@@ -0,0 +1,109 @@
+<?php
+
+class XcfHandler extends ImageHandler {
+	function isEnabled() {
+		global $wgXCFConverters, $wgXCFConverter;
+		if ( !isset( $wgXCFConverters[$wgXCFConverter] ) ) {
+			wfDebug( "\$wgXCFConverter is invalid, disabling XCF rendering.\n" );
+			return false;
+		} else {
+			return true;
+		}
+	}
+
+	function mustRender( $file ) { return true; }
+
+	function normaliseParams( $image, &$params ) {
+		global $wgXCFMaxSize;
+		if ( !parent::normaliseParams( $image, $params ) ) {
+			return false;
+		}
+
+		$params['physicalWidth'] = $params['width'];
+		$params['physicalHeight'] = $params['height'];
+		if ( $params['physicalWidth'] > $wgXCFMaxSize ) {
+			$srcWidth = $image->getWidth( $params['page'] );
+			$srcHeight = $image->getHeight( $params['page'] );
+			$params['physicalWidth'] = $wgXCFMaxSize;
+			$params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight, $wgXCFMaxSize );
+		}
+		return true;
+	}
+
+	function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+		global $wgXCFConverters, $wgXCFConverter, $wgXCFConverterPath;
+
+		if ( !$this->normaliseParams( $image, $params ) ) {
+			return new TransformParameterError( $params );
+		}
+		$clientWidth = $params['width'];
+		$clientHeight = $params['height'];
+		$physicalWidth = $params['physicalWidth'];
+		$physicalHeight = $params['physicalHeight'];
+		$srcPath = $image->getPath();
+
+		if ( $flags & self::TRANSFORM_LATER ) {
+			return new ThumbnailImage( $image, $dstUrl, $clientWidth, $clientHeight, $dstPath );
+		}
+
+		if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+			return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
+				wfMsg( 'thumbnail_dest_directory' ) );
+		}
+
+		$status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight );
+		if( $status === true ) {
+			return new ThumbnailImage( $image, $dstUrl, $clientWidth, $clientHeight, $dstPath );
+		} else {
+			return $status;
+		}
+	}
+
+	public function rasterize( $srcPath, $dstPath, $width, $height ) {
+		global $wgXCFConverters, $wgXCFConverter, $wgXCFConverterPath;
+
+		$output = false;
+		if ( isset( $wgXCFConverters[$wgXCFConverter] ) && $wgXCFConverter == 'pyxcf' ) {
+			$cmd = str_replace(
+				array( '$path/', '$input', '$output', '$width', '$height' ),
+				array( $wgXCFConverterPath ? wfEscapeShellArg( "$wgXCFConverterPath/" ) : "",
+					   wfEscapeShellArg( $srcPath ),
+					   wfEscapeShellArg( $dstPath ),
+					   intval( $width ),
+					   intval( $height ) ),
+				$wgXCFConverters[$wgXCFConverter]
+			) . " 2>&1";
+
+			wfProfileIn( 'xcf' );
+			wfDebug( __METHOD__.": $cmd\n" );
+			$output = wfShellExec( $cmd, $retval );
+			wfProfileOut( 'xcf' );
+		}
+		$removed = $this->removeBadFile( $dstPath, $retval );
+		if ( $retval != 0 || $removed ) {
+			wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
+					wfHostname(), $retval, trim($output), $cmd ) );
+			return new MediaTransformError( 'thumbnail_error', $width, $height );
+		}
+		return true;
+	}
+
+	function getImageSize( $image, $path ) {
+		$f = fopen( $path, 'r' );
+		if (!$f)
+			return false;
+		$header = fread( $f, '22' );
+		
+		$width = substr( $header, 14, 4 );
+		$height = substr( $header, 18, 4 );
+		
+		$width = unpack( 'N', $width );
+		$height = unpack ( 'N', $height );
+		
+		return array( $width[1], $height[1]);
+	}
+
+	function getThumbType( $ext, $mime ) {
+		return array( 'png', 'image/png' );
+	}
+}
