Пример #1
0
static bool HHVM_METHOD(ImagickDraw, setStrokeColor,
    const Variant& stroke_pixel) {
  auto wand = getDrawingWandResource(Object{this_});
  auto pixel(buildColorWand(stroke_pixel));
  DrawSetStrokeColor(wand->getWand(), pixel->getWand());
  return true;
}
Пример #2
0
MAGICK_NET_EXPORT void DrawingWand_StrokeColor(DrawingWand *instance, PixelInfo *color, ExceptionInfo **exception)
{
  MAGICK_NET_GET_PIXEL_WAND(color);
  DrawSetStrokeColor(instance, pixel_wand);
  MAGICK_NET_REMOVE_PIXEL_WAND;
  MAGICK_NET_SET_DRAW_EXCEPTION;
}
Пример #3
0
char* get_phone_img(const char *phone_str, size_t *img_size){
    if(phone_str == NULL){
	return NULL;
    }

    MagickWand *m_wand  = NULL;
    PixelWand *p_wand  = NULL;
    DrawingWand *d_wand = NULL;


    /* Create a wand */
    m_wand = NewMagickWand();
    p_wand = NewPixelWand();
    d_wand = NewDrawingWand();

    PixelSetColor(p_wand,"white");

    int height = 18;
    int width = strlen(phone_str) * 130 / 12;

    MagickNewImage(m_wand, width,height ,p_wand);

    //draw number
    PixelSetColor(p_wand,"black");
    DrawSetFillColor(d_wand,p_wand);
    DrawSetFont (d_wand, "Arial" ) ;
    DrawSetFontSize(d_wand,20);
    DrawSetStrokeColor(d_wand,p_wand);
    DrawAnnotation(d_wand,0,height -2,phone_str);
    MagickDrawImage(m_wand,d_wand);
    //MagickTrimImage(m_wand,0);
    //ImageFormat MUST be SET,otherwise,otherwise we will not MagickGetImageBlob properly
    MagickSetImageFormat(m_wand,"JPEG");

    char *p = NULL;
    char *data = NULL;

    p = (char *)MagickGetImageBlob(m_wand,img_size);

    if(p != NULL){
	data = (char *)malloc(*img_size);
	if(data != NULL){
	    memcpy(data,p,*img_size);
	}else{
	    LOG_PRINT(LOG_INFO, "malloc Failed!");
	}
    }else{
	LOG_PRINT(LOG_INFO, "MagickGetImageBlob Failed!");
    }
    
    /* Tidy up */
    MagickRelinquishMemory(p);
    DestroyMagickWand(m_wand);
    DestroyPixelWand(p_wand);

    return data;
}
Пример #4
0
/**
 * Prints count lines on output with background bg. Lines can be scaled by using factor.
 * NOTE: This performs some filtering removing lines passing through origin
 */
