diff --git a/doc/function/add_bleed_edge.txt b/doc/function/add_bleed_edge.txt new file mode 100644 index 00000000..6db1d216 --- /dev/null +++ b/doc/function/add_bleed_edge.txt @@ -0,0 +1,15 @@ +Function: add_bleed_edge + +--Usage-- +> add_bleed_edge(input: image, horizontal_size: number, vertical_size: number, background_color: color) + +Add a crude print bleed edge around an image. +The sizes are given in percentages of the image size, so @horizontal_size: 1@ makes the image three times as large by putting a bleed edge of 100% on both sides. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:image]] Image that needs a print bleed edge +| @horizontal_size@ [[type:double]] Size of the bleed edge on the sides, in percentage of the image width. Optional, defaults to 0.048 +| @vertical_size@ [[type:double]] Size of the bleed edge on the top and bottom, in percentage of the image height. Optional, defaults to 0.037 +| @background_color@ [[type:color]] Background color, optional, defaults to transparent + diff --git a/doc/function/enlarge.txt b/doc/function/enlarge.txt index 65f94ef5..c9353636 100644 --- a/doc/function/enlarge.txt +++ b/doc/function/enlarge.txt @@ -9,7 +9,7 @@ The border size is given in percentages of the image size, so @border_size: 1@ m --Parameters-- ! Parameter Type Description | @input@ [[type:image]] Image to enlarge -| @border_size@ [[type:double]] Amount of border to add +| @border_size@ [[type:double]] Amount of border to add, in percentage of the image --Examples-- > enlarge(input: "image1.png", border_size: 0.1) == [[Image]] diff --git a/doc/function/index.txt b/doc/function/index.txt index b4fa5497..188ddfe0 100644 --- a/doc/function/index.txt +++ b/doc/function/index.txt @@ -89,6 +89,7 @@ These functions are built into the program, other [[type:function]]s can be defi | [[fun:recolor_image]] Change the colors of an image to match the font color. | [[fun:resize_image]] Stretch or squeeze an image to a given height and width. | [[fun:enlarge]] Enlarge an image by putting a border around it. +| [[fun:add_bleed_edge]] Add a crude print bleed edge around an image. | [[fun:crop]] Crop an image, giving only a small subset of it. | [[fun:flip_horizontal]] Flip an image horizontally. | [[fun:flip_vertical]] Flip an image vertically. diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp index 720019b7..36dc44e0 100644 --- a/src/gfx/generated_image.cpp +++ b/src/gfx/generated_image.cpp @@ -318,6 +318,193 @@ bool ResizeImage::operator == (const GeneratedImage& that) const { && height == that2->height; } +// ----------------------------------------------------------------------------- : BleedEdgedImage + +Image BleedEdgedImage::generate(const Options& opt) const { + // create enlarged image + Image base_img = base_image->generate(opt); + int w = base_img.GetWidth(), h = base_img.GetHeight(); + if (w <= 0 || h <= 0) { + queue_message(MESSAGE_ERROR, _("Cannot add bleed edge to empty image")); + return base_img; + } + bool is_landscape = w > h; + int dw = int(w * (horizontal_size > 0.0 ? horizontal_size : is_landscape ? 0.037 : 0.048)); + int dh = int(h * (vertical_size > 0.0 ? vertical_size : is_landscape ? 0.048 : 0.037)); + if (dw <= 0 && dh <= 0) { + return base_img; + } + int width = w + dw + dw, height = h + dh + dh; + UInt size = width * height; + Image img = wxImage(width, height, false); + img.InitAlpha(); + Byte* data = img.GetData(); + Byte* alpha = img.GetAlpha(); + // fill with background color + for (UInt i = 0; i < size; ++i) { + data[3 * i + 0] = background_color.Red(); + data[3 * i + 1] = background_color.Green(); + data[3 * i + 2] = background_color.Blue(); + alpha[i] = background_color.Alpha(); + } + // paste original image + img.Paste(base_img, dw, dh, wxIMAGE_ALPHA_BLEND_COMPOSE); + // fill top left corner + int pixel; + int x_start = 0; + int y_start = 0; + int ref = dw + dh * width; + for (int y = 0; y < dh; ++y) { + for (int x = 0; x < dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill top right corner + x_start = width - dw; + y_start = 0; + ref = x_start - 1 + dh * width; + for (int y = 0; y < dh; ++y) { + for (int x = 0; x < dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill bottom left corner + x_start = 0; + y_start = height - dh; + ref = dw + (y_start - 1) * width; + for (int y = 0; y < dh; ++y) { + for (int x = 0; x < dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill bottom right corner + x_start = width - dw; + y_start = height - dh; + ref = (x_start - 1) + (y_start - 1) * width; + for (int y = 0; y < dh; ++y) { + for (int x = 0; x < dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill left border + x_start = 0; + y_start = dh; + for (int y = 0; y < height - dh - dh; ++y) { + ref = dw + (y_start + y) * width; + for (int x = 0; x < dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill top border + x_start = dw; + y_start = 0; + for (int y = 0; y < dh; ++y) { + for (int x = 0; x < width - dw - dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + ref = x_start + x + dh * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill right border + x_start = width - dw; + y_start = dh; + for (int y = 0; y < height - dh - dh; ++y) { + ref = width - dw - 1 + (y_start + y) * width; + for (int x = 0; x < dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // fill bottom border + x_start = dw; + y_start = height - dh; + for (int y = 0; y < dh; ++y) { + for (int x = 0; x < width - dw - dw; ++x) { + pixel = x_start + x + (y_start + y) * width; + ref = x_start + x + (height - dh - 1) * width; + data[3 * pixel + 0] = data[3 * ref + 0]; + data[3 * pixel + 1] = data[3 * ref + 1]; + data[3 * pixel + 2] = data[3 * ref + 2]; + alpha[pixel] = alpha[ref]; + } + } + // done + return img; +} +bool BleedEdgedImage::operator == (const GeneratedImage& that) const { + const BleedEdgedImage* that2 = dynamic_cast(&that); + return that2 && *base_image == *that2->base_image + && horizontal_size == that2->horizontal_size + && vertical_size == that2->vertical_size + && background_color == that2->background_color; +} + +// ----------------------------------------------------------------------------- : InsertedImage + +Image InsertedImage::generate(const Options& opt) const { + Image base_img = base_image->generate(opt); + Image inserted_img = inserted_image->generate(opt); + int base_x = offset_x < 0 ? -offset_x : 0; + int base_y = offset_y < 0 ? -offset_y : 0; + int inserted_x = offset_x < 0 ? 0 : offset_x; + int inserted_y = offset_y < 0 ? 0 : offset_y; + int width = max(base_x + base_img.GetWidth(), inserted_x + inserted_img.GetWidth()); + int height = max(base_y + base_img.GetHeight(), inserted_y + inserted_img.GetHeight()); + UInt size = width * height; + Image img = wxImage(width, height, false); + img.InitAlpha(); + Byte* data = img.GetData(); + Byte* alpha = img.GetAlpha(); + for (UInt i = 0; i < size; ++i) { + data[0] = background_color.Red(); + data[1] = background_color.Green(); + data[2] = background_color.Blue(); + data += 3; + alpha[0] = background_color.Alpha(); + alpha += 1; + } + img.Paste(base_img, base_x, base_y, wxIMAGE_ALPHA_BLEND_COMPOSE); + img.Paste(inserted_img, inserted_x, inserted_y, wxIMAGE_ALPHA_BLEND_COMPOSE); + return img; +} +ImageCombine InsertedImage::combine() const { + return base_image->combine(); +} +bool InsertedImage::operator == (const GeneratedImage& that) const { + const InsertedImage* that2 = dynamic_cast(&that); + return that2 + && *base_image == *that2->base_image + && *inserted_image == *that2->inserted_image + && offset_x == that2->offset_x + && offset_y == that2->offset_y; +} + // ----------------------------------------------------------------------------- : CropImage Image CropImage::generate(const Options& opt) const { diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp index 380dccab..d24f16f3 100644 --- a/src/gfx/generated_image.hpp +++ b/src/gfx/generated_image.hpp @@ -317,6 +317,40 @@ private: double offset_x, offset_y; }; +// ----------------------------------------------------------------------------- : BleedEdgedImage + +/// Add a crude bleed edge to an image +class BleedEdgedImage : public GeneratedImage { +public: + inline BleedEdgedImage(const GeneratedImageP& base_image, double horizontal_size, double vertical_size, Color background_color) + : base_image(base_image), horizontal_size(horizontal_size), vertical_size(vertical_size), background_color(background_color) + {} + Image generate(const Options& opt) const override; + bool operator == (const GeneratedImage& that) const override; +private: + GeneratedImageP base_image; + double horizontal_size, vertical_size; + Color background_color; +}; + +// ----------------------------------------------------------------------------- : InsertedImage + +/// Insert an image at a certain point inside another image +class InsertedImage : public GeneratedImage { +public: + inline InsertedImage(const GeneratedImageP& base_image, const GeneratedImageP& inserted_image, int offset_x, int offset_y, Color background_color) + : base_image(base_image), inserted_image(inserted_image), offset_x(offset_x), offset_y(offset_y), background_color(background_color) + {} + Image generate(const Options& opt) const override; + ImageCombine combine() const override; + bool operator == (const GeneratedImage& that) const override; + bool local() const override { return base_image->local() && inserted_image->local(); } +private: + GeneratedImageP base_image, inserted_image; + int offset_x, offset_y; + Color background_color; +}; + // ----------------------------------------------------------------------------- : DropShadowImage /// Add a drop shadow to an image diff --git a/src/script/functions/image.cpp b/src/script/functions/image.cpp index dfa16cfd..98c5d68f 100644 --- a/src/script/functions/image.cpp +++ b/src/script/functions/image.cpp @@ -68,6 +68,15 @@ SCRIPT_FUNCTION(height_of) { SCRIPT_PARAM(GeneratedImageP, input); Image image = input->generate(GeneratedImage::Options(0, 0, set->stylesheet.get())); SCRIPT_RETURN(image.GetHeight()); +} + +SCRIPT_FUNCTION(insert_image) { + SCRIPT_PARAM(GeneratedImageP, base_image); + SCRIPT_PARAM(GeneratedImageP, inserted_image); + SCRIPT_PARAM(int, offset_x); + SCRIPT_PARAM(int, offset_y); + SCRIPT_OPTIONAL_PARAM_(Color, background_color); + return make_intrusive(base_image, inserted_image, offset_x, offset_y, background_color); } SCRIPT_FUNCTION(linear_blend) { @@ -144,6 +153,14 @@ SCRIPT_FUNCTION(enlarge) { return make_intrusive(input, border_size); } +SCRIPT_FUNCTION(add_bleed_edge) { + SCRIPT_PARAM_C(GeneratedImageP, input); + SCRIPT_PARAM_DEFAULT(double, horizontal_size, -1.0); + SCRIPT_PARAM_DEFAULT(double, vertical_size, -1.0); + SCRIPT_OPTIONAL_PARAM_(Color, background_color); + return make_intrusive(input, horizontal_size, vertical_size, background_color); +} + SCRIPT_FUNCTION(resize_image) { SCRIPT_PARAM_C(GeneratedImageP, input); SCRIPT_PARAM(int, width); @@ -263,6 +280,7 @@ void init_script_image_functions(Context& ctx) { ctx.setVariable(_("linear_blend"), script_linear_blend); ctx.setVariable(_("masked_blend"), script_masked_blend); ctx.setVariable(_("combine_blend"), script_combine_blend); + ctx.setVariable(_("insert_image"), script_insert_image); ctx.setVariable(_("set_mask"), script_set_mask); ctx.setVariable(_("set_alpha"), script_set_alpha); ctx.setVariable(_("set_combine"), script_set_combine); @@ -270,6 +288,7 @@ void init_script_image_functions(Context& ctx) { ctx.setVariable(_("invert_image"), script_invert_image); ctx.setVariable(_("recolor_image"), script_recolor_image); ctx.setVariable(_("enlarge"), script_enlarge); + ctx.setVariable(_("add_bleed_edge"), script_add_bleed_edge); ctx.setVariable(_("resize_image"), script_resize_image); ctx.setVariable(_("crop"), script_crop); ctx.setVariable(_("flip_horizontal"), script_flip_horizontal); diff --git a/tools/website/drupal/mse-drupal-modules/highlight.inc b/tools/website/drupal/mse-drupal-modules/highlight.inc index fc6928c7..11558f7a 100644 --- a/tools/website/drupal/mse-drupal-modules/highlight.inc +++ b/tools/website/drupal/mse-drupal-modules/highlight.inc @@ -85,6 +85,7 @@ $built_in_functions = array( 'recolor_image' =>'', 'resize_image' =>'', 'enlarge' =>'', + 'add_bleed_edge' =>'', 'crop' =>'', 'flip_horizontal' =>'', 'flip_vertical' =>'',