1<?php
2/**
3 * iReflect Plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     i-net software <tools@inetsoftware.de>
7 * @author     Gerry Weissbach <gweissbach@inetsoftware.de>
8 */
9
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
14require_once(DOKU_PLUGIN.'action.php');
15
16class action_plugin_reflect extends DokuWiki_Action_Plugin {
17
18    private $functions = null;
19
20    function register(Doku_Event_Handler $controller) {
21
22        if ( empty($_REQUEST['reflect']) ) { return; }
23        $controller->register_hook('MEDIA_SENDFILE', 'BEFORE', $this, 'reflect__reflect');
24    }
25
26    function reflect__reflect(&$event, $args) {
27
28        if (extension_loaded('gd') == false && !@dl('gd.so')) { return; }
29
30        /* Filename for reflect Image */
31        $data = $event->data;
32        $ext = empty( $_REQUEST['return_type'] ) ? $this->getConf('return_type') : (in_array( hsc($_REQUEST['return_type']), array('png', 'jpg') ) ? hsc($_REQUEST['return_type']) : $this->getConf('return_type'));
33
34        $data['height'] = $this->getConf('reflect_height');
35        if ( empty($_REQUEST['bgc']) ) $_REQUEST['bgc'] = $this->getConf('bgc');
36        if ( !empty($_REQUEST['reflect_height']) ) $data['height'] = $_REQUEST['reflect_height'];
37        $cacheFile = getCacheName($data['file'],".media.reflect.{$_REQUEST['bgc']}.$ext");
38
39        $mtime = @filemtime($cacheFile); // 0 if not exists
40        $cache = $data['cache'];
41
42        if( ($mtime == 0) ||           // cache does not exist
43            ($mtime < time()-$cache)   // 'recache' and cache has expired
44        ){
45            if ( $this->create_reflect_image( $data, $cacheFile, $_REQUEST['bgc'] ) ) {
46                $data['orig'] = $data['file'];
47                $data['file'] = $cacheFile;
48                list($data['ext'],$data['mime'],$data['download']) = mimetype($cacheFile);
49                $event->data = $data;
50            }
51        }
52    }
53
54    function create_reflect_image( $data, $cache_path, $imagebgcolor=null ) {
55        global $conf;
56
57        $input = $data['file'];
58        $imagebgcolor = $this->calc_bgcolor($imagebgcolor);
59
60        //    How big is the image?
61        if ( !($image_details = getimagesize($input)) ) { return false; }
62
63        $width = $image_details[0];
64        $height = $image_details[1];
65        $type = $image_details[2];
66        $mime = $image_details['mime'];
67
68        //    height (how tall should the reflection be?)
69        if (isset($data['height']) ) {
70            $output_height = $data['height'];
71            if ( $output_height == 0 ) $output_height = $this->getConf('reflect_height');
72        } else {
73            //    No height was given, so default to 50% of the source images height
74            $output_height = $this->getConf('reflect_height');
75        }
76
77        //    Calculate the height of the output image
78        if ($output_height < 1) {
79            //    The output height is a percentage
80            $new_height = $height * $output_height;
81        } else {
82            //    The output height is a fixed pixel value
83            $new_height = $output_height;
84        }
85
86        if (isset($_REQUEST['fade_start']))
87        {
88            if (strpos($_REQUEST['fade_start'], '%') !== false) {
89                $alpha_start = str_replace('%', '', $_REQUEST['fade_start']);
90                $alpha_start = (int) (127 * $alpha_start / 100);
91            } else {
92                $alpha_start = (int) $_REQUEST['fade_start'];
93
94                if ($alpha_start < 1 || $alpha_start > 127) {
95                    $alpha_start = $this->getConf('fade_start');
96                }
97            }
98        } else {
99            $alpha_start = $this->getConf('fade_start');
100        }
101
102        if (isset($_REQUEST['fade_end']))
103        {
104            if (strpos($_REQUEST['fade_end'], '%') !== false) {
105                $alpha_end = str_replace('%', '', $_REQUEST['fade_end']);
106                $alpha_end = (int) (127 * $alpha_end / 100);
107            } else {
108                $alpha_end = (int) $_REQUEST['fade_end'];
109
110                if ($alpha_end < 1 || $alpha_end > 127) {
111                    $alpha_end = $this->getConf('fade_end');
112                }
113            }
114        } else {
115            $alpha_end = $this->getConf('fade_end');
116        }
117
118        $alpha_start = 127 - $alpha_start;
119        $alpha_end = 127 - $alpha_end;
120
121        //    Detect the source image format - only GIF, JPEG and PNG are supported. If you need more, extend this yourself.
122        switch ($type)
123        {
124            case 1://    GIF
125                        $source = imagecreatefromgif($input); break;
126            case 2://    JPG
127                        $source = imagecreatefromjpeg($input); break;
128            case 3://    PNG
129                        $source = imagecreatefrompng($input); break;
130            default:    return false;
131        }
132
133        /* ----------------------------------------------------------------
134            Build the reflection image
135        ---------------------------------------------------------------- */
136        $output = $this->imagereflection($source, $width, $height, $new_height, $alpha_start, $alpha_end);
137
138        /* ----------------------------------------------------------------
139            Output our final Image
140        ---------------------------------------------------------------- */
141        if ( headers_sent() ) { return false; }
142
143        //    If you'd rather output a JPEG instead of a PNG then pass the parameter 'jpeg' (no value needed) on the querystring
144        if ( substr($cache_path, -3) == 'png' ) {
145            imagepng($output, $cache_path, intval($conf['jpg_quality'] / 11));
146        } else if ( substr($cache_path, -3) == 'jpg' ) {
147            /* -----------------------------------------------------------------------
148                HACK - Build the reflection image by combining the png output
149                image AND the color background in one new image!
150            ------------------------------------------------------------------------ */
151
152            // Create transparent BG
153            $finaloutput = imagecreatetruecolor($width, $height+$new_height);
154            $white = imagecolorallocatealpha($finaloutput, $imagebgcolor['red'], $imagebgcolor['green'], $imagebgcolor['blue'], $imagebgcolor['alpha']);
155            imagecolortransparent($finaloutput, $white);
156            imagefill($finaloutput, 0, 0, $white);
157
158            imagecopy($finaloutput, $output, 0, 0, 0, 0, $width, $height+$new_height);
159            imagejpeg($finaloutput, $cache_path, intval($conf['jpg_quality']));
160        }
161        imagedestroy($output);
162        return true;
163
164        }
165
166    function calc_bgcolor( $bgcolor ) {
167
168        if ( empty($bgcolor) ) { $bgcolor = $this->getConf('bgc'); }
169
170        //    Does it start with a hash? If so then strip it
171        $bgcolor = str_replace('#', '', $bgcolor);
172
173        switch (strlen($bgcolor))
174        {
175            case 8:
176                $red = hexdec(substr($bgcolor, 0, 2));
177                $green = hexdec(substr($bgcolor, 2, 2));
178                $blue = hexdec(substr($bgcolor, 4, 2));
179                $alpha = hexdec(substr($bgcolor, 6, 2));
180                break;
181
182            case 6:
183                $red = hexdec(substr($bgcolor, 0, 2));
184                $green = hexdec(substr($bgcolor, 2, 2));
185                $blue = hexdec(substr($bgcolor, 4, 2));
186                $alpha = hexdec('00');
187                break;
188
189            case 4:
190                $red = substr($bgcolor, 0, 1);
191                $green = substr($bgcolor, 1, 1);
192                $blue = substr($bgcolor, 2, 1);
193                $alpha = substr($bgcolor, 3, 1);
194                $red = hexdec($red . $red);
195                $green = hexdec($green . $green);
196                $blue = hexdec($blue . $blue);
197                $alpha = hexdec($alpha . $alpha);
198                break;
199
200            case 3:
201                $red = substr($bgcolor, 0, 1);
202                $green = substr($bgcolor, 1, 1);
203                $blue = substr($bgcolor, 2, 1);
204                $red = hexdec($red . $red);
205                $green = hexdec($green . $green);
206                $blue = hexdec($blue . $blue);
207                $alpha = hexdec('00');
208                break;
209
210            default:
211                //    Wrong values passed, default to black
212                $red = 0;
213                $green = 0;
214                $blue = 0;
215                $alpha = 0;
216        }
217
218        $alpha = floor($alpha / 2);
219        return array('red' => $red, 'green' => $green, 'blue' => $blue, 'alpha' => $alpha );
220    }
221
222    function imagereflection($src_img, $src_width, $src_height, $reflection_height, $alpha_start, $alpha_end) {
223        $dest_height = $src_height + $reflection_height;
224        $dest_width = $src_width;
225
226        // Create Reflected Object
227        $reflected = imagecreatetruecolor($dest_width, $dest_height);
228        imagealphablending($reflected, false);
229        imagesavealpha($reflected, true);
230
231        // Copy Source
232        imagecopy($reflected, $src_img, 0, 0, 0, 0, $src_width, $src_height);
233        if ( empty($reflection_height) ) $reflection_height = $src_height / 2;
234
235        // Calc alpha width and step
236        $alpha_length = abs($alpha_start - $alpha_end);
237
238        // For each Pixel in the reflection area
239        for ($y = 1; $y <= $reflection_height; $y++) {
240
241            $pct = $y / $reflection_height;
242            for ($x = 0; $x < $dest_width; $x++) {
243                // copy pixel from x / $src_height - y to x / $src_height + y
244                $rgba = imagecolorat($src_img, $x, $src_height - $y);
245                $alpha = ($rgba & 0x7F000000) >> 24;
246
247                //  Get % of alpha
248                if ($alpha_start > $alpha_end) { $alpha_calc = (int) ($alpha_start - ($pct * $alpha_length)); }
249                else { $alpha_calc = (int) ($alpha_start + ($pct * $alpha_length)); }
250
251                $alpha =  max($alpha, $alpha_calc);
252                $rgba = imagecolorsforindex($src_img, $rgba);
253                $rgba = imagecolorallocatealpha($reflected, $rgba['red'], $rgba['green'], $rgba['blue'], $alpha);
254                imagesetpixel($reflected, $x, $src_height + $y - 1, $rgba);
255            }
256        }
257
258        return $reflected;
259    }
260}