xref: /imagick/imagickkernel_class.c (revision ab50000e)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5 / Imagick	                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Dan Ackroyd <danack@php.net>                                 |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "php_imagick.h"
18 #include "php_imagick_defs.h"
19 #include "php_imagick_macros.h"
20 #include "php_imagick_helpers.h"
21 
22 #ifdef IMAGICK_WITH_KERNEL
23 
24 #if MagickLibVersion < 0x691
25 // These function defines are required currently as the functions are
26 // currently not available in the public ImageMagick header files
27 // This is being fixed for the next version of ImageMagick.
28 Image
29   *MorphologyApply(const Image *,const ChannelType,const MorphologyMethod,
30     const ssize_t,const KernelInfo *,const CompositeOperator,const double,
31     ExceptionInfo *);
32 
33 void
34   ScaleKernelInfo(KernelInfo *,const double,const GeometryFlags),
35   UnityAddKernelInfo(KernelInfo *,const double),
36   ZeroKernelNans(KernelInfo *);
37 #endif
38 
39 
php_imagickkernelvalues_to_zval(zval * zv,KernelInfo * kernel_info)40 static void php_imagickkernelvalues_to_zval(zval *zv, KernelInfo *kernel_info) {
41 	int count;
42 	double value;
43 	unsigned int x, y;
44 #if PHP_VERSION_ID >= 70000
45 	zval row;
46 #else
47 	zval *row;
48 #endif
49 
50 	zval *p_row;
51 
52 	count = 0;
53 
54 	for (y=0; y<kernel_info->height ; y++) {
55 #if PHP_VERSION_ID >= 70000
56 		p_row = &row;
57 #else
58 		MAKE_STD_ZVAL(row);
59 		p_row = row;
60 #endif
61 
62 		array_init(p_row);
63 		for (x=0; x<kernel_info->width ; x++) {
64 			value = kernel_info->values[count];
65 			count++;
66 
67 			//nan is not equal to itself
68 			if (value != value) {
69 				//this will be broken by some compilers - need to investigate more...
70 				add_next_index_bool(p_row, 0);
71 			}
72 			else {
73 				add_next_index_double(p_row, value);
74 			}
75 		}
76 
77 		add_next_index_zval(zv, p_row);
78 	}
79 }
80 
81 
82 #if PHP_VERSION_ID >= 80000
php_imagickkernel_get_debug_info(zend_object * obj,int * is_temp TSRMLS_DC)83 HashTable* php_imagickkernel_get_debug_info(zend_object *obj, int *is_temp TSRMLS_DC) /* {{{ */
84 #else
85 HashTable* php_imagickkernel_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
86 #endif
87 {
88 	php_imagickkernel_object *internp;
89 	HashTable *debug_info;
90 	KernelInfo *kernel_info;
91 #if PHP_VERSION_ID >= 70000
92 	zval matrix;
93 #else
94 	zval *matrix;
95 #endif
96 
97 	*is_temp = 1; //var_dump will destroy the hashtable
98 
99 #if PHP_VERSION_ID >= 80000
100 	internp = php_imagickkernel_fetch_object(obj);
101 #else
102 	internp = Z_IMAGICKKERNEL_P(obj);
103 #endif
104 	kernel_info = internp->kernel_info;
105 
106 	ALLOC_HASHTABLE(debug_info);
107 	ZEND_INIT_SYMTABLE_EX(debug_info, 1, 0);
108 
109 	while (kernel_info != NULL) {
110 #if PHP_VERSION_ID >= 70000
111 		array_init(&matrix);
112 		php_imagickkernelvalues_to_zval(&matrix, kernel_info);
113 		zend_hash_next_index_insert(debug_info, &matrix);
114 #else
115 		MAKE_STD_ZVAL(matrix);
116 		array_init(matrix);
117 		php_imagickkernelvalues_to_zval(matrix, kernel_info);
118 		zend_hash_next_index_insert(debug_info, &matrix, sizeof(zval *), NULL);
119 #endif
120 		kernel_info = kernel_info->next;
121 	}
122 
123 	return debug_info;
124 }
125 
126 
im_CalcKernelMetaData(KernelInfo * kernel)127 static void im_CalcKernelMetaData(KernelInfo *kernel) {
128 	size_t i;
129 
130 	kernel->minimum = kernel->maximum = 0.0;
131 	kernel->negative_range = kernel->positive_range = 0.0;
132 
133 	for (i=0; i < (kernel->width*kernel->height); i++) {
134 		if (fabs(kernel->values[i]) < MagickEpsilon) {
135 			kernel->values[i] = 0.0;
136 		}
137 		if (kernel->values[i] < 0) {
138 			kernel->negative_range += kernel->values[i];
139 		}
140 		else {
141 			kernel->positive_range += kernel->values[i];
142 		}
143 		if (kernel->values[i] < kernel->minimum) {
144 			kernel->minimum = kernel->values[i];
145 		}
146 
147 		if (kernel->values[i] > kernel->maximum) {
148 			kernel->maximum = kernel->values[i];
149 		}
150 	}
151 
152 	return;
153 }
154 
155 
156 #if MagickLibVersion > 0x661
imagick_createKernel(KernelValueType * values,size_t width,size_t height,size_t origin_x,size_t origin_y)157 static KernelInfo *imagick_createKernel(KernelValueType *values, size_t width, size_t height, size_t origin_x, size_t origin_y)
158 {
159 	KernelInfo *kernel_info;
160 
161 #if MagickLibVersion >= 0x700
162 	unsigned int i;
163 	ExceptionInfo *_exception_info = (ExceptionInfo *) NULL;
164 	//TODO - inspect exception info
165 	kernel_info=AcquireKernelInfo(NULL, _exception_info);
166 #else
167 	kernel_info=AcquireKernelInfo(NULL);
168 #endif
169 	if (kernel_info == (KernelInfo *) NULL) {
170 		return NULL;
171 	}
172 
173 	kernel_info->width = width;
174 	kernel_info->height = height;
175 
176 	kernel_info->x = origin_x;
177 	kernel_info->y = origin_y;
178 
179 	//Need to free old values?
180 	if (kernel_info->values != NULL) {
181 		RelinquishAlignedMemory(kernel_info->values);
182 	}
183 
184 #if MagickLibVersion >= 0x700
185 	kernel_info->values = (MagickRealType *)AcquireAlignedMemory(width*height, sizeof(MagickRealType));
186 
187 	for (i=0; i<width*height;i++) {
188 		kernel_info->values[i] = (MagickRealType)values[i];
189 	}
190 
191 #else
192 	kernel_info->values = values;
193 #endif
194 
195 	im_CalcKernelMetaData(kernel_info);
196 
197 	return kernel_info;
198 }
199 #endif
200 
createKernelZval(zval * pzval,KernelInfo * kernel_info TSRMLS_DC)201 static void createKernelZval(zval *pzval, KernelInfo *kernel_info TSRMLS_DC) {
202 	php_imagickkernel_object *intern_return;
203 
204 	object_init_ex(pzval, php_imagickkernel_sc_entry);
205 	intern_return = Z_IMAGICKKERNEL_P(pzval);
206 	intern_return->kernel_info = kernel_info;
207 }
208 
209 
210 /* {{{ proto ImagickKernel ImagickKernel::__construct()
211    The ImagickKernel constructor
212 */
PHP_METHOD(ImagickKernel,__construct)213 PHP_METHOD(ImagickKernel, __construct)
214 {
215 	// This suppresses an 'unused parameter' warning.
216 	(void)return_value;
217 
218 	if (zend_parse_parameters_none() == FAILURE) {
219 		return;
220 	}
221 	// this method is private.
222 }
223 /* }}} */
224 
225 #define MATRIX_ERROR_EMPTY "Cannot create kernel, matrix is empty."
226 #define MATRIX_ERROR_UNEVEN "Values must be matrix, with the same number of columns in each row."
227 #define MATRIX_ERROR_BAD_VALUE "Only numbers or false are valid values in a kernel matrix."
228 #define MATRIX_ORIGIN_REQUIRED "For kernels with even numbered rows or columns, the origin position must be specified."
229 
230 /* {{{ proto ImagickKernel ImagickKernel::fromMatrix(array matrix, [array origin])
231 	Create a kernel from an 2d matrix of values. Each value should either be a float
232 	(if the element should be used) or 'false' if the element should be skipped. For
233 	matrixes that are odd sizes in both dimensions the the origin pixel will default
234 	to the centre of the kernel. For all other kernel sizes the origin pixel must be specified.
235 */
236 #if PHP_VERSION_ID >= 70000
PHP_METHOD(ImagickKernel,fromMatrix)237 PHP_METHOD(ImagickKernel, fromMatrix)
238 
239 {
240 	zval *kernel_array;
241 	zval *origin_array;
242 	HashTable *inner_array;
243 	KernelInfo *kernel_info;
244 	unsigned long num_rows, num_columns = 0;
245 	unsigned int previous_num_columns = (unsigned int)-1;
246 	unsigned int row, column;
247 
248 	zval *pzval_outer;
249 	zval *pzval_inner;
250 
251 	int count = 0;
252 	size_t origin_x, origin_y;
253 	zval *tmp;
254 
255 	KernelValueType *values = NULL;
256 	double notanumber = sqrt((double)-1.0);  /* Special Value : Not A Number */
257 
258 	count = 0;
259 	row = 0;
260 	origin_array = NULL;
261 
262 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a", &kernel_array, &origin_array) == FAILURE) {
263 		return;
264 	}
265 
266 	num_rows = zend_hash_num_elements(Z_ARRVAL_P(kernel_array));
267 
268 	if (num_rows == 0) {
269 		//error - array has zero elements.
270 		php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
271 		return;
272 	}
273 
274 
275 	for (row=0 ; row<num_rows ; row++) {
276 		pzval_outer = zend_hash_index_find(Z_ARRVAL_P(kernel_array), row);
277 		if (pzval_outer == NULL) {
278 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
279 			goto cleanup;
280 		}
281 		ZVAL_DEREF(pzval_outer);
282 
283 		column = 0;
284 
285 		if (Z_TYPE_P(pzval_outer) == IS_ARRAY ) {
286 			inner_array = Z_ARRVAL_P(pzval_outer);
287 			num_columns = zend_hash_num_elements(inner_array);
288 
289 			if (num_columns == 0) {
290 				php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
291 				goto cleanup;
292 			}
293 
294 			if (values == NULL) {
295 				values = (KernelValueType *)AcquireAlignedMemory(num_columns, num_rows*sizeof(KernelValueType));
296 			}
297 
298 			if (previous_num_columns != ((unsigned int)-1)) {
299 				if (previous_num_columns != num_columns) {
300 					php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
301 					goto cleanup;
302 				}
303 			}
304 
305 			previous_num_columns = num_columns;
306 
307 			for (column=0; column<num_columns ; column++) {
308 				pzval_inner = zend_hash_index_find(inner_array, column);
309 				if (pzval_inner == NULL) {
310 					php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
311 					goto cleanup;
312 				}
313 
314 				ZVAL_DEREF(pzval_inner);
315 				if (Z_TYPE_P(pzval_inner) == IS_DOUBLE) {
316 					//It's a float lets use it.
317 					values[count] = Z_DVAL_P(pzval_inner);
318 				}
319 				else if (Z_TYPE_P(pzval_inner) == IS_LONG) {
320 					//It's a long lets use it.
321 					values[count] = (float)Z_LVAL_P(pzval_inner);
322 				}
323 				else if (Z_TYPE_P(pzval_inner) == IS_FALSE) {
324 					//It's false, use nan
325 					values[count] = notanumber;
326 				}
327 				else {
328 					php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_BAD_VALUE TSRMLS_CC);
329 					goto cleanup;
330 				}
331 				count++;
332 			}
333 		}
334 		else {
335 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
336 			goto cleanup;
337 		}
338 	}
339 
340 	if (origin_array == NULL) {
341 		if (((num_columns%2) == 0) || ((num_rows%2) == 0)) {
342 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
343 			goto cleanup;
344 		}
345 		origin_x = (num_columns - 1) >> 1;
346 		origin_y = (num_rows - 1) >> 1;
347 	}
348 	else {
349 		HashTable *origin_array_ht;
350 		origin_array_ht = Z_ARRVAL_P(origin_array);
351 
352 		// parse the origin_x
353 		tmp = zend_hash_index_find(origin_array_ht, 0);
354 		if (tmp != NULL) {
355 			ZVAL_DEREF(tmp);
356 			origin_x = Z_LVAL_P(tmp);
357 		}
358 		else {
359 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
360 			goto cleanup;
361 		}
362 		// origin_x is unsigned, so checking for > num_columns, also
363 		// checks for < 0
364 		if (origin_x>=num_columns) {
365 			zend_throw_exception_ex(
366 				php_imagickkernel_exception_class_entry,
367 				5 TSRMLS_CC,
368 				"origin_x for matrix is outside bounds of columns: " ZEND_LONG_FMT,
369 				origin_x
370 			);
371 			goto cleanup;
372 		}
373 
374 		// parse the origin_y
375 		tmp = zend_hash_index_find(origin_array_ht, 1);
376 		if (tmp != NULL) {
377 			ZVAL_DEREF(tmp);
378 			origin_y = Z_LVAL_P(tmp);
379 		}
380 		else {
381 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
382 			goto cleanup;
383 		}
384 		// origin_y is unsigned, so checking for > num_rows, also
385 		// checks for < 0
386 		if (origin_y>=num_rows) {
387 			zend_throw_exception_ex(
388 				php_imagickkernel_exception_class_entry,
389 				5 TSRMLS_CC,
390 				"origin_y for matrix is outside bounds of rows: " ZEND_LONG_FMT,
391 				origin_x
392 			);
393 			goto cleanup;
394 		}
395 	}
396 
397 	kernel_info = imagick_createKernel(values, num_columns, num_rows, origin_x, origin_y);
398 	createKernelZval(return_value, kernel_info TSRMLS_CC);
399 
400 	return;
401 
402 cleanup:
403 	if (values != NULL) {
404 		RelinquishAlignedMemory(values);
405 	}
406 }
407 
408 #else // PHP 5
409 
410 
PHP_METHOD(ImagickKernel,fromMatrix)411 PHP_METHOD(ImagickKernel, fromMatrix)
412 {
413 	zval *kernel_array;
414 	zval *origin_array;
415 	HashTable *inner_array;
416 	KernelInfo *kernel_info;
417 	unsigned long num_rows, num_columns = 0;
418 	unsigned int previous_num_columns = (unsigned int)-1;
419 	unsigned int row, column;
420 
421 	HashTable *origin_array_ht;
422 	zval **ppzval_outer;
423 	zval **ppzval_inner;
424 
425 	int count = 0;
426 	size_t origin_x, origin_y;
427 	zval **tmp;
428 
429 	KernelValueType *values = NULL;
430 	double notanumber = sqrt((double)-1.0);  /* Special Value : Not A Number */
431 
432 	previous_num_columns = -1;
433 	count = 0;
434 	row = 0;
435 	origin_array = NULL;
436 
437 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a", &kernel_array, &origin_array) == FAILURE) {
438 		return;
439 	}
440 
441 	num_rows = zend_hash_num_elements(Z_ARRVAL_P(kernel_array));
442 
443 	if (num_rows == 0) {
444 		//error - array has zero elements.
445 		php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
446 		return;
447 	}
448 
449 	for (row=0 ; row<num_rows ; row++) {
450 		if (zend_hash_index_find(Z_ARRVAL_P(kernel_array), row, (void **) &ppzval_outer) != SUCCESS) {
451 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
452 			goto cleanup;
453 		}
454 
455 		column = 0;
456 
457 		if (Z_TYPE_PP(ppzval_outer) == IS_ARRAY ) {
458 			//parse this row
459 			inner_array = Z_ARRVAL_PP(ppzval_outer);
460 			num_columns = zend_hash_num_elements(inner_array);
461 
462 			if (num_columns == 0) {
463 				php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
464 				goto cleanup;
465 			}
466 
467 			if (values == NULL) {
468 				values = (KernelValueType *)AcquireAlignedMemory(num_columns, num_rows*sizeof(KernelValueType));
469 			}
470 
471 			if (previous_num_columns != ((unsigned int)-1)) {
472 				if (previous_num_columns != num_columns) {
473 					php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
474 					goto cleanup;
475 				}
476 			}
477 
478 			previous_num_columns = num_columns;
479 
480 			for (column=0; column<num_columns ; column++) {
481 				if (zend_hash_index_find(inner_array, column, (void **) &ppzval_inner) != SUCCESS) {
482 					php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
483 					goto cleanup;
484 				}
485 
486 				if (Z_TYPE_PP(ppzval_inner) == IS_DOUBLE) {
487 					//It's a float lets use it.
488 					values[count] = Z_DVAL_PP(ppzval_inner);
489 				}
490 				else if (Z_TYPE_PP(ppzval_inner) == IS_LONG) {
491 					//It's a long lets use it.
492 					values[count] = (float)Z_LVAL_PP(ppzval_inner);
493 				}
494 				else if (Z_TYPE_PP(ppzval_inner) == IS_BOOL && Z_BVAL_PP(ppzval_inner) == 0) {
495 					//It's false, use nan
496 					values[count] = notanumber;
497 				}
498 				else {
499 					php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_BAD_VALUE TSRMLS_CC);
500 					goto cleanup;
501 				}
502 				count++;
503 			}
504 		}
505 		else {
506 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
507 			goto cleanup;
508 		}
509 	}
510 
511 	if (origin_array == NULL) {
512 		if (((num_columns%2) == 0) || ((num_rows%2) == 0)) {
513 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
514 			goto cleanup;
515 		}
516 		origin_x = (num_columns - 1) >> 1;
517 		origin_y = (num_rows - 1) >> 1;
518 	}
519 	else {
520 		origin_array_ht = Z_ARRVAL_P(origin_array);
521 
522 		// parse and check the origin_x
523 		if (zend_hash_index_find(origin_array_ht, 0, (void**)&tmp) == SUCCESS) {
524 			origin_x = Z_LVAL_PP(tmp);
525 		}
526 		else {
527 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
528 			goto cleanup;
529 		}
530 
531 		// origin_x is unsigned, so checking for > num_columns, also
532 		// checks for < 0
533 		if (origin_x>=num_columns) {
534 			zend_throw_exception_ex(
535 				php_imagickkernel_exception_class_entry,
536 				5 TSRMLS_CC,
537 				"origin_x for matrix is outside bounds of columns: %d",
538 				origin_x
539 			);
540 			goto cleanup;
541 		}
542 
543         // parse and check the origin_y
544 		if (zend_hash_index_find(origin_array_ht, 1, (void**)&tmp) == SUCCESS) {
545 			origin_y = Z_LVAL_PP(tmp);
546 		}
547 		else {
548 			php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
549 			goto cleanup;
550 		}
551 
552 		// origin_y is unsigned, so checking for > num_rows, also
553 		// checks for < 0
554 		if (origin_y>=num_rows) {
555 			zend_throw_exception_ex(
556 				php_imagickkernel_exception_class_entry,
557 				5 TSRMLS_CC,
558 				"origin_y for matrix is outside bounds of rows: %d",
559 				origin_y
560 			);
561 			goto cleanup;
562 		}
563 	}
564 
565 	kernel_info = imagick_createKernel(values, num_columns, num_rows, origin_x, origin_y);
566 	createKernelZval(return_value, kernel_info TSRMLS_CC);
567 
568 	return;
569 
570 cleanup:
571 	if (values != NULL) {
572 		RelinquishAlignedMemory(values);
573 	}
574 }
575 #endif //end of zend_engine_3
576 /* }}} */
577 
imagick_fiddle_with_geometry_info(ssize_t type,GeometryFlags flags,GeometryInfo * geometry_info)578 static void imagick_fiddle_with_geometry_info(ssize_t type, GeometryFlags flags, GeometryInfo *geometry_info) {
579 
580 	/* special handling of missing values in input string */
581 	switch( type ) {
582 		/* Shape Kernel Defaults */
583 		case UnityKernel: {
584 			if ((flags & WidthValue) == 0)
585 			geometry_info->rho = 1.0;    /* Default scale = 1.0, zero is valid */
586 			break;
587 		}
588 		case SquareKernel:
589 		case DiamondKernel:
590 		case OctagonKernel:
591 		case DiskKernel:
592 		case PlusKernel:
593 		case CrossKernel: {
594 			if ( (flags & HeightValue) == 0 ) {
595 				geometry_info->sigma = 1.0;    /* Default scale = 1.0, zero is valid */
596 			}
597 			break;
598 		}
599 		case RingKernel: {
600 			if ((flags & XValue) == 0) {
601 				geometry_info->xi = 1.0;       /* Default scale = 1.0, zero is valid */
602 			}
603 			break;
604 		}
605 		case RectangleKernel: {    /* Rectangle - set size defaults */
606 			if ((flags & WidthValue) == 0) { /* if no width then */
607 				geometry_info->rho = geometry_info->sigma;         /* then  width = height */
608 			}
609 			if (geometry_info->rho < 1.0) {            /* if width too small */
610 				geometry_info->rho = 3;                 /* then  width = 3 */
611 			}
612 			if (geometry_info->sigma < 1.0) {          /* if height too small */
613 				geometry_info->sigma = geometry_info->rho;         /* then  height = width */
614 			}
615 			if ((flags & XValue) == 0) {    /* center offset if not defined */
616 				geometry_info->xi = (double)(((ssize_t)geometry_info->rho-1)/2);
617 			}
618 			if ((flags & YValue) == 0) {
619 				geometry_info->psi = (double)(((ssize_t)geometry_info->sigma-1)/2);
620 			}
621 			break;
622 		}
623 		/* Distance Kernel Defaults */
624 		case ChebyshevKernel:
625 		case ManhattanKernel:
626 		case OctagonalKernel:
627 		case EuclideanKernel: {
628 			if ((flags & HeightValue) == 0) {           /* no distance scale */
629 				geometry_info->sigma = 100.0;                       /* default distance scaling */
630 			}
631 			else if ((flags & AspectValue ) != 0) {     /* '!' flag */
632 				geometry_info->sigma = QuantumRange/(geometry_info->sigma+1); /* maximum pixel distance */
633 			}
634 			else if ((flags & PercentValue ) != 0) {    /* '%' flag */
635 				geometry_info->sigma *= QuantumRange/100.0;         /* percentage of color range */
636 			}
637 			break;
638 		}
639 		default: {
640 			break;
641 		}
642 	}
643 }
644 
645 /* {{{ proto ImagickKernel ImagickKernel::fromBuiltin(type, string)
646  Create a kernel from a builtin in kernel. See http://www.imagemagick.org/Usage/morphology/#kernel for examples. Currently the 'rotation' symbols are not supported. Example:
647  $diamondKernel = ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DIAMOND, "2");
648 */
PHP_METHOD(ImagickKernel,fromBuiltin)649 PHP_METHOD(ImagickKernel, fromBuiltin)
650 {
651 	im_long kernel_type;
652 	GeometryInfo geometry_info = {
653 		0, //rho,
654 		0, //sigma,
655 		0, //xi,
656 		0, //psi,
657 		0, //chi;
658 	};
659 	KernelInfo *kernel_info;
660 	char *string;
661 	IM_LEN_TYPE string_len;
662 	GeometryFlags flags;
663 #if MagickLibVersion >= 0x700
664 	ExceptionInfo *_exception_info = NULL;
665 #endif
666 
667 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &kernel_type, &string, &string_len) == FAILURE) {
668 		return;
669 	}
670 
671 	flags = ParseGeometry(string, &geometry_info);
672 	imagick_fiddle_with_geometry_info(kernel_type, flags, &geometry_info);
673 
674 #if MagickLibVersion >= 0x700
675 	//TODO - inspect exception info
676 	kernel_info = AcquireKernelBuiltIn(kernel_type, &geometry_info, _exception_info);
677 #else
678 	kernel_info = AcquireKernelBuiltIn(kernel_type, &geometry_info);
679 #endif
680 	createKernelZval(return_value, kernel_info TSRMLS_CC);
681 
682 	return;
683 }
684 
685 /* }}} */
686 
687 
688 /* {{{ proto void ImagickKernel::addKernel(ImagickKernel kernel)
689 	Attach another kernel to this kernel to allow them to both be applied in a single morphology or filter function. Returns the new combined kernel.
690 */
PHP_METHOD(ImagickKernel,addKernel)691 PHP_METHOD(ImagickKernel, addKernel)
692 {
693 	zval *objvar;
694 	KernelInfo *kernel_info_add_clone;
695 
696 	KernelInfo *kernel_info;
697 	KernelInfo *kernel_info_target;
698 
699 	php_imagickkernel_object *kernel;
700 	php_imagickkernel_object *internp;
701 
702 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &objvar, php_imagickkernel_sc_entry) == FAILURE) {
703 		return;
704 	}
705 
706 	kernel = Z_IMAGICKKERNEL_P(objvar);
707 	internp = Z_IMAGICKKERNEL_P(getThis());
708 
709 	if (kernel->kernel_info == NULL) {
710 		zend_throw_exception(php_imagickkernel_exception_class_entry, "ImagickKernel is empty, cannot be used", (long)0 TSRMLS_CC);
711 		RETURN_NULL();
712 	}
713 
714 	kernel_info = internp->kernel_info;
715 	do {
716 		kernel_info_target = kernel_info;
717 		kernel_info = kernel_info->next;
718 	} while (kernel_info != NULL);
719 
720 	kernel_info_add_clone = CloneKernelInfo(kernel->kernel_info);
721 	kernel_info_target->next = kernel_info_add_clone;
722 
723 	return;
724 }
725 /* }}} */
726 
727 
728 /* {{{ proto ImagickKernel[] ImagickKernel::separate(void)
729 	Separates a linked set of kernels and returns an array of ImagickKernels.
730 */
PHP_METHOD(ImagickKernel,separate)731 PHP_METHOD(ImagickKernel, separate)
732 {
733 	php_imagickkernel_object *internp;
734 	KernelInfo *kernel_info;
735 	KernelInfo *kernel_info_copy;
736 	int number_values;
737 	KernelValueType * values_copy;
738 
739 #if PHP_VERSION_ID >= 70000
740 	zval separate_object;
741 #else
742 	zval *separate_object;
743 #endif
744 
745 	if (zend_parse_parameters_none() == FAILURE) {
746 		return;
747 	}
748 
749 	internp = Z_IMAGICKKERNEL_P(getThis());
750 	IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
751 	kernel_info = internp->kernel_info;
752 
753 	array_init(return_value);
754 
755 	while (kernel_info != NULL) {
756 		number_values = kernel_info->width * kernel_info->height;
757 		values_copy = (KernelValueType *)AcquireAlignedMemory(kernel_info->width, kernel_info->height*sizeof(KernelValueType));
758 		memcpy(values_copy, kernel_info->values, number_values * sizeof(KernelValueType));
759 
760 		kernel_info_copy = imagick_createKernel(
761 			values_copy,
762 			kernel_info->width,
763 			kernel_info->height,
764 			kernel_info->x,
765 			kernel_info->y
766 		);
767 
768 #if PHP_VERSION_ID >= 70000
769 		createKernelZval(&separate_object, kernel_info_copy TSRMLS_CC);
770 		add_next_index_zval(return_value, &separate_object);
771 #else
772 		MAKE_STD_ZVAL(separate_object);
773 		createKernelZval(separate_object, kernel_info_copy TSRMLS_CC);
774 		add_next_index_zval(return_value, separate_object);
775 #endif
776 		kernel_info = kernel_info->next;
777 	}
778 
779 	return;
780 }
781 /* }}} */
782 
783 
784 /* {{{ proto [] ImagickKernel::getMatrix(void)
785 	Get the 2d matrix of values used in this kernel. The elements are either
786 	float for elements that are used or 'false' if the element should be skipped.
787 */
PHP_METHOD(ImagickKernel,getMatrix)788 PHP_METHOD(ImagickKernel, getMatrix)
789 {
790 	php_imagickkernel_object *internp;
791 
792 	if (zend_parse_parameters_none() == FAILURE) {
793 		return;
794 	}
795 
796 	internp = Z_IMAGICKKERNEL_P(getThis());
797 	IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
798 
799 	array_init(return_value);
800 	php_imagickkernelvalues_to_zval(return_value, internp->kernel_info);
801 
802 	return;
803 }
804 /* }}} */
805 
806 
807 
808 
809 
810 /* {{{ proto [] ImagickKernel::scale(float scaling_factor[, int NORMALIZE_KERNEL_FLAG])
811 	ScaleKernelInfo() scales the given kernel list by the given amount, with or without
812 	normalization of the sum of the kernel values (as per given flags).
813 
814 	The exact behaviour of this function depends on the normalization type being used
815 	please see http://www.imagemagick.org/api/morphology.php#ScaleKernelInfo for details.
816 
817 	Flag should be one of NORMALIZE_KERNEL_VALUE, NORMALIZE_KERNEL_CORRELATE,
818 	NORMALIZE_KERNEL_PERCENT or not set.
819 */
PHP_METHOD(ImagickKernel,scale)820 PHP_METHOD(ImagickKernel, scale)
821 {
822 	php_imagickkernel_object *internp;
823 	double scale;
824 	im_long normalize_flag = 0;
825 
826 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|l", &scale, &normalize_flag) == FAILURE) {
827 		return;
828 	}
829 
830 	internp = Z_IMAGICKKERNEL_P(getThis());
831 	IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
832 	ScaleKernelInfo(internp->kernel_info, scale, normalize_flag);
833 
834 	return;
835 }
836 /* }}} */
837 
838 
839 
840 
841 /* {{{ proto [] ImagickKernel::addUnityKernel(float scale)
842 	Adds a given amount of the 'Unity' Convolution Kernel to the given pre-scaled
843 	and normalized Kernel. This in effect adds that amount of the original image
844 	into the resulting convolution kernel. The resulting effect is to convert the
845 	defined kernels into blended soft-blurs, unsharp kernels or into sharpening kernels.
846 */
PHP_METHOD(ImagickKernel,addUnityKernel)847 PHP_METHOD(ImagickKernel, addUnityKernel)
848 {
849 	php_imagickkernel_object *internp;
850 	double scale;
851 
852 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &scale) == FAILURE) {
853 		return;
854 	}
855 
856 	internp = Z_IMAGICKKERNEL_P(getThis());
857 	IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
858 	UnityAddKernelInfo(internp->kernel_info, scale);
859 
860 	return;
861 }
862 /* }}} */
863 
864 #endif
865