diff --git a/src/conio/GNUmakefile b/src/conio/GNUmakefile index 52aece4de9a9bdce7af0cdd265d27669b3c7b67a..04a86f256d5a317b0527b605756e5cbffb301081 100644 --- a/src/conio/GNUmakefile +++ b/src/conio/GNUmakefile @@ -15,7 +15,9 @@ ifdef NO_X CFLAGS += -DNO_X else OBJS += $(MTOBJODIR)$(DIRSEP)x_events$(OFILE) \ - $(MTOBJODIR)$(DIRSEP)x_cio$(OFILE) + $(MTOBJODIR)$(DIRSEP)x_cio$(OFILE) \ + $(MTOBJODIR)$(DIRSEP)scale$(OFILE) \ + $(MTOBJODIR)$(DIRSEP)xbr$(OFILE) NEED_BITMAP := 1 endif diff --git a/src/conio/hqx.c b/src/conio/hqx.c new file mode 100644 index 0000000000000000000000000000000000000000..2d7fd4db5144466d21d0ca7f703a78fe775f74c8 --- /dev/null +++ b/src/conio/hqx.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2014 Clément Bœsch + * + * This file is part of FFmpeg. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file + * hqx magnification filters (hq2x, hq3x, hq4x) + * + * Originally designed by Maxim Stephin. + * + * @see http://en.wikipedia.org/wiki/Hqx + * @see http://web.archive.org/web/20131114143602/http://www.hiend3d.com/hq3x.html + * @see http://blog.pkh.me/p/19-butchering-hqx-scaling-filters.html + */ + +#include <inttypes.h> + +#if 0 +/* (c1*w1 + c2*w2) >> s */ +inline __attribute__((always_inline)) uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s) +{ + return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2) << (8 - s)) & 0xff00ff00) | + (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2) >> s ) & 0x00ff00ff); +} + +/* (c1*w1 + c2*w2 + c3*w3) >> s */ +inline __attribute__((always_inline)) uint32_t interp_3px(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s) +{ + return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2 + ((c3 & 0xff00ff00) >> 8) * w3) << (8 - s)) & 0xff00ff00) | + (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2 + ((c3 & 0x00ff00ff) ) * w3) >> s ) & 0x00ff00ff); +} +#else +/* (c1*w1 + c2*w2) >> s */ +inline __attribute__((always_inline)) uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s) +{ + return w2 > w1 ? c2 : c1; +} + +/* (c1*w1 + c2*w2 + c3*w3) >> s */ +inline __attribute__((always_inline)) uint32_t interp_3px(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s) +{ + if (w3 > w2 && w3 > w1) + return c3; + if (w2 > w1) + return c2; + return c1; +} +#endif + +/* m is the mask of diff with the center pixel that matters in the pattern, and + * r is the expected result (bit set to 1 if there is difference with the + * center, 0 otherwise) */ +#define P(m, r) ((k_shuffled & (m)) == (r)) + +/* adjust 012345678 to 01235678: the mask doesn't contain the (null) diff + * between the center/current pixel and itself */ +#define DROP4(z) ((z) > 4 ? (z)-1 : (z)) + +/* shuffle the input mask: move bit n (4-adjusted) to position stored in p<n> */ +#define SHF(x, rot, n) (((x) >> ((rot) ? 7-DROP4(n) : DROP4(n)) & 1) << DROP4(p##n)) + +/* used to check if there is YUV difference between 2 pixels */ +#define WDIFF(c1, c2) (c1 != c2) + +/* bootstrap template for every interpolation code. It defines the shuffled + * masks and surrounding pixels. The rot flag is used to indicate if it's a + * rotation; its basic effect is to shuffle k using p8..p0 instead of p0..p8 */ +#define INTERP_BOOTSTRAP(rot) \ + const int k_shuffled = SHF(k,rot,0) | SHF(k,rot,1) | SHF(k,rot,2) \ + | SHF(k,rot,3) | 0 | SHF(k,rot,5) \ + | SHF(k,rot,6) | SHF(k,rot,7) | SHF(k,rot,8); \ + \ + const uint32_t w0 = w[p0], w1 = w[p1], \ + w3 = w[p3], w4 = w[p4], w5 = w[p5], \ + w7 = w[p7] + +/* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the + * top-left pixel in the total of the 2x2 pixels to interpolates. The function + * is also used for the 3 other pixels */ +inline __attribute__((always_inline)) uint32_t hq2x_interp_1x1(int k, + const uint32_t *w, + int p0, int p1, int p2, + int p3, int p4, int p5, + int p6, int p7, int p8) +{ + INTERP_BOOTSTRAP(0); + + if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5)) + return interp_2px(w4, 3, w3, 1, 2); + if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3)) + return interp_2px(w4, 3, w1, 1, 2); + if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1)) + return w4; + if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) || + P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) || + P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) || + P(0xeb,0x8a)) && WDIFF(w3, w1)) + return interp_2px(w4, 3, w0, 1, 2); + if (P(0x0b,0x08)) + return interp_3px(w4, 2, w0, 1, w1, 1, 2); + if (P(0x0b,0x02)) + return interp_3px(w4, 2, w0, 1, w3, 1, 2); + if (P(0x2f,0x2f)) + return interp_3px(w4, 14, w3, 1, w1, 1, 4); + if (P(0xbf,0x37) || P(0xdb,0x13)) + return interp_3px(w4, 5, w1, 2, w3, 1, 3); + if (P(0xdb,0x49) || P(0xef,0x6d)) + return interp_3px(w4, 5, w3, 2, w1, 1, 3); + if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43)) + return interp_2px(w4, 3, w3, 1, 2); + if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19)) + return interp_2px(w4, 3, w1, 1, 2); + if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e)) + return interp_3px(w4, 2, w3, 3, w1, 3, 3); + if (P(0xfb,0x6a) || P(0x6f,0x6e) || P(0x3f,0x3e) || P(0xfb,0xfa) || + P(0xdf,0xde) || P(0xdf,0x1e)) + return interp_2px(w4, 3, w0, 1, 2); + if (P(0x0a,0x00) || P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || + P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || + P(0x3b,0x1b)) + return interp_3px(w4, 2, w3, 1, w1, 1, 2); + return interp_3px(w4, 6, w3, 1, w1, 1, 3); +} + +/* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the + * top-left and top-center pixel in the total of the 3x3 pixels to + * interpolates. The function is also used for the 3 other couples of pixels + * defining the outline. The center pixel is not defined through this function, + * since it's just the same as the original value. */ +inline __attribute__((always_inline)) void hq3x_interp_2x1(uint32_t *dst, int dst_linesize, + int k, + const uint32_t *w, + int pos00, int pos01, + int p0, int p1, int p2, + int p3, int p4, int p5, + int p6, int p7, int p8, + int rotate) +{ + INTERP_BOOTSTRAP(rotate); + + uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)]; + uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)]; + + if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3)) + *dst00 = interp_2px(w4, 3, w1, 1, 2); + else if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5)) + *dst00 = interp_2px(w4, 3, w3, 1, 2); + else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1)) + *dst00 = w4; + else if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) || + P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) || + P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) || + P(0xeb,0x8a)) && WDIFF(w3, w1)) + *dst00 = interp_2px(w4, 3, w0, 1, 2); + else if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19)) + *dst00 = interp_2px(w4, 3, w1, 1, 2); + else if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43)) + *dst00 = interp_2px(w4, 3, w3, 1, 2); + else if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e)) + *dst00 = interp_2px(w3, 1, w1, 1, 1); + else if (P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || P(0xbe,0x0a) || + P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || P(0x3b,0x1b)) + *dst00 = interp_3px(w4, 2, w3, 7, w1, 7, 4); + else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || P(0x6d,0x6c) || + P(0x67,0x66) || P(0x3d,0x3c) || P(0x37,0x36) || P(0xf9,0xf8) || + P(0xdd,0xdc) || P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) || + P(0xd7,0x16) || P(0x0b,0x02)) + *dst00 = interp_2px(w4, 3, w0, 1, 2); + else + *dst00 = interp_3px(w4, 2, w3, 1, w1, 1, 2); + + if ((P(0xfe,0xde) || P(0x9e,0x16) || P(0xda,0x12) || P(0x17,0x16) || + P(0x5b,0x12) || P(0xbb,0x12)) && WDIFF(w1, w5)) + *dst01 = w4; + else if ((P(0x0f,0x0b) || P(0x5e,0x0a) || P(0xfb,0x7b) || P(0x3b,0x0b) || + P(0xbe,0x0a) || P(0x7a,0x0a)) && WDIFF(w3, w1)) + *dst01 = w4; + else if (P(0xbf,0x8f) || P(0x7e,0x0e) || P(0xbf,0x37) || P(0xdb,0x13)) + *dst01 = interp_2px(w1, 3, w4, 1, 2); + else if (P(0x02,0x00) || P(0x7c,0x28) || P(0xed,0xa9) || P(0xf5,0xb4) || + P(0xd9,0x90)) + *dst01 = interp_2px(w4, 3, w1, 1, 2); + else if (P(0x4f,0x4b) || P(0xfb,0x7b) || P(0xfe,0x7e) || P(0x9f,0x1b) || + P(0x2f,0x0b) || P(0xbe,0x0a) || P(0x7e,0x0a) || P(0xfb,0x4b) || + P(0xfb,0xdb) || P(0xfe,0xde) || P(0xfe,0x56) || P(0x57,0x56) || + P(0x97,0x16) || P(0x3f,0x1e) || P(0xdb,0x12) || P(0xbb,0x12)) + *dst01 = interp_2px(w4, 7, w1, 1, 3); + else + *dst01 = w4; +} + +/* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the + * top-left block of 2x2 pixels in the total of the 4x4 pixels (or 4 blocks) to + * interpolates. The function is also used for the 3 other blocks of 2x2 + * pixels. */ +inline __attribute__((always_inline)) void hq4x_interp_2x2(uint32_t *dst, int dst_linesize, + int k, + const uint32_t *w, + int pos00, int pos01, + int pos10, int pos11, + int p0, int p1, int p2, + int p3, int p4, int p5, + int p6, int p7, int p8) +{ + INTERP_BOOTSTRAP(0); + + uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)]; + uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)]; + uint32_t *dst10 = &dst[dst_linesize*(pos10>>1) + (pos10&1)]; + uint32_t *dst11 = &dst[dst_linesize*(pos11>>1) + (pos11&1)]; + + const int cond00 = (P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5); + const int cond01 = (P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3); + const int cond02 = (P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || + P(0xdf,0x5a) || P(0x9f,0x8a) || P(0xcf,0x8a) || + P(0xef,0x4e) || P(0x3f,0x0e) || P(0xfb,0x5a) || + P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) || + P(0xeb,0x8a)) && WDIFF(w3, w1); + const int cond03 = P(0xdb,0x49) || P(0xef,0x6d); + const int cond04 = P(0xbf,0x37) || P(0xdb,0x13); + const int cond05 = P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || + P(0x6b,0x43); + const int cond06 = P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || + P(0x3b,0x19); + const int cond07 = P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || + P(0x6d,0x6c) || P(0x67,0x66) || P(0x3d,0x3c) || + P(0x37,0x36) || P(0xf9,0xf8) || P(0xdd,0xdc) || + P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) || + P(0xd7,0x16) || P(0x0b,0x02); + const int cond08 = (P(0x0f,0x0b) || P(0x2b,0x0b) || P(0xfe,0x4a) || + P(0xfe,0x1a)) && WDIFF(w3, w1); + const int cond09 = P(0x2f,0x2f); + const int cond10 = P(0x0a,0x00); + const int cond11 = P(0x0b,0x09); + const int cond12 = P(0x7e,0x2a) || P(0xef,0xab); + const int cond13 = P(0xbf,0x8f) || P(0x7e,0x0e); + const int cond14 = P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || + P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || + P(0xeb,0x4b) || P(0x3b,0x1b); + const int cond15 = P(0x0b,0x03); + + if (cond00) + *dst00 = interp_2px(w4, 5, w3, 3, 3); + else if (cond01) + *dst00 = interp_2px(w4, 5, w1, 3, 3); + else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1)) + *dst00 = w4; + else if (cond02) + *dst00 = interp_2px(w4, 5, w0, 3, 3); + else if (cond03) + *dst00 = interp_2px(w4, 3, w3, 1, 2); + else if (cond04) + *dst00 = interp_2px(w4, 3, w1, 1, 2); + else if (cond05) + *dst00 = interp_2px(w4, 5, w3, 3, 3); + else if (cond06) + *dst00 = interp_2px(w4, 5, w1, 3, 3); + else if (P(0x0f,0x0b) || P(0x5e,0x0a) || P(0x2b,0x0b) || P(0xbe,0x0a) || + P(0x7a,0x0a) || P(0xee,0x0a)) + *dst00 = interp_2px(w1, 1, w3, 1, 1); + else if (cond07) + *dst00 = interp_2px(w4, 5, w0, 3, 3); + else + *dst00 = interp_3px(w4, 2, w1, 1, w3, 1, 2); + + if (cond00) + *dst01 = interp_2px(w4, 7, w3, 1, 3); + else if (cond08) + *dst01 = w4; + else if (cond02) + *dst01 = interp_2px(w4, 3, w0, 1, 2); + else if (cond09) + *dst01 = w4; + else if (cond10) + *dst01 = interp_3px(w4, 5, w1, 2, w3, 1, 3); + else if (P(0x0b,0x08)) + *dst01 = interp_3px(w4, 5, w1, 2, w0, 1, 3); + else if (cond11) + *dst01 = interp_2px(w4, 5, w1, 3, 3); + else if (cond04) + *dst01 = interp_2px(w1, 3, w4, 1, 2); + else if (cond12) + *dst01 = interp_3px(w1, 2, w4, 1, w3, 1, 2); + else if (cond13) + *dst01 = interp_2px(w1, 5, w3, 3, 3); + else if (cond05) + *dst01 = interp_2px(w4, 7, w3, 1, 3); + else if (P(0xf3,0x62) || P(0x67,0x66) || P(0x37,0x36) || P(0xf3,0xf2) || + P(0xd7,0xd6) || P(0xd7,0x16) || P(0x0b,0x02)) + *dst01 = interp_2px(w4, 3, w0, 1, 2); + else if (cond14) + *dst01 = interp_2px(w1, 1, w4, 1, 1); + else + *dst01 = interp_2px(w4, 3, w1, 1, 2); + + if (cond01) + *dst10 = interp_2px(w4, 7, w1, 1, 3); + else if (cond08) + *dst10 = w4; + else if (cond02) + *dst10 = interp_2px(w4, 3, w0, 1, 2); + else if (cond09) + *dst10 = w4; + else if (cond10) + *dst10 = interp_3px(w4, 5, w3, 2, w1, 1, 3); + else if (P(0x0b,0x02)) + *dst10 = interp_3px(w4, 5, w3, 2, w0, 1, 3); + else if (cond15) + *dst10 = interp_2px(w4, 5, w3, 3, 3); + else if (cond03) + *dst10 = interp_2px(w3, 3, w4, 1, 2); + else if (cond13) + *dst10 = interp_3px(w3, 2, w4, 1, w1, 1, 2); + else if (cond12) + *dst10 = interp_2px(w3, 5, w1, 3, 3); + else if (cond06) + *dst10 = interp_2px(w4, 7, w1, 1, 3); + else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0x6d,0x6c) || P(0x3d,0x3c) || + P(0xf9,0xf8) || P(0xdd,0xdc) || P(0xdd,0x1c)) + *dst10 = interp_2px(w4, 3, w0, 1, 2); + else if (cond14) + *dst10 = interp_2px(w3, 1, w4, 1, 1); + else + *dst10 = interp_2px(w4, 3, w3, 1, 2); + + if ((P(0x7f,0x2b) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7f,0x0f)) && + WDIFF(w3, w1)) + *dst11 = w4; + else if (cond02) + *dst11 = interp_2px(w4, 7, w0, 1, 3); + else if (cond15) + *dst11 = interp_2px(w4, 7, w3, 1, 3); + else if (cond11) + *dst11 = interp_2px(w4, 7, w1, 1, 3); + else if (P(0x0a,0x00) || P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || + P(0x7e,0x0e)) + *dst11 = interp_3px(w4, 6, w3, 1, w1, 1, 3); + else if (cond07) + *dst11 = interp_2px(w4, 7, w0, 1, 3); + else + *dst11 = w4; +} + +void hqx_filter(uint32_t* src, uint32_t* dst, int width, int height, int n) +{ + int x, y; + const int dst32_linesize = width * n; + const int src32_linesize = width; + uint32_t *src32, *dst32; + + src32 = src; + dst32 = dst; + for (y = 0; y < height; y++) { + const int prevline = y > 0 ? -src32_linesize : 0; + const int nextline = y < height - 1 ? src32_linesize : 0; + + for (x = 0; x < width; x++) { + const int prevcol = x > 0 ? -1 : 0; + const int nextcol = x < width -1 ? 1 : 0; + const uint32_t w[3*3] = { + src32[prevcol + prevline], src32[prevline], src32[prevline + nextcol], + src32[prevcol ], src32[ 0], src32[ nextcol], + src32[prevcol + nextline], src32[nextline], src32[nextline + nextcol] + }; + const uint32_t yuv1 = w[4]; + const int pattern = (w[4] != w[0] ? 1 : 0) + | (w[4] != w[1] ? 1 : 0) << 1 + | (w[4] != w[2] ? 1 : 0) << 2 + | (w[4] != w[3] ? 1 : 0) << 3 + | (w[4] != w[5] ? 1 : 0) << 4 + | (w[4] != w[6] ? 1 : 0) << 5 + | (w[4] != w[7] ? 1 : 0) << 6 + | (w[4] != w[8] ? 1 : 0) << 7; + + if (n == 2) { + dst32[dst32_linesize*0 + 0] = hq2x_interp_1x1(pattern, w, 0,1,2,3,4,5,6,7,8); // 00 + dst32[dst32_linesize*0 + 1] = hq2x_interp_1x1(pattern, w, 2,1,0,5,4,3,8,7,6); // 01 (vert mirrored) + dst32[dst32_linesize*1 + 0] = hq2x_interp_1x1(pattern, w, 6,7,8,3,4,5,0,1,2); // 10 (horiz mirrored) + dst32[dst32_linesize*1 + 1] = hq2x_interp_1x1(pattern, w, 8,7,6,5,4,3,2,1,0); // 11 (center mirrored) + } else if (n == 3) { + hq3x_interp_2x1(dst32, dst32_linesize, pattern, w, 0,1, 0,1,2,3,4,5,6,7,8, 0); // 00 01 + hq3x_interp_2x1(dst32 + 1, dst32_linesize, pattern, w, 1,3, 2,5,8,1,4,7,0,3,6, 1); // 02 12 (rotated to the right) + hq3x_interp_2x1(dst32 + 1*dst32_linesize, dst32_linesize, pattern, w, 2,0, 6,3,0,7,4,1,8,5,2, 1); // 20 10 (rotated to the left) + hq3x_interp_2x1(dst32 + 1*dst32_linesize + 1, dst32_linesize, pattern, w, 3,2, 8,7,6,5,4,3,2,1,0, 0); // 22 21 (center mirrored) + dst32[dst32_linesize + 1] = w[4]; // 11 + } else if (n == 4) { + hq4x_interp_2x2(dst32, dst32_linesize, pattern, w, 0,1,2,3, 0,1,2,3,4,5,6,7,8); // 00 01 10 11 + hq4x_interp_2x2(dst32 + 2, dst32_linesize, pattern, w, 1,0,3,2, 2,1,0,5,4,3,8,7,6); // 02 03 12 13 (vert mirrored) + hq4x_interp_2x2(dst32 + 2*dst32_linesize, dst32_linesize, pattern, w, 2,3,0,1, 6,7,8,3,4,5,0,1,2); // 20 21 30 31 (horiz mirrored) + hq4x_interp_2x2(dst32 + 2*dst32_linesize + 2, dst32_linesize, pattern, w, 3,2,1,0, 8,7,6,5,4,3,2,1,0); // 22 23 32 33 (center mirrored) + } else { + return; + } + + src32 += 1; + dst32 += n; + } + dst32 += dst32_linesize * (n - 1); + } +} diff --git a/src/conio/hqx.h b/src/conio/hqx.h new file mode 100644 index 0000000000000000000000000000000000000000..c9f347e05ca6090d1f32a6fd091fe005ef71fec2 --- /dev/null +++ b/src/conio/hqx.h @@ -0,0 +1,11 @@ +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void hqx_filter(uint32_t* src, uint32_t* dst, int height, int width, int n); + +#ifdef __cplusplus +} +#endif diff --git a/src/conio/objects.mk b/src/conio/objects.mk index 0ce6f3c87b4fde32c10a5033ce186a3618d7d619..b5a513acac5a49d9bc5182c551a263db2de1eeb1 100644 --- a/src/conio/objects.mk +++ b/src/conio/objects.mk @@ -4,5 +4,6 @@ OBJS = $(MTOBJODIR)$(DIRSEP)ansi_cio$(OFILE)\ $(MTOBJODIR)$(DIRSEP)vidmodes$(OFILE)\ $(MTOBJODIR)$(DIRSEP)allfonts$(OFILE)\ $(MTOBJODIR)$(DIRSEP)mouse$(OFILE)\ + $(MTOBJODIR)$(DIRSEP)scale$(OFILE)\ $(MTOBJODIR)$(DIRSEP)syncicon64$(OFILE)\ $(MTOBJODIR)$(DIRSEP)utf8_codepages$(OFILE) diff --git a/src/conio/scale.c b/src/conio/scale.c new file mode 100644 index 0000000000000000000000000000000000000000..ff9fc37c5870bc989f460aae82b49543c867fc4a --- /dev/null +++ b/src/conio/scale.c @@ -0,0 +1,341 @@ +#include "scale.h" +#include "xbr.h" + +static void pointy_scale3(uint32_t* src, uint32_t* dest, int width, int height); +static void pointy_scale5(uint32_t* src, uint32_t* dest, int width, int height); + +uint32_t* +do_scale(struct rectlist* rect, uint32_t **target, size_t *targetsz, int* xscale, int* yscale, int* w, int* h) +{ + int pointy5 = 0; + int pointy3 = 0; + int xbr2 = 0; + int xbr4 = 0; + int newscale = 1; + int total_scaling = 1; + static uint32_t *tmptarget = NULL; + static size_t tmptargetsz = 0; + uint32_t *ctarget; + uint32_t *csrc = rect->data; + uint32_t* nt; + int swidth = rect->rect.width; + int sheight = rect->rect.height; + + *w = rect->rect.width; + *h = rect->rect.height; + // As a first pass, only do equal integer scaling + if (*xscale != *yscale) + return rect->data; + switch (*xscale) { + case 1: + return rect->data; + case 2: + xbr2 = 1; + break; + case 3: + pointy3 = 1; + break; + case 4: + xbr4 = 1; + break; + case 5: + pointy5 = 1; + break; + case 6: + pointy3 = 1; + xbr2 = 1; + break; + default: + total_scaling = *xscale; + *xscale = 1; + while (total_scaling > 1 && ((total_scaling % 5) == 0)) { + pointy5++; + total_scaling /= 5; + *xscale *= 5; + } + while (total_scaling > 1 && ((total_scaling % 3) == 0)) { + pointy3++; + total_scaling /= 3; + *xscale *= 3; + } + while (total_scaling > 1 && ((total_scaling % 4) == 0)) { + xbr4++; + total_scaling /= 4; + *xscale *= 4; + } + while (total_scaling > 1 && ((total_scaling % 2) == 0)) { + xbr2++; + total_scaling /= 2; + *xscale *= 2; + } + if (*xscale == 1) + return rect->data; + break; + } + + // Now make sure target is big enough... + size_t needsz = rect->rect.width * rect->rect.height * (*xscale) * (*xscale) * sizeof(uint32_t); + if (needsz > *targetsz) { + nt = realloc(*target, needsz); + if (nt == NULL) + return rect->data; + *target = nt; + *targetsz = needsz; + } + ctarget = *target; + + // And if we need an extra target, do the same there... + if (pointy3 + pointy5 + xbr4 + xbr2 > 1) { + if (needsz > tmptargetsz) { + nt = realloc(tmptarget, needsz); + if (nt == NULL) + return rect->data; + tmptarget = nt; + tmptargetsz = needsz; + } + } + + // And finally, scale... + while (xbr4 > 0) { + xbr_filter(csrc, ctarget, swidth, sheight, 4); + xbr4--; + swidth *= 4; + sheight *= 4; + csrc = ctarget; + if (ctarget == tmptarget) + ctarget = *target; + else + ctarget = tmptarget; + } + while (xbr2 > 0) { + xbr_filter(csrc, ctarget, swidth, sheight, 2); + xbr2--; + swidth *= 2; + sheight *= 2; + csrc = ctarget; + if (ctarget == tmptarget) + ctarget = *target; + else + ctarget = tmptarget; + } + while (pointy5 > 0) { + pointy_scale5(csrc, ctarget, swidth, sheight); + pointy5--; + swidth *= 5; + sheight *= 5; + csrc = ctarget; + if (ctarget == tmptarget) + ctarget = *target; + else + ctarget = tmptarget; + } + while (pointy3 > 0) { + pointy_scale3(csrc, ctarget, swidth, sheight); + pointy3--; + swidth *= 3; + sheight *= 3; + csrc = ctarget; + if (ctarget == tmptarget) + ctarget = *target; + else + ctarget = tmptarget; + } + *w *= *xscale; + *h *= *xscale; + *xscale = newscale; + *yscale = newscale; + return csrc; +} + +static void +pointy_scale5(uint32_t* src, uint32_t* dest, int width, int height) +{ + int x, y; + uint32_t* s; + uint32_t* d; + int w5 = width * 5; + int w10 = width * 10; + int w15 = width * 15; + int w20 = width * 20; + int prevline, prevcol, nextline, nextcol; + + s = src; + d = dest; + prevline = 0; + nextline = width; + for (y = 0; y < height; y++) { + if (y == height - 1) + nextline = 0; + prevcol = 0; + nextcol = 1; + for (x = 0; x < width; x++) { + if (x == width - 1) + nextcol = 0; + + if (s[prevline + prevcol] == s[0]) { + d[0] = s[0]; + d[1] = s[0]; + d[w5] = s[0]; + } + else if (s[prevcol] == s[prevline]) { + d[0] = s[prevcol]; + d[1] = s[prevcol]; + d[w5] = s[prevcol]; + } + else { + d[0] = *s; + d[1] = *s; + d[w5] = *s; + } + + // Top-middle stays OG. + d[2] = *s; + d[w5+1] = *s; + d[w5+2] = *s; + d[w5+3] = *s; + + // And so on around the outside (round the outside) + if (s[prevline + nextcol] == s[0]) { + d[3] = s[0]; + d[4] = s[0]; + d[w5 + 4] = s[0]; + } + else if (s[nextcol] == s[prevline]) { + d[3] = s[nextcol]; + d[4] = s[nextcol]; + d[w5 + 4] = s[nextcol]; + } + else { + d[3] = s[0]; + d[4] = s[0]; + d[w5 + 4] = s[0]; + } + + d[w10] = *s; + d[w10+1] = *s; + d[w10+2] = *s; + d[w10+3] = *s; + d[w10+4] = *s; + + if (s[prevcol + nextline] == s[0]) { + d[w15] = s[0]; + d[w20] = s[0]; + d[w20 + 1] = s[0]; + } + else if(s[prevcol] == s[nextline]) { + d[w15] = s[prevcol]; + d[w20] = s[prevcol]; + d[w20 + 1] = s[prevcol]; + } + else { + d[w15] = s[0]; + d[w20] = s[0]; + d[w20 + 1] = s[0]; + } + + d[w15 + 1] = *s; + d[w15 + 2] = *s; + d[w15 + 3] = *s; + d[w20 + 2] = *s; + + if (s[nextcol + nextline] == s[0]) { + d[w15 + 4] = s[0]; + d[w20 + 3] = s[0]; + d[w20 + 4] = s[0]; + } + else if (s[nextcol] == s[nextline]) { + d[w15 + 4] = s[nextcol]; + d[w20 + 3] = s[nextcol]; + d[w20 + 4] = s[nextcol]; + } + else { + d[w15 + 4] = s[0]; + d[w20 + 3] = s[0]; + d[w20 + 4] = s[0]; + } + + d += 5; + s++; + + if (x == 0) + prevcol = -1; + } + d += w20; + if (y == 0) + prevline = -width; + } +} + +static void +pointy_scale3(uint32_t* src, uint32_t* dest, int width, int height) +{ + int x, y; + uint32_t* s; + uint32_t* d; + int w3 = width * 3; + int w6 = width * 6; + int prevline, prevcol, nextline, nextcol; + + s = src; + d = dest; + prevline = 0; + nextline = width; + for (y = 0; y < height; y++) { + if (y == height - 1) + nextline = 0; + prevcol = 0; + nextcol = 1; + for (x = 0; x < width; x++) { + if (x == width - 1) + nextcol = 0; + + // Top-left is filled if both left and top are the same. + if (s[prevline + prevcol] == s[0]) + d[0] = s[0]; + else if (s[prevcol] == s[prevline]) + d[0] = s[prevcol]; + else + d[0] = *s; + + // Top-middle stays OG. + d[1] = *s; + + // And so on around the outside (round the outside) + if (s[prevline + nextcol] == s[0]) + d[2] = s[0]; + else if (s[nextcol] == s[prevline]) + d[2] = s[nextcol]; + else + d[2] = *s; + + d[w3] = *s; + d[w3 + 1] = *s; + d[w3 + 2] = *s; + + if (s[prevcol + nextline] == s[0]) + d[w6] = s[0]; + else if(s[prevcol] == s[nextline]) + d[w6] = s[prevcol]; + else + d[w6] = *s; + + d[w6 + 1] = *s; + + if (s[nextcol + nextline] == s[0]) + d[w6 + 2] = s[0]; + else if (s[nextcol] == s[nextline]) + d[w6 + 2] = s[nextcol]; + else + d[w6 + 2] = s[0]; + + d += 3; + s++; + + if (x == 0) + prevcol = -1; + } + d += w6; + if (y == 0) + prevline = -width; + } +} diff --git a/src/conio/scale.h b/src/conio/scale.h new file mode 100644 index 0000000000000000000000000000000000000000..b867d034b669190cb182f1eecbe6ff0de3252b71 --- /dev/null +++ b/src/conio/scale.h @@ -0,0 +1,3 @@ +#include "bitmap_con.h" + +uint32_t* do_scale(struct rectlist* rect, uint32_t **target, size_t *targetsz, int* xscale, int* yscale, int* w, int* h); diff --git a/src/conio/x_events.c b/src/conio/x_events.c index ace6613adcd39e1aed2f09a95de5f930c2668647..e3014eedf551fe6ed7e1ff041fb1373fb3bfe17c 100644 --- a/src/conio/x_events.c +++ b/src/conio/x_events.c @@ -27,6 +27,7 @@ #define BITMAP_CIOLIB_DRIVER #include "bitmap_con.h" #include "link_list.h" +#include "scale.h" #include "x_events.h" #include "x_cio.h" #include "utf8_codepages.h" @@ -449,16 +450,25 @@ static int video_init() static void local_draw_rect(struct rectlist *rect) { int x,y,xscale,yscale,xoff=0,yoff=0; + int xscaling, yscaling; + int rxscaling, ryscaling; unsigned int r, g, b; unsigned long pixel; - int cleft = rect->rect.width; - int cright = -1; - int ctop = rect->rect.height; + int cleft; + int cright = -100; + int ctop; int cbottom = -1; int idx; + int ridx; + int ridx_part; uint32_t last_pixel = 0x55555555; + static uint32_t* target = NULL; + size_t targetsz = 0; + uint32_t* source; + int width, height; + int lheight, lines; - if (bitmap_width != cleft || bitmap_height != ctop) + if (bitmap_width != rect->rect.width || bitmap_height != rect->rect.height) return; xoff = (x11_window_width - xim->width) / 2; @@ -468,12 +478,26 @@ static void local_draw_rect(struct rectlist *rect) if (yoff < 0) yoff = 0; + // Scale... + xscaling = x_cvstat.scaling; + yscaling = x_cvstat.scaling*x_cvstat.vmultiplier; + source = do_scale(rect, &target, &targetsz, &xscaling, &yscaling, &width, &height); + rxscaling = width / rect->rect.width; + ryscaling = height / rect->rect.height; + cleft = width; + ctop = height; + lines = 0; + lheight = x_cvstat.charheight * ryscaling; + /* TODO: Translate into local colour depth */ - for(y=0;y<rect->rect.height;y++) { - idx = y*rect->rect.width; - for(x=0; x<rect->rect.width; x++) { + for(y=0;y<height;y++) { + idx = y * width; + ridx = y / ryscaling * rect->rect.width; + ridx_part = 0; + for(x=0; x<width; x++) { if (last) { - if (last->data[idx] != rect->data[idx]) { + // TODO: Based on source pixel, not target pixel. :( + if (last->data[ridx] != rect->data[ridx]) { if (x < cleft) cleft = x; if (x > cright) @@ -484,15 +508,29 @@ static void local_draw_rect(struct rectlist *rect) cbottom = y; } else { - idx++; - continue; + if (source == rect->data) { + idx += (rxscaling); + x += (rxscaling - 1); + ridx++; + continue; + } + else { + if (cright < x - rxscaling * 2 + && (x >= width - (rxscaling * 3) + || last->data[ridx + 1] == rect->data[ridx + 1])) { + idx += (rxscaling); + x += (rxscaling - 1); + ridx++; + continue; + } + } } } - if (last_pixel != rect->data[idx]) { - last_pixel = rect->data[idx]; - r = rect->data[idx] >> 16 & 0xff; - g = rect->data[idx] >> 8 & 0xff; - b = rect->data[idx] & 0xff; + if (last_pixel != source[idx]) { + last_pixel = source[idx]; + r = source[idx] >> 16 & 0xff; + g = source[idx] >> 8 & 0xff; + b = source[idx] & 0xff; r = (r<<8)|r; g = (g<<8)|g; b = (b<<8)|b; @@ -510,28 +548,49 @@ static void local_draw_rect(struct rectlist *rect) else pixel |= (b >> (0-b_shift)) & visual.blue_mask; } - for(yscale=0; yscale<x_cvstat.scaling*x_cvstat.vmultiplier; yscale++) { - for(xscale=0; xscale<x_cvstat.scaling; xscale++) { + for(yscale=0; yscale<yscaling; yscale++) { + for(xscale=0; xscale<xscaling; xscale++) { #ifdef XPutPixel - XPutPixel(xim,(x+rect->rect.x)*x_cvstat.scaling+xscale,(y+rect->rect.y)*x_cvstat.scaling*x_cvstat.vmultiplier+yscale,pixel); + XPutPixel(xim, (x + rect->rect.x) * xscaling + xscale + , (y + rect->rect.y) * yscaling + yscale, pixel); #else - x11.XPutPixel(xim,(x+rect->rect.x)*x_cvstat.scaling+xscale,(y+rect->rect.y)*x_cvstat.scaling*x_cvstat.vmultiplier+yscale,pixel); + x11.XPutPixel(xim, (x + rect->rect.x) * xscaling + xscale + , (y + rect->rect.y) * yscaling + yscale, pixel); #endif } } idx++; + ridx_part++; + if (ridx_part >= rxscaling) { + ridx_part = 0; + ridx++; + } } + lines++; /* This line was changed */ - if (last && (((y & 0x1f) == 0x1f) || (y == rect->rect.height-1)) && cright >= 0) { - x11.XPutImage(dpy, win, gc, xim, cleft*x_cvstat.scaling, ctop*x_cvstat.scaling*x_cvstat.vmultiplier, cleft*x_cvstat.scaling + xoff, ctop*x_cvstat.scaling*x_cvstat.vmultiplier + yoff, (cright-cleft+1)*x_cvstat.scaling, (cbottom-ctop+1)*x_cvstat.scaling*x_cvstat.vmultiplier); - cleft = rect->rect.width; - cright = cbottom = -1; - ctop = rect->rect.height; + if (last && ((lines == lheight) || (y == height - 1)) && cright >= 0) { +#ifdef USE_XBRZ + if (source != rect->data) { + cleft -= 2 * rxscaling; + if (cleft < 0) + cleft = 0; + cright += 2 * rxscaling; + if (cright >= width) + cright = width - 1; + } +#endif + lines = 0; + x11.XPutImage(dpy, win, gc, xim, cleft * xscaling, ctop * yscaling + , cleft * xscaling + xoff, ctop * yscaling + yoff + , (cright - cleft + 1) * xscaling, (cbottom - ctop + 1) * yscaling); + cleft = width; + cright = cbottom = -100; + ctop = height; } } if (last == NULL) - x11.XPutImage(dpy, win, gc, xim, rect->rect.x*x_cvstat.scaling, rect->rect.y*x_cvstat.scaling*x_cvstat.vmultiplier, rect->rect.x*x_cvstat.scaling + xoff, rect->rect.y*x_cvstat.scaling*x_cvstat.vmultiplier + yoff, rect->rect.width*x_cvstat.scaling, rect->rect.height*x_cvstat.scaling*x_cvstat.vmultiplier); + x11.XPutImage(dpy, win, gc, xim, rect->rect.x*xscaling, rect->rect.y*yscaling, rect->rect.x*xscaling + xoff, rect->rect.y*yscaling + yoff, width * xscaling, height * yscaling); else bitmap_drv_free_rect(last); last = rect; @@ -644,11 +703,14 @@ static int x11_event(XEvent *ev) break; /* Graphics related events */ case ConfigureNotify: - x11_window_xpos=ev->xconfigure.x; - x11_window_ypos=ev->xconfigure.y; - x11_window_width=ev->xconfigure.width; - x11_window_height=ev->xconfigure.height; - handle_resize_event(ev->xconfigure.width, ev->xconfigure.height); + if (x11_window_xpos != ev->xconfigure.x || x11_window_ypos != ev->xconfigure.y + || x11_window_width != ev->xconfigure.width || x11_window_height != ev->xconfigure.height) { + x11_window_xpos=ev->xconfigure.x; + x11_window_ypos=ev->xconfigure.y; + x11_window_width=ev->xconfigure.width; + x11_window_height=ev->xconfigure.height; + handle_resize_event(ev->xconfigure.width, ev->xconfigure.height); + } break; case NoExpose: break; diff --git a/src/conio/xbr.c b/src/conio/xbr.c new file mode 100644 index 0000000000000000000000000000000000000000..64211daea20f668454644700cf2602f1e5ff1fde --- /dev/null +++ b/src/conio/xbr.c @@ -0,0 +1,293 @@ +/* + * This file is part of FFmpeg. + * + * Copyright (c) 2011, 2012 Hyllian/Jararaca <sergiogdb@gmail.com> + * Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com> + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * XBR Filter is used for depixelization of image. + * This is based on Hyllian's xBR shader. + * + * @see https://forums.libretro.com/t/xbr-algorithm-tutorial/123 + * @see https://github.com/yoyofr/iFBA/blob/master/fba_src/src/intf/video/scalers/xbr.cpp + */ + +#include <inttypes.h> +#include <stdlib.h> + +#define LB_MASK 0x00FEFEFE +#define RED_BLUE_MASK 0x00FF00FF +#define GREEN_MASK 0x0000FF00 + +#ifdef PI +#undef PI +#endif + +uint32_t r2y[1<<24]; + +static uint32_t pixel_diff(uint32_t x, uint32_t y) +{ +#define YMASK 0xff0000 +#define UMASK 0x00ff00 +#define VMASK 0x0000ff +#define ABSDIFF(a,b) (abs((int)(a)-(int)(b))) + + uint32_t yuv1 = r2y[x & 0xffffff]; + + uint32_t yuv2 = r2y[y & 0xffffff]; + + return (ABSDIFF(yuv1 & YMASK, yuv2 & YMASK) >> 16) + + (ABSDIFF(yuv1 & UMASK, yuv2 & UMASK) >> 8) + + ABSDIFF(yuv1 & VMASK, yuv2 & VMASK); +} + +#define ALPHA_BLEND_128_W(a, b) ((((a) & LB_MASK) >> 1) + (((b) & LB_MASK) >> 1)) +#define ALPHA_BLEND_BASE(a, b, m, s) ( (RED_BLUE_MASK & (((a) & RED_BLUE_MASK) + (((((b) & RED_BLUE_MASK) - ((a) & RED_BLUE_MASK)) * (m)) >> (s)))) \ + | (GREEN_MASK & (((a) & GREEN_MASK) + (((((b) & GREEN_MASK) - ((a) & GREEN_MASK)) * (m)) >> (s))))) +#define ALPHA_BLEND_32_W(a, b) ALPHA_BLEND_BASE(a, b, 1, 3) +#define ALPHA_BLEND_64_W(a, b) ALPHA_BLEND_BASE(a, b, 1, 2) +#define ALPHA_BLEND_192_W(a, b) ALPHA_BLEND_BASE(a, b, 3, 2) +#define ALPHA_BLEND_224_W(a, b) ALPHA_BLEND_BASE(a, b, 7, 3) + +#define df(A, B) pixel_diff(A, B) +#define eq(A, B) (df(A, B) < 155) + +#define FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \ + N0, N1, N2, N3) do { \ + if (PE != PH && PE != PF) { \ + const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \ + const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \ + if (e <= i) { \ + const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \ + if (e < i && (!eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) \ + && (!eq(PF,I4) && !eq(PH,I5)) \ + || eq(PE,PG) || eq(PE,PC))) { \ + const unsigned ke = df(PF,PG); \ + const unsigned ki = df(PH,PC); \ + const int left = ke<<1 <= ki && PE != PG && PD != PG; \ + const int up = ke >= ki<<1 && PE != PC && PB != PC; \ + if (left && up) { \ + E[N3] = ALPHA_BLEND_224_W(E[N3], px); \ + E[N2] = ALPHA_BLEND_64_W( E[N2], px); \ + E[N1] = E[N2]; \ + } else if (left) { \ + E[N3] = ALPHA_BLEND_192_W(E[N3], px); \ + E[N2] = ALPHA_BLEND_64_W( E[N2], px); \ + } else if (up) { \ + E[N3] = ALPHA_BLEND_192_W(E[N3], px); \ + E[N1] = ALPHA_BLEND_64_W( E[N1], px); \ + } else { /* diagonal */ \ + E[N3] = ALPHA_BLEND_128_W(E[N3], px); \ + } \ + } else { \ + E[N3] = ALPHA_BLEND_128_W(E[N3], px); \ + } \ + } \ + } \ +} while (0) + +#define FILT3(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \ + N0, N1, N2, N3, N4, N5, N6, N7, N8) do { \ + if (PE != PH && PE != PF) { \ + const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \ + const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \ + if (e <= i) { \ + const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \ + if (e < i && (!eq(PF,PB) && !eq(PF,PC) || !eq(PH,PD) && !eq(PH,PG) || eq(PE,PI) \ + && (!eq(PF,F4) && !eq(PF,I4) || !eq(PH,H5) && !eq(PH,I5)) \ + || eq(PE,PG) || eq(PE,PC))) { \ + const unsigned ke = df(PF,PG); \ + const unsigned ki = df(PH,PC); \ + const int left = ke<<1 <= ki && PE != PG && PD != PG; \ + const int up = ke >= ki<<1 && PE != PC && PB != PC; \ + if (left && up) { \ + E[N7] = ALPHA_BLEND_192_W(E[N7], px); \ + E[N6] = ALPHA_BLEND_64_W( E[N6], px); \ + E[N5] = E[N7]; \ + E[N2] = E[N6]; \ + E[N8] = px; \ + } else if (left) { \ + E[N7] = ALPHA_BLEND_192_W(E[N7], px); \ + E[N5] = ALPHA_BLEND_64_W( E[N5], px); \ + E[N6] = ALPHA_BLEND_64_W( E[N6], px); \ + E[N8] = px; \ + } else if (up) { \ + E[N5] = ALPHA_BLEND_192_W(E[N5], px); \ + E[N7] = ALPHA_BLEND_64_W( E[N7], px); \ + E[N2] = ALPHA_BLEND_64_W( E[N2], px); \ + E[N8] = px; \ + } else { /* diagonal */ \ + E[N8] = ALPHA_BLEND_224_W(E[N8], px); \ + E[N5] = ALPHA_BLEND_32_W( E[N5], px); \ + E[N7] = ALPHA_BLEND_32_W( E[N7], px); \ + } \ + } else { \ + E[N8] = ALPHA_BLEND_128_W(E[N8], px); \ + } \ + } \ + } \ +} while (0) + +#define FILT4(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \ + N15, N14, N11, N3, N7, N10, N13, N12, N9, N6, N2, N1, N5, N8, N4, N0) do { \ + if (PE != PH && PE != PF) { \ + const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \ + const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \ + if (e <= i) { \ + const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \ + if (e < i && (!eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) \ + && (!eq(PF,I4) && !eq(PH,I5)) \ + || eq(PE,PG) || eq(PE,PC))) { \ + const unsigned ke = df(PF,PG); \ + const unsigned ki = df(PH,PC); \ + const int left = ke<<1 <= ki && PE != PG && PD != PG; \ + const int up = ke >= ki<<1 && PE != PC && PB != PC; \ + if (left && up) { \ + E[N13] = ALPHA_BLEND_192_W(E[N13], px); \ + E[N12] = ALPHA_BLEND_64_W( E[N12], px); \ + E[N15] = E[N14] = E[N11] = px; \ + E[N10] = E[N3] = E[N12]; \ + E[N7] = E[N13]; \ + } else if (left) { \ + E[N11] = ALPHA_BLEND_192_W(E[N11], px); \ + E[N13] = ALPHA_BLEND_192_W(E[N13], px); \ + E[N10] = ALPHA_BLEND_64_W( E[N10], px); \ + E[N12] = ALPHA_BLEND_64_W( E[N12], px); \ + E[N14] = px; \ + E[N15] = px; \ + } else if (up) { \ + E[N14] = ALPHA_BLEND_192_W(E[N14], px); \ + E[N7 ] = ALPHA_BLEND_192_W(E[N7 ], px); \ + E[N10] = ALPHA_BLEND_64_W( E[N10], px); \ + E[N3 ] = ALPHA_BLEND_64_W( E[N3 ], px); \ + E[N11] = px; \ + E[N15] = px; \ + } else { /* diagonal */ \ + E[N11] = ALPHA_BLEND_128_W(E[N11], px); \ + E[N14] = ALPHA_BLEND_128_W(E[N14], px); \ + E[N15] = px; \ + } \ + } else { \ + E[N15] = ALPHA_BLEND_128_W(E[N15], px); \ + } \ + } \ + } \ +} while (0) + +void +xbr_filter(uint32_t *data, uint32_t *out, int width, int height, int n) +{ + int x, y; + const int nl = width * n; + const int nl1 = nl + nl; + const int nl2 = nl1 + nl; + + for (y = 0; y < height; y++) { + + uint32_t *E = out + y * width * n * n; + const uint32_t *sa2 = (data + y * width - 2); /* center */ + const uint32_t *sa1 = sa2 - (width); /* up x1 */ + const uint32_t *sa0 = sa1 - (width); /* up x2 */ + const uint32_t *sa3 = sa2 + (width); /* down x1 */ + const uint32_t *sa4 = sa3 + (width); /* down x2 */ + + if (y <= 1) { + sa0 = sa1; + if (y == 0) { + sa0 = sa1 = sa2; + } + } + + if (y >= height - 2) { + sa4 = sa3; + if (y == height - 1) { + sa4 = sa3 = sa2; + } + } + + for (x = 0; x < width; x++) { + const uint32_t B1 = sa0[2]; + const uint32_t PB = sa1[2]; + const uint32_t PE = sa2[2]; + const uint32_t PH = sa3[2]; + const uint32_t H5 = sa4[2]; + + const int pprev = 2 - (x > 0); + const uint32_t A1 = sa0[pprev]; + const uint32_t PA = sa1[pprev]; + const uint32_t PD = sa2[pprev]; + const uint32_t PG = sa3[pprev]; + const uint32_t G5 = sa4[pprev]; + + const int pprev2 = pprev - (x > 1); + const uint32_t A0 = sa1[pprev2]; + const uint32_t D0 = sa2[pprev2]; + const uint32_t G0 = sa3[pprev2]; + + const int pnext = 3 - (x == width - 1); + const uint32_t C1 = sa0[pnext]; + const uint32_t PC = sa1[pnext]; + const uint32_t PF = sa2[pnext]; + const uint32_t PI = sa3[pnext]; + const uint32_t I5 = sa4[pnext]; + + const int pnext2 = pnext + 1 - (x >= width - 2); + const uint32_t C4 = sa1[pnext2]; + const uint32_t F4 = sa2[pnext2]; + const uint32_t I4 = sa3[pnext2]; + + if (n == 2) { + E[0] = E[1] = // 0, 1 + E[nl] = E[nl + 1] = PE; // 2, 3 + + FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, 0, 1, nl, nl+1); + FILT2(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, nl, 0, nl+1, 1); + FILT2(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, nl+1, nl, 1, 0); + FILT2(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, 1, nl+1, 0, nl); + } else if (n == 3) { + E[0] = E[1] = E[2] = // 0, 1, 2 + E[nl] = E[nl+1] = E[nl+2] = // 3, 4, 5 + E[nl1] = E[nl1+1] = E[nl1+2] = PE; // 6, 7, 8 + + FILT3(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, 0, 1, 2, nl, nl+1, nl+2, nl1, nl1+1, nl1+2); + FILT3(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, nl1, nl, 0, nl1+1, nl+1, 1, nl1+2, nl+2, 2); + FILT3(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, nl1+2, nl1+1, nl1, nl+2, nl+1, nl, 2, 1, 0); + FILT3(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, 2, nl+2, nl1+2, 1, nl+1, nl1+1, 0, nl, nl1); + } else if (n == 4) { + E[0] = E[1] = E[2] = E[3] = // 0, 1, 2, 3 + E[nl] = E[nl+1] = E[nl+2] = E[nl+3] = // 4, 5, 6, 7 + E[nl1] = E[nl1+1] = E[nl1+2] = E[nl1+3] = // 8, 9, 10, 11 + E[nl2] = E[nl2+1] = E[nl2+2] = E[nl2+3] = PE; // 12, 13, 14, 15 + + FILT4(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, nl2+3, nl2+2, nl1+3, 3, nl+3, nl1+2, nl2+1, nl2, nl1+1, nl+2, 2, 1, nl+1, nl1, nl, 0); + FILT4(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, 3, nl+3, 2, 0, 1, nl+2, nl1+3, nl2+3, nl1+2, nl+1, nl, nl1, nl1+1, nl2+2, nl2+1, nl2); + FILT4(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, 0, 1, nl, nl2, nl1, nl+1, 2, 3, nl+2, nl1+1, nl2+1, nl2+2, nl1+2, nl+3, nl1+3, nl2+3); + FILT4(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, nl2, nl1, nl2+1, nl2+3, nl2+2, nl1+1, nl, 0, nl+1, nl1+2, nl1+3, nl+3, nl+2, 1, 2, 3); + } + + sa0 += 1; + sa1 += 1; + sa2 += 1; + sa3 += 1; + sa4 += 1; + + E += n; + } + } +} diff --git a/src/conio/xbr.h b/src/conio/xbr.h new file mode 100644 index 0000000000000000000000000000000000000000..c8c32aeb647a5f3ab1410368cd7a02cb5f9589c2 --- /dev/null +++ b/src/conio/xbr.h @@ -0,0 +1,4 @@ +#include <inttypes.h> + +void xbr_filter(uint32_t *data, uint32_t *out, int width, int height, int n); +