// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program 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 General Public License for more details. // // DESCRIPTION: // AdvMAME scale2x/scale3x filters by Andrea Mazzoleni, reimplemented // in-repo (GPLv2+, same license as the project). Each source pixel E // with its neighbors // // A B C // D E F // G H I // // expands to a 2x2 (scale2x) or 3x3 (scale3x) block of output pixels. // Factor 4 is scale2x applied twice, the reference composition. // #include #include "common.h" #include "i_upscale.h" // Exact per-byte average of two packed RGBA pixels: the common bits plus // half the differing bits. The mask keeps the halving inside each byte // lane, so no carry crosses channels and no unpacking is needed. static uint32_t pixel_blend(uint32_t x, uint32_t y) { return (x & y) + (((x ^ y) >> 1) & 0x7f7f7f7f); } static void scale2x_pass(const uint32_t *src, int spitch, int w, int h, uint32_t *dst, int dpitch) { int x, y; for (y = 0; y < h; ++y) { const uint32_t *row = src + y * spitch; const uint32_t *above = y > 0 ? row - spitch : row; const uint32_t *below = y < h - 1 ? row + spitch : row; uint32_t *out0 = dst + (y * 2) * dpitch; uint32_t *out1 = dst + (y * 2 + 1) * dpitch; for (x = 0; x < w; ++x) { // Out-of-bounds neighbors are treated as E (edges of the // above/below row aliases and of the x clamp do exactly that). uint32_t E = row[x]; uint32_t B = above[x]; uint32_t H = below[x]; uint32_t D = x > 0 ? row[x - 1] : E; uint32_t F = x < w - 1 ? row[x + 1] : E; uint32_t a, b, c, d; a = (D == B && D != H && B != F) ? D : E; b = (B == F && B != D && F != H) ? B : E; c = (D == H && D != B && H != F) ? D : E; d = (H == F && H != D && F != B) ? F : E; out0[x * 2] = a; out0[x * 2 + 1] = b; out1[x * 2] = c; out1[x * 2 + 1] = d; } } } static void scale3x_pass(const uint32_t *src, int spitch, int w, int h, uint32_t *dst, int dpitch) { int x, y; for (y = 0; y < h; ++y) { const uint32_t *row = src + y * spitch; const uint32_t *above = y > 0 ? row - spitch : row; const uint32_t *below = y < h - 1 ? row + spitch : row; uint32_t *out0 = dst + (y * 3) * dpitch; uint32_t *out1 = dst + (y * 3 + 1) * dpitch; uint32_t *out2 = dst + (y * 3 + 2) * dpitch; for (x = 0; x < w; ++x) { uint32_t E = row[x]; uint32_t B = above[x]; uint32_t H = below[x]; uint32_t D = x > 0 ? row[x - 1] : E; uint32_t F = x < w - 1 ? row[x + 1] : E; uint32_t a0, a1, a2, a3, a4, a5, a6, a7, a8; a4 = E; a1 = (B == D) ? D : E; a5 = (B == F) ? F : E; a7 = (H == F) ? F : E; a3 = (H == D) ? D : E; a0 = (B == D && B != F && D != H) ? D : ((D == B && D == H && F != H && B != F) ? pixel_blend(D, B) : E); a2 = (B == F && B != D && F != H) ? F : ((B == F && H == F && D != H && B != D) ? pixel_blend(B, F) : E); a6 = (D == H && D != B && H != F) ? D : ((D == H && H == F && B != F && D != B) ? pixel_blend(D, H) : E); a8 = (H == F && H != D && F != B) ? F : ((H == F && F == B && D != B && H != D) ? pixel_blend(H, F) : E); out0[x * 3] = a0; out0[x * 3 + 1] = a1; out0[x * 3 + 2] = a2; out1[x * 3] = a3; out1[x * 3 + 1] = a4; out1[x * 3 + 2] = a5; out2[x * 3] = a6; out2[x * 3 + 1] = a7; out2[x * 3 + 2] = a8; } } } void I_UpscaleFrame(const uint32_t *src, int srcpitch_px, int w, int h, uint32_t *dst, int dstpitch_px, int factor) { if (factor == 2) { scale2x_pass(src, srcpitch_px, w, h, dst, dstpitch_px); } else if (factor == 3) { scale3x_pass(src, srcpitch_px, w, h, dst, dstpitch_px); } else if (factor == 4) { // scale2x applied twice (640x400x4 = 1 MB temporary). uint32_t *tmp = (uint32_t *) malloc(sizeof(uint32_t) * w * 2 * h * 2); if (tmp == NULL) { EXIT_Error("I_UpscaleFrame: memory"); } scale2x_pass(src, srcpitch_px, w, h, tmp, w * 2); scale2x_pass(tmp, w * 2, w * 2, h * 2, dst, dstpitch_px); free(tmp); } else { EXIT_Error("I_UpscaleFrame: bad factor %d", factor); } }