DOSBox-X
|
00001 // **************************************************************************** 00002 // * This file is part of the xBRZ project. It is distributed under * 00003 // * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * 00004 // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * 00005 // * * 00006 // * Additionally and as a special exception, the author gives permission * 00007 // * to link the code of this program with the following libraries * 00008 // * (or with modified versions that use the same licenses), and distribute * 00009 // * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * 00010 // * You must obey the GNU General Public License in all respects for all of * 00011 // * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * 00012 // * If you modify this file, you may extend this exception to your version * 00013 // * of the file, but you are not obligated to do so. If you do not wish to * 00014 // * do so, delete this exception statement from your version. * 00015 // **************************************************************************** 00016 00017 #ifndef XBRZ_TOOLS_H_825480175091875 00018 #define XBRZ_TOOLS_H_825480175091875 00019 00020 #include <cassert> 00021 #include <algorithm> 00022 #include <type_traits> 00023 00024 00025 namespace xbrz 00026 { 00027 template <uint32_t N> inline 00028 unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); } 00029 00030 inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); } 00031 inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); } 00032 inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); } 00033 inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); } 00034 00035 inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return ((uint32_t)a << (uint32_t)24) | ((uint32_t)r << (uint32_t)16) | ((uint32_t)g << (uint32_t)8) | (uint32_t)b; } 00036 inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return ((uint32_t)r << (uint32_t)16) | ((uint32_t)g << (uint32_t)8) | (uint32_t)b; } 00037 00038 inline uint32_t rgb555to888(uint16_t pix) { return (((uint32_t)pix & (uint32_t)0x7C00) << (uint32_t)9) | (((uint32_t)pix & (uint32_t)0x03E0) << (uint32_t)6) | (((uint32_t)pix & (uint32_t)0x001F) << (uint32_t)3); } 00039 inline uint32_t rgb565to888(uint16_t pix) { return (((uint32_t)pix & (uint32_t)0xF800) << (uint32_t)8) | (((uint32_t)pix & (uint32_t)0x07E0) << (uint32_t)5) | (((uint32_t)pix & (uint32_t)0x001F) << (uint32_t)3); } 00040 00041 inline uint16_t rgb888to555(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); } 00042 inline uint16_t rgb888to565(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); } 00043 00044 00045 template <class Pix> inline 00046 Pix* byteAdvance(Pix* ptr, int bytes) 00047 { 00048 using PixNonConst = typename std::remove_cv<Pix>::type; 00049 using PixByte = typename std::conditional<std::is_same<Pix, PixNonConst>::value, char, const char>::type; 00050 00051 static_assert(std::is_integral<PixNonConst>::value, "Pix* is expected to be cast-able to char*"); 00052 00053 return reinterpret_cast<Pix*>(reinterpret_cast<PixByte*>(ptr) + bytes); 00054 } 00055 00056 00057 //fill block with the given color 00058 template <class Pix> inline 00059 void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight) 00060 { 00061 //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) 00062 // std::fill(trg, trg + blockWidth, col); 00063 00064 for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) 00065 for (int x = 0; x < blockWidth; ++x) 00066 trg[x] = col; 00067 } 00068 00069 // pitch change (use to change image pitch without any scaling, useful for fitting scaled image into D3D texture) 00070 template <class PixSrc, class PixTrg, class PixConverter> 00071 void pitchChange(const PixSrc* src, PixTrg* trg, int width, int height, int srcPitch, int trgPitch, 00072 int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) 00073 { 00074 static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*"); 00075 static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*"); 00076 00077 static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format"); 00078 00079 if (srcPitch < width * static_cast<int>(sizeof(PixSrc)) || 00080 trgPitch < width * static_cast<int>(sizeof(PixTrg))) 00081 { 00082 assert(false); 00083 return; 00084 } 00085 00086 yFirst = (std::max)(yFirst, 0); 00087 yLast = (std::min)(yLast, height); 00088 if (yFirst >= yLast || height <= 0 || width <= 0) return; 00089 00090 for (int y = yFirst; y < yLast; ++y) 00091 { 00092 const PixSrc* const srcLine = byteAdvance(src, y * srcPitch); 00093 PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); 00094 for (int x = 0; x < width; ++x) 00095 trgLine[x] = pixCvrt(srcLine[x]); 00096 } 00097 } 00098 00099 //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) 00100 template <class PixSrc, class PixTrg, class PixConverter> 00101 void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch, 00102 PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, 00103 int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) 00104 { 00105 static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*"); 00106 static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*"); 00107 00108 static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format"); 00109 00110 if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) || 00111 trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg))) 00112 { 00113 assert(false); 00114 return; 00115 } 00116 00117 yFirst = (std::max)(yFirst, 0); 00118 yLast = (std::min)(yLast, trgHeight); 00119 if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; 00120 00121 for (int y = yFirst; y < yLast; ++y) 00122 { 00123 const int ySrc = srcHeight * y / trgHeight; 00124 const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch); 00125 PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); 00126 00127 for (int x = 0; x < trgWidth; ++x) 00128 { 00129 const int xSrc = srcWidth * x / trgWidth; 00130 trgLine[x] = pixCvrt(srcLine[xSrc]); 00131 } 00132 } 00133 } 00134 00135 00136 //nearest-neighbor (going over source image - fast for upscaling, since source is read only once 00137 template <class PixSrc, class PixTrg, class PixConverter> 00138 void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch, 00139 PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, 00140 int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) 00141 { 00142 static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*"); 00143 static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*"); 00144 00145 static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format"); 00146 00147 if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) || 00148 trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg))) 00149 { 00150 assert(false); 00151 return; 00152 } 00153 00154 yFirst = std::max(yFirst, 0); 00155 yLast = std::min(yLast, srcHeight); 00156 if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; 00157 00158 for (int y = yFirst; y < yLast; ++y) 00159 { 00160 //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) 00161 // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight 00162 00163 //keep within for loop to support MT input slices! 00164 const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) 00165 const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) 00166 const int blockHeight = yTrgLast - yTrgFirst; 00167 00168 if (blockHeight > 0) 00169 { 00170 const PixSrc* srcLine = byteAdvance(src, y * srcPitch); 00171 PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch); 00172 int xTrgFirst = 0; 00173 00174 for (int x = 0; x < srcWidth; ++x) 00175 { 00176 const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; 00177 const int blockWidth = xTrgLast - xTrgFirst; 00178 if (blockWidth > 0) 00179 { 00180 xTrgFirst = xTrgLast; 00181 00182 const auto trgPix = pixCvrt(srcLine[x]); 00183 fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight); 00184 trgLine += blockWidth; 00185 } 00186 } 00187 } 00188 } 00189 } 00190 00191 00192 template <class PixTrg, class PixConverter> 00193 void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, 00194 PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, 00195 int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/) 00196 { 00197 static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*"); 00198 static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format"); 00199 00200 if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) || 00201 trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg))) 00202 { 00203 assert(false); 00204 return; 00205 } 00206 00207 yFirst = (std::max)(yFirst, 0); 00208 yLast = (std::min)(yLast, trgHeight); 00209 if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; 00210 00211 const double scaleX = static_cast<double>(trgWidth ) / srcWidth; 00212 const double scaleY = static_cast<double>(trgHeight) / srcHeight; 00213 00214 //perf notes: 00215 // -> double-based calculation is (slightly) faster than float 00216 // -> precalculation gives significant boost; std::vector<> memory allocation is negligible! 00217 struct CoeffsX 00218 { 00219 int x1 = 0; 00220 int x2 = 0; 00221 double xx1 = 0; 00222 double x2x = 0; 00223 }; 00224 std::vector<CoeffsX> buf((size_t)trgWidth); 00225 for (int x = 0; x < trgWidth; ++x) 00226 { 00227 const int x1 = srcWidth * x / trgWidth; 00228 int x2 = x1 + 1; 00229 if (x2 == srcWidth) --x2; 00230 00231 const double xx1 = x / scaleX - x1; 00232 const double x2x = 1 - xx1; 00233 00234 buf[(unsigned int)x].x1 = x1; 00235 buf[(unsigned int)x].x2 = x2; 00236 buf[(unsigned int)x].xx1 = xx1; 00237 buf[(unsigned int)x].x2x = x2x; 00238 } 00239 00240 for (int y = yFirst; y < yLast; ++y) 00241 { 00242 const int y1 = srcHeight * y / trgHeight; 00243 int y2 = y1 + 1; 00244 if (y2 == srcHeight) --y2; 00245 00246 const double yy1 = y / scaleY - y1; 00247 const double y2y = 1 - yy1; 00248 00249 const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch); 00250 const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch); 00251 PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); 00252 00253 for (int x = 0; x < trgWidth; ++x) 00254 { 00255 //perf: do NOT "simplify" the variable layout without measurement! 00256 const int x1 = buf[(unsigned int)x].x1; 00257 const int x2 = buf[(unsigned int)x].x2; 00258 const double xx1 = buf[(unsigned int)x].xx1; 00259 const double x2x = buf[(unsigned int)x].x2x; 00260 00261 const double x2xy2y = x2x * y2y; 00262 const double xx1y2y = xx1 * y2y; 00263 const double x2xyy1 = x2x * yy1; 00264 const double xx1yy1 = xx1 * yy1; 00265 00266 auto interpolate = [=](int offset) 00267 { 00268 /* 00269 https://en.wikipedia.org/wiki/Bilinear_interpolation 00270 (c11(x2 - x) + c21(x - x1)) * (y2 - y ) + 00271 (c12(x2 - x) + c22(x - x1)) * (y - y1) 00272 */ 00273 const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff; 00274 const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff; 00275 const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff; 00276 const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff; 00277 00278 return c11 * x2xy2y + c21 * xx1y2y + 00279 c12 * x2xyy1 + c22 * xx1yy1; 00280 }; 00281 00282 const double bi = interpolate(0); 00283 const double gi = interpolate(1); 00284 const double ri = interpolate(2); 00285 const double ai = interpolate(3); 00286 00287 const auto b = static_cast<uint32_t>(bi + 0.5); 00288 const auto g = static_cast<uint32_t>(gi + 0.5); 00289 const auto r = static_cast<uint32_t>(ri + 0.5); 00290 const auto a = static_cast<uint32_t>(ai + 0.5); 00291 00292 const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b; 00293 00294 trgLine[(unsigned int)x] = pixCvrt(trgPix); 00295 } 00296 } 00297 } 00298 } 00299 00300 #endif //XBRZ_TOOLS_H_825480175091875