int magickCompressImage(const char *imageFile, const char *outputImage, const char *format, int compressionType, double compressionRate){ MagickBooleanType status; MagickWand *magick_wand; /* Read an image. */ // printf("compressionRate: %d", compressionRate); MagickWandGenesis(); magick_wand=NewMagickWand(); status=MagickReadImage(magick_wand, (char *)imageFile); if (status == MagickFalse){ ThrowWandException(magick_wand); } /* Turn the images into a thumbnail sequence. */ // MagickResetIterator(magick_wand); // while (MagickNextImage(magick_wand) != MagickFalse){ MagickSetFormat(magick_wand, (char *)format); MagickSetImageCompression(magick_wand, compressionType); MagickSetImageCompressionQuality(magick_wand, compressionRate); // } /* Write the image then destroy it. */ status=MagickWriteImages(magick_wand, (char *)outputImage, MagickTrue); if (status == MagickFalse) ThrowWandException(magick_wand); magick_wand=DestroyMagickWand(magick_wand); MagickWandTerminus(); return status; }
static int set_wi_quality(lua_State *L) { int quality = lua_tonumber(L, 1); lua_arg *larg = pthread_getspecific(thread_key); int ret = MagickSetImageCompressionQuality(larg->img, quality); lua_pushnumber(L, ret); return 1; }
apr_status_t dims_quality_operation (dims_request_rec *d, char *args, char **err) { int quality = apr_strtoi64(args, NULL, 0); int existing_quality = MagickGetImageCompressionQuality(d->wand); if(existing_quality == 0 || quality < existing_quality) { MAGICK_CHECK(MagickSetImageCompressionQuality(d->wand, quality), d); } return DIMS_SUCCESS; }
int main(int argc, char* argv[]) { if (argc < 3) { fprintf (stderr, "Usage : %s IN_FILE OUT_FILE\n", argv[0]); exit(1); } MagickWand* m_wand; int w, h; MagickWandGenesis (); m_wand = NewMagickWand (); if (MagickReadImage (m_wand, argv[1]) == MagickFalse) { fprintf(stderr, "Cannot read image : %s\n", argv[1]); exit(1); } w = MagickGetImageWidth (m_wand); h = MagickGetImageHeight (m_wand); MagickResizeImage (m_wand, w/2, h/2, LanczosFilter, 1.0); MagickSetImageCompressionQuality (m_wand, QUALITY); if (MagickWriteImage (m_wand, argv[2]) == MagickFalse) { fprintf (stderr, "Cannot wright image : %s\n", argv[2]); exit(1); } if (m_wand) { m_wand = DestroyMagickWand (m_wnad); } MagickWandTerminus (); return 0; }
int convert2jpg(const char *buff, const int len,const char *path){ //http://members.shaw.ca/el.supremo/MagickWand/resize.htm //MagickGetImageFormat MagickWand *m_wand = NULL; m_wand = NewMagickWand(); MagickReadImageBlob(m_wand, buff, len); //strip exif,GPS data MagickStripImage(m_wand); // Set the compression quality to 75 (high quality = low compression) MagickSetImageCompressionQuality(m_wand,75); /* Write the new image */ MagickWriteImage(m_wand,path); /* Clean up */ if(m_wand)m_wand = DestroyMagickWand(m_wand); return ZIMG_OK; }
/** * @brief get_img_mode_db Get image from db mode backend. * * @param req zimg request struct * @param buff_ptr buff pointer * @param img_size buff size * * @return 1 for success and -1 for failed */ int get_img_mode_db(zimg_req_t *req, evhtp_request_t *request) { int result = -1; char cache_key[CACHE_KEY_SIZE]; char *img_format = NULL; char *buff_ptr = NULL; size_t img_size; MagickBooleanType status; MagickWand *magick_wand = NULL; bool got_rsp = true; bool got_color = false; LOG_PRINT(LOG_DEBUG, "get_img() start processing zimg request..."); if(req->gray == 1) { gen_key(cache_key, req->md5, 4, req->width, req->height, req->proportion, req->gray); } else { if(req->proportion == 0 && req->width == 0 && req->height == 0) { gen_key(cache_key, req->md5, 0); } else { gen_key(cache_key, req->md5, 3, req->width, req->height, req->proportion); } } if(find_cache_bin(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Hit Cache[Key: %s].", cache_key); goto done; } LOG_PRINT(LOG_DEBUG, "Start to Find the Image..."); if(get_img_db(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Get image [%s] from backend db succ.", cache_key); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } goto done; } magick_wand = NewMagickWand(); got_rsp = false; if(req->gray == 1) { gen_key(cache_key, req->md5, 3, req->width, req->height, req->proportion); if(find_cache_bin(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Hit Color Image Cache[Key: %s, len: %d].", cache_key, img_size); status = MagickReadImageBlob(magick_wand, buff_ptr, img_size); free(buff_ptr); buff_ptr = NULL; if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Color Image Cache[Key: %s] is Bad. Remove.", cache_key); del_cache(req->thr_arg, cache_key); } else { got_color = true; LOG_PRINT(LOG_DEBUG, "Read Image from Color Image Cache[Key: %s, len: %d] Succ. Goto Convert.", cache_key, img_size); goto convert; } } if(get_img_db(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Get color image [%s] from backend db.", cache_key); status = MagickReadImageBlob(magick_wand, buff_ptr, img_size); if(status == MagickTrue) { got_color = true; LOG_PRINT(LOG_DEBUG, "Read Image from Color Image[%s] Succ. Goto Convert.", cache_key); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } free(buff_ptr); buff_ptr = NULL; goto convert; } } } gen_key(cache_key, req->md5, 0); if(find_cache_bin(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Hit Cache[Key: %s].", cache_key); } else { if(get_img_db(req->thr_arg, cache_key, &buff_ptr, &img_size) == -1) { LOG_PRINT(LOG_DEBUG, "Get image [%s] from backend db failed.", cache_key); goto err; } else if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } } status = MagickReadImageBlob(magick_wand, buff_ptr, img_size); free(buff_ptr); buff_ptr = NULL; if(status == MagickFalse) { ThrowWandException(magick_wand); del_cache(req->thr_arg, cache_key); LOG_PRINT(LOG_DEBUG, "Read image [%s] from blob failed.", cache_key); goto err; } if(req->width == 0 && req->height == 0) { LOG_PRINT(LOG_DEBUG, "Image [%s] needn't resize. Goto Convert.", cache_key); goto convert; } int width, height; width = req->width; height = req->height; float owidth = MagickGetImageWidth(magick_wand); float oheight = MagickGetImageHeight(magick_wand); if(width <= owidth && height <= oheight) { if(req->proportion == 1 || (req->proportion == 0 && req->width * req->height == 0)) { if(req->height == 0) { height = width * oheight / owidth; } else { width = height * owidth / oheight; } } status = MagickResizeImage(magick_wand, width, height, LanczosFilter, 1.0); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Resize Failed!", cache_key); goto err; } LOG_PRINT(LOG_DEBUG, "Resize img succ."); } else { got_rsp = true; LOG_PRINT(LOG_DEBUG, "Args width/height is bigger than real size, return original image."); } convert: //compress image if(got_color == false) { LOG_PRINT(LOG_DEBUG, "Start to Compress the Image!"); img_format = MagickGetImageFormat(magick_wand); LOG_PRINT(LOG_DEBUG, "Image Format is %s", img_format); if(strcmp(img_format, "JPEG") != 0) { LOG_PRINT(LOG_DEBUG, "Convert Image Format from %s to JPEG.", img_format); status = MagickSetImageFormat(magick_wand, "JPEG"); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Convert Format Failed!", cache_key); } LOG_PRINT(LOG_DEBUG, "Compress Image with JPEGCompression"); status = MagickSetImageCompression(magick_wand, JPEGCompression); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Compression Failed!", cache_key); } } size_t quality = MagickGetImageCompressionQuality(magick_wand); LOG_PRINT(LOG_DEBUG, "Image Compression Quality is %u.", quality); if(quality > WAP_QUALITY) { quality = WAP_QUALITY; } LOG_PRINT(LOG_DEBUG, "Set Compression Quality to 75%."); status = MagickSetImageCompressionQuality(magick_wand, quality); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Set Compression Quality Failed!"); } //strip image EXIF infomation LOG_PRINT(LOG_DEBUG, "Start to Remove Exif Infomation of the Image..."); status = MagickStripImage(magick_wand); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Remove Exif Infomation of the ImageFailed!"); } buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(buff_ptr == NULL) { LOG_PRINT(LOG_DEBUG, "Magick Get Image Blob Failed!"); goto err; } gen_key(cache_key, req->md5, 3, req->width, req->height, req->proportion); if(got_rsp == false) { save_img_db(req->thr_arg, cache_key, buff_ptr, img_size); } if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } if(req->gray == 1) { free(buff_ptr); buff_ptr = NULL; } else { goto done; } } //gray image if(req->gray == 1) { LOG_PRINT(LOG_DEBUG, "Start to Remove Color!"); status = MagickSetImageColorspace(magick_wand, GRAYColorspace); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Remove Color Failed!", cache_key); goto err; } LOG_PRINT(LOG_DEBUG, "Image Remove Color Finish!"); buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(buff_ptr == NULL) { LOG_PRINT(LOG_DEBUG, "Magick Get Image Blob Failed!"); goto err; } gen_key(cache_key, req->md5, 4, req->width, req->height, req->proportion, req->gray); if(got_rsp == false) { save_img_db(req->thr_arg, cache_key, buff_ptr, img_size); } if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } } done: result = evbuffer_add(request->buffer_out, buff_ptr, img_size); if(result != -1) { result = 1; } err: if(magick_wand) { magick_wand=DestroyMagickWand(magick_wand); } if(img_format) free(img_format); if(buff_ptr) free(buff_ptr); return result; }
/** * @brief convert convert image function * * @param im the image * @param req the zimg request * * @return 1 for OK and -1 for fail */ int convert(MagickWand *im, zimg_req_t *req) { int result = 1, ret = -1; MagickResetIterator(im); MagickSetImageOrientation(im, TopLeftOrientation); int x = req->x, y = req->y, cols = req->width, rows = req->height; if (!(cols == 0 && rows == 0)) { /* crop and scale */ if (x == -1 && y == -1) { LOG_PRINT(LOG_DEBUG, "proportion(im, %d, %d, %d)", req->proportion, cols, rows); ret = proportion(im, req->proportion, cols, rows); if (ret != MagickTrue) return -1; } else { LOG_PRINT(LOG_DEBUG, "crop(im, %d, %d, %d, %d)", x, y, cols, rows); ret = crop(im, x, y, cols, rows); if (ret != MagickTrue) return -1; } } /* rotate image */ if (req->rotate != 0) { LOG_PRINT(LOG_DEBUG, "wi_rotate(im, %d)", req->rotate); PixelWand *background = NewPixelWand(); if (background == NULL) return -1; ret = PixelSetColor(background, "white"); if (ret != MagickTrue) { DestroyPixelWand(background); return -1; } ret = MagickRotateImage(im, background, req->rotate); LOG_PRINT(LOG_DEBUG, "rotate() ret = %d", ret); DestroyPixelWand(background); if (ret != MagickTrue) return -1; } /* set gray */ if (req->gray == 1) { LOG_PRINT(LOG_DEBUG, "wi_gray(im)"); //several ways to grayscale an image: //ret = MagickSetImageColorspace(im, GRAYColorspace); //ret = MagickQuantizeImage(im, 256, GRAYColorspace, 0, MagickFalse, MagickFalse); //ret = MagickSeparateImageChannel(im, GrayChannel); ret = MagickSetImageType(im, GrayscaleType); LOG_PRINT(LOG_DEBUG, "gray() ret = %d", ret); if (ret != MagickTrue) return -1; } /* set quality */ /* int quality = 100; int im_quality = MagickGetImageCompressionQuality(im); im_quality = (im_quality == 0 ? 100 : im_quality); LOG_PRINT(LOG_DEBUG, "wi_quality = %d", im_quality); quality = req->quality < im_quality ? req->quality : im_quality; */ LOG_PRINT(LOG_DEBUG, "wi_set_quality(im, %d)", req->quality); ret = MagickSetImageCompressionQuality(im, req->quality); if (ret != MagickTrue) return -1; /* set format */ if (strncmp(req->fmt, "none", 4) != 0) { LOG_PRINT(LOG_DEBUG, "wi_set_format(im, %s)", req->fmt); ret = MagickSetImageFormat(im, req->fmt); if (ret != MagickTrue) return -1; } LOG_PRINT(LOG_DEBUG, "convert(im, req) %d", result); return result; }
// 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); }
/** * @brief get_img The function of getting a image buffer and it's length. * * @param req The zimg_req_t from zhttp and it has the params of a request. * @param buff_ptr This function return image buffer in it. * @param img_size Get_img will change this number to return the size of image buffer. * * @return 1 for success and -1 for fail. */ int get_img(zimg_req_t *req, evhtp_request_t *request) { int result = -1; char cache_key[CACHE_KEY_SIZE]; char *img_format = NULL; int fd = -1; struct stat f_stat; char *buff_ptr; size_t img_size; MagickBooleanType status; MagickWand *magick_wand = NULL; bool got_rsp = true; bool got_color = false; LOG_PRINT(LOG_DEBUG, "get_img() start processing zimg request..."); // to gen cache_key like this: 926ee2f570dc50b2575e35a6712b08ce:0:0:1:0 gen_key(cache_key, req->md5, 4, req->width, req->height, req->proportion, req->gray); if(find_cache_bin(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Hit Cache[Key: %s].", cache_key); goto done; } LOG_PRINT(LOG_DEBUG, "Start to Find the Image..."); char whole_path[512]; int lvl1 = str_hash(req->md5); int lvl2 = str_hash(req->md5 + 3); snprintf(whole_path, 512, "%s/%d/%d/%s", settings.img_path, lvl1, lvl2, req->md5); LOG_PRINT(LOG_DEBUG, "docroot: %s", settings.img_path); LOG_PRINT(LOG_DEBUG, "req->md5: %s", req->md5); LOG_PRINT(LOG_DEBUG, "whole_path: %s", whole_path); char name[128]; if(req->proportion && req->gray) snprintf(name, 128, "%d*%dpg", req->width, req->height); else if(req->proportion && !req->gray) snprintf(name, 128, "%d*%dp", req->width, req->height); else if(!req->proportion && req->gray) snprintf(name, 128, "%d*%dg", req->width, req->height); else snprintf(name, 128, "%d*%d", req->width, req->height); char color_name[128]; if(req->proportion) snprintf(color_name, 128, "%d*%dp", req->width, req->height); else snprintf(color_name, 128, "%d*%d", req->width, req->height); char orig_path[512]; snprintf(orig_path, strlen(whole_path) + 6, "%s/0*0", whole_path); LOG_PRINT(LOG_DEBUG, "0rig File Path: %s", orig_path); char rsp_path[512]; if(req->width == 0 && req->height == 0 && req->proportion == 0 && req->gray == 0) { LOG_PRINT(LOG_DEBUG, "Return original image."); strncpy(rsp_path, orig_path, 512); } else { snprintf(rsp_path, 512, "%s/%s", whole_path, name); } LOG_PRINT(LOG_DEBUG, "Got the rsp_path: %s", rsp_path); char color_path[512]; snprintf(color_path, 512, "%s/%s", whole_path, color_name); //status=MagickReadImage(magick_wand, rsp_path); if((fd = open(rsp_path, O_RDONLY)) == -1) { magick_wand = NewMagickWand(); got_rsp = false; if(req->gray == 1) { gen_key(cache_key, req->md5, 3, req->width, req->height, req->proportion); if(find_cache_bin(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Hit Color Image Cache[Key: %s, len: %d].", cache_key, img_size); status = MagickReadImageBlob(magick_wand, buff_ptr, img_size); free(buff_ptr); buff_ptr = NULL; if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Color Image Cache[Key: %s] is Bad. Remove.", cache_key); del_cache(req->thr_arg, cache_key); } else { got_color = true; LOG_PRINT(LOG_DEBUG, "Read Image from Color Image Cache[Key: %s, len: %d] Succ. Goto Convert.", cache_key, img_size); goto convert; } } status=MagickReadImage(magick_wand, color_path); if(status == MagickTrue) { got_color = true; LOG_PRINT(LOG_DEBUG, "Read Image from Color Image[%s] Succ. Goto Convert.", rsp_path); buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } free(buff_ptr); buff_ptr = NULL; goto convert; } } // to gen cache_key like this: rsp_path-/926ee2f570dc50b2575e35a6712b08ce gen_key(cache_key, req->md5, 0); if(find_cache_bin(req->thr_arg, cache_key, &buff_ptr, &img_size) == 1) { LOG_PRINT(LOG_DEBUG, "Hit Orignal Image Cache[Key: %s].", cache_key); status = MagickReadImageBlob(magick_wand, buff_ptr, img_size); free(buff_ptr); buff_ptr = NULL; if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Open Original Image From Blob Failed! Begin to Open it From Disk."); ThrowWandException(magick_wand); del_cache(req->thr_arg, cache_key); status = MagickReadImage(magick_wand, orig_path); if(status == MagickFalse) { ThrowWandException(magick_wand); goto err; } else { buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } free(buff_ptr); buff_ptr = NULL; } } } else { LOG_PRINT(LOG_DEBUG, "Not Hit Original Image Cache. Begin to Open it."); status = MagickReadImage(magick_wand, orig_path); if(status == MagickFalse) { ThrowWandException(magick_wand); goto err; } else { buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } free(buff_ptr); buff_ptr = NULL; } } int width, height; width = req->width; height = req->height; if(width == 0 && height == 0) { LOG_PRINT(LOG_DEBUG, "Image[%s] needn't resize. Goto Convert.", orig_path); goto convert; } float owidth = MagickGetImageWidth(magick_wand); float oheight = MagickGetImageHeight(magick_wand); if(width <= owidth && height <= oheight) { if(req->proportion == 1 || (req->proportion == 0 && req->width * req->height == 0)) { if(req->height == 0) { height = width * oheight / owidth; } else { width = height * owidth / oheight; } } status = MagickResizeImage(magick_wand, width, height, LanczosFilter, 1.0); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Resize Failed!", orig_path); goto err; } LOG_PRINT(LOG_DEBUG, "Resize img succ."); } else { // Note this strcpy because rsp_path is not useful. We needn't to save the new image. got_rsp = true; LOG_PRINT(LOG_DEBUG, "Args width/height is bigger than real size, return original image."); } } else { fstat(fd, &f_stat); size_t rlen = 0; img_size = f_stat.st_size; if(img_size <= 0) { LOG_PRINT(LOG_DEBUG, "File[%s] is Empty.", rsp_path); goto err; } if((buff_ptr = (char *)malloc(img_size)) == NULL) { LOG_PRINT(LOG_DEBUG, "buff_ptr Malloc Failed!"); goto err; } LOG_PRINT(LOG_DEBUG, "img_size = %d", img_size); if((rlen = read(fd, buff_ptr, img_size)) == -1) { LOG_PRINT(LOG_DEBUG, "File[%s] Read Failed.", rsp_path); LOG_PRINT(LOG_DEBUG, "Error: %s.", strerror(errno)); goto err; } else if(rlen < img_size) { LOG_PRINT(LOG_DEBUG, "File[%s] Read Not Compeletly.", rsp_path); goto err; } if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } goto done; } convert: if(got_color == false) { //compress image LOG_PRINT(LOG_DEBUG, "Start to Compress the Image!"); img_format = MagickGetImageFormat(magick_wand); LOG_PRINT(LOG_DEBUG, "Image Format is %s", img_format); if(strcmp(img_format, "JPEG") != 0) { LOG_PRINT(LOG_DEBUG, "Convert Image Format from %s to JPEG.", img_format); status = MagickSetImageFormat(magick_wand, "JPEG"); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Convert Format Failed!", orig_path); } LOG_PRINT(LOG_DEBUG, "Compress Image with JPEGCompression"); status = MagickSetImageCompression(magick_wand, JPEGCompression); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Compression Failed!", orig_path); } } size_t quality = MagickGetImageCompressionQuality(magick_wand); LOG_PRINT(LOG_DEBUG, "Image Compression Quality is %u.", quality); if(quality > WAP_QUALITY) { quality = WAP_QUALITY; } LOG_PRINT(LOG_DEBUG, "Set Compression Quality to 75%."); status = MagickSetImageCompressionQuality(magick_wand, quality); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Set Compression Quality Failed!"); } //strip image EXIF infomation LOG_PRINT(LOG_DEBUG, "Start to Remove Exif Infomation of the Image..."); status = MagickStripImage(magick_wand); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Remove Exif Infomation of the ImageFailed!"); } buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(buff_ptr == NULL) { LOG_PRINT(LOG_DEBUG, "Magick Get Image Blob Failed!"); goto err; } gen_key(cache_key, req->md5, 3, req->width, req->height, req->proportion); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } if(got_rsp == false) { LOG_PRINT(LOG_DEBUG, "Color Image[%s] is Not Existed. Begin to Save it.", color_path); if(new_img(buff_ptr, img_size, color_path) == -1) { LOG_PRINT(LOG_DEBUG, "Color Image[%s] Save Failed!", color_path); LOG_PRINT(LOG_WARNING, "fail save %s", color_path); } } if(req->gray == 1) { free(buff_ptr); buff_ptr = NULL; } else { goto done; } } //gray image if(req->gray == 1) { LOG_PRINT(LOG_DEBUG, "Start to Remove Color!"); status = MagickSetImageColorspace(magick_wand, GRAYColorspace); if(status == MagickFalse) { LOG_PRINT(LOG_DEBUG, "Image[%s] Remove Color Failed!", orig_path); goto err; } LOG_PRINT(LOG_DEBUG, "Image Remove Color Finish!"); buff_ptr = (char *)MagickGetImageBlob(magick_wand, &img_size); if(buff_ptr == NULL) { LOG_PRINT(LOG_DEBUG, "Magick Get Image Blob Failed!"); goto err; } gen_key(cache_key, req->md5, 4, req->width, req->height, req->proportion, req->gray); if(img_size < CACHE_MAX_SIZE) { set_cache_bin(req->thr_arg, cache_key, buff_ptr, img_size); } if(got_rsp == false) { if(new_img(buff_ptr, img_size, rsp_path) == -1) { LOG_PRINT(LOG_DEBUG, "New Image[%s] Save Failed!", rsp_path); LOG_PRINT(LOG_WARNING, "fail save %s", rsp_path); } } else LOG_PRINT(LOG_DEBUG, "Image Needn't to Storage.", rsp_path); } done: result = evbuffer_add(request->buffer_out, buff_ptr, img_size); if(result != -1) { result = 1; } err: if(fd != -1) close(fd); if(magick_wand) { magick_wand=DestroyMagickWand(magick_wand); } if(img_format) free(img_format); if(buff_ptr) free(buff_ptr); return result; }
/* * given a source image, a destination filepath and a set of image metadata thresholds, * search for the lowest-quality version of the source image whose properties fall within our * thresholds. * this will produce an image file that looks the same to the casual observer, but which * contains much less information and results in a smaller file. * typical savings on unoptimized images vary widely from 10-80%, with 25-50% being most common. */ MagickWand * search_quality(MagickWand *mw, const char *dst, const struct imgmin_options *opt) { MagickWand *tmp = NULL; char tmpfile[MAX_PATH] = "/tmp/imgminXXXXXX"; if (0 == strcmp("-", dst)) { if (-1 == mkstemp(tmpfile)) { perror("mkstemp"); return CloneMagickWand(mw); } } else { strcpy(tmpfile, dst); } /* * The overwhelming majority of JPEGs are TrueColorType; it is those types, with a low * unique color count, that we must avoid. */ if (!enough_colors(mw, opt)) { fprintf(stdout, " Color count is too low, skipping...\n"); return CloneMagickWand(mw); } if (quality(mw) < opt->quality_in_min) { fprintf(stdout, " Quality < %u, won't second-guess...\n", opt->quality_in_min); return CloneMagickWand(mw); } size_t width = MagickGetImageWidth(mw); size_t height = MagickGetImageHeight(mw); dssim_info *dssim = dssim_init(1); void *convert_data = convert_row_start(mw); dssim_set_original_float_callback(dssim, width, height, convert_row_callback, convert_data); convert_row_finish(convert_data); { ExceptionInfo *exception = AcquireExceptionInfo(); const double original_density = color_density(mw); unsigned qmax = min(quality(mw), opt->quality_out_max); unsigned qmin = opt->quality_out_min; unsigned steps = 0; /* * binary search of quality space for optimally lowest quality that * produces an acceptable level of distortion */ while (qmax > qmin + 1 && steps < opt->max_steps) { double density_ratio; unsigned q; steps++; q = (qmax + qmin) / 2; /* change quality */ tmp = CloneMagickWand(mw); MagickSetImageCompressionQuality(tmp, q); /* apply quality change */ MagickWriteImages(tmp, tmpfile, MagickTrue); DestroyMagickWand(tmp); tmp = NewMagickWand(); MagickReadImage(tmp, tmpfile); void *convert_data = convert_row_start(tmp); dssim_set_modified_float_callback(dssim, width, height, convert_row_callback, convert_data); convert_row_finish(convert_data); double error = 20.0 * dssim_compare(dssim, NULL); // scaled to threshold of previous implementation density_ratio = fabs(color_density(tmp) - original_density) / original_density; /* color density ratio threshold is an alternative quality measure. If it's exceeded, pretend MSE was higher to increase quality */ if (density_ratio > opt->color_density_ratio) { error *= 1.25 + density_ratio; // fudge factor } /* eliminate half search space based on whether distortion within thresholds */ if (error > opt->error_threshold) { qmin = q; } else { qmax = q; } if (opt->show_progress) { fprintf(stdout, "%.2f/%.2f@%u ", error, density_ratio, q); } /* Stop searching if close enough to the target */ if (fabs(error - opt->error_threshold) < opt->error_threshold * ERROR_THRESHOLD_INACCURACY) { qmax = q; break; } } if (opt->show_progress) { putc('\n', stdout); } MagickSetImageCompressionQuality(mw, qmax); /* "Chroma sub-sampling works because human vision is relatively insensitive to * small areas of colour. It gives a significant reduction in file sizes, with * little loss of perceived quality." [3] */ #if MagickLibVersion >= 0x630 /* FIXME: available in 0x660, not available in 0x628, not sure which version it was introduced in */ (void) MagickSetImageProperty(mw, "jpeg:sampling-factor", "2x2"); #endif /* strip an image of all profiles and comments */ (void) MagickStripImage(mw); MagickWriteImages(mw, tmpfile, MagickTrue); (void) DestroyMagickWand(tmp); tmp = NewMagickWand(); MagickReadImage(tmp, tmpfile); exception = DestroyExceptionInfo(exception); } return tmp; }
/** * @brief get_img The function of getting a image buffer and it's length. * * @param req The zimg_req_t from zhttp and it has the params of a request. * @param buff_ptr This function return image buffer in it. * @param img_size Get_img will change this number to return the size of image buffer. * * @return ZIMG_OK for success and ZIMG_ERR for fail. */ int get_img(zimg_req_t *req, char **buff_ptr, size_t *img_size) { int result = -1; char *rsp_path = NULL; char *whole_path = NULL; char *orig_path = NULL; char *color_path = NULL; char *img_format = NULL; size_t len; int fd = -1; struct stat f_stat; MagickBooleanType status; MagickWand *magick_wand = NULL; LOG_PRINT(LOG_INFO, "get_img() start processing zimg request..."); char *cache_key = (char *)malloc(strlen(req->md5) + 32); if(cache_key == NULL){ LOG_PRINT(LOG_INFO, "malloc failed!"); return ZIMG_ERR; } // to gen cache_key like this: img:926ee2f570dc50b2575e35a6712b08ce:0:0:1:0 sprintf(cache_key, "img:%s:%d:%d:%d:%d", req->md5, req->width, req->height, req->proportion, req->gray); if(find_cache_bin(cache_key, buff_ptr, img_size) == 1){ LOG_PRINT(LOG_INFO, "Hit Cache[Key: %s].", cache_key); // sprintf(cache_key, "type:%s:%d:%d:%d:%d", req->md5, req->width, req->height, req->proportion, req->gray); // if(find_cache(cache_key, img_format) == -1) // { // LOG_PRINT(LOG_WARNING, "Cannot Hit Type Cache[Key: %s]. Use jpeg As Default.", cache_key); // strcpy(img_format, "jpeg"); // } free(cache_key); return ZIMG_OK; } free(cache_key); LOG_PRINT(LOG_INFO, "Start to Find the Image..."); //check img dir // /1023/1023/xxxxxxxxxxxxxxx.png len = strlen(req->md5) + strlen(settings.img_path) + 12; whole_path = malloc(len); if (whole_path == NULL){ LOG_PRINT(LOG_ERROR, "whole_path malloc failed!"); return ZIMG_ERR; } int lvl1 = str_hash(req->md5); int lvl2 = str_hash(req->md5 + 3); sprintf(whole_path, "%s/%d/%d/%s", settings.img_path, lvl1, lvl2, req->md5); LOG_PRINT(LOG_INFO, "docroot: %s", settings.img_path); LOG_PRINT(LOG_INFO, "req->md5: %s", req->md5); LOG_PRINT(LOG_INFO, "whole_path: %s", whole_path); char name[128]; if(req->proportion && req->gray) sprintf(name, "%d*%dpg", req->width, req->height); else if(req->proportion && !req->gray) sprintf(name, "%d*%dp", req->width, req->height); else if(!req->proportion && req->gray) sprintf(name, "%d*%dg", req->width, req->height); else sprintf(name, "%d*%d", req->width, req->height); orig_path = (char *)malloc(strlen(whole_path) + 6); sprintf(orig_path, "%s/0*0p", whole_path); LOG_PRINT(LOG_INFO, "0rig File Path: %s", orig_path); rsp_path = (char *)malloc(512); if(req->width == 0 && req->height == 0 && req->gray == 0) { LOG_PRINT(LOG_INFO, "Return original image."); strcpy(rsp_path, orig_path); } else { sprintf(rsp_path, "%s/%s", whole_path, name); } LOG_PRINT(LOG_INFO, "Got the rsp_path: %s", rsp_path); bool got_rsp = true; bool got_color = false; //status=MagickReadImage(magick_wand, rsp_path); if((fd = open(rsp_path, O_RDONLY)) == -1) //if(status == MagickFalse) { magick_wand = NewMagickWand(); got_rsp = false; if(req->gray == 1) { sprintf(cache_key, "img:%s:%d:%d:%d:0", req->md5, req->width, req->height, req->proportion); if(find_cache_bin(cache_key, buff_ptr, img_size) == 1) { LOG_PRINT(LOG_INFO, "Hit Color Image Cache[Key: %s, len: %d].", cache_key, *img_size); status = MagickReadImageBlob(magick_wand, *buff_ptr, *img_size); if(status == MagickFalse) { LOG_PRINT(LOG_WARNING, "Color Image Cache[Key: %s] is Bad. Remove.", cache_key); del_cache(cache_key); } else { got_color = true; LOG_PRINT(LOG_INFO, "Read Image from Color Image Cache[Key: %s, len: %d] Succ. Goto Convert.", cache_key, *img_size); goto convert; } } len = strlen(rsp_path); color_path = (char *)malloc(len); strncpy(color_path, rsp_path, len); color_path[len - 1] = '\0'; LOG_PRINT(LOG_INFO, "color_path: %s", color_path); status=MagickReadImage(magick_wand, color_path); if(status == MagickTrue) { got_color = true; LOG_PRINT(LOG_INFO, "Read Image from Color Image[%s] Succ. Goto Convert.", color_path); *buff_ptr = (char *)MagickGetImageBlob(magick_wand, img_size); if(*img_size < CACHE_MAX_SIZE) { set_cache_bin(cache_key, *buff_ptr, *img_size); // img_format = MagickGetImageFormat(magick_wand); // sprintf(cache_key, "type:%s:%d:%d:%d:0", req->md5, req->width, req->height, req->proportion); // set_cache(cache_key, img_format); } goto convert; } } // to gen cache_key like this: rsp_path-/926ee2f570dc50b2575e35a6712b08ce sprintf(cache_key, "img:%s:0:0:1:0", req->md5); if(find_cache_bin(cache_key, buff_ptr, img_size) == 1) { LOG_PRINT(LOG_INFO, "Hit Orignal Image Cache[Key: %s].", cache_key); status = MagickReadImageBlob(magick_wand, *buff_ptr, *img_size); if(status == MagickFalse) { LOG_PRINT(LOG_WARNING, "Open Original Image From Blob Failed! Begin to Open it From Disk."); ThrowWandException(magick_wand); del_cache(cache_key); status = MagickReadImage(magick_wand, orig_path); if(status == MagickFalse) { ThrowWandException(magick_wand); goto err; } else { *buff_ptr = (char *)MagickGetImageBlob(magick_wand, img_size); if(*img_size < CACHE_MAX_SIZE) { set_cache_bin(cache_key, *buff_ptr, *img_size); // img_format = MagickGetImageFormat(magick_wand); // sprintf(cache_key, "type:%s:0:0:1:0", req->md5); // set_cache(cache_key, img_format); } } } } else { LOG_PRINT(LOG_INFO, "Not Hit Original Image Cache. Begin to Open it."); status = MagickReadImage(magick_wand, orig_path); if(status == MagickFalse) { ThrowWandException(magick_wand); goto err; } else { *buff_ptr = (char *)MagickGetImageBlob(magick_wand, img_size); if(*img_size < CACHE_MAX_SIZE) { set_cache_bin(cache_key, *buff_ptr, *img_size); // img_format = MagickGetImageFormat(magick_wand); // sprintf(cache_key, "type:%s:0:0:1:0", req->md5); // set_cache(cache_key, img_format); } } } int width, height; width = req->width; height = req->height; float owidth = MagickGetImageWidth(magick_wand); float oheight = MagickGetImageHeight(magick_wand); if(width <= owidth && height <= oheight) { if(req->proportion == 1) { if(req->width != 0 && req->height == 0) { height = width * oheight / owidth; } //else if(height != 0 && width == 0) else { width = height * owidth / oheight; } } status = MagickResizeImage(magick_wand, width, height, LanczosFilter, 1.0); if(status == MagickFalse) { LOG_PRINT(LOG_ERROR, "Image[%s] Resize Failed!", orig_path); goto err; } LOG_PRINT(LOG_INFO, "Resize img succ."); } /* this section can caculate the correct rsp_path, but not use, so I note them else if(req->gray == true) { strcpy(rsp_path, orig_path); rsp_path[strlen(orig_path)] = 'g'; rsp_path[strlen(orig_path) + 1] = '\0'; got_rsp = true; LOG_PRINT(LOG_INFO, "Args width/height is bigger than real size, return original gray image."); LOG_PRINT(LOG_INFO, "Original Gray Image: %s", rsp_path); } */ else { // Note this strcpy because rsp_path is not useful. We needn't to save the new image. //strcpy(rsp_path, orig_path); got_rsp = true; LOG_PRINT(LOG_INFO, "Args width/height is bigger than real size, return original image."); } } else { fstat(fd, &f_stat); size_t rlen = 0; *img_size = f_stat.st_size; if(*img_size <= 0) { LOG_PRINT(LOG_ERROR, "File[%s] is Empty.", rsp_path); goto err; } if((*buff_ptr = (char *)malloc(*img_size)) == NULL) { LOG_PRINT(LOG_ERROR, "buff_ptr Malloc Failed!"); goto err; } LOG_PRINT(LOG_INFO, "img_size = %d", *img_size); //*buff_ptr = (char *)MagickGetImageBlob(magick_wand, img_size); if((rlen = read(fd, *buff_ptr, *img_size)) == -1) { LOG_PRINT(LOG_ERROR, "File[%s] Read Failed.", rsp_path); LOG_PRINT(LOG_ERROR, "Error: %s.", strerror(errno)); goto err; } else if(rlen < *img_size) { LOG_PRINT(LOG_ERROR, "File[%s] Read Not Compeletly.", rsp_path); goto err; } goto done; } convert: //gray image if(req->gray == true) { LOG_PRINT(LOG_INFO, "Start to Remove Color!"); status = MagickSetImageColorspace(magick_wand, GRAYColorspace); if(status == MagickFalse) { LOG_PRINT(LOG_ERROR, "Image[%s] Remove Color Failed!", orig_path); goto err; } LOG_PRINT(LOG_INFO, "Image Remove Color Finish!"); } if(got_color == false || (got_color == true && req->width == 0) ) { //compress image LOG_PRINT(LOG_INFO, "Start to Compress the Image!"); img_format = MagickGetImageFormat(magick_wand); LOG_PRINT(LOG_INFO, "Image Format is %s", img_format); if(strcmp(img_format, "JPEG") != 0) { LOG_PRINT(LOG_INFO, "Convert Image Format from %s to JPEG.", img_format); status = MagickSetImageFormat(magick_wand, "JPEG"); if(status == MagickFalse) { LOG_PRINT(LOG_WARNING, "Image[%s] Convert Format Failed!", orig_path); } LOG_PRINT(LOG_INFO, "Compress Image with JPEGCompression"); status = MagickSetImageCompression(magick_wand, JPEGCompression); if(status == MagickFalse) { LOG_PRINT(LOG_WARNING, "Image[%s] Compression Failed!", orig_path); } } size_t quality = MagickGetImageCompressionQuality(magick_wand) * 0.75; LOG_PRINT(LOG_INFO, "Image Compression Quality is %u.", quality); if(quality == 0) { quality = 75; } LOG_PRINT(LOG_INFO, "Set Compression Quality to 75%."); status = MagickSetImageCompressionQuality(magick_wand, quality); if(status == MagickFalse) { LOG_PRINT(LOG_WARNING, "Set Compression Quality Failed!"); } //strip image EXIF infomation LOG_PRINT(LOG_INFO, "Start to Remove Exif Infomation of the Image..."); status = MagickStripImage(magick_wand); if(status == MagickFalse) { LOG_PRINT(LOG_WARNING, "Remove Exif Infomation of the ImageFailed!"); } } *buff_ptr = (char *)MagickGetImageBlob(magick_wand, img_size); if(*buff_ptr == NULL) { LOG_PRINT(LOG_ERROR, "Magick Get Image Blob Failed!"); goto err; } done: if(*img_size < CACHE_MAX_SIZE) { // to gen cache_key like this: rsp_path-/926ee2f570dc50b2575e35a6712b08ce sprintf(cache_key, "img:%s:%d:%d:%d:%d", req->md5, req->width, req->height, req->proportion, req->gray); set_cache_bin(cache_key, *buff_ptr, *img_size); // sprintf(cache_key, "type:%s:%d:%d:%d:%d", req->md5, req->width, req->height, req->proportion, req->gray); // set_cache(cache_key, img_format); } result = 1; if(got_rsp == false) { LOG_PRINT(LOG_INFO, "Image[%s] is Not Existed. Begin to Save it.", rsp_path); result = 2; } else LOG_PRINT(LOG_INFO, "Image Needn't to Storage.", rsp_path); err: if(fd != -1) close(fd); req->rsp_path = rsp_path; if(magick_wand) { magick_wand=DestroyMagickWand(magick_wand); } if(img_format) free(img_format); if(cache_key) free(cache_key); if (orig_path) free(orig_path); if (whole_path) free(whole_path); return result; }
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; }
unsigned char *covert_image(MagickWand *magick_wand, img_transition_info *image_transition_info, size_t *thumbnail_size) { unsigned char *image_data = NULL; MagickBooleanType status; // MagickWand *tmp_magick_wand = NULL; PixelWand *background = NULL; size_t height = MagickGetImageHeight(magick_wand); size_t old_height = height; size_t width = MagickGetImageWidth(magick_wand); size_t old_width = width; int is_crop = 0; int is_Crop = 0; int is_thumbnail = 0; ssize_t i = 0, j = 0; char is_gif_flag = 0; char is_jpeg_flag = 0; int do_quality = 0; char *fileformat = NULL; size_t cw = 0; //crop weight size_t ch = 0; //crop height size_t x_offset = 0; size_t y_offset = 0; fileformat = MagickGetImageFormat(magick_wand); if (fileformat == NULL) { return NULL; } if (0 == strcasecmp(fileformat, image_format[GIFEXT])) { is_gif_flag = 1; } else if (0 == strcasecmp(fileformat, image_format[JPEGEXT])) { is_jpeg_flag = 1; } else if (0 == strcasecmp(fileformat, image_format[JPGEXT])) { is_jpeg_flag = 1; } fileformat = (char *) MagickRelinquishMemory(fileformat); //free(); if ('c' == image_transition_info->transition_str[0]) { is_crop = 1; } else if ('C' == image_transition_info->transition_str[0]) { is_Crop = 1; } else { is_thumbnail = 1; } if (is_crop) { ParseMetaGeometry(image_transition_info->transition_str + 1, &i, &j, &width, &height); } else if (is_thumbnail) { ParseMetaGeometry(image_transition_info->transition_str, &i, &j, &width, &height); if (old_width == width && height == old_height) //���ߴ���ͬ���������� is_thumbnail = 0; } else if (is_Crop) { if (0 >= get_Crop_width_height( image_transition_info->transition_str + 1, &cw, &ch, &x_offset, &y_offset)) { logError("%s%s:Crop %s error\n", __FILE__, __func__, image_transition_info->transition_str + 1); return NULL; } #if 0 if(cw > width || ch > height) { image_data = MagickGetImagesBlob(magick_wand, thumbnail_size); magick_wand = DestroyMagickWand(magick_wand); return image_data; } #endif //�õ�height��width,�����Ӧ��x_offset,yoffset; get_Crop_offset_and_wh(cw, ch, &width, &height, &x_offset, &y_offset); } if (old_width == width && height == old_height && (is_Crop == 0) && (image_transition_info->is_rotate == 0) && (image_transition_info->is_quality == 0)) { image_data = MagickGetImagesBlob(magick_wand, thumbnail_size); } else if (width <= 0 || height <= 0) { logError("%s%s:Geometry %s error\n", __FILE__, __func__, image_transition_info->transition_str); } else { /* * if type of the image is GIF, maybe have more than one frame, so do this different * from others */ // if (is_gif_flag) { // tmp_magick_wand = magick_wand; // magick_wand = MagickCoalesceImages(tmp_magick_wand); // tmp_magick_wand = magick_wand; // magick_wand = MagickOptimizeImageLayers(tmp_magick_wand); // tmp_magick_wand = DestroyMagickWand(tmp_magick_wand); // } /* * if size of the image less than 800 * 600 and that's type is JPEG, then do * quality 100 OP */ if ((old_width < 800) && (old_height < 600) && is_jpeg_flag && is_crop != 1 && (image_transition_info->is_quality == 0)) { do_quality = 1; } background = NewPixelWand(); status = PixelSetColor(background, "#000000"); /* for gif MagickResetIterator(magick_wand); */ MagickWand *tmp_magick_wand = MagickGetImage(magick_wand); // while (MagickNextImage(tmp_magick_wand) != MagickFalse) { if (do_quality) { MagickSetImageCompressionQuality(tmp_magick_wand, 100); MagickStripImage(tmp_magick_wand); } if (is_thumbnail == 1) { MagickThumbnailImage(tmp_magick_wand, width, height); } else if (is_crop == 1) { MagickCropImage(tmp_magick_wand, width, height, i, j); } else if (is_Crop == 1) { MagickThumbnailImage(tmp_magick_wand, width, height); MagickCropImage(tmp_magick_wand, cw, ch, x_offset, y_offset); if (is_gif_flag) { // gif should thumbnail again MagickThumbnailImage(tmp_magick_wand, cw, ch); } } if (image_transition_info->is_rotate == 1) { MagickRotateImage(tmp_magick_wand, background, (double) (image_transition_info->degree)); } if (image_transition_info->is_quality) { MagickSetImageCompressionQuality(tmp_magick_wand, image_transition_info->quality); } MagickStripImage(tmp_magick_wand); // } background = DestroyPixelWand(background); image_data = MagickGetImagesBlob(tmp_magick_wand, thumbnail_size); tmp_magick_wand = DestroyMagickWand(tmp_magick_wand); // // if (is_gif_flag) { // magick_wand = DestroyMagickWand(magick_wand); // } } return image_data; }
unsigned char *get_thumbnail(char *full_filename, char *thumbnail_str, size_t *thumbnail_size, int is_rotate, int rotate_degree) { unsigned char *image_data = NULL; if (full_filename == NULL || thumbnail_str == NULL) return NULL; MagickBooleanType status; MagickWand *tmp_magick_wand = NULL; MagickWand *magick_wand = NULL; magick_wand = NewMagickWand(); status = MagickReadImage(magick_wand, full_filename); if (status == MagickFalse) { ThrowWandException(magick_wand); return NULL; } PixelWand *background = NULL; size_t height = MagickGetImageHeight(magick_wand); size_t old_height = height; size_t width = MagickGetImageWidth(magick_wand); size_t old_width = width; ssize_t i = 0, j = 0; int is_crop = 0; char is_gif_flag = 0; char is_jpeg_flag = 0; int do_quality = 0; char *fileformat = NULL; fileformat = MagickGetImageFormat(magick_wand); if (fileformat == NULL) { return NULL; } if (0 == strcasecmp(fileformat, image_format[GIFEXT])) { is_gif_flag = 1; } else if (0 == strcasecmp(fileformat, image_format[JPEGEXT])) { is_jpeg_flag = 1; } else if (0 == strcasecmp(fileformat, image_format[JPGEXT])) { is_jpeg_flag = 1; } fileformat = (char *)MagickRelinquishMemory(fileformat); //free(); if( 'C' == *thumbnail_str ||'c' == *thumbnail_str ) { is_crop = 1; } if(is_crop) { ParseMetaGeometry(thumbnail_str + 1, &i, &j, &width, &height); } else { ParseMetaGeometry(thumbnail_str, &i, &j, &width, &height); } if (old_width == width && height == old_height) { image_data = MagickGetImagesBlob(magick_wand, thumbnail_size); } else if (width <= 0 || height <= 0) { logError("%s%s:Geometry %s error\n", __FILE__, __func__, thumbnail_str); } else { /* * if type of the image is GIF, maybe have more than one frame, so do this different * from others */ if (is_gif_flag) { tmp_magick_wand = magick_wand; magick_wand = MagickCoalesceImages(tmp_magick_wand); tmp_magick_wand = DestroyMagickWand(tmp_magick_wand); } /* * if size of the image less than 800 * 600 and that's type is JPEG, then do * quality 100 OP */ if ((old_width < 800) && (old_height < 600) && is_jpeg_flag && is_crop != 1) { do_quality = 1; } MagickResetIterator(magick_wand); while (MagickNextImage(magick_wand) != MagickFalse) { if(do_quality) { MagickSetImageCompressionQuality(magick_wand, 100); MagickStripImage(magick_wand); } if(is_crop == 0) MagickThumbnailImage(magick_wand, width, height); else { logInfo("crop Image %ld, %ld", i, j); MagickCropImage(magick_wand, width, height, i, j); } if(is_rotate == 1) { background=NewPixelWand(); status=PixelSetColor(background,"#000000"); MagickRotateImage(magick_wand, background,(double)rotate_degree); background=DestroyPixelWand(background); } } image_data = MagickGetImagesBlob(magick_wand, thumbnail_size); } magick_wand = DestroyMagickWand(magick_wand); return image_data; }
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; }
/* * given a source image, a destination filepath and a set of image metadata thresholds, * search for the lowest-quality version of the source image whose properties fall within our * thresholds. * this will produce an image file that looks the same to the casual observer, but which * contains much less information and results in a smaller file. * typical savings on unoptimized images vary widely from 10-80%, with 25-50% being most common. */ MagickWand * search_quality(MagickWand *mw, const char *dst, const struct imgmin_options *opt) { MagickWand *tmp = NULL; char tmpfile[MAX_PATH] = "/tmp/imgminXXXXXX"; if (0 == strcmp("-", dst)) { if (-1 == mkstemp(tmpfile)) { perror("mkstemp"); return CloneMagickWand(mw); } } else { strcpy(tmpfile, dst); } /* * The overwhelming majority of JPEGs are TrueColorType; it is those types, with a low * unique color count, that we must avoid. */ if (unique_colors(mw) < opt->min_unique_colors && MagickGetType(mw) != GrayscaleType) { fprintf(stderr, " Color count is too low, skipping...\n"); return CloneMagickWand(mw); } if (quality(mw) < opt->quality_in_min) { fprintf(stderr, " Quality < %u, won't second-guess...\n", opt->quality_in_min); return CloneMagickWand(mw); } { ExceptionInfo *exception = AcquireExceptionInfo(); const double original_density = color_density(mw); unsigned qmax = min(quality(mw), opt->quality_out_max); unsigned qmin = opt->quality_out_min; unsigned steps = 0; /* * binary search of quality space for optimally lowest quality that * produces an acceptable level of distortion */ while (qmax > qmin + 1 && steps < opt->max_steps) { double distortion[CompositeChannels+1]; double error; double density_ratio; unsigned q; steps++; q = (qmax + qmin) / 2; /* change quality */ tmp = CloneMagickWand(mw); MagickSetImageCompressionQuality(tmp, q); /* apply quality change */ MagickWriteImages(tmp, tmpfile, MagickTrue); DestroyMagickWand(tmp); tmp = NewMagickWand(); MagickReadImage(tmp, tmpfile); /* quantify distortion produced by quality change * NOTE: writes to distortion[], which we don't care about * and to image(tmp)->error, which we do care about */ (void) GetImageDistortion(GetImageFromMagickWand(tmp), GetImageFromMagickWand(mw), MeanErrorPerPixelMetric, distortion, exception); /* FIXME: in perlmagick i was getting back a number [0,255.0], * here something else is happening... the hardcoded divisor * is an imperfect attempt to scale the number back to the * perlmagick results. I really need to look into this. */ error = GetImageFromMagickWand(tmp)->error.mean_error_per_pixel / 380.; density_ratio = fabs(color_density(tmp) - original_density) / original_density; /* eliminate half search space based on whether distortion within thresholds */ if (error > opt->error_threshold || density_ratio > opt->color_density_ratio) { qmin = q; } else { qmax = q; } if (opt->show_progress) { fprintf(stderr, "%.2f/%.2f@%u ", error, density_ratio, q); } } if (opt->show_progress) { putc('\n', stderr); } MagickSetImageCompressionQuality(mw, qmax); /* "Chroma sub-sampling works because human vision is relatively insensitive to * small areas of colour. It gives a significant reduction in file sizes, with * little loss of perceived quality." [3] */ (void) MagickSetImageProperty(mw, "jpeg:sampling-factor", "2x2"); /* strip an image of all profiles and comments */ (void) MagickStripImage(mw); MagickWriteImages(mw, tmpfile, MagickTrue); (void) DestroyMagickWand(tmp); tmp = NewMagickWand(); MagickReadImage(tmp, tmpfile); exception = DestroyExceptionInfo(exception); } return tmp; }