├── CREDITS ├── EXPERIMENTAL ├── README.md ├── config.m4 ├── config.w32 ├── example.png ├── php_smartcrop.h ├── smartcrop.c ├── smartcrop.php └── tests └── 001.phpt /CREDITS: -------------------------------------------------------------------------------- 1 | smartcrop -------------------------------------------------------------------------------- /EXPERIMENTAL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xymak/php-smartcrop-extension/7e41b4852f768b55f04af3321b0d57d5d2279828/EXPERIMENTAL -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-smartcrop-extension 2 | 3 | smartcrop implementation in php-extension. 4 | 5 | smartcrop finds optimal crops for images, based on Jonas Wagner's [smartcrop.js](https://github.com/jwagner/smartcrop.js). 6 | 7 | ![Example](./example.png) 8 | 9 | ## Installation 10 | 11 | Make sure you have compiled PHP7 environment. 12 | 13 | Additionally PHP GD extension is needed to be load into PHP. 14 | 15 | This PHP extension can only work on Linux OS. 16 | 17 | You can install it by running: 18 | ``` 19 | cd /path/to/php/extention/source/directory 20 | https://github.com/xymak/php-smartcrop-extension.git 21 | cd php-smartcrop-extension 22 | phpize 23 | ./configure --prefix='/path/to/php/directory' --with-php-config='/path/to/php/config' 24 | make && make install 25 | echo "extension=smartcrop.so" >> /path/to/etc/php/php.d/smartcorp.ini 26 | ``` 27 | 28 | ## Example 29 | ``` 30 | check with-path 24 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 25 | dnl SEARCH_FOR="/include/smartcrop.h" # you most likely want to change this 26 | dnl if test -r $PHP_SMARTCROP/$SEARCH_FOR; then # path given as parameter 27 | dnl SMARTCROP_DIR=$PHP_SMARTCROP 28 | dnl else # search default path list 29 | dnl AC_MSG_CHECKING([for smartcrop files in default path]) 30 | dnl for i in $SEARCH_PATH ; do 31 | dnl if test -r $i/$SEARCH_FOR; then 32 | dnl SMARTCROP_DIR=$i 33 | dnl AC_MSG_RESULT(found in $i) 34 | dnl fi 35 | dnl done 36 | dnl fi 37 | dnl 38 | dnl if test -z "$SMARTCROP_DIR"; then 39 | dnl AC_MSG_RESULT([not found]) 40 | dnl AC_MSG_ERROR([Please reinstall the smartcrop distribution]) 41 | dnl fi 42 | 43 | dnl # --with-smartcrop -> add include path 44 | dnl PHP_ADD_INCLUDE($SMARTCROP_DIR/include) 45 | 46 | dnl # --with-smartcrop -> check for lib and symbol presence 47 | dnl LIBNAME=smartcrop # you may want to change this 48 | dnl LIBSYMBOL=smartcrop # you most likely want to change this 49 | 50 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 51 | dnl [ 52 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SMARTCROP_DIR/$PHP_LIBDIR, SMARTCROP_SHARED_LIBADD) 53 | dnl AC_DEFINE(HAVE_SMARTCROPLIB,1,[ ]) 54 | dnl ],[ 55 | dnl AC_MSG_ERROR([wrong smartcrop lib version or lib not found]) 56 | dnl ],[ 57 | dnl -L$SMARTCROP_DIR/$PHP_LIBDIR -lm 58 | dnl ]) 59 | dnl 60 | dnl PHP_SUBST(SMARTCROP_SHARED_LIBADD) 61 | 62 | PHP_NEW_EXTENSION(smartcrop, smartcrop.c , $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) 63 | PHP_ADD_EXTENSION_DEP(smartcrop, gd) 64 | fi 65 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("smartcrop", "for smartcrop support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("smartcrop", "enable smartcrop support", "no"); 9 | 10 | if (PHP_SMARTCROP != "no") { 11 | EXTENSION("smartcrop", "smartcrop.c", PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xymak/php-smartcrop-extension/7e41b4852f768b55f04af3321b0d57d5d2279828/example.png -------------------------------------------------------------------------------- /php_smartcrop.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2016 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_SMARTCROP_H 22 | #define PHP_SMARTCROP_H 23 | 24 | extern zend_module_entry smartcrop_module_entry; 25 | #define phpext_smartcrop_ptr &smartcrop_module_entry 26 | 27 | #define PHP_SMARTCROP_VERSION "0.1.0" /* Replace with version number for your extension */ 28 | 29 | #ifdef PHP_WIN32 30 | # define PHP_SMARTCROP_API __declspec(dllexport) 31 | #elif defined(__GNUC__) && __GNUC__ >= 4 32 | # define PHP_SMARTCROP_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_SMARTCROP_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | #include "TSRM.h" 39 | #endif 40 | 41 | /* 42 | Declare any global variables you may need between the BEGIN 43 | and END macros here: 44 | 45 | ZEND_BEGIN_MODULE_GLOBALS(smartcrop) 46 | zend_long global_value; 47 | char *global_string; 48 | ZEND_END_MODULE_GLOBALS(smartcrop) 49 | */ 50 | 51 | /* Always refer to the globals in your function as SMARTCROP_G(variable). 52 | You are encouraged to rename these macros something shorter, see 53 | examples in any other php module directory. 54 | */ 55 | #define SMARTCROP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(smartcrop, v) 56 | 57 | #if defined(ZTS) && defined(COMPILE_DL_SMARTCROP) 58 | ZEND_TSRMLS_CACHE_EXTERN() 59 | #endif 60 | 61 | #endif /* PHP_SMARTCROP_H */ 62 | 63 | 64 | /* 65 | * Local variables: 66 | * tab-width: 4 67 | * c-basic-offset: 4 68 | * End: 69 | * vim600: noet sw=4 ts=4 fdm=marker 70 | * vim<600: noet sw=4 ts=4 71 | */ 72 | -------------------------------------------------------------------------------- /smartcrop.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2016 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Xiyuan Mak | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "php_ini.h" 27 | #include "ext/standard/info.h" 28 | #include "php_smartcrop.h" 29 | #include "ext/gd/php_gd.h" 30 | #include 31 | #define OPTION_DETAIL_WEIGHT 0.2 32 | #define OPTION_SKIN_COLOR_R 0.78 33 | #define OPTION_SKIN_COLOR_G 0.57 34 | #define OPTION_SKIN_COLOR_B 0.44 35 | #define OPTION_SKIN_BIAS 0.01 36 | #define OPTION_SKIN_BRIGHTNESS_MIN 0.2 37 | #define OPTION_SKIN_BRIGHTNESS_MAX 1.0 38 | #define OPTION_SKIN_THRESHOLD 0.8 39 | #define OPTION_SKIN_WEIGHT 1.8 40 | #define OPTION_SATURATION_BRIGHTNESS_MIN 0.05 41 | #define OPTION_SATURATION_BRIGHTNESS_MAX 0.9 42 | #define OPTION_SATURATION_THRESHOLD 0.4 43 | #define OPTION_SATURATION_BIAS 0.2 44 | #define OPTION_SATURATION_WEIGHT 0.3 45 | #define OPTION_SCORE_DOWN_SAMPLE 8 46 | #define OPTION_STEP 8 47 | #define OPTION_SCALE_STEP 0.1 48 | #define OPTION_MIN_SCALE 1.0 49 | #define OPTION_MAX_SCALE 1.0 50 | #define OPTION_EDGE_RADIUS 0.4 51 | #define OPTION_EDGE_WEIGHT -20.0 52 | #define OPTION_OUTSIDE_IMPORTANCE -0.5 53 | #define OPTION_BOOST_WEIGHT 100 54 | #define OPTION_RULE_OF_THIRDS 1 55 | 56 | /* If you declare any globals in php_smartcrop.h uncomment this: 57 | ZEND_DECLARE_MODULE_GLOBALS(smartcrop) 58 | */ 59 | 60 | /* True global resources - no need for thread safety here */ 61 | static int le_smartcrop; 62 | 63 | /* {{{ PHP_INI 64 | */ 65 | /* Remove comments and fill if you need to have entries in php.ini 66 | PHP_INI_BEGIN() 67 | STD_PHP_INI_ENTRY("smartcrop.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_smartcrop_globals, smartcrop_globals) 68 | STD_PHP_INI_ENTRY("smartcrop.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_smartcrop_globals, smartcrop_globals) 69 | PHP_INI_END() 70 | */ 71 | /* }}} */ 72 | 73 | /* Remove the following function when you have successfully modified config.m4 74 | so that your module can be compiled into PHP, it exists only for testing 75 | purposes. */ 76 | 77 | /* Every user-visible function in PHP should document itself in the source */ 78 | /* {{{ proto string confirm_smartcrop_compiled(string arg) 79 | Return a string to confirm that the module is compiled in */ 80 | static int *getRgbColorAt(zval *im, zend_long x, zend_long y){ 81 | zval functionName, retval, aParams[3]; 82 | static int aRgb[4]; 83 | int rgb; 84 | 85 | ZVAL_STRING(&functionName, "imagecolorat"); 86 | ZVAL_ZVAL(&aParams[0], im, 1, 0); 87 | ZVAL_LONG(&aParams[1], x); 88 | ZVAL_LONG(&aParams[2], y); 89 | call_user_function(EG(function_table),NULL,&functionName, &retval,3,aParams TSRMLS_CC); 90 | rgb = Z_LVAL_P(&retval); 91 | aRgb[0] = rgb >> 16; 92 | aRgb[1] = rgb >> 8 & 255; 93 | aRgb[2] = rgb & 255; 94 | 95 | return aRgb; 96 | } 97 | static float cie(float r, float g, float b) { 98 | return 0.5126 * b + 0.7152 * g + 0.0722 * r; 99 | } 100 | static float sample(zval *im, zend_long x, zend_long y){ 101 | int *aRgb; 102 | aRgb = getRgbColorAt(im,x,y); 103 | return cie(*aRgb,*(aRgb+1),*(aRgb+2)); 104 | } 105 | static float edgeDetect(zval *im, zend_long x, zend_long y, zend_long w, zend_long h){ 106 | float lightness, leftLightness, topLightness,bottomLightness; 107 | static float centerLightness, rightLightness; 108 | if (x == 0 || x >= w-1 || y == 0 || y >= h-1) { 109 | lightness = sample(im, x, y); 110 | } else { 111 | if ( x > 1) { 112 | leftLightness = centerLightness; 113 | centerLightness = rightLightness; 114 | } 115 | rightLightness = sample(im, x+1, y); 116 | topLightness = sample(im, x, y-1); 117 | bottomLightness = sample(im, x, y+1); 118 | lightness = centerLightness * 4.0 - leftLightness - rightLightness - topLightness - bottomLightness; 119 | } 120 | return lightness; 121 | } 122 | static float skinColor(float r, float g, float b) { 123 | float mag, rd, gd, bd, d; 124 | mag = sqrt(r * r + g * g + b * b); 125 | rd = (r / mag - OPTION_SKIN_COLOR_R); 126 | gd = (g / mag - OPTION_SKIN_COLOR_G); 127 | bd = (b / mag - OPTION_SKIN_COLOR_B); 128 | d = sqrt(rd * rd + gd * gd + bd * bd); 129 | return 1.0 - d; 130 | } 131 | static float skinDetect(float r, float g, float b, float lightness) { 132 | float skin; 133 | int isSkinColor, isSkinBrightness; 134 | 135 | lightness = lightness / 255; 136 | skin = skinColor(r, g, b); 137 | isSkinColor = skin > OPTION_SKIN_THRESHOLD ? 1 : 0; 138 | isSkinBrightness = lightness > OPTION_SKIN_BRIGHTNESS_MIN && lightness <= OPTION_SKIN_BRIGHTNESS_MAX ? 1 : 0; 139 | 140 | if (isSkinColor && isSkinBrightness) { 141 | return (skin - OPTION_SKIN_THRESHOLD) * (255 / (1 - OPTION_SKIN_THRESHOLD)); 142 | } else { 143 | return 0; 144 | } 145 | } 146 | static float saturation(float r, float g, float b){ 147 | float maximum, minimum, l, d; 148 | 149 | maximum = (r / 255) > (g / 255) ? (r / 255) : (g / 255); 150 | maximum = maximum > (b / 255) ? maximum : (b / 255); 151 | minimum = (r / 255) < (g / 255) ? (r / 255) : (g / 255); 152 | minimum = minimum < (b / 255) ? minimum : (b / 255); 153 | 154 | if (maximum == minimum) { 155 | return 0; 156 | } 157 | 158 | l = (maximum + minimum) / 2; 159 | d = (maximum - minimum); 160 | 161 | return l > 0.5 ? d / (2 - maximum - minimum) : (d / (maximum + minimum)); 162 | } 163 | static float saturationDetect(float r, float g, float b, float lightness){ 164 | float sat; 165 | int acceptableSaturation, acceptableLightness; 166 | 167 | lightness = lightness / 255; 168 | sat = saturation(r, g, b); 169 | acceptableSaturation = sat > OPTION_SATURATION_THRESHOLD ? 1 : 0; 170 | acceptableLightness = (lightness >= OPTION_SATURATION_BRIGHTNESS_MIN && lightness <= OPTION_SATURATION_BRIGHTNESS_MAX) ? 1 : 0; 171 | if (acceptableLightness && acceptableSaturation) { 172 | return (sat - OPTION_SATURATION_THRESHOLD) * (255 / (1 - OPTION_SATURATION_THRESHOLD)); 173 | } else { 174 | return 0; 175 | } 176 | } 177 | static int *downSample(int w, int h, float *od){ 178 | int p; 179 | float width, height, ifactor2, x, y, u, v, r, g, b, a, mr, mg, mb, pR, pG, pB, pA; 180 | static float *data; 181 | 182 | width = floor(w / OPTION_SCORE_DOWN_SAMPLE); 183 | height = floor(h / OPTION_SCORE_DOWN_SAMPLE); 184 | ifactor2 = 1.0 / (OPTION_SCORE_DOWN_SAMPLE * OPTION_SCORE_DOWN_SAMPLE); 185 | 186 | data = (float*) emalloc(width * height * 4 * sizeof(float)); 187 | 188 | for (y = 0; y < height; y++) { 189 | for (x = 0; x < width; x++) { 190 | r = g = b = a = mr = mg = mb = 0.0; 191 | 192 | for(v = 0; v < OPTION_SCORE_DOWN_SAMPLE; v++) { 193 | for (u = 0; u < OPTION_SCORE_DOWN_SAMPLE; u++) { 194 | p = (y * OPTION_SCORE_DOWN_SAMPLE + v) * w * 3 + (x * OPTION_SCORE_DOWN_SAMPLE + u) * 3; 195 | pR = *(od + p); 196 | pG = *(od + p + 1); 197 | pB = *(od + p + 2); 198 | pA = 0.0; 199 | 200 | r += pR; 201 | g += pG; 202 | b += pB; 203 | a += pA; 204 | 205 | mr = mr > pR ? mr : pR; 206 | mg = mg > pG ? mg : pG; 207 | mb = mb > pB ? mb : pB; 208 | } 209 | } 210 | 211 | p = y * width * 4 + x * 4; 212 | data[p] = r * ifactor2 * 0.5 + mr * 0.5; 213 | data[p+1] = g * ifactor2 * 0.7 + mg * 0.3; 214 | data[p+2] = b * ifactor2; 215 | data[p+3] = a * ifactor2; 216 | } 217 | } 218 | return data; 219 | } 220 | static int *generateCrops(int w, int h, int cw, int ch){ 221 | int cropWidth, cropHeight, x, y, p; 222 | static int *result; 223 | cropWidth = cw; 224 | cropHeight = ch; 225 | if (cw == w) { 226 | result = (int*) emalloc(sizeof(int) * (h - cropHeight) / OPTION_STEP * 2); 227 | } else { 228 | result = (int*) emalloc(sizeof(int) * (w - cropWidth) / OPTION_STEP * 2); 229 | } 230 | p = 0; 231 | for (y = 0; y + cropHeight <= h; y += OPTION_STEP) { 232 | for (x = 0; x + cropWidth <= w; x += OPTION_STEP) { 233 | result[p]=x; 234 | result[p + 1]=y; 235 | p += 2; 236 | } 237 | } 238 | return result; 239 | } 240 | static float thirds(float x) { 241 | x = ((float)((int)(x - (1.0 / 3.0) + 1.0) % 2) * 0.5 - 0.5) * 16.0; 242 | return (1.0 - x * x) > 0.0 ? (1.0 - x * x) : 0.0; 243 | } 244 | static float importance(float cx, float cy, float cw, float ch, float x, float y) { 245 | float px, py, dx, dy, d, s; 246 | 247 | if (cx > x || x >= cx + cw || cy > y || y > cy + ch) { 248 | return OPTION_OUTSIDE_IMPORTANCE; 249 | } 250 | x = (x - cx) / cw; 251 | y = (y - cy) / ch; 252 | px = fabs(0.5 - x) * 2.0; 253 | py = fabs(0.5 - y) * 2,0; 254 | dx = (px - 1.0 + OPTION_EDGE_RADIUS); 255 | dx = dx > 0.0 ? dx : 0.0; 256 | dy = (py - 1.0 + OPTION_EDGE_RADIUS); 257 | dy = dy > 0.0 ? dy : 0.0; 258 | d = (dx * dx + dy * dy) * OPTION_EDGE_WEIGHT; 259 | s = 1.41 - sqrt(px * px + py * py); 260 | if (OPTION_RULE_OF_THIRDS) { 261 | s += (0.0 > (s + d + 0.5) ? 0.0 : (s + d + 0.5)) * 1.2 * (thirds(px) + thirds(py)); 262 | } 263 | return s+d; 264 | } 265 | static float score(float *output, int cx, int cy, int cw, int ch, int w, int h) { 266 | float downSample, invDownSample,i,detail,skin,saturation, total; 267 | int outputHeightDownSample, outputWidthDownSample, outputWidth, y, x, p; 268 | 269 | downSample = OPTION_SCORE_DOWN_SAMPLE; 270 | invDownSample = 1 /downSample; 271 | outputHeightDownSample = h; 272 | outputWidthDownSample = w; 273 | outputWidth = w / downSample; 274 | 275 | detail = skin = saturation = 0; 276 | for (y = 0; y < outputHeightDownSample; y += downSample) { 277 | for (x = 0; x < outputWidthDownSample; x += downSample) { 278 | i = importance(cx, cy, cw, ch, x, y); 279 | p = floor(y / downSample) * outputWidth * 4 + floor(x / downSample) * 4; 280 | detail += (*(output+p+1) / 255) * i; 281 | skin += *(output + p) / 255 * (detail + OPTION_SKIN_BIAS) * i; 282 | saturation += *(output + p + 2) / 255 * (detail + OPTION_SATURATION_BIAS) * i; 283 | } 284 | } 285 | total = (detail * OPTION_DETAIL_WEIGHT + skin * OPTION_SKIN_WEIGHT + saturation * OPTION_SATURATION_WEIGHT) / (cw * ch); 286 | return total; 287 | 288 | } 289 | PHP_FUNCTION(smartcrop) 290 | { 291 | zval *sim; 292 | zend_long DW, DH; 293 | zval functionName; 294 | zval retval; 295 | zend_long SW, SH, RW, RH; 296 | zval rim; 297 | float dw, dh, rw, rh, scale; 298 | 299 | 300 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zll", &sim, &DW, &DH ) 301 | == FAILURE) 302 | { 303 | RETURN_FALSE; 304 | } 305 | 306 | 307 | zval params[1]; 308 | ZVAL_ZVAL(¶ms[0], sim, 1, 0); 309 | 310 | ZVAL_STRING(&functionName, "imagesx"); 311 | call_user_function(EG(function_table),NULL,&functionName,&retval,1,params TSRMLS_CC); 312 | SW = Z_LVAL_P(&retval); 313 | 314 | ZVAL_STRING(&functionName, "imagesy"); 315 | call_user_function(EG(function_table),NULL,&functionName,&retval,1,params TSRMLS_CC); 316 | SH = Z_LVAL_P(&retval); 317 | 318 | dw = DW; 319 | dh = DH; 320 | scale = (dw / SW) > (dh / SH) ? (dw / SW) : (dh / SH); 321 | RW = floor(SW * scale); 322 | RH = floor(SH * scale); 323 | 324 | zval canvas_params[2]; 325 | ZVAL_LONG(&canvas_params[0], RW); 326 | ZVAL_LONG(&canvas_params[1], RH); 327 | 328 | ZVAL_STRING(&functionName, "imagecreatetruecolor"); 329 | call_user_function(EG(function_table),NULL,&functionName,&rim,2,canvas_params TSRMLS_CC); 330 | 331 | zval resize_params[10]; 332 | ZVAL_ZVAL(&resize_params[0], &rim, 1, 0); 333 | ZVAL_ZVAL(&resize_params[1], sim, 1, 0); 334 | ZVAL_LONG(&resize_params[2],0); 335 | ZVAL_LONG(&resize_params[3],0); 336 | ZVAL_LONG(&resize_params[4],0); 337 | ZVAL_LONG(&resize_params[5],0); 338 | ZVAL_LONG(&resize_params[6],RW); 339 | ZVAL_LONG(&resize_params[7],RH); 340 | ZVAL_LONG(&resize_params[8],SW); 341 | ZVAL_LONG(&resize_params[9],SH); 342 | ZVAL_STRING(&functionName, "imagecopyresampled"); 343 | call_user_function(EG(function_table),NULL,&functionName,&retval,10,resize_params TSRMLS_CC); 344 | 345 | int x,y,p,z; 346 | rw = RW; 347 | rh = RH; 348 | int *rgb_ptr; 349 | float *od; 350 | float *scoreOutput; 351 | int *crops; 352 | 353 | od = (float*)emalloc(RW*RH*3*sizeof(float)); 354 | 355 | for (y = 0; y < rh; y++) { 356 | for (x = 0; x < rw; x++) { 357 | rgb_ptr = getRgbColorAt(&rim,x,y); 358 | p = y * rw * 3 + x * 3; 359 | od[p+1] = edgeDetect(&rim,x,y,rw,rh); 360 | od[p] = skinDetect(*(rgb_ptr),*(rgb_ptr+1),*(rgb_ptr+2),sample(&rim,x,y)); 361 | od[p+2] = saturationDetect(*(rgb_ptr),*(rgb_ptr+1),*(rgb_ptr+2),sample(&rim,x,y)); 362 | } 363 | } 364 | 365 | scoreOutput = downSample(rw, rh, od); 366 | crops = generateCrops(rw, rh, dw, dh); 367 | 368 | float topScore = -1.0/0.0; 369 | int topCrop; 370 | float scoreTmp; 371 | int cropsNum; 372 | 373 | if (dw == rw) { 374 | cropsNum = (rh - dh)/OPTION_STEP; 375 | } else { 376 | cropsNum = (rw - dw)/OPTION_STEP; 377 | } 378 | 379 | for (z = 0; z <= cropsNum*2; z += 2){ 380 | scoreTmp = score(scoreOutput, *(crops+z), *(crops+z+1),dw,dh,rw,rh); 381 | if (topScore < scoreTmp){ 382 | topScore = scoreTmp; 383 | topCrop = z; 384 | } 385 | } 386 | 387 | zval cim; 388 | ZVAL_LONG(&canvas_params[0], DW); 389 | ZVAL_LONG(&canvas_params[1], DH); 390 | 391 | ZVAL_STRING(&functionName, "imagecreatetruecolor"); 392 | call_user_function(EG(function_table),NULL,&functionName,&cim,2,canvas_params TSRMLS_CC); 393 | 394 | ZVAL_ZVAL(&resize_params[0], &cim, 1, 0); 395 | ZVAL_ZVAL(&resize_params[1], &rim, 1, 0); 396 | ZVAL_LONG(&resize_params[2],0); 397 | ZVAL_LONG(&resize_params[3],0); 398 | ZVAL_LONG(&resize_params[4],*(crops+topCrop)); 399 | ZVAL_LONG(&resize_params[5],*(crops+topCrop+1)); 400 | ZVAL_LONG(&resize_params[6],DW); 401 | ZVAL_LONG(&resize_params[7],DH); 402 | ZVAL_LONG(&resize_params[8],DW); 403 | ZVAL_LONG(&resize_params[9],DH); 404 | ZVAL_STRING(&functionName, "imagecopyresampled"); 405 | call_user_function(EG(function_table),NULL,&functionName,&retval,10,resize_params TSRMLS_CC); 406 | 407 | RETURN_ZVAL(&cim,1,0); 408 | } 409 | /* }}} */ 410 | /* The previous line is meant for vim and emacs, so it can correctly fold and 411 | unfold functions in source code. See the corresponding marks just before 412 | function definition, where the functions purpose is also documented. Please 413 | follow this convention for the convenience of others editing your code. 414 | */ 415 | 416 | 417 | /* {{{ php_smartcrop_init_globals 418 | */ 419 | /* Uncomment this function if you have INI entries 420 | static void php_smartcrop_init_globals(zend_smartcrop_globals *smartcrop_globals) 421 | { 422 | smartcrop_globals->global_value = 0; 423 | smartcrop_globals->global_string = NULL; 424 | } 425 | */ 426 | /* }}} */ 427 | 428 | /* {{{ PHP_MINIT_FUNCTION 429 | */ 430 | PHP_MINIT_FUNCTION(smartcrop) 431 | { 432 | /* If you have INI entries, uncomment these lines 433 | REGISTER_INI_ENTRIES(); 434 | */ 435 | return SUCCESS; 436 | } 437 | /* }}} */ 438 | 439 | /* {{{ PHP_MSHUTDOWN_FUNCTION 440 | */ 441 | PHP_MSHUTDOWN_FUNCTION(smartcrop) 442 | { 443 | /* uncomment this line if you have INI entries 444 | UNREGISTER_INI_ENTRIES(); 445 | */ 446 | return SUCCESS; 447 | } 448 | /* }}} */ 449 | /* The previous line is meant for vim and emacs, so it can correctly fold and 450 | unfold functions in source code. See the corresponding marks just before 451 | function definition, where the functions purpose is also documented. Please 452 | follow this convention for the convenience of others editing your code. 453 | */ 454 | 455 | /* Remove if there's nothing to do at request start */ 456 | /* {{{ PHP_RINIT_FUNCTION 457 | */ 458 | PHP_RINIT_FUNCTION(smartcrop) 459 | { 460 | #if defined(COMPILE_DL_SMARTCROP) && defined(ZTS) 461 | ZEND_TSRMLS_CACHE_UPDATE(); 462 | #endif 463 | return SUCCESS; 464 | } 465 | /* }}} */ 466 | 467 | /* Remove if there's nothing to do at request end */ 468 | /* {{{ PHP_RSHUTDOWN_FUNCTION 469 | */ 470 | PHP_RSHUTDOWN_FUNCTION(smartcrop) 471 | { 472 | return SUCCESS; 473 | } 474 | /* }}} */ 475 | 476 | /* {{{ PHP_MINFO_FUNCTION 477 | */ 478 | PHP_MINFO_FUNCTION(smartcrop) 479 | { 480 | php_info_print_table_start(); 481 | php_info_print_table_header(2, "smartcrop support", "enabled"); 482 | php_info_print_table_end(); 483 | 484 | /* Remove comments if you have entries in php.ini 485 | DISPLAY_INI_ENTRIES(); 486 | */ 487 | } 488 | /* }}} */ 489 | 490 | /* {{{ smartcrop_functions[] 491 | * 492 | * Every user visible function must have an entry in smartcrop_functions[]. 493 | */ 494 | const zend_function_entry smartcrop_functions[] = { 495 | PHP_FE(smartcrop,NULL) /* For testing, remove later. */ 496 | PHP_FE_END /* Must be the last line in smartcrop_functions[] */ 497 | }; 498 | /* }}} */ 499 | 500 | /* {{{ smartcrop_module_entry 501 | */ 502 | zend_module_entry smartcrop_module_entry = { 503 | STANDARD_MODULE_HEADER, 504 | "smartcrop", 505 | smartcrop_functions, 506 | PHP_MINIT(smartcrop), 507 | PHP_MSHUTDOWN(smartcrop), 508 | PHP_RINIT(smartcrop), /* Replace with NULL if there's nothing to do at request start */ 509 | PHP_RSHUTDOWN(smartcrop), /* Replace with NULL if there's nothing to do at request end */ 510 | PHP_MINFO(smartcrop), 511 | PHP_SMARTCROP_VERSION, 512 | STANDARD_MODULE_PROPERTIES 513 | }; 514 | /* }}} */ 515 | 516 | #ifdef COMPILE_DL_SMARTCROP 517 | #ifdef ZTS 518 | ZEND_TSRMLS_CACHE_DEFINE() 519 | #endif 520 | ZEND_GET_MODULE(smartcrop) 521 | #endif 522 | 523 | /* 524 | * Local variables: 525 | * tab-width: 4 526 | * c-basic-offset: 4 527 | * End: 528 | * vim600: noet sw=4 ts=4 fdm=marker 529 | * vim<600: noet sw=4 ts=4 530 | */ 531 | -------------------------------------------------------------------------------- /smartcrop.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('smartcrop')) { 5 | dl('smartcrop.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'smartcrop'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | $function = 'confirm_' . $module . '_compiled'; 15 | if (extension_loaded($module)) { 16 | $str = $function($module); 17 | } else { 18 | $str = "Module $module is not compiled into PHP"; 19 | } 20 | echo "$str\n"; 21 | ?> 22 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for smartcrop presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | smartcrop extension is available 22 | --------------------------------------------------------------------------------