rewrite getXCFMetaData() to get ride of im identify
authorAntoine Musso <hashar@users.mediawiki.org>
Thu, 5 Jan 2012 14:48:55 +0000 (14:48 +0000)
committerAntoine Musso <hashar@users.mediawiki.org>
Thu, 5 Jan 2012 14:48:55 +0000 (14:48 +0000)
By reading the file header and unpacking the data, we can avoid
shelling out to imagemagick 'identify'. Save up some CPU cycles :D

An XCF is made of a canvas of a given width / height, the various
layers are applied to it which must fit in the canvas. So we just
use the canvas size :-)

I do not think we have any usage for channels count, so I have just
skip that part. I am not sure it makes any sense when the picture
can be made of several layers each using different channels count.

Bits per color is always 8 per definition. Grayscale is 0 - 255
and indexed palette is 256 colors at most.

XCF spec:
http://svn.gnome.org/viewvc/gimp/trunk/devel-docs/xcf.txt?view=markup

pack() / unpack() is familiar to perl monkeys

Fully reimplements r107351

includes/media/XCF.php

index 837c855..c72927b 100644 (file)
@@ -42,55 +42,75 @@ class XCFHandler extends BitmapHandler {
                return self::getXCFMetaData( $filename );
        }
 
+       /**
+        * Metadata for a given XCF file
+        *
+        * Will return false if file magic signature is not recognized
+        * @author Hexmode
+        * @author Hashar
+        *
+        * @param $filename String Full path to a XCF file
+        * @return false|metadata array just like PHP getimagesize()
+        */
        static function getXCFMetaData( $filename ) {
-               global $wgImageMagickIdentifyCommand;
-
-               $cmd = wfEscapeShellArg( $wgImageMagickIdentifyCommand ) . ' -verbose ' . wfEscapeShellArg( $filename );
-               wfDebug( __METHOD__ . ": Running $cmd \n" );
-
-               $retval = null;
-               $return = wfShellExec( $cmd, $retval );
-               if( $retval !== 0 ) {
-                       wfDebug( __METHOD__ . ": error encountered while running $cmd\n" );
+               # Decode master structure
+               $f = fopen( $filename, 'rb' );
+               if( !$f ) {
                        return false;
                }
+               # The image structure always starts at offset 0 in the XCF file.
+               # So we just read it :-)
+               $binaryHeader = fread( $f, 26 );
+               fclose($f);
 
-               $colorspace = preg_match_all( '/ *Colorspace: RGB/', $return, $match );
-               $frameCount = preg_match_all( '/ *Geometry: ([0-9]+x[0-9]+)\+[+0-9]*/', $return, $match );
-               wfDebug( __METHOD__ . ": Got $frameCount matches\n" );
-
-               /* if( $frameCount == 1 ) { */
-               /*      preg_match( '/([0-9]+)x([0-9]+)/sm', $match[1][0], $m ); */
-               /*      $sizeX = $m[1]; */
-               /*      $sizeY = $m[2]; */
-               /* } else { */
-                       $sizeX = 0;
-                       $sizeY = 0;
+               # Master image structure:
+               #
+               # byte[9] "gimp xcf "  File type magic
+               # byte[4] version      XCF version
+               #                        "file" - version 0
+               #                        "v001" - version 1
+               #                        "v002" - version 2
+               # byte    0            Zero-terminator for version tag
+               # uint32  width        With of canvas
+               # uint32  height       Height of canvas
+               # uint32  base_type    Color mode of the image; one of
+               #                         0: RGB color
+               #                         1: Grayscale
+               #                         2: Indexed color
+               #        (enum GimpImageBaseType in libgimpbase/gimpbaseenums.h)
+               $header = unpack(
+                         "A9magic"     # A: space padded
+                       . "/a5version"  # a: zero padded
+                       . "/Nwidth"     # \
+                       . "/Nheight"    # N: unsigned long 32bit big endian
+                       . "/Nbase_type" # /
+               , $binaryHeader
+               );
 
-                       # Find out the largest width and height used in any frame
-                       foreach( $match[1] as $res ) {
-                               preg_match( '/([0-9]+)x([0-9]+)/sm', $res, $m );
-                               if( $m[1] > $sizeX ) {
-                                       $sizeX = $m[1];
-                               }
-                               if( $m[2] > $sizeY ) {
-                                       $sizeY = $m[2];
-                               }
-                       }
-               /* } */
+               # Check values
+               if( $header['magic'] !== 'gimp xcf' ) {
+                       var_dump( $header );
+                       wfDebug( __METHOD__ . " '$filename' has invalid magic signature.\n" );
+                       return false;
+               }
+               # TODO: we might want to check for sane values of width and height
 
-               wfDebug( __METHOD__ . ": Found $sizeX x $sizeY x $frameCount \n" );
+               wfDebug( __METHOD__ . ": canvas size of '$filename' is {$header['width']} x {$header['height']} px\n" );
 
                # Forge a return array containing metadata information just like getimagesize()
                # See PHP documentation at: http://www.php.net/getimagesize
                $metadata = array();
-               $metadata['frameCount'] = $frameCount;
-               $metadata[0] = $sizeX;
-               $metadata[1] = $sizeY;
-               $metadata[2] = null;
-               $metadata[3] = "height=\"$sizeY\" width=\"$sizeX\"";
+               $metadata[0] = $header['width'];
+               $metadata[1] = $header['height'];
+               $metadata[2] = null;   # IMAGETYPE constant, none exist for XCF.
+               $metadata[3] = sprintf(
+                       'height="%s" width="%s"', $header['height'], $header['width']
+               );
                $metadata['mime'] = 'image/x-xcf';
-               $metadata['channels'] = $colorspace == 1 ? 3 : 4;
+               $metadata['channels'] = null;
+               $metadata['bits'] = 8;  # Always 8-bits per color
+
+               assert( '7 == count($metadata); # return array must contains 7 elements just like getimagesize() return' );
 
                return $metadata;
        }