/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GRAPHICS_SCALER_EDGE_H #define GRAPHICS_SCALER_EDGE_H #include "graphics/scalerplugin.h" class EdgeScaler : public SourceScaler { public: EdgeScaler(const Graphics::PixelFormat &format); virtual uint increaseFactor() override; virtual uint decreaseFactor() override; protected: virtual void internScale(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, const uint8 *oldSrcPtr, uint32 oldSrcPitch, int width, int height, const uint8 *buffer, uint32 bufferPitch) override; private: /** * Choose greyscale bitplane to use, return diff array. Exit early and * return NULL for a block of solid color (all diffs zero). * * No matter how you do it, mapping 3 bitplanes into a single greyscale * bitplane will always result in colors which are very different mapping to * the same greyscale value. Inevitably, these pixels will appear next to * each other at some point in some image, and edge detection on a single * bitplane will behave quite strangely due to them having the same or nearly * the same greyscale values. Calculating distances between pixels using all * three RGB bitplanes is *way* too time consuming, so single bitplane * edge detection is used for speed's sake. In order to try to avoid the * color mapping problems of using a single bitplane, 3 different greyscale * mappings are tested for each 3x3 grid, and the one with the most "signal" * (sum of squares difference from center pixel) is chosen. This usually * results in useable contrast within the 3x3 grid. * * This results in a whopping 25% increase in overall runtime of the filter * over simply using luma or some other single greyscale bitplane, but it * does greatly reduce the amount of errors due to greyscale mapping * problems. I think this is the best compromise between accuracy and * speed, and is still a lot faster than edge detecting over all three RGB * bitplanes. The increase in image quality is well worth the speed hit. */ template int16 *chooseGreyscale(typename ColorMask::PixelType *pixels); /** * Calculate the distance between pixels in RGB space. Greyscale isn't * accurate enough for choosing nearest-neighbors :( Luma-like weighting * of the individual bitplane distances prior to squaring gives the most * useful results. */ template int32 calcPixelDiffNosqrt(typename ColorMask::PixelType pixel1, typename ColorMask::PixelType pixel2); /** * Create vectors of all delta grey values from center pixel, with magnitudes * ranging from [1.0, 0.0] (zero difference, maximum difference). Find * the two principle axes of the grid by calculating the eigenvalues and * eigenvectors of the inertia tensor. Use the eigenvectors to calculate the * edge direction. In other words, find the angle of the line that optimally * passes through the 3x3 pattern of pixels. * * Return horizontal (-), vertical (|), diagonal (/,\), multi (*), or none '0' * * Don't replace any of the double math with integer-based approximations, * since everything I have tried has lead to slight mis-detection errors. */ int findPrincipleAxis(int16 *diffs, int16 *bplane, int8 *sim, int32 *return_angle); /** * Check for mis-detected arrow patterns. Return 1 (good), 0 (bad). */ template int checkArrows(int best_dir, Pixel *pixels, int8 *sim, int half_flag); /** * Take original direction, refine it by testing different pixel difference * patterns based on the initial gross edge direction. * * The angle value is not currently used, but may be useful for future * refinement algorithms. */ template int refineDirection(char edge_type, Pixel *pixels, int16 *bptr, int8 *sim, double angle); /** * "Chess Knight" patterns can be mis-detected, fix easy cases. */ template int fixKnights(int sub_type, Pixel *pixels, int8 *sim); /** * Initialize various lookup tables */ void initTables(const uint8 *srcPtr, uint32 srcPitch, int width, int height); /** * Fill pixel grid with or without interpolation, using the detected edge */ template void antiAliasGrid2x(uint8 *dptr, int dstPitch, typename ColorMask::PixelType *pixels, int sub_type, int16 *bptr, int8 *sim, int interpolate_2x); /** * Fill pixel grid without interpolation, using the detected edge */ template void antiAliasGridClean3x(uint8 *dptr, int dstPitch, typename ColorMask::PixelType *pixels, int sub_type, int16 *bptr); /** * Perform edge detection, draw the new 2x pixels */ template void antiAliasPass2x(const uint8 *src, uint8 *dst, int w, int h, int srcPitch, int dstPitch, int interpolate_2x, bool haveOldSrc, const uint8 *oldSrc, int oldSrcPitch, const uint8 *buffer, int bufferPitch); /** * Perform edge detection, draw the new 3x pixels */ template void antiAliasPass3x(const uint8 *src, uint8 *dst, int w, int h, int srcPitch, int dstPitch, bool haveOldSrc, const uint8* oldSrc, int oldPitch, const uint8 *buffer, int bufferPitch); int16 _rgbTable[65536][3]; ///< table lookup for RGB int16 _greyscaleTable[3][65536]; ///< greyscale tables int16 *_chosenGreyscale; ///< pointer to chosen greyscale table int16 *_bptr; ///< too awkward to pass variables int8 _simSum; ///< sum of similarity matrix int16 _greyscaleDiffs[3][8]; int16 _bplanes[3][9]; }; #endif