encrypted-images/src/encryption/images.rs

346 lines
25 KiB
Rust

/// Creates an image from ciphertext with optional watermark and additional styling options.
///
/// This function takes ciphertext and an optional watermark as input and generates an image
/// where the ciphertext is visually represented. You can customize the image's style, overlay
/// an optional watermark (e.g., Bitcoin, Ethereum, Cardano, or none), adjust color, and more.
/// The generated image is encoded as a PNG image and then Base64 encoded before being returned
/// as an `Option<String>`.
///
/// # Arguments
///
/// * `ciphertext` - The ciphertext to be represented in the image.
/// * `style` - The style of the generated image:
/// - "h": Default position (no change).
/// - "h2": Vertical flip.
/// - "v": 90-degree rotation.
/// - "v2": 270-degree rotation with a vertical flip.
/// * `watermark` - The watermark to overlay on the image:
/// - "empty": No watermark.
/// - "bitcoin": Bitcoin watermark.
/// - "ethereum": Ethereum watermark.
/// - "cardano": Cardano watermark.
/// - __your own base64 encoded watermark__
/// * `r` - Custom red color component (0-255) for gradient.
/// * `g` - Custom green color component (0-255) for gradient.
/// * `b` - Custom blue color component (0-255) for gradient.
/// * `a` - Custom alpha (opacity) value (0-255). Should be None unless using custom watermark.
/// * `w` - Custom width for the watermark image. Should be None unless using custom watermark.
/// * `h` - Custom height for the watermark image. Should be None unless using custom watermark.
///
/// # Returns
///
/// An `Option<String>` containing the Base64 encoded image if successful, or `None` if there
/// was an error during image creation or encoding.
///
/// # Examples
///
/// ```
/// use encrypted_images::encryption::images::create_img;
///
/// let ciphertext = "ThisIsCiphertext";
/// let style = "h2";
/// let watermark = "Bitcoin";
/// let image_data = create_img(ciphertext, style, watermark, Some(100), Some(134), Some(131), None, None, None);
/// assert!(image_data.is_some());
/// ```
use image::{ColorType, DynamicImage, RgbaImage, Rgba, imageops};
use image::png::PngEncoder;
use image::io::Reader as ImageReader;
use crate::char_mappings::maps::mappings::get_color;
use std::io::Cursor;
use base64::{Engine as _, engine::{self, general_purpose}, alphabet};
fn load_watermark(
watermark: &str,
alpha: Option<u8>,
width: Option<u32>,
height: Option<u32>,
) -> Option<DynamicImage> {
let custom_engine: engine::GeneralPurpose = engine::GeneralPurpose::new(&alphabet::STANDARD, general_purpose::PAD);
let decoded = custom_engine.decode(watermark);
let is_base64 = decoded.is_ok();
if is_base64 {
if let (Some(_a), Some(w), Some(h)) = (alpha, width, height) {
let img = match image::load_from_memory(&decoded.ok()?) {
Ok(img) => img,
Err(_) => return None,
};
Some(img.resize(w, h, image::imageops::FilterType::Nearest))
} else {
None
}
} else {
let watermark: &str = match watermark {
"empty" => "",
"bitcoin" => "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TxQ8qInYQUchQXbQgKuIoVSyChdJWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIq4uToouU+L+k0CLGg+N+vLv3uHsHCLUSU822CUDVLCMRjYjpzKrY8YouBNCHMQxLzNRjycUUPMfXPXx8vQvzLO9zf44eJWsywCcSzzHdsIg3iGc2LZ3zPnGQFSSF+Jx43KALEj9yXXb5jXPeYYFnBo1UYp44SCzmW1huYVYwVOJp4pCiapQvpF1WOG9xVksV1rgnf2Egq60kuU5zCFEsIYY4RMiooIgSLIRp1UgxkaD9iId/0PHHySWTqwhGjgWUoUJy/OB/8LtbMzc16SYFIkD7i21/jAAdu0C9atvfx7ZdPwH8z8CV1vSXa8DsJ+nVphY6Anq3gYvrpibvAZc7wMCTLhmSI/lpCrkc8H5G35QB+m+B7jW3t8Y+Th+AFHW1fAMcHAKjecpe93h3Z2tv/55p9PcDpKVyu4rx+1IAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfnCQwTBQINStQTAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAoZJREFUWMPtl81LVUEYxn9XKsRyKAejlZaWeSstwV0r21QE7fI/iKRVjYuIImxhqxohWhStIyIhIgpbFUgFUV0/Em8bQxSRaiQmslDktmikyzj36D3n0MoXzmKe886Z5zzzfszAusUwq2Wf1XLYanm/CDsU51sVMTk0A61Ak1u8FchZLQtWy1P/iwDAuDcGmCjnQxtWkboJeAXkgTFg2C2607nkAwRGyyGQWYXASeBxhMukI1QPZIEJoUxjagoA34G3wD5gS+B9vXuWrcFqOQncA24JZWYSKfBPiZpKyPQDJ8r4uXngilDmRuIgFGruN/DBg6eBOuAIcAl4572vAq5bLU8n2YJQ5C9bXigzBUwBL4BrVsszwG3Prxe4m0YariCwUilzB3jmwbVWy21pEGjxxqXSbWlFoFUUbCICVsuWADwS8OsAjnlwrvrc3FLSGGgOYG+sljngK/AFaAP2B/yuphGE2RJ4W8ScceCCUOZJGr3AV+AjoF2VnC4xpz4QN6ltwaBQprto7/cCXcBZYFNRHei1WlYJZS4nVcDv9cNe+n0SypwHjgbmdlstt8YmYLXMBkr2WLhimpfAkAdXAo1lbYHVUgINLtc7Ai75qMqdRjfsAB5GNJmfJYhfdMSLbTHqjFCKwOEI0lXAvNXyM2BcFtQCe4DtAf9HQpmFcglY4D1wMMJnl3vaV2nJPYnOA1bL3cBx4KaDZoEda9jeWaBTKDOYwoFEdgIP3LA9AyOFv6mYdcey5556r4EBocyv1AtRBkarlVm0WtY5aEAo0xX3jlFuKZ6oVmbBLb7ZYUNJLjlrVaAfmHHy+qV5PAmBTJxJP/pqNhYKmVbgAPBUKPNt/cIa1/4AtWS4hwYohc4AAAAASUVORK5CYII=",
"ethereum" => "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TxQ8qInYQUchQXbQgKuIoVSyChdJWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIq4uToouU+L+k0CLGg+N+vLv3uHsHCLUSU822CUDVLCMRjYjpzKrY8YouBNCHMQxLzNRjycUUPMfXPXx8vQvzLO9zf44eJWsywCcSzzHdsIg3iGc2LZ3zPnGQFSSF+Jx43KALEj9yXXb5jXPeYYFnBo1UYp44SCzmW1huYVYwVOJp4pCiapQvpF1WOG9xVksV1rgnf2Egq60kuU5zCFEsIYY4RMiooIgSLIRp1UgxkaD9iId/0PHHySWTqwhGjgWUoUJy/OB/8LtbMzc16SYFIkD7i21/jAAdu0C9atvfx7ZdPwH8z8CV1vSXa8DsJ+nVphY6Anq3gYvrpibvAZc7wMCTLhmSI/lpCrkc8H5G35QB+m+B7jW3t8Y+Th+AFHW1fAMcHAKjecpe93h3Z2tv/55p9PcDpKVyu4rx+1IAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfnCQwSFQ84+9DIAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAA1ZJREFUWMO9l01oXFUUx3/3vXkz+TYwpgQEF8ZN/IqSgrHXBI2LcWNJzUYskTpxIQimcayCQoSi3XTevAo2Wl1LoZsqLmpWWchtQ11odQilGSsGgk1N22RoOjO+9+a6MA3T5mvmZWYuvMU799xz/+fc8wm7WMmk6u7tVd27kRHazWEh+CIUQgCDQWUYQQ/atnpNawY9jxf7+tTrdQWQSqkmrXHu/rsuTiymmusGQGuOAp0lpD3LyxytCwDbVt1ac/h+uu8zLmXlDmkE0P4rwNyELlyXUzUFYNvqIDCw1b7v09/Xp0ZqAmByUjVrTWonPtfFHhlRLVUHkMvxKbCnDNaOuTk+qyoAx1GPa81YuUJdl3cHBtQTVQPg+5wCRCVvm8/zdVUA2LZ6A5D307NZSKfBuwWeuyno5/btU4d2TOfbbZ48qVpzOf4AOu7Slpbg6lWYn1/zjczaRhPQCqGGe0QsdXXx6JkzciVQMcrnOQZ0FItw7RpkMnD9+hbMd/7/vAjQBmYjCMGD8/McA96p2AKplHqyUOC3hQW4fBlu394iOjLbqPYAGE3Q3s7T09PyUkUWmJ1ldG4OCoWAZc4DbkDxFty8w5uwMX3v6AP9/epgsUjS9+8pPOVZAMBikTCJS3/JbyuOAsdRLwwNkfU8uiyL44ZBvmztLQq0cJwcjzQ/xOrgoIoFigLbVt9pjaU17509ixACx/d5eVsLRJiiyGGjExEOc0Jr/r14Ub4SNA/EgR4h+GV4mEMHDjAciTBkGGQ20TpDO0OhTl5teJh4KMSvWvOUYRAPnIgSCXkTeAto1JoPgSv799OUz9NjWUysB18bEyzT0xClxTD40/M4UiwSNk3iMzPyn8CJqOQpklqTKCH9ZJq8fe4cq4uLEI3Sms3ypdY8v/4SEU6cPy/Hq1ILXJcJIF0aIL5POhbj/cZGPlhZ4ffSy4VgdjXLx2V11uU6tuOox3yfn9eS7vo6fXpjTmxr49npaZmuaj8wPi5nheCjnfjCYT4p9/KKW7JEQn4O/LjNoDJ14YJM1rQptSziwAbPNgxuRKOM1rwrHhuTfwux8SLTZHRqSi7UZTBJJOQPwOR6RQvxzcyM/L7ew+kR4CXTRHje5pWu5iuZVM/s3at6dyPjP1yoK/XOy4qhAAAAAElFTkSuQmCC",
"cardano" => "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAI9HpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjapVhpsvIwDvyvU8wR4lX2cbxWzQ3m+NOSkxB4wIP3QRGHYGypu7UkNP7330n/wctxjuQDp5hj3PDy2WdbcJK29Sp6NJvXo77s/hO+312n8weLSw6jW19T3Ocf1825wBoKzsJlodT2H+r9D9nv66eHhfaNnFgkVvR9obwv5Oz6wewLlOXWFnPiqwt1rLEfnqT1ITk4Xq4fizx+9wz0esBFZ+1wxm04OmeXAU4+jlzBScbRYBIMxgS5kvTIuyUA5BlO5yvDoimm+qeT7lg5z8zz6/TIlrf7FPcAcjzHp9fJhOesKPSXnX3az+z9dcikL4se0JfPnD1N9RleFB8BddydOlzRM8yr2EK2TgTT4sb4BCzB+s54J6i6gbW+ta3i3Uw2FnRN4003xUwzdGymwURvB1nGibXNOr2YHNtsmxP+vLzNtAxWO3i0rint3tnTFqPb5q2R7pawczeYag0WgxC+f9O3f5hTQsGYLZ1YwS5rBWyYIczJEdPAiJk7qEEBPt6PL+HVgcEgKEuIZABb1xI1mFsmcEq0w8SAcYWL4b4vAIiwdYAxxoEBsGZcMNFsbC0bAyATCCow3TpvKxgwIdgOI613LoKbZGVr/IWNTrXB4jLhOpIZmAguOgY3iDuQ5X2AftgnaKgEF3wIIQYOKeRQoos+hhgjR0mKhR174sCRmRNnLskln0KKiVNKOZVss0PSDDlmzinnXAr2LFi54N8FE0qptrrqa6AaK9dUcy0N8mm+hRYbt9RyK91215E/euzcU8+9DDMgpeFHGHHwSCOPMiG16Wj6GWacPNPMs5ys7bT+eH/BmtlZs8qUTOSTNVxlPpYwkk6CcAbCLHkDxlkokMwmnG3JeG+FOeFsy0h/LlgYGYSzboQxMOiHsWGagzuyi1Fh7p94I/Z3vNm/MkdC3ZfM/eTtGWtdylBTxlYUCqibQ/Th95GKTUWK3Y+RXv1wjrXaMr0UFjNLh4hrj3CihMl15txmGHVWC0FmrrnFbkLPgU310VVYEMLA1CKzfY09Tge2Agzu+B7S2mq6NpBL5QshRevJv4yjlUqp69hnk3wfBrc6g2uzMfessBhA3zkGGUwePFut6wc4WwOQ7t4mUguBpL8BI6gMF9MOzZgFPnAFs72WDopnNgJNYiU8VgNQSFAJRoAxfYYiwAARWFUHyLQAxjvMw/fefgBz+AW36ObXtv2DZ0r/g2dfjBdp0Dtt3EsDBynBLLb7CYnOmVDzZsd/aqWpTkHK8FM3wWl/R7lYYYGGQNHcnIXzxOrUDBDBRUhvMiPbTSn/SsEODOr5C4k6rB14IlK7pR633ZLpyp+AWpKhpRlFjaeFqcgWygRQgCAig6eC8uUlfKCdXSdqXRTrglqHkr2UghAKtxDKwImdgCG2DuB421ylKpOScX3qpFNHoGgpWAmsSmBXAs2AJcVgErYa46opGH5VFcV5p6vPZVXvQaGXqAgkR2LBhGnH1B9QekbXANiueqE3QlGZ4MoSytLJTSWqkSVUkSzJVAvrxgQ+gCJO6BT/qwABgQtl72aJrDzWSx0/VljksNkYoxkc6yAsn5Hpj5nBrCjYYwDitAYBg7lzqH0X2u6wpFfgfYsdvQLvbV69S6sr/dDP/ONVxoBnMxfVYBgbKq9iOh2EXXKfEF91sLgfrk1u450Vy2UZ2oagRHKHNUDSiZQRtSOQLJzFBgNIcfN2J950lxKZx2sY6ON6I/mqyo3AcHWU2hAxHWl9IGYhHrR+CE8EE0o7wqmpApYWFA2U99lhf2PcmqB0TMgDihuStvyS7l5sSAqxVpuu1SZotcnIV9hGVg+l7fBzbkMhOJLdXTqhI59otts02+H7mR3gVFre1KDeQPsDcyAetGhS2uzqZWjh3Ac6rHALHpNG2yYqQsZ/aiizWwRNbaho2gGg0PlV6FYldZNmRSEYkMSKAIwFshgTATu2M872tSE2Xbvr2lHXTrK2d/S2in6REeiWEtRFWKA3gk7KN0AEJJU9si1r0QAlE70jHGWtX2sDtMSVEOLo3nZCJfp+liWhDKdlW3US37pIc1WouCoUk5Qo2a3BL+2j6q2PGtPldpAnA+JhVcGMvSTCgCU4QbVgGsVc99TzbTvaNx2kCKCfR+XLPBSdM1RvkUq3kMTWzvTVKQrZKCZTuwHOuRvoWLqBcXQDgii6HsbtiTpMzz0+Hearw6qZm5drV2mOwQGBU40LbDsgbQ2MuQfGijwEXYRrXbKWiktRuk+/0vrpNu5Bl+isr8JMR0OhwjQizMC5Qph9dIQUQCBV5tSiXaVoS3CtMIMOYPzYjZcOcmoHyeggAZl2kEgiVRsDQztiR/5QmhS01VLG1VJyUE1aVYk2lfUWDqoSeiKTnyL5QCO0RIKe7b1MVuCJUFQjPx0meNx/evzgsFQVeRyStX8cclwRms+cSSvG9pyMPTek25XGqqYx1jTWB24BIQ5pcKY0OPc5GRmZ7lMy9HFLyvJ465dG59Yfk/myP0bue+gNVoeMDFmlQ26avo5SvL1K0/EuT1/TNH2dp48QT2W/GVw1iC5F6PsadClBdKlBZ25uT3Lz09x5aenp957+s5aefutW37Xwe9+hMNMtWuTJxF2wsDDXtH3ZbUKDnl7YRGLDR2G7Osy9Bbt2gNKAcV5d7WoDYfeXzfsFBvpL4/ms76QnjefZiqwi3df9HPOKAC3SOAPF6x5ySYBWFKmDLkg+t7NJMqy1bjgCH+TxUPe+dEW8tO0Fsdhbn1bSD/5HEuyQUcStziU3mJV8JPXsAjTzfV6gP2noCXb07o7n0yZVn4189PBA7/5eJYem0UQR1efodvS+028ztFXdgEEdrt3oPCfhxp/b2dUWkEnSg2vXLPdAcWTtmuG40RyJNJGlIFx1DJzyVcZV23j6AzJPR/qsr/+9rafXff0HwaZQrgYZzehDIyL3Pn/okOm350yfjvTjhvCPT6Podeb58AHkXkTpnx9A7qFCz250//Kcjb57IPLxs9o/PYJUddC/PIK8jm+e1UKe6Bzo/y4fGVakXTc9AAABhWlDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw0AcxV9TxQ8qInYQUchQXbQgKuIoVSyChdJWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIq4uToouU+L+k0CLGg+N+vLv3uHsHCLUSU822CUDVLCMRjYjpzKrY8YouBNCHMQxLzNRjycUUPMfXPXx8vQvzLO9zf44eJWsywCcSzzHdsIg3iGc2LZ3zPnGQFSSF+Jx43KALEj9yXXb5jXPeYYFnBo1UYp44SCzmW1huYVYwVOJp4pCiapQvpF1WOG9xVksV1rgnf2Egq60kuU5zCFEsIYY4RMiooIgSLIRp1UgxkaD9iId/0PHHySWTqwhGjgWUoUJy/OB/8LtbMzc16SYFIkD7i21/jAAdu0C9atvfx7ZdPwH8z8CV1vSXa8DsJ+nVphY6Anq3gYvrpibvAZc7wMCTLhmSI/lpCrkc8H5G35QB+m+B7jW3t8Y+Th+AFHW1fAMcHAKjecpe93h3Z2tv/55p9PcDpKVyu4IiMZUAAA12aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjJkOWJhMWJjLTU5ODUtNGI2YS04NWMxLWNiNDU3MmY1Y2EyOCIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpiMzMzZmM1ZS1kZTJjLTRkNDEtOGYwNy1mMmVlYzgzOWUwNGEiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplZDFjN2Q0OC01MmEwLTRhY2UtYWI0Ni1mOWQwOTk5ZGE0OGMiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJXaW5kb3dzIgogICBHSU1QOlRpbWVTdGFtcD0iMTY5NDU0NTY4Nzg3MTk2NSIKICAgR0lNUDpWZXJzaW9uPSIyLjEwLjMyIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAgMi4xMCIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMzowOToxMlQxMzowODowNy0wNjowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjM6MDk6MTJUMTM6MDg6MDctMDY6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMGY3MGJmYy04OTJhLTQzNTktOWRhNi1lMTdkMmNkOTNkMTkiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OndoZW49IjIwMjMtMDktMTJUMTM6MDg6MDciLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+P3y/GgAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+cJDBMIB8iOXtEAAALnSURBVFjD3ZdLSJVBFMd/hkHeRYty0YMem7BbUNlUO4tKslpENRGCkhGlbcIHEbSSoCh6eF300h4EXVOEySgio9ootEiHiiCDVi7F2mnZQ2zRuTV83jvffbjIDnx8M+c535z/nDkfTGtS5jzKnM7FxYyQAGUo89GjsRaIeuyHUWadL0R+yAItcCq1VJeG2NcD75n+pEwlyvSgzPI0dBXK9KNMZTqu89Ncwn6gBCgGPkigQqBH5BuwekTGUUCJfjw3EP6lA8AWrG53eEslWBRY7OAiDqyX/Ge81a0ocyYD/TqUqctAP4YyLb4UFAGRECc7gN1AG1Y3C28zUA50YXW3x3pOcNfzQgEFR4CzWD2IMhWBvJbJgrsc3j6sNihTDBwFbmJ1X7YgjAmYFgC7gJ0BuU5isw0wwAVgq2BkY7YL6BAHnTJ/l6RQzQrwBuR9B5gL3PYFyMsAQAuBceCwg4HYHzD+PqqPgFtAAVYPTmUhakaZCXmqPXo1jt71yfL7MyfXAWWaBDCJKjaKMlccp0VAbQAXPswkqAZloo6fuzAxhjIlTqy2GcA852hE5JntKVa+4jURmI8741FgDPgi85/AULJtLEjCu+ZsbY0nBdUoMy56TelkNxMQLgG+Cgj3AO1Y3SSyeqACeADckJI8lPsClDkI1AEXsTqOMg3AJUejFvgGuIA7idXn5DaMAY1YfTXby+gQsBqockq1SyulK3JplXODFkqJzvo2PA60Aidk/jwgf+IUqQQ9lvcxoCXsVswLbPk94AdWV3nSsl0w0InVL4RXIl/6EKufemzjwCKs3pSqFI8CI/4uUXcD3eKwAfiO1ZeB3jQwN+gcwwxOgTIRYAVW9zu8vXLpILIBR1YKvMbqz1PVEcWAvkCf1yPt2Vv5skTwcuAZ0DaVPWGv9HkDTio+pfgneCP6Hf9FOx5Jt732tPOFuWBgjXQ2qQK8QhnjsW+U1GWJAatfAvNDft2GPfbL/vks/wIuPeijyMLzBQAAAABJRU5ErkJggg==",
_ => "",
};
let decoded = custom_engine.decode(watermark).ok()?;
let cursor = Cursor::new(decoded);
ImageReader::with_format(cursor, image::ImageFormat::Png)
.decode()
.ok()
}
}
fn adjust_alpha(image: &mut RgbaImage, custom_opacity: u8) {
for pixel in image.pixels_mut() {
if pixel[3] == 0 {
pixel[3] = custom_opacity;
}
}
let mut found_fully_transparent = false;
for pixel in image.pixels() {
if pixel[3] == 0 {
found_fully_transparent = true;
break;
}
}
if !found_fully_transparent {
for pixel in image.pixels_mut() {
pixel[3] = custom_opacity;
}
}
}
pub fn create_img(ciphertext: &str, style: &str, watermark: &str, r: Option<u8>, g: Option<u8>, b: Option<u8>, a: Option<u8>, w: Option<u32>, h: Option<u32>) -> Option<String> {
let custom_engine: engine::GeneralPurpose = engine::GeneralPurpose::new(&alphabet::STANDARD, general_purpose::PAD);
let r = r.unwrap_or(100);
let g = g.unwrap_or(134);
let b = b.unwrap_or(131);
let width = ciphertext.len() as u32;
let height = width;
let mut img: RgbaImage = image::ImageBuffer::new(width, height);
let last_column = ciphertext.chars().last();
let shifted_ciphertext = if let Some(last) = last_column {
last.to_string() + &ciphertext[..width as usize - 1]
} else {
ciphertext.to_string()
};
for x in 0..width {
let char = shifted_ciphertext.chars().nth(x as usize).unwrap_or('a');
let color = get_color(char).unwrap_or((0, 0, 0));
for y in 0..height {
let red = if y == 0 {
color.0
} else {
(color.0 as i32 - (y as i32 + r as i32)).abs().min(255) as u8
};
let green = if y == 0 {
color.1
} else {
(color.1 as i32 - (y as i32 + g as i32)).abs().min(255) as u8
};
let blue = if y == 0 {
color.2
} else {
(color.2 as i32 - (y as i32 + b as i32)).abs().min(255) as u8
};
let rgba_color = Rgba([red, green, blue, 255]);
img.put_pixel(x as u32, y, rgba_color);
}
}
let mut row_count = 0;
for y in 0..height {
let contains_white = (0..width).any(|x| {
let pixel = img.get_pixel(x, y);
pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255
});
if contains_white && y != 0 {
row_count += 1;
}
}
let offset = if row_count > 10 { height / row_count } else { 1 };
let mut new_img = image::ImageBuffer::new(width, height);
let mut row_shift = 0;
for y in 0..height {
if (y as u32) % offset == 0 {
row_shift += 1;
}
for x in 0..width {
if (y as u32) >= row_shift && y != 0 {
let pixel = *img.get_pixel(x, y - row_shift);
new_img.put_pixel(x, y, pixel);
} else {
let pixel = *img.get_pixel(x, y);
new_img.put_pixel(x, y, pixel);
}
}
}
match style {
"v" => {
new_img = imageops::rotate90(&new_img);
},
"v2" => {
new_img = imageops::rotate270(&new_img);
new_img = imageops::flip_vertical(&new_img);
},
"h" => {
},
"h2" => {
new_img = imageops::flip_vertical(&new_img);
},
_ => { /* Default, no change */ }
}
let (alpha, center_w, center_h) = if custom_engine.decode(watermark).ok().is_some() {
if let (Some(a), Some(w), Some(h)) = (a, w, h) {
(Some(a), Some(w), Some(h))
} else {
return None;
}
} else {
(Some(0), Some(32), Some(32))
};
let watermark_img = load_watermark(watermark, alpha, center_w, center_h);
if let Some(watermark_img) = watermark_img {
let nw = (width / 2) - (center_w.unwrap() / 2);
let nh = (height / 2) - (center_h.unwrap() / 2);
let mut watermark_img = watermark_img.to_rgba8();
adjust_alpha(&mut watermark_img, alpha.unwrap_or(0));
image::imageops::overlay(&mut new_img, &watermark_img, nw, nh);
}
let mut buf = Vec::new();
let encoder = PngEncoder::new(&mut buf);
let dyn_img: DynamicImage = DynamicImage::ImageRgba8(new_img.clone());
if let Err(err) = encoder.encode(&dyn_img.to_rgba8(), width, height, ColorType::Rgba8) {
eprintln!("Error encoding image: {:?}", err);
return None;
}
let encoded_image = custom_engine.encode(&buf);
Some(encoded_image)
}
/*
pub fn create_img(ciphertext: &str, style: &str, watermark: &str, r: Option<u8>, g: Option<u8>, b: Option<u8>, a: Option<u8>, w: Option<u32>, h: Option<u32>) -> Option<String> {
let custom_engine: engine::GeneralPurpose = engine::GeneralPurpose::new(&alphabet::STANDARD, general_purpose::PAD);
let r = r.unwrap_or(100);
let g = g.unwrap_or(134);
let b = b.unwrap_or(131);
let width = ciphertext.len() as u32;
let height = width;
let mut img: RgbaImage = image::ImageBuffer::new(width, height);
let last_column = ciphertext.chars().last();
let shifted_ciphertext = if let Some(last) = last_column {
last.to_string() + &ciphertext[..width as usize - 1]
} else {
ciphertext.to_string()
};
for x in 0..width {
let char = shifted_ciphertext.chars().nth(x as usize).unwrap_or('a');
let color = get_color(char).unwrap_or((0, 0, 0));
for y in 0..height {
let red = if y == 0 {
color.0
} else {
(color.0 as i32 - (y as i32 + r as i32)).abs().min(255) as u8
};
let green = if y == 0 {
color.1
} else {
(color.1 as i32 - (y as i32 + g as i32)).abs().min(255) as u8
};
let blue = if y == 0 {
color.2
} else {
(color.2 as i32 - (y as i32 + b as i32)).abs().min(255) as u8
};
let rgba_color = Rgba([red, green, blue, 255]);
img.put_pixel(x as u32, y, rgba_color);
}
}
let mut buf = Vec::new();
let encoder = PngEncoder::new(&mut buf);
let dyn_img: DynamicImage = DynamicImage::ImageRgba8(img.clone());
encoder
.encode(&dyn_img.to_rgba8(), width, height, ColorType::Rgba8)
.ok()?;
let mut row_count = 0;
for y in 0..height {
let contains_white = (0..width).any(|x| {
let pixel = img.get_pixel(x, y);
pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255
});
if contains_white && y != 0 {
row_count += 1;
}
}
let offset = if row_count > 10 { height / row_count } else { 1 };
let mut new_img = image::ImageBuffer::new(width, height);
let mut row_shift = 0;
for y in 0..height {
if (y as u32) % offset == 0 {
row_shift += 1;
}
for x in 0..width {
if (y as u32) >= row_shift && y != 0 {
let pixel = *img.get_pixel(x, y - row_shift);
new_img.put_pixel(x, y, pixel);
} else {
let pixel = *img.get_pixel(x, y);
new_img.put_pixel(x, y, pixel);
}
}
}
let (alpha, center_w, center_h) = if custom_engine.decode(watermark).ok().is_some() {
if let (Some(a), Some(w), Some(h)) = (a, w, h) {
(Some(a), Some(w), Some(h))
} else {
return None;
}
} else {
(Some(0), Some(32), Some(32))
};
let watermark_img = load_watermark(watermark, alpha, center_w, center_h);
if let Some(watermark_img) = watermark_img {
let nw = (width / 2) - (center_w.unwrap() / 2);
let nh = (height / 2) - (center_h.unwrap() / 2);
let mut watermark_img = watermark_img.to_rgba8();
adjust_alpha(&mut watermark_img, alpha.unwrap_or(0));
image::imageops::overlay(&mut new_img, &watermark_img, nw, nh);
}
let mut buf = Vec::new(); // Define buf here.
let encoder = PngEncoder::new(&mut buf);
match style {
"v" => {
new_img = imageops::rotate90(&new_img);
},
"v2" => {
new_img = imageops::rotate270(&new_img);
new_img = imageops::flip_vertical(&new_img);
},
"h" => {
},
"h2" => {
new_img = imageops::flip_vertical(&new_img);
},
_ => { /* Default, no change */ }
}
let dyn_img: DynamicImage = DynamicImage::ImageRgba8(new_img); // Use new_img.
if let Err(err) = encoder.encode(&dyn_img.to_rgba8(), width, height, ColorType::Rgba8) {
eprintln!("Error encoding image: {:?}", err);
return None; // Handle the error more explicitly.
}
let encoded_image = custom_engine.encode(&buf);
Some(encoded_image)
}
*/