Version Description
- 2016-07-06 =
- Now uses Grafika for image editing.
- Added link in slide preview image that opens in new tab when clicked.
Download this release
Release Info
Developer | kosinix |
Plugin | Cyclone Slider |
Version | 2.12.0 |
Comparing to | |
See all releases |
Code changes from version 2.11.0 to 2.12.0
- README.txt +6 -2
- cyclone-slider.php +2 -10
- js/admin.js +6 -6
- src/CycloneSlider/Admin.php +1 -0
- src/CycloneSlider/Data.php +19 -8
- src/CycloneSlider/Frontend.php +3 -2
- src/CycloneSlider/Grafika/Color.php +105 -0
- src/CycloneSlider/Grafika/DrawingObject/CubicBezier.php +103 -0
- src/CycloneSlider/Grafika/DrawingObject/Ellipse.php +115 -0
- src/CycloneSlider/Grafika/DrawingObject/Line.php +83 -0
- src/CycloneSlider/Grafika/DrawingObject/Polygon.php +108 -0
- src/CycloneSlider/Grafika/DrawingObject/QuadraticBezier.php +86 -0
- src/CycloneSlider/Grafika/DrawingObject/Rectangle.php +112 -0
- src/CycloneSlider/Grafika/DrawingObjectInterface.php +17 -0
- src/CycloneSlider/Grafika/EditorInterface.php +348 -0
- src/CycloneSlider/Grafika/EffectInterface.php +17 -0
- src/CycloneSlider/Grafika/Gd/DrawingObject/CubicBezier.php +412 -0
- src/CycloneSlider/Grafika/Gd/DrawingObject/Ellipse.php +42 -0
- src/CycloneSlider/Grafika/Gd/DrawingObject/Line.php +36 -0
- src/CycloneSlider/Grafika/Gd/DrawingObject/Polygon.php +64 -0
- src/CycloneSlider/Grafika/Gd/DrawingObject/QuadraticBezier.php +208 -0
- src/CycloneSlider/Grafika/Gd/DrawingObject/Rectangle.php +36 -0
- src/CycloneSlider/Grafika/Gd/Editor.php +1115 -0
- src/CycloneSlider/Grafika/Gd/Effect/Dither.php +100 -0
- src/CycloneSlider/Grafika/Gd/Image.php +285 -0
- src/CycloneSlider/Grafika/Grafika.php +105 -0
- src/CycloneSlider/Grafika/ImageInterface.php +49 -0
- src/CycloneSlider/Grafika/ImageType.php +21 -0
- src/CycloneSlider/Grafika/Imagick/DrawingObject/CubicBezier.php +51 -0
- src/CycloneSlider/Grafika/Imagick/DrawingObject/Ellipse.php +40 -0
- src/CycloneSlider/Grafika/Imagick/DrawingObject/Line.php +41 -0
- src/CycloneSlider/Grafika/Imagick/DrawingObject/Polygon.php +48 -0
- src/CycloneSlider/Grafika/Imagick/DrawingObject/QuadraticBezier.php +55 -0
- src/CycloneSlider/Grafika/Imagick/DrawingObject/Rectangle.php +41 -0
- src/CycloneSlider/Grafika/Imagick/Editor.php +975 -0
- src/CycloneSlider/Grafika/Imagick/Effect/Dither.php +91 -0
- src/CycloneSlider/Grafika/Imagick/Image.php +135 -0
- src/CycloneSlider/ImageResizer.php +33 -12
- src/autoloader.php +11 -0
- views/slide-edit.php +1 -1
- views/slider-advanced-settings.php +14 -5
README.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: kosinix
|
|
3 |
Donate link: http://www.codefleet.net/donate/
|
4 |
Tags: slider, slideshow, drag-and-drop, wordpress-slider, wordpress-slideshow, cycle 2, jquery, responsive, translation-ready, custom-post, cyclone-slider
|
5 |
Requires at least: 3.5
|
6 |
-
Tested up to: 4.
|
7 |
Stable tag: trunk
|
8 |
License: GPLv3
|
9 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
@@ -117,7 +117,11 @@ Inside `wp-content` create a folder named "cycloneslider". Add your templates in
|
|
117 |
|
118 |
== Changelog ==
|
119 |
|
120 |
-
= 2.
|
|
|
|
|
|
|
|
|
121 |
* New. Templates can now be added using a sub-plugin. See [docs](http://docs.codefleet.net/cyclone-slider-2/creating-your-own-template/).
|
122 |
* New filter cycloneslider_view_vars. See [docs for a full list of filters](http://docs.codefleet.net/cyclone-slider-2/filters/).
|
123 |
* Change overall textdomain handling to be compatible with wordpress.org translation.
|
3 |
Donate link: http://www.codefleet.net/donate/
|
4 |
Tags: slider, slideshow, drag-and-drop, wordpress-slider, wordpress-slideshow, cycle 2, jquery, responsive, translation-ready, custom-post, cyclone-slider
|
5 |
Requires at least: 3.5
|
6 |
+
Tested up to: 4.5.3
|
7 |
Stable tag: trunk
|
8 |
License: GPLv3
|
9 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
117 |
|
118 |
== Changelog ==
|
119 |
|
120 |
+
= 2.12.0 - 2016-07-06 =
|
121 |
+
* Now uses [Grafika](http://kosinix.github.io/grafika/) for image editing.
|
122 |
+
* Added link in slide preview image that opens in new tab when clicked.
|
123 |
+
|
124 |
+
= 2.11.0 - 2016-03-30 =
|
125 |
* New. Templates can now be added using a sub-plugin. See [docs](http://docs.codefleet.net/cyclone-slider-2/creating-your-own-template/).
|
126 |
* New filter cycloneslider_view_vars. See [docs for a full list of filters](http://docs.codefleet.net/cyclone-slider-2/filters/).
|
127 |
* Change overall textdomain handling to be compatible with wordpress.org translation.
|
cyclone-slider.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Plugin Name: Cyclone Slider 2
|
4 |
Plugin URI: http://www.codefleet.net/cyclone-slider-2/
|
5 |
Description: Create and manage sliders with ease. Built for both casual users and developers.
|
6 |
-
Version: 2.
|
7 |
Author: Nico Amarilla
|
8 |
Author URI: http://www.codefleet.net/
|
9 |
License: GPLv3
|
@@ -12,15 +12,7 @@ Domain Path: /languages
|
|
12 |
Text Domain: cyclone-slider-2
|
13 |
*/
|
14 |
|
15 |
-
|
16 |
-
function cycloneslider_autoloader( $class_name ) {
|
17 |
-
if( false !== strpos( $class_name, 'CycloneSlider' ) ){
|
18 |
-
$classes_dir = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR;
|
19 |
-
$class_file = str_replace( '_', DIRECTORY_SEPARATOR, $class_name ) . '.php';
|
20 |
-
require_once $classes_dir . $class_file;
|
21 |
-
}
|
22 |
-
}
|
23 |
-
spl_autoload_register('cycloneslider_autoloader');
|
24 |
|
25 |
$cyclone_slider_plugin_instance = null;
|
26 |
$cyclone_slider_saved_done = false;
|
3 |
Plugin Name: Cyclone Slider 2
|
4 |
Plugin URI: http://www.codefleet.net/cyclone-slider-2/
|
5 |
Description: Create and manage sliders with ease. Built for both casual users and developers.
|
6 |
+
Version: 2.12.0
|
7 |
Author: Nico Amarilla
|
8 |
Author URI: http://www.codefleet.net/
|
9 |
License: GPLv3
|
12 |
Text Domain: cyclone-slider-2
|
13 |
*/
|
14 |
|
15 |
+
require_once 'src/autoloader.php';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
$cyclone_slider_plugin_instance = null;
|
18 |
$cyclone_slider_saved_done = false;
|
js/admin.js
CHANGED
@@ -151,14 +151,14 @@ jQuery(document).ready(function($){
|
|
151 |
});
|
152 |
|
153 |
/*** Add image to slide ***/
|
154 |
-
$('#cyclone-slides-metabox').on('wpAddImage', '.cs-media-gallery-show', function(e, image_url, attachment_id){
|
155 |
var current_slide_box, slide_thumb, slide_attachment_id;
|
156 |
|
157 |
current_slide_box = $(this).parents('.cs-slide');/*** Get current box ***/
|
158 |
slide_thumb = current_slide_box.find('.cs-image-thumb');/*** Find the thumb ***/
|
159 |
slide_attachment_id = current_slide_box.find('.cs-image-id ');/*** Find the hidden field that will hold the attachment id ***/
|
160 |
|
161 |
-
slide_thumb.html('<img src="'+image_url+'" alt="thumb">').show();
|
162 |
slide_attachment_id.val(attachment_id);
|
163 |
|
164 |
});
|
@@ -166,12 +166,12 @@ jQuery(document).ready(function($){
|
|
166 |
/*** Add multiple images as slide ***/
|
167 |
$('#cyclone-slides-metabox').on('wpAddImages', '.cs-multiple-slides', function(e, media_attachments){
|
168 |
var cur_slide_count = $('.cs-sortables .cs-slide').length;
|
169 |
-
|
170 |
for(i=0; i<media_attachments.length; ++i){
|
171 |
|
172 |
$('#cyclone-slides-metabox .cs-add-slide').trigger('click');
|
173 |
|
174 |
-
$('.cs-sortables .cs-slide').eq(cur_slide_count+i).find('.cs-media-gallery-show').trigger('wpAddImage', [media_attachments[i].url, media_attachments[i].id]);
|
175 |
}
|
176 |
|
177 |
});
|
@@ -459,8 +459,8 @@ jQuery(document).ready(function($){
|
|
459 |
} else {
|
460 |
img_url = media_attachment.sizes.medium.url;
|
461 |
}
|
462 |
-
|
463 |
-
triggering_element.trigger('wpAddImage', [img_url, media_attachment.id]);
|
464 |
});
|
465 |
|
466 |
// Now that everything has been set, let's open up the frame.
|
151 |
});
|
152 |
|
153 |
/*** Add image to slide ***/
|
154 |
+
$('#cyclone-slides-metabox').on('wpAddImage', '.cs-media-gallery-show', function(e, image_url, attachment_id, media_attachment){
|
155 |
var current_slide_box, slide_thumb, slide_attachment_id;
|
156 |
|
157 |
current_slide_box = $(this).parents('.cs-slide');/*** Get current box ***/
|
158 |
slide_thumb = current_slide_box.find('.cs-image-thumb');/*** Find the thumb ***/
|
159 |
slide_attachment_id = current_slide_box.find('.cs-image-id ');/*** Find the hidden field that will hold the attachment id ***/
|
160 |
|
161 |
+
slide_thumb.html('<a target="_blank" href="'+media_attachment.url+'"><img src="'+image_url+'" alt="thumb"></a>').show();
|
162 |
slide_attachment_id.val(attachment_id);
|
163 |
|
164 |
});
|
166 |
/*** Add multiple images as slide ***/
|
167 |
$('#cyclone-slides-metabox').on('wpAddImages', '.cs-multiple-slides', function(e, media_attachments){
|
168 |
var cur_slide_count = $('.cs-sortables .cs-slide').length;
|
169 |
+
|
170 |
for(i=0; i<media_attachments.length; ++i){
|
171 |
|
172 |
$('#cyclone-slides-metabox .cs-add-slide').trigger('click');
|
173 |
|
174 |
+
$('.cs-sortables .cs-slide').eq(cur_slide_count+i).find('.cs-media-gallery-show').trigger('wpAddImage', [media_attachments[i].url, media_attachments[i].id, media_attachments[i]]);
|
175 |
}
|
176 |
|
177 |
});
|
459 |
} else {
|
460 |
img_url = media_attachment.sizes.medium.url;
|
461 |
}
|
462 |
+
|
463 |
+
triggering_element.trigger('wpAddImage', [img_url, media_attachment.id, media_attachment]);
|
464 |
});
|
465 |
|
466 |
// Now that everything has been set, let's open up the frame.
|
src/CycloneSlider/Admin.php
CHANGED
@@ -334,6 +334,7 @@ class CycloneSlider_Admin {
|
|
334 |
$vars['slider_settings'] = $slider_settings;
|
335 |
$vars['slide'] = $slide;
|
336 |
$vars['image_url'] = $image_url;
|
|
|
337 |
$vars['box_title'] = $box_title;
|
338 |
$vars['debug'] = ($this->debug) ? cyclone_slider_debug($slide) : '';
|
339 |
$vars['effects'] = $this->data->get_slide_effects();
|
334 |
$vars['slider_settings'] = $slider_settings;
|
335 |
$vars['slide'] = $slide;
|
336 |
$vars['image_url'] = $image_url;
|
337 |
+
$vars['full_image_url'] = wp_get_attachment_url($slide['id']);
|
338 |
$vars['box_title'] = $box_title;
|
339 |
$vars['debug'] = ($this->debug) ? cyclone_slider_debug($slide) : '';
|
340 |
$vars['effects'] = $this->data->get_slide_effects();
|
src/CycloneSlider/Data.php
CHANGED
@@ -150,7 +150,7 @@ class CycloneSlider_Data {
|
|
150 |
$settings_to_save['width_management'] = sanitize_text_field( $settings['width_management'] );
|
151 |
// Pro options
|
152 |
$settings_to_save['easing'] = '';
|
153 |
-
$settings_to_save['resize_option'] = '
|
154 |
$settings_to_save['allow_wrap'] = 'true';
|
155 |
$settings_to_save['dynamic_height'] = 'off';
|
156 |
$settings_to_save['dynamic_height_speed'] = 250;
|
@@ -801,13 +801,24 @@ class CycloneSlider_Data {
|
|
801 |
* @return array The array of resize options in a key-value pair
|
802 |
*/
|
803 |
public function get_resize_options(){
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
811 |
}
|
812 |
|
813 |
/**
|
150 |
$settings_to_save['width_management'] = sanitize_text_field( $settings['width_management'] );
|
151 |
// Pro options
|
152 |
$settings_to_save['easing'] = '';
|
153 |
+
$settings_to_save['resize_option'] = 'fit';
|
154 |
$settings_to_save['allow_wrap'] = 'true';
|
155 |
$settings_to_save['dynamic_height'] = 'off';
|
156 |
$settings_to_save['dynamic_height_speed'] = 250;
|
801 |
* @return array The array of resize options in a key-value pair
|
802 |
*/
|
803 |
public function get_resize_options(){
|
804 |
+
if(version_compare(PHP_VERSION, '5.3', '>=')) { // 5.3+
|
805 |
+
return array(
|
806 |
+
'fit' => 'Fit',
|
807 |
+
'fill' => 'Fill',
|
808 |
+
'crop' => 'Crop',
|
809 |
+
'exact' => 'Exact',
|
810 |
+
'exactWidth' => 'Exact Width',
|
811 |
+
'exactHeight' => 'Exact Height'
|
812 |
+
);
|
813 |
+
} else { // 5.2
|
814 |
+
return array(
|
815 |
+
'auto' => 'Auto',
|
816 |
+
'crop' => 'Crop',
|
817 |
+
'exact' => 'Exact',
|
818 |
+
'landscape' => 'Landscape',
|
819 |
+
'portrait' => 'Portrait'
|
820 |
+
);
|
821 |
+
}
|
822 |
}
|
823 |
|
824 |
/**
|
src/CycloneSlider/Frontend.php
CHANGED
@@ -15,7 +15,7 @@ class CycloneSlider_Frontend {
|
|
15 |
/**
|
16 |
* @var CycloneSlider_Vimeo
|
17 |
*/
|
18 |
-
|
19 |
/**
|
20 |
* @var CycloneSlider_View
|
21 |
*/
|
@@ -218,7 +218,8 @@ class CycloneSlider_Frontend {
|
|
218 |
|
219 |
$this->view->set_view_folder( dirname( $view_file ) ); // Set to template folder
|
220 |
|
221 |
-
$vars = apply_filters('cycloneslider_view_vars', $vars);
|
|
|
222 |
$slider_html = $this->view->get_render( basename( $view_file ), $vars );
|
223 |
|
224 |
$this->view->set_view_folder( $current_view_folder ); // Restore
|
15 |
/**
|
16 |
* @var CycloneSlider_Vimeo
|
17 |
*/
|
18 |
+
public $vimeo;
|
19 |
/**
|
20 |
* @var CycloneSlider_View
|
21 |
*/
|
218 |
|
219 |
$this->view->set_view_folder( dirname( $view_file ) ); // Set to template folder
|
220 |
|
221 |
+
$vars = apply_filters('cycloneslider_view_vars', $vars, $this);
|
222 |
+
|
223 |
$slider_html = $this->view->get_render( basename( $view_file ), $vars );
|
224 |
|
225 |
$this->view->set_view_folder( $current_view_folder ); // Restore
|
src/CycloneSlider/Grafika/Color.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Color
|
6 |
+
* @package Grafika
|
7 |
+
*/
|
8 |
+
class Color {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @var string Hex string: #FFFFFF
|
12 |
+
*/
|
13 |
+
protected $hexString;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var float Transparency value 0-1
|
17 |
+
*/
|
18 |
+
protected $alpha;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Color constructor.
|
22 |
+
*
|
23 |
+
* @param string $hexString Hex string
|
24 |
+
* @param float $alpha Transparency value 0-1
|
25 |
+
*/
|
26 |
+
public function __construct( $hexString = '', $alpha = 1.0 ){
|
27 |
+
$this->hexString = $hexString; // TODO: Validate hexstring
|
28 |
+
$this->alpha = $alpha;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get RGB array
|
33 |
+
*
|
34 |
+
* @return array Contains array($r, $g, $b)
|
35 |
+
*/
|
36 |
+
public function getRgb(){
|
37 |
+
return $this->hexToRgb( $this->hexString );
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Get RGBA array
|
42 |
+
*
|
43 |
+
* @return array Contains array($r, $g, $b, $a)
|
44 |
+
*/
|
45 |
+
public function getRgba(){
|
46 |
+
$rgba = $this->hexToRgb( $this->hexString );
|
47 |
+
$rgba[] = $this->alpha;
|
48 |
+
return $rgba;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Convert hex string to RGB
|
53 |
+
* @param string $hex Hex string. Possible values: #ffffff, #fff, fff
|
54 |
+
* @return array Contains (RGB) values red, green and blue
|
55 |
+
*/
|
56 |
+
public function hexToRgb( $hex ) {
|
57 |
+
$hex = ltrim($hex, '#'); // Remove #
|
58 |
+
|
59 |
+
if(strlen($hex) == 3) {
|
60 |
+
$r = hexdec(substr($hex,0,1).substr($hex,0,1));
|
61 |
+
$g = hexdec(substr($hex,1,1).substr($hex,1,1));
|
62 |
+
$b = hexdec(substr($hex,2,1).substr($hex,2,1));
|
63 |
+
} else {
|
64 |
+
$r = hexdec(substr($hex,0,2));
|
65 |
+
$g = hexdec(substr($hex,2,2));
|
66 |
+
$b = hexdec(substr($hex,4,2));
|
67 |
+
}
|
68 |
+
return array($r, $g, $b); // Returns an array with the rgb values
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Get hex string.
|
73 |
+
*
|
74 |
+
* @return string
|
75 |
+
*/
|
76 |
+
public function getHexString() {
|
77 |
+
return $this->hexString;
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Set hex string.
|
82 |
+
*
|
83 |
+
* @param string $hexString
|
84 |
+
*/
|
85 |
+
public function setHexString($hexString) {
|
86 |
+
$this->hexString = $hexString;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Alpha value.
|
91 |
+
* @return float
|
92 |
+
*/
|
93 |
+
public function getAlpha() {
|
94 |
+
return $this->alpha;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @param float $alpha
|
99 |
+
*/
|
100 |
+
public function setAlpha($alpha) {
|
101 |
+
$this->alpha = $alpha;
|
102 |
+
}
|
103 |
+
|
104 |
+
|
105 |
+
}
|
src/CycloneSlider/Grafika/DrawingObject/CubicBezier.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\Color;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Base class
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
abstract class CubicBezier
|
11 |
+
{
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Starting point. Array of X Y values.
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
protected $point1;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Control point 1. Array of X Y values.
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
protected $control1;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Control point 2. Array of X Y values.
|
27 |
+
* @var array
|
28 |
+
*/
|
29 |
+
protected $control2;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* End point. Array of X Y values.
|
33 |
+
* @var array
|
34 |
+
*/
|
35 |
+
protected $point2;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Color of curve.
|
39 |
+
*
|
40 |
+
* @var Color
|
41 |
+
*/
|
42 |
+
protected $color;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* CubicBezier constructor.
|
46 |
+
* @param $point1
|
47 |
+
* @param $control1
|
48 |
+
* @param $control2
|
49 |
+
* @param $point2
|
50 |
+
* @param Color $color
|
51 |
+
*/
|
52 |
+
public function __construct($point1, $control1, $control2, $point2, Color $color)
|
53 |
+
{
|
54 |
+
|
55 |
+
$this->point1 = $point1;
|
56 |
+
$this->control1 = $control1;
|
57 |
+
$this->control2 = $control2;
|
58 |
+
$this->point2 = $point2;
|
59 |
+
$this->color = $color;
|
60 |
+
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @return array
|
65 |
+
*/
|
66 |
+
public function getPoint1()
|
67 |
+
{
|
68 |
+
return $this->point1;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @return array
|
73 |
+
*/
|
74 |
+
public function getControl1()
|
75 |
+
{
|
76 |
+
return $this->control1;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @return array
|
81 |
+
*/
|
82 |
+
public function getControl2()
|
83 |
+
{
|
84 |
+
return $this->control2;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @return array
|
89 |
+
*/
|
90 |
+
public function getPoint2()
|
91 |
+
{
|
92 |
+
return $this->point2;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* @return Color
|
97 |
+
*/
|
98 |
+
public function getColor()
|
99 |
+
{
|
100 |
+
return $this->color;
|
101 |
+
}
|
102 |
+
|
103 |
+
}
|
src/CycloneSlider/Grafika/DrawingObject/Ellipse.php
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\Color;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Base class
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
abstract class Ellipse
|
11 |
+
{
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Image width in pixels
|
15 |
+
* @var int
|
16 |
+
*/
|
17 |
+
protected $width;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Image height in pixels
|
21 |
+
* @var int
|
22 |
+
*/
|
23 |
+
protected $height;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* X,Y pos.
|
27 |
+
* @var array
|
28 |
+
*/
|
29 |
+
protected $pos;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var int
|
33 |
+
*/
|
34 |
+
protected $borderSize;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var Color
|
38 |
+
*/
|
39 |
+
protected $fillColor;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @var Color
|
43 |
+
*/
|
44 |
+
protected $borderColor;
|
45 |
+
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Ellipse constructor.
|
49 |
+
*
|
50 |
+
* @param int $width Width of ellipse in pixels.
|
51 |
+
* @param int $height Height of ellipse in pixels.
|
52 |
+
* @param array $pos Array containing int X and int Y position from top left of canvass.
|
53 |
+
* @param int $borderSize Border thickness in pixels.
|
54 |
+
* @param Color|null $borderColor Border color.
|
55 |
+
* @param Color|null $fillColor Border color.
|
56 |
+
*/
|
57 |
+
public function __construct($width, $height, $pos, $borderSize, $borderColor, $fillColor) {
|
58 |
+
$this->width = $width;
|
59 |
+
$this->height = $height;
|
60 |
+
$this->pos = $pos;
|
61 |
+
$this->borderSize = $borderSize;
|
62 |
+
$this->borderColor = $borderColor;
|
63 |
+
$this->fillColor = $fillColor;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @return int
|
68 |
+
*/
|
69 |
+
public function getWidth()
|
70 |
+
{
|
71 |
+
return $this->width;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @return int
|
76 |
+
*/
|
77 |
+
public function getHeight()
|
78 |
+
{
|
79 |
+
return $this->height;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @return array
|
84 |
+
*/
|
85 |
+
public function getPos()
|
86 |
+
{
|
87 |
+
return $this->pos;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @return int
|
92 |
+
*/
|
93 |
+
public function getBorderSize()
|
94 |
+
{
|
95 |
+
return $this->borderSize;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @return Color
|
100 |
+
*/
|
101 |
+
public function getFillColor()
|
102 |
+
{
|
103 |
+
return $this->fillColor;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @return Color
|
108 |
+
*/
|
109 |
+
public function getBorderColor()
|
110 |
+
{
|
111 |
+
return $this->borderColor;
|
112 |
+
}
|
113 |
+
|
114 |
+
|
115 |
+
}
|
src/CycloneSlider/Grafika/DrawingObject/Line.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\Color;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Base class
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
abstract class Line
|
11 |
+
{
|
12 |
+
|
13 |
+
/**
|
14 |
+
* X,Y pos 1.
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
protected $point1;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* X,Y pos 2.
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
protected $point2;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var int Thickness of line.
|
27 |
+
*/
|
28 |
+
protected $thickness;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var Color
|
32 |
+
*/
|
33 |
+
protected $color;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Creates a line.
|
37 |
+
*
|
38 |
+
* @param array $point1 Array containing int X and int Y position of the starting point.
|
39 |
+
* @param array $point2 Array containing int X and int Y position of the starting point.
|
40 |
+
* @param int $thickness Thickness in pixel. Note: This is currently ignored in GD editor and falls back to 1.
|
41 |
+
* @param Color $color Color of the line.
|
42 |
+
*/
|
43 |
+
public function __construct(array $point1, array $point2, $thickness = 1, Color $color)
|
44 |
+
{
|
45 |
+
$this->point1 = $point1;
|
46 |
+
$this->point2 = $point2;
|
47 |
+
$this->thickness = $thickness;
|
48 |
+
$this->color = $color;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @return array
|
53 |
+
*/
|
54 |
+
public function getPoint1()
|
55 |
+
{
|
56 |
+
return $this->point1;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* @return array
|
61 |
+
*/
|
62 |
+
public function getPoint2()
|
63 |
+
{
|
64 |
+
return $this->point2;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @return int
|
69 |
+
*/
|
70 |
+
public function getThickness()
|
71 |
+
{
|
72 |
+
return $this->thickness;
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @return Color
|
77 |
+
*/
|
78 |
+
public function getColor()
|
79 |
+
{
|
80 |
+
return $this->color;
|
81 |
+
}
|
82 |
+
|
83 |
+
}
|
src/CycloneSlider/Grafika/DrawingObject/Polygon.php
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\Color;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Base class
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
abstract class Polygon
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Image width in pixels
|
14 |
+
* @var int
|
15 |
+
*/
|
16 |
+
protected $width;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Image height in pixels
|
20 |
+
* @var int
|
21 |
+
*/
|
22 |
+
protected $height;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Array of all X and Y positions. Must have at least three positions (x,y).
|
26 |
+
* @var array
|
27 |
+
*/
|
28 |
+
protected $points;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var int
|
32 |
+
*/
|
33 |
+
protected $borderSize;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var Color
|
37 |
+
*/
|
38 |
+
protected $fillColor;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var Color
|
42 |
+
*/
|
43 |
+
protected $borderColor;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Creates a polygon.
|
47 |
+
* @param array $points Array of all X and Y positions. Must have at least three positions.
|
48 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 0 or no border.
|
49 |
+
* @param Color $borderColor Border color. Defaults to black.
|
50 |
+
* @param Color $fillColor Fill color. Defaults to none (null).
|
51 |
+
*/
|
52 |
+
public function __construct($points = array(array(0,0), array(0,0), array(0,0)), $borderSize, $borderColor, $fillColor) {
|
53 |
+
$this->points = $points;
|
54 |
+
$this->borderSize = $borderSize;
|
55 |
+
$this->borderColor = $borderColor;
|
56 |
+
$this->fillColor = $fillColor;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* @return int
|
61 |
+
*/
|
62 |
+
public function getWidth()
|
63 |
+
{
|
64 |
+
return $this->width;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @return int
|
69 |
+
*/
|
70 |
+
public function getHeight()
|
71 |
+
{
|
72 |
+
return $this->height;
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @return array
|
77 |
+
*/
|
78 |
+
public function getPoints()
|
79 |
+
{
|
80 |
+
return $this->points;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* @return int
|
85 |
+
*/
|
86 |
+
public function getBorderSize()
|
87 |
+
{
|
88 |
+
return $this->borderSize;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* @return Color
|
93 |
+
*/
|
94 |
+
public function getFillColor()
|
95 |
+
{
|
96 |
+
return $this->fillColor;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @return Color
|
101 |
+
*/
|
102 |
+
public function getBorderColor()
|
103 |
+
{
|
104 |
+
return $this->borderColor;
|
105 |
+
}
|
106 |
+
|
107 |
+
|
108 |
+
}
|
src/CycloneSlider/Grafika/DrawingObject/QuadraticBezier.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\Color;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Base class
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
abstract class QuadraticBezier
|
11 |
+
{
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Starting point.
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
protected $point1;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Control point.
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
protected $control;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* End point.
|
27 |
+
* @var array
|
28 |
+
*/
|
29 |
+
protected $point2;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Color of curve.
|
33 |
+
*
|
34 |
+
* @var Color
|
35 |
+
*/
|
36 |
+
protected $color;
|
37 |
+
|
38 |
+
public function __construct($point1, $control, $point2, $color = null)
|
39 |
+
{
|
40 |
+
|
41 |
+
$this->point1 = $point1;
|
42 |
+
$this->control = $control;
|
43 |
+
$this->point2 = $point2;
|
44 |
+
$this->color = $color;
|
45 |
+
if (null === $color) {
|
46 |
+
$this->color = new Color('#000000');
|
47 |
+
} else {
|
48 |
+
if (is_string($color)) {
|
49 |
+
$this->color = new Color($color);
|
50 |
+
}
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @return array
|
56 |
+
*/
|
57 |
+
public function getPoint1()
|
58 |
+
{
|
59 |
+
return $this->point1;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @return array
|
64 |
+
*/
|
65 |
+
public function getControl()
|
66 |
+
{
|
67 |
+
return $this->control;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @return array
|
72 |
+
*/
|
73 |
+
public function getPoint2()
|
74 |
+
{
|
75 |
+
return $this->point2;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* @return Color
|
80 |
+
*/
|
81 |
+
public function getColor()
|
82 |
+
{
|
83 |
+
return $this->color;
|
84 |
+
}
|
85 |
+
|
86 |
+
}
|
src/CycloneSlider/Grafika/DrawingObject/Rectangle.php
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\Color;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Base class
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
abstract class Rectangle
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Image width in pixels
|
14 |
+
* @var int
|
15 |
+
*/
|
16 |
+
protected $width;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Image height in pixels
|
20 |
+
* @var int
|
21 |
+
*/
|
22 |
+
protected $height;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* X and Y position in an array.
|
26 |
+
* @var array
|
27 |
+
*/
|
28 |
+
protected $pos;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @var int
|
32 |
+
*/
|
33 |
+
protected $borderSize;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var Color
|
37 |
+
*/
|
38 |
+
protected $fillColor;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var Color
|
42 |
+
*/
|
43 |
+
protected $borderColor;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Creates a rectangle.
|
47 |
+
* @param int $width Width of rectangle in pixels.
|
48 |
+
* @param int $height Height in pixels.
|
49 |
+
* @param array $pos Array containing int X and int Y position. X is the distance in pixels from the left of the canvass to the left of the shape. Y is the distance from the top of the canvass to the top of the shape.
|
50 |
+
* @param int $borderSize Size of the border in pixels.
|
51 |
+
* @param Color|null $borderColor Border color.
|
52 |
+
* @param Color|null $fillColor Fill color.
|
53 |
+
*/
|
54 |
+
public function __construct($width, $height, $pos, $borderSize, $borderColor, $fillColor) {
|
55 |
+
$this->width = $width;
|
56 |
+
$this->height = $height;
|
57 |
+
$this->pos = $pos;
|
58 |
+
$this->borderSize = $borderSize;
|
59 |
+
$this->borderColor = $borderColor;
|
60 |
+
$this->fillColor = $fillColor;
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @return int
|
65 |
+
*/
|
66 |
+
public function getWidth()
|
67 |
+
{
|
68 |
+
return $this->width;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @return int
|
73 |
+
*/
|
74 |
+
public function getHeight()
|
75 |
+
{
|
76 |
+
return $this->height;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @return array
|
81 |
+
*/
|
82 |
+
public function getPos()
|
83 |
+
{
|
84 |
+
return $this->pos;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @return int
|
89 |
+
*/
|
90 |
+
public function getBorderSize()
|
91 |
+
{
|
92 |
+
return $this->borderSize;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* @return Color
|
97 |
+
*/
|
98 |
+
public function getFillColor()
|
99 |
+
{
|
100 |
+
return $this->fillColor;
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* @return Color
|
105 |
+
*/
|
106 |
+
public function getBorderColor()
|
107 |
+
{
|
108 |
+
return $this->borderColor;
|
109 |
+
}
|
110 |
+
|
111 |
+
|
112 |
+
}
|
src/CycloneSlider/Grafika/DrawingObjectInterface.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Interface DrawingObjectInterface
|
6 |
+
* @package Grafika
|
7 |
+
*/
|
8 |
+
interface DrawingObjectInterface {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @param ImageInterface $image
|
12 |
+
*
|
13 |
+
* @return ImageInterface
|
14 |
+
*/
|
15 |
+
public function draw( $image );
|
16 |
+
|
17 |
+
}
|
src/CycloneSlider/Grafika/EditorInterface.php
ADDED
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Interface EditorInterface
|
6 |
+
* @package Grafika
|
7 |
+
*/
|
8 |
+
interface EditorInterface {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Apply an effect to the image. See Effects section for a list of effects to apply.
|
12 |
+
*
|
13 |
+
* @param EffectInterface $effect
|
14 |
+
*
|
15 |
+
* @return $this
|
16 |
+
*/
|
17 |
+
public function apply( $effect );
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Creates a cubic bezier. Cubic bezier has 2 control points.
|
21 |
+
* @param array $point1 Array of X and Y value for start point.
|
22 |
+
* @param array $control1 Array of X and Y value for control point 1.
|
23 |
+
* @param array $control2 Array of X and Y value for control point 2.
|
24 |
+
* @param array $point2 Array of X and Y value for end point.
|
25 |
+
* @param Color|string $color Color of the curve. Accepts hex string or a Color object. Defaults to black.
|
26 |
+
* @return EditorInterface An instance of image editor.
|
27 |
+
*/
|
28 |
+
public function bezierCubic($point1, $control1, $control2, $point2, $color = '#000000');
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Creates a quadratic bezier. Quadratic bezier has 1 control point.
|
32 |
+
* @param array $point1 Array of X and Y value for start point.
|
33 |
+
* @param array $control Array of X and Y value for control point.
|
34 |
+
* @param array $point2 Array of X and Y value for end point.
|
35 |
+
* @param Color|string $color Color of the curve. Accepts hex string or a Color object. Defaults to black.
|
36 |
+
* @return EditorInterface An instance of image editor.
|
37 |
+
*/
|
38 |
+
public function bezierQuad($point1, $control, $point2, $color = '#000000');
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Create a blank image given width and height.
|
42 |
+
*
|
43 |
+
* @param int $width Width of image in pixels.
|
44 |
+
* @param int $height Height of image in pixels.
|
45 |
+
*
|
46 |
+
* @return EditorInterface An instance of image editor.
|
47 |
+
*/
|
48 |
+
public function blank( $width, $height );
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Compare two images and returns a hamming distance. A value of 0 indicates a likely similar picture. A value between 1 and 10 is potentially a variation. A value greater than 10 is likely a different image.
|
52 |
+
*
|
53 |
+
* @param string|ImageInterface $image1 Can be an instance of Image or string containing the file system path to image.
|
54 |
+
* @param string|ImageInterface $image2 Can be an instance of Image or string containing the file system path to image.
|
55 |
+
*
|
56 |
+
* @return int Hamming distance. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor.
|
57 |
+
* @throws \Exception
|
58 |
+
*/
|
59 |
+
public function compare( $image1, $image2 );
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Crop the image to the given dimension and position.
|
63 |
+
*
|
64 |
+
* @param int $cropWidth Crop width in pixels.
|
65 |
+
* @param int $cropHeight Crop Height in pixels.
|
66 |
+
* @param int|string $cropX The number of pixels from the left of the image. This parameter can be a number or any of the words "left", "center", "right".
|
67 |
+
* @param int|string $cropY The number of pixels from the top of the image. This parameter can be a number or any of the words "top", "center", "bottom".
|
68 |
+
*
|
69 |
+
* @return EditorInterface An instance of image editor.
|
70 |
+
*/
|
71 |
+
public function crop( $cropWidth, $cropHeight, $cropX='center', $cropY='center');
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Dither image using Floyd-Steinberg algorithm. Dithering will reduce the color to black and white and add noise.
|
75 |
+
* @return EditorInterface An instance of image editor.
|
76 |
+
*/
|
77 |
+
public function dither();
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Draw a DrawingObject on the image. See Drawing Objects section.
|
81 |
+
*
|
82 |
+
* @param DrawingObjectInterface $drawingObject
|
83 |
+
*
|
84 |
+
* @return EditorInterface An instance of image editor.
|
85 |
+
*/
|
86 |
+
public function draw( $drawingObject );
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Creates an ellipse.
|
90 |
+
*
|
91 |
+
* @param int $width Width of ellipse in pixels.
|
92 |
+
* @param int $height Height of ellipse in pixels.
|
93 |
+
* @param array $pos Array containing int X and int Y position of the ellipse from top left of the canvass.
|
94 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
95 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
96 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
97 |
+
*
|
98 |
+
* @return EditorInterface An instance of image editor.
|
99 |
+
*/
|
100 |
+
public function ellipse($width, $height, array $pos, $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF');
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Compare if two images are equal. It will compare if the two images are of the same width and height. If the dimensions differ, it will return false. If the dimensions are equal, it will loop through each pixels. If one of the pixel don't match, it will return false. The pixels are compared using their RGB (Red, Green, Blue) values.
|
104 |
+
*
|
105 |
+
* @param string|ImageInterface $image1 Can be an instance of Image or string containing the file system path to image.
|
106 |
+
* @param string|ImageInterface $image2 Can be an instance of Image or string containing the file system path to image.
|
107 |
+
*
|
108 |
+
* @return bool True if equals false if not. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor.
|
109 |
+
* @throws \Exception
|
110 |
+
*/
|
111 |
+
public function equal( $image1, $image2 );
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Fill entire image with color.
|
115 |
+
*
|
116 |
+
* @param Color $color An instance of Grafika\Color class.
|
117 |
+
* @param int $x X-coordinate of start point.
|
118 |
+
* @param int $y Y-coordinate of start point.
|
119 |
+
*
|
120 |
+
* @return EditorInterface An instance of image editor.
|
121 |
+
*/
|
122 |
+
public function fill( $color, $x = 0, $y = 0 );
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Free the current image clearing resources associated with it.
|
126 |
+
*/
|
127 |
+
public function free();
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Converts image to grayscale.
|
131 |
+
*
|
132 |
+
* @return EditorInterface An instance of image editor.
|
133 |
+
*/
|
134 |
+
public function grayscale();
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Alias for grayscale.
|
138 |
+
*
|
139 |
+
* @return EditorInterface An instance of image editor.
|
140 |
+
*/
|
141 |
+
public function greyscale();
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Get image instance.
|
145 |
+
*
|
146 |
+
* @return ImageInterface An instance of image.
|
147 |
+
*/
|
148 |
+
public function getImage();
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Checks the PHP install if the editor is available.
|
152 |
+
*
|
153 |
+
* @return bool True if available false if not.
|
154 |
+
*/
|
155 |
+
public function isAvailable();
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Creates a line.
|
159 |
+
*
|
160 |
+
* @param array $point1 Array containing int X and int Y position of the starting point.
|
161 |
+
* @param array $point2 Array containing int X and int Y position of the starting point.
|
162 |
+
* @param int $thickness Thickness in pixel. Note: This is currently ignored in GD editor and falls back to 1.
|
163 |
+
* @param Color|string $color Color of the line. Defaults to black.
|
164 |
+
*
|
165 |
+
* @return EditorInterface An instance of image editor.
|
166 |
+
*/
|
167 |
+
public function line(array $point1, array $point2, $thickness = 1, $color = '#000000');
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Change the image opacity.
|
171 |
+
*
|
172 |
+
* @param float $opacity The opacity level where 1.0 is fully opaque and 0.0 is fully transparent.
|
173 |
+
*
|
174 |
+
* @return EditorInterface An instance of image editor.
|
175 |
+
*/
|
176 |
+
public function opacity( $opacity );
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Opens an image file for manipulation specified by $target.
|
180 |
+
*
|
181 |
+
* @param mixed $target Can be an instance of Image or a string containing file system path to the image.
|
182 |
+
*
|
183 |
+
* @return EditorInterface An instance of image editor.
|
184 |
+
*/
|
185 |
+
public function open( $target );
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Open an image by passing an instance of Image.
|
189 |
+
*
|
190 |
+
* @param ImageInterface $image
|
191 |
+
*
|
192 |
+
* @return $this
|
193 |
+
*/
|
194 |
+
public function openImage( $image );
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Open an image by passing a file system path.
|
198 |
+
*
|
199 |
+
* @param string $file A full path to the image in the file system.
|
200 |
+
*
|
201 |
+
* @return $this
|
202 |
+
* @throws \Exception
|
203 |
+
*/
|
204 |
+
public function openFile( $file );
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Overlay an image on top of the current image.
|
208 |
+
*
|
209 |
+
* @param ImageInterface|string $overlay Can be a string containing a file path of the image to overlay or an Image object.
|
210 |
+
* @param string|int $xPos Horizontal position of image. Can be 'left','center','right' or integer number. Defaults to 'center'.
|
211 |
+
* @param string|int $yPos Vertical position of image. Can be 'top', 'center','bottom' or integer number. Defaults to 'center'.
|
212 |
+
* @param null $width Width of overlay in pixels.
|
213 |
+
* @param null $height Height of overlay in pixels.
|
214 |
+
*
|
215 |
+
* @return EditorInterface An instance of image editor.
|
216 |
+
*/
|
217 |
+
public function overlay( $overlay, $xPos = 'center', $yPos = 'center', $width = null, $height = null );
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Creates a polygon.
|
221 |
+
*
|
222 |
+
* @param array $points Array of all X and Y positions. Must have at least three positions.
|
223 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
224 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
225 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
226 |
+
*
|
227 |
+
* @return EditorInterface An instance of image editor.
|
228 |
+
*/
|
229 |
+
public function polygon($points, $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF');
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Creates a rectangle.
|
233 |
+
* @param int $width Width of rectangle in pixels.
|
234 |
+
* @param int $height Height in pixels.
|
235 |
+
* @param array $pos Array of X and Y position. X is the distance in pixels from the left of the canvass to the left of the rectangle. Y is the distance from the top of the canvass to the top of the rectangle. Defaults to array(0,0).
|
236 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
237 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
238 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
239 |
+
*
|
240 |
+
* @return EditorInterface An instance of image editor.
|
241 |
+
*/
|
242 |
+
public function rectangle($width, $height, $pos = array(0, 0), $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF');
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Wrapper function for the resizeXXX family of functions. Resize an image to a given width, height and mode.
|
246 |
+
*
|
247 |
+
* @param int $newWidth Width in pixels.
|
248 |
+
* @param int $newHeight Height in pixels.
|
249 |
+
* @param string $mode Resize mode. Possible values: "exact", "exactHeight", "exactWidth", "fill", "fit".
|
250 |
+
*
|
251 |
+
* @return EditorInterface An instance of image editor.
|
252 |
+
*/
|
253 |
+
public function resize( $newWidth, $newHeight, $mode='fit' );
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Resize image to exact dimensions ignoring aspect ratio. Useful if you want to force exact width and height.
|
257 |
+
*
|
258 |
+
* @param int $newWidth Width in pixels.
|
259 |
+
* @param int $newHeight Height in pixels.
|
260 |
+
*
|
261 |
+
* @return EditorInterface An instance of image editor.
|
262 |
+
*/
|
263 |
+
public function resizeExact( $newWidth, $newHeight );
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Resize image to exact height. Width is auto calculated. Useful for creating row of images with the same height.
|
267 |
+
*
|
268 |
+
* @param int $newHeight Height in pixels.
|
269 |
+
*
|
270 |
+
* @return EditorInterface An instance of image editor.
|
271 |
+
*/
|
272 |
+
public function resizeExactHeight( $newHeight );
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Resize image to exact width. Height is auto calculated. Useful for creating column of images with the same width.
|
276 |
+
*
|
277 |
+
* @param int $newWidth Width in pixels.
|
278 |
+
*
|
279 |
+
* @return EditorInterface An instance of image editor.
|
280 |
+
*/
|
281 |
+
public function resizeExactWidth( $newWidth );
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Resize image to fill all the space in the given dimension. Excess parts are cropped.
|
285 |
+
*
|
286 |
+
* @param int $newWidth Width in pixels.
|
287 |
+
* @param int $newHeight Height in pixels.
|
288 |
+
*
|
289 |
+
* @return EditorInterface An instance of image editor.
|
290 |
+
*/
|
291 |
+
public function resizeFill( $newWidth, $newHeight );
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Resize an image to fit within the given width and height. The re-sized image will not exceed the given dimension. Useful if you want to preserve the aspect ratio.
|
295 |
+
*
|
296 |
+
* @param int $newWidth Width in pixels.
|
297 |
+
* @param int $newHeight Width in pixels.
|
298 |
+
*
|
299 |
+
* @return EditorInterface An instance of image editor.
|
300 |
+
*/
|
301 |
+
public function resizeFit( $newWidth, $newHeight );
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Rotate an image counter-clockwise.
|
305 |
+
*
|
306 |
+
* @param int $angle The angle in degrees.
|
307 |
+
* @param Color|null $color The Color object containing the background color.
|
308 |
+
*
|
309 |
+
* @return EditorInterface An instance of image editor.
|
310 |
+
*/
|
311 |
+
public function rotate( $angle, $color = null );
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Save the image to an image format.
|
315 |
+
*
|
316 |
+
* @param string $file File path where to save the image.
|
317 |
+
* @param null|string $type Type of image. Can be null, "GIF", "PNG", or "JPEG". If null, an appropriate file type will be used.
|
318 |
+
* @param null|string $quality Quality of image. Applies to JPEG only. Accepts number 0 - 100 where 0 is lowest and 100 is the highest quality. Or null for default.
|
319 |
+
* @param bool $interlace Set to true for progressive JPEG. Applies to JPEG only.
|
320 |
+
*
|
321 |
+
* @return EditorInterface An instance of image editor.
|
322 |
+
*/
|
323 |
+
public function save( $file, $type = null, $quality = null, $interlace = false );
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Set image instance.
|
327 |
+
*
|
328 |
+
* @param ImageInterface $image An instance of a class implementing Grafika/ImageInterface.
|
329 |
+
*/
|
330 |
+
public function setImage( $image );
|
331 |
+
|
332 |
+
/**
|
333 |
+
* Write text to image.
|
334 |
+
*
|
335 |
+
* @param string $text The text to be written.
|
336 |
+
* @param int $size The font size. Defaults to 12.
|
337 |
+
* @param int $x The distance from the left edge of the image to the left of the text. Defaults to 0.
|
338 |
+
* @param int $y The distance from the top edge of the image to the baseline of the text. Defaults to 12 (equal to font size) so that the text is placed within the image.
|
339 |
+
* @param Color $color The Color object. Default text color is black.
|
340 |
+
* @param string $font Full path to font file. If blank, will default to Liberation Sans font.
|
341 |
+
* @param int $angle Angle of text from 0 - 359. Defaults to 0.
|
342 |
+
*
|
343 |
+
* @return EditorInterface An instance of image editor.
|
344 |
+
* @throws \Exception
|
345 |
+
*/
|
346 |
+
public function text( $text, $size = 12, $x = 0, $y = 12, $color = null, $font = '', $angle = 0 );
|
347 |
+
|
348 |
+
}
|
src/CycloneSlider/Grafika/EffectInterface.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Interface EffectInterface
|
6 |
+
* @package Grafika
|
7 |
+
*/
|
8 |
+
interface EffectInterface {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @param ImageInterface $image
|
12 |
+
*
|
13 |
+
* @return ImageInterface
|
14 |
+
*/
|
15 |
+
public function apply( $image );
|
16 |
+
|
17 |
+
}
|
src/CycloneSlider/Grafika/Gd/DrawingObject/CubicBezier.php
ADDED
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\CubicBezier as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Image;
|
7 |
+
use CycloneSlider\Grafika\ImageInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class CubicBezier
|
11 |
+
* @package Grafika
|
12 |
+
*/
|
13 |
+
class CubicBezier extends Base implements DrawingObjectInterface
|
14 |
+
{
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param ImageInterface $image
|
18 |
+
* @return Image
|
19 |
+
*/
|
20 |
+
public function draw($image)
|
21 |
+
{
|
22 |
+
// Localize vars
|
23 |
+
$width = $image->getWidth();
|
24 |
+
$height = $image->getHeight();
|
25 |
+
$gd = $image->getCore();
|
26 |
+
|
27 |
+
list($x0, $y0) = $this->point1;
|
28 |
+
list($x1, $y1) = $this->control1;
|
29 |
+
list($x2, $y2) = $this->control2;
|
30 |
+
list($x3, $y3) = $this->point2;
|
31 |
+
|
32 |
+
$this->plot($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
|
33 |
+
|
34 |
+
$type = $image->getType();
|
35 |
+
$file = $image->getImageFile();
|
36 |
+
return new Image($gd, $file, $width, $height, $type); // Create new image with updated core
|
37 |
+
}
|
38 |
+
|
39 |
+
protected function plot($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
40 |
+
{ /* plot any cubic Bezier curve */
|
41 |
+
$n = 0;
|
42 |
+
$i = 0;
|
43 |
+
$xc = $x0 + $x1 - $x2 - $x3;
|
44 |
+
$xa = $xc - 4 * ($x1 - $x2);
|
45 |
+
$xb = $x0 - $x1 - $x2 + $x3;
|
46 |
+
$xd = $xb + 4 * ($x1 + $x2);
|
47 |
+
$yc = $y0 + $y1 - $y2 - $y3;
|
48 |
+
$ya = $yc - 4 * ($y1 - $y2);
|
49 |
+
$yb = $y0 - $y1 - $y2 + $y3;
|
50 |
+
$yd = $yb + 4 * ($y1 + $y2);
|
51 |
+
$fx0 = $x0;
|
52 |
+
$fx1 = 0;
|
53 |
+
$fx2 = 0;
|
54 |
+
$fx3 = 0;
|
55 |
+
$fy0 = $y0;
|
56 |
+
$fy1 = 0;
|
57 |
+
$fy2 = 0;
|
58 |
+
$fy3 = 0;
|
59 |
+
$t1 = $xb * $xb - $xa * $xc;
|
60 |
+
$t2 = 0;
|
61 |
+
$t = array();
|
62 |
+
/* sub-divide curve at gradient sign changes */
|
63 |
+
if ($xa == 0) { /* horizontal */
|
64 |
+
if (abs($xc) < 2 * abs($xb)) {
|
65 |
+
$t[$n++] = $xc / (2.0 * $xb);
|
66 |
+
} /* one change */
|
67 |
+
} else {
|
68 |
+
if ($t1 > 0.0) { /* two changes */
|
69 |
+
$t2 = sqrt($t1);
|
70 |
+
$t1 = ($xb - $t2) / $xa;
|
71 |
+
if (abs($t1) < 1.0) {
|
72 |
+
$t[$n++] = $t1;
|
73 |
+
}
|
74 |
+
$t1 = ($xb + $t2) / $xa;
|
75 |
+
if (abs($t1) < 1.0) {
|
76 |
+
$t[$n++] = $t1;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
}
|
80 |
+
$t1 = $yb * $yb - $ya * $yc;
|
81 |
+
if ($ya == 0) { /* vertical */
|
82 |
+
if (abs($yc) < 2 * abs($yb)) {
|
83 |
+
$t[$n++] = $yc / (2.0 * $yb);
|
84 |
+
} /* one change */
|
85 |
+
} else {
|
86 |
+
if ($t1 > 0.0) { /* two changes */
|
87 |
+
$t2 = sqrt($t1);
|
88 |
+
$t1 = ($yb - $t2) / $ya;
|
89 |
+
if (abs($t1) < 1.0) {
|
90 |
+
$t[$n++] = $t1;
|
91 |
+
}
|
92 |
+
$t1 = ($yb + $t2) / $ya;
|
93 |
+
if (abs($t1) < 1.0) {
|
94 |
+
$t[$n++] = $t1;
|
95 |
+
}
|
96 |
+
}
|
97 |
+
}
|
98 |
+
for ($i = 1; $i < $n; $i++) /* bubble sort of 4 points */ {
|
99 |
+
if (($t1 = $t[$i - 1]) > $t[$i]) {
|
100 |
+
$t[$i - 1] = $t[$i];
|
101 |
+
$t[$i] = $t1;
|
102 |
+
$i = 0;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
$t1 = -1.0;
|
106 |
+
$t[$n] = 1.0; /* begin / end point */
|
107 |
+
for ($i = 0; $i <= $n; $i++) { /* plot each segment separately */
|
108 |
+
$t2 = $t[$i]; /* sub-divide at $t[$i-1], $t[$i] */
|
109 |
+
$fx1 = ($t1 * ($t1 * $xb - 2 * $xc) - $t2 * ($t1 * ($t1 * $xa - 2 * $xb) + $xc) + $xd) / 8 - $fx0;
|
110 |
+
$fy1 = ($t1 * ($t1 * $yb - 2 * $yc) - $t2 * ($t1 * ($t1 * $ya - 2 * $yb) + $yc) + $yd) / 8 - $fy0;
|
111 |
+
$fx2 = ($t2 * ($t2 * $xb - 2 * $xc) - $t1 * ($t2 * ($t2 * $xa - 2 * $xb) + $xc) + $xd) / 8 - $fx0;
|
112 |
+
$fy2 = ($t2 * ($t2 * $yb - 2 * $yc) - $t1 * ($t2 * ($t2 * $ya - 2 * $yb) + $yc) + $yd) / 8 - $fy0;
|
113 |
+
$fx0 -= $fx3 = ($t2 * ($t2 * (3 * $xb - $t2 * $xa) - 3 * $xc) + $xd) / 8;
|
114 |
+
$fy0 -= $fy3 = ($t2 * ($t2 * (3 * $yb - $t2 * $ya) - 3 * $yc) + $yd) / 8;
|
115 |
+
$x3 = floor($fx3 + 0.5);
|
116 |
+
$y3 = floor($fy3 + 0.5); /* scale bounds to int */
|
117 |
+
if ($fx0 != 0.0) {
|
118 |
+
$fx1 *= $fx0 = ($x0 - $x3) / $fx0;
|
119 |
+
$fx2 *= $fx0;
|
120 |
+
}
|
121 |
+
if ($fy0 != 0.0) {
|
122 |
+
$fy1 *= $fy0 = ($y0 - $y3) / $fy0;
|
123 |
+
$fy2 *= $fy0;
|
124 |
+
}
|
125 |
+
if ($x0 != $x3 || $y0 != $y3) /* segment $t1 - $t2 */ {
|
126 |
+
$this->plotCubicSegment($gd, $x0, $y0, $x0 + $fx1, $y0 + $fy1, $x0 + $fx2, $y0 + $fy2, $x3, $y3);
|
127 |
+
}
|
128 |
+
$x0 = $x3;
|
129 |
+
$y0 = $y3;
|
130 |
+
$fx0 = $fx3;
|
131 |
+
$fy0 = $fy3;
|
132 |
+
$t1 = $t2;
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
protected function plotCubicSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
|
137 |
+
{ /* plot limited anti-aliased cubic Bezier segment */
|
138 |
+
$f = 0;
|
139 |
+
$fx = 0;
|
140 |
+
$fy = 0;
|
141 |
+
$leg = 1;
|
142 |
+
$sx = $x0 < $x3 ? 1 : -1;
|
143 |
+
$sy = $y0 < $y3 ? 1 : -1; /* step direction */
|
144 |
+
|
145 |
+
$xc = -abs($x0 + $x1 - $x2 - $x3);
|
146 |
+
$xa = $xc - 4 * $sx * ($x1 - $x2);
|
147 |
+
$xb = $sx * ($x0 - $x1 - $x2 + $x3);
|
148 |
+
$yc = -abs($y0 + $y1 - $y2 - $y3);
|
149 |
+
$ya = $yc - 4 * $sy * ($y1 - $y2);
|
150 |
+
$yb = $sy * ($y0 - $y1 - $y2 + $y3);
|
151 |
+
|
152 |
+
$ab = 0;
|
153 |
+
$ac = 0;
|
154 |
+
$bc = 0;
|
155 |
+
$ba = 0;
|
156 |
+
$xx = 0;
|
157 |
+
$xy = 0;
|
158 |
+
$yy = 0;
|
159 |
+
$dx = 0;
|
160 |
+
$dy = 0;
|
161 |
+
$ex = 0;
|
162 |
+
$px = 0;
|
163 |
+
$py = 0;
|
164 |
+
$ed = 0;
|
165 |
+
$ip = 0;
|
166 |
+
$EP = 0.01;
|
167 |
+
/* check for curve restrains */
|
168 |
+
/* slope P0-P1 == P2-P3 and (P0-P3 == P1-P2 or no slope change) */
|
169 |
+
assert(($x1 - $x0) * ($x2 - $x3) < $EP && (($x3 - $x0) * ($x1 - $x2) < $EP || $xb * $xb < $xa * $xc + $EP));
|
170 |
+
assert(($y1 - $y0) * ($y2 - $y3) < $EP && (($y3 - $y0) * ($y1 - $y2) < $EP || $yb * $yb < $ya * $yc + $EP));
|
171 |
+
if ($xa == 0 && $ya == 0) { /* quadratic Bezier */
|
172 |
+
$sx = floor((3 * $x1 - $x0 + 1) / 2);
|
173 |
+
$sy = floor((3 * $y1 - $y0 + 1) / 2); /* new midpoint */
|
174 |
+
$this->plotQuadSegment($gd, $x0, $y0, $sx, $sy, $x3, $y3);
|
175 |
+
return;
|
176 |
+
}
|
177 |
+
$x1 = ($x1 - $x0) * ($x1 - $x0) + ($y1 - $y0) * ($y1 - $y0) + 1; /* line lengths */
|
178 |
+
$x2 = ($x2 - $x3) * ($x2 - $x3) + ($y2 - $y3) * ($y2 - $y3) + 1;
|
179 |
+
do { /* loop over both ends */
|
180 |
+
$ab = $xa * $yb - $xb * $ya;
|
181 |
+
$ac = $xa * $yc - $xc * $ya;
|
182 |
+
$bc = $xb * $yc - $xc * $yb;
|
183 |
+
$ip = 4 * $ab * $bc - $ac * $ac; /* self intersection loop at all? */
|
184 |
+
$ex = $ab * ($ab + $ac - 3 * $bc) + $ac * $ac; /* P0 part of self-intersection loop? */
|
185 |
+
$f = $ex > 0 ? 1 : sqrt(1 + 1024 / $x1); /* calculate resolution */
|
186 |
+
$ab *= $f;
|
187 |
+
$ac *= $f;
|
188 |
+
$bc *= $f;
|
189 |
+
$ex *= $f * $f; /* increase resolution */
|
190 |
+
$xy = 9 * ($ab + $ac + $bc) / 8;
|
191 |
+
$ba = 8 * ($xa - $ya);/* init differences of 1st degree */
|
192 |
+
$dx = 27 * (8 * $ab * ($yb * $yb - $ya * $yc) + $ex * ($ya + 2 * $yb + $yc)) / 64 - $ya * $ya * ($xy - $ya);
|
193 |
+
$dy = 27 * (8 * $ab * ($xb * $xb - $xa * $xc) - $ex * ($xa + 2 * $xb + $xc)) / 64 - $xa * $xa * ($xy + $xa);
|
194 |
+
/* init differences of 2nd degree */
|
195 |
+
$xx = 3 * (3 * $ab * (3 * $yb * $yb - $ya * $ya - 2 * $ya * $yc) - $ya * (3 * $ac * ($ya + $yb) + $ya * $ba)) / 4;
|
196 |
+
$yy = 3 * (3 * $ab * (3 * $xb * $xb - $xa * $xa - 2 * $xa * $xc) - $xa * (3 * $ac * ($xa + $xb) + $xa * $ba)) / 4;
|
197 |
+
$xy = $xa * $ya * (6 * $ab + 6 * $ac - 3 * $bc + $ba);
|
198 |
+
$ac = $ya * $ya;
|
199 |
+
$ba = $xa * $xa;
|
200 |
+
$xy = 3 * ($xy + 9 * $f * ($ba * $yb * $yc - $xb * $xc * $ac) - 18 * $xb * $yb * $ab) / 8;
|
201 |
+
if ($ex < 0) { /* negate values if inside self-intersection loop */
|
202 |
+
$dx = -$dx;
|
203 |
+
$dy = -$dy;
|
204 |
+
$xx = -$xx;
|
205 |
+
$yy = -$yy;
|
206 |
+
$xy = -$xy;
|
207 |
+
$ac = -$ac;
|
208 |
+
$ba = -$ba;
|
209 |
+
} /* init differences of 3rd degree */
|
210 |
+
$ab = 6 * $ya * $ac;
|
211 |
+
$ac = -6 * $xa * $ac;
|
212 |
+
$bc = 6 * $ya * $ba;
|
213 |
+
$ba = -6 * $xa * $ba;
|
214 |
+
$dx += $xy;
|
215 |
+
$ex = $dx + $dy;
|
216 |
+
$dy += $xy; /* error of 1st step */
|
217 |
+
for ($fx = $fy = $f; $x0 != $x3 && $y0 != $y3;) {
|
218 |
+
$y1 = min($xy - $dx, $dy - $xy);
|
219 |
+
$ed = max($xy - $dx, $dy - $xy); /* approximate error distance */
|
220 |
+
$ed = $f * ($ed + 2 * $ed * $y1 * $y1 / (4 * $ed * $ed + $y1 * $y1));
|
221 |
+
$y1 = 255 * abs($ex - ($f - $fx + 1) * $dx - ($f - $fy + 1) * $dy + $f * $xy) / $ed;
|
222 |
+
if ($y1 < 256) {
|
223 |
+
$this->setPixel($gd, $x0, $y0, $y1 / 255);
|
224 |
+
} /* plot curve */
|
225 |
+
$px = abs($ex - ($f - $fx + 1) * $dx + ($fy - 1) * $dy); /* pixel intensity x move */
|
226 |
+
$py = abs($ex + ($fx - 1) * $dx - ($f - $fy + 1) * $dy); /* pixel intensity y move */
|
227 |
+
$y2 = $y0;
|
228 |
+
do { /* move sub-steps of one pixel */
|
229 |
+
if ($ip >= -$EP) /* intersection possible? -> check.. */ {
|
230 |
+
if ($dx + $xx > $xy || $dy + $yy < $xy) {
|
231 |
+
goto exits;
|
232 |
+
}
|
233 |
+
} /* two x or y steps */
|
234 |
+
$y1 = 2 * $ex + $dx; /* save value for test of y step */
|
235 |
+
if (2 * $ex + $dy > 0) { /* x sub-step */
|
236 |
+
$fx--;
|
237 |
+
$ex += $dx += $xx;
|
238 |
+
$dy += $xy += $ac;
|
239 |
+
$yy += $bc;
|
240 |
+
$xx += $ab;
|
241 |
+
} else {
|
242 |
+
if ($y1 > 0) {
|
243 |
+
goto exits;
|
244 |
+
}
|
245 |
+
} /* tiny nearly cusp */
|
246 |
+
if ($y1 <= 0) { /* y sub-step */
|
247 |
+
$fy--;
|
248 |
+
$ex += $dy += $yy;
|
249 |
+
$dx += $xy += $bc;
|
250 |
+
$xx += $ac;
|
251 |
+
$yy += $ba;
|
252 |
+
}
|
253 |
+
} while ($fx > 0 && $fy > 0); /* pixel complete? */
|
254 |
+
if (2 * $fy <= $f) { /* x+ anti-aliasing pixel */
|
255 |
+
if ($py < $ed) {
|
256 |
+
$this->setPixel($gd, $x0 + $sx, $y0, $py / $ed);
|
257 |
+
} /* plot curve */
|
258 |
+
$y0 += $sy;
|
259 |
+
$fy += $f; /* y step */
|
260 |
+
}
|
261 |
+
if (2 * $fx <= $f) { /* y+ anti-aliasing pixel */
|
262 |
+
if ($px < $ed) {
|
263 |
+
$this->setPixel($gd, $x0, $y2 + $sy, $px / $ed);
|
264 |
+
} /* plot curve */
|
265 |
+
$x0 += $sx;
|
266 |
+
$fx += $f; /* x step */
|
267 |
+
}
|
268 |
+
}
|
269 |
+
break; /* finish curve by line */
|
270 |
+
exits:
|
271 |
+
if (2 * $ex < $dy && 2 * $fy <= $f + 2) { /* round x+ approximation pixel */
|
272 |
+
if ($py < $ed) {
|
273 |
+
$this->setPixel($gd, $x0 + $sx, $y0, $py / $ed);
|
274 |
+
} /* plot curve */
|
275 |
+
$y0 += $sy;
|
276 |
+
}
|
277 |
+
if (2 * $ex > $dx && 2 * $fx <= $f + 2) { /* round y+ approximation pixel */
|
278 |
+
if ($px < $ed) {
|
279 |
+
$this->setPixel($gd, $x0, $y2 + $sy, $px / $ed);
|
280 |
+
} /* plot curve */
|
281 |
+
$x0 += $sx;
|
282 |
+
}
|
283 |
+
$xx = $x0;
|
284 |
+
$x0 = $x3;
|
285 |
+
$x3 = $xx;
|
286 |
+
$sx = -$sx;
|
287 |
+
$xb = -$xb; /* swap legs */
|
288 |
+
$yy = $y0;
|
289 |
+
$y0 = $y3;
|
290 |
+
$y3 = $yy;
|
291 |
+
$sy = -$sy;
|
292 |
+
$yb = -$yb;
|
293 |
+
$x1 = $x2;
|
294 |
+
} while ($leg--); /* try other end */
|
295 |
+
$this->plotLine($gd, $x0, $y0, $x3, $y3); /* remaining part in case of cusp or crunode */
|
296 |
+
}
|
297 |
+
|
298 |
+
protected function plotQuadSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2)
|
299 |
+
{ /* draw an limited anti-aliased quadratic Bezier segment */
|
300 |
+
$sx = $x2 - $x1;
|
301 |
+
$sy = $y2 - $y1;
|
302 |
+
$xx = $x0 - $x1;
|
303 |
+
$yy = $y0 - $y1;
|
304 |
+
$xy = $dx = $dy = $err = $ed = 0;
|
305 |
+
$cur = $xx * $sy - $yy * $sx; /* $curvature */
|
306 |
+
if ($sx * (int)$sx + $sy * (int)$sy > $xx * $xx + $yy * $yy) { /* begin with longer part */
|
307 |
+
$x2 = $x0;
|
308 |
+
$x0 = $sx + $x1;
|
309 |
+
$y2 = $y0;
|
310 |
+
$y0 = $sy + $y1;
|
311 |
+
$cur = -$cur; /* swap P0 P2 */
|
312 |
+
}
|
313 |
+
if ($cur != 0) { /* no straight line */
|
314 |
+
$xx += $sx;
|
315 |
+
$xx *= $sx = $x0 < $x2 ? 1 : -1; /* x step direction */
|
316 |
+
$yy += $sy;
|
317 |
+
$yy *= $sy = $y0 < $y2 ? 1 : -1; /* y step direction */
|
318 |
+
$xy = 2 * $xx * $yy;
|
319 |
+
$xx *= $xx;
|
320 |
+
$yy *= $yy; /* differences 2nd degree */
|
321 |
+
if ($cur * $sx * $sy < 0) { /* negat$ed $curvature? */
|
322 |
+
$xx = -$xx;
|
323 |
+
$yy = -$yy;
|
324 |
+
$xy = -$xy;
|
325 |
+
$cur = -$cur;
|
326 |
+
}
|
327 |
+
$dx = 4.0 * $sy * ($x1 - $x0) * $cur + $xx - $xy; /* differences 1st degree */
|
328 |
+
$dy = 4.0 * $sx * ($y0 - $y1) * $cur + $yy - $xy;
|
329 |
+
$xx += $xx;
|
330 |
+
$yy += $yy;
|
331 |
+
$err = $dx + $dy + $xy; /* $error 1st step */
|
332 |
+
do {
|
333 |
+
$cur = min($dx + $xy, -$xy - $dy);
|
334 |
+
$ed = max($dx + $xy, -$xy - $dy); /* approximate $error distance */
|
335 |
+
$ed += 2 * $ed * $cur * $cur / (4 * $ed * $ed + $cur * $cur);
|
336 |
+
$this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy - $xy) / $ed); /* plot $curve */
|
337 |
+
if ($x0 == $x2 || $y0 == $y2) {
|
338 |
+
break;
|
339 |
+
} /* $curve finish$ed */
|
340 |
+
$x1 = $x0;
|
341 |
+
$cur = $dx - $err;
|
342 |
+
$y1 = 2 * $err + $dy < 0;
|
343 |
+
if (2 * $err + $dx > 0) { /* x step */
|
344 |
+
if ($err - $dy < $ed) {
|
345 |
+
$this->setPixel($gd, $x0, $y0 + $sy, abs($err - $dy) / $ed);
|
346 |
+
}
|
347 |
+
$x0 += $sx;
|
348 |
+
$dx -= $xy;
|
349 |
+
$err += $dy += $yy;
|
350 |
+
}
|
351 |
+
if ($y1) { /* y step */
|
352 |
+
if ($cur < $ed) {
|
353 |
+
$this->setPixel($gd, $x1 + $sx, $y0, abs($cur) / $ed);
|
354 |
+
}
|
355 |
+
$y0 += $sy;
|
356 |
+
$dy -= $xy;
|
357 |
+
$err += $dx += $xx;
|
358 |
+
}
|
359 |
+
} while ($dy < $dx); /* gradient negates -> close curves */
|
360 |
+
}
|
361 |
+
$this->plotLine($gd, $x0, $y0, $x2, $y2); /* plot remaining needle to end */
|
362 |
+
}
|
363 |
+
|
364 |
+
protected function plotLine($gd, $x0, $y0, $x1, $y1)
|
365 |
+
{ /* draw a black (0) anti-aliased line on white (255) background */
|
366 |
+
$dx = abs($x1 - $x0);
|
367 |
+
$sx = $x0 < $x1 ? 1 : -1;
|
368 |
+
$dy = -abs($y1 - $y0);
|
369 |
+
$sy = $y0 < $y1 ? 1 : -1;
|
370 |
+
$err = $dx + $dy;
|
371 |
+
$e2 = $x2 = 0; /* $error value e_xy */
|
372 |
+
$ed = $dx - $dy == 0 ? 1 : sqrt((float)$dx * $dx + (float)$dy * $dy);
|
373 |
+
for (; ;) { /* pixel loop */
|
374 |
+
$this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy) / $ed);
|
375 |
+
$e2 = $err;
|
376 |
+
$x2 = $x0;
|
377 |
+
if (2 * $e2 + $dx >= 0) { /* x step */
|
378 |
+
if ($x0 == $x1) {
|
379 |
+
break;
|
380 |
+
}
|
381 |
+
if ($e2 - $dy < $ed) {
|
382 |
+
$this->setPixel($gd, $x0, $y0 + $sy, ($e2 - $dy) / $ed);
|
383 |
+
}
|
384 |
+
$err += $dy;
|
385 |
+
$x0 += $sx;
|
386 |
+
}
|
387 |
+
if (2 * $e2 + $dy <= 0) { /* y step */
|
388 |
+
if ($y0 == $y1) {
|
389 |
+
break;
|
390 |
+
}
|
391 |
+
if ($dx - $e2 < $ed) {
|
392 |
+
$this->setPixel($gd, $x2 + $sx, $y0, ($dx - $e2) / $ed);
|
393 |
+
}
|
394 |
+
$err += $dx;
|
395 |
+
$y0 += $sy;
|
396 |
+
}
|
397 |
+
}
|
398 |
+
}
|
399 |
+
|
400 |
+
/**
|
401 |
+
* @param resource $gd
|
402 |
+
* @param int $x
|
403 |
+
* @param int $y
|
404 |
+
* @param float $ar Alpha ratio
|
405 |
+
*/
|
406 |
+
protected function setPixel($gd, $x, $y, $ar)
|
407 |
+
{
|
408 |
+
list($r, $g, $b) = $this->color->getRgb();
|
409 |
+
$c = imagecolorallocatealpha($gd, $r, $g, $b, 127 * $ar);
|
410 |
+
imagesetpixel($gd, $x, $y, $c);
|
411 |
+
}
|
412 |
+
}
|
src/CycloneSlider/Grafika/Gd/DrawingObject/Ellipse.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Ellipse as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Editor;
|
7 |
+
use CycloneSlider\Grafika\ImageInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class Ellipse
|
11 |
+
* @package Grafika
|
12 |
+
*/
|
13 |
+
class Ellipse extends Base implements DrawingObjectInterface
|
14 |
+
{
|
15 |
+
|
16 |
+
/**
|
17 |
+
* TODO: Anti-aliased curves
|
18 |
+
* @param ImageInterface $image
|
19 |
+
* @return ImageInterface
|
20 |
+
*/
|
21 |
+
public function draw($image)
|
22 |
+
{
|
23 |
+
|
24 |
+
list($x, $y) = $this->pos;
|
25 |
+
$left = $x + $this->width / 2;
|
26 |
+
$top = $y + $this->height / 2;
|
27 |
+
|
28 |
+
if( null !== $this->fillColor ){
|
29 |
+
list($r, $g, $b, $alpha) = $this->fillColor->getRgba();
|
30 |
+
$fillColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha));
|
31 |
+
imagefilledellipse($image->getCore(), $left, $top, $this->width, $this->height, $fillColorResource);
|
32 |
+
}
|
33 |
+
// Create borders. It will be placed on top of the filled ellipse (if present)
|
34 |
+
if ( 0 < $this->getBorderSize() and null !== $this->borderColor) { // With border > 0 AND borderColor !== null
|
35 |
+
list($r, $g, $b, $alpha) = $this->borderColor->getRgba();
|
36 |
+
$borderColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha));
|
37 |
+
imageellipse($image->getCore(), $left, $top, $this->width, $this->height, $borderColorResource);
|
38 |
+
}
|
39 |
+
|
40 |
+
return $image;
|
41 |
+
}
|
42 |
+
}
|
src/CycloneSlider/Grafika/Gd/DrawingObject/Line.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Line as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Image;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Line
|
10 |
+
* @package Grafika
|
11 |
+
*/
|
12 |
+
class Line extends Base implements DrawingObjectInterface
|
13 |
+
{
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param Image $image
|
17 |
+
*
|
18 |
+
* @return Image
|
19 |
+
*/
|
20 |
+
public function draw($image)
|
21 |
+
{
|
22 |
+
|
23 |
+
list( $x1, $y1 ) = $this->point1;
|
24 |
+
list( $x2, $y2 ) = $this->point2;
|
25 |
+
list( $r, $g, $b ) = $this->color->getRgb();
|
26 |
+
$color = imagecolorallocate( $image->getCore(), $r, $g, $b );
|
27 |
+
if ( function_exists( 'imageantialias' ) ) { // Not available on some if PHP is not precompiled with it even if GD is enabled
|
28 |
+
imageantialias( $image->getCore(), true );
|
29 |
+
}
|
30 |
+
imageline( $image->getCore(), $x1, $y1, $x2, $y2, $color );
|
31 |
+
|
32 |
+
return $image;
|
33 |
+
}
|
34 |
+
|
35 |
+
|
36 |
+
}
|
src/CycloneSlider/Grafika/Gd/DrawingObject/Polygon.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Polygon as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Editor;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Rectangle
|
10 |
+
* @package Grafika
|
11 |
+
*/
|
12 |
+
class Polygon extends Base implements DrawingObjectInterface
|
13 |
+
{
|
14 |
+
|
15 |
+
public function draw($image)
|
16 |
+
{
|
17 |
+
if(function_exists('imageantialias')){
|
18 |
+
imageantialias($image->getCore(), true);
|
19 |
+
}
|
20 |
+
list($r, $g, $b, $alpha) = $this->getBorderColor()->getRgba();
|
21 |
+
$borderColorResource = imagecolorallocatealpha(
|
22 |
+
$image->getCore(), $r, $g, $b,
|
23 |
+
Editor::gdAlpha($alpha)
|
24 |
+
);
|
25 |
+
|
26 |
+
$points = $this->points();
|
27 |
+
$count = count($this->pos);
|
28 |
+
|
29 |
+
|
30 |
+
// Create filled polygon
|
31 |
+
if( null !== $this->fillColor){
|
32 |
+
list($r, $g, $b, $alpha) = $this->getFillColor()->getRgba();
|
33 |
+
$fillColorResource = imagecolorallocatealpha(
|
34 |
+
$image->getCore(), $r, $g, $b,
|
35 |
+
Editor::gdAlpha($alpha)
|
36 |
+
);
|
37 |
+
imagefilledpolygon($image->getCore(), $points,
|
38 |
+
$count,
|
39 |
+
$fillColorResource
|
40 |
+
);
|
41 |
+
}
|
42 |
+
|
43 |
+
// Create polygon borders. It will be placed on top of the filled polygon (if present)
|
44 |
+
if ( 0 < $this->getBorderSize() ) { // With border > 0
|
45 |
+
imagepolygon($image->getCore(), $points,
|
46 |
+
$count,
|
47 |
+
$borderColorResource
|
48 |
+
);
|
49 |
+
}
|
50 |
+
return $image;
|
51 |
+
}
|
52 |
+
|
53 |
+
protected function points(){
|
54 |
+
$points = array();
|
55 |
+
foreach($this->pos as $pos){
|
56 |
+
$points[] = $pos[0];
|
57 |
+
$points[] = $pos[1];
|
58 |
+
}
|
59 |
+
if( count($points) < 6 ){
|
60 |
+
throw new \Exception('Polygon needs at least 3 points.');
|
61 |
+
}
|
62 |
+
return $points;
|
63 |
+
}
|
64 |
+
}
|
src/CycloneSlider/Grafika/Gd/DrawingObject/QuadraticBezier.php
ADDED
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\QuadraticBezier as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Image;
|
7 |
+
use CycloneSlider\Grafika\ImageInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class QuadraticBezier
|
11 |
+
* @package Grafika
|
12 |
+
*/
|
13 |
+
class QuadraticBezier extends Base implements DrawingObjectInterface
|
14 |
+
{
|
15 |
+
/**
|
16 |
+
* @link http://members.chello.at/easyfilter/bresenham.pdf
|
17 |
+
* @param ImageInterface $image
|
18 |
+
* @return Image
|
19 |
+
*/
|
20 |
+
public function draw($image)
|
21 |
+
{
|
22 |
+
// Localize vars
|
23 |
+
$width = $image->getWidth();
|
24 |
+
$height = $image->getHeight();
|
25 |
+
$gd = $image->getCore();
|
26 |
+
|
27 |
+
list($x0, $y0) = $this->point1;
|
28 |
+
list($x1, $y1) = $this->control;
|
29 |
+
list($x2, $y2) = $this->point2;
|
30 |
+
|
31 |
+
$this->plot($gd, $x0, $y0, $x1, $y1, $x2, $y2);
|
32 |
+
|
33 |
+
$type = $image->getType();
|
34 |
+
$file = $image->getImageFile();
|
35 |
+
return new Image($gd, $file, $width, $height, $type); // Create new image with updated core
|
36 |
+
}
|
37 |
+
|
38 |
+
protected function plot($gd, $x0, $y0, $x1, $y1, $x2, $y2)
|
39 |
+
{
|
40 |
+
/* plot any quadratic Bezier curve */
|
41 |
+
$x = $x0 - $x1;
|
42 |
+
$y = $y0 - $y1;
|
43 |
+
$t = $x0 - 2 * $x1 + $x2; //double
|
44 |
+
|
45 |
+
if ((int)$x * ($x2 - $x1) > 0) { /* horizontal cut at P4? */
|
46 |
+
if ((int)$y * ($y2 - $y1) > 0) /* vertical cut at P6 too? */ {
|
47 |
+
if (abs(($y0 - 2 * $y1 + $y2) / $t * $x) > abs($y)) { /* which first? */
|
48 |
+
$x0 = $x2;
|
49 |
+
$x2 = $x + $x1;
|
50 |
+
$y0 = $y2;
|
51 |
+
$y2 = $y + $y1; /* swap points */
|
52 |
+
}
|
53 |
+
} /* now horizontal cut at P4 comes first */
|
54 |
+
$t = ($x0 - $x1) / $t;
|
55 |
+
$r = (1 - $t) * ((1 - $t) * $y0 + 2.0 * $t * $y1) + $t * $t * $y2; /* By(t=P4) */
|
56 |
+
$t = ($x0 * $x2 - $x1 * $x1) * $t / ($x0 - $x1); /* gradient dP4/dx=0 */
|
57 |
+
$x = floor($t + 0.5);
|
58 |
+
$y = floor($r + 0.5);
|
59 |
+
$r = ($y1 - $y0) * ($t - $x0) / ($x1 - $x0) + $y0; /* intersect P3 | P0 P1 */
|
60 |
+
$this->plotSegment($gd, $x0, $y0, $x, floor($r + 0.5), $x, $y);
|
61 |
+
$r = ($y1 - $y2) * ($t - $x2) / ($x1 - $x2) + $y2; /* intersect P4 | P1 P2 */
|
62 |
+
$x0 = $x1 = $x;
|
63 |
+
$y0 = $y;
|
64 |
+
$y1 = floor($r + 0.5); /* P0 = P4, P1 = P8 */
|
65 |
+
}
|
66 |
+
if ((int)($y0 - $y1) * ($y2 - $y1) > 0) { /* vertical cut at P6? */
|
67 |
+
$t = $y0 - 2 * $y1 + $y2;
|
68 |
+
$t = ($y0 - $y1) / $t;
|
69 |
+
$r = (1 - $t) * ((1 - $t) * $x0 + 2.0 * $t * $x1) + $t * $t * $x2; /* Bx(t=P6) */
|
70 |
+
$t = ($y0 * $y2 - $y1 * $y1) * $t / ($y0 - $y1); /* gradient dP6/dy=0 */
|
71 |
+
$x = floor($r + 0.5);
|
72 |
+
$y = floor($t + 0.5);
|
73 |
+
$r = ($x1 - $x0) * ($t - $y0) / ($y1 - $y0) + $x0; /* intersect P6 | P0 P1 */
|
74 |
+
$this->plotSegment($gd, $x0, $y0, floor($r + 0.5), $y, $x, $y);
|
75 |
+
$r = ($x1 - $x2) * ($t - $y2) / ($y1 - $y2) + $x2; /* intersect P7 | P1 P2 */
|
76 |
+
$x0 = $x;
|
77 |
+
$x1 = floor($r + 0.5);
|
78 |
+
$y0 = $y1 = $y; /* P0 = P6, P1 = P7 */
|
79 |
+
}
|
80 |
+
$this->plotSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2); /* remaining part */
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Draw an limited anti-aliased quadratic Bezier segment.
|
85 |
+
* @param $gd
|
86 |
+
* @param $x0
|
87 |
+
* @param $y0
|
88 |
+
* @param $x1
|
89 |
+
* @param $y1
|
90 |
+
* @param $x2
|
91 |
+
* @param $y2
|
92 |
+
*/
|
93 |
+
protected function plotSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2)
|
94 |
+
{
|
95 |
+
$sx = $x2 - $x1;
|
96 |
+
$sy = $y2 - $y1;
|
97 |
+
$xx = $x0 - $x1;
|
98 |
+
$yy = $y0 - $y1;
|
99 |
+
|
100 |
+
$cur = $xx * $sy - $yy * $sx; /* $curvature */
|
101 |
+
assert($xx * $sx <= 0 && $yy * $sy <= 0);
|
102 |
+
if ($sx * (int)$sx + $sy * (int)$sy > $xx * $xx + $yy * $yy) { /* begin with longer part */
|
103 |
+
$x2 = $x0;
|
104 |
+
$x0 = $sx + $x1;
|
105 |
+
$y2 = $y0;
|
106 |
+
$y0 = $sy + $y1;
|
107 |
+
$cur = -$cur; /* swap P0 P2 */
|
108 |
+
}
|
109 |
+
if ($cur != 0) { /* no straight line */
|
110 |
+
$xx += $sx;
|
111 |
+
$xx *= $sx = $x0 < $x2 ? 1 : -1; /* x step direction */
|
112 |
+
$yy += $sy;
|
113 |
+
$yy *= $sy = $y0 < $y2 ? 1 : -1; /* y step direction */
|
114 |
+
$xy = 2 * $xx * $yy;
|
115 |
+
$xx *= $xx;
|
116 |
+
$yy *= $yy; /* differences 2nd degree */
|
117 |
+
if ($cur * $sx * $sy < 0) { /* negat$ed $curvature? */
|
118 |
+
$xx = -$xx;
|
119 |
+
$yy = -$yy;
|
120 |
+
$xy = -$xy;
|
121 |
+
$cur = -$cur;
|
122 |
+
}
|
123 |
+
$dx = 4.0 * $sy * ($x1 - $x0) * $cur + $xx - $xy; /* differences 1st degree */
|
124 |
+
$dy = 4.0 * $sx * ($y0 - $y1) * $cur + $yy - $xy;
|
125 |
+
$xx += $xx;
|
126 |
+
$yy += $yy;
|
127 |
+
$err = $dx + $dy + $xy; /* $error 1st step */
|
128 |
+
do {
|
129 |
+
$cur = min($dx + $xy, -$xy - $dy);
|
130 |
+
$ed = max($dx + $xy, -$xy - $dy); /* approximate $error distance */
|
131 |
+
$ed += 2 * $ed * $cur * $cur / (4 * $ed * $ed + $cur * $cur);
|
132 |
+
$this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy - $xy) / $ed); /* plot $curve */
|
133 |
+
if ($x0 == $x2 || $y0 == $y2) {
|
134 |
+
break;
|
135 |
+
} /* $curve finish$ed */
|
136 |
+
$x1 = $x0;
|
137 |
+
$cur = $dx - $err;
|
138 |
+
$y1 = 2 * $err + $dy < 0;
|
139 |
+
if (2 * $err + $dx > 0) { /* x step */
|
140 |
+
if ($err - $dy < $ed) {
|
141 |
+
$this->setPixel($gd, $x0, $y0 + $sy, abs($err - $dy) / $ed);
|
142 |
+
}
|
143 |
+
$x0 += $sx;
|
144 |
+
$dx -= $xy;
|
145 |
+
$err += $dy += $yy;
|
146 |
+
}
|
147 |
+
if ($y1) { /* y step */
|
148 |
+
if ($cur < $ed) {
|
149 |
+
$this->setPixel($gd, $x1 + $sx, $y0, abs($cur) / $ed);
|
150 |
+
}
|
151 |
+
$y0 += $sy;
|
152 |
+
$dy -= $xy;
|
153 |
+
$err += $dx += $xx;
|
154 |
+
}
|
155 |
+
} while ($dy < $dx); /* gradient negates -> close curves */
|
156 |
+
}
|
157 |
+
$this->plotLine($gd, $x0, $y0, $x2, $y2); /* plot remaining needle to end */
|
158 |
+
}
|
159 |
+
|
160 |
+
protected function plotLine($gd, $x0, $y0, $x1, $y1)
|
161 |
+
{
|
162 |
+
$dx = abs($x1 - $x0);
|
163 |
+
$sx = $x0 < $x1 ? 1 : -1;
|
164 |
+
$dy = -abs($y1 - $y0);
|
165 |
+
$sy = $y0 < $y1 ? 1 : -1;
|
166 |
+
$err = $dx + $dy;
|
167 |
+
|
168 |
+
$ed = $dx - $dy == 0 ? 1 : sqrt((float)$dx * $dx + (float)$dy * $dy);
|
169 |
+
for (; ;) { /* pixel loop */
|
170 |
+
$this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy) / $ed);
|
171 |
+
$e2 = $err;
|
172 |
+
$x2 = $x0;
|
173 |
+
if (2 * $e2 + $dx >= 0) { /* x step */
|
174 |
+
if ($x0 == $x1) {
|
175 |
+
break;
|
176 |
+
}
|
177 |
+
if ($e2 - $dy < $ed) {
|
178 |
+
$this->setPixel($gd, $x0, $y0 + $sy, ($e2 - $dy) / $ed);
|
179 |
+
}
|
180 |
+
$err += $dy;
|
181 |
+
$x0 += $sx;
|
182 |
+
}
|
183 |
+
if (2 * $e2 + $dy <= 0) { /* y step */
|
184 |
+
if ($y0 == $y1) {
|
185 |
+
break;
|
186 |
+
}
|
187 |
+
if ($dx - $e2 < $ed) {
|
188 |
+
$this->setPixel($gd, $x2 + $sx, $y0, ($dx - $e2) / $ed);
|
189 |
+
}
|
190 |
+
$err += $dx;
|
191 |
+
$y0 += $sy;
|
192 |
+
}
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* @param resource $gd
|
198 |
+
* @param int $x
|
199 |
+
* @param int $y
|
200 |
+
* @param float $ar Alpha ratio
|
201 |
+
*/
|
202 |
+
protected function setPixel($gd, $x, $y, $ar)
|
203 |
+
{
|
204 |
+
list($r, $g, $b) = $this->color->getRgb();
|
205 |
+
$c = imagecolorallocatealpha($gd, $r, $g, $b, 127 * $ar);
|
206 |
+
imagesetpixel($gd, $x, $y, $c);
|
207 |
+
}
|
208 |
+
}
|
src/CycloneSlider/Grafika/Gd/DrawingObject/Rectangle.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Rectangle as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Editor;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Rectangle
|
10 |
+
* @package Grafika
|
11 |
+
*/
|
12 |
+
class Rectangle extends Base implements DrawingObjectInterface
|
13 |
+
{
|
14 |
+
|
15 |
+
public function draw($image)
|
16 |
+
{
|
17 |
+
$x1 = $this->pos[0];
|
18 |
+
$x2 = $x1 + $this->getWidth();
|
19 |
+
$y1 = $this->pos[1];
|
20 |
+
$y2 = $y1 + $this->getHeight();
|
21 |
+
|
22 |
+
if( null !== $this->fillColor ){
|
23 |
+
list($r, $g, $b, $alpha) = $this->fillColor->getRgba();
|
24 |
+
$fillColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha));
|
25 |
+
imagefilledrectangle($image->getCore(), $x1, $y1, $x2, $y2, $fillColorResource);
|
26 |
+
}
|
27 |
+
// Create borders. It will be placed on top of the filled rectangle (if present)
|
28 |
+
if ( 0 < $this->getBorderSize() and null !== $this->borderColor) { // With border > 0 AND borderColor !== null
|
29 |
+
list($r, $g, $b, $alpha) = $this->borderColor->getRgba();
|
30 |
+
$borderColorResource = imagecolorallocatealpha($image->getCore(), $r, $g, $b, Editor::gdAlpha($alpha));
|
31 |
+
imagerectangle($image->getCore(), $x1, $y1, $x2, $y2, $borderColorResource);
|
32 |
+
}
|
33 |
+
|
34 |
+
return $image;
|
35 |
+
}
|
36 |
+
}
|
src/CycloneSlider/Grafika/Gd/Editor.php
ADDED
@@ -0,0 +1,1115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace CycloneSlider\Grafika\Gd;
|
4 |
+
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\EditorInterface;
|
7 |
+
use CycloneSlider\Grafika\EffectInterface;
|
8 |
+
use CycloneSlider\Grafika\Gd\DrawingObject\CubicBezier;
|
9 |
+
use CycloneSlider\Grafika\Gd\DrawingObject\Ellipse;
|
10 |
+
use CycloneSlider\Grafika\Gd\DrawingObject\Line;
|
11 |
+
use CycloneSlider\Grafika\Gd\DrawingObject\Polygon;
|
12 |
+
use CycloneSlider\Grafika\Gd\DrawingObject\QuadraticBezier;
|
13 |
+
use CycloneSlider\Grafika\Gd\DrawingObject\Rectangle;
|
14 |
+
use CycloneSlider\Grafika\Gd\Effect\Dither;
|
15 |
+
use CycloneSlider\Grafika\Grafika;
|
16 |
+
use CycloneSlider\Grafika\ImageInterface;
|
17 |
+
use CycloneSlider\Grafika\ImageType;
|
18 |
+
use CycloneSlider\Grafika\Color;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* GD Editor class. Uses the PHP GD library.
|
22 |
+
* @package Grafika\Gd
|
23 |
+
*/
|
24 |
+
final class Editor implements EditorInterface {
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var Image Holds the image instance.
|
28 |
+
*/
|
29 |
+
private $image;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Constructor
|
33 |
+
*/
|
34 |
+
function __construct() {
|
35 |
+
$this->image = null;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param EffectInterface $effect
|
40 |
+
*
|
41 |
+
* @return $this
|
42 |
+
*/
|
43 |
+
public function apply( $effect ){
|
44 |
+
$this->image = $effect->apply( $this->image );
|
45 |
+
return $this;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Creates a cubic bezier. Cubic bezier has 2 control points.
|
50 |
+
* @param array $point1 Array of X and Y value for start point.
|
51 |
+
* @param array $control1 Array of X and Y value for control point 1.
|
52 |
+
* @param array $control2 Array of X and Y value for control point 2.
|
53 |
+
* @param array $point2 Array of X and Y value for end point.
|
54 |
+
* @param Color|string $color Color of the curve. Accepts hex string or a Color object. Defaults to black.
|
55 |
+
* @return Editor
|
56 |
+
*/
|
57 |
+
public function bezierCubic($point1, $control1, $control2, $point2, $color = '#000000') {
|
58 |
+
if(is_string($color)){
|
59 |
+
$color = new Color($color);
|
60 |
+
}
|
61 |
+
$obj = new CubicBezier($point1, $control1, $control2, $point2, $color);
|
62 |
+
return $this->draw($obj);
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Creates a quadratic bezier. Quadratic bezier has 1 control point.
|
67 |
+
* @param array $point1 Array of X and Y value for start point.
|
68 |
+
* @param array $control Array of X and Y value for control point.
|
69 |
+
* @param array $point2 Array of X and Y value for end point.
|
70 |
+
* @param Color|string $color Color of the curve. Accepts hex string or a Color object. Defaults to black.
|
71 |
+
* @return Editor
|
72 |
+
*/
|
73 |
+
public function bezierQuad($point1, $control, $point2, $color = '#000000') {
|
74 |
+
if(is_string($color)){
|
75 |
+
$color = new Color($color);
|
76 |
+
}
|
77 |
+
$obj = new QuadraticBezier($point1, $control, $point2, $color);
|
78 |
+
return $this->draw($obj);
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Create a blank image given width and height.
|
83 |
+
*
|
84 |
+
* @param int $width Width of image in pixels.
|
85 |
+
* @param int $height Height of image in pixels.
|
86 |
+
*
|
87 |
+
* @return self
|
88 |
+
*/
|
89 |
+
public function blank( $width, $height ) {
|
90 |
+
$this->image = Image::createBlank( $width, $height );
|
91 |
+
|
92 |
+
return $this;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Compare two images and returns a hamming distance. A value of 0 indicates a likely similar picture. A value between 1 and 10 is potentially a variation. A value greater than 10 is likely a different image.
|
97 |
+
* @param ImageInterface|string $image1
|
98 |
+
* @param ImageInterface|string $image2
|
99 |
+
* @return int Hamming distance. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor.
|
100 |
+
* @throws \Exception
|
101 |
+
*/
|
102 |
+
public function compare( $image1, $image2 ){
|
103 |
+
|
104 |
+
if ( is_string( $image1 ) ) { // If string passed, turn it into a Image object
|
105 |
+
$image1 = Image::createFromFile( $image1 );
|
106 |
+
}
|
107 |
+
|
108 |
+
if ( is_string( $image2 ) ) { // If string passed, turn it into a Image object
|
109 |
+
$image2 = Image::createFromFile( $image2 );
|
110 |
+
}
|
111 |
+
|
112 |
+
$bin1 = $this->_differenceHash($image1);
|
113 |
+
$bin2 = $this->_differenceHash($image2);
|
114 |
+
$str1 = str_split($bin1);
|
115 |
+
$str2 = str_split($bin2);
|
116 |
+
$distance = 0;
|
117 |
+
foreach($str1 as $i=>$char){
|
118 |
+
if($char !== $str2[$i]){
|
119 |
+
$distance++;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
return $distance;
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Crop the image to the given dimension and position.
|
128 |
+
*
|
129 |
+
* @param int $cropWidth Crop width in pixels.
|
130 |
+
* @param int $cropHeight Crop Height in pixels.
|
131 |
+
* @param int|string $cropX The number of pixels from the left of the image. This parameter can be a number or any of the words "left", "center", "right".
|
132 |
+
* @param int|string $cropY The number of pixels from the top of the image. This parameter can be a number or any of the words "top", "center", "bottom".
|
133 |
+
*
|
134 |
+
* @return self
|
135 |
+
*/
|
136 |
+
public function crop($cropWidth, $cropHeight, $cropX='center', $cropY='center') {
|
137 |
+
|
138 |
+
if(is_string($cropX)){
|
139 |
+
// Compute position from string
|
140 |
+
switch ($cropX){
|
141 |
+
case 'left':
|
142 |
+
$x = 0;
|
143 |
+
break;
|
144 |
+
|
145 |
+
case 'right':
|
146 |
+
$x = $this->image->getWidth() - $cropWidth;
|
147 |
+
break;
|
148 |
+
|
149 |
+
case 'center':
|
150 |
+
default:
|
151 |
+
$x = (int) round( ($this->image->getWidth()/2) - ($cropWidth/2) );
|
152 |
+
break;
|
153 |
+
}
|
154 |
+
} else {
|
155 |
+
$x = $cropX;
|
156 |
+
}
|
157 |
+
|
158 |
+
if(is_string($cropY)){
|
159 |
+
switch ($cropY){
|
160 |
+
case 'top':
|
161 |
+
$y = 0;
|
162 |
+
break;
|
163 |
+
|
164 |
+
case 'bottom':
|
165 |
+
$y = $this->image->getHeight() - $cropHeight;
|
166 |
+
break;
|
167 |
+
|
168 |
+
case 'center':
|
169 |
+
default:
|
170 |
+
$y = (int) round( ($this->image->getHeight()/2) - ($cropHeight/2) );
|
171 |
+
break;
|
172 |
+
}
|
173 |
+
} else {
|
174 |
+
$y = $cropY;
|
175 |
+
}
|
176 |
+
|
177 |
+
// Create blank image
|
178 |
+
$newImageResource = imagecreatetruecolor($cropWidth, $cropHeight);
|
179 |
+
|
180 |
+
// Now crop
|
181 |
+
imagecopyresampled(
|
182 |
+
$newImageResource, // Target image
|
183 |
+
$this->image->getCore(), // Source image
|
184 |
+
0, // Target x
|
185 |
+
0, // Target y
|
186 |
+
$x, // Src x
|
187 |
+
$y, // Src y
|
188 |
+
$cropWidth, // Target width
|
189 |
+
$cropHeight, // Target height
|
190 |
+
$cropWidth, // Src width
|
191 |
+
$cropHeight // Src height
|
192 |
+
);
|
193 |
+
|
194 |
+
// Free memory of old resource
|
195 |
+
imagedestroy( $this->image->getCore() );
|
196 |
+
|
197 |
+
// Cropped image instance
|
198 |
+
$this->image = new Image($newImageResource, $this->image->getImageFile(), $cropWidth, $cropHeight, $this->image->getType());
|
199 |
+
|
200 |
+
return $this;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Dither image using Floyd-Steinberg algorithm. Dithering will reduce the color to black and white and add noise.
|
205 |
+
* @return EditorInterface An instance of image editor.
|
206 |
+
*/
|
207 |
+
public function dither(){
|
208 |
+
$e = new Dither();
|
209 |
+
return $this->apply($e);
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @param DrawingObjectInterface $drawingObject
|
214 |
+
*
|
215 |
+
* @return $this
|
216 |
+
*/
|
217 |
+
public function draw( $drawingObject ){
|
218 |
+
$this->image = $drawingObject->draw( $this->image );
|
219 |
+
return $this;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Creates an ellipse.
|
224 |
+
*
|
225 |
+
* @param int $width Width of ellipse in pixels.
|
226 |
+
* @param int $height Height of ellipse in pixels.
|
227 |
+
* @param array $pos Array containing int X and int Y position of the ellipse from top left of the canvass.
|
228 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
229 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
230 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
231 |
+
*
|
232 |
+
* @return EditorInterface An instance of image editor.
|
233 |
+
*/
|
234 |
+
public function ellipse($width, $height, array $pos, $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF') {
|
235 |
+
if(is_string($borderColor)){
|
236 |
+
$borderColor = new Color($borderColor);
|
237 |
+
}
|
238 |
+
if(is_string($fillColor)){
|
239 |
+
$fillColor = new Color($fillColor);
|
240 |
+
}
|
241 |
+
$obj = new Ellipse($width, $height, $pos, $borderSize, $borderColor, $fillColor);
|
242 |
+
return $this->draw($obj);
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Compare if two images are equal. It will compare if the two images are of the same width and height. If the dimensions differ, it will return false. If the dimensions are equal, it will loop through each pixels. If one of the pixel don't match, it will return false. The pixels are compared using their RGB (Red, Green, Blue) values.
|
247 |
+
*
|
248 |
+
* @param string|ImageInterface $image1 Can be an instance of Image or string containing the file system path to image.
|
249 |
+
* @param string|ImageInterface $image2 Can be an instance of Image or string containing the file system path to image.
|
250 |
+
*
|
251 |
+
* @return bool True if equals false if not. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor.
|
252 |
+
* @throws \Exception
|
253 |
+
*/
|
254 |
+
public function equal( $image1, $image2 ){
|
255 |
+
|
256 |
+
if ( is_string( $image1 ) ) { // If string passed, turn it into a Image object
|
257 |
+
$image1 = Image::createFromFile( $image1 );
|
258 |
+
}
|
259 |
+
|
260 |
+
if ( is_string( $image2 ) ) { // If string passed, turn it into a Image object
|
261 |
+
$image2 = Image::createFromFile( $image2 );
|
262 |
+
}
|
263 |
+
|
264 |
+
// Check if image dimensions are equal
|
265 |
+
if($image1->getWidth() !== $image2->getWidth() or $image1->getHeight() !== $image2->getHeight()) {
|
266 |
+
return false;
|
267 |
+
|
268 |
+
} else {
|
269 |
+
|
270 |
+
// Loop using image1
|
271 |
+
for ( $y = 0; $y < $image1->getHeight(); $y ++ ) {
|
272 |
+
for ( $x = 0; $x < $image1->getWidth(); $x ++ ) {
|
273 |
+
|
274 |
+
// Get image1 pixel
|
275 |
+
$rgb1 = imagecolorat( $image1->getCore(), $x, $y );
|
276 |
+
$r1 = ($rgb1 >> 16) & 0xFF;
|
277 |
+
$g1 = ($rgb1 >> 8) & 0xFF;
|
278 |
+
$b1 = $rgb1 & 0xFF;
|
279 |
+
|
280 |
+
// Get image2 pixel
|
281 |
+
$rgb2 = imagecolorat( $image2->getCore(), $x, $y );
|
282 |
+
$r2 = ($rgb2 >> 16) & 0xFF;
|
283 |
+
$g2 = ($rgb2 >> 8) & 0xFF;
|
284 |
+
$b2 = $rgb2 & 0xFF;
|
285 |
+
|
286 |
+
// Compare pixel value
|
287 |
+
if (
|
288 |
+
$r1 !== $r2 or
|
289 |
+
$g1 !== $g2 or
|
290 |
+
$b1 !== $b2
|
291 |
+
) {
|
292 |
+
return false;
|
293 |
+
}
|
294 |
+
}
|
295 |
+
}
|
296 |
+
}
|
297 |
+
return true;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Free the current image clearing resources associated with it.
|
302 |
+
*/
|
303 |
+
public function free(){
|
304 |
+
if ( null !== $this->image ) {
|
305 |
+
if( null !== $this->image->getCore() ) {
|
306 |
+
imagedestroy( $this->image->getCore() );
|
307 |
+
}
|
308 |
+
} else {
|
309 |
+
$this->image = null;
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Fill entire image with color.
|
315 |
+
*
|
316 |
+
* @param Color $color Color object
|
317 |
+
* @param int $x X-coordinate of start point
|
318 |
+
* @param int $y Y-coordinate of start point
|
319 |
+
*
|
320 |
+
* @return self
|
321 |
+
*/
|
322 |
+
public function fill( $color, $x = 0, $y = 0 ) {
|
323 |
+
|
324 |
+
$this->_imageCheck();
|
325 |
+
|
326 |
+
list( $r, $g, $b, $alpha ) = $color->getRgba();
|
327 |
+
|
328 |
+
$colorResource = imagecolorallocatealpha( $this->image->getCore(), $r, $g, $b,
|
329 |
+
$this->gdAlpha( $alpha ) );
|
330 |
+
imagefill( $this->image->getCore(), $x, $y, $colorResource );
|
331 |
+
|
332 |
+
return $this;
|
333 |
+
}
|
334 |
+
|
335 |
+
/**
|
336 |
+
* Converts image to grayscale.
|
337 |
+
*
|
338 |
+
* @return $this
|
339 |
+
*/
|
340 |
+
public function grayscale() {
|
341 |
+
$this->_imageCheck();
|
342 |
+
|
343 |
+
imagefilter( $this->image->getCore(), IMG_FILTER_GRAYSCALE );
|
344 |
+
|
345 |
+
return $this;
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* Alias for grayscale. They are the same.
|
350 |
+
*
|
351 |
+
* @return $this
|
352 |
+
*/
|
353 |
+
public function greyscale() {
|
354 |
+
return $this->grayscale();
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Get image instance.
|
359 |
+
*
|
360 |
+
* @return Image
|
361 |
+
*/
|
362 |
+
public function getImage() {
|
363 |
+
return $this->image;
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Checks the PHP install if the editor is available.
|
368 |
+
*
|
369 |
+
* @return bool True if available false if not.
|
370 |
+
*/
|
371 |
+
public function isAvailable() {
|
372 |
+
if ( false === extension_loaded( 'gd' ) || false === function_exists( 'gd_info' ) ) {
|
373 |
+
return false;
|
374 |
+
}
|
375 |
+
|
376 |
+
// On some setups GD library does not provide imagerotate()
|
377 |
+
if ( ! function_exists( 'imagerotate' ) ) {
|
378 |
+
|
379 |
+
return false;
|
380 |
+
}
|
381 |
+
|
382 |
+
return true;
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Creates a line.
|
387 |
+
*
|
388 |
+
* @param array $point1 Array containing int X and int Y position of the starting point.
|
389 |
+
* @param array $point2 Array containing int X and int Y position of the starting point.
|
390 |
+
* @param int $thickness Thickness in pixel. Note: This is currently ignored in GD editor and falls back to 1.
|
391 |
+
* @param Color|string $color Color of the line. Defaults to black.
|
392 |
+
* @return Editor
|
393 |
+
*/
|
394 |
+
public function line(array $point1, array $point2, $thickness = 1, $color = '#000000') {
|
395 |
+
if(is_string($color)){
|
396 |
+
$color = new Color($color);
|
397 |
+
}
|
398 |
+
$obj = new Line($point1, $point2, $thickness, $color);
|
399 |
+
return $this->draw($obj);
|
400 |
+
}
|
401 |
+
|
402 |
+
/**
|
403 |
+
* Sets the image to the specified opacity level where 1.0 is fully opaque and 0.0 is fully transparent.
|
404 |
+
* Warning: This function loops thru each pixel manually which can be slow. Use sparingly.
|
405 |
+
*
|
406 |
+
* @param float $opacity
|
407 |
+
*
|
408 |
+
* @return self
|
409 |
+
* @throws \Exception
|
410 |
+
*/
|
411 |
+
public function opacity( $opacity ){
|
412 |
+
|
413 |
+
$this->_imageCheck();
|
414 |
+
|
415 |
+
// Bounds checks
|
416 |
+
$opacity = ($opacity > 1) ? 1 : $opacity;
|
417 |
+
$opacity = ($opacity < 0) ? 0 : $opacity;
|
418 |
+
|
419 |
+
for($y = 0; $y < $this->image->getHeight(); $y++){
|
420 |
+
for($x = 0; $x < $this->image->getWidth(); $x++){
|
421 |
+
$rgb = imagecolorat($this->image->getCore(), $x, $y);
|
422 |
+
$alpha = ($rgb >> 24) & 0x7F; // 127 in hex. These are binary operations.
|
423 |
+
$r = ($rgb >> 16) & 0xFF;
|
424 |
+
$g = ($rgb >> 8) & 0xFF;
|
425 |
+
$b = $rgb & 0xFF;
|
426 |
+
|
427 |
+
// Reverse alpha values from 127-0 (transparent to opaque) to 0-127 for easy math
|
428 |
+
// Previously: 0 = opaque, 127 = transparent.
|
429 |
+
// Now: 0 = transparent, 127 = opaque
|
430 |
+
$reverse = 127 - $alpha;
|
431 |
+
$reverse = round($reverse * $opacity);
|
432 |
+
|
433 |
+
if( $alpha < 127 ) { // Process non transparent pixels only
|
434 |
+
imagesetpixel( $this->image->getCore(), $x, $y, imagecolorallocatealpha( $this->image->getCore(), $r, $g, $b, 127 - $reverse ) );
|
435 |
+
}
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
return $this;
|
440 |
+
}
|
441 |
+
|
442 |
+
/**
|
443 |
+
* Opens an image file for manipulation specified by $target.
|
444 |
+
*
|
445 |
+
* @param mixed $target Can be an instance of Image or a string containing file system path to the image.
|
446 |
+
*
|
447 |
+
* @return Editor
|
448 |
+
* @throws \Exception
|
449 |
+
*/
|
450 |
+
public function open( $target ) {
|
451 |
+
if( $target instanceof ImageInterface) {
|
452 |
+
$this->openImage( $target );
|
453 |
+
} else if (is_string($target)){
|
454 |
+
$this->openFile( $target );
|
455 |
+
} else {
|
456 |
+
throw new \Exception( 'Could not open image.' );
|
457 |
+
}
|
458 |
+
|
459 |
+
return $this;
|
460 |
+
}
|
461 |
+
|
462 |
+
/**
|
463 |
+
* Open an image by passing an instance of Image.
|
464 |
+
*
|
465 |
+
* @param ImageInterface $image
|
466 |
+
*
|
467 |
+
* @return $this
|
468 |
+
*/
|
469 |
+
public function openImage( $image ){
|
470 |
+
$this->image = $image;
|
471 |
+
|
472 |
+
return $this;
|
473 |
+
}
|
474 |
+
|
475 |
+
/**
|
476 |
+
* Open an image by passing a file system path.
|
477 |
+
*
|
478 |
+
* @param string $file A full path to the image in the file system.
|
479 |
+
*
|
480 |
+
* @return $this
|
481 |
+
* @throws \Exception
|
482 |
+
*/
|
483 |
+
public function openFile( $file ){
|
484 |
+
$this->image = Image::createFromFile( $file );
|
485 |
+
|
486 |
+
return $this;
|
487 |
+
}
|
488 |
+
|
489 |
+
/**
|
490 |
+
* Overlay an image on top of the current image.
|
491 |
+
*
|
492 |
+
* @param Image|string $overlay Can be a string containing a file path of the image to overlay or an Image object.
|
493 |
+
* @param string|int $xPos Horizontal position of image. Can be 'left','center','right' or integer number. Defaults to 'center'.
|
494 |
+
* @param string|int $yPos Vertical position of image. Can be 'top', 'center','bottom' or integer number. Defaults to 'center'.
|
495 |
+
* @param null $width
|
496 |
+
* @param null $height
|
497 |
+
*
|
498 |
+
* @return Editor
|
499 |
+
* @throws \Exception
|
500 |
+
*/
|
501 |
+
public function overlay( $overlay, $xPos = 'center', $yPos = 'center', $width = null, $height = null ) {
|
502 |
+
|
503 |
+
$this->_imageCheck();
|
504 |
+
|
505 |
+
if ( is_string( $overlay ) ) { // If string passed, turn it into a Image object
|
506 |
+
$overlay = Image::createFromFile( $overlay );
|
507 |
+
}
|
508 |
+
|
509 |
+
// Resize overlay
|
510 |
+
if($width and $height){
|
511 |
+
|
512 |
+
$overlayWidth = $overlay->getWidth();
|
513 |
+
$overlayHeight = $overlay->getHeight();
|
514 |
+
|
515 |
+
if(is_numeric($width)){
|
516 |
+
$overlayWidth = (int) $width;
|
517 |
+
} else {
|
518 |
+
$percent = strpos( $width, '%' );
|
519 |
+
if( false !== $percent){
|
520 |
+
$overlayWidth = intval( $width ) / 100 * $this->image->getWidth();
|
521 |
+
}
|
522 |
+
}
|
523 |
+
|
524 |
+
if(is_numeric($height)){
|
525 |
+
$overlayHeight = (int) $height;
|
526 |
+
} else {
|
527 |
+
$percent = strpos( $height, '%' );
|
528 |
+
if( false !== $percent){
|
529 |
+
$overlayHeight = intval( $height ) / 100 * $this->image->getHeight();
|
530 |
+
}
|
531 |
+
}
|
532 |
+
|
533 |
+
$editor = new Editor();
|
534 |
+
$editor->setImage($overlay);
|
535 |
+
$editor->resizeFit( $overlayWidth, $overlayHeight );
|
536 |
+
$overlay = $editor->getImage();
|
537 |
+
|
538 |
+
//$overlay->setImageResource( $editor->getImage()->getImageResource() );
|
539 |
+
}
|
540 |
+
|
541 |
+
//$x = $y = 0;
|
542 |
+
|
543 |
+
if ( is_string( $xPos ) ) {
|
544 |
+
// Compute position from string
|
545 |
+
switch ( $xPos ) {
|
546 |
+
case 'left':
|
547 |
+
$x = 0;
|
548 |
+
break;
|
549 |
+
|
550 |
+
case 'right':
|
551 |
+
$x = $this->image->getWidth() - $overlay->getWidth();
|
552 |
+
break;
|
553 |
+
|
554 |
+
case 'center':
|
555 |
+
default:
|
556 |
+
$x = (int) round( ( $this->image->getWidth() / 2 ) - ( $overlay->getWidth() / 2 ) );
|
557 |
+
break;
|
558 |
+
}
|
559 |
+
} else {
|
560 |
+
$x = $xPos;
|
561 |
+
}
|
562 |
+
|
563 |
+
if ( is_string( $yPos ) ) {
|
564 |
+
switch ( $yPos ) {
|
565 |
+
case 'top':
|
566 |
+
$y = 0;
|
567 |
+
break;
|
568 |
+
|
569 |
+
case 'bottom':
|
570 |
+
$y = $this->image->getHeight() - $overlay->getHeight();
|
571 |
+
break;
|
572 |
+
|
573 |
+
case 'center':
|
574 |
+
default:
|
575 |
+
$y = (int) round( ( $this->image->getHeight() / 2 ) - ( $overlay->getHeight() / 2 ) );
|
576 |
+
break;
|
577 |
+
}
|
578 |
+
} else {
|
579 |
+
$y = $yPos;
|
580 |
+
}
|
581 |
+
|
582 |
+
imagecopyresampled(
|
583 |
+
$this->image->getCore(), // Base image
|
584 |
+
$overlay->getCore(), // Overlay
|
585 |
+
(int) $x, // Overlay x position
|
586 |
+
(int) $y, // Overlay y position
|
587 |
+
0,
|
588 |
+
0,
|
589 |
+
$overlay->getWidth(), // Overlay final width
|
590 |
+
$overlay->getHeight(), // Overlay final height
|
591 |
+
$overlay->getWidth(), // Overlay source width
|
592 |
+
$overlay->getHeight() // Overlay source height
|
593 |
+
);
|
594 |
+
|
595 |
+
return $this;
|
596 |
+
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Creates a polygon.
|
601 |
+
*
|
602 |
+
* @param array $points Array of all X and Y positions. Must have at least three positions.
|
603 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
604 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
605 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
606 |
+
*
|
607 |
+
* @return EditorInterface An instance of image editor.
|
608 |
+
*/
|
609 |
+
public function polygon($points, $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF'){
|
610 |
+
if(is_string($borderColor)){
|
611 |
+
$borderColor = new Color($borderColor);
|
612 |
+
}
|
613 |
+
if(is_string($fillColor)){
|
614 |
+
$fillColor = new Color($fillColor);
|
615 |
+
}
|
616 |
+
$obj = new Polygon($points, $borderSize, $borderColor, $fillColor);
|
617 |
+
return $this->draw($obj);
|
618 |
+
}
|
619 |
+
|
620 |
+
/**
|
621 |
+
* Creates a rectangle.
|
622 |
+
* @param int $width Width of rectangle in pixels.
|
623 |
+
* @param int $height Height in pixels.
|
624 |
+
* @param array $pos Array of X and Y position. X is the distance in pixels from the left of the canvass to the left of the rectangle. Y is the distance from the top of the canvass to the top of the rectangle. Defaults to array(0,0).
|
625 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
626 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
627 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
628 |
+
* @return Editor
|
629 |
+
*/
|
630 |
+
public function rectangle($width, $height, $pos = array(0, 0), $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF') {
|
631 |
+
if(is_string($borderColor)){
|
632 |
+
$borderColor = new Color($borderColor);
|
633 |
+
}
|
634 |
+
if(is_string($fillColor)){
|
635 |
+
$fillColor = new Color($fillColor);
|
636 |
+
}
|
637 |
+
$obj = new Rectangle($width, $height, $pos, $borderSize, $borderColor, $fillColor);
|
638 |
+
return $this->draw($obj);
|
639 |
+
}
|
640 |
+
|
641 |
+
|
642 |
+
/**
|
643 |
+
* Wrapper function for the resizeXXX family of functions. Resize image given width, height and mode.
|
644 |
+
*
|
645 |
+
* @param int $newWidth Width in pixels.
|
646 |
+
* @param int $newHeight Height in pixels.
|
647 |
+
* @param string $mode Resize mode. Possible values: "exact", "exactHeight", "exactWidth", "fill", "fit".
|
648 |
+
*
|
649 |
+
* @return Editor
|
650 |
+
* @throws \Exception
|
651 |
+
*/
|
652 |
+
public function resize( $newWidth, $newHeight, $mode='fit' ){
|
653 |
+
/*
|
654 |
+
* Resize formula:
|
655 |
+
* ratio = w / h
|
656 |
+
* h = w / ratio
|
657 |
+
* w = h * ratio
|
658 |
+
*/
|
659 |
+
switch ($mode){
|
660 |
+
case 'exact':
|
661 |
+
$this->resizeExact( $newWidth, $newHeight );
|
662 |
+
break;
|
663 |
+
case 'fill':
|
664 |
+
$this->resizeFill($newWidth, $newHeight);
|
665 |
+
break;
|
666 |
+
case 'exactWidth':
|
667 |
+
$this->resizeExactWidth( $newWidth );
|
668 |
+
break;
|
669 |
+
case 'exactHeight':
|
670 |
+
$this->resizeExactHeight( $newHeight );
|
671 |
+
break;
|
672 |
+
case 'fit':
|
673 |
+
$this->resizeFit($newWidth, $newHeight);
|
674 |
+
break;
|
675 |
+
default:
|
676 |
+
throw new \Exception( sprintf('Invalid resize mode "%s".', $mode) );
|
677 |
+
}
|
678 |
+
|
679 |
+
return $this;
|
680 |
+
}
|
681 |
+
|
682 |
+
/**
|
683 |
+
* Resize image to exact dimensions ignoring aspect ratio. Useful if you want to force exact width and height.
|
684 |
+
*
|
685 |
+
* @param int $newWidth Width in pixels.
|
686 |
+
* @param int $newHeight Height in pixels.
|
687 |
+
*
|
688 |
+
* @return self
|
689 |
+
*/
|
690 |
+
public function resizeExact( $newWidth, $newHeight ){
|
691 |
+
|
692 |
+
$this->_resize( $newWidth, $newHeight );
|
693 |
+
|
694 |
+
return $this;
|
695 |
+
}
|
696 |
+
|
697 |
+
/**
|
698 |
+
* Resize image to exact height. Width is auto calculated. Useful for creating row of images with the same height.
|
699 |
+
*
|
700 |
+
* @param int $newHeight Height in pixels.
|
701 |
+
*
|
702 |
+
* @return self
|
703 |
+
*/
|
704 |
+
public function resizeExactHeight( $newHeight ){
|
705 |
+
|
706 |
+
$width = $this->image->getWidth();
|
707 |
+
$height = $this->image->getHeight();
|
708 |
+
$ratio = $width / $height;
|
709 |
+
|
710 |
+
$resizeHeight = $newHeight;
|
711 |
+
$resizeWidth = $newHeight * $ratio;
|
712 |
+
|
713 |
+
$this->_resize( $resizeWidth, $resizeHeight );
|
714 |
+
|
715 |
+
return $this;
|
716 |
+
}
|
717 |
+
|
718 |
+
/**
|
719 |
+
* Resize image to exact width. Height is auto calculated. Useful for creating column of images with the same width.
|
720 |
+
*
|
721 |
+
* @param int $newWidth Width in pixels.
|
722 |
+
*
|
723 |
+
* @return self
|
724 |
+
*/
|
725 |
+
public function resizeExactWidth( $newWidth ){
|
726 |
+
|
727 |
+
$width = $this->image->getWidth();
|
728 |
+
$height = $this->image->getHeight();
|
729 |
+
$ratio = $width / $height;
|
730 |
+
|
731 |
+
$resizeWidth = $newWidth;
|
732 |
+
$resizeHeight = round($newWidth / $ratio);
|
733 |
+
|
734 |
+
$this->_resize( $resizeWidth, $resizeHeight );
|
735 |
+
|
736 |
+
return $this;
|
737 |
+
}
|
738 |
+
|
739 |
+
/**
|
740 |
+
* Resize image to fill all the space in the given dimension. Excess parts are cropped.
|
741 |
+
*
|
742 |
+
* @param int $newWidth Width in pixels.
|
743 |
+
* @param int $newHeight Height in pixels.
|
744 |
+
*
|
745 |
+
* @return self
|
746 |
+
*/
|
747 |
+
public function resizeFill( $newWidth, $newHeight ){
|
748 |
+
$width = $this->image->getWidth();
|
749 |
+
$height = $this->image->getHeight();
|
750 |
+
$ratio = $width / $height;
|
751 |
+
|
752 |
+
// Base optimum size on new width
|
753 |
+
$optimumWidth = $newWidth;
|
754 |
+
$optimumHeight = round($newWidth / $ratio);
|
755 |
+
|
756 |
+
if( ($optimumWidth < $newWidth) or ($optimumHeight < $newHeight) ){ // Oops, where trying to fill and there are blank areas
|
757 |
+
// So base optimum size on height instead
|
758 |
+
$optimumWidth = $newHeight * $ratio;
|
759 |
+
$optimumHeight = $newHeight;
|
760 |
+
}
|
761 |
+
|
762 |
+
$this->_resize( $optimumWidth, $optimumHeight);
|
763 |
+
$this->crop( $newWidth, $newHeight ); // Trim excess parts
|
764 |
+
|
765 |
+
return $this;
|
766 |
+
}
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Resize image to fit inside the given dimension. No part of the image is lost.
|
770 |
+
*
|
771 |
+
* @param int $newWidth Width in pixels.
|
772 |
+
* @param int $newHeight Height in pixels.
|
773 |
+
*
|
774 |
+
* @return self
|
775 |
+
*/
|
776 |
+
public function resizeFit( $newWidth, $newHeight ){
|
777 |
+
|
778 |
+
$width = $this->image->getWidth();
|
779 |
+
$height = $this->image->getHeight();
|
780 |
+
$ratio = $width / $height;
|
781 |
+
|
782 |
+
// Try basing it on width first
|
783 |
+
$resizeWidth = $newWidth;
|
784 |
+
$resizeHeight = round($newWidth / $ratio);
|
785 |
+
|
786 |
+
if( ( $resizeWidth > $newWidth ) or ( $resizeHeight > $newHeight ) ){ // Oops, either with or height does not fit
|
787 |
+
// So base on height instead
|
788 |
+
$resizeHeight = $newHeight;
|
789 |
+
$resizeWidth = $newHeight * $ratio;
|
790 |
+
}
|
791 |
+
|
792 |
+
$this->_resize( $resizeWidth, $resizeHeight );
|
793 |
+
|
794 |
+
return $this;
|
795 |
+
}
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Rotate an image counter-clockwise.
|
799 |
+
*
|
800 |
+
* @param int $angle The angle in degrees.
|
801 |
+
* @param Color|null $color The Color object containing the background color.
|
802 |
+
*
|
803 |
+
* @return EditorInterface An instance of image editor.
|
804 |
+
*/
|
805 |
+
public function rotate( $angle, $color = null ){
|
806 |
+
|
807 |
+
$this->_imageCheck();
|
808 |
+
|
809 |
+
$color = ($color !== null) ? $color : new Color('#000000');
|
810 |
+
list( $r, $g, $b, $alpha ) = $color->getRgba();
|
811 |
+
|
812 |
+
imagerotate($this->image->getCore(), $angle, imagecolorallocatealpha( $this->image->getCore(), $r, $g, $b, $alpha ) );
|
813 |
+
|
814 |
+
return $this;
|
815 |
+
}
|
816 |
+
|
817 |
+
/**
|
818 |
+
* Save the image to an image format.
|
819 |
+
*
|
820 |
+
* @param string $file File path where to save the image.
|
821 |
+
* @param null|string $type Type of image. Can be null, "GIF", "PNG", or "JPEG".
|
822 |
+
* @param null|string $quality Quality of image. Applies to JPEG only. Accepts number 0 - 100 where 0 is lowest and 100 is the highest quality. Or null for default.
|
823 |
+
* @param bool|false $interlace Set to true for progressive JPEG. Applies to JPEG only.
|
824 |
+
* @param int $permission Default permission when creating non-existing target directory.
|
825 |
+
*
|
826 |
+
* @return Editor
|
827 |
+
* @throws \Exception
|
828 |
+
*/
|
829 |
+
public function save( $file, $type = null, $quality = null, $interlace = false, $permission = 0755 ){
|
830 |
+
|
831 |
+
$this->_imageCheck();
|
832 |
+
|
833 |
+
if ( null === $type ) {
|
834 |
+
|
835 |
+
$type = $this->_getImageTypeFromFileName( $file ); // Null given, guess type from file extension
|
836 |
+
if ( ImageType::UNKNOWN === $type ) {
|
837 |
+
$type = $this->image->getType(); // 0 result, use original image type
|
838 |
+
}
|
839 |
+
}
|
840 |
+
|
841 |
+
$targetDir = dirname( $file ); // $file's directory
|
842 |
+
if( false === is_dir( $targetDir ) ){ // Check if $file's directory exist
|
843 |
+
// Create and set default perms to 0755
|
844 |
+
if( !mkdir( $targetDir, $permission, true ) ){
|
845 |
+
throw new \Exception( sprintf('Cannot create %s', $targetDir) );
|
846 |
+
}
|
847 |
+
}
|
848 |
+
|
849 |
+
switch ( strtoupper($type) ) {
|
850 |
+
case ImageType::GIF :
|
851 |
+
imagegif( $this->image->getCore(), $file );
|
852 |
+
break;
|
853 |
+
|
854 |
+
case ImageType::PNG :
|
855 |
+
$quality = ( $quality === null ) ? 0 : $quality;
|
856 |
+
$quality = ( $quality > 9 ) ? 9 : $quality;
|
857 |
+
$quality = ( $quality < 0 ) ? 0 : $quality;
|
858 |
+
imagepng( $this->image->getCore(), $file, $quality );
|
859 |
+
break;
|
860 |
+
|
861 |
+
default: // Defaults to jpeg
|
862 |
+
$quality = ( $quality === null ) ? 100 : $quality;
|
863 |
+
$quality = ( $quality > 100 ) ? 100 : $quality;
|
864 |
+
$quality = ( $quality < 0 ) ? 0 : $quality;
|
865 |
+
imageinterlace( $this->image->getCore(), $interlace );
|
866 |
+
imagejpeg( $this->image->getCore(), $file, $quality );
|
867 |
+
}
|
868 |
+
|
869 |
+
return $this;
|
870 |
+
}
|
871 |
+
|
872 |
+
/**
|
873 |
+
* Set image instance.
|
874 |
+
*
|
875 |
+
* @param Image $image
|
876 |
+
*/
|
877 |
+
public function setImage( $image ) {
|
878 |
+
$this->image = $image;
|
879 |
+
}
|
880 |
+
|
881 |
+
/**
|
882 |
+
* Write text to image.
|
883 |
+
*
|
884 |
+
* @param string $text The text to be written.
|
885 |
+
* @param int $size The font size. Defaults to 12.
|
886 |
+
* @param int $x The distance from the left edge of the image to the left of the text. Defaults to 0.
|
887 |
+
* @param int $y The distance from the top edge of the image to the top of the text. Defaults to 12 (equal to font size) so that the text is placed within the image.
|
888 |
+
* @param Color $color The Color object. Default text color is black.
|
889 |
+
* @param string $font Full path to font file. If blank, will default to Liberation Sans font.
|
890 |
+
* @param int $angle Angle of text from 0 - 359. Defaults to 0.
|
891 |
+
*
|
892 |
+
* @return EditorInterface
|
893 |
+
* @throws \Exception
|
894 |
+
*/
|
895 |
+
public function text( $text, $size = 12, $x = 0, $y = 0, $color = null, $font = '', $angle = 0 ) {
|
896 |
+
|
897 |
+
$this->_imageCheck();
|
898 |
+
|
899 |
+
$y += $size;
|
900 |
+
|
901 |
+
$color = ($color !== null) ? $color : new Color('#000000');
|
902 |
+
$font = ($font !== '') ? $font : Grafika::fontsDir().DIRECTORY_SEPARATOR.'LiberationSans-Regular.ttf';
|
903 |
+
|
904 |
+
list( $r, $g, $b, $alpha ) = $color->getRgba();
|
905 |
+
|
906 |
+
$colorResource = imagecolorallocatealpha(
|
907 |
+
$this->image->getCore(),
|
908 |
+
$r, $g, $b,
|
909 |
+
$this->gdAlpha( $alpha )
|
910 |
+
);
|
911 |
+
|
912 |
+
imagettftext(
|
913 |
+
$this->image->getCore(),
|
914 |
+
$size,
|
915 |
+
$angle,
|
916 |
+
$x,
|
917 |
+
$y,
|
918 |
+
$colorResource,
|
919 |
+
$font,
|
920 |
+
$text
|
921 |
+
);
|
922 |
+
|
923 |
+
return $this;
|
924 |
+
}
|
925 |
+
|
926 |
+
/**
|
927 |
+
* Get difference hash of image.
|
928 |
+
* Algorithm:
|
929 |
+
* Reduce size. The fastest way to remove high frequencies and detail is to shrink the image. In this case, shrink it to 9x8 so that there are 72 total pixels.
|
930 |
+
* Reduce color. Convert the image to a grayscale picture. This changes the hash from 72 pixels to a total of 72 colors.
|
931 |
+
* Compute the difference. The algorithm works on the difference between adjacent pixels. This identifies the relative gradient direction. In this case, the 9 pixels per row yields 8 differences between adjacent pixels. Eight rows of eight differences becomes 64 bits.
|
932 |
+
* Assign bits. Each bit is simply set based on whether the left pixel is brighter than the right pixel.
|
933 |
+
*
|
934 |
+
* http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
|
935 |
+
* @param Image $image
|
936 |
+
* @return string
|
937 |
+
*/
|
938 |
+
private function _differenceHash($image)
|
939 |
+
{
|
940 |
+
|
941 |
+
$width = 9;
|
942 |
+
$height = 8;
|
943 |
+
|
944 |
+
$editor = new Editor();
|
945 |
+
$editor->setImage($image);
|
946 |
+
$editor->resizeExact( $width, $height); // Resize to exactly 9x8
|
947 |
+
$gd = $editor->getImage()->getCore();
|
948 |
+
|
949 |
+
// Build hash
|
950 |
+
$hash = '';
|
951 |
+
for ($y = 0; $y < $height; $y++) {
|
952 |
+
// Get the pixel value for the leftmost pixel.
|
953 |
+
$rgba = imagecolorat($gd, 0, $y);
|
954 |
+
$r = ($rgba >> 16) & 0xFF;
|
955 |
+
$g = ($rgba >> 8) & 0xFF;
|
956 |
+
$b = $rgba & 0xFF;
|
957 |
+
|
958 |
+
$left = floor(($r + $g + $b) / 3);
|
959 |
+
for ($x = 1; $x < $width; $x++) {
|
960 |
+
// Get the pixel value for each pixel starting from position 1.
|
961 |
+
$rgba = imagecolorat($gd, $x, $y);
|
962 |
+
$r = ($rgba >> 16) & 0xFF;
|
963 |
+
$g = ($rgba >> 8) & 0xFF;
|
964 |
+
$b = $rgba & 0xFF;
|
965 |
+
$right = floor(($r + $g + $b) / 3);
|
966 |
+
// Each hash bit is set based on whether the left pixel is brighter than the right pixel.
|
967 |
+
if ($left > $right) {
|
968 |
+
$hash .= '1';
|
969 |
+
} else {
|
970 |
+
$hash .= '0';
|
971 |
+
}
|
972 |
+
// Prepare the next loop.
|
973 |
+
$left = $right;
|
974 |
+
}
|
975 |
+
}
|
976 |
+
|
977 |
+
return $hash;
|
978 |
+
}
|
979 |
+
|
980 |
+
// TODO: Maybe detach hashes from editor and move to their own classes
|
981 |
+
// private function _averageHash($image)
|
982 |
+
// {
|
983 |
+
// // Resize the image.
|
984 |
+
// $width = 8;
|
985 |
+
// $height = 8;
|
986 |
+
//
|
987 |
+
// $editor = new Editor();
|
988 |
+
// $editor->setImage($image);
|
989 |
+
// $editor->resizeExact( $width, $height); // Resize to exactly 9x8
|
990 |
+
// $gd = $editor->getImage()->getCore();
|
991 |
+
//
|
992 |
+
// // Create an array of greyscale pixel values.
|
993 |
+
// $pixels = array();
|
994 |
+
// for ($y = 0; $y < $height; $y++) {
|
995 |
+
// for ($x = 0; $x < $width; $x++) {
|
996 |
+
// $rgba = imagecolorat($gd, $x, $y);
|
997 |
+
// $r = ($rgba >> 16) & 0xFF;
|
998 |
+
// $g = ($rgba >> 8) & 0xFF;
|
999 |
+
// $b = $rgba & 0xFF;
|
1000 |
+
//
|
1001 |
+
// $pixels[] = floor(($r + $g + $b) / 3); // Gray
|
1002 |
+
// }
|
1003 |
+
// }
|
1004 |
+
//
|
1005 |
+
// // Get the average pixel value.
|
1006 |
+
// $average = floor(array_sum($pixels) / count($pixels));
|
1007 |
+
// // Each hash bit is set based on whether the current pixels value is above or below the average.
|
1008 |
+
// $hash = '';
|
1009 |
+
// foreach ($pixels as $pixel) {
|
1010 |
+
// if ($pixel > $average) {
|
1011 |
+
// $hash .= '1';
|
1012 |
+
// } else {
|
1013 |
+
// $hash .= '0';
|
1014 |
+
// }
|
1015 |
+
// }
|
1016 |
+
// return $hash;
|
1017 |
+
// }
|
1018 |
+
|
1019 |
+
/**
|
1020 |
+
* Resize helper function.
|
1021 |
+
*
|
1022 |
+
* @param int $newWidth
|
1023 |
+
* @param int $newHeight
|
1024 |
+
* @param int $targetX
|
1025 |
+
* @param int $targetY
|
1026 |
+
* @param int $srcX
|
1027 |
+
* @param int $srcY
|
1028 |
+
*
|
1029 |
+
* @throws \Exception
|
1030 |
+
*/
|
1031 |
+
private function _resize( $newWidth, $newHeight, $targetX=0, $targetY=0, $srcX=0, $srcY=0 ){
|
1032 |
+
|
1033 |
+
$this->_imageCheck();
|
1034 |
+
|
1035 |
+
// Create blank image
|
1036 |
+
$newImage = Image::createBlank( $newWidth, $newHeight );
|
1037 |
+
|
1038 |
+
if( ImageType::PNG === $this->image->getType() ){
|
1039 |
+
// Preserve PNG transparency
|
1040 |
+
$newImage->fullAlphaMode( true );
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
imagecopyresampled(
|
1044 |
+
$newImage->getCore(),
|
1045 |
+
$this->image->getCore(),
|
1046 |
+
$targetX,
|
1047 |
+
$targetY,
|
1048 |
+
$srcX,
|
1049 |
+
$srcY,
|
1050 |
+
$newWidth,
|
1051 |
+
$newHeight,
|
1052 |
+
$this->image->getWidth(),
|
1053 |
+
$this->image->getHeight()
|
1054 |
+
);
|
1055 |
+
|
1056 |
+
// Free memory of old resource
|
1057 |
+
imagedestroy( $this->image->getCore() );
|
1058 |
+
|
1059 |
+
// Resize image instance
|
1060 |
+
$this->image = new Image(
|
1061 |
+
$newImage->getCore(),
|
1062 |
+
$this->image->getImageFile(),
|
1063 |
+
$newWidth,
|
1064 |
+
$newHeight,
|
1065 |
+
$this->image->getType()
|
1066 |
+
);
|
1067 |
+
|
1068 |
+
}
|
1069 |
+
|
1070 |
+
/**
|
1071 |
+
* Convert alpha value of 0 - 1 to GD compatible alpha value of 0 - 127 where 0 is opaque and 127 is transparent
|
1072 |
+
*
|
1073 |
+
* @param float $alpha Alpha value of 0 - 1. Example: 0, 0.60, 0.9, 1
|
1074 |
+
*
|
1075 |
+
* @return int
|
1076 |
+
*/
|
1077 |
+
public static function gdAlpha( $alpha ) {
|
1078 |
+
|
1079 |
+
$scale = round( 127 * $alpha );
|
1080 |
+
|
1081 |
+
return $invert = 127 - $scale;
|
1082 |
+
}
|
1083 |
+
|
1084 |
+
/**
|
1085 |
+
* Get image type base on file extension.
|
1086 |
+
*
|
1087 |
+
* @param int $imageFile File path to image.
|
1088 |
+
*
|
1089 |
+
* @return ImageType string Type of image.
|
1090 |
+
*/
|
1091 |
+
private function _getImageTypeFromFileName( $imageFile ) {
|
1092 |
+
$ext = strtolower( (string) pathinfo( $imageFile, PATHINFO_EXTENSION ) );
|
1093 |
+
|
1094 |
+
if ( 'jpg' == $ext or 'jpeg' == $ext ) {
|
1095 |
+
return ImageType::JPEG;
|
1096 |
+
} else if ( 'gif' == $ext ) {
|
1097 |
+
return ImageType::GIF;
|
1098 |
+
} else if ( 'png' == $ext ) {
|
1099 |
+
return ImageType::PNG;
|
1100 |
+
} else {
|
1101 |
+
return ImageType::UNKNOWN;
|
1102 |
+
}
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
/**
|
1106 |
+
* Check if editor has already been assigned an image.
|
1107 |
+
*
|
1108 |
+
* @throws \Exception
|
1109 |
+
*/
|
1110 |
+
private function _imageCheck() {
|
1111 |
+
if ( null === $this->image ) {
|
1112 |
+
throw new \Exception( 'No image to edit.' );
|
1113 |
+
}
|
1114 |
+
}
|
1115 |
+
}
|
src/CycloneSlider/Grafika/Gd/Effect/Dither.php
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace CycloneSlider\Grafika\Gd\Effect;
|
4 |
+
|
5 |
+
use CycloneSlider\Grafika\EffectInterface;
|
6 |
+
use CycloneSlider\Grafika\Gd\Image;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Dither image using Floyd-Steinberg algorithm. Dithering will reduce the color to black and white and add noise.
|
10 |
+
*/
|
11 |
+
class Dither implements EffectInterface{
|
12 |
+
/**
|
13 |
+
* Dither an image.
|
14 |
+
*/
|
15 |
+
public function __construct()
|
16 |
+
{
|
17 |
+
}
|
18 |
+
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @param Image $image
|
22 |
+
*
|
23 |
+
* @return Image
|
24 |
+
*/
|
25 |
+
public function apply( $image ) {
|
26 |
+
return $this->floydSteinberg( $image );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @param Image $image
|
31 |
+
*
|
32 |
+
* @return Image
|
33 |
+
*/
|
34 |
+
private function floydSteinberg( $image ){
|
35 |
+
$pixel = array();
|
36 |
+
|
37 |
+
// Localize vars
|
38 |
+
$width = $image->getWidth();
|
39 |
+
$height = $image->getHeight();
|
40 |
+
$gd = $image->getCore();
|
41 |
+
|
42 |
+
for ( $y = 0; $y < $height; $y+=1 ) {
|
43 |
+
for ( $x = 0; $x < $width; $x+=1 ) {
|
44 |
+
|
45 |
+
$color = imagecolorat( $gd, $x, $y );
|
46 |
+
$r = ($color >> 16) & 0xFF;
|
47 |
+
$g = ($color >> 8) & 0xFF;
|
48 |
+
$b = $color & 0xFF;
|
49 |
+
|
50 |
+
$gray = round($r * 0.3 + $g * 0.59 + $b * 0.11);
|
51 |
+
|
52 |
+
if(isset($pixel[$x][$y])){ // Add errors to color if there are
|
53 |
+
$gray += $pixel[$x][$y];
|
54 |
+
}
|
55 |
+
|
56 |
+
if ( $gray <= 127 ) { // Determine if black or white. Also has the benefit of clipping excess val due to adding the error
|
57 |
+
$blackOrWhite = 0;
|
58 |
+
} else {
|
59 |
+
$blackOrWhite = 255;
|
60 |
+
}
|
61 |
+
|
62 |
+
$oldPixel = $gray;
|
63 |
+
$newPixel = $blackOrWhite;
|
64 |
+
|
65 |
+
// Current pixel
|
66 |
+
imagesetpixel( $gd, $x, $y,
|
67 |
+
imagecolorallocate( $gd,
|
68 |
+
$newPixel,
|
69 |
+
$newPixel,
|
70 |
+
$newPixel
|
71 |
+
)
|
72 |
+
);
|
73 |
+
|
74 |
+
$qError = $oldPixel - $newPixel; // Quantization error
|
75 |
+
|
76 |
+
// Propagate error on neighbor pixels
|
77 |
+
if ( $x + 1 < $width ) {
|
78 |
+
$pixel[$x+1][$y] = (isset($pixel[$x+1][$y]) ? $pixel[$x+1][$y] : 0) + ($qError * (7 / 16));
|
79 |
+
}
|
80 |
+
|
81 |
+
if ( $x - 1 > 0 and $y + 1 < $height ) {
|
82 |
+
$pixel[$x-1][$y+1] = (isset($pixel[$x-1][$y+1]) ? $pixel[$x-1][$y+1] : 0) + ($qError * (3 / 16));
|
83 |
+
}
|
84 |
+
|
85 |
+
if ( $y + 1 < $height ) {
|
86 |
+
$pixel[$x][$y+1] = (isset($pixel[$x][$y+1]) ? $pixel[$x][$y+1] : 0) + ($qError * (5 / 16));
|
87 |
+
}
|
88 |
+
|
89 |
+
if ( $x + 1 < $width and $y + 1 < $height ) {
|
90 |
+
$pixel[$x+1][$y+1] = (isset($pixel[$x+1][$y+1]) ? $pixel[$x+1][$y+1] : 0) + ($qError * (1 / 16));
|
91 |
+
}
|
92 |
+
|
93 |
+
}
|
94 |
+
}
|
95 |
+
$type = $image->getType();
|
96 |
+
$file = $image->getImageFile();
|
97 |
+
|
98 |
+
return new Image( $gd, $file, $width, $height, $type ); // Create new image with updated core
|
99 |
+
}
|
100 |
+
}
|
src/CycloneSlider/Grafika/Gd/Image.php
ADDED
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Gd;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\ImageType;
|
5 |
+
use CycloneSlider\Grafika\ImageInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Immutable image class for GD.
|
9 |
+
* @package Grafika\Gd
|
10 |
+
*/
|
11 |
+
final class Image implements ImageInterface {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var resource GD resource ID.
|
15 |
+
*/
|
16 |
+
private $gd;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var string File path to image.
|
20 |
+
*/
|
21 |
+
private $imageFile;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var int Image width in pixels.
|
25 |
+
*/
|
26 |
+
private $width;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var int Image height in pixels.
|
30 |
+
*/
|
31 |
+
private $height;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var string Image type. See \Grafika\ImageType
|
35 |
+
*/
|
36 |
+
private $type;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Image constructor.
|
40 |
+
*
|
41 |
+
* @param resource $gd Must use GD's imagecreate* family of functions to create a GD resource.
|
42 |
+
* @param string $imageFile
|
43 |
+
* @param int $width
|
44 |
+
* @param int $height
|
45 |
+
* @param string $type
|
46 |
+
*/
|
47 |
+
public function __construct( $gd, $imageFile, $width, $height, $type ) {
|
48 |
+
$this->gd = $gd;
|
49 |
+
$this->imageFile = $imageFile;
|
50 |
+
$this->width = $width;
|
51 |
+
$this->height = $height;
|
52 |
+
$this->type = $type;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* @param $imageFile
|
57 |
+
*
|
58 |
+
* @return Image
|
59 |
+
* @throws \Exception
|
60 |
+
*/
|
61 |
+
public static function createFromFile( $imageFile ){
|
62 |
+
if ( ! file_exists( $imageFile ) ) {
|
63 |
+
throw new \Exception( sprintf('Could not open "%s". File does not exist.', $imageFile) );
|
64 |
+
}
|
65 |
+
|
66 |
+
$type = self::_guessType($imageFile);
|
67 |
+
if ( ImageType::GIF == $type) {
|
68 |
+
|
69 |
+
return self::createGif($imageFile);
|
70 |
+
|
71 |
+
} else if ( ImageType::JPEG == $type) {
|
72 |
+
|
73 |
+
return self::createJpeg($imageFile);
|
74 |
+
|
75 |
+
} else if ( ImageType::PNG == $type) {
|
76 |
+
|
77 |
+
return self::createPng($imageFile);
|
78 |
+
|
79 |
+
} else if ( ImageType::WBMP == $type) {
|
80 |
+
|
81 |
+
return self::createWbmp($imageFile);
|
82 |
+
|
83 |
+
} else {
|
84 |
+
throw new \Exception( sprintf('Could not open "%s". File type not supported.', $imageFile) );
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Load a JPEG image.
|
90 |
+
*
|
91 |
+
* @param string $imageFile File path to image.
|
92 |
+
*
|
93 |
+
* @return Image
|
94 |
+
* @throws \Exception
|
95 |
+
*/
|
96 |
+
public static function createJpeg( $imageFile ){
|
97 |
+
$gd = @imagecreatefromjpeg( $imageFile );
|
98 |
+
|
99 |
+
if(!$gd){
|
100 |
+
throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::JPEG ) );
|
101 |
+
}
|
102 |
+
|
103 |
+
return new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::JPEG );
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Load a PNG image.
|
108 |
+
*
|
109 |
+
* @param string $imageFile File path to image.
|
110 |
+
*
|
111 |
+
* @return Image
|
112 |
+
* @throws \Exception
|
113 |
+
*/
|
114 |
+
public static function createPng( $imageFile ){
|
115 |
+
$gd = @imagecreatefrompng( $imageFile );
|
116 |
+
|
117 |
+
if(!$gd){
|
118 |
+
throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::PNG) );
|
119 |
+
}
|
120 |
+
|
121 |
+
$image = new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::PNG );
|
122 |
+
$image->fullAlphaMode( true );
|
123 |
+
return $image;
|
124 |
+
}
|
125 |
+
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Load a GIF image.
|
129 |
+
*
|
130 |
+
* @param string $imageFile
|
131 |
+
*
|
132 |
+
* @return Image
|
133 |
+
* @throws \Exception
|
134 |
+
*/
|
135 |
+
public static function createGif( $imageFile ){
|
136 |
+
$gd = @imagecreatefromgif( $imageFile );
|
137 |
+
|
138 |
+
if(!$gd){
|
139 |
+
throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::GIF) );
|
140 |
+
}
|
141 |
+
|
142 |
+
return new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::GIF );
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Load a WBMP image.
|
147 |
+
*
|
148 |
+
* @param string $imageFile
|
149 |
+
*
|
150 |
+
* @return Image
|
151 |
+
* @throws \Exception
|
152 |
+
*/
|
153 |
+
public static function createWbmp( $imageFile ){
|
154 |
+
$gd = @imagecreatefromwbmp( $imageFile );
|
155 |
+
|
156 |
+
if(!$gd){
|
157 |
+
throw new \Exception( sprintf('Could not open "%s". Not a valid %s file.', $imageFile, ImageType::WBMP) );
|
158 |
+
}
|
159 |
+
|
160 |
+
return new self( $gd, $imageFile, imagesx( $gd ), imagesy( $gd ), ImageType::WBMP );
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Create a blank image.
|
165 |
+
*
|
166 |
+
* @param int $width Width in pixels.
|
167 |
+
* @param int $height Height in pixels.
|
168 |
+
*
|
169 |
+
* @return Image
|
170 |
+
*/
|
171 |
+
public static function createBlank($width = 1, $height = 1){
|
172 |
+
|
173 |
+
return new self(imagecreatetruecolor($width, $height), '', $width, $height, ImageType::UNKNOWN);
|
174 |
+
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Set the blending mode for an image. Allows transparent overlays on top of an image.
|
179 |
+
*
|
180 |
+
* @param bool $flag True to enable blending mode.
|
181 |
+
* @return self
|
182 |
+
*/
|
183 |
+
public function alphaBlendingMode( $flag ){
|
184 |
+
imagealphablending( $this->gd, $flag );
|
185 |
+
|
186 |
+
return $this;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Enable/Disable transparency
|
191 |
+
*
|
192 |
+
* @param bool $flag True to enable alpha mode.
|
193 |
+
* @return self
|
194 |
+
*/
|
195 |
+
public function fullAlphaMode( $flag ){
|
196 |
+
if( true === $flag ){
|
197 |
+
$this->alphaBlendingMode( false ); // Must be false for full alpha mode to work
|
198 |
+
}
|
199 |
+
imagesavealpha( $this->gd, $flag );
|
200 |
+
|
201 |
+
return $this;
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Get GD resource ID.
|
206 |
+
*
|
207 |
+
* @return resource
|
208 |
+
*/
|
209 |
+
public function getCore() {
|
210 |
+
return $this->gd;
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Get image file path.
|
215 |
+
*
|
216 |
+
* @return string File path to image.
|
217 |
+
*/
|
218 |
+
public function getImageFile() {
|
219 |
+
return $this->imageFile;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Get image width in pixels.
|
224 |
+
*
|
225 |
+
* @return int
|
226 |
+
*/
|
227 |
+
public function getWidth() {
|
228 |
+
return $this->width;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Get image height in pixels.
|
233 |
+
*
|
234 |
+
* @return int
|
235 |
+
*/
|
236 |
+
public function getHeight() {
|
237 |
+
return $this->height;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Get image type.
|
242 |
+
*
|
243 |
+
* @return string
|
244 |
+
*/
|
245 |
+
public function getType() {
|
246 |
+
return $this->type;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* @param $imageFile
|
251 |
+
*
|
252 |
+
* @return string
|
253 |
+
*/
|
254 |
+
private static function _guessType( $imageFile ){
|
255 |
+
// Values from http://php.net/manual/en/image.constants.php starting with IMAGETYPE_GIF.
|
256 |
+
// 0 - unknown,
|
257 |
+
// 1 - GIF,
|
258 |
+
// 2 - JPEG,
|
259 |
+
// 3 - PNG
|
260 |
+
// 15 - WBMP
|
261 |
+
list($width, $height, $type) = getimagesize( $imageFile );
|
262 |
+
|
263 |
+
unset($width, $height);
|
264 |
+
|
265 |
+
if ( 1 == $type) {
|
266 |
+
|
267 |
+
return ImageType::GIF;
|
268 |
+
|
269 |
+
} else if ( 2 == $type) {
|
270 |
+
|
271 |
+
return ImageType::JPEG;
|
272 |
+
|
273 |
+
} else if ( 3 == $type) {
|
274 |
+
|
275 |
+
return ImageType::PNG;
|
276 |
+
|
277 |
+
} else if ( 15 == $type) {
|
278 |
+
|
279 |
+
return ImageType::WBMP;
|
280 |
+
|
281 |
+
}
|
282 |
+
|
283 |
+
return ImageType::UNKNOWN;
|
284 |
+
}
|
285 |
+
}
|
src/CycloneSlider/Grafika/Grafika.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace CycloneSlider\Grafika;
|
4 |
+
|
5 |
+
use CycloneSlider\Grafika\Gd\Editor as GdEditor;
|
6 |
+
use CycloneSlider\Grafika\Gd\Image as GdImage;
|
7 |
+
use CycloneSlider\Grafika\Imagick\Editor as ImagickEditor;
|
8 |
+
use CycloneSlider\Grafika\Imagick\Image as ImagickImage;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Contains factory methods for detecting editors, creating editors and images.
|
12 |
+
* @package Grafika
|
13 |
+
*/
|
14 |
+
class Grafika
|
15 |
+
{
|
16 |
+
|
17 |
+
const DIR = __DIR__; // Grafika directory
|
18 |
+
|
19 |
+
public static function fontsDir()
|
20 |
+
{
|
21 |
+
$ds = DIRECTORY_SEPARATOR;
|
22 |
+
return realpath(self::DIR . $ds . '..' . $ds . '..') . $ds . 'fonts';
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @param array $editorList Array of editor list names. Use this to change the order of evaluation for editors. Default order of evaluation is Imagick then GD.
|
27 |
+
*
|
28 |
+
* @return string Name of available editor.
|
29 |
+
* @throws \Exception Throws exception if there are no supported editors.
|
30 |
+
*/
|
31 |
+
public static function detectAvailableEditor($editorList = array('Imagick', 'Gd'))
|
32 |
+
{
|
33 |
+
|
34 |
+
/* Get first supported editor instance. Order of editorList matter. */
|
35 |
+
foreach ($editorList as $editorName) {
|
36 |
+
if ('Imagick' === $editorName) {
|
37 |
+
$editorInstance = new ImagickEditor();
|
38 |
+
} else {
|
39 |
+
$editorInstance = new GdEditor();
|
40 |
+
}
|
41 |
+
/** @var EditorInterface $editorInstance */
|
42 |
+
if (true === $editorInstance->isAvailable()) {
|
43 |
+
return $editorName;
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
throw new \Exception('No supported editor.');
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Creates the first available editor.
|
52 |
+
*
|
53 |
+
* @param array $editorList Array of editor list names. Use this to change the order of evaluation for editors. Default order of evaluation is Imagick then GD.
|
54 |
+
*
|
55 |
+
* @return EditorInterface
|
56 |
+
* @throws \Exception
|
57 |
+
*/
|
58 |
+
public static function createEditor($editorList = array('Imagick', 'Gd'))
|
59 |
+
{
|
60 |
+
$editorName = self::detectAvailableEditor($editorList);
|
61 |
+
if ('Imagick' === $editorName) {
|
62 |
+
return new ImagickEditor();
|
63 |
+
} else {
|
64 |
+
return new GdEditor();
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Create an image.
|
70 |
+
* @param string $imageFile Path to image file.
|
71 |
+
*
|
72 |
+
* @return ImageInterface
|
73 |
+
* @throws \Exception
|
74 |
+
*/
|
75 |
+
public static function createImage($imageFile)
|
76 |
+
{
|
77 |
+
$editorName = self::detectAvailableEditor();
|
78 |
+
if ('Imagick' === $editorName) {
|
79 |
+
return ImagickImage::createFromFile($imageFile);
|
80 |
+
} else {
|
81 |
+
return GdImage::createFromFile($imageFile);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Create a blank image.
|
88 |
+
*
|
89 |
+
* @param int $width Width of image in pixels.
|
90 |
+
* @param int $height Height of image in pixels.
|
91 |
+
*
|
92 |
+
* @return ImageInterface
|
93 |
+
* @throws \Exception
|
94 |
+
*/
|
95 |
+
public static function createBlankImage($width = 1, $height = 1)
|
96 |
+
{
|
97 |
+
$editorName = self::detectAvailableEditor();
|
98 |
+
if ('Imagick' === $editorName) {
|
99 |
+
return ImagickImage::createBlank($width, $height);
|
100 |
+
} else {
|
101 |
+
return GdImage::createBlank($width, $height);
|
102 |
+
}
|
103 |
+
}
|
104 |
+
|
105 |
+
}
|
src/CycloneSlider/Grafika/ImageInterface.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Interface ImageInterface
|
6 |
+
* @package Grafika
|
7 |
+
*/
|
8 |
+
interface ImageInterface {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @param $imageFile
|
12 |
+
*
|
13 |
+
* @return mixed
|
14 |
+
*/
|
15 |
+
public static function createFromFile( $imageFile );
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @param int $width
|
19 |
+
* @param int $height
|
20 |
+
*
|
21 |
+
* @return mixed
|
22 |
+
*/
|
23 |
+
public static function createBlank($width = 1, $height = 1);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @return mixed
|
27 |
+
*/
|
28 |
+
public function getCore();
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @return mixed
|
32 |
+
*/
|
33 |
+
public function getImageFile();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @return mixed
|
37 |
+
*/
|
38 |
+
public function getWidth();
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @return mixed
|
42 |
+
*/
|
43 |
+
public function getHeight();
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @return mixed
|
47 |
+
*/
|
48 |
+
public function getType();
|
49 |
+
}
|
src/CycloneSlider/Grafika/ImageType.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace CycloneSlider\Grafika;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class ImageType. Represent the different image types for GD and Imagick consistently.
|
7 |
+
*
|
8 |
+
* @package Grafika
|
9 |
+
*/
|
10 |
+
class ImageType {
|
11 |
+
|
12 |
+
const UNKNOWN = '';
|
13 |
+
|
14 |
+
const GIF = 'GIF';
|
15 |
+
|
16 |
+
const JPEG = 'JPEG';
|
17 |
+
|
18 |
+
const PNG = 'PNG';
|
19 |
+
|
20 |
+
const WBMP = 'WBMP';
|
21 |
+
}
|
src/CycloneSlider/Grafika/Imagick/DrawingObject/CubicBezier.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\CubicBezier as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Imagick\Image;
|
7 |
+
use CycloneSlider\Grafika\ImageInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class CubicBezier
|
11 |
+
* @package Grafika
|
12 |
+
*/
|
13 |
+
class CubicBezier extends Base implements DrawingObjectInterface
|
14 |
+
{
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param ImageInterface $image
|
18 |
+
* @return Image
|
19 |
+
*/
|
20 |
+
public function draw($image)
|
21 |
+
{
|
22 |
+
// Localize vars
|
23 |
+
$width = $image->getWidth();
|
24 |
+
$height = $image->getHeight();
|
25 |
+
$imagick = $image->getCore();
|
26 |
+
|
27 |
+
$draw = new \ImagickDraw();
|
28 |
+
|
29 |
+
$strokeColor = new \ImagickPixel($this->getColor()->getHexString());
|
30 |
+
$fillColor = new \ImagickPixel('rgba(0,0,0,0)');
|
31 |
+
|
32 |
+
$draw->setStrokeOpacity(1);
|
33 |
+
$draw->setStrokeColor($strokeColor);
|
34 |
+
$draw->setFillColor($fillColor);
|
35 |
+
|
36 |
+
$points = array(
|
37 |
+
array('x'=> $this->point1[0], 'y'=> $this->point1[1]),
|
38 |
+
array('x'=> $this->control1[0], 'y'=> $this->control1[1]),
|
39 |
+
array('x'=> $this->control2[0], 'y'=> $this->control2[1]),
|
40 |
+
array('x'=> $this->point2[0], 'y'=> $this->point2[1]),
|
41 |
+
);
|
42 |
+
$draw->bezier($points);
|
43 |
+
|
44 |
+
// Render the draw commands in the ImagickDraw object
|
45 |
+
$imagick->drawImage($draw);
|
46 |
+
|
47 |
+
$type = $image->getType();
|
48 |
+
$file = $image->getImageFile();
|
49 |
+
return new Image($imagick, $file, $width, $height, $type); // Create new image with updated core
|
50 |
+
}
|
51 |
+
}
|
src/CycloneSlider/Grafika/Imagick/DrawingObject/Ellipse.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Ellipse as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\ImageInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Ellipse
|
10 |
+
* @package Grafika
|
11 |
+
*/
|
12 |
+
class Ellipse extends Base implements DrawingObjectInterface
|
13 |
+
{
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param ImageInterface $image
|
17 |
+
* @return ImageInterface
|
18 |
+
*/
|
19 |
+
public function draw($image)
|
20 |
+
{
|
21 |
+
|
22 |
+
$strokeColor = new \ImagickPixel($this->getBorderColor()->getHexString());
|
23 |
+
$fillColor = new \ImagickPixel($this->getFillColor()->getHexString());
|
24 |
+
|
25 |
+
$draw = new \ImagickDraw();
|
26 |
+
$draw->setStrokeColor($strokeColor);
|
27 |
+
$draw->setFillColor($fillColor);
|
28 |
+
|
29 |
+
$draw->setStrokeWidth($this->borderSize);
|
30 |
+
|
31 |
+
list($x, $y) = $this->pos;
|
32 |
+
$left = $x + $this->width / 2;
|
33 |
+
$top = $y + $this->height / 2;
|
34 |
+
$draw->ellipse($left, $top, $this->width/2, $this->height/2, 0, 360);
|
35 |
+
|
36 |
+
$image->getCore()->drawImage($draw);
|
37 |
+
|
38 |
+
return $image;
|
39 |
+
}
|
40 |
+
}
|
src/CycloneSlider/Grafika/Imagick/DrawingObject/Line.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Line as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Imagick\Image;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Line
|
10 |
+
* @package Grafika
|
11 |
+
*/
|
12 |
+
class Line extends Base implements DrawingObjectInterface
|
13 |
+
{
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param Image $image
|
17 |
+
*
|
18 |
+
* @return Image
|
19 |
+
*/
|
20 |
+
public function draw($image)
|
21 |
+
{
|
22 |
+
|
23 |
+
$strokeColor = new \ImagickPixel($this->getColor()->getHexString());
|
24 |
+
|
25 |
+
$draw = new \ImagickDraw();
|
26 |
+
|
27 |
+
$draw->setStrokeColor($strokeColor);
|
28 |
+
|
29 |
+
$draw->setStrokeWidth($this->thickness);
|
30 |
+
|
31 |
+
list($x1, $y1) = $this->point1;
|
32 |
+
list($x2, $y2) = $this->point2;
|
33 |
+
$draw->line($x1, $y1, $x2, $y2);
|
34 |
+
|
35 |
+
$image->getCore()->drawImage($draw);
|
36 |
+
|
37 |
+
return $image;
|
38 |
+
}
|
39 |
+
|
40 |
+
|
41 |
+
}
|
src/CycloneSlider/Grafika/Imagick/DrawingObject/Polygon.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Polygon as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Polygon
|
9 |
+
* @package Grafika
|
10 |
+
*/
|
11 |
+
class Polygon extends Base implements DrawingObjectInterface{
|
12 |
+
|
13 |
+
public function draw( $image ) {
|
14 |
+
$strokeColor = new \ImagickPixel($this->getBorderColor()->getHexString());
|
15 |
+
if( null !== $this->fillColor) {
|
16 |
+
$fillColor = new \ImagickPixel($this->getFillColor()->getHexString());
|
17 |
+
} else {
|
18 |
+
$fillColor = new \ImagickPixel('rgba(0,0,0,0)');
|
19 |
+
}
|
20 |
+
|
21 |
+
$draw = new \ImagickDraw();
|
22 |
+
$draw->setStrokeOpacity(1);
|
23 |
+
$draw->setStrokeColor($strokeColor);
|
24 |
+
$draw->setFillColor($fillColor);
|
25 |
+
$draw->setStrokeWidth($this->borderSize);
|
26 |
+
|
27 |
+
$draw->polygon($this->points());
|
28 |
+
|
29 |
+
$image->getCore()->drawImage($draw);
|
30 |
+
|
31 |
+
return $image;
|
32 |
+
}
|
33 |
+
|
34 |
+
protected function points(){
|
35 |
+
$points = array();
|
36 |
+
foreach($this->points as $i=>$pos){
|
37 |
+
$points[$i] = array(
|
38 |
+
'x' => $pos[0],
|
39 |
+
'y' => $pos[1]
|
40 |
+
);
|
41 |
+
}
|
42 |
+
if( count($points) < 3 ){
|
43 |
+
throw new \Exception('Polygon needs at least 3 points.');
|
44 |
+
}
|
45 |
+
return $points;
|
46 |
+
}
|
47 |
+
|
48 |
+
}
|
src/CycloneSlider/Grafika/Imagick/DrawingObject/QuadraticBezier.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\QuadraticBezier as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\Imagick\Image;
|
7 |
+
use CycloneSlider\Grafika\ImageInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class QuadraticBezier
|
11 |
+
* @package Grafika
|
12 |
+
*/
|
13 |
+
class QuadraticBezier extends Base implements DrawingObjectInterface
|
14 |
+
{
|
15 |
+
/**
|
16 |
+
* @param ImageInterface $image
|
17 |
+
* @return Image
|
18 |
+
*/
|
19 |
+
public function draw($image)
|
20 |
+
{
|
21 |
+
// Localize vars
|
22 |
+
$width = $image->getWidth();
|
23 |
+
$height = $image->getHeight();
|
24 |
+
$imagick = $image->getCore();
|
25 |
+
|
26 |
+
$draw = new \ImagickDraw();
|
27 |
+
|
28 |
+
$strokeColor = new \ImagickPixel($this->getColor()->getHexString());
|
29 |
+
$fillColor = new \ImagickPixel('rgba(0,0,0,0)');
|
30 |
+
|
31 |
+
$draw->setStrokeOpacity(1);
|
32 |
+
$draw->setStrokeColor($strokeColor);
|
33 |
+
$draw->setFillColor($fillColor);
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
list($x1, $y1) = $this->point1;
|
38 |
+
list($x2, $y2) = $this->control;
|
39 |
+
list($x3, $y3) = $this->point2;
|
40 |
+
$draw->pathStart();
|
41 |
+
$draw->pathMoveToAbsolute($x1, $y1);
|
42 |
+
$draw->pathCurveToQuadraticBezierAbsolute(
|
43 |
+
$x2, $y2,
|
44 |
+
$x3, $y3
|
45 |
+
);
|
46 |
+
$draw->pathFinish();
|
47 |
+
|
48 |
+
// Render the draw commands in the ImagickDraw object
|
49 |
+
$imagick->drawImage($draw);
|
50 |
+
|
51 |
+
$type = $image->getType();
|
52 |
+
$file = $image->getImageFile();
|
53 |
+
return new Image($imagick, $file, $width, $height, $type); // Create new image with updated core
|
54 |
+
}
|
55 |
+
}
|
src/CycloneSlider/Grafika/Imagick/DrawingObject/Rectangle.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick\DrawingObject;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\DrawingObject\Rectangle as Base;
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Rectangle
|
9 |
+
* @package Grafika
|
10 |
+
*/
|
11 |
+
class Rectangle extends Base implements DrawingObjectInterface{
|
12 |
+
|
13 |
+
// TODO: check use of stroke property instead of using two rectangles
|
14 |
+
public function draw( $image ) {
|
15 |
+
$strokeColor = new \ImagickPixel( $this->getBorderColor()->getHexString() );
|
16 |
+
$fillColor = new \ImagickPixel( $this->getFillColor()->getHexString() );
|
17 |
+
|
18 |
+
$draw = new \ImagickDraw();
|
19 |
+
$draw->setFillColor($strokeColor);
|
20 |
+
$draw->setStrokeOpacity( 0 );
|
21 |
+
|
22 |
+
$x1 = $this->pos[0];
|
23 |
+
$x2 = $x1 + $this->getWidth();
|
24 |
+
$y1 = $this->pos[1];
|
25 |
+
$y2 = $y1 + $this->getHeight();
|
26 |
+
|
27 |
+
$draw->rectangle( $x1, $y1, $x2, $y2 );
|
28 |
+
|
29 |
+
$borderSize = $this->getBorderSize();
|
30 |
+
$innerRect = new \ImagickDraw();
|
31 |
+
$innerRect->setStrokeOpacity( 0 );
|
32 |
+
$innerRect->setFillColor($fillColor);
|
33 |
+
$innerRect->rectangle( $x1 + $borderSize, $y1 + $borderSize, $x2 - $borderSize, $y2 - $borderSize );
|
34 |
+
|
35 |
+
$image->getCore()->drawImage($draw);
|
36 |
+
$image->getCore()->drawImage($innerRect);
|
37 |
+
|
38 |
+
return $image;
|
39 |
+
}
|
40 |
+
|
41 |
+
}
|
src/CycloneSlider/Grafika/Imagick/Editor.php
ADDED
@@ -0,0 +1,975 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace CycloneSlider\Grafika\Imagick;
|
4 |
+
|
5 |
+
use CycloneSlider\Grafika\DrawingObjectInterface;
|
6 |
+
use CycloneSlider\Grafika\EditorInterface;
|
7 |
+
use CycloneSlider\Grafika\EffectInterface;
|
8 |
+
use CycloneSlider\Grafika\Grafika;
|
9 |
+
use CycloneSlider\Grafika\ImageInterface;
|
10 |
+
use CycloneSlider\Grafika\ImageType;
|
11 |
+
use CycloneSlider\Grafika\Color;
|
12 |
+
use CycloneSlider\Grafika\Imagick\DrawingObject\CubicBezier;
|
13 |
+
use CycloneSlider\Grafika\Imagick\DrawingObject\Ellipse;
|
14 |
+
use CycloneSlider\Grafika\Imagick\DrawingObject\Line;
|
15 |
+
use CycloneSlider\Grafika\Imagick\DrawingObject\Polygon;
|
16 |
+
use CycloneSlider\Grafika\Imagick\DrawingObject\QuadraticBezier;
|
17 |
+
use CycloneSlider\Grafika\Imagick\DrawingObject\Rectangle;
|
18 |
+
use CycloneSlider\Grafika\Imagick\Effect\Dither;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Imagick Editor class. Uses the PHP Imagick library.
|
22 |
+
* @package Grafika\Imagick
|
23 |
+
*/
|
24 |
+
final class Editor implements EditorInterface {
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var Image Holds the image instance
|
28 |
+
*/
|
29 |
+
private $image;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Constructor.
|
33 |
+
*/
|
34 |
+
function __construct() {
|
35 |
+
$this->image = null;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param EffectInterface $effect
|
40 |
+
*
|
41 |
+
* @return $this
|
42 |
+
*/
|
43 |
+
public function apply( $effect ){
|
44 |
+
$this->image = $effect->apply( $this->image );
|
45 |
+
return $this;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Creates a cubic bezier. Cubic bezier has 2 control points.
|
50 |
+
* @param array $point1 Array of X and Y value for start point.
|
51 |
+
* @param array $control1 Array of X and Y value for control point 1.
|
52 |
+
* @param array $control2 Array of X and Y value for control point 2.
|
53 |
+
* @param array $point2 Array of X and Y value for end point.
|
54 |
+
* @param Color|string $color Color of the curve. Accepts hex string or a Color object. Defaults to black.
|
55 |
+
* @return Editor
|
56 |
+
*/
|
57 |
+
public function bezierCubic($point1, $control1, $control2, $point2, $color = '#000000') {
|
58 |
+
if(is_string($color)){
|
59 |
+
$color = new Color($color);
|
60 |
+
}
|
61 |
+
$obj = new CubicBezier($point1, $control1, $control2, $point2, $color);
|
62 |
+
return $this->draw($obj);
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Creates a quadratic bezier. Quadratic bezier has 1 control point.
|
67 |
+
* @param array $point1 Array of X and Y value for start point.
|
68 |
+
* @param array $control Array of X and Y value for control point.
|
69 |
+
* @param array $point2 Array of X and Y value for end point.
|
70 |
+
* @param Color|string $color Color of the curve. Accepts hex string or a Color object. Defaults to black.
|
71 |
+
* @return Editor
|
72 |
+
*/
|
73 |
+
public function bezierQuad($point1, $control, $point2, $color = '#000000') {
|
74 |
+
if(is_string($color)){
|
75 |
+
$color = new Color($color);
|
76 |
+
}
|
77 |
+
$obj = new QuadraticBezier($point1, $control, $point2, $color);
|
78 |
+
return $this->draw($obj);
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Create a blank image given width and height.
|
83 |
+
*
|
84 |
+
* @param int $width Width of image in pixels.
|
85 |
+
* @param int $height Height of image in pixels.
|
86 |
+
*
|
87 |
+
* @return self
|
88 |
+
*/
|
89 |
+
public function blank( $width, $height ) {
|
90 |
+
$this->image = Image::createBlank( $width, $height );
|
91 |
+
|
92 |
+
return $this;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Compare two images and returns a hamming distance. A value of 0 indicates a likely similar picture. A value between 1 and 10 is potentially a variation. A value greater than 10 is likely a different image.
|
97 |
+
* @param ImageInterface|string $image1
|
98 |
+
* @param ImageInterface|string $image2
|
99 |
+
* @return int Hamming distance. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor.
|
100 |
+
* @throws \Exception
|
101 |
+
*/
|
102 |
+
public function compare( $image1, $image2 ){
|
103 |
+
|
104 |
+
if ( is_string( $image1 ) ) { // If string passed, turn it into a Image object
|
105 |
+
$image1 = Image::createFromFile( $image1 );
|
106 |
+
}
|
107 |
+
|
108 |
+
if ( is_string( $image2 ) ) { // If string passed, turn it into a Image object
|
109 |
+
$image2 = Image::createFromFile( $image2 );
|
110 |
+
}
|
111 |
+
|
112 |
+
$bin1 = $this->_differenceHash($image1);
|
113 |
+
$bin2 = $this->_differenceHash($image2);
|
114 |
+
$str1 = str_split($bin1);
|
115 |
+
$str2 = str_split($bin2);
|
116 |
+
$distance = 0;
|
117 |
+
foreach($str1 as $i=>$char){
|
118 |
+
if($char !== $str2[$i]){
|
119 |
+
$distance++;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
return $distance;
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Crop the image to the given dimension and position.
|
128 |
+
*
|
129 |
+
* @param int $cropWidth Crop width in pixels.
|
130 |
+
* @param int $cropHeight Crop Height in pixels.
|
131 |
+
* @param int|string $cropX The number of pixels from the left of the image. Can also be the words "left", "center", and "right".
|
132 |
+
* @param int|string $cropY The number of pixels from the right of the image. Can also be the words "top", "center", and "bottom".
|
133 |
+
*
|
134 |
+
* @return self
|
135 |
+
*/
|
136 |
+
public function crop($cropWidth, $cropHeight, $cropX='center', $cropY='center') {
|
137 |
+
|
138 |
+
if(is_string($cropX)){
|
139 |
+
// Compute position from string
|
140 |
+
switch ($cropX){
|
141 |
+
case 'left':
|
142 |
+
$x = 0;
|
143 |
+
break;
|
144 |
+
|
145 |
+
case 'right':
|
146 |
+
$x = $this->image->getWidth() - $cropWidth;
|
147 |
+
break;
|
148 |
+
|
149 |
+
case 'center':
|
150 |
+
default:
|
151 |
+
$x = (int) round( ($this->image->getWidth()/2) - ($cropWidth/2) );
|
152 |
+
break;
|
153 |
+
}
|
154 |
+
} else {
|
155 |
+
$x = $cropX;
|
156 |
+
}
|
157 |
+
|
158 |
+
if(is_string($cropY)){
|
159 |
+
switch ($cropY){
|
160 |
+
case 'top':
|
161 |
+
$y = 0;
|
162 |
+
break;
|
163 |
+
|
164 |
+
case 'bottom':
|
165 |
+
$y = $this->image->getHeight() - $cropHeight;
|
166 |
+
break;
|
167 |
+
|
168 |
+
case 'center':
|
169 |
+
default:
|
170 |
+
$y = (int) round( ($this->image->getHeight()/2) - ($cropHeight/2) );
|
171 |
+
break;
|
172 |
+
}
|
173 |
+
} else {
|
174 |
+
$y = $cropY;
|
175 |
+
}
|
176 |
+
|
177 |
+
$this->image->getCore()->cropImage( $cropWidth, $cropHeight, $x, $y );
|
178 |
+
|
179 |
+
return $this;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Dither image using Floyd-Steinberg algorithm. Dithering will reduce the color to black and white and add noise.
|
184 |
+
* @return EditorInterface An instance of image editor.
|
185 |
+
*/
|
186 |
+
public function dither(){
|
187 |
+
$e = new Dither();
|
188 |
+
return $this->apply($e);
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* @param DrawingObjectInterface $drawingObject
|
193 |
+
*
|
194 |
+
* @return $this
|
195 |
+
*/
|
196 |
+
public function draw( $drawingObject ){
|
197 |
+
$this->image = $drawingObject->draw( $this->image );
|
198 |
+
return $this;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Creates an ellipse.
|
203 |
+
*
|
204 |
+
* @param int $width Width of ellipse in pixels.
|
205 |
+
* @param int $height Height of ellipse in pixels.
|
206 |
+
* @param array $pos Array containing int X and int Y position of the ellipse from top left of the canvass.
|
207 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
208 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
209 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
210 |
+
*
|
211 |
+
* @return EditorInterface An instance of image editor.
|
212 |
+
*/
|
213 |
+
public function ellipse($width, $height, array $pos, $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF') {
|
214 |
+
if(is_string($borderColor)){
|
215 |
+
$borderColor = new Color($borderColor);
|
216 |
+
}
|
217 |
+
if(is_string($fillColor)){
|
218 |
+
$fillColor = new Color($fillColor);
|
219 |
+
}
|
220 |
+
$obj = new Ellipse($width, $height, $pos, $borderSize, $borderColor, $fillColor);
|
221 |
+
return $this->draw($obj);
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Compare if two images are equal. It will compare if the two images are of the same width and height. If the dimensions differ, it will return false. If the dimensions are equal, it will loop through each pixels. If one of the pixel don't match, it will return false. The pixels are compared using their RGB (Red, Green, Blue) values.
|
226 |
+
*
|
227 |
+
* @param string|ImageInterface $image1 Can be an instance of Image or string containing the file system path to image.
|
228 |
+
* @param string|ImageInterface $image2 Can be an instance of Image or string containing the file system path to image.
|
229 |
+
*
|
230 |
+
* @return bool True if equals false if not. Note: This breaks the chain if you are doing fluent api calls as it does not return an Editor.
|
231 |
+
* @throws \Exception
|
232 |
+
*/
|
233 |
+
public function equal( $image1, $image2 ){
|
234 |
+
|
235 |
+
if ( is_string( $image1 ) ) { // If string passed, turn it into a Image object
|
236 |
+
$image1 = Image::createFromFile( $image1 );
|
237 |
+
}
|
238 |
+
|
239 |
+
if ( is_string( $image2 ) ) { // If string passed, turn it into a Image object
|
240 |
+
$image2 = Image::createFromFile( $image2 );
|
241 |
+
}
|
242 |
+
|
243 |
+
// Check if image dimensions are equal
|
244 |
+
if($image1->getWidth() !== $image2->getWidth() or $image1->getHeight() !== $image2->getHeight()) {
|
245 |
+
|
246 |
+
return false;
|
247 |
+
|
248 |
+
} else {
|
249 |
+
|
250 |
+
// Loop using image1
|
251 |
+
$pixelIterator = $image1->getCore()->getPixelIterator();
|
252 |
+
foreach ($pixelIterator as $row => $pixels) { /* Loop through pixel rows */
|
253 |
+
foreach ($pixels as $column => $pixel) { /* Loop through the pixels in the row (columns) */
|
254 |
+
/**
|
255 |
+
* Get image1 pixel
|
256 |
+
* @var $pixel \ImagickPixel */
|
257 |
+
$rgba1 = $pixel->getColor();
|
258 |
+
|
259 |
+
// Get image2 pixel
|
260 |
+
$rgba2 = $image2->getCore()->getImagePixelColor( $column, $row )->getColor();
|
261 |
+
|
262 |
+
// Compare pixel value
|
263 |
+
if (
|
264 |
+
$rgba1['r'] !== $rgba2['r'] or
|
265 |
+
$rgba1['g'] !== $rgba2['g'] or
|
266 |
+
$rgba1['b'] !== $rgba2['b']
|
267 |
+
) {
|
268 |
+
return false;
|
269 |
+
}
|
270 |
+
}
|
271 |
+
$pixelIterator->syncIterator(); /* Sync the iterator, this is important to do on each iteration */
|
272 |
+
}
|
273 |
+
}
|
274 |
+
return true;
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Fill entire image with color.
|
279 |
+
*
|
280 |
+
* @param Color $color Color object
|
281 |
+
* @param int $x X-coordinate of start point
|
282 |
+
* @param int $y Y-coordinate of start point
|
283 |
+
*
|
284 |
+
* @return self
|
285 |
+
*/
|
286 |
+
public function fill( $color, $x = 0, $y = 0 ){
|
287 |
+
|
288 |
+
$this->_imageCheck();
|
289 |
+
|
290 |
+
$target = $this->image->getCore()->getImagePixelColor($x, $y);
|
291 |
+
$this->image->getCore()->floodfillPaintImage( $color->getHexString(), 1, $target, $x, $y, false);
|
292 |
+
|
293 |
+
return $this;
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Free the current image clearing resources associated with it.
|
298 |
+
*/
|
299 |
+
public function free(){
|
300 |
+
if ( null !== $this->image ) {
|
301 |
+
if( null !== $this->image->getCore() ) {
|
302 |
+
$this->image->getCore()->clear();
|
303 |
+
}
|
304 |
+
} else {
|
305 |
+
$this->image = null;
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Converts image to grayscale.
|
311 |
+
*
|
312 |
+
* @return EditorInterface An instance of image editor.
|
313 |
+
*/
|
314 |
+
public function grayscale(){
|
315 |
+
|
316 |
+
$this->_imageCheck();
|
317 |
+
|
318 |
+
$this->image->getCore()->modulateImage(100, 0, 100);
|
319 |
+
|
320 |
+
return $this;
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Alias for grayscale.
|
325 |
+
*
|
326 |
+
* @return EditorInterface An instance of image editor.
|
327 |
+
*/
|
328 |
+
public function greyscale(){
|
329 |
+
return $this->grayscale();
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* @return Image
|
334 |
+
*/
|
335 |
+
public function getImage() {
|
336 |
+
return $this->image;
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Checks if the editor is available on the current PHP install.
|
341 |
+
*
|
342 |
+
* @return bool
|
343 |
+
*/
|
344 |
+
public function isAvailable(){
|
345 |
+
// First, test Imagick's extension and classes.
|
346 |
+
if ( false === extension_loaded( 'imagick' ) ||
|
347 |
+
false === class_exists( 'Imagick' ) ||
|
348 |
+
false === class_exists( 'ImagickDraw' ) ||
|
349 |
+
false === class_exists( 'ImagickPixel' ) ||
|
350 |
+
false === class_exists( 'ImagickPixelIterator' )
|
351 |
+
){
|
352 |
+
return false;
|
353 |
+
}
|
354 |
+
|
355 |
+
return true;
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Creates a line.
|
360 |
+
*
|
361 |
+
* @param array $point1 Array containing int X and int Y position of the starting point.
|
362 |
+
* @param array $point2 Array containing int X and int Y position of the starting point.
|
363 |
+
* @param int $thickness Thickness in pixel. Note: This is currently ignored in GD editor and falls back to 1.
|
364 |
+
* @param Color|string $color Color of the line. Defaults to black.
|
365 |
+
* @return Editor
|
366 |
+
*/
|
367 |
+
public function line(array $point1, array $point2, $thickness = 1, $color = '#000000') {
|
368 |
+
if(is_string($color)){
|
369 |
+
$color = new Color($color);
|
370 |
+
}
|
371 |
+
$obj = new Line($point1, $point2, $thickness, $color);
|
372 |
+
return $this->draw($obj);
|
373 |
+
}
|
374 |
+
|
375 |
+
/**
|
376 |
+
* Sets the image to the specified opacity level where 1.0 is fully opaque and 0.0 is fully transparent.
|
377 |
+
*
|
378 |
+
* @param float $opacity
|
379 |
+
*
|
380 |
+
* @return self
|
381 |
+
* @throws \Exception
|
382 |
+
*/
|
383 |
+
public function opacity( $opacity ){
|
384 |
+
|
385 |
+
$this->_imageCheck();
|
386 |
+
|
387 |
+
// Bounds checks
|
388 |
+
$opacity = ($opacity > 1) ? 1 : $opacity;
|
389 |
+
$opacity = ($opacity < 0) ? 0 : $opacity;
|
390 |
+
|
391 |
+
$this->image->getCore()->setImageOpacity( $opacity );
|
392 |
+
|
393 |
+
return $this;
|
394 |
+
}
|
395 |
+
|
396 |
+
/**
|
397 |
+
* Opens an image file for manipulation specified by $target.
|
398 |
+
*
|
399 |
+
* @param mixed $target Can be an instance of Image or a string containing file system path to the image.
|
400 |
+
*
|
401 |
+
* @return Editor
|
402 |
+
* @throws \Exception
|
403 |
+
*/
|
404 |
+
public function open( $target ) {
|
405 |
+
if( $target instanceof ImageInterface) {
|
406 |
+
$this->openImage( $target );
|
407 |
+
} else if (is_string($target)){
|
408 |
+
$this->openFile( $target );
|
409 |
+
} else {
|
410 |
+
throw new \Exception( 'Could not open image.' );
|
411 |
+
}
|
412 |
+
|
413 |
+
return $this;
|
414 |
+
}
|
415 |
+
|
416 |
+
/**
|
417 |
+
* Open an image by passing an instance of Image.
|
418 |
+
*
|
419 |
+
* @param ImageInterface $image
|
420 |
+
*
|
421 |
+
* @return $this
|
422 |
+
*/
|
423 |
+
public function openImage( $image ){
|
424 |
+
$this->image = $image;
|
425 |
+
|
426 |
+
return $this;
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Open an image by passing a file system path.
|
431 |
+
*
|
432 |
+
* @param string $file A full path to the image in the file system.
|
433 |
+
*
|
434 |
+
* @return $this
|
435 |
+
* @throws \Exception
|
436 |
+
*/
|
437 |
+
public function openFile( $file ){
|
438 |
+
$this->image = Image::createFromFile( $file );
|
439 |
+
|
440 |
+
return $this;
|
441 |
+
}
|
442 |
+
|
443 |
+
/**
|
444 |
+
* Overlay an image on top of the current image.
|
445 |
+
*
|
446 |
+
* @param Image|string $overlay Can be a string containing a file path of the image to overlay or an Image object.
|
447 |
+
* @param string|int $xPos Horizontal position of image. Can be 'left','center','right' or integer number. Defaults to 'center'.
|
448 |
+
* @param string|int $yPos Vertical position of image. Can be 'top', 'center','bottom' or integer number. Defaults to 'center'.
|
449 |
+
* @param null $width
|
450 |
+
* @param null $height
|
451 |
+
*
|
452 |
+
* @return Editor
|
453 |
+
* @throws \Exception
|
454 |
+
*/
|
455 |
+
public function overlay( $overlay, $xPos = 'center', $yPos = 'center', $width = null, $height = null ) {
|
456 |
+
|
457 |
+
$this->_imageCheck();
|
458 |
+
|
459 |
+
if ( is_string( $overlay ) ) { // If string passed, turn it into a Image object
|
460 |
+
$overlay = Image::createFromFile( $overlay );
|
461 |
+
}
|
462 |
+
|
463 |
+
// Resize overlay
|
464 |
+
if($width and $height){
|
465 |
+
|
466 |
+
$overlayWidth = $overlay->getWidth();
|
467 |
+
$overlayHeight = $overlay->getHeight();
|
468 |
+
|
469 |
+
if(is_numeric($width)){
|
470 |
+
$overlayWidth = (int) $width;
|
471 |
+
} else {
|
472 |
+
$percent = strpos( $width, '%' );
|
473 |
+
if( false !== $percent){
|
474 |
+
$overlayWidth = intval( $width ) / 100 * $this->image->getWidth();
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
if(is_numeric($height)){
|
479 |
+
$overlayHeight = (int) $height;
|
480 |
+
} else {
|
481 |
+
$percent = strpos( $height, '%' );
|
482 |
+
if( false !== $percent){
|
483 |
+
$overlayHeight = intval( $height ) / 100 * $this->image->getHeight();
|
484 |
+
}
|
485 |
+
}
|
486 |
+
|
487 |
+
$editor = new Editor();
|
488 |
+
$editor->setImage($overlay);
|
489 |
+
$editor->resizeFit( $overlayWidth, $overlayHeight );
|
490 |
+
$overlay = $editor->getImage();
|
491 |
+
}
|
492 |
+
|
493 |
+
//$x = $y = 0;
|
494 |
+
|
495 |
+
if ( is_string( $xPos ) ) {
|
496 |
+
// Compute position from string
|
497 |
+
switch ( $xPos ) {
|
498 |
+
case 'left':
|
499 |
+
$x = 0;
|
500 |
+
break;
|
501 |
+
|
502 |
+
case 'right':
|
503 |
+
$x = $this->image->getWidth() - $overlay->getWidth();
|
504 |
+
break;
|
505 |
+
|
506 |
+
case 'center':
|
507 |
+
default:
|
508 |
+
$x = (int) round( ( $this->image->getWidth() / 2 ) - ( $overlay->getWidth() / 2 ) );
|
509 |
+
break;
|
510 |
+
}
|
511 |
+
} else {
|
512 |
+
$x = $xPos;
|
513 |
+
}
|
514 |
+
|
515 |
+
if ( is_string( $yPos ) ) {
|
516 |
+
switch ( $yPos ) {
|
517 |
+
case 'top':
|
518 |
+
$y = 0;
|
519 |
+
break;
|
520 |
+
|
521 |
+
case 'bottom':
|
522 |
+
$y = $this->image->getHeight() - $overlay->getHeight();
|
523 |
+
break;
|
524 |
+
|
525 |
+
case 'center':
|
526 |
+
default:
|
527 |
+
$y = (int) round( ( $this->image->getHeight() / 2 ) - ( $overlay->getHeight() / 2 ) );
|
528 |
+
break;
|
529 |
+
}
|
530 |
+
} else {
|
531 |
+
$y = $yPos;
|
532 |
+
}
|
533 |
+
|
534 |
+
// Overlay the image on the original image
|
535 |
+
$this->image->getCore()->compositeImage($overlay->getCore(), \Imagick::COMPOSITE_OVER, $x, $y);
|
536 |
+
|
537 |
+
return $this;
|
538 |
+
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* Creates a polygon.
|
543 |
+
*
|
544 |
+
* @param array $points Array of all X and Y positions. Must have at least three positions.
|
545 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
546 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
547 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
548 |
+
*
|
549 |
+
* @return EditorInterface An instance of image editor.
|
550 |
+
*/
|
551 |
+
public function polygon($points, $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF'){
|
552 |
+
if(is_string($borderColor)){
|
553 |
+
$borderColor = new Color($borderColor);
|
554 |
+
}
|
555 |
+
if(is_string($fillColor)){
|
556 |
+
$fillColor = new Color($fillColor);
|
557 |
+
}
|
558 |
+
$obj = new Polygon($points, $borderSize, $borderColor, $fillColor);
|
559 |
+
return $this->draw($obj);
|
560 |
+
}
|
561 |
+
|
562 |
+
/**
|
563 |
+
* Creates a rectangle.
|
564 |
+
* @param int $width Width of rectangle in pixels.
|
565 |
+
* @param int $height Height in pixels.
|
566 |
+
* @param array $pos Array of X and Y position. X is the distance in pixels from the left of the canvass to the left of the rectangle. Y is the distance from the top of the canvass to the top of the rectangle. Defaults to array(0,0).
|
567 |
+
* @param int $borderSize Size of the border in pixels. Defaults to 1 pixel. Set to 0 for no border.
|
568 |
+
* @param Color|string|null $borderColor Border color. Defaults to black. Set to null for no color.
|
569 |
+
* @param Color|string|null $fillColor Fill color. Defaults to white. Set to null for no color.
|
570 |
+
* @return Editor
|
571 |
+
*/
|
572 |
+
public function rectangle($width, $height, $pos = array(0, 0), $borderSize = 1, $borderColor = '#000000', $fillColor = '#FFFFFF') {
|
573 |
+
if(is_string($borderColor)){
|
574 |
+
$borderColor = new Color($borderColor);
|
575 |
+
}
|
576 |
+
if(is_string($fillColor)){
|
577 |
+
$fillColor = new Color($fillColor);
|
578 |
+
}
|
579 |
+
$obj = new Rectangle($width, $height, $pos, $borderSize, $borderColor, $fillColor);
|
580 |
+
return $this->draw($obj);
|
581 |
+
}
|
582 |
+
|
583 |
+
/**
|
584 |
+
* Wrapper function for the resizeXXX family of functions. Resize image given width, height and mode.
|
585 |
+
*
|
586 |
+
* @param int $newWidth Width in pixels.
|
587 |
+
* @param int $newHeight Height in pixels.
|
588 |
+
* @param string $mode Resize mode. Possible values: "exact", "exactHeight", "exactWidth", "fill", "fit".
|
589 |
+
*
|
590 |
+
* @return self
|
591 |
+
*/
|
592 |
+
public function resize( $newWidth, $newHeight, $mode='fit' ){
|
593 |
+
|
594 |
+
switch ($mode){
|
595 |
+
case 'exact':
|
596 |
+
$this->resizeExact( $newWidth, $newHeight );
|
597 |
+
break;
|
598 |
+
case 'fill':
|
599 |
+
$this->resizeFill($newWidth, $newHeight);
|
600 |
+
break;
|
601 |
+
case 'exactWidth':
|
602 |
+
$this->resizeExactWidth( $newWidth );
|
603 |
+
break;
|
604 |
+
case 'exactHeight':
|
605 |
+
$this->resizeExactHeight( $newHeight );
|
606 |
+
break;
|
607 |
+
case 'fit':
|
608 |
+
$this->resizeFit($newWidth, $newHeight);
|
609 |
+
break;
|
610 |
+
}
|
611 |
+
|
612 |
+
return $this;
|
613 |
+
}
|
614 |
+
|
615 |
+
/**
|
616 |
+
* Resize image to exact dimensions ignoring aspect ratio. Useful if you want to force exact width and height.
|
617 |
+
*
|
618 |
+
* @param int $newWidth Width in pixels.
|
619 |
+
* @param int $newHeight Height in pixels.
|
620 |
+
*
|
621 |
+
* @return self
|
622 |
+
*/
|
623 |
+
public function resizeExact( $newWidth, $newHeight ){
|
624 |
+
|
625 |
+
$this->_resize($newWidth, $newHeight);
|
626 |
+
|
627 |
+
return $this;
|
628 |
+
}
|
629 |
+
|
630 |
+
/**
|
631 |
+
* Resize image to exact height. Width is auto calculated. Useful for creating row of images with the same height.
|
632 |
+
*
|
633 |
+
* @param int $newHeight Height in pixels.
|
634 |
+
*
|
635 |
+
* @return self
|
636 |
+
*/
|
637 |
+
public function resizeExactHeight( $newHeight ){
|
638 |
+
|
639 |
+
$width = $this->image->getWidth();
|
640 |
+
$height = $this->image->getHeight();
|
641 |
+
$ratio = $width / $height;
|
642 |
+
|
643 |
+
$resizeHeight = $newHeight;
|
644 |
+
$resizeWidth = $newHeight * $ratio;
|
645 |
+
|
646 |
+
$this->_resize( $resizeWidth, $resizeHeight );
|
647 |
+
|
648 |
+
return $this;
|
649 |
+
}
|
650 |
+
|
651 |
+
/**
|
652 |
+
* Resize image to exact width. Height is auto calculated. Useful for creating column of images with the same width.
|
653 |
+
*
|
654 |
+
* @param int $newWidth Width in pixels.
|
655 |
+
*
|
656 |
+
* @return self
|
657 |
+
*/
|
658 |
+
public function resizeExactWidth( $newWidth ){
|
659 |
+
|
660 |
+
$width = $this->image->getWidth();
|
661 |
+
$height = $this->image->getHeight();
|
662 |
+
$ratio = $width / $height;
|
663 |
+
|
664 |
+
$resizeWidth = $newWidth;
|
665 |
+
$resizeHeight = round($newWidth / $ratio);
|
666 |
+
|
667 |
+
$this->_resize( $resizeWidth, $resizeHeight );
|
668 |
+
|
669 |
+
return $this;
|
670 |
+
}
|
671 |
+
|
672 |
+
/**
|
673 |
+
* Resize image to fill all the space in the given dimension. Excess parts are cropped.
|
674 |
+
*
|
675 |
+
* @param int $newWidth Width in pixels.
|
676 |
+
* @param int $newHeight Height in pixels.
|
677 |
+
*
|
678 |
+
* @return self
|
679 |
+
*/
|
680 |
+
public function resizeFill( $newWidth, $newHeight ){
|
681 |
+
$width = $this->image->getWidth();
|
682 |
+
$height = $this->image->getHeight();
|
683 |
+
$ratio = $width / $height;
|
684 |
+
|
685 |
+
// Base optimum size on new width
|
686 |
+
$optimumWidth = $newWidth;
|
687 |
+
$optimumHeight = round($newWidth / $ratio);
|
688 |
+
|
689 |
+
if( ($optimumWidth < $newWidth) or ($optimumHeight < $newHeight) ){ // Oops, where trying to fill and there are blank areas
|
690 |
+
// So base optimum size on height instead
|
691 |
+
$optimumWidth = $newHeight * $ratio;
|
692 |
+
$optimumHeight = $newHeight;
|
693 |
+
}
|
694 |
+
|
695 |
+
$this->_resize( $optimumWidth, $optimumHeight);
|
696 |
+
$this->crop( $newWidth, $newHeight ); // Trim excess parts
|
697 |
+
|
698 |
+
return $this;
|
699 |
+
}
|
700 |
+
|
701 |
+
/**
|
702 |
+
* Resize image to fit inside the given dimension. No part of the image is lost.
|
703 |
+
*
|
704 |
+
* @param int $newWidth Width in pixels.
|
705 |
+
* @param int $newHeight Height in pixels.
|
706 |
+
*
|
707 |
+
* @return self
|
708 |
+
*/
|
709 |
+
public function resizeFit( $newWidth, $newHeight ) {
|
710 |
+
|
711 |
+
$width = $this->image->getWidth();
|
712 |
+
$height = $this->image->getHeight();
|
713 |
+
$ratio = $width / $height;
|
714 |
+
|
715 |
+
// Try basing it on width first
|
716 |
+
$resizeWidth = $newWidth;
|
717 |
+
$resizeHeight = round($newWidth / $ratio);
|
718 |
+
|
719 |
+
if( ( $resizeWidth > $newWidth ) or ( $resizeHeight > $newHeight ) ){ // Oops, either with or height does not fit
|
720 |
+
// So base on height instead
|
721 |
+
$resizeHeight = $newHeight;
|
722 |
+
$resizeWidth = $newHeight * $ratio;
|
723 |
+
}
|
724 |
+
|
725 |
+
$this->_resize( $resizeWidth, $resizeHeight );
|
726 |
+
|
727 |
+
return $this;
|
728 |
+
}
|
729 |
+
|
730 |
+
/**
|
731 |
+
* Rotate an image counter-clockwise.
|
732 |
+
*
|
733 |
+
* @param int $angle The angle in degrees.
|
734 |
+
* @param Color|null $color The Color object containing the background color.
|
735 |
+
*
|
736 |
+
* @return EditorInterface An instance of image editor.
|
737 |
+
*/
|
738 |
+
public function rotate( $angle, $color = null ){
|
739 |
+
|
740 |
+
$this->_imageCheck();
|
741 |
+
|
742 |
+
$color = ($color !== null) ? $color : new Color('#000000');
|
743 |
+
list( $r, $g, $b, $alpha ) = $color->getRgba();
|
744 |
+
|
745 |
+
$this->image->getCore()->rotateImage( new \ImagickPixel("rgba($r, $g, $b, $alpha)"), $angle * -1 );
|
746 |
+
|
747 |
+
return $this;
|
748 |
+
}
|
749 |
+
|
750 |
+
/**
|
751 |
+
* Save the image to an image format.
|
752 |
+
*
|
753 |
+
* @param string $file File path where to save the image.
|
754 |
+
* @param null|string $type Type of image. Can be null, "GIF", "PNG", or "JPEG".
|
755 |
+
* @param null|string $quality Quality of image. Applies to JPEG only. Accepts number 0 - 100 where 0 is lowest and 100 is the highest quality. Or null for default.
|
756 |
+
* @param bool|false $interlace Set to true for progressive JPEG. Applies to JPEG only.
|
757 |
+
* @param int $permission Default permission when creating non-existing target directory.
|
758 |
+
*
|
759 |
+
* @return Editor
|
760 |
+
* @throws \Exception
|
761 |
+
*/
|
762 |
+
public function save( $file, $type = null, $quality = null, $interlace = false, $permission = 0755 ){
|
763 |
+
|
764 |
+
$this->_imageCheck();
|
765 |
+
|
766 |
+
if ( null === $type ) {
|
767 |
+
|
768 |
+
$type = $this->_getImageTypeFromFileName( $file ); // Null given, guess type from file extension
|
769 |
+
if ( ImageType::UNKNOWN === $type ) {
|
770 |
+
$type = $this->image->getType(); // Unknown result, use original image type
|
771 |
+
}
|
772 |
+
}
|
773 |
+
|
774 |
+
$targetDir = dirname( $file ); // $file's directory
|
775 |
+
if( false === is_dir( $targetDir ) ){ // Check if $file's directory exist
|
776 |
+
// Create and set default perms to 755
|
777 |
+
if( !mkdir( $targetDir, $permission, true ) ){
|
778 |
+
throw new \Exception( sprintf('Cannot create %s', $targetDir) );
|
779 |
+
}
|
780 |
+
}
|
781 |
+
|
782 |
+
switch ( $type ) {
|
783 |
+
case ImageType::GIF :
|
784 |
+
$this->image->getCore()->writeImages( $file, true ); // Support animated image. Eg. GIF
|
785 |
+
break;
|
786 |
+
|
787 |
+
case ImageType::PNG :
|
788 |
+
// PNG is lossless and does not need compression
|
789 |
+
$this->image->getCore()->setImageFormat($type);
|
790 |
+
$this->image->getCore()->writeImage( $file );
|
791 |
+
break;
|
792 |
+
|
793 |
+
default: // Defaults to jpeg
|
794 |
+
$quality = ( $quality === null ) ? 60 : $quality; // Default to 60 when null given
|
795 |
+
$quality = ( $quality > 100 ) ? 100 : $quality;
|
796 |
+
$quality = ( $quality < 0 ) ? 0 : $quality;
|
797 |
+
|
798 |
+
if($interlace){
|
799 |
+
$this->image->getCore()->setImageInterlaceScheme( \Imagick::INTERLACE_JPEG );
|
800 |
+
}
|
801 |
+
$this->image->getCore()->setImageFormat($type);
|
802 |
+
$this->image->getCore()->setImageCompressionQuality($quality);
|
803 |
+
$this->image->getCore()->writeImage( $file ); // Single frame image. Eg. JPEG
|
804 |
+
}
|
805 |
+
return $this;
|
806 |
+
}
|
807 |
+
|
808 |
+
/**
|
809 |
+
* @param Image $image
|
810 |
+
*/
|
811 |
+
public function setImage( $image ) {
|
812 |
+
$this->image = $image;
|
813 |
+
}
|
814 |
+
|
815 |
+
/**
|
816 |
+
* Write text to image.
|
817 |
+
*
|
818 |
+
* @param string $text The text to be written.
|
819 |
+
* @param int $size The font size. Defaults to 12.
|
820 |
+
* @param int $x The distance from the left edge of the image to the left of the text. Defaults to 0.
|
821 |
+
* @param int $y The distance from the top edge of the image to the top of the text. Defaults to 12 (equal to font size) so that the text is placed within the image.
|
822 |
+
* @param Color $color The Color object. Default text color is black.
|
823 |
+
* @param string $font Full path to font file. If blank, will default to Liberation Sans font.
|
824 |
+
* @param int $angle Angle of text from 0 - 359. Defaults to 0.
|
825 |
+
*
|
826 |
+
* @return EditorInterface
|
827 |
+
* @throws \Exception
|
828 |
+
*/
|
829 |
+
public function text( $text, $size = 12, $x = 0, $y = 0, $color = null, $font = '', $angle = 0 ) {
|
830 |
+
|
831 |
+
$this->_imageCheck();
|
832 |
+
|
833 |
+
$y += $size;
|
834 |
+
|
835 |
+
$color = ($color !== null) ? $color : new Color('#000000');
|
836 |
+
$font = ($font !== '') ? $font : Grafika::fontsDir().DIRECTORY_SEPARATOR.'LiberationSans-Regular.ttf';
|
837 |
+
|
838 |
+
list( $r, $g, $b, $alpha ) = $color->getRgba();
|
839 |
+
|
840 |
+
// Set up draw properties
|
841 |
+
$draw = new \ImagickDraw();
|
842 |
+
// Text color
|
843 |
+
$draw->setFillColor( new \ImagickPixel("rgba($r, $g, $b, $alpha)") );
|
844 |
+
// Font properties
|
845 |
+
$draw->setFont( $font );
|
846 |
+
$draw->setFontSize( $size );
|
847 |
+
|
848 |
+
// Write text
|
849 |
+
$this->image->getCore()->annotateImage(
|
850 |
+
$draw,
|
851 |
+
$x,
|
852 |
+
$y,
|
853 |
+
$angle,
|
854 |
+
$text
|
855 |
+
);
|
856 |
+
|
857 |
+
return $this;
|
858 |
+
}
|
859 |
+
|
860 |
+
/**
|
861 |
+
* Get difference hash of image.
|
862 |
+
* Algorithm:
|
863 |
+
* Reduce size. The fastest way to remove high frequencies and detail is to shrink the image. In this case, shrink it to 9x8 so that there are 72 total pixels.
|
864 |
+
* Reduce color. Convert the image to a grayscale picture. This changes the hash from 72 pixels to a total of 72 colors.
|
865 |
+
* Compute the difference. The algorithm works on the difference between adjacent pixels. This identifies the relative gradient direction. In this case, the 9 pixels per row yields 8 differences between adjacent pixels. Eight rows of eight differences becomes 64 bits.
|
866 |
+
* Assign bits. Each bit is simply set based on whether the left pixel is brighter than the right pixel.
|
867 |
+
*
|
868 |
+
* http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
|
869 |
+
* @param Image $image
|
870 |
+
* @return string
|
871 |
+
*/
|
872 |
+
private function _differenceHash($image)
|
873 |
+
{
|
874 |
+
|
875 |
+
$width = 9;
|
876 |
+
$height = 8;
|
877 |
+
|
878 |
+
$editor = new Editor();
|
879 |
+
$editor->setImage($image);
|
880 |
+
$editor->resizeExact( $width, $height); // Resize to exactly 9x8
|
881 |
+
$imagick = $editor->getImage()->getCore();
|
882 |
+
|
883 |
+
// Build hash
|
884 |
+
$hash = '';
|
885 |
+
for ($y = 0; $y < $height; $y++) {
|
886 |
+
// Get the pixel value for the leftmost pixel.
|
887 |
+
$rgba = $imagick->getImagePixelColor( 0, $y )->getColor();
|
888 |
+
|
889 |
+
$left = floor(($rgba['r'] + $rgba['g'] + $rgba['b']) / 3);
|
890 |
+
for ($x = 1; $x < $width; $x++) {
|
891 |
+
// Get the pixel value for each pixel starting from position 1.
|
892 |
+
$rgba = $imagick->getImagePixelColor( $x, $y )->getColor();
|
893 |
+
$right = floor(($rgba['r'] + $rgba['g'] + $rgba['b']) / 3);
|
894 |
+
// Each hash bit is set based on whether the left pixel is brighter than the right pixel.
|
895 |
+
if ($left > $right) {
|
896 |
+
$hash .= '1';
|
897 |
+
} else {
|
898 |
+
$hash .= '0';
|
899 |
+
}
|
900 |
+
// Prepare the next loop.
|
901 |
+
$left = $right;
|
902 |
+
}
|
903 |
+
}
|
904 |
+
|
905 |
+
return $hash;
|
906 |
+
}
|
907 |
+
|
908 |
+
/**
|
909 |
+
* Resize helper function.
|
910 |
+
*
|
911 |
+
* @param int $newWidth
|
912 |
+
* @param int $newHeight
|
913 |
+
*
|
914 |
+
* @return self
|
915 |
+
* @throws \Exception
|
916 |
+
*/
|
917 |
+
private function _resize( $newWidth, $newHeight ){
|
918 |
+
$this->_imageCheck();
|
919 |
+
|
920 |
+
if ( 'GIF' == $this->image->getType() ) { // Animated image. Eg. GIF
|
921 |
+
|
922 |
+
$imagick = $this->image->getCore()->coalesceImages();
|
923 |
+
|
924 |
+
foreach ($imagick as $frame) {
|
925 |
+
$frame->resizeImage($newWidth, $newHeight, \Imagick::FILTER_BOX, 1, false);
|
926 |
+
$frame->setImagePage($newWidth, $newHeight, 0, 0);
|
927 |
+
}
|
928 |
+
|
929 |
+
// Assign new image with frames
|
930 |
+
$this->image = new Image($imagick->deconstructImages(), $this->image->getImageFile(), $newWidth, $newHeight, $this->image->getType());
|
931 |
+
} else { // Single frame image. Eg. JPEG, PNG
|
932 |
+
|
933 |
+
$this->image->getCore()->resizeImage($newWidth, $newHeight, \Imagick::FILTER_LANCZOS, 1, false);
|
934 |
+
// Assign new image
|
935 |
+
$this->image = new Image($this->image->getCore(), $this->image->getImageFile(), $newWidth, $newHeight, $this->image->getType());
|
936 |
+
}
|
937 |
+
|
938 |
+
}
|
939 |
+
|
940 |
+
/**
|
941 |
+
* Get image type base on file extension.
|
942 |
+
*
|
943 |
+
* @param int $imageFile File path to image.
|
944 |
+
*
|
945 |
+
* @return ImageType string Type of image.
|
946 |
+
*/
|
947 |
+
private function _getImageTypeFromFileName( $imageFile ) {
|
948 |
+
$ext = strtolower( (string) pathinfo( $imageFile, PATHINFO_EXTENSION ) );
|
949 |
+
|
950 |
+
if ( 'jpg' == $ext or 'jpeg' == $ext ) {
|
951 |
+
return ImageType::JPEG;
|
952 |
+
|
953 |
+
} else if ( 'gif' == $ext ) {
|
954 |
+
return ImageType::GIF;
|
955 |
+
|
956 |
+
} else if ( 'png' == $ext ) {
|
957 |
+
return ImageType::PNG;
|
958 |
+
|
959 |
+
} else {
|
960 |
+
return ImageType::UNKNOWN;
|
961 |
+
}
|
962 |
+
}
|
963 |
+
|
964 |
+
/**
|
965 |
+
* Check if editor has already been assigned an image.
|
966 |
+
*
|
967 |
+
* @throws \Exception
|
968 |
+
*/
|
969 |
+
private function _imageCheck(){
|
970 |
+
if( null === $this->image ){
|
971 |
+
throw new \Exception('No image to edit.');
|
972 |
+
}
|
973 |
+
}
|
974 |
+
|
975 |
+
}
|
src/CycloneSlider/Grafika/Imagick/Effect/Dither.php
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace CycloneSlider\Grafika\Imagick\Effect;
|
4 |
+
|
5 |
+
use CycloneSlider\Grafika\EffectInterface;
|
6 |
+
use CycloneSlider\Grafika\Imagick\Image;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Dither image using Floyd-Steinberg algorithm. Dithering will turn the image black and white and add noise.
|
10 |
+
*/
|
11 |
+
class Dither implements EffectInterface{
|
12 |
+
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @param Image $image
|
16 |
+
*
|
17 |
+
* @return Image
|
18 |
+
*/
|
19 |
+
public function apply( $image ) {
|
20 |
+
return $this->floydSteinberg( $image );
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @param Image $image
|
25 |
+
*
|
26 |
+
* @return Image
|
27 |
+
*/
|
28 |
+
private function floydSteinberg( $image ){
|
29 |
+
$pixels = array();
|
30 |
+
|
31 |
+
// Localize vars
|
32 |
+
$width = $image->getWidth();
|
33 |
+
$height = $image->getHeight();
|
34 |
+
|
35 |
+
// Loop using image1
|
36 |
+
$pixelIterator = $image->getCore()->getPixelIterator();
|
37 |
+
foreach ($pixelIterator as $y => $rows) { /* Loop through pixel rows */
|
38 |
+
foreach ( $rows as $x => $px ) { /* Loop through the pixels in the row (columns) */
|
39 |
+
/**
|
40 |
+
* @var $px \ImagickPixel */
|
41 |
+
$rgba = $px->getColor();
|
42 |
+
|
43 |
+
$gray = round($rgba['r'] * 0.3 + $rgba['g'] * 0.59 + $rgba['b'] * 0.11);
|
44 |
+
|
45 |
+
if(isset($pixels[$x][$y])){ // Add errors to color if there are
|
46 |
+
$gray += $pixels[$x][$y];
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( $gray <= 127 ) { // Determine if black or white. Also has the benefit of clipping excess val due to adding the error
|
50 |
+
$blackOrWhite = 0;
|
51 |
+
} else {
|
52 |
+
$blackOrWhite = 255;
|
53 |
+
}
|
54 |
+
|
55 |
+
$oldPixel = $gray;
|
56 |
+
$newPixel = $blackOrWhite;
|
57 |
+
|
58 |
+
// Current pixel
|
59 |
+
$px->setColor("rgb($newPixel,$newPixel,$newPixel)");
|
60 |
+
|
61 |
+
$qError = $oldPixel - $newPixel; // Quantization error
|
62 |
+
|
63 |
+
// Propagate error on neighbor pixels
|
64 |
+
if ( $x + 1 < $width ) {
|
65 |
+
$pixels[$x+1][$y] = (isset($pixels[$x+1][$y]) ? $pixels[$x+1][$y] : 0) + ($qError * (7 / 16));
|
66 |
+
}
|
67 |
+
|
68 |
+
if ( $x - 1 > 0 and $y + 1 < $height ) {
|
69 |
+
$pixels[$x-1][$y+1] = (isset($pixels[$x-1][$y+1]) ? $pixels[$x-1][$y+1] : 0) + ($qError * (3 / 16));
|
70 |
+
}
|
71 |
+
|
72 |
+
if ( $y + 1 < $height ) {
|
73 |
+
$pixels[$x][$y+1] = (isset($pixels[$x][$y+1]) ? $pixels[$x][$y+1] : 0) + ($qError * (5 / 16));
|
74 |
+
}
|
75 |
+
|
76 |
+
if ( $x + 1 < $width and $y + 1 < $height ) {
|
77 |
+
$pixels[$x+1][$y+1] = (isset($pixels[$x+1][$y+1]) ? $pixels[$x+1][$y+1] : 0) + ($qError * (1 / 16));
|
78 |
+
}
|
79 |
+
|
80 |
+
}
|
81 |
+
$pixelIterator->syncIterator(); /* Sync the iterator, this is important to do on each iteration */
|
82 |
+
}
|
83 |
+
|
84 |
+
$type = $image->getType();
|
85 |
+
$file = $image->getImageFile();
|
86 |
+
$image = $image->getCore();
|
87 |
+
|
88 |
+
return new Image( $image, $file, $width, $height, $type ); // Create new image with updated core
|
89 |
+
|
90 |
+
}
|
91 |
+
}
|
src/CycloneSlider/Grafika/Imagick/Image.php
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace CycloneSlider\Grafika\Imagick;
|
3 |
+
|
4 |
+
use CycloneSlider\Grafika\ImageInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Immutable image class for Imagick.
|
8 |
+
* @package Grafika\Gd
|
9 |
+
*/
|
10 |
+
final class Image implements ImageInterface {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var \Imagick Imagick instance
|
14 |
+
*/
|
15 |
+
private $imagick;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var string File path to image
|
19 |
+
*/
|
20 |
+
private $imageFile;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var int Image width in pixels
|
24 |
+
*/
|
25 |
+
private $width;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var int Image height in pixels
|
29 |
+
*/
|
30 |
+
private $height;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var string Image type. Return value of Imagick::queryFormats(). See http://phpimagick.com/Imagick/queryFormats
|
34 |
+
* Sample values: JPEG, PNG, GIF, WBMP
|
35 |
+
*/
|
36 |
+
private $type;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Image constructor.
|
40 |
+
*
|
41 |
+
* @param \Imagick $imagick
|
42 |
+
* @param string $imageFile
|
43 |
+
* @param int $width
|
44 |
+
* @param int $height
|
45 |
+
* @param string $type
|
46 |
+
*/
|
47 |
+
public function __construct( \Imagick $imagick, $imageFile, $width, $height, $type ) {
|
48 |
+
$this->imagick = $imagick;
|
49 |
+
$this->imageFile = $imageFile;
|
50 |
+
$this->width = $width;
|
51 |
+
$this->height = $height;
|
52 |
+
$this->type = $type;
|
53 |
+
}
|
54 |
+
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @param $imageFile
|
58 |
+
*
|
59 |
+
* @return Image
|
60 |
+
* @throws \Exception
|
61 |
+
*/
|
62 |
+
public static function createFromFile( $imageFile ){
|
63 |
+
$imageFile = realpath( $imageFile );
|
64 |
+
|
65 |
+
if ( ! file_exists( $imageFile ) ) {
|
66 |
+
throw new \Exception( sprintf('Could not open image file "%s"', $imageFile) );
|
67 |
+
}
|
68 |
+
|
69 |
+
$imagick = new \Imagick( realpath($imageFile) );
|
70 |
+
return new self( $imagick, $imageFile, $imagick->getImageWidth(), $imagick->getImageHeight(), $imagick->getImageFormat());
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Create a blank image.
|
75 |
+
*
|
76 |
+
* @param int $width Width in pixels.
|
77 |
+
* @param int $height Height in pixels.
|
78 |
+
*
|
79 |
+
* @return self
|
80 |
+
*/
|
81 |
+
public static function createBlank($width = 1, $height = 1){
|
82 |
+
$imagick = new \Imagick();
|
83 |
+
$imagick->newImage($width, $height, new \ImagickPixel('black'));
|
84 |
+
$imagick->setImageFormat('png'); // Default to PNG.
|
85 |
+
|
86 |
+
return new self( $imagick, '', $imagick->getImageWidth(), $imagick->getImageHeight(), $imagick->getImageFormat());
|
87 |
+
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Get Imagick instance
|
92 |
+
*
|
93 |
+
* @return \Imagick
|
94 |
+
*/
|
95 |
+
public function getCore() {
|
96 |
+
return $this->imagick;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Get image file path.
|
101 |
+
*
|
102 |
+
* @return string File path to image.
|
103 |
+
*/
|
104 |
+
public function getImageFile() {
|
105 |
+
return $this->imageFile;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Get image width in pixels.
|
110 |
+
*
|
111 |
+
* @return int
|
112 |
+
*/
|
113 |
+
public function getWidth() {
|
114 |
+
return $this->width;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Get image height in pixels.
|
119 |
+
*
|
120 |
+
* @return int
|
121 |
+
*/
|
122 |
+
public function getHeight() {
|
123 |
+
return $this->height;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Get image type.
|
128 |
+
*
|
129 |
+
* @return string
|
130 |
+
*/
|
131 |
+
public function getType() {
|
132 |
+
return $this->type;
|
133 |
+
}
|
134 |
+
|
135 |
+
}
|
src/CycloneSlider/ImageResizer.php
CHANGED
@@ -22,7 +22,6 @@ class CycloneSlider_ImageResizer {
|
|
22 |
*
|
23 |
* @return bool
|
24 |
* @throws Exception
|
25 |
-
* @internal param int $slider_id Slider post ID
|
26 |
*/
|
27 |
public function resize_images( $slider_settings, $slides ){
|
28 |
|
@@ -101,19 +100,41 @@ class CycloneSlider_ImageResizer {
|
|
101 |
* @return boolean True or false
|
102 |
*/
|
103 |
private function resize_slide_image( $image_file, $image_file_dest, $width, $height, $resize_option, $resize_quality){
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
}
|
116 |
-
|
117 |
}
|
118 |
|
119 |
/**
|
22 |
*
|
23 |
* @return bool
|
24 |
* @throws Exception
|
|
|
25 |
*/
|
26 |
public function resize_images( $slider_settings, $slides ){
|
27 |
|
100 |
* @return boolean True or false
|
101 |
*/
|
102 |
private function resize_slide_image( $image_file, $image_file_dest, $width, $height, $resize_option, $resize_quality){
|
103 |
+
if(version_compare(PHP_VERSION, '5.3', '>=')){
|
104 |
+
|
105 |
+
$editor = \CycloneSlider\Grafika\Grafika::createEditor();
|
106 |
+
$editor->open( $image_file );
|
107 |
+
if('fill'==$resize_option){
|
108 |
+
$editor->resizeFill( $width, $height );
|
109 |
+
} else if('crop'==$resize_option){
|
110 |
+
$editor->crop( $width, $height );
|
111 |
+
} else if('exact'==$resize_option){
|
112 |
+
$editor->resizeExact( $width, $height );
|
113 |
+
} else if('exactHeight'==$resize_option){
|
114 |
+
$editor->resizeExactHeight( $height );
|
115 |
+
} else if('exactWidth'==$resize_option){
|
116 |
+
$editor->resizeExactWidth( $width );
|
117 |
+
} else {
|
118 |
+
$editor->resizeFit( $width, $height );
|
119 |
+
}
|
120 |
+
$editor->save( $image_file_dest, null, $resize_quality );
|
121 |
+
|
122 |
return true;
|
123 |
+
} else {
|
124 |
+
// Create
|
125 |
+
$image = new CycloneSlider_ImageEditor($image_file);
|
126 |
+
// Load
|
127 |
+
if( $image->load() ){
|
128 |
+
|
129 |
+
// Do resize
|
130 |
+
$image->resize( $width, $height, $resize_option );
|
131 |
+
$image->save( $image_file_dest, $resize_quality );
|
132 |
+
|
133 |
+
return true;
|
134 |
+
}
|
135 |
+
return false;
|
136 |
}
|
137 |
+
|
138 |
}
|
139 |
|
140 |
/**
|
src/autoloader.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// Autoloader
|
3 |
+
function cycloneslider_autoloader( $class_name ) {
|
4 |
+
if ( 0 === strpos( $class_name, 'CycloneSlider' ) ) {
|
5 |
+
$src = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
|
6 |
+
$class = str_replace( '_', DIRECTORY_SEPARATOR, $class_name ) . '.php';
|
7 |
+
require_once $src . $class;
|
8 |
+
}
|
9 |
+
}
|
10 |
+
|
11 |
+
spl_autoload_register( 'cycloneslider_autoloader' );
|
views/slide-edit.php
CHANGED
@@ -40,7 +40,7 @@
|
|
40 |
<div class="cs-image-preview">
|
41 |
<div class="cs-image-thumb" <?php echo (empty($image_url)) ? 'style="display:none"' : '';?>>
|
42 |
<?php if($image_url): ?>
|
43 |
-
|
44 |
<?php endif; ?>
|
45 |
</div>
|
46 |
<input class="cs-image-id" name="cycloneslider_metas[<?php echo esc_attr($i); ?>][id]" type="hidden" value="<?php echo esc_attr($slide['id']); ?>" />
|
40 |
<div class="cs-image-preview">
|
41 |
<div class="cs-image-thumb" <?php echo (empty($image_url)) ? 'style="display:none"' : '';?>>
|
42 |
<?php if($image_url): ?>
|
43 |
+
<a target="_blank" href="<?php echo esc_url($full_image_url); ?>"><img src="<?php echo esc_url($image_url); ?>" alt="thumb"></a>
|
44 |
<?php endif; ?>
|
45 |
</div>
|
46 |
<input class="cs-image-id" name="cycloneslider_metas[<?php echo esc_attr($i); ?>][id]" type="hidden" value="<?php echo esc_attr($slide['id']); ?>" />
|
views/slider-advanced-settings.php
CHANGED
@@ -57,11 +57,20 @@
|
|
57 |
<?php endforeach; ?>
|
58 |
</select>
|
59 |
<span class="note">
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
</span>
|
66 |
<div class="clear"></div>
|
67 |
</div>
|
57 |
<?php endforeach; ?>
|
58 |
</select>
|
59 |
<span class="note">
|
60 |
+
<?php if(version_compare(PHP_VERSION, '5.3', '>=')){ ?>
|
61 |
+
<?php _e('Fit - (Default) Fit images inside the slideshow maintaining aspect ratio.', 'cyclone-slider-2'); ?><br>
|
62 |
+
<?php _e('Fill - Images will fill the entire slideshow with no empty space.', 'cyclone-slider-2'); ?><br>
|
63 |
+
<?php _e('Crop - Excess parts of images are removed.', 'cyclone-slider-2'); ?><br>
|
64 |
+
<?php _e('Exact - Resize images to exact dimensions ignoring aspect ratio.', 'cyclone-slider-2'); ?><br>
|
65 |
+
<?php _e('Exact Width - Resize to exact width.', 'cyclone-slider-2'); ?><br>
|
66 |
+
<?php _e('Exact Height - Resize to exact height.', 'cyclone-slider-2'); ?><br>
|
67 |
+
<?php } else { ?>
|
68 |
+
<?php _e('Auto - Cyclone Slider decides the resize option.', 'cyclone-slider-2'); ?><br>
|
69 |
+
<?php _e('Crop - Resize and remove excess parts.', 'cyclone-slider-2'); ?><br>
|
70 |
+
<?php _e('Exact - Resize to exact dimensions.', 'cyclone-slider-2'); ?><br>
|
71 |
+
<?php _e('Landscape - Resize to exact width.', 'cyclone-slider-2'); ?><br>
|
72 |
+
<?php _e('Portrait - Resize to exact height.', 'cyclone-slider-2'); ?><br>
|
73 |
+
<?php } ?>
|
74 |
</span>
|
75 |
<div class="clear"></div>
|
76 |
</div>
|