void print_lines(char* output, char* bg, LINE_TYPE* lines, sizep_t count, double factor) {
	MagickWand *mw = NULL;
	DrawingWand *dw = NULL;
	PixelWand *pmw = NULL;
	unsigned long width, semi_width, height, semi_height;

	MagickWandGenesis();
	mw = NewMagickWand();
	dw = NewDrawingWand();
	pmw = NewPixelWand();

	MagickReadImage(mw, bg);
	width = MagickGetImageWidth(mw);
	semi_width = ceil(width/2.0);
	height = MagickGetImageHeight(mw);
	semi_height = ceil(height/2.0);
	PixelSetColor(pmw,"red");
	DrawSetStrokeColor(dw, pmw);
	DrawSetStrokeWidth(dw, .5*factor);
	DrawSetStrokeAntialias(dw, 0);

	sizep_t n;
	for(n=0; n<count; n++) {
		LINE_TYPE cline = lines[n];

		double m = -cos(cline.t)/sin(cline.t);
		double b = cline.r/sin(cline.t);

		if ((-0.5 < m && m < 0.5) || (-1 < b && b < 1)) continue; // remove lines too horizontal

		double x0 = - ((long) semi_width); double y0 = x0*m+b;
		double x1 = ((long) semi_width); double y1 = x1*m+b;

		// Apply factor
		x0 *= factor; y0 *= factor; x1 *= factor; y1 *= factor;

		// Fix coordinates and plot over the image
		DrawLine(dw, x0+semi_width, height-y0-semi_height, x1+semi_width, height-y1-semi_height);
	}

	MagickDrawImage(mw,dw);
	MagickWriteImage(mw, output);

	pmw = DestroyPixelWand(pmw);
	mw = DestroyMagickWand(mw);
	dw = DestroyDrawingWand(dw);

	MagickWandTerminus();
}
Пример #5
0
/* {{{ proto GmagickDraw GmagickDraw::setStrokeColor(PixelWand stroke_wand)
	Sets the color used for stroking object outlines.
*/
PHP_METHOD(gmagickdraw, setstrokecolor)
{
	zval *param;
	php_gmagickdraw_object *internd;
	php_gmagickpixel_object *internp;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &param) == FAILURE) {
		return;
	}

	internd = (php_gmagickdraw_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
	
	GMAGICK_CAST_PARAMETER_TO_COLOR(param, internp, 2);

	if (internd->drawing_wand != NULL) {
		DrawSetStrokeColor(internd->drawing_wand, internp->pixel_wand);
	}

	GMAGICK_CHAIN_METHOD;
}
Пример #6
0
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();
}
Пример #7
0
static MagickBooleanType ScribbleImage (MagickWand *canvas)
{
  DrawingWand
    *picasso;

  PixelWand
    *color;

  picasso=NewDrawingWand();
  color=NewPixelWand();
  (void) PushDrawingWand(picasso);
  {
    DrawSetViewbox(picasso,0,0,(ssize_t) MagickGetImageWidth(canvas),
      (ssize_t) MagickGetImageHeight(canvas));
    DrawScale(picasso,1.101,1.08);
    DrawTranslate(picasso,-23.69,-22.97);
    DrawRotate(picasso,0);
    (void) PixelSetColor(color,"#ffffff");
    DrawSetFillColor(picasso,color);
    DrawRectangle(picasso,23.69,22.97,564.6,802.2);
    DrawSetFillAlpha(picasso,1.0);
    (void) PixelSetColor(color,"none");
    DrawSetFillColor(picasso,color);
    DrawSetStrokeColor(picasso,color);
    DrawSetStrokeAntialias(picasso,MagickTrue);
    DrawSetStrokeLineCap(picasso,RoundCap);
    DrawSetStrokeLineJoin(picasso,RoundJoin);
    DrawPushDefs(picasso);
    {
      DrawPushClipPath(picasso,"clip_1");
      {
        (void) PushDrawingWand(picasso);
        {
          DrawRectangle(picasso,0,0,595.3,841.9);
        }
        (void) PopDrawingWand(picasso);
      }
      DrawPopClipPath(picasso);
    }
    DrawPopDefs(picasso);
    (void) PushDrawingWand(picasso);
    {
      (void) DrawSetClipPath(picasso, "url(#clip_1)");

      (void) PushDrawingWand(picasso);
      {
        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,4.032);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#ff0000");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#ff00ff");
        DrawSetFillColor(picasso,color);
        DrawRectangle(picasso,72,72,144,144);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,9);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#00ff00");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#0080ff");
        DrawSetFillColor(picasso,color);
        DrawRoundRectangle(picasso,72,216,360,432,9,9);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[37] =
        {
          { 378.1,81.72 }, { 381.1,79.56 }, { 384.3,78.12 }, { 387.6,77.33 },
          { 391.1,77.11 }, { 394.6,77.62 }, { 397.8,78.77 }, { 400.9,80.57 },
          { 403.6,83.02 }, { 523.9,216.8 }, { 526.2,219.7 }, { 527.6,223 },
          { 528.4,226.4 }, { 528.6,229.8 }, { 528,233.3 },   { 526.9,236.5 },
          { 525.1,239.5 }, { 522.6,242.2 }, { 495.9,266.3 }, { 493,268.5 },
          { 489.7,269.9 }, { 486.4,270.8 }, { 482.9,270.9 }, { 479.5,270.4 },
          { 476.2,269.3 }, { 473.2,267.5 }, { 470.4,265 },   { 350,131.2 },
          { 347.8,128.3 }, { 346.4,125.1 }, { 345.6,121.7 }, {345.4,118.2 },
          { 346,114.8 },   { 347.1,111.5 }, { 348.9,108.5 }, { 351.4,105.8 },
          { 378.1,81.72 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,2.016);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#000080");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#c2c280");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,37,points);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,3.024);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#000080");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#000080");
        DrawSetFillColor(picasso,color);
        DrawEllipse(picasso,489.6,424.8,72,129.6,0,360);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[48] =
        {
          { 213.8,25.13},  { 216.7,24.48 }, {219.8,24.55 },  { 223.1,25.42 },
          { 226.7,27 },    { 230.3,29.3 },  { 234.1,32.26 }, { 237.9,35.86 },
          { 241.8,40.03 }, { 249.7,50.11 }, { 257.4,62.14 }, { 264.8,75.89 },
          { 271.6,91.15 }, { 277.3,106.8 }, { 281.6,121.8 }, { 284.4,135.9 },
          { 285.7,148.5 }, { 285.6,159.6 }, { 284.9,164.3 }, { 283.8,168.5 },
          { 282.5,172.1 }, { 280.7,175 },   { 278.5,177.3 }, { 275.9,178.7 },
          { 273,179.4 },   { 269.9,179.3 }, { 266.6,178.4 }, { 263.1,176.8 },
          { 259.5,174.5},  { 255.7,171.6 }, { 251.9,168 },   { 248,163.8 },
          { 244.1,159 },   { 240.1,153.7 }, { 232.3,141.7 }, { 225,127.9 },
          { 218.2,112.7 }, { 212.5,97.06 }, { 208.2,82.01 }, { 205.4,67.97 },
          { 204,55.3 },    { 204.3,44.35 }, { 204.9,39.6 },  { 205.9,35.42 },
          { 207.4,31.82 }, { 209.2,28.87 }, { 211.3,26.64},  { 213.8,25.13 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,3.024);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#ff8000");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#00ffff");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,48,points);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,12.02);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        (void) PixelSetColor(color,"none");
        DrawSetFillColor(picasso,color);
        DrawArc(picasso,360,554.4,187.2,237.6,0,90);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,9);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetFillColor(picasso,color);
        DrawEllipse(picasso,388.8,626.4,100.8,122.4,0,90);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[6] =
        {
          { 180,504 }, { 282.7,578.6 }, { 243.5,699.4 }, { 116.5,699.4 },
          { 77.26,578.6 }, { 180,504 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,9);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#800000");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,6,points);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[11] =
        {
          { 180,504 },     { 211.8,568.3 }, { 282.7,578.6 }, { 231.3,628.7 },
          { 243.5,699.4 }, { 180,666 },     { 116.5,699.4 }, { 128.7,628.7 },
          { 77.26,578.6 }, { 148.2,568.3 }, { 180,504 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,9);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#800000");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,11,points);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[15] =
        {
          { 540,288 },     { 561.6,216 },   { 547.2,43.2 },  { 280.8,36 },
          { 302.4,194.4 }, { 331.2,64.8 },  { 504,64.8 },    { 475.2,115.2 },
          { 525.6,93.6 },  { 496.8,158.4 }, { 532.8,136.8 }, { 518.4,180 },
          { 540,172.8 },   { 540,223.2 },   { 540,288 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,5.976);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#ffff00");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,15,points);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[7] =
        {
          { 57.6,640.8 }, { 57.6,784.8 }, { 194.4,799.2 }, { 259.2,777.6 },
          { 151.2,756 }, { 86.4,748.8 }, { 57.6,640.8 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,5.976);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#ffff00");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,7,points);
      }
      (void) PopDrawingWand(picasso);
      (void) PushDrawingWand(picasso);
      {
        const PointInfo points[193] =
        {
          { 27.86,565.3 }, { 29.66,550.8 }, { 31.97,538.1 }, { 34.85,527.1 },
          { 38.09,517.7 }, { 41.83,509.8 }, { 45.86,503.1 }, { 50.33,497.6 },
          { 55.08,493.2 }, { 60.19,489.8 }, { 65.45,487.3 }, { 70.92,485.4 },
          { 76.61,484.2 }, { 88.42,483 },   { 100.4,482.9 }, { 108.4,482.2 },
          { 119.8,480.3 }, { 150.8,474.1 }, { 189.4,466.6 }, { 210.3,463 },
          { 231.5,459.9 }, { 252.4,457.8 }, { 272.7,456.6 }, { 291.8,456.9 },
          { 300.7,457.7 }, { 309.1,458.9 }, { 316.9,460.6 }, { 324.1,462.8 },
          { 330.7,465.6 }, { 336.4,469 },   { 341.3,473 },   { 345.3,477.7 },
          { 348.4,483.1 }, { 350.4,489.2},  { 352.4,495.4 }, { 355.2,500.9 },
          { 358.8,505.8 }, { 363,510 },     { 367.8,513.6 }, { 373,516.8 },
          { 378.6,519.6 }, { 384.3,521.8 }, { 396.4,525.4 }, { 408.2,527.9 },
          { 428,531.2 },   { 434.6,532.9 }, { 436.7,533.8 }, { 437.8,534.9 },
          { 437.8,536.2 }, { 436.8,537.8 }, { 434.5,539.6 }, { 430.9,541.8 },
          { 419.3,547.6 }, { 401.3,555.2 }, { 342.4,577.9 }, {325.2,584.9 },
          { 311,591.3 },   { 300,597.3 },   { 291.6,602.8 }, { 285.8,607.8 },
          { 282.3,612.3 }, { 281.4,614.4 }, { 280.9,616.2 }, { 281.2,619.6 },
          { 282.1,621.2 }, { 283.3,622.6 }, { 286.8,624.9 }, { 291.5,626.6 },
          { 297.1,627.8 }, { 303.6,628.3 }, { 310.5,628.3 }, { 317.9,627.6 },
          { 325.2,626.3 }, { 332.6,624.3 }, { 339.5,621.7 }, { 345.9,618.4 },
          { 351.4,614.4 }, { 353.9,612.2 }, { 356,609.8 }, { 357.9,607.1 },
          { 359.4,604.3 }, { 360.6,601.3 }, { 361.4,598.2 }, { 361.7,594.9 },
          { 361.7,591.3 }, { 361.2,587.7 }, { 360.1,583.7 }, { 358.6,579.7 },
          { 356.4,575.4 }, { 353.7,570.9 }, { 350.4,566.2 }, { 346.4,561.3 },
          { 341.8,556.2 }, { 336.5,550.9 }, { 330.6,545.5 }, { 323.8,539.8 },
          { 316.2,533.9 }, { 298.7,521.5 }, { 277.8,508.2 }, { 256.1,495.5 },
          { 236,484.5 },   { 217.7,475.1 }, { 200.8,467.1 }, { 185.6,460.7 },
          { 171.9,455.5 }, { 159.6,451.6 }, { 148.6,448.8 }, { 139,447 },
          { 130.5,446.2 }, { 123.3,446.2 }, { 117.1,446.9 }, { 112,448.3 },
          { 107.9,450.2 }, { 104.8,452.5 }, { 102.5,455.2 }, { 101,458.1 },
          { 100.2,461.2 }, { 100.2,464.3 }, { 100.7,467.4 }, { 101.8,470.3 },
          { 103.4,473 },   { 105.4,475.3 }, { 107.8,477.1 }, { 110.5,478.4 },
          { 113.4,479.1 }, { 116.5,478.9 }, { 119.7,478 },   { 123,476.2 },
          { 126.4,473.3 }, { 129.6,469.2 }, { 132.7,463.9 }, { 135.2,458.4 },
          { 136.6,453.7 }, { 137,449.9 },   { 136.6,446.8 }, { 135.4,444.5 },
          { 133.3,442.9 }, { 130.8,441.9 }, { 127.5,441.4 }, { 123.9,441.6 },
          { 119.8,442.3 }, { 110.7,445.1 }, { 101.1,449.5 }, { 91.37,455.2 },
          { 82.37,461.9 }, { 74.66,469.2 }, { 71.57,473 },   { 68.98,476.8 },
          { 67.03,480.7 }, { 65.81,484.4 }, { 65.45,488.2 }, { 65.95,491.7 },
          { 67.46,495.1 }, { 69.98,498.3 }, { 73.66,501.3 }, { 78.55,503.9 },
          { 84.82,506.3 }, { 92.38,508.2 }, { 107.1,511.6 }, { 118.2,514.8 },
          { 125.9,517.8 }, { 130.7,520.4 }, { 132.1,521.7 }, { 132.8,522.9 },
          { 133,524.2 },   { 132.6,525.3 }, { 131.8,526.5 }, { 130.5,527.5 },
          { 126.6,529.6 }, { 121.5,531.7 }, { 115.3,533.7 }, { 101.4,537.6 },
          { 87.55,541.8 }, { 81.36,544 },   { 76.25,546.3 }, { 71.64,549.5 },
          { 66.89,554.1 }, { 62.14,559.8 }, { 57.38,566.1 }, { 48.17,579.6 },
          { 39.96,591.4 }, { 36.43,595.9 }, { 34.78,597.6 }, { 33.26,598.8 },
          { 31.9,599.6 },  { 30.67,599.9 }, { 29.59,599.7 }, { 28.66,598.8 },
          { 27.86,597.4 }, { 27.29,595.2 }, { 26.64,588.7 }, { 26.86,578.8 },
          { 27.86,565.3 }
        };

        DrawSetStrokeAntialias(picasso,MagickTrue);
        DrawSetStrokeWidth(picasso,5.904);
        DrawSetStrokeLineCap(picasso,RoundCap);
        DrawSetStrokeLineJoin(picasso,RoundJoin);
        (void) DrawSetStrokeDashArray(picasso,0,(const double *)NULL);
        (void) PixelSetColor(color,"#4000c2");
        DrawSetStrokeColor(picasso,color);
        DrawSetFillRule(picasso,EvenOddRule);
        (void) PixelSetColor(color,"#ffff00");
        DrawSetFillColor(picasso,color);
        DrawPolygon(picasso,193,points);
      }
      (void) PopDrawingWand(picasso);
    }
    (void) PopDrawingWand(picasso);
  }
  (void) PopDrawingWand(picasso);
  (void) MagickDrawImage(canvas,picasso);
  color=DestroyPixelWand(color);
  picasso=DestroyDrawingWand(picasso);
  return(MagickTrue);
}
// 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);
}
Пример #9
0
void
generate(char *source_image_path, char *sink_image_path, char *top_text, char *bottom_text)
{

  MagickWand *wand = NULL;
  DrawingWand *drawing_wand = NULL;
  PixelWand *pixel_wand = NULL;

  MagickWandGenesis();

  wand = NewMagickWand();
  drawing_wand = NewDrawingWand();
  pixel_wand = NewPixelWand();

  // read base image
  MagickReadImage(wand, source_image_path);

  ssize_t width;
  ssize_t pointsize;
  ssize_t stroke_width;
  double scale;
  char formatted_text[100] = { '\0' };

  // Scale text
  width = MagickGetImageWidth(wand);
  scale = scaleText(top_text, formatted_text);
  pointsize = width / 5.0;
  stroke_width = pointsize / 30.0;

  // Draw top text
  PixelSetColor(pixel_wand, "white");
  DrawSetFillColor(drawing_wand, pixel_wand);
  DrawSetFont(drawing_wand, "Impact");
  DrawSetFontSize(drawing_wand, pointsize * scale);
  DrawSetFontWeight(drawing_wand, 700);
  DrawSetGravity(drawing_wand, NorthGravity);

  // Add a black outline to the text
  PixelSetColor(pixel_wand, "black");
  DrawSetStrokeWidth(drawing_wand, stroke_width * scale);
  DrawSetStrokeColor(drawing_wand, pixel_wand);

  // Turn on Anitalias
  DrawSetTextAntialias(drawing_wand, MagickTrue);

  // Draw the text
  DrawAnnotation(drawing_wand, 0, 0, (const unsigned char *)formatted_text);

  if (bottom_text) {
    char formatted_bottom_text[100] = { '\0' };

    // Scale text
    width = MagickGetImageWidth(wand);
    scale = scaleText(bottom_text, formatted_bottom_text);
    pointsize = width / 5.0;
    stroke_width = pointsize / 30.0;

    // Draw bottom text
    PixelSetColor(pixel_wand, "white");
    DrawSetFillColor(drawing_wand, pixel_wand);
    DrawSetFont(drawing_wand, "Impact");
    DrawSetFontSize(drawing_wand, pointsize * scale);
    DrawSetFontWeight(drawing_wand, 700);
    DrawSetGravity(drawing_wand, SouthGravity);

    // Add a black outline to the text
    PixelSetColor(pixel_wand, "black");
    DrawSetStrokeWidth(drawing_wand, stroke_width * scale);
    DrawSetStrokeColor(drawing_wand, pixel_wand);

    // Turn on Anitalias
    DrawSetTextAntialias(drawing_wand, MagickTrue);

    // Draw the text
    DrawAnnotation(drawing_wand, 0, 0, (const unsigned char *)formatted_bottom_text);
  }

  // Draw the image on the magick wand
  MagickDrawImage(wand, drawing_wand);

  // Write the image
  if (sink_image_path) {
    MagickWriteImage(wand, sink_image_path);
  } else {
    MagickWriteImageFile(wand, stdout);
  }

  // Clean up
  if(pixel_wand) pixel_wand = DestroyPixelWand(pixel_wand);
  if(drawing_wand) drawing_wand = DestroyDrawingWand(drawing_wand);
  if(wand) wand = DestroyMagickWand(wand);

  MagickWandTerminus();
}
Пример #10
0
/* gcc -o imagick imagick-landscape-3d.c -Wall -Werror -I/usr/include/ImageMagick -lMagickWand */
int main()
{
  MagickWand *mw, *canvas;
  PixelWand  *pw;
  DrawingWand *line;
  size_t w, h, offset;
  int x, y, r, g, b, grey, lh;
  MagickBooleanType mbt;

  MagickWandGenesis();  /* startup */

  mw = NewMagickWand();

  mbt = MagickReadImage(mw, PNG_IN_FILE);
  assert(mbt == MagickTrue);
  w = MagickGetImageWidth(mw);
  h = MagickGetImageHeight(mw);

  pw = NewPixelWand();
  PixelSetColor(pw, "transparent");

  mbt = MagickShearImage(mw, pw, 45, 0);
  assert(mbt == MagickTrue);

  w = MagickGetImageWidth(mw);
  h = MagickGetImageHeight(mw);

  mbt = MagickScaleImage(mw, w, h/2);
  assert(mbt = MagickTrue);

  w = MagickGetImageWidth(mw);
  h = MagickGetImageHeight(mw);

  canvas = NewMagickWand();
  MagickGetImagePixelColor(mw, 0, 0, pw);
  MagickNewImage(canvas, w, h*2, pw);

  offset = h;
  for (x = 0; x < w; ++x) {
    line = NewDrawingWand();
    lh = 0;
    for (y = h-1; y >= 0; --y) {
      if (MagickGetImagePixelColor(mw, x, y, pw) == MagickFalse) continue;
      r = 255 * PixelGetRed(pw);
      g = 255 * PixelGetGreen(pw);
      b = 255 * PixelGetBlue(pw);

      grey = (r + g + b)/5;
      if (lh == 0 || lh < grey) {
        DrawSetFillColor(line, pw);
        DrawSetStrokeColor(line, pw);
        DrawLine(line, x, y + offset - lh, x, y - grey + offset);
        lh = grey;
      }
      lh--;
    }

    MagickDrawImage(canvas, line);
    DestroyDrawingWand(line);
  }

  MagickScaleImage(canvas, w - h, h * 2);
  
  mbt = MagickSetImageFormat(canvas, "png");
  assert(mbt == MagickTrue);
  mbt = MagickWriteImage(canvas, PNG_OUT_FILE);
  assert(mbt == MagickTrue);

  pw = DestroyPixelWand(pw);
  mw = DestroyMagickWand(mw);
  canvas = DestroyMagickWand(canvas);
  
  MagickWandTerminus(); /* shutdown */

  return 0;
}  
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;
}