857 lines
24 KiB
C
857 lines
24 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 5 / Imagick |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Dan Ackroyd <danack@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "php_imagick.h"
|
|
#include "php_imagick_defs.h"
|
|
#include "php_imagick_macros.h"
|
|
#include "php_imagick_helpers.h"
|
|
|
|
#ifdef IMAGICK_WITH_KERNEL
|
|
|
|
#if MagickLibVersion < 0x691
|
|
// These function defines are required currently as the functions are
|
|
// currently not available in the public ImageMagick header files
|
|
// This is being fixed for the next version of ImageMagick.
|
|
Image
|
|
*MorphologyApply(const Image *,const ChannelType,const MorphologyMethod,
|
|
const ssize_t,const KernelInfo *,const CompositeOperator,const double,
|
|
ExceptionInfo *);
|
|
|
|
void
|
|
ScaleKernelInfo(KernelInfo *,const double,const GeometryFlags),
|
|
UnityAddKernelInfo(KernelInfo *,const double),
|
|
ZeroKernelNans(KernelInfo *);
|
|
#endif
|
|
|
|
|
|
static void php_imagickkernelvalues_to_zval(zval *zv, KernelInfo *kernel_info) {
|
|
int count;
|
|
double value;
|
|
unsigned int x, y;
|
|
#if PHP_VERSION_ID >= 70000
|
|
zval row;
|
|
#else
|
|
zval *row;
|
|
#endif
|
|
|
|
zval *p_row;
|
|
|
|
count = 0;
|
|
|
|
for (y=0; y<kernel_info->height ; y++) {
|
|
#if PHP_VERSION_ID >= 70000
|
|
p_row = &row;
|
|
#else
|
|
MAKE_STD_ZVAL(row);
|
|
p_row = row;
|
|
#endif
|
|
|
|
array_init(p_row);
|
|
for (x=0; x<kernel_info->width ; x++) {
|
|
value = kernel_info->values[count];
|
|
count++;
|
|
|
|
//nan is not equal to itself
|
|
if (value != value) {
|
|
//this will be broken by some compilers - need to investigate more...
|
|
add_next_index_bool(p_row, 0);
|
|
}
|
|
else {
|
|
add_next_index_double(p_row, value);
|
|
}
|
|
}
|
|
|
|
add_next_index_zval(zv, p_row);
|
|
}
|
|
}
|
|
|
|
|
|
#if PHP_VERSION_ID >= 80000
|
|
HashTable* php_imagickkernel_get_debug_info(zend_object *obj, int *is_temp TSRMLS_DC) /* {{{ */
|
|
#else
|
|
HashTable* php_imagickkernel_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
|
|
#endif
|
|
{
|
|
php_imagickkernel_object *internp;
|
|
HashTable *debug_info;
|
|
KernelInfo *kernel_info;
|
|
#if PHP_VERSION_ID >= 70000
|
|
zval matrix;
|
|
#else
|
|
zval *matrix;
|
|
#endif
|
|
|
|
*is_temp = 1; //var_dump will destroy the hashtable
|
|
|
|
#if PHP_VERSION_ID >= 80000
|
|
internp = php_imagickkernel_fetch_object(obj);
|
|
#else
|
|
internp = Z_IMAGICKKERNEL_P(obj);
|
|
#endif
|
|
kernel_info = internp->kernel_info;
|
|
|
|
ALLOC_HASHTABLE(debug_info);
|
|
ZEND_INIT_SYMTABLE_EX(debug_info, 1, 0);
|
|
|
|
while (kernel_info != NULL) {
|
|
#if PHP_VERSION_ID >= 70000
|
|
array_init(&matrix);
|
|
php_imagickkernelvalues_to_zval(&matrix, kernel_info);
|
|
zend_hash_next_index_insert(debug_info, &matrix);
|
|
#else
|
|
MAKE_STD_ZVAL(matrix);
|
|
array_init(matrix);
|
|
php_imagickkernelvalues_to_zval(matrix, kernel_info);
|
|
zend_hash_next_index_insert(debug_info, &matrix, sizeof(zval *), NULL);
|
|
#endif
|
|
kernel_info = kernel_info->next;
|
|
}
|
|
|
|
return debug_info;
|
|
}
|
|
|
|
|
|
static void im_CalcKernelMetaData(KernelInfo *kernel) {
|
|
size_t i;
|
|
|
|
kernel->minimum = kernel->maximum = 0.0;
|
|
kernel->negative_range = kernel->positive_range = 0.0;
|
|
|
|
for (i=0; i < (kernel->width*kernel->height); i++) {
|
|
if (fabs(kernel->values[i]) < MagickEpsilon) {
|
|
kernel->values[i] = 0.0;
|
|
}
|
|
if (kernel->values[i] < 0) {
|
|
kernel->negative_range += kernel->values[i];
|
|
}
|
|
else {
|
|
kernel->positive_range += kernel->values[i];
|
|
}
|
|
if (kernel->values[i] < kernel->minimum) {
|
|
kernel->minimum = kernel->values[i];
|
|
}
|
|
|
|
if (kernel->values[i] > kernel->maximum) {
|
|
kernel->maximum = kernel->values[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if MagickLibVersion > 0x661
|
|
static KernelInfo *imagick_createKernel(KernelValueType *values, size_t width, size_t height, size_t origin_x, size_t origin_y)
|
|
{
|
|
KernelInfo *kernel_info;
|
|
|
|
#if MagickLibVersion >= 0x700
|
|
unsigned int i;
|
|
ExceptionInfo *_exception_info = (ExceptionInfo *) NULL;
|
|
//TODO - inspect exception info
|
|
kernel_info=AcquireKernelInfo(NULL, _exception_info);
|
|
#else
|
|
kernel_info=AcquireKernelInfo(NULL);
|
|
#endif
|
|
if (kernel_info == (KernelInfo *) NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
kernel_info->width = width;
|
|
kernel_info->height = height;
|
|
|
|
kernel_info->x = origin_x;
|
|
kernel_info->y = origin_y;
|
|
|
|
//Need to free old values?
|
|
if (kernel_info->values != NULL) {
|
|
RelinquishAlignedMemory(kernel_info->values);
|
|
}
|
|
|
|
#if MagickLibVersion >= 0x700
|
|
kernel_info->values = (MagickRealType *)AcquireAlignedMemory(width*height, sizeof(MagickRealType));
|
|
|
|
for (i=0; i<width*height;i++) {
|
|
kernel_info->values[i] = (MagickRealType)values[i];
|
|
}
|
|
// The values are not going to be used so free them
|
|
RelinquishAlignedMemory(values);
|
|
|
|
#else
|
|
// For IM 6, the values get used
|
|
kernel_info->values = values;
|
|
#endif
|
|
|
|
im_CalcKernelMetaData(kernel_info);
|
|
|
|
return kernel_info;
|
|
}
|
|
#endif
|
|
|
|
static void createKernelZval(zval *pzval, KernelInfo *kernel_info TSRMLS_DC) {
|
|
php_imagickkernel_object *intern_return;
|
|
|
|
object_init_ex(pzval, php_imagickkernel_sc_entry);
|
|
intern_return = Z_IMAGICKKERNEL_P(pzval);
|
|
intern_return->kernel_info = kernel_info;
|
|
}
|
|
|
|
|
|
/* {{{ proto ImagickKernel ImagickKernel::__construct()
|
|
The ImagickKernel constructor
|
|
*/
|
|
PHP_METHOD(ImagickKernel, __construct)
|
|
{
|
|
// This suppresses an 'unused parameter' warning.
|
|
(void)return_value;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
// this method is private.
|
|
}
|
|
/* }}} */
|
|
|
|
#define MATRIX_ERROR_EMPTY "Cannot create kernel, matrix is empty."
|
|
#define MATRIX_ERROR_UNEVEN "Values must be matrix, with the same number of columns in each row."
|
|
#define MATRIX_ERROR_BAD_VALUE "Only numbers or false are valid values in a kernel matrix."
|
|
#define MATRIX_ORIGIN_REQUIRED "For kernels with even numbered rows or columns, the origin position must be specified."
|
|
|
|
/* {{{ proto ImagickKernel ImagickKernel::fromMatrix(array matrix, [array origin])
|
|
Create a kernel from an 2d matrix of values. Each value should either be a float
|
|
(if the element should be used) or 'false' if the element should be skipped. For
|
|
matrixes that are odd sizes in both dimensions the the origin pixel will default
|
|
to the centre of the kernel. For all other kernel sizes the origin pixel must be specified.
|
|
*/
|
|
#if PHP_VERSION_ID >= 70000
|
|
PHP_METHOD(ImagickKernel, fromMatrix)
|
|
{
|
|
zval *kernel_array;
|
|
zval *origin_array;
|
|
HashTable *inner_array;
|
|
KernelInfo *kernel_info;
|
|
unsigned long num_rows, num_columns = 0;
|
|
unsigned int previous_num_columns = (unsigned int)-1;
|
|
unsigned int row, column;
|
|
|
|
zval *pzval_outer;
|
|
zval *pzval_inner;
|
|
|
|
int count = 0;
|
|
size_t origin_x, origin_y;
|
|
zval *tmp;
|
|
|
|
KernelValueType *values = NULL;
|
|
double notanumber = sqrt((double)-1.0); /* Special Value : Not A Number */
|
|
|
|
count = 0;
|
|
row = 0;
|
|
origin_array = NULL;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a!", &kernel_array, &origin_array) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
num_rows = zend_hash_num_elements(Z_ARRVAL_P(kernel_array));
|
|
|
|
if (num_rows == 0) {
|
|
//error - array has zero elements.
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
|
|
for (row=0 ; row<num_rows ; row++) {
|
|
pzval_outer = zend_hash_index_find(Z_ARRVAL_P(kernel_array), row);
|
|
if (pzval_outer == NULL) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
ZVAL_DEREF(pzval_outer);
|
|
|
|
column = 0;
|
|
|
|
if (Z_TYPE_P(pzval_outer) == IS_ARRAY ) {
|
|
inner_array = Z_ARRVAL_P(pzval_outer);
|
|
num_columns = zend_hash_num_elements(inner_array);
|
|
|
|
if (num_columns == 0) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (values == NULL) {
|
|
values = (KernelValueType *)AcquireAlignedMemory(num_columns, num_rows*sizeof(KernelValueType));
|
|
}
|
|
|
|
if (previous_num_columns != ((unsigned int)-1)) {
|
|
if (previous_num_columns != num_columns) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
previous_num_columns = num_columns;
|
|
|
|
for (column=0; column<num_columns ; column++) {
|
|
pzval_inner = zend_hash_index_find(inner_array, column);
|
|
if (pzval_inner == NULL) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
ZVAL_DEREF(pzval_inner);
|
|
if (Z_TYPE_P(pzval_inner) == IS_DOUBLE) {
|
|
//It's a float lets use it.
|
|
values[count] = Z_DVAL_P(pzval_inner);
|
|
}
|
|
else if (Z_TYPE_P(pzval_inner) == IS_LONG) {
|
|
//It's a long lets use it.
|
|
values[count] = (float)Z_LVAL_P(pzval_inner);
|
|
}
|
|
else if (Z_TYPE_P(pzval_inner) == IS_FALSE) {
|
|
//It's false, use nan
|
|
values[count] = notanumber;
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_BAD_VALUE TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (origin_array == NULL) {
|
|
if (((num_columns%2) == 0) || ((num_rows%2) == 0)) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
origin_x = (num_columns - 1) >> 1;
|
|
origin_y = (num_rows - 1) >> 1;
|
|
}
|
|
else {
|
|
HashTable *origin_array_ht;
|
|
origin_array_ht = Z_ARRVAL_P(origin_array);
|
|
|
|
// parse the origin_x
|
|
tmp = zend_hash_index_find(origin_array_ht, 0);
|
|
if (tmp != NULL) {
|
|
ZVAL_DEREF(tmp);
|
|
origin_x = Z_LVAL_P(tmp);
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
// origin_x is unsigned, so checking for > num_columns, also
|
|
// checks for < 0
|
|
if (origin_x>=num_columns) {
|
|
zend_throw_exception_ex(
|
|
php_imagickkernel_exception_class_entry,
|
|
5 TSRMLS_CC,
|
|
"origin_x for matrix is outside bounds of columns: %zu",
|
|
origin_x
|
|
);
|
|
goto cleanup;
|
|
}
|
|
|
|
// parse the origin_y
|
|
tmp = zend_hash_index_find(origin_array_ht, 1);
|
|
if (tmp != NULL) {
|
|
ZVAL_DEREF(tmp);
|
|
origin_y = Z_LVAL_P(tmp);
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
// origin_y is unsigned, so checking for > num_rows, also
|
|
// checks for < 0
|
|
if (origin_y>=num_rows) {
|
|
zend_throw_exception_ex(
|
|
php_imagickkernel_exception_class_entry,
|
|
5 TSRMLS_CC,
|
|
"origin_y for matrix is outside bounds of rows: %zu",
|
|
origin_x
|
|
);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
kernel_info = imagick_createKernel(values, num_columns, num_rows, origin_x, origin_y);
|
|
createKernelZval(return_value, kernel_info TSRMLS_CC);
|
|
|
|
// values are freed / used by imagick_createKernel so do not try to free them
|
|
return;
|
|
|
|
cleanup:
|
|
if (values != NULL) {
|
|
RelinquishAlignedMemory(values);
|
|
}
|
|
}
|
|
|
|
#else // PHP 5
|
|
|
|
|
|
PHP_METHOD(ImagickKernel, fromMatrix)
|
|
{
|
|
zval *kernel_array;
|
|
zval *origin_array;
|
|
HashTable *inner_array;
|
|
KernelInfo *kernel_info;
|
|
unsigned long num_rows, num_columns = 0;
|
|
unsigned int previous_num_columns = (unsigned int)-1;
|
|
unsigned int row, column;
|
|
|
|
HashTable *origin_array_ht;
|
|
zval **ppzval_outer;
|
|
zval **ppzval_inner;
|
|
|
|
int count = 0;
|
|
size_t origin_x, origin_y;
|
|
zval **tmp;
|
|
|
|
KernelValueType *values = NULL;
|
|
double notanumber = sqrt((double)-1.0); /* Special Value : Not A Number */
|
|
|
|
previous_num_columns = -1;
|
|
count = 0;
|
|
row = 0;
|
|
origin_array = NULL;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a!", &kernel_array, &origin_array) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
num_rows = zend_hash_num_elements(Z_ARRVAL_P(kernel_array));
|
|
|
|
if (num_rows == 0) {
|
|
//error - array has zero elements.
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
for (row=0 ; row<num_rows ; row++) {
|
|
if (zend_hash_index_find(Z_ARRVAL_P(kernel_array), row, (void **) &ppzval_outer) != SUCCESS) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
column = 0;
|
|
|
|
if (Z_TYPE_PP(ppzval_outer) == IS_ARRAY ) {
|
|
//parse this row
|
|
inner_array = Z_ARRVAL_PP(ppzval_outer);
|
|
num_columns = zend_hash_num_elements(inner_array);
|
|
|
|
if (num_columns == 0) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_EMPTY TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (values == NULL) {
|
|
values = (KernelValueType *)AcquireAlignedMemory(num_columns, num_rows*sizeof(KernelValueType));
|
|
}
|
|
|
|
if (previous_num_columns != ((unsigned int)-1)) {
|
|
if (previous_num_columns != num_columns) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
previous_num_columns = num_columns;
|
|
|
|
for (column=0; column<num_columns ; column++) {
|
|
if (zend_hash_index_find(inner_array, column, (void **) &ppzval_inner) != SUCCESS) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (Z_TYPE_PP(ppzval_inner) == IS_DOUBLE) {
|
|
//It's a float lets use it.
|
|
values[count] = Z_DVAL_PP(ppzval_inner);
|
|
}
|
|
else if (Z_TYPE_PP(ppzval_inner) == IS_LONG) {
|
|
//It's a long lets use it.
|
|
values[count] = (float)Z_LVAL_PP(ppzval_inner);
|
|
}
|
|
else if (Z_TYPE_PP(ppzval_inner) == IS_BOOL && Z_BVAL_PP(ppzval_inner) == 0) {
|
|
//It's false, use nan
|
|
values[count] = notanumber;
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_BAD_VALUE TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ERROR_UNEVEN TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (origin_array == NULL) {
|
|
if (((num_columns%2) == 0) || ((num_rows%2) == 0)) {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
origin_x = (num_columns - 1) >> 1;
|
|
origin_y = (num_rows - 1) >> 1;
|
|
}
|
|
else {
|
|
origin_array_ht = Z_ARRVAL_P(origin_array);
|
|
|
|
// parse and check the origin_x
|
|
if (zend_hash_index_find(origin_array_ht, 0, (void**)&tmp) == SUCCESS) {
|
|
origin_x = Z_LVAL_PP(tmp);
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
// origin_x is unsigned, so checking for > num_columns, also
|
|
// checks for < 0
|
|
if (origin_x>=num_columns) {
|
|
zend_throw_exception_ex(
|
|
php_imagickkernel_exception_class_entry,
|
|
5 TSRMLS_CC,
|
|
"origin_x for matrix is outside bounds of columns: %d",
|
|
origin_x
|
|
);
|
|
goto cleanup;
|
|
}
|
|
|
|
// parse and check the origin_y
|
|
if (zend_hash_index_find(origin_array_ht, 1, (void**)&tmp) == SUCCESS) {
|
|
origin_y = Z_LVAL_PP(tmp);
|
|
}
|
|
else {
|
|
php_imagick_throw_exception(IMAGICKKERNEL_CLASS, MATRIX_ORIGIN_REQUIRED TSRMLS_CC);
|
|
goto cleanup;
|
|
}
|
|
|
|
// origin_y is unsigned, so checking for > num_rows, also
|
|
// checks for < 0
|
|
if (origin_y>=num_rows) {
|
|
zend_throw_exception_ex(
|
|
php_imagickkernel_exception_class_entry,
|
|
5 TSRMLS_CC,
|
|
"origin_y for matrix is outside bounds of rows: %d",
|
|
origin_y
|
|
);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
kernel_info = imagick_createKernel(values, num_columns, num_rows, origin_x, origin_y);
|
|
createKernelZval(return_value, kernel_info TSRMLS_CC);
|
|
|
|
return;
|
|
|
|
cleanup:
|
|
if (values != NULL) {
|
|
RelinquishAlignedMemory(values);
|
|
}
|
|
}
|
|
#endif //end of zend_engine_3
|
|
/* }}} */
|
|
|
|
static void imagick_fiddle_with_geometry_info(ssize_t type, GeometryFlags flags, GeometryInfo *geometry_info) {
|
|
|
|
/* special handling of missing values in input string */
|
|
switch( type ) {
|
|
/* Shape Kernel Defaults */
|
|
case UnityKernel: {
|
|
if ((flags & WidthValue) == 0)
|
|
geometry_info->rho = 1.0; /* Default scale = 1.0, zero is valid */
|
|
break;
|
|
}
|
|
case SquareKernel:
|
|
case DiamondKernel:
|
|
case OctagonKernel:
|
|
case DiskKernel:
|
|
case PlusKernel:
|
|
case CrossKernel: {
|
|
if ( (flags & HeightValue) == 0 ) {
|
|
geometry_info->sigma = 1.0; /* Default scale = 1.0, zero is valid */
|
|
}
|
|
break;
|
|
}
|
|
case RingKernel: {
|
|
if ((flags & XValue) == 0) {
|
|
geometry_info->xi = 1.0; /* Default scale = 1.0, zero is valid */
|
|
}
|
|
break;
|
|
}
|
|
case RectangleKernel: { /* Rectangle - set size defaults */
|
|
if ((flags & WidthValue) == 0) { /* if no width then */
|
|
geometry_info->rho = geometry_info->sigma; /* then width = height */
|
|
}
|
|
if (geometry_info->rho < 1.0) { /* if width too small */
|
|
geometry_info->rho = 3; /* then width = 3 */
|
|
}
|
|
if (geometry_info->sigma < 1.0) { /* if height too small */
|
|
geometry_info->sigma = geometry_info->rho; /* then height = width */
|
|
}
|
|
if ((flags & XValue) == 0) { /* center offset if not defined */
|
|
geometry_info->xi = (double)(((ssize_t)geometry_info->rho-1)/2);
|
|
}
|
|
if ((flags & YValue) == 0) {
|
|
geometry_info->psi = (double)(((ssize_t)geometry_info->sigma-1)/2);
|
|
}
|
|
break;
|
|
}
|
|
/* Distance Kernel Defaults */
|
|
case ChebyshevKernel:
|
|
case ManhattanKernel:
|
|
case OctagonalKernel:
|
|
case EuclideanKernel: {
|
|
if ((flags & HeightValue) == 0) { /* no distance scale */
|
|
geometry_info->sigma = 100.0; /* default distance scaling */
|
|
}
|
|
else if ((flags & AspectValue ) != 0) { /* '!' flag */
|
|
geometry_info->sigma = QuantumRange/(geometry_info->sigma+1); /* maximum pixel distance */
|
|
}
|
|
else if ((flags & PercentValue ) != 0) { /* '%' flag */
|
|
geometry_info->sigma *= QuantumRange/100.0; /* percentage of color range */
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* {{{ proto ImagickKernel ImagickKernel::fromBuiltin(type, string)
|
|
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:
|
|
$diamondKernel = ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DIAMOND, "2");
|
|
*/
|
|
PHP_METHOD(ImagickKernel, fromBuiltin)
|
|
{
|
|
im_long kernel_type;
|
|
GeometryInfo geometry_info = {
|
|
0, //rho,
|
|
0, //sigma,
|
|
0, //xi,
|
|
0, //psi,
|
|
0, //chi;
|
|
};
|
|
KernelInfo *kernel_info;
|
|
char *string;
|
|
IM_LEN_TYPE string_len;
|
|
GeometryFlags flags;
|
|
#if MagickLibVersion >= 0x700
|
|
ExceptionInfo *_exception_info = NULL;
|
|
#endif
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &kernel_type, &string, &string_len) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
flags = ParseGeometry(string, &geometry_info);
|
|
imagick_fiddle_with_geometry_info(kernel_type, flags, &geometry_info);
|
|
|
|
#if MagickLibVersion >= 0x700
|
|
//TODO - inspect exception info
|
|
kernel_info = AcquireKernelBuiltIn(kernel_type, &geometry_info, _exception_info);
|
|
#else
|
|
kernel_info = AcquireKernelBuiltIn(kernel_type, &geometry_info);
|
|
#endif
|
|
createKernelZval(return_value, kernel_info TSRMLS_CC);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto void ImagickKernel::addKernel(ImagickKernel kernel)
|
|
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.
|
|
*/
|
|
PHP_METHOD(ImagickKernel, addKernel)
|
|
{
|
|
zval *objvar;
|
|
KernelInfo *kernel_info_add_clone;
|
|
|
|
KernelInfo *kernel_info;
|
|
KernelInfo *kernel_info_target;
|
|
|
|
php_imagickkernel_object *kernel;
|
|
php_imagickkernel_object *internp;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &objvar, php_imagickkernel_sc_entry) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
kernel = Z_IMAGICKKERNEL_P(objvar);
|
|
internp = Z_IMAGICKKERNEL_P(getThis());
|
|
|
|
if (kernel->kernel_info == NULL) {
|
|
zend_throw_exception(php_imagickkernel_exception_class_entry, "ImagickKernel is empty, cannot be used", (long)0 TSRMLS_CC);
|
|
RETURN_NULL();
|
|
}
|
|
|
|
kernel_info = internp->kernel_info;
|
|
do {
|
|
kernel_info_target = kernel_info;
|
|
kernel_info = kernel_info->next;
|
|
} while (kernel_info != NULL);
|
|
|
|
kernel_info_add_clone = CloneKernelInfo(kernel->kernel_info);
|
|
kernel_info_target->next = kernel_info_add_clone;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto ImagickKernel[] ImagickKernel::separate(void)
|
|
Separates a linked set of kernels and returns an array of ImagickKernels.
|
|
*/
|
|
PHP_METHOD(ImagickKernel, separate)
|
|
{
|
|
php_imagickkernel_object *internp;
|
|
KernelInfo *kernel_info;
|
|
KernelInfo *kernel_info_copy;
|
|
int number_values;
|
|
KernelValueType * values_copy;
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
zval separate_object;
|
|
#else
|
|
zval *separate_object;
|
|
#endif
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
internp = Z_IMAGICKKERNEL_P(getThis());
|
|
IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
|
|
kernel_info = internp->kernel_info;
|
|
|
|
array_init(return_value);
|
|
|
|
while (kernel_info != NULL) {
|
|
number_values = kernel_info->width * kernel_info->height;
|
|
// The values are created because they can by used by imagick_createKernel which takes their ownership
|
|
values_copy = (KernelValueType *)AcquireAlignedMemory(kernel_info->width, kernel_info->height*sizeof(KernelValueType));
|
|
memcpy(values_copy, kernel_info->values, number_values * sizeof(KernelValueType));
|
|
|
|
kernel_info_copy = imagick_createKernel(
|
|
values_copy,
|
|
kernel_info->width,
|
|
kernel_info->height,
|
|
kernel_info->x,
|
|
kernel_info->y
|
|
);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
createKernelZval(&separate_object, kernel_info_copy TSRMLS_CC);
|
|
add_next_index_zval(return_value, &separate_object);
|
|
#else
|
|
MAKE_STD_ZVAL(separate_object);
|
|
createKernelZval(separate_object, kernel_info_copy TSRMLS_CC);
|
|
add_next_index_zval(return_value, separate_object);
|
|
#endif
|
|
kernel_info = kernel_info->next;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ proto [] ImagickKernel::getMatrix(void)
|
|
Get the 2d matrix of values used in this kernel. The elements are either
|
|
float for elements that are used or 'false' if the element should be skipped.
|
|
*/
|
|
PHP_METHOD(ImagickKernel, getMatrix)
|
|
{
|
|
php_imagickkernel_object *internp;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
internp = Z_IMAGICKKERNEL_P(getThis());
|
|
IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
|
|
|
|
array_init(return_value);
|
|
php_imagickkernelvalues_to_zval(return_value, internp->kernel_info);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
|
|
|
|
|
|
/* {{{ proto [] ImagickKernel::scale(float scaling_factor[, int NORMALIZE_KERNEL_FLAG])
|
|
ScaleKernelInfo() scales the given kernel list by the given amount, with or without
|
|
normalization of the sum of the kernel values (as per given flags).
|
|
|
|
The exact behaviour of this function depends on the normalization type being used
|
|
please see http://www.imagemagick.org/api/morphology.php#ScaleKernelInfo for details.
|
|
|
|
Flag should be one of NORMALIZE_KERNEL_VALUE, NORMALIZE_KERNEL_CORRELATE,
|
|
NORMALIZE_KERNEL_PERCENT or not set.
|
|
*/
|
|
PHP_METHOD(ImagickKernel, scale)
|
|
{
|
|
php_imagickkernel_object *internp;
|
|
double scale;
|
|
im_long normalize_flag = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|l", &scale, &normalize_flag) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
(void)return_value;
|
|
|
|
internp = Z_IMAGICKKERNEL_P(getThis());
|
|
IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
|
|
ScaleKernelInfo(internp->kernel_info, scale, normalize_flag);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
|
|
|
|
/* {{{ proto [] ImagickKernel::addUnityKernel(float scale)
|
|
Adds a given amount of the 'Unity' Convolution Kernel to the given pre-scaled
|
|
and normalized Kernel. This in effect adds that amount of the original image
|
|
into the resulting convolution kernel. The resulting effect is to convert the
|
|
defined kernels into blended soft-blurs, unsharp kernels or into sharpening kernels.
|
|
*/
|
|
PHP_METHOD(ImagickKernel, addUnityKernel)
|
|
{
|
|
php_imagickkernel_object *internp;
|
|
double scale;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &scale) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
(void)return_value;
|
|
|
|
internp = Z_IMAGICKKERNEL_P(getThis());
|
|
IMAGICK_KERNEL_NOT_NULL_EMPTY(internp);
|
|
UnityAddKernelInfo(internp->kernel_info, scale);
|
|
}
|
|
/* }}} */
|
|
|
|
#endif
|