int main(int argc,char **argv) { MagickWand *magick_wand_background; MagickWand *magick_wand_foreground; MagickWandGenesis(); magick_wand_background = NewMagickWand(); magick_wand_foreground = NewMagickWand(); MagickReadImage(magick_wand_background, "magick:logo"); MagickReadImage(magick_wand_foreground, "magick:rose"); MagickSetGravity(magick_wand_background, CenterGravity); MagickSetGravity(magick_wand_foreground, CenterGravity); MagickCompositeImage( magick_wand_background, magick_wand_foreground, AtopCompositeOp, 0, 0 ); MagickSetFormat(magick_wand_background, "png"); MagickWriteImage(magick_wand_background, "./output/gravity.png"); MagickWandTerminus(); return 0; }
Object c_Gmagick::t_compositeimage(CObjRef gm, int64 compose, int64 x, int64 y) { INSTANCE_METHOD_INJECTION_BUILTIN(Gmagick, Gmagick::compositeimage); c_Gmagick *gm2 = gm.getTyped<c_Gmagick>(); int result = MagickCompositeImage(magick_wand, gm2->magick_wand, (CompositeOperator) compose, x, y); checkResult(result); return this; }
caddr_t bif_im_DeepZoom4to1 (caddr_t * qst, caddr_t * err, state_slot_t ** args) { im_env_t env; caddr_t res; int fmt_is_set = 0; int image_ctr; im_init (&env, qst, args, "IM DeepZoom4to1"); im_set_background (&env, "#000000"); env.ime_target_magick_wand = NewMagickWand (); if (MagickFalse == MagickNewImage (env.ime_target_magick_wand, 256, 256, env.ime_background)) im_leave_with_error (&env, "22023", "IM001", "Can not make new image"); if (MagickFalse == MagickSetImageType (env.ime_target_magick_wand, TrueColorType)) im_leave_with_error (&env, "22023", "IM001", "Can not set image type"); if (MagickFalse == MagickSetImageDepth (env.ime_target_magick_wand, 16)) im_leave_with_error (&env, "22023", "IM001", "Can not set image depth"); if (MagickFalse == MagickSetImageExtent (env.ime_target_magick_wand, 256, 256)) im_leave_with_error (&env, "22023", "IM001", "Can not set image extent"); if (MagickFalse == MagickSetImageBackgroundColor (env.ime_target_magick_wand, env.ime_background)) im_leave_with_error (&env, "22023", "IM001", "Can not set image background"); image_ctr = BOX_ELEMENTS (args) / 2; if (image_ctr > 4) image_ctr = 4; while (0 < image_ctr--) { if (DV_DB_NULL == DV_TYPE_OF (bif_arg (qst, args, image_ctr*2, "IM DeepZoom4to1"))) continue; im_env_set_input_blob (&env, image_ctr * 2); /*im_env_set_blob_ext (&env, 2);*/ im_read (&env); MagickResetIterator (env.ime_magick_wand); while (MagickNextImage (env.ime_magick_wand) != MagickFalse) { unsigned long v_size, h_size; if (!fmt_is_set) { if (MagickFalse == MagickSetImageFormat (env.ime_target_magick_wand, MagickGetImageFormat (env.ime_magick_wand))) im_leave_with_error (&env, "22023", "IM001", "Can not set image format"); fmt_is_set = 1; } h_size = MagickGetImageWidth (env.ime_magick_wand); v_size = MagickGetImageHeight (env.ime_magick_wand); if ((256 < h_size) || (256 < v_size)) continue; MagickResizeImage (env.ime_magick_wand, h_size/2, v_size/2, BoxFilter, 1.0); if (MagickFalse == MagickCompositeImage (env.ime_target_magick_wand, env.ime_magick_wand, OverCompositeOp, (image_ctr & 1) * 128, (image_ctr & 2) * 64)) im_leave_with_error (&env, "22023", "IM001", "Can not composite image"); } im_reset_read (&env); } MagickProfileImage (env.ime_target_magick_wand, "*", NULL, 0); DestroyMagickWand (env.ime_magick_wand); env.ime_magick_wand = env.ime_target_magick_wand; env.ime_target_magick_wand = NULL; res = im_write (&env); im_leave (&env); return res; }
int main(int argc,char **argv) { MagickBooleanType status; PixelWand *pixelWand; MagickWand *magick_wand_a; MagickWand *magick_wand_b; //MagickWand *magick_wand_c; MagickWandGenesis(); magick_wand_a = NewMagickWand(); status = MagickReadImage(magick_wand_a, "./displaceCompositeA.png"); if (status == MagickFalse) { ThrowWandException(magick_wand_a); } magick_wand_b = NewMagickWand(); status = MagickReadImage(magick_wand_b, "./displaceMask.png"); if (status == MagickFalse) { ThrowWandException(magick_wand_b); } MagickSetImageVirtualPixelMethod(magick_wand_a, TransparentVirtualPixelMethod); pixelWand = makePixelWand("none"); status = MagickSetImageBackgroundColor(magick_wand_a, pixelWand); MagickSetOption(magick_wand_a, "compose:args", "300x53.033"); status = MagickCompositeImage(magick_wand_a, magick_wand_b, DisplaceCompositeOp, 0, 0); if (status == MagickFalse) { printf("Failed to composite image b"); exit(-1); } status = MagickWriteImages(magick_wand_a, "displaceOutputMerged.png", MagickTrue); MagickWandTerminus(); printf("Finished - please compare displaceOutputMerged.png with the result of:\n\n"); printf("convert displaceCompositeA.png displaceMask.png -virtual-pixel transparent -channel rgba -alpha on -background transparent -define compose:args=300x53.033 -compose displace -composite displaceMerged.png"); return(0); }
MagickBooleanType MagickCompositeImageChannel(MagickWand *wand, const ChannelType channel,const MagickWand *source_wand, const CompositeOperator compose,const ssize_t x,const ssize_t y) { MagickBooleanType status; ChannelType previous_channel_mask; long clip_to_self = 1; if (channel != UndefinedChannel) { previous_channel_mask = MagickSetImageChannelMask(wand, channel); } status = MagickCompositeImage(wand, source_wand, compose, clip_to_self, x,y); if (channel != UndefinedChannel) { (void) MagickSetImageChannelMask(wand, previous_channel_mask); } return status; }
bool img_render_thumb (img_t *im, const ssize_t banner_x, const ssize_t banner_y, const size_t thumb_w, const size_t thumb_h) { if (im) { if (MagickCompositeImage ((MagickWand *) im->screen, (MagickWand *) im->banner, OverCompositeOp, banner_x, banner_y) == MagickTrue && MagickScaleImage ((MagickWand *) im->screen, thumb_w, thumb_h) == MagickTrue) { return true; } img_exception ((MagickWand *) im->screen); } return false; }
void test_wand(void) { MagickWand *magick_wand = NULL; MagickWand *c_wand = NULL; DrawingWand *d_wand = NULL; PixelWand *p_wand = NULL; // Used for text effect #3 double dargs[1] = {120.}; // Used for text effect #5 double d_args[8] = { -0.02,0.0, 0.0,1.02, 0.0,0.0, -0.5,1.9 }; MagickWandGenesis(); // Text effect 1 - shadow effect using MagickShadowImage // This is derived from Anthony's Soft Shadow effect // convert -size 300x100 xc:none -font Candice -pointsize 72 \ // -fill white -stroke black -annotate +25+65 'Anthony' \ // \( +clone -background navy -shadow 70x4+5+5 \) +swap \ // -background lightblue -flatten -trim +repage font_shadow_soft.jpg //NOTE - if an image has a transparent background, adding a border of any colour other // than "none" will remove all the transparency and replace it with the border's colour magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); PixelSetColor(p_wand,"none"); // Create a new transparent image MagickNewImage(magick_wand,350,100,p_wand); // Set up a 72 point white font PixelSetColor(p_wand,"white"); DrawSetFillColor(d_wand,p_wand); DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Add a black outline to the text PixelSetColor(p_wand,"black"); DrawSetStrokeColor(d_wand,p_wand); // Turn antialias on - not sure this makes a difference DrawSetTextAntialias(d_wand,MagickTrue); // Now draw the text DrawAnnotation(d_wand,25,65,(const unsigned char *)"Magick"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); // Trim the image down to include only the text MagickTrimImage(magick_wand,0); // equivalent to the command line +repage MagickResetImagePage(magick_wand,""); // Make a copy of the text image c_wand = CloneMagickWand(magick_wand); // Set the background colour to blue for the shadow PixelSetColor(p_wand,"blue"); MagickSetImageBackgroundColor(magick_wand,p_wand); // Opacity is a real number indicating (apparently) percentage MagickShadowImage(magick_wand,70,4,5,5); // Composite the text on top of the shadow MagickCompositeImage(magick_wand,c_wand,OverCompositeOp,5,5); if(c_wand)c_wand = DestroyMagickWand(c_wand); c_wand = NewMagickWand(); // Create a new image the same size as the text image and put a solid colour // as its background PixelSetColor(p_wand,"rgb(125,215,255)"); MagickNewImage(c_wand,MagickGetImageWidth(magick_wand),MagickGetImageHeight(magick_wand),p_wand); // Now composite the shadowed text over the plain background MagickCompositeImage(c_wand,magick_wand,OverCompositeOp,0,0); // and write the result MagickWriteImage(c_wand,"text_shadow.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(c_wand)c_wand = DestroyMagickWand(c_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); // Text effect 2 - tiled text using the builtin checkerboard pattern // Anthony's Tiled Font effect // convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \ // -tile pattern:checkerboard -annotate +28+68 'Anthony' \ // font_tile.jpg magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); set_tile_pattern(d_wand,"#check","pattern:checkerboard"); PixelSetColor(p_wand,"lightblue"); // Create a new transparent image MagickNewImage(magick_wand,320,100,p_wand); // Set up a 72 point font DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Now draw the text DrawAnnotation(d_wand,28,68,(const unsigned char *)"Magick"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); // Trim the image MagickTrimImage(magick_wand,0); // Add a transparent border PixelSetColor(p_wand,"lightblue"); MagickBorderImage(magick_wand,p_wand,5,5); // and write it MagickWriteImage(magick_wand,"text_pattern.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); // Text effect 3 - arc font (similar to http://www.imagemagick.org/Usage/fonts/#arc) //convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \ // -annotate +25+65 'Anthony' -distort Arc 120 \ // -trim +repage -bordercolor lightblue -border 10 font_arc.jpg magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); // Create a 320x100 lightblue canvas PixelSetColor(p_wand,"lightblue"); MagickNewImage(magick_wand,320,100,p_wand); // Set up a 72 point font DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Now draw the text DrawAnnotation(d_wand,25,65,(const unsigned char *)"Magick"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); MagickDistortImage(magick_wand,ArcDistortion,1,dargs,MagickFalse); // Trim the image MagickTrimImage(magick_wand,0); // Add the border PixelSetColor(p_wand,"lightblue"); MagickBorderImage(magick_wand,p_wand,10,10); // and write it MagickWriteImage(magick_wand,"text_arc.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); // Text effect 4 - bevelled font http://www.imagemagick.org/Usage/fonts/#bevel // convert -size 320x100 xc:black -font Candice -pointsize 72 \ // -fill white -annotate +25+65 'Anthony' \ // -shade 140x60 font_beveled.jpg magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); // Create a 320x100 canvas PixelSetColor(p_wand,"gray"); MagickNewImage(magick_wand,320,100,p_wand); // Set up a 72 point font DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Set up a 72 point white font PixelSetColor(p_wand,"white"); DrawSetFillColor(d_wand,p_wand); // Now draw the text DrawAnnotation(d_wand,25,65,(const unsigned char *)"Magick"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); // the "gray" parameter must be true to get the effect shown on Anthony's page MagickShadeImage(magick_wand,MagickTrue,140,60); #ifdef COLORIZE PixelSetColor(p_wand,"yellow"); DrawSetFillColor(d_wand,p_wand); cp_wand = NewPixelWand(); PixelSetColor(cp_wand,"gold"); MagickColorizeImage(magick_wand,p_wand,cp_wand); #endif // and write it MagickWriteImage(magick_wand,"text_bevel.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); #ifdef COLORIZE if(cp_wand)cp_wand = DestroyPixelWand(cp_wand); #endif // Text effect 5 and 6 - Plain text and then Barrel distortion // This one uses d_args magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); // Create a 320x100 transparent canvas PixelSetColor(p_wand,"none"); MagickNewImage(magick_wand,320,100,p_wand); // Set up a 72 point font DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Now draw the text DrawAnnotation(d_wand,25,65,(const unsigned char *)"Magick"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); MagickWriteImage(magick_wand,"text_plain.png"); // Trim the image MagickTrimImage(magick_wand,0); // Add the border PixelSetColor(p_wand,"none"); MagickBorderImage(magick_wand,p_wand,10,10); // MagickSetImageMatte(magick_wand,MagickTrue); // MagickSetImageVirtualPixelMethod(magick_wand,TransparentVirtualPixelMethod); // d_args[0] = 0.1;d_args[1] = -0.25;d_args[2] = -0.25; [3] += .1 // The first value should be positive. If it is negative the image is *really* distorted d_args[0] = 0.0; d_args[1] = 0.0; d_args[2] = 0.5; // d_args[3] should normally be chosen such the sum of all 4 values is 1 // so that the result is the same size as the original // You can override the sum with a different value // If the sum is greater than 1 the resulting image will be smaller than the original d_args[3] = 1 - (d_args[0] + d_args[1] + d_args[2]); // Make the result image smaller so that it isn't as likely // to overflow the edges // d_args[3] += 0.1; // 0.0,0.0,0.5,0.5,0.0,0.0,-0.5,1.9 d_args[3] = 0.5; d_args[4] = 0.0; d_args[5] = 0.0; d_args[6] = -0.5; d_args[7] = 1.9; // DON'T FORGET to set the correct number of arguments here MagickDistortImage(magick_wand,BarrelDistortion,8,d_args,MagickTrue); // MagickResetImagePage(magick_wand,""); // Trim the image again MagickTrimImage(magick_wand,0); // Add the border PixelSetColor(p_wand,"none"); MagickBorderImage(magick_wand,p_wand,10,10); // and write it MagickWriteImage(magick_wand,"text_barrel.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); // Text effect 7 - Polar distortion // This one uses d_args[0] magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); // Create a 320x200 transparent canvas PixelSetColor(p_wand,"none"); MagickNewImage(magick_wand,320,200,p_wand); // Set up a 72 point font DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Now draw the text DrawAnnotation(d_wand,25,65,(const unsigned char *)"Magick"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); d_args[0] = 0.0; // DON'T FORGET to set the correct number of arguments here MagickDistortImage(magick_wand,PolarDistortion,1,d_args,MagickTrue); // MagickResetImagePage(magick_wand,""); // Trim the image again MagickTrimImage(magick_wand,0); // Add the border PixelSetColor(p_wand,"none"); MagickBorderImage(magick_wand,p_wand,10,10); // and write it MagickWriteImage(magick_wand,"text_polar.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); // Text effect 8 - Shepard's distortion // This one uses d_args[0] magick_wand = NewMagickWand(); d_wand = NewDrawingWand(); p_wand = NewPixelWand(); // Create a 320x200 transparent canvas PixelSetColor(p_wand,"none"); MagickNewImage(magick_wand,640,480,p_wand); // Set up a 72 point font DrawSetFont (d_wand, "Verdana-Bold-Italic" ) ; DrawSetFontSize(d_wand,72); // Now draw the text DrawAnnotation(d_wand,50,240,(const unsigned char *)"Magick Rocks"); // Draw the image on to the magick_wand MagickDrawImage(magick_wand,d_wand); d_args[0] = 150.0; d_args[1] = 190.0; d_args[2] = 100.0; d_args[3] = 290.0; d_args[4] = 500.0; d_args[5] = 200.0; d_args[6] = 430.0; d_args[7] = 130.0; // DON'T FORGET to set the correct number of arguments here MagickDistortImage(magick_wand,ShepardsDistortion,8,d_args,MagickTrue); // Trim the image MagickTrimImage(magick_wand,0); // Add the border PixelSetColor(p_wand,"none"); MagickBorderImage(magick_wand,p_wand,10,10); // and write it MagickWriteImage(magick_wand,"text_shepards.png"); /* Clean up */ if(magick_wand)magick_wand = DestroyMagickWand(magick_wand); if(d_wand)d_wand = DestroyDrawingWand(d_wand); if(p_wand)p_wand = DestroyPixelWand(p_wand); MagickWandTerminus(); }
// output_data apr_status_t small_light_filter_imagemagick_output_data( ap_filter_t *f, apr_bucket_brigade *bb, void *v_ctx, apr_bucket *e) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "small_light_filter_imagemagick_output_data"); request_rec *r = f->r; small_light_module_ctx_t* ctx = (small_light_module_ctx_t*)v_ctx; small_light_module_imagemagick_ctx_t *lctx = ctx->lctx; struct timeval t2, t21, t22, t23, t3; MagickBooleanType status = MagickFalse; // check data received. if (lctx->image == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "no data received."); r->status = HTTP_INTERNAL_SERVER_ERROR; return APR_EGENERAL; } // start image modifing. gettimeofday(&t2, NULL); small_light_image_size_t sz; small_light_calc_image_size(&sz, r, ctx, 10000.0, 10000.0); // init wand small_light_filter_imagemagick_output_data_init(); lctx->wand = NewMagickWand(); // prepare. if (sz.jpeghint_flg != 0) { char *jpeg_size_opt = (char *)apr_psprintf(r->pool, "%dx%d", (int)sz.dw, (int)sz.dh); MagickSetOption(lctx->wand, "jpeg:size", jpeg_size_opt); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickSetOption(jpeg:size, %s)", jpeg_size_opt); } // load image. gettimeofday(&t21, NULL); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickReadImageBlob"); status = MagickReadImageBlob(lctx->wand, (void *)lctx->image, lctx->image_len); if (status == MagickFalse) { small_light_filter_imagemagick_output_data_fini(ctx); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "couldn't read image"); r->status = HTTP_INTERNAL_SERVER_ERROR; return APR_EGENERAL; } // calc size. gettimeofday(&t22, NULL); double iw = (double)MagickGetImageWidth(lctx->wand); double ih = (double)MagickGetImageHeight(lctx->wand); small_light_calc_image_size(&sz, r, ctx, iw, ih); // pass through. if (sz.pt_flg != 0) { small_light_filter_imagemagick_output_data_fini(ctx); apr_bucket *b = apr_bucket_pool_create(lctx->image, lctx->image_len, r->pool, ctx->bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->bb, b); APR_BRIGADE_INSERT_TAIL(ctx->bb, apr_bucket_eos_create(ctx->bb->bucket_alloc)); return ap_pass_brigade(f->next, ctx->bb); } // crop, scale. status = MagickTrue; if (sz.scale_flg != 0) { char *crop_geo = (char *)apr_psprintf(r->pool, "%f!x%f!+%f+%f", sz.sw, sz.sh, sz.sx, sz.sy); char *size_geo = (char *)apr_psprintf(r->pool, "%f!x%f!", sz.dw, sz.dh); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickTransformImage(wand, ""%s"", ""%s"")", crop_geo, size_geo); MagickWand *trans_wand; trans_wand = MagickTransformImage(lctx->wand, crop_geo, size_geo); if (trans_wand == NULL || trans_wand == lctx->wand) { small_light_filter_imagemagick_output_data_fini(ctx); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "MagickTransformImage failed"); r->status = HTTP_INTERNAL_SERVER_ERROR; return APR_EGENERAL; } DestroyMagickWand(lctx->wand); lctx->wand = trans_wand; } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "no scale"); } // create canvas then draw image to the canvas. if (sz.cw > 0.0 && sz.ch > 0.0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "NewMagickWand()"); MagickWand *canvas_wand = NewMagickWand(); PixelWand *canvas_color = NewPixelWand(); PixelSetRed(canvas_color, sz.cc.r / 255.0); PixelSetGreen(canvas_color, sz.cc.g / 255.0); PixelSetBlue(canvas_color, sz.cc.b / 255.0); PixelSetAlpha(canvas_color, sz.cc.a / 255.0); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickNewImage(canvas_wand, %f, %f, bgcolor)", sz.cw, sz.ch); status = MagickNewImage(canvas_wand, sz.cw, sz.ch, canvas_color); DestroyPixelWand(canvas_color); if (status == MagickFalse) { small_light_filter_imagemagick_output_data_fini(ctx); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "MagickNewImage(canvas_wand, %f, %f, bgcolor) failed", sz.cw, sz.ch); r->status = HTTP_INTERNAL_SERVER_ERROR; return APR_EGENERAL; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickCompositeImage(canvas_wand, wand, AtopCompositeOp, %f, %f)", sz.dx, sz.dy); status = MagickCompositeImage(canvas_wand, lctx->wand, AtopCompositeOp, sz.dx, sz.dy); if (status == MagickFalse) { small_light_filter_imagemagick_output_data_fini(ctx); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "MagickCompositeImage(canvas_wand, wand, AtopCompositeOp, %f, %f) failed", sz.dx, sz.dy); r->status = HTTP_INTERNAL_SERVER_ERROR; return APR_EGENERAL; } DestroyMagickWand(lctx->wand); lctx->wand = canvas_wand; } // effects. char *unsharp = (char *)apr_table_get(ctx->prm, "unsharp"); if (unsharp) { GeometryInfo geo; ParseGeometry(unsharp, &geo); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickUnsharpMaskImage(wand, %f, %f, %f, %f)", geo.rho, geo.sigma, geo.xi, geo.psi); status = MagickUnsharpMaskImage(lctx->wand, geo.rho, geo.sigma, geo.xi, geo.psi); if (status == MagickFalse) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unsharp failed"); } } char *sharpen = (char *)apr_table_get(ctx->prm, "sharpen"); if (sharpen) { GeometryInfo geo; ParseGeometry(sharpen, &geo); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickSharpenImage(wand, %f, %f)", geo.rho, geo.sigma); status = MagickSharpenImage(lctx->wand, geo.rho, geo.sigma); if (status == MagickFalse) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "sharpen failed"); } } char *blur = (char *)apr_table_get(ctx->prm, "blur"); if (blur) { GeometryInfo geo; ParseGeometry(blur, &geo); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickBlurImage(wand, %f, %f)", geo.rho, geo.sigma); status = MagickBlurImage(lctx->wand, geo.rho, geo.sigma); if (status == MagickFalse) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "blur failed"); } } // border. if (sz.bw > 0.0 || sz.bh > 0.0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "draw border"); DrawingWand *border_wand = NewDrawingWand(); PixelWand *border_color; border_color = NewPixelWand(); PixelSetRed(border_color, sz.bc.r / 255.0); PixelSetGreen(border_color, sz.bc.g / 255.0); PixelSetBlue(border_color, sz.bc.b / 255.0); PixelSetAlpha(border_color, sz.bc.a / 255.0); DrawSetFillColor(border_wand, border_color); DrawSetStrokeColor(border_wand, border_color); DrawSetStrokeWidth(border_wand, 1); DrawRectangle(border_wand, 0, 0, sz.cw - 1, sz.bh - 1); DrawRectangle(border_wand, 0, 0, sz.bw - 1, sz.ch - 1); DrawRectangle(border_wand, 0, sz.ch - sz.bh, sz.cw - 1, sz.ch - 1); DrawRectangle(border_wand, sz.cw - sz.bw, 0, sz.cw - 1, sz.ch - 1); MagickDrawImage(lctx->wand, border_wand); DestroyPixelWand(border_color); DestroyDrawingWand(border_wand); } gettimeofday(&t23, NULL); // set params. double q = small_light_parse_double(r, (char *)apr_table_get(ctx->prm, "q")); if (q > 0.0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickSetImageComressionQualty(wand, %f)", q); MagickSetImageCompressionQuality(lctx->wand, q); } char *of = (char *)apr_table_get(ctx->prm, "of"); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "MagickSetFormat(wand, '%s')", of); MagickSetFormat(lctx->wand, of); // get small_lighted image as binary. unsigned char *canvas_buff; const char *sled_image; size_t sled_image_size; canvas_buff = MagickGetImageBlob(lctx->wand, &sled_image_size); sled_image = (const char *)apr_pmemdup(r->pool, canvas_buff, sled_image_size); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "sled_image_size = %d", sled_image_size); // free buffer and wand. MagickRelinquishMemory(canvas_buff); small_light_filter_imagemagick_output_data_fini(ctx); // insert new bucket to bucket brigade. apr_bucket *b = apr_bucket_pool_create(sled_image, sled_image_size, r->pool, ctx->bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->bb, b); // insert eos to bucket brigade. APR_BRIGADE_INSERT_TAIL(ctx->bb, apr_bucket_eos_create(ctx->bb->bucket_alloc)); // set correct Content-Type and Content-Length. char *cont_type = apr_psprintf(r->pool, "image/%s", of); ap_set_content_type(r, cont_type); ap_set_content_length(r, sled_image_size); // end. gettimeofday(&t3, NULL); // http header. int info = small_light_parse_int(r, (char *)apr_table_get(ctx->prm, "info")); if (info != SMALL_LIGHT_INT_INVALID_VALUE && info != 0) { char *info = (char *)apr_psprintf(r->pool, "transfer=%ldms, modify image=%ldms (load=%ldms, scale=%ldms, save=%ldms)", small_light_timeval_diff(&ctx->t, &t2) / 1000L, small_light_timeval_diff(&t2, &t3) / 1000L, small_light_timeval_diff(&t21, &t22) / 1000L, small_light_timeval_diff(&t22, &t23) / 1000L, small_light_timeval_diff(&t23, &t3) / 1000L ); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "uri=%s, info=%s)", r->unparsed_uri, info); apr_table_setn(r->headers_out, "X-SmallLight-Description", info); } return ap_pass_brigade(f->next, ctx->bb); }
unsigned char* generate_thumbnail( const void *image, const size_t size, size_t *new_size ) { MagickWand *magick_wand, *magick_wand_canvas; PixelWand *pixel_wand; unsigned int cols, rows, ncols, nrows; unsigned char *new_blob; // Initialize MagickWand: MagickWandGenesis(); magick_wand = NewMagickWand(); magick_wand_canvas = NewMagickWand(); pixel_wand = NewPixelWand(); // Read image from memory: DoMagick( magick_wand, MagickReadImageBlob( magick_wand, image, size ) ); // If input consists of several images, we'll only take the first one: MagickResetIterator( magick_wand ); MagickSetFirstIterator( magick_wand ); // Get image size: cols = MagickGetImageWidth( magick_wand ); rows = MagickGetImageHeight( magick_wand ); printf("Image size: %ix%i\n", (int)cols, (int)rows); if (cols==0 || rows==0) { fprintf(stderr, "generateThumbnail: Warning, imagesize==0, skipping image...\n"); pixel_wand = DestroyPixelWand( pixel_wand ); magick_wand_canvas = DestroyMagickWand( magick_wand_canvas ); magick_wand = DestroyMagickWand( magick_wand ); MagickWandTerminus(); return NULL; } if (cols >= rows) { ncols = 98; nrows = (rows*98)/cols; } else { nrows = 98; ncols = (cols*98)/rows; } // Convert the image into a thumbnail: DoMagick( magick_wand, MagickThumbnailImage( magick_wand, ncols, nrows ) ); PixelSetColor( pixel_wand, "#8f7f6f" ); DoMagick( magick_wand, MagickBorderImage( magick_wand, pixel_wand, 1, 1 ) ); DoMagick( magick_wand, MagickSetImageBackgroundColor( magick_wand, pixel_wand ) ); PixelSetColor( pixel_wand, "white" ); DoMagick( magick_wand_canvas, MagickNewImage( magick_wand_canvas, 100, 100, pixel_wand ) ); DoMagick( magick_wand_canvas, MagickCompositeImage( magick_wand_canvas, magick_wand, OverCompositeOp, 49-(ncols/2), 49-(nrows/2) ) ); // DoMagick( magick_wand_canvas, MagickWriteImage( magick_wand_canvas, "thumbnail.png" ) ); DoMagick( magick_wand_canvas, MagickSetImageFormat( magick_wand_canvas, "PNG" ) ); new_blob = MagickGetImageBlob( magick_wand_canvas, new_size ); // unsigned char *MagickGetImageBlob(MagickWand *wand,size_t *length) pixel_wand = DestroyPixelWand( pixel_wand ); magick_wand = DestroyMagickWand( magick_wand ); magick_wand_canvas = DestroyMagickWand( magick_wand_canvas ); MagickWandTerminus(); return new_blob; }
ngx_int_t ngx_http_small_light_imagemagick_process(ngx_http_request_t *r, ngx_http_small_light_ctx_t *ctx) { ngx_http_small_light_imagemagick_ctx_t *ictx; ngx_http_small_light_image_size_t sz; MagickBooleanType status; int rmprof_flg, progressive_flg, cmyk2rgb_flg; double iw, ih, q; char *unsharp, *sharpen, *blur, *of, *of_orig; MagickWand *trans_wand, *canvas_wand; DrawingWand *border_wand; PixelWand *bg_color, *canvas_color, *border_color; GeometryInfo geo; ngx_fd_t fd; MagickWand *icon_wand; u_char *p, *embedicon; size_t embedicon_path_len, embedicon_len, sled_image_size; ngx_int_t type; u_char jpeg_size_opt[32], crop_geo[128], size_geo[128], embedicon_path[256]; ColorspaceType color_space; #if MagickLibVersion >= 0x690 int autoorient_flg; #endif status = MagickFalse; ictx = (ngx_http_small_light_imagemagick_ctx_t *)ctx->ictx; /* adjust image size */ ngx_http_small_light_calc_image_size(r, ctx, &sz, 10000.0, 10000.0); /* prepare */ if (sz.jpeghint_flg != 0) { p = ngx_snprintf((u_char *)jpeg_size_opt, sizeof(jpeg_size_opt) - 1, "%dx%d", (ngx_int_t)sz.dw, (ngx_int_t)sz.dh); *p = '\0'; ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "jpeg_size_opt:%s", jpeg_size_opt); MagickSetOption(ictx->wand, "jpeg:size", (char *)jpeg_size_opt); } /* load image. */ status = MagickReadImageBlob(ictx->wand, (void *)ictx->image, ictx->image_len); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "couldn't read image %s:%d", __FUNCTION__, __LINE__); return NGX_ERROR; } MagickSetFirstIterator(ictx->wand); color_space = MagickGetImageColorspace(ictx->wand); /* remove all profiles */ rmprof_flg = ngx_http_small_light_parse_flag(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "rmprof")); if (rmprof_flg != 0) { status = MagickProfileImage(ictx->wand, "*", NULL, 0); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "couldn't profiling image %s:%d", __FUNCTION__, __LINE__); } } of_orig = MagickGetImageFormat(ictx->wand); status = MagickTrue; #if MagickLibVersion >= 0x690 /* auto-orient */ autoorient_flg = ngx_http_small_light_parse_flag(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "autoorient")); if (autoorient_flg != 0) { status = MagickAutoOrientImage(ictx->wand); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; DestroyString(of_orig); return NGX_ERROR; } } #endif /* rotate. */ if (sz.angle) { bg_color = NewPixelWand(); PixelSetRed(bg_color, sz.cc.r / 255.0); PixelSetGreen(bg_color, sz.cc.g / 255.0); PixelSetBlue(bg_color, sz.cc.b / 255.0); PixelSetAlpha(bg_color, sz.cc.a / 255.0); switch (sz.angle) { case 90: case 180: case 270: MagickRotateImage(ictx->wand, bg_color, sz.angle); break; default: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "image not rotated. 'angle'(%ui) must be 90 or 180 or 270. %s:%d", sz.angle, __FUNCTION__, __LINE__); break; } DestroyPixelWand(bg_color); } /* calc size. */ iw = (double)MagickGetImageWidth(ictx->wand); ih = (double)MagickGetImageHeight(ictx->wand); ngx_http_small_light_calc_image_size(r, ctx, &sz, iw, ih); /* pass through. */ if (sz.pt_flg != 0) { ctx->of = ctx->inf; DestroyString(of_orig); return NGX_OK; } /* crop, scale. */ if (sz.scale_flg != 0) { p = ngx_snprintf(crop_geo, sizeof(crop_geo) - 1, "%f!x%f!+%f+%f", sz.sw, sz.sh, sz.sx, sz.sy); *p = '\0'; p = ngx_snprintf(size_geo, sizeof(size_geo) - 1, "%f!x%f!", sz.dw, sz.dh); *p = '\0'; ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "crop_geo:%s", crop_geo); ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "size_geo:%s", size_geo); MagickResetImagePage(ictx->wand, "+0+0"); trans_wand = MagickTransformImage(ictx->wand, (char *)crop_geo, (char *)size_geo); if (trans_wand == NULL || trans_wand == ictx->wand) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; DestroyString(of_orig); return NGX_ERROR; } DestroyMagickWand(ictx->wand); ictx->wand = trans_wand; } /* create canvas then draw image to the canvas. */ if (sz.cw > 0.0 && sz.ch > 0.0) { canvas_wand = NewMagickWand(); canvas_color = NewPixelWand(); PixelSetRed(canvas_color, sz.cc.r / 255.0); PixelSetGreen(canvas_color, sz.cc.g / 255.0); PixelSetBlue(canvas_color, sz.cc.b / 255.0); PixelSetAlpha(canvas_color, sz.cc.a / 255.0); status = MagickNewImage(canvas_wand, sz.cw, sz.ch, canvas_color); DestroyPixelWand(canvas_color); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; DestroyMagickWand(canvas_wand); DestroyString(of_orig); return NGX_ERROR; } status = MagickTransformImageColorspace(canvas_wand, color_space); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; DestroyMagickWand(canvas_wand); DestroyString(of_orig); return NGX_ERROR; } status = MagickCompositeImage(canvas_wand, ictx->wand, AtopCompositeOp, sz.dx, sz.dy); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; DestroyMagickWand(canvas_wand); DestroyString(of_orig); return NGX_ERROR; } DestroyMagickWand(ictx->wand); ictx->wand = canvas_wand; } /* CMYK to sRGB */ cmyk2rgb_flg = ngx_http_small_light_parse_flag(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "cmyk2rgb")); if (cmyk2rgb_flg != 0 && color_space == CMYKColorspace) { status = MagickTransformImageColorspace(ictx->wand, sRGBColorspace); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; DestroyString(of_orig); return NGX_ERROR; } } /* effects. */ unsharp = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "unsharp"); if (ngx_strlen(unsharp) > 0) { ParseGeometry(unsharp, &geo); if (geo.rho > ctx->radius_max || geo.sigma > ctx->sigma_max) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "As unsharp geometry is too large, ignored. %s:%d", __FUNCTION__, __LINE__); } else { status = MagickUnsharpMaskImage(ictx->wand, geo.rho, geo.sigma, geo.xi, geo.psi); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsharp failed %s:%d", __FUNCTION__, __LINE__); } } } sharpen = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "sharpen"); if (ngx_strlen(sharpen) > 0) { ParseGeometry(sharpen, &geo); if (geo.rho > ctx->radius_max || geo.sigma > ctx->sigma_max) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "As sharpen geometry is too large, ignored. %s:%d", __FUNCTION__, __LINE__); } else { status = MagickSharpenImage(ictx->wand, geo.rho, geo.sigma); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "sharpen failed %s:%d", __FUNCTION__, __LINE__); } } } blur = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "blur"); if (ngx_strlen(blur) > 0) { ParseGeometry(blur, &geo); if (geo.rho > ctx->radius_max || geo.sigma > ctx->sigma_max) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "As blur geometry is too large, ignored. %s:%d", __FUNCTION__, __LINE__); } else { status = MagickBlurImage(ictx->wand, geo.rho, geo.sigma); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "blur failed %s:%d", __FUNCTION__, __LINE__); } } } /* border. */ if (sz.bw > 0.0 || sz.bh > 0.0) { border_wand = NewDrawingWand(); border_color = NewPixelWand(); PixelSetRed(border_color, sz.bc.r / 255.0); PixelSetGreen(border_color, sz.bc.g / 255.0); PixelSetBlue(border_color, sz.bc.b / 255.0); PixelSetAlpha(border_color, sz.bc.a / 255.0); DrawSetFillColor(border_wand, border_color); DrawSetStrokeColor(border_wand, border_color); DrawSetStrokeWidth(border_wand, 1); if (sz.cw > 0.0 && sz.ch > 0.0) { DrawRectangle(border_wand, 0, 0, sz.cw - 1, sz.bh - 1); DrawRectangle(border_wand, 0, 0, sz.bw - 1, sz.ch - 1); DrawRectangle(border_wand, 0, sz.ch - sz.bh, sz.cw - 1, sz.ch - 1); DrawRectangle(border_wand, sz.cw - sz.bw, 0, sz.cw - 1, sz.ch - 1); } else { DrawRectangle(border_wand, 0, 0, sz.dw - 1, sz.bh - 1); DrawRectangle(border_wand, 0, 0, sz.bw - 1, sz.dh - 1); DrawRectangle(border_wand, 0, sz.dh - sz.bh, sz.dw - 1, sz.dh - 1); DrawRectangle(border_wand, sz.dw - sz.bw, 0, sz.dw - 1, sz.dh - 1); } MagickDrawImage(ictx->wand, border_wand); DestroyPixelWand(border_color); DestroyDrawingWand(border_wand); } /* embed icon */ embedicon = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "embedicon"); if (ctx->material_dir->len > 0 && ngx_strlen(embedicon) > 0) { if (ngx_strstrn((u_char *)embedicon, "/", 1 - 1)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid parameter 'embedicon':%s %s:%d", embedicon, __FUNCTION__, __LINE__); DestroyString(of_orig); return NGX_ERROR; } embedicon_len = ngx_strlen(embedicon); embedicon_path_len = ctx->material_dir->len + ngx_strlen("/") + embedicon_len; if (embedicon_path_len > sizeof(embedicon_path) - 1) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "embedicon path is too long. maximun value is %z %s:%d", sizeof(embedicon_path) - 1, __FUNCTION__, __LINE__); DestroyString(of_orig); return NGX_ERROR; } p = embedicon_path; p = ngx_cpystrn(p, ctx->material_dir->data, ctx->material_dir->len + 1); p = ngx_cpystrn(p, (u_char *)"/", 1 + 1); p = ngx_cpystrn(p, embedicon, embedicon_len + 1); if ((fd = ngx_open_file(embedicon_path, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0)) == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to open embeddedicon file:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); DestroyString(of_orig); return NGX_ERROR; } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to close:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); DestroyString(of_orig); return NGX_ERROR; } if (ngx_strstrn(embedicon_path, "..", 2 - 1)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid embeddedicon_path:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); DestroyString(of_orig); return NGX_ERROR; } icon_wand = NewMagickWand(); if (MagickReadImage(icon_wand, (char *)embedicon_path) == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to read embed icon image file:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); DestroyMagickWand(icon_wand); DestroyString(of_orig); return NGX_ERROR; } MagickCompositeImageChannel(ictx->wand, AllChannels, icon_wand, OverCompositeOp, sz.ix, sz.iy); DestroyMagickWand(icon_wand); } /* set params. */ q = ngx_http_small_light_parse_double(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "q")); if (q > 0.0) { MagickSetImageCompressionQuality(ictx->wand, q); } progressive_flg = ngx_http_small_light_parse_flag(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "progressive")); if (progressive_flg != 0) { MagickSetInterlaceScheme(ictx->wand, LineInterlace); } of = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "of"); if (ngx_strlen(of) > 0) { type = ngx_http_small_light_type(of); if (type == NGX_HTTP_SMALL_LIGHT_IMAGE_NONE) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "of is invalid(%s) %s:%d", of, __FUNCTION__, __LINE__); of = (char *)ngx_http_small_light_image_exts[ictx->type - 1]; } else if (type == NGX_HTTP_SMALL_LIGHT_IMAGE_WEBP) { #if defined(MAGICKCORE_WEBP_DELEGATE) ictx->type = type; #else ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "WebP is not supported %s:%d", __FUNCTION__, __LINE__); of = (char *)ngx_http_small_light_image_exts[ictx->type - 1]; #endif } else { ictx->type = type; } MagickSetFormat(ictx->wand, of); ctx->of = ngx_http_small_light_image_types[ictx->type - 1]; } else { MagickSetFormat(ictx->wand, of_orig); ctx->of = ctx->inf; } DestroyString(of_orig); ctx->content = MagickGetImageBlob(ictx->wand, &sled_image_size); ctx->content_length = sled_image_size; ngx_pfree(r->pool, ctx->content_orig); ictx->complete = 1; return NGX_OK; }
ngx_int_t ngx_http_small_light_imagemagick_process(ngx_http_request_t *r, ngx_http_small_light_ctx_t *ctx) { ngx_http_small_light_imagemagick_ctx_t *ictx; ngx_http_small_light_image_size_t sz; MagickBooleanType status; int rmprof_flg, progressive_flg; double iw, ih, q; char *jpeg_size_opt, *of_orig, *crop_geo, *size_geo; char *unsharp, *sharpen, *blur, *dealpha, *of; MagickWand *trans_wand, *canvas_wand; DrawingWand *border_wand; PixelWand *bg_color, *canvas_color, *border_color; GeometryInfo geo; ngx_fd_t fd; MagickWand *icon_wand; u_char *p, *embedicon, *embedicon_path; size_t embedicon_path_len, embedicon_len, sled_image_size; ngx_int_t type; status = MagickFalse; ictx = (ngx_http_small_light_imagemagick_ctx_t *)ctx->ictx; /* adjust image size */ ngx_http_small_light_calc_image_size(r, ctx, &sz, 10000.0, 10000.0); /* init */ ictx->wand = NewMagickWand(); /* prepare */ if (sz.jpeghint_flg != 0) { jpeg_size_opt = ngx_pcalloc(r->pool, 32 + 1); if (jpeg_size_opt == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to allocate memory from r->pool %s:%d", __FUNCTION__, __LINE__); return NGX_ERROR; } ngx_snprintf((u_char *)jpeg_size_opt, 32 + 1, "%dx%d", (ngx_int_t)sz.dw, (ngx_int_t)sz.dh); MagickSetOption(ictx->wand, "jpeg:size", jpeg_size_opt); } /* load image. */ status = MagickReadImageBlob(ictx->wand, (void *)ictx->image, ictx->image_len); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "couldn't read image %s:%d", __FUNCTION__, __LINE__); return NGX_ERROR; } /* remove all profiles */ rmprof_flg = ngx_http_small_light_parse_flag(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "rmprof")); if (rmprof_flg != 0) { status = MagickProfileImage(ictx->wand, "*", NULL, 0); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "couldn't profiling image %s:%d", __FUNCTION__, __LINE__); } } /* calc size. */ iw = (double)MagickGetImageWidth(ictx->wand); ih = (double)MagickGetImageHeight(ictx->wand); ngx_http_small_light_calc_image_size(r, ctx, &sz, iw, ih); /* pass through. */ if (sz.pt_flg != 0) { return NGX_OK; } of_orig = MagickGetImageFormat(ictx->wand); /* crop, scale. */ status = MagickTrue; if (sz.scale_flg != 0) { crop_geo = ngx_pcalloc(r->pool, 128 + 1); if (crop_geo == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to allocate memory from r->pool %s:%d", __FUNCTION__, __LINE__); return NGX_ERROR; } size_geo = ngx_pcalloc(r->pool, 128 + 1); if (size_geo == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to allocate memory from r->pool %s:%d", __FUNCTION__, __LINE__); return NGX_ERROR; } ngx_snprintf((u_char *)crop_geo, 128 + 1, "%f!x%f!+%f+%f", sz.sw, sz.sh, sz.sx, sz.sy); ngx_snprintf((u_char *)size_geo, 128 + 1, "%f!x%f!", sz.dw, sz.dh); trans_wand = MagickTransformImage(ictx->wand, crop_geo, size_geo); if (trans_wand == NULL || trans_wand == ictx->wand) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; return NGX_ERROR; } DestroyMagickWand(ictx->wand); ictx->wand = trans_wand; } /* rotate */ if (sz.angle) { bg_color = NewPixelWand(); PixelSetRed(bg_color, sz.cc.r / 255.0); PixelSetGreen(bg_color, sz.cc.g / 255.0); PixelSetBlue(bg_color, sz.cc.b / 255.0); PixelSetAlpha(bg_color, sz.cc.a / 255.0); switch (sz.angle) { case 90: case 180: case 270: MagickRotateImage(ictx->wand, bg_color, sz.angle); break; } DestroyPixelWand(bg_color); } /* create canvas then draw image to the canvas. */ if (sz.cw > 0.0 && sz.ch > 0.0) { canvas_wand = NewMagickWand(); canvas_color = NewPixelWand(); PixelSetRed(canvas_color, sz.cc.r / 255.0); PixelSetGreen(canvas_color, sz.cc.g / 255.0); PixelSetBlue(canvas_color, sz.cc.b / 255.0); PixelSetAlpha(canvas_color, sz.cc.a / 255.0); status = MagickNewImage(canvas_wand, sz.cw, sz.ch, canvas_color); DestroyPixelWand(canvas_color); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; return NGX_ERROR; } status = MagickCompositeImage(canvas_wand, ictx->wand, AtopCompositeOp, sz.dx, sz.dy); if (status == MagickFalse) { r->err_status = NGX_HTTP_INTERNAL_SERVER_ERROR; return NGX_ERROR; } DestroyMagickWand(ictx->wand); ictx->wand = canvas_wand; } /* effects. */ unsharp = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "unsharp"); if (unsharp != NULL) { ParseGeometry(unsharp, &geo); status = MagickUnsharpMaskImage(ictx->wand, geo.rho, geo.sigma, geo.xi, geo.psi); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsharp failed %s:%d", __FUNCTION__, __LINE__); } } sharpen = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "sharpen"); if (sharpen != NULL) { ParseGeometry(sharpen, &geo); status = MagickSharpenImage(ictx->wand, geo.rho, geo.sigma); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "sharpen failed %s:%d", __FUNCTION__, __LINE__); } } blur = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "blur"); if (blur) { ParseGeometry(blur, &geo); status = MagickBlurImage(ictx->wand, geo.rho, geo.sigma); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "blur failed %s:%d", __FUNCTION__, __LINE__); } } dealpha = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "dealpha"); if (dealpha != NULL) { status = MagickSetImageAlphaChannel(ictx->wand, DeactivateAlphaChannel); if (status == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "dealpha failed %s:%d", __FUNCTION__, __LINE__); } } /* border. */ if (sz.bw > 0.0 || sz.bh > 0.0) { border_wand = NewDrawingWand(); border_color = NewPixelWand(); PixelSetRed(border_color, sz.bc.r / 255.0); PixelSetGreen(border_color, sz.bc.g / 255.0); PixelSetBlue(border_color, sz.bc.b / 255.0); PixelSetAlpha(border_color, sz.bc.a / 255.0); DrawSetFillColor(border_wand, border_color); DrawSetStrokeColor(border_wand, border_color); DrawSetStrokeWidth(border_wand, 1); if (sz.cw > 0.0 && sz.ch > 0.0) { DrawRectangle(border_wand, 0, 0, sz.cw - 1, sz.bh - 1); DrawRectangle(border_wand, 0, 0, sz.bw - 1, sz.ch - 1); DrawRectangle(border_wand, 0, sz.ch - sz.bh, sz.cw - 1, sz.ch - 1); DrawRectangle(border_wand, sz.cw - sz.bw, 0, sz.cw - 1, sz.ch - 1); } else { DrawRectangle(border_wand, 0, 0, sz.dw - 1, sz.bh - 1); DrawRectangle(border_wand, 0, 0, sz.bw - 1, sz.dh - 1); DrawRectangle(border_wand, 0, sz.dh - sz.bh, sz.dw - 1, sz.dh - 1); DrawRectangle(border_wand, sz.dw - sz.bw, 0, sz.dw - 1, sz.dh - 1); } MagickDrawImage(ictx->wand, border_wand); DestroyPixelWand(border_color); DestroyDrawingWand(border_wand); } /* embed icon */ embedicon = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "embedicon"); if (ngx_strlen(ctx->material_dir) > 0 && ngx_strlen(embedicon) > 0) { if (ngx_strstrn((u_char *)embedicon, "/", 1 - 1)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid parameter 'embedicon':%s %s:%d", embedicon, __FUNCTION__, __LINE__); return NGX_ERROR; } icon_wand = NewMagickWand(); embedicon_len = ngx_strlen(embedicon); embedicon_path_len = ctx->material_dir->len + ngx_strlen("/") + embedicon_len; embedicon_path = ngx_palloc(r->pool, embedicon_path_len + 1); if (embedicon_path == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to allocate memory from r->pool %s:%d", __FUNCTION__, __LINE__); return NGX_ERROR; } p = embedicon_path; p = ngx_cpystrn(p, ctx->material_dir->data, ctx->material_dir->len + 1); p = ngx_cpystrn(p, (u_char *)"/", 1 + 1); p = ngx_cpystrn(p, embedicon, embedicon_len + 1); if ((fd = ngx_open_file(embedicon_path, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0)) == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to open embeddedicon file:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); return NGX_ERROR; } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to close:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); return NGX_ERROR; } if (ngx_strstrn(embedicon_path, "..", 2 - 1)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid embeddedicon_path:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); return NGX_ERROR; } if (MagickReadImage(icon_wand, (char *)embedicon_path) == MagickFalse) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to read embed icon image file:%s %s:%d", embedicon_path, __FUNCTION__, __LINE__); return NGX_ERROR; } MagickCompositeImageChannel(ictx->wand, AllChannels, icon_wand, OverCompositeOp, sz.ix, sz.iy); ClearMagickWand(icon_wand); } /* set params. */ q = ngx_http_small_light_parse_double(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "q")); if (q > 0.0) { MagickSetImageCompressionQuality(ictx->wand, q); } progressive_flg = ngx_http_small_light_parse_flag(NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "progressive")); if (progressive_flg != 0) { MagickSetInterlaceScheme(ictx->wand, LineInterlace); } of = NGX_HTTP_SMALL_LIGHT_PARAM_GET_LIT(&ctx->hash, "of"); if (ngx_strlen(of) > 0) { type = ngx_http_small_light_type(of); if (type == NGX_HTTP_SMALL_LIGHT_IMAGE_NONE) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "of is invalid(%s) %s:%d", of, __FUNCTION__, __LINE__); of = (char *)ngx_http_small_light_image_exts[ictx->type - 1]; } else if (type == NGX_HTTP_SMALL_LIGHT_IMAGE_WEBP) { #if defined(MAGICKCORE_WEBP_DELEGATE) ictx->type = type; #else ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "WebP is not supported %s:%d", __FUNCTION__, __LINE__); of = (char *)ngx_http_small_light_image_exts[ictx->type - 1]; #endif } else { ictx->type = type; } MagickSetFormat(ictx->wand, of); ctx->of = ngx_http_small_light_image_types[ictx->type - 1]; } else { MagickSetFormat(ictx->wand, of_orig); ctx->of = ctx->inf; } ctx->content = MagickGetImageBlob(ictx->wand, &sled_image_size); ctx->content_length = sled_image_size; ictx->complete = 1; return NGX_OK; }