Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
VipsConvolution
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 2
182
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 1
42
 getFormat
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * PHP wrapper class for VIPS under MediaWiki
4 *
5 * Copyright © Bryan Tong Minh, 2011
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/gpl.html
21 * @file
22 */
23
24namespace MediaWiki\Extension\VipsScaler;
25
26use MediaWiki\Shell\Shell;
27use RuntimeException;
28
29/**
30 * A wrapper class around im_conv because that command expects a convolution
31 * matrix file as its last argument
32 */
33class VipsConvolution extends VipsCommand {
34
35    /**
36     * @return int
37     */
38    public function execute() {
39        $format = $this->getFormat( $this->input );
40        # Convert a 2D array into a space/newline separated matrix
41        $convolutionMatrix = array_pop( $this->args );
42        $convolutionString = '';
43        foreach ( $convolutionMatrix as $row ) {
44            $convolutionString .= implode( ' ', $row ) . "\n";
45        }
46        # Save the matrix in a tempfile
47        $tmpFile = self::makeTemp( 'conv' );
48        $tmpFile->bind( $this );
49        $convolutionFile = $tmpFile->getPath();
50        file_put_contents( $convolutionFile, $convolutionString );
51        array_push( $this->args, $convolutionFile );
52
53        $tmpOutput = self::makeTemp( 'v' );
54        $tmpOutput->bind( $this );
55        $tmpOutputPath = $tmpOutput->getPath();
56
57        wfDebug( __METHOD__ . ": Convolving image [\n" . $convolutionString . "] \n" );
58
59        $limits = [ 'filesize' => 409600 ];
60        $env = [ 'IM_CONCURRENCY' => '1' ];
61
62        $cmd = [
63            $this->vips,
64            array_shift( $this->args ),
65            $this->input,
66            $tmpOutputPath
67        ];
68        $cmd = array_merge( $cmd, $this->args );
69
70        // Execute
71        $result = Shell::command( $cmd )
72            ->environment( $env )
73            ->limits( $limits )
74            ->includeStderr()
75            ->execute();
76        $retval = $result->getExitCode();
77        $this->err = $result->getStdout();
78
79        if ( $retval === 0 ) {
80            // vips seems to get confused about the bit depth after a convolution
81            // so reset it. Without this step, 16-bit tiff files seem to become all
82            // black when converted to pngs (https://github.com/jcupitt/libvips/issues/344)
83            $formatCmd = [
84                $this->vips, 'im_clip2fmt', $tmpOutputPath, $this->output, (string)$format
85            ];
86            $result = Shell::command( $formatCmd )
87                ->environment( $env )
88                ->limits( $limits )
89                ->includeStderr()
90                ->execute();
91            $retval = $result->getExitCode();
92            $this->err .= $result->getStdout();
93        }
94
95        // Cleanup temp file
96        if ( $retval != 0 && file_exists( $this->output ) ) {
97            unlink( $this->output );
98        }
99        if ( $this->removeInput ) {
100            unlink( $this->input );
101        }
102
103        // Remove the temporary matrix file
104        $tmpFile->purge();
105        $tmpOutput->purge();
106
107        return $retval;
108    }
109
110    /**
111     * Get the vips internal format (aka bit depth)
112     *
113     * @see https://github.com/jcupitt/libvips/issues/344 for why we do this
114     * @param string $input Path to file
115     * @return int Vips internal format number
116     *   (common value 0 = VIPS_FORMAT_UCHAR, 2 = VIPS_FORMAT_USHORT)
117     */
118    private function getFormat( $input ) {
119        $cmd = [ $this->vips, 'im_header_int', 'format', $input ];
120        $result = Shell::command( $cmd )
121            ->environment( [ 'IM_CONCURRENCY' => '1' ] )
122            ->execute();
123
124        $res = trim( $result->getStdout() );
125
126        if ( $result->getExitCode() !== 0 || !is_numeric( $res ) ) {
127            throw new RuntimeException( "Cannot determine vips format of image" );
128        }
129
130        $format = (int)$res;
131        // Must be in range -1 to 10
132        // We might want to be even stricter. Its assumed that the answer will usually be 0 or 2.
133        if ( $format < -1 || $format > 10 ) {
134            throw new RuntimeException( "vips format '$format' is invalid" );
135        }
136        if ( $format === -1 || $format >= 6 ) {
137            // This will still work, but not something we expect to ever get. So log.
138            wfDebugLog( 'vips', __METHOD__ . ": Vips format value is outside the range expected " .
139                "(got: $format)\n" );
140        }
141
142        return $format;
143    }
144}