From 055c0446962bdb3f87d393af5dcab6b2650f9e6c Mon Sep 17 00:00:00 2001 From: b Date: Wed, 30 Nov 2022 18:28:28 +0000 Subject: [PATCH] npb ready but debug needed --- core.c | 37 ++- core.h | 15 +- enhance.c | 4 +- f2h.c | 4 +- npb.c | 611 ++++++++++++++++++++++++++++++++++++++++++--- testimg/133x94.png | Bin 0 -> 1810 bytes testimg/133x95.png | Bin 0 -> 2573 bytes testimg/134x94.png | Bin 0 -> 8767 bytes testimg/96x68.png | Bin 0 -> 527 bytes testimg/96x69.png | Bin 0 -> 642 bytes testimg/97x68.png | Bin 0 -> 696 bytes 11 files changed, 633 insertions(+), 38 deletions(-) create mode 100644 testimg/133x94.png create mode 100644 testimg/133x95.png create mode 100644 testimg/134x94.png create mode 100644 testimg/96x68.png create mode 100644 testimg/96x69.png create mode 100644 testimg/97x68.png diff --git a/core.c b/core.c index 4283561..5e80a17 100644 --- a/core.c +++ b/core.c @@ -514,6 +514,21 @@ int load_picture (uint_fast16_t id, char *path, struct IL_full_info *info, FLAG_ return convert_picture(id, info, flags); } +int load_picture_mem (uint_fast16_t id, const void *address, ILuint size, struct IL_full_info *info, FLAG_TYPE *flags) +{ + if (id >= n_pictures) + return EINVAL; + + // if(!(picture[id].open)) + create_picture(id); + + ilBindImage(picture[id].handle); + if (!ilLoadL(IL_TYPE_UNKNOWN, address, size)) + return EIO; + + return convert_picture(id, info, flags); +} + int build_picture (uint_fast16_t id, ILint width, ILint height, ILint frames, struct IL_full_info *info, FLAG_TYPE *flags) { struct IL_full_info reference_info; @@ -702,6 +717,24 @@ int perform_action_1picture ( ); } +int perform_action_1picture_1pixel ( + uint_fast16_t id, + ILint x, ILint y, ILint f, + ACTION_F *function, + FLAG_TYPE flags, + void *data +) +{ + return perform_action_1picture ( + id, + x, y, f, + 1, 1, 1, + function, + flags | IN_WINDOW, + data + ); +} + int perform_action ( uint_fast16_t n, uint_fast16_t *id, @@ -1470,7 +1503,7 @@ int copy_pixels (ILuint n, struct PixelInfo *p, void *data) { if (!(p[1].flags & EFF_GRAY)) return EINVAL; - p[1].value = p[0].value + p[1].value = p[0].value; } else { @@ -1513,7 +1546,7 @@ int fill_color (ILuint n, struct PixelInfo *p, void *data) } -ILuint copy_1p_alpha (ILint64 src, ILint64 dst, ILint64 alpha, iILint64 max) +ILuint copy_1p_alpha (ILint64 src, ILint64 dst, ILint64 alpha, ILint64 max) { ILint64 v = (src * alpha + dst * (max - alpha)) / max; return (ILuint)v; diff --git a/core.h b/core.h index 15af406..2c9f2fe 100644 --- a/core.h +++ b/core.h @@ -1,7 +1,7 @@ /* core.h The tool with multiple enhancements and manipulations of pictures -29.11.2022 +30.11.2022 Copyright (C) 2022 Balthasar Szczepański @@ -179,7 +179,7 @@ struct ColorInfo ILuint value; ILuint alpha; ILuint index; -} +}; typedef int (ACTION_F)(ILuint n, struct PixelInfo *info, void *data); @@ -201,6 +201,7 @@ int reserve_pictures (uint_fast16_t n); void get_flags (struct IL_full_info *info, FLAG_TYPE *flags); int convert_picture (uint_fast16_t id, struct IL_full_info *info, FLAG_TYPE *flags); int load_picture (uint_fast16_t id, char *path, struct IL_full_info *info, FLAG_TYPE *flags); +int load_picture_mem (uint_fast16_t id, const void *address, ILuint size, struct IL_full_info *info, FLAG_TYPE *flags); int save_picture (uint_fast16_t id, char *path, FLAG_TYPE flags); int build_picture (uint_fast16_t id, ILint width, ILint height, ILint frames, struct IL_full_info *info, FLAG_TYPE *flags); int build_picture_from_info (uint_fast16_t id, struct IL_full_info *reference_info, struct IL_full_info *info, FLAG_TYPE *flags); @@ -209,6 +210,14 @@ int get_info (uint_fast16_t id, struct IL_full_info *info, ILint frame); ILuint upscale_value (ILubyte x, ILint bytes); ILubyte downscale_value (ILuint x, ILint bytes); +int perform_action_1picture_1pixel ( + uint_fast16_t id, + ILint x, ILint y, ILint f, + ACTION_F *function, + FLAG_TYPE flags, + void *data +); + int perform_action_1picture ( uint_fast16_t id, ILint x0, ILint y0, ILint f0, @@ -238,5 +247,5 @@ int perform_action_palette_mix ( int copy_pixels (ILuint n, struct PixelInfo *p, void *data); -ILuint copy_1p_alpha (ILint64 src, ILint64 dst, ILint64 alpha, iILint64 max); +ILuint copy_1p_alpha (ILint64 src, ILint64 dst, ILint64 alpha, ILint64 max); int fill_color (ILuint n, struct PixelInfo *p, void *data); \ No newline at end of file diff --git a/enhance.c b/enhance.c index 7fc3a75..42ac72d 100644 --- a/enhance.c +++ b/enhance.c @@ -1,7 +1,7 @@ /* enhance.c The tool with multiple enhancements and manipulations of pictures -29.11.2022 +30.11.2022 Copyright (C) 2022 Balthasar Szczepański @@ -60,6 +60,8 @@ int main (int argc, char **argv) f = &subtool_reveal; else if (strcmp(argv[1], "diff")==0) f = &subtool_diff; + else if (strcmp(argv[1], "npb")==0) + f = &subtool_npb; else finish(EINVAL, "Unknown mode.\n"); diff --git a/f2h.c b/f2h.c index 32fb519..fefc018 100644 --- a/f2h.c +++ b/f2h.c @@ -1,7 +1,7 @@ /* f2h.c make file into h -29.11.2022 +30.11.2022 Copyright (C) 2022 Balthasar Szczepański @@ -90,7 +90,7 @@ int main (int argc, char **argv) (i==0) ? "\n\t" : " ", x ); - ++size + ++size; } fputs ("\n};\n", outfile); fprintf( diff --git a/npb.c b/npb.c index 1c03b99..422ebd9 100644 --- a/npb.c +++ b/npb.c @@ -1,7 +1,7 @@ /* npb.c Take a picture and add a newpixbot to it! -29.11.2022 +30.11.2022 Copyright (C) 2013 - 2015, 2022 Balthasar Szczepański @@ -28,6 +28,7 @@ on Pentium III libdevil must be recompiled with #include #include +#include #include "core.h" #include "npb.h" @@ -38,15 +39,30 @@ on Pentium III libdevil must be recompiled with #include "npb_ong1.h" #include "npb_403.h" -#define NPB0_L 21 -#define NPB0_R 116 -#define NPB0_T 122 -#define NPB0_B 189 +#define NPB0_L 21 +#define NPB0_R 116 +#define NPB0_T 122 +#define NPB0_B 189 -#define NPB1_L 22 -#define NPB1_R 154 -#define NPB1_T 69 -#define NPB1_B 162 +#define NPB1_L 22 +#define NPB1_R 154 +#define NPB1_T 69 +#define NPB1_B 162 + +#define NPB2_L 36 +#define NPB2_R 199 +#define NPB2_T 171 +#define NPB2_B 171 + +#define NPB_ONG1_L -74 +#define NPB_ONG1_R 102 +#define NPB_ONG1_T 66 +#define NPB_ONG1_B 69 + +#define NPB_403_L 224 +#define NPB_403_R 265 +#define NPB_403_T 261 +#define NPB_403_B 261 char NPB_MISSING_ARGS[] = "Missing parameters.\nnpb [-o -4 -r -b -c] inPix outPix\n"; @@ -58,29 +74,38 @@ int subtool_npb (int argc, char **argv, int argi, char **err) uint_fast8_t corners = 0; uint_fast8_t fullsize = 0; uint_fast8_t fill_white = 0; + uint_fast8_t npb_after = 0; uint_fast8_t expand_left = 0; uint_fast8_t expand_right = 0; uint_fast8_t expand_up = 0; uint_fast8_t expand_down = 0; uint_fast8_t border_outside = 0; + uint_fast8_t fixed_frame = 0; ILuint cut_border = 0; ILuint new_border = 2; ILuint max = 0xFF; //upscale_value(0xFF, info[0].image_bpc); // bpc=1 - ILuint effective_width, effective_height; + ILint width_content, height_content, x0_content_own, y0_content_own, x0_content_global, y0_content_global; + ILint width_border, height_border, x0_border, y0_border; + ILint width_npb, height_npb, x0_npb, y0_npb; + ILint width_total, height_total; const uint8_t *npb_pix; uint_fast32_t npb_size; - ILuint x_l, x_r, y_t, y_b; + ILint space_l, space_r, space_t, space_b; struct ColorInfo color; - struct IL_full_info info[3]; - FLAG_TYPE flags [3] = { - CANNOT_BE_GRAY | CANNOT_BE_INDEXED | NOT_WRITABLE, - CANNOT_BE_GRAY | CANNOT_BE_INDEXED | NOT_WRITABLE | MUST_HAVE_ALPHA, - CANNOT_BE_GRAY | CANNOT_BE_INDEXED | NOT_READABLE | MUST_HAVE_ALPHA, - } - ILuint x0[3]; - ILuint y0[3]; + struct IL_full_info info_content; + struct IL_full_info info_npb; + struct IL_full_info info_total; + FLAG_TYPE flags_content = CANNOT_BE_GRAY | CANNOT_BE_INDEXED | NOT_WRITABLE; + FLAG_TYPE flags_npb = CANNOT_BE_GRAY | CANNOT_BE_INDEXED | NOT_WRITABLE | MUST_HAVE_ALPHA; + FLAG_TYPE flags_total = CANNOT_BE_GRAY | CANNOT_BE_INDEXED | MUST_HAVE_ALPHA; + + uint_fast16_t id[2]; + FLAG_TYPE flags[2]; + ILint x0[2]; + ILint y0[2]; + ILint f0[2] = {0, 0}; struct option long_options[] = { {"ong1", no_argument, NULL, 'o'}, @@ -96,6 +121,8 @@ int subtool_npb (int argc, char **argv, int argi, char **err) int opt; int r; + /* we get options first */ + optind = argi; while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { @@ -108,10 +135,10 @@ int subtool_npb (int argc, char **argv, int argi, char **err) t403 = 1; break; case 'r': - sscanf(optarg, "%lu, &cut_border); + sscanf(optarg, "%u", &cut_border); break; case 'b': - sscanf(optarg, "%lu, &new_border); + sscanf(optarg, "%u", &new_border); fullsize = 1; break; case 'c': @@ -129,56 +156,580 @@ int subtool_npb (int argc, char **argv, int argi, char **err) return EINVAL; } - r = reserve_pictures(1); + r = reserve_pictures(3); if (r) { *err = CREATE_FAILED; return r; } - r = load_picture(0, argv[optind], &(info[0]), &(flags[0]); + /* load content to insert to npb */ + + r = load_picture(0, argv[optind], &(info_content), &(flags_content)); if (r) { *err = LOAD_FAILED; return r; } - effective_width = (info[0].image_width > (2*cut_border)) ? (info[0].image_width - (2*cut_border)) : 0; - effective_height = (info[0].image_height > (2*cut_border)) ? (info[0].image_height - (2*cut_border)) : 0; + /* determine actually used rectangle without border */ + + width_content = (info_content.image_width > (2*cut_border)) ? (info_content.image_width - (2*cut_border)) : 0; + height_content = (info_content.image_height > (2*cut_border)) ? (info_content.image_height - (2*cut_border)) : 0; + x0_content_own = cut_border; + y0_content_own = cut_border; + + /* select npb mode */ if (t403) { npb_pix = npb_403; npb_size = npb_403_size; + space_l = NPB_403_L; + space_r = NPB_403_R; + space_t = NPB_403_T; + space_b = NPB_403_B; + expand_right = 1; + expand_up = 1; + border_outside = 1; + fill_white = 1; } else if (ong1) { npb_pix = npb_ong1; npb_size = npb_ong1_size; + space_l = NPB_ONG1_L; + space_r = NPB_ONG1_R; + space_t = NPB_ONG1_T; + space_b = NPB_ONG1_B; + expand_left = 1; + expand_up = 1; + npb_after = 1; } - else if ((effective_width <= NPB0_X_MAX) && (effective_height <= NPB0_Y_MAX) && (!fullsize)) + else if ((width_content <= (NPB0_R - NPB0_L + 1)) && (height_content <= (NPB0_B - NPB0_T + 1)) && (!fullsize)) { npb_pix = npb0; npb_size = npb0_size; + space_l = NPB0_L; + space_r = NPB0_R; + space_t = NPB0_T; + space_b = NPB0_B; fill_white = 1; new_border = 0; + npb_after = 1; + fixed_frame = 1; } - else if ((effective_width <= NPB1_X_MAX) && (effective_height <= NPB1_Y_MAX) && (!fullsize)) + else if ((width_content <= (NPB1_R - NPB1_L + 1)) && (height_content <= (NPB1_B - NPB1_T + 1)) && (!fullsize)) { npb_pix = npb1; npb_size = npb1_size; + space_l = NPB1_L; + space_r = NPB1_R; + space_t = NPB1_T; + space_b = NPB1_B; fill_white = 1; new_border = 0; + npb_after = 1; + fixed_frame = 1; } else { npb_pix = npb2; npb_size = npb2_size; + space_l = NPB2_L; + space_r = NPB2_R; + space_t = NPB2_T; + space_b = NPB2_B; + expand_left = 1; + expand_up = 1; + npb_after = 1; } - - + + /* load selected npb */ + + r = load_picture_mem(1, npb_pix, npb_size, &(info_npb), &(flags_npb)); + if (r) + { + *err = LOAD_FAILED; + return r; + } + + /* determine rectangle of content with new border */ + + if (border_outside) + { + width_border = width_content; + height_border = height_content; + } + else + { + width_border = width_content + new_border * 2; + height_border = height_content + new_border * 2; + } + + /* npb rectangle too */ + + width_npb = info_npb.image_width; + height_npb = info_npb.image_width; + x0_npb = 0; + y0_npb = 0; + + /* initial total size, to update later */ + + width_total = width_npb; + height_total = height_npb; + + /* determine content border placement in full picture */ + + if (expand_right && (width_border <= (space_r - space_l +1))) + x0_border = space_l; + else if (expand_left && (width_border <= (space_r - space_l +1))) + x0_border = space_r - width_border + 1; + else + x0_border = (space_l + space_r + 1 - width_border) / 2; + + if (expand_down && (height_border <= (space_b - space_t +1))) + y0_border = space_t; + else if (expand_up && (height_border <= (space_b - space_t +1))) + y0_border = space_b - height_border + 1; + else + y0_border = (space_t + space_b + 1 - height_border) / 2; + + /* determine content placement in border in full picture */ + + if (border_outside) + { + x0_content_global = x0_border; + y0_content_global = y0_border; + } + else + { + x0_content_global = x0_border + new_border; + y0_content_global = y0_border + new_border; + } + + /* adjust full picture size & offsets if content doesn't fit */ + + if (width_total < (x0_border + width_border)) + width_total = x0_border + width_border; + if (height_total < (y0_border + height_border)) + height_total = y0_border + height_border; + if (x0_border < 0) + { + width_total -= x0_border; + x0_npb -= x0_border; + x0_content_global -= x0_border; + x0_border = 0; + } + if (y0_border < 0) + { + height_total -= y0_border; + y0_npb -= y0_border; + y0_content_global -= y0_border; + y0_border = 0; + } + + /* adjust for external border */ + + if (border_outside) + { + width_total += 2 * new_border; + height_total += 2 * new_border; + x0_border += new_border; + y0_border += new_border; + x0_content_global += new_border; + y0_content_global += new_border; + x0_npb += new_border; + y0_npb += new_border; + } + + /* ok setup done, now perform actions */ + + /* 1: create target picture */ + + r = build_picture(2, width_total, height_total, 1, &info_total, &flags_total); + if (r!=0) + { + *err = LOAD_FAILED; + return r; + } + + /* 2: init background */ + + color.red = max; + color.green = max; + color.blue = max; + color.alpha = (border_outside && fill_white) ? max : 0; + + r = perform_action_1picture( + 2, //id + border_outside ? new_border : 0, //x0 + border_outside ? new_border : 0, //y0 + 0, //f0 + border_outside ? (width_total - 2 * new_border) : width_total, //width + border_outside ? (height_total - 2 * new_border) : height_total, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + /* 3: draw border outside */ + + if (border_outside && (new_border >0)) + { + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = max; + + r = perform_action_1picture( //top + 2, //id + 0, //x0 + 0, //y0 + 0, //f0 + width_total, //width + new_border, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture( //bottom + 2, //id + 0, //x0 + height_total - new_border, //y0 + 0, //f0 + width_total, //width + new_border, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture( //left + 2, //id + 0, //x0 + new_border, //y0 + 0, //f0 + new_border, //width + height_total - 2*new_border, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture( //right + 2, //id + width_total - new_border, //x0 + new_border, //y0 + 0, //f0 + new_border, //width + height_total - 2*new_border, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + if (corners) + { + r = perform_action_1picture_1pixel( //top-left + 2, //id + new_border, //x0 + new_border, //y0 + 0, //f0 + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture_1pixel( //top-right + 2, //id + width_total - new_border - 1, //x0 + new_border, //y0 + 0, //f0 + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture_1pixel( //bottom-right + 2, //id + width_total - new_border - 1, //x0 + height_total - new_border - 1, //y0 + 0, //f0 + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture_1pixel( //bottom-left + 2, //id + new_border, //x0 + height_total - new_border - 1, //y0 + 0, //f0 + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + } + } + + /* 4: insert npb before */ + + if (!npb_after) + { + id[0] = 1; //npb + id[1] = 2; //total + x0[0] = 0; + x0[1] = x0_npb; + y0[0] = 0; + y0[1] = y0_npb; + flags[0] = flags_npb | IN_WINDOW; + flags[1] = flags_total | IN_WINDOW; + + r = perform_action( + 2, //n + id, + x0, y0, f0, + width_npb, height_npb, 1, + ©_pixels, + flags, + &max //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + } + + /* 5: fill_background */ + + if ((!border_outside) && fill_white) + { + color.red = max; + color.green = max; + color.blue = max; + color.alpha = max; + + r = perform_action_1picture( + 2, //id + fixed_frame ? space_l : x0_content_global, //x0 + fixed_frame ? space_t : y0_content_global, //y0 + 0, //f0 + fixed_frame ? (space_r - space_l + 1) : width_content, //width + fixed_frame ? (space_b - space_t + 1) : height_content, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + } + + /* 6: insert content */ + + id[0] = 0; //content + id[1] = 2; //total + x0[0] = x0_content_own; + x0[1] = x0_content_global; + y0[0] = y0_content_own; + y0[1] = y0_content_global; + flags[0] = flags_content | IN_WINDOW; + flags[1] = flags_total | IN_WINDOW; + + r = perform_action( + 2, //n + id, + x0, y0, f0, + width_content, height_content, 1, + ©_pixels, + flags, + &max //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + /* 7: draw border */ + + if ((!border_outside) && (new_border >0)) + { + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = max; + + r = perform_action_1picture( //top + 2, //id + corners ? (x0_border + 1) : x0_border, //x0 + y0_border, //y0 + 0, //f0 + corners ? (width_border - 2) : width_border, //width + new_border, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture( //bottom + 2, //id + corners ? (x0_border + 1) : x0_border, //x0 + y0_border + height_border - new_border, //y0 + 0, //f0 + corners ? (width_border - 2) : width_border, //width + new_border, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture( //left + 2, //id + x0_border, //x0 + corners ? (y0_border + 1) : y0_content_global , //y0 + 0, //f0 + new_border, //width + corners ? (height_border - 2) : height_content, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + + r = perform_action_1picture( //right + 2, //id + x0_border + width_border - new_border, //x0 + corners ? (y0_border + 1) : y0_content_global , //y0 + 0, //f0 + new_border, //width + corners ? (height_border - 2) : height_content, //height + 1, //frames + &fill_color, //function + flags_total | IN_WINDOW | NOT_READABLE, //flags + &color //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + } + + /* 8: insert npb after */ + + if (npb_after) + { + id[0] = 1; //npb + id[1] = 2; //total + x0[0] = 0; + x0[1] = x0_npb; + y0[0] = 0; + y0[1] = y0_npb; + flags[0] = flags_npb | IN_WINDOW; + flags[1] = flags_total | IN_WINDOW; + + r = perform_action( + 2, //n + id, + x0, y0, f0, + width_npb, height_npb, 1, + ©_pixels, + flags, + &max //data + ); + if (r!=0) + { + *err = CONVERT_FAILED; + return r; + } + } + + /* 9: ok, done, now save */ + + r = save_picture(2, argv[optind+1], flags_total); + if (r!=0) + { + *err = SAVE_FAILED; + return r; + } + + return 0; } - +/* #define INPIX_MUSTARD 1 #define OUTPIX_MUSTARD 2 #define NPBPIX_MUSTARD 3 diff --git a/testimg/133x94.png b/testimg/133x94.png new file mode 100644 index 0000000000000000000000000000000000000000..32985cf884427eb7dee1c0b2d9af9d82cd686b8e GIT binary patch literal 1810 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 literal 0 HcmV?d00001 diff --git a/testimg/96x68.png b/testimg/96x68.png new file mode 100644 index 0000000000000000000000000000000000000000..72562bd2e08dbd71ba26e08a5a9783551a6fea33 GIT binary patch literal 527 zcmV+q0`UEbP)Z;$4n2XtwdpIlCeF9TvkYn;K^9t%PzfrezXyYaf`SO*U=2Xh!&$o zwDE9kXrs62E$%N|$FhKi7HH9}xYB_8IV-U?0N>|K#NV$j3V3*j{0AJ>0jEK84eC~0 zMF4EX=eTkJ6Y&aH3jhQ0J+2Vo;vKRJM`^%m(0YTq6;}lSDRB;00w5%=q~ixbHi0H6_B>8Jr< zjpd+bqywA=Q5w{(=|}+tq9Pp~0LesMIwC;!SPpSUbiip4w?W;Sjsk!*q9+|XAnCK5 z>2Lwa`7CcbOu%W7pg}+PLE6$G0Nkl#?YKrY;* zqYMByVia6;0QC+_3q`C8unTe(bfm(S25=xU;OYYCUdfZ1<;6Yb;x zXeQ#?KQC|rR7@rj$+I~bK&8o~qfaa-!%@3Kc6b0U1x-03 z&*mI}_k#S6KDVGqM-5mI1E5z>j3cle2Eau@Xh$J-!U3)eaylZFojKYCT{*gWBo$)^27oH)&k^L< zF2)W909eq^52=oTXRAXBvQtOo*?eFD;03)q+6BGZNpb|g!2ns%`6HsEpj#d=1zl~R zIEuUpLvgct=!iUCjbBd literal 0 HcmV?d00001 diff --git a/testimg/97x68.png b/testimg/97x68.png new file mode 100644 index 0000000000000000000000000000000000000000..1736103074921d0ec66a6ed2b103484c474a3d44 GIT binary patch literal 696 zcmV;p0!RIcP)^pSHoDzuybdrT6LD=|bbR(ZQ>o zIjnr0yhZB-)M2`IAmUa40(Nac2z|bG1k$aChn26s0K6dR(3k^}nzGqwatGiv(YJRb z(8P4qcTOXw3Nq5vk2~7s+tw5fFcviL$cAr2Qx3pD(7YouqzoR_n*fhdeRjlYVmj)! zrx8;H=>U#9vgcc*tO79W$cs-*Sqos;5xG(Zk7^Tuvm+6}b4T6#G-9eCJwPPiA!T8J z7RxuGtPKz_DFw5$Ho)1Dq@x~x8ZlMSGC<4Vd!o#F>otH>%1l8E04kU0do2BG_3$+P!4_ylm@td=?tEf@06th&W@xVVJcsBfLKcAFRr2hEsj$3mwP<` zz`)rlx-wA=01)8p=l}o^V9*h!@>K%33wrO!hjRH^b~eD1pkYVWl*)T+5(QoDFden< zNm&Nqk4^7RB((2yhOhGqIZnM+s-eYe7Mo|0u zj(9<$jxbe_CxA%Mb!MAW8Vm9P5DNlm)|4)StN~U8$vbM{quLE%QPB5wv@U3=BTN-! z0iY7J)DcJNE@%%xD`>T&Ic1O_Ex?8#MMopbP(kYedxCTwwZPA^ZGde-pWcy$AazHW zDo6%kBuL%Soia@j2Vf^?rz6vcuWbQL1?_cY_z=G#fVH5H>&QXSMn~p*9qa@62-0@s zv?at2fSVvyN3J_(=l}ry1gSc5-GM~`@OD}C>}V*cR7U|D)o29yYO>v$AW@K`re-I| eSraeFOVdARS6!V_*mwv40000