Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
@ 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 3 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, see <http://www.gnu.org/licenses/>.
@
@ @author Robin Watts (robin@wss.co.uk)
.text
.global Normal2xARM
@ Assumes dst is aligned (so did the C)
@ Assumes 16bit (so did the C)
.align 2
Normal2xARM:
@ r0 = src
@ r1 = srcPitch
@ r2 = dst
@ r3 = dstPitch
@ r4 = w
@ r5 = h
STMFD r13!,{r4-r11,r14}
LDR r4, [r13,#4*9] @ r4 = w
LDR r5, [r13,#4*10] @ r5 = h
ADD r12,r2, r3
SUB r1, r1, r4, LSL #1
SUB r6, r3, r4, LSL #2
ADD r3, r3, r6
yloop:
SUBS r14,r4, #4
ADDLT r14,r14, #4
BLT thin
xloop:
LDRH r6, [r0], #2
LDRH r7, [r0], #2
LDRH r8, [r0], #2
LDRH r9, [r0], #2
ORR r6, r6, r6, LSL #16
ORR r7, r7, r7, LSL #16
ORR r8, r8, r8, LSL #16
ORR r9, r9, r9, LSL #16
STMIA r2!, {r6-r9}
STMIA r12!,{r6-r9}
SUBS r14,r14,#4
BGE xloop
ADDS r14,r14,#4
BNE thin
ADD r0, r0, r1
ADD r2, r2, r3
ADD r12,r12,r3
SUBS r5, r5, #1
BGT yloop
LDMFD r13!,{r4-r11,PC}
thin:
LDRH r6, [r0], #2
ORR r6, r6, r6, LSL #16
STR r6, [r2], #4
STR r6, [r12],#4
SUBS r14,r14,#1
BGT thin
ADD r0, r0, r1
ADD r2, r2, r3
ADD r12,r12,r3
SUBS r5, r5, #1
BGT yloop
LDMFD r13!,{r4-r11,PC}

272
graphics/scaler/aspect.cpp Normal file
View File

@@ -0,0 +1,272 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/scaler/intern.h"
#include "graphics/scaler/aspect.h"
#ifdef USE_ARM_NEON_ASPECT_CORRECTOR
#include <arm_neon.h>
#endif
#define kSuperFastAndUglyAspectMode 0 // No interpolation at all, but super-fast
#define kVeryFastAndGoodAspectMode 1 // Good quality with very good speed
#define kFastAndVeryGoodAspectMode 2 // Very good quality with good speed
#define kSlowAndPerfectAspectMode 3 // Accurate but slow code
#define ASPECT_MODE kVeryFastAndGoodAspectMode
#if ASPECT_MODE == kSlowAndPerfectAspectMode
template<typename ColorMask, int scale>
static inline uint16 interpolate5(uint16 A, uint16 B) {
uint16 r = (uint16)(((A & ColorMask::kRedBlueMask & 0xFF00) * scale + (B & ColorMask::kRedBlueMask & 0xFF00) * (5 - scale)) / 5);
uint16 g = (uint16)(((A & ColorMask::kGreenMask) * scale + (B & ColorMask::kGreenMask) * (5 - scale)) / 5);
uint16 b = (uint16)(((A & ColorMask::kRedBlueMask & 0x00FF) * scale + (B & ColorMask::kRedBlueMask & 0x00FF) * (5 - scale)) / 5);
return (uint16)((r & ColorMask::kRedBlueMask & 0xFF00) | (g & ColorMask::kGreenMask) | (b & ColorMask::kRedBlueMask & 0x00FF));
}
template<typename ColorMask, int scale>
static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
// Accurate but slightly slower code
while (width--) {
*dst++ = interpolate5<ColorMask, scale>(*srcA++, *srcB++);
}
}
#endif
#if ASPECT_MODE == kVeryFastAndGoodAspectMode
#ifdef USE_ARM_NEON_ASPECT_CORRECTOR
template<typename ColorMask>
static void interpolate5LineNeon(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width, int k1, int k2) {
uint16x4_t kRedBlueMask_4 = vdup_n_u16(ColorMask::kRedBlueMask);
uint16x4_t kGreenMask_4 = vdup_n_u16(ColorMask::kGreenMask);
uint16x4_t k1_4 = vdup_n_u16(k1);
uint16x4_t k2_4 = vdup_n_u16(k2);
while (width >= 4) {
uint16x4_t srcA_4 = vld1_u16(srcA);
uint16x4_t srcB_4 = vld1_u16(srcB);
uint16x4_t p1_4 = srcB_4;
uint16x4_t p2_4 = srcA_4;
uint16x4_t p1_rb_4 = vand_u16(p1_4, kRedBlueMask_4);
uint16x4_t p1_g_4 = vand_u16(p1_4, kGreenMask_4);
uint16x4_t p2_rb_4 = vand_u16(p2_4, kRedBlueMask_4);
uint16x4_t p2_g_4 = vand_u16(p2_4, kGreenMask_4);
uint32x4_t tmp_rb_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_rb_4, k2_4), p1_rb_4, k1_4), 3);
uint32x4_t tmp_g_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_g_4, k2_4), p1_g_4, k1_4), 3);
uint16x4_t p_rb_4 = vmovn_u32(tmp_rb_4);
p_rb_4 = vand_u16(p_rb_4, kRedBlueMask_4);
uint16x4_t p_g_4 = vmovn_u32(tmp_g_4);
p_g_4 = vand_u16(p_g_4, kGreenMask_4);
uint16x4_t result_4 = p_rb_4 | p_g_4;
vst1_u16(dst, result_4);
dst += 4;
srcA += 4;
srcB += 4;
width -= 4;
}
}
#endif // USE_ARM_NEON_ASPECT_CORRECTOR
template<typename ColorMask, int scale>
static void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
if (scale == 1) {
#ifdef USE_NEON_ASPECT_CORRECTOR
int width4 = width & ~3;
interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 7, 1);
srcA += width4;
srcB += width4;
dst += width4;
width -= width4;
#endif // USE_ARM_NEON_ASPECT_CORRECTOR
while (width--) {
*dst++ = interpolate16_7_1<ColorMask>(*srcB++, *srcA++);
}
} else {
#ifdef USE_ARM_NEON_ASPECT_CORRECTOR
int width4 = width & ~3;
interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 5, 3);
srcA += width4;
srcB += width4;
dst += width4;
width -= width4;
#endif // USE_ARM_NEON_ASPECT_CORRECTOR
while (width--) {
*dst++ = interpolate16_5_3<ColorMask>(*srcB++, *srcA++);
}
}
}
#endif
#if ASPECT_MODE == kFastAndVeryGoodAspectMode
template<typename ColorMask, int scale>
static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) {
// For efficiency reasons we blit two pixels at a time, so it is important
// that makeRectStretchable() guarantees that the width is even and that
// the rect starts on a well-aligned address. (Even where unaligned memory
// access is allowed there may be a speed penalty for it.)
// These asserts are disabled for maximal speed; but I leave them in here
// in case other people want to test if the memory alignment (to an
// address divisible by 4) is really working properly.
//assert(((int)dst & 3) == 0);
//assert(((int)srcA & 3) == 0);
//assert(((int)srcB & 3) == 0);
//assert((width & 1) == 0);
width /= 2;
const uint32 *sA = (const uint32 *)srcA;
const uint32 *sB = (const uint32 *)srcB;
uint32 *d = (uint32 *)dst;
if (scale == 1) {
while (width--) {
*d++ = interpolate32_3_1<ColorMask>(*sB++, *sA++);
}
} else {
while (width--) {
*d++ = interpolate32_1_1<ColorMask>(*sB++, *sA++);
}
}
}
#endif
void makeRectStretchable(int &x, int &y, int &w, int &h, bool interpolate) {
#if ASPECT_MODE != kSuperFastAndUglyAspectMode
if (!interpolate)
return;
int m = real2Aspect(y) % 6;
// Ensure that the rect will start on a line that won't have its
// colors changed by the stretching function.
if (m != 0 && m != 5) {
y -= m;
h += m;
}
#if ASPECT_MODE == kVeryFastAndGoodAspectMode
// Force x to be even, to ensure aligned memory access (this assumes
// that each line starts at an even memory location, but that should
// be the case on every target anyway).
if (x & 1) {
x--;
w++;
}
// Finally force the width to be even, since we blit 2 pixels at a time.
// While this means we may sometimes blit one column more than necessary,
// this should actually be faster than having the check for the
if (w & 1)
w++;
#endif
#endif
}
/**
* Stretch a 16bpp image vertically by factor 1.2. Used to correct the
* aspect-ratio in games using 320x200 pixel graphics with non-qudratic
* pixels. Applying this method effectively turns that into 320x240, which
* provides the correct aspect-ratio on modern displays.
*
* The image would normally have occupied y coordinates origSrcY through
* origSrcY + height - 1.
*
* However, we have already placed it at srcY - the aspect-corrected y
* coordinate - to allow in-place stretching.
*
* Therefore, the source image now occupies Y coordinates srcY through
* srcY + height - 1, and it should be stretched to Y coordinates srcY
* through real2Aspect(srcY + height - 1).
*/
int stretch200To240Nearest(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY, const Graphics::PixelFormat &format) {
int maxDstY = real2Aspect(origSrcY + height - 1);
int y;
const uint8 *startSrcPtr = buf + srcX * format.bytesPerPixel + (srcY - origSrcY) * pitch;
uint8 *dstPtr = buf + srcX * format.bytesPerPixel + maxDstY * pitch;
for (y = maxDstY; y >= srcY; y--) {
const uint8 *srcPtr = startSrcPtr + aspect2Real(y) * pitch;
if (srcPtr == dstPtr)
break;
memcpy(dstPtr, srcPtr, format.bytesPerPixel * width);
dstPtr -= pitch;
}
return 1 + maxDstY - srcY;
}
template<typename ColorMask>
int stretch200To240Interpolated(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY) {
int maxDstY = real2Aspect(origSrcY + height - 1);
int y;
const uint8 *startSrcPtr = buf + srcX * 2 + (srcY - origSrcY) * pitch;
uint8 *dstPtr = buf + srcX * 2 + maxDstY * pitch;
for (y = maxDstY; y >= srcY; y--) {
const uint8 *srcPtr = startSrcPtr + aspect2Real(y) * pitch;
switch (y % 6) {
case 0:
case 5:
if (srcPtr != dstPtr)
memcpy(dstPtr, srcPtr, sizeof(uint16) * width);
break;
case 1:
interpolate5Line<ColorMask, 1>((uint16 *)dstPtr, (const uint16 *)(srcPtr - pitch), (const uint16 *)srcPtr, width);
break;
case 2:
interpolate5Line<ColorMask, 2>((uint16 *)dstPtr, (const uint16 *)(srcPtr - pitch), (const uint16 *)srcPtr, width);
break;
case 3:
interpolate5Line<ColorMask, 2>((uint16 *)dstPtr, (const uint16 *)srcPtr, (const uint16 *)(srcPtr - pitch), width);
break;
case 4:
interpolate5Line<ColorMask, 1>((uint16 *)dstPtr, (const uint16 *)srcPtr, (const uint16 *)(srcPtr - pitch), width);
break;
default:
break;
}
dstPtr -= pitch;
}
return 1 + maxDstY - srcY;
}
int stretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY, bool interpolate, const Graphics::PixelFormat &format) {
#if ASPECT_MODE != kSuperFastAndUglyAspectMode
if (interpolate && format.bytesPerPixel == 2) {
if (format.gLoss == 2)
return stretch200To240Interpolated<Graphics::ColorMasks<565> >(buf, pitch, width, height, srcX, srcY, origSrcY);
else if (format.gLoss == 3)
return stretch200To240Interpolated<Graphics::ColorMasks<555> >(buf, pitch, width, height, srcX, srcY, origSrcY);
}
#endif
return stretch200To240Nearest(buf, pitch, width, height, srcX, srcY, origSrcY, format);
}

63
graphics/scaler/aspect.h Normal file
View File

@@ -0,0 +1,63 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_SCALER_ASPECT_H
#define GRAPHICS_SCALER_ASPECT_H
#include "common/scummsys.h"
#include "graphics/scaler.h"
/**
* TODO: explain
*/
FORCEINLINE int real2Aspect(int y) {
return y + (y + 1) / 5;
}
/**
* TODO: explain
*/
FORCEINLINE int aspect2Real(int y) {
return (y * 5 + 4) / 6;
}
/**
* TODO: explain
*/
void makeRectStretchable(int &x, int &y, int &w, int &h, bool interpolate);
/**
* TODO: explain
*/
int stretch200To240(uint8 *buf,
uint32 pitch,
int width,
int height,
int srcX,
int srcY,
int origSrcY,
bool interpolate,
const Graphics::PixelFormat &format);
int stretch200To240Nearest(uint8 *buf, uint32 pitch, int width, int height, int srcX, int srcY, int origSrcY, const Graphics::PixelFormat &format);
#endif

View File

@@ -0,0 +1,129 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "graphics/scaler/dotmatrix.h"
#include "graphics/scaler.h"
DotMatrixScaler::DotMatrixScaler(const Graphics::PixelFormat &format) : Scaler(format) {
_factor = 2;
if (format.bytesPerPixel == 2) {
uint16 *lookup16 = (uint16 *)lookup;
lookup16[0] = lookup16[10] = format.RGBToColor(0, 63, 0);
lookup16[1] = lookup16[11] = format.RGBToColor(0, 0, 63);
lookup16[2] = lookup16[8] = format.RGBToColor(63, 0, 0);
lookup16[4] = lookup16[6] =
lookup16[12] = lookup16[14] = format.RGBToColor(63, 63, 63);
lookup16[3] = lookup16[5] = lookup16[7] =
lookup16[9] = lookup16[13] =
lookup16[15] = lookup16[16] = format.RGBToColor(0, 0, 0);
} else {
uint32 *lookup32 = (uint32 *)lookup;
lookup32[0] = lookup32[10] = format.ARGBToColor(0, 0, 63, 0);
lookup32[1] = lookup32[11] = format.ARGBToColor(0, 0, 0, 63);
lookup32[2] = lookup32[8] = format.ARGBToColor(0, 63, 0, 0);
lookup32[4] = lookup32[6] =
lookup32[12] = lookup32[14] = format.ARGBToColor(0, 63, 63, 63);
lookup32[3] = lookup32[5] = lookup32[7] =
lookup32[9] = lookup32[13] =
lookup32[15] = lookup32[16] = format.ARGBToColor(0, 0, 0, 0);
}
}
void DotMatrixScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_format.bytesPerPixel == 2) {
scaleIntern<uint16>(srcPtr, srcPitch, dstPtr, dstPitch, width, height, x, y);
} else {
scaleIntern<uint32>(srcPtr, srcPitch, dstPtr, dstPitch, width, height, x, y);
}
}
uint DotMatrixScaler::increaseFactor() {
return _factor;
}
uint DotMatrixScaler::decreaseFactor() {
return _factor;
}
template<typename Pixel>
static inline Pixel DOT(const Pixel *dotmatrix, Pixel c, int j, int i) {
return c - ((c >> 2) & dotmatrix[((j & 3) << 2) + (i & 3)]);
}
template<typename Pixel>
void DotMatrixScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height, int x, int y) {
const Pixel *dotmatrix = (Pixel *)lookup;
const uint32 nextlineSrc = srcPitch / sizeof(Pixel);
const Pixel *p = (const Pixel *)srcPtr;
const uint32 nextlineDst = dstPitch / sizeof(Pixel);
Pixel *q = (Pixel *)dstPtr;
int ja = (y * 2) & 3;
int ia = (x * 2) & 3;
for (int j = 0, jj = 0; j < height; ++j, jj += 2) {
for (int i = 0, ii = 0; i < width; ++i, ii += 2) {
Pixel c = *(p + i);
*(q + ii) = DOT<Pixel>(dotmatrix, c, jj + ja, ii + ia);
*(q + ii + 1) = DOT<Pixel>(dotmatrix, c, jj + ja, ii + ia + 1);
*(q + ii + nextlineDst) = DOT<Pixel>(dotmatrix, c, jj + ja + 1, ii + ia);
*(q + ii + nextlineDst + 1) = DOT<Pixel>(dotmatrix, c, jj + ja + 1, ii + ia+ 1);
}
p += nextlineSrc;
q += nextlineDst << 1;
}
}
class DotMatrixPlugin final : public ScalerPluginObject {
public:
DotMatrixPlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return false; }
uint extraPixels() const override { return 0; }
const char *getName() const override;
const char *getPrettyName() const override;
};
DotMatrixPlugin::DotMatrixPlugin() {
_factors.push_back(2);
}
Scaler *DotMatrixPlugin::createInstance(const Graphics::PixelFormat &format) const {
return new DotMatrixScaler(format);
}
const char *DotMatrixPlugin::getName() const {
return "dotmatrix";
}
const char *DotMatrixPlugin::getPrettyName() const {
return "DotMatrix";
}
REGISTER_PLUGIN_STATIC(DOTMATRIX, PLUGIN_TYPE_SCALER, DotMatrixPlugin);

View File

@@ -0,0 +1,43 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_DOTMATRIX_H
#define GRAPHICS_SCALER_DOTMATRIX_H
#include "graphics/scalerplugin.h"
class DotMatrixScaler : public Scaler {
public:
DotMatrixScaler(const Graphics::PixelFormat &format);
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
private:
// Allocate enough for 32bpp formats
uint32 lookup[17];
template<typename Pixel>
void scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
uint32 dstPitch, int width, int height, int x, int y);
};
#endif

View File

@@ -0,0 +1,146 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/textconsole.h"
#include "graphics/scaler/downscaler.h"
#include "graphics/scaler/intern.h"
namespace Graphics {
#ifdef USE_ARM_SCALER_ASM
extern "C" {
void downscaleAllByHalfARM(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height, int mask, int round);
}
void downscaleAllByHalf(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height, int gBitFormat) {
// Rounding constants and masks used for different pixel formats
static const int roundingconstants[] = { 0x00200802, 0x00201002 };
static const int redbluegreenMasks[] = { 0x03E07C1F, 0x07E0F81F };
const int maskUsed = (gBitFormat == 565);
downscaleAllByHalfARM(srcPtr, srcPitch, dstPtr, dstPitch, width, height, redbluegreenMasks[maskUsed], roundingconstants[maskUsed]);
}
#else
template<typename ColorMask>
void downscaleAllByHalfTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
uint8 *work;
uint16 srcPitch16 = (uint16)(srcPitch / sizeof(uint16));
while ((height -= 2) >= 0) {
work = dstPtr;
for (int i=0; i<width; i+=2) {
// Another lame filter attempt :)
uint16 color1 = *(((const uint16 *)srcPtr) + i);
uint16 color2 = *(((const uint16 *)srcPtr) + (i + 1));
uint16 color3 = *(((const uint16 *)srcPtr) + (i + srcPitch16));
uint16 color4 = *(((const uint16 *)srcPtr) + (i + srcPitch16 + 1));
*(((uint16 *)work) + 0) = interpolate16_1_1_1_1<ColorMask>(color1, color2, color3, color4);
work += sizeof(uint16);
}
srcPtr += 2 * srcPitch;
dstPtr += dstPitch;
}
}
void downscaleAllByHalf(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height, int gBitFormat) {
if (gBitFormat == 565)
downscaleAllByHalfTemplate<Graphics::ColorMasks<565> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
downscaleAllByHalfTemplate<Graphics::ColorMasks<555> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
}
#endif
namespace {
uint32 getBrightness(byte col, const byte *pal) {
return pal[3 * col] * pal[3 * col] + pal[3 * col + 1] * pal[3 * col + 1] + pal[3 * col + 2] * pal[3 * col + 2];
}
void downscaleCLUT8ByHalf(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int maxX, int maxY, const byte *palette) {
const byte *src1 = srcPtr;
const byte *src2 = srcPtr + srcPitch;
byte *dst = dstPtr;
int32 srcSkip = 2 * srcPitch - maxX * 2;
int32 dstSkip = dstPitch - maxX;
for (int y = 0; y < maxY; y++) {
for (int x = 0; x < maxX; x++) {
// Choose the brightest pixel. Writing often is bright on
// a dark background, so this preserves text as much as we can.
byte colors[4] = {
*src1++,
*src1++,
*src2++,
*src2++
};
byte col = colors[0];
uint32 bri = getBrightness(col, palette);
for (uint i = 1; i < 4; i++) {
uint32 nbri = getBrightness(colors[i], palette);
if (nbri > bri) {
bri = nbri;
col = colors[i];
}
}
*dst++ = col;
}
src1 += srcSkip;
src2 += srcSkip;
dst += dstSkip;
}
}
} // end of anonymous namespsce
void downscaleSurfaceByHalf(Surface *out, const Surface *in, const byte *palette) {
if (in->format.isCLUT8() && out->format.isCLUT8() && palette != nullptr) {
downscaleCLUT8ByHalf((const byte *) in->getBasePtr(0, 0), in->pitch, (byte *) out->getBasePtr(0, 0), out->pitch,
MIN<int>(out->w, in->w / 2), MIN<int>(out->h, in->h / 2), palette);
return;
}
if ((in->format == PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)
&& out->format == PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0))
|| (in->format == PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)
&& out->format == PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0))) {
downscaleAllByHalf((const byte *) in->getBasePtr(0, 0), in->pitch, (byte *) out->getBasePtr(0, 0), out->pitch,
MIN<int>(in->w, out->w * 2), MIN<int>(in->h, out->h * 2), 565);
return;
}
if ((in->format == PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)
&& out->format == PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0))
|| (in->format == PixelFormat(2, 5, 5, 5, 0, 0, 5, 10, 0)
&& out->format == PixelFormat(2, 5, 5, 5, 0, 0, 5, 10, 0))) {
downscaleAllByHalf((const byte *) in->getBasePtr(0, 0), in->pitch, (byte *) out->getBasePtr(0, 0), out->pitch,
MIN<int>(in->w, out->w * 2), MIN<int>(in->h, out->h * 2), 555);
return;
}
error("downscaleCLUT8ByHalf(): Unsupported downscale format %s->%s", in->format.toString().c_str(), out->format.toString().c_str());
}
} // end of namespace Graphics

View File

@@ -0,0 +1,40 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_SCALER_DOWNSCALER_H
#define GRAPHICS_SCALER_DOWNSCALER_H
#include "common/scummsys.h"
#include "graphics/surface.h"
namespace Graphics {
/**
* This filter (down)scales the source image by a factor of 1/2.
* For example, a 320x200 image is scaled to 160x100.
*/
extern void downscaleAllByHalf(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
uint32 dstPitch, int width, int height, int gBits = 565);
void downscaleSurfaceByHalf(Surface *out, const Surface *in, const byte *palette);
}
#endif

View File

@@ -0,0 +1,184 @@
@ 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 3 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, see <http://www.gnu.org/licenses/>.
@
@ @author Robin Watts (robin@wss.co.uk)
.text
.global downscaleAllByHalfARM
@ ARM implementation of DownscaleAllByHalf scaler.
@ Scales a width x height block of 16bpp pixels from srcPtr to
@ dstPtr. srcPitch and dstPitch identify how to reach subsequent
@ lines. redblueMask and round allow for one routine to do both
@ 565 and 555 formats.
.align 2
downscaleAllByHalfARM:
@ r0 = srcPtr
@ r1 = srcPitch
@ r2 = dstPtr
@ r3 = dstPitch
MOV r12,r13
STMFD r13!,{r4-r11,r14}
LDMIA r12,{r4-r7}
@ r4 = width
@ r5 = height
@ r6 = redblueMask
@ r7 = round
SUB r3,r3,r4 @ dstPitch -= width
SUBS r5,r5,#2 @ while ((height-=2) >= 0)
BLT end
height_loop:
SUBS r11, r4, #8 @ r11= width_minus_8
BLT thin
width_loop:
@ unroll loop 4 times here
LDRH r8,[r0],r1 @ r8 = A = srcPtr[0]
LDRH r9,[r0],#2 @ r9 = C = srcPtr[dstPitch]
LDRH r12,[r0],-r1 @ r12= D = srcPtr[dstPitch+2]
LDRH r14,[r0],#2 @ r14= B = srcPtr[2]
ORR r8, r8, r8, LSL #16 @ r8 = b | g | r | b | g | r
ORR r9, r9, r9, LSL #16 @ r9 = b | g | r | b | g | r
ORR r12,r12,r12,LSL #16 @ r12= b | g | r | b | g | r
ORR r14,r14,r14,LSL #16 @ r14= b | g | r | b | g | r
AND r8, r8, r6 @ r8 = 0 | g | 0 | b | 0 | r
AND r9, r9, r6 @ r9 = 0 | g | 0 | b | 0 | r
AND r12,r12,r6 @ r12= 0 | g | 0 | b | 0 | r
AND r14,r14,r6 @ r14= 0 | g | 0 | b | 0 | r
ADD r8, r8, r9
ADD r8, r8, r12
ADD r8, r8, r14
ADD r8, r8, r7 @ r8 = summed pixels + rounding
AND r8, r6, r8, LSR #2 @ r8 = 0 | g | 0 | b | 0 | r
ORR r10,r8, r8, LSR #16 @ r10= 0 | g | 0 | b | g | r
LDRH r8,[r0],r1 @ r8 = A = srcPtr[0]
LDRH r9,[r0],#2 @ r9 = C = srcPtr[dstPitch]
LDRH r12,[r0],-r1 @ r12= D = srcPtr[dstPitch+2]
LDRH r14,[r0],#2 @ r14= B = srcPtr[2]
STRH r10,[r2],#2 @ *dstPtr++
ORR r8, r8, r8, LSL #16 @ r8 = b | g | r | b | g | r
ORR r9, r9, r9, LSL #16 @ r9 = b | g | r | b | g | r
ORR r12,r12,r12,LSL #16 @ r12= b | g | r | b | g | r
ORR r14,r14,r14,LSL #16 @ r14= b | g | r | b | g | r
AND r8, r8, r6 @ r8 = 0 | g | 0 | b | 0 | r
AND r9, r9, r6 @ r9 = 0 | g | 0 | b | 0 | r
AND r12,r12,r6 @ r12= 0 | g | 0 | b | 0 | r
AND r14,r14,r6 @ r14= 0 | g | 0 | b | 0 | r
ADD r8, r8, r9
ADD r8, r8, r12
ADD r8, r8, r14
ADD r8, r8, r7 @ r8 = summed pixels + rounding
AND r8, r6, r8, LSR #2 @ r8 = 0 | g | 0 | b | 0 | r
ORR r10,r8, r8, LSR #16 @ r10= 0 | g | 0 | b | g | r
LDRH r8,[r0],r1 @ r8 = A = srcPtr[0]
LDRH r9,[r0],#2 @ r9 = C = srcPtr[dstPitch]
LDRH r12,[r0],-r1 @ r12= D = srcPtr[dstPitch+2]
LDRH r14,[r0],#2 @ r14= B = srcPtr[2]
STRH r10,[r2],#2 @ *dstPtr++
ORR r8, r8, r8, LSL #16 @ r8 = b | g | r | b | g | r
ORR r9, r9, r9, LSL #16 @ r9 = b | g | r | b | g | r
ORR r12,r12,r12,LSL #16 @ r12= b | g | r | b | g | r
ORR r14,r14,r14,LSL #16 @ r14= b | g | r | b | g | r
AND r8, r8, r6 @ r8 = 0 | g | 0 | b | 0 | r
AND r9, r9, r6 @ r9 = 0 | g | 0 | b | 0 | r
AND r12,r12,r6 @ r12= 0 | g | 0 | b | 0 | r
AND r14,r14,r6 @ r14= 0 | g | 0 | b | 0 | r
ADD r8, r8, r9
ADD r8, r8, r12
ADD r8, r8, r14
ADD r8, r8, r7 @ r8 = summed pixels + rounding
AND r8, r6, r8, LSR #2 @ r8 = 0 | g | 0 | b | 0 | r
ORR r10,r8, r8, LSR #16 @ r10= 0 | g | 0 | b | g | r
LDRH r8,[r0],r1 @ r8 = A = srcPtr[0]
LDRH r9,[r0],#2 @ r9 = C = srcPtr[dstPitch]
LDRH r12,[r0],-r1 @ r12= D = srcPtr[dstPitch+2]
LDRH r14,[r0],#2 @ r14= B = srcPtr[2]
STRH r10,[r2],#2 @ *dstPtr++
ORR r8, r8, r8, LSL #16 @ r8 = b | g | r | b | g | r
ORR r9, r9, r9, LSL #16 @ r9 = b | g | r | b | g | r
ORR r12,r12,r12,LSL #16 @ r12= b | g | r | b | g | r
ORR r14,r14,r14,LSL #16 @ r14= b | g | r | b | g | r
AND r8, r8, r6 @ r8 = 0 | g | 0 | b | 0 | r
AND r9, r9, r6 @ r9 = 0 | g | 0 | b | 0 | r
AND r12,r12,r6 @ r12= 0 | g | 0 | b | 0 | r
AND r14,r14,r6 @ r14= 0 | g | 0 | b | 0 | r
ADD r8, r8, r9
ADD r8, r8, r12
ADD r8, r8, r14
ADD r8, r8, r7 @ r8 = summed pixels + rounding
AND r8, r6, r8, LSR #2 @ r8 = 0 | g | 0 | b | 0 | r
ORR r10, r8, r8, LSR #16 @ r8 = 0 | g | 0 | b | g | r
STRH r10,[r2],#2 @ *dstPtr++
SUBS r11,r11,#8 @ width_minus_8 -= 8
BGE width_loop @ (width_minus_8 >= 0) => do 8+ more
thin:
ADDS r11,r11,#8 @ r11= width
BEQ width_end @ if no more left to do, then bail
thin_lp:
@ single output pixels done in this bit
LDRH r8,[r0],r1 @ r8 = A = srcPtr[0]
LDRH r9,[r0],#2 @ r9 = C = srcPtr[dstPitch]
LDRH r12,[r0],-r1 @ r12= D = srcPtr[dstPitch+2]
LDRH r14,[r0],#2 @ r14= B = srcPtr[2]
ORR r8, r8, r8, LSL #16 @ r8 = b | g | r | b | g | r
ORR r9, r9, r9, LSL #16 @ r9 = b | g | r | b | g | r
ORR r12,r12,r12,LSL #16 @ r12= b | g | r | b | g | r
ORR r14,r14,r14,LSL #16 @ r14= b | g | r | b | g | r
AND r8, r8, r6 @ r8 = 0 | g | 0 | b | 0 | r
AND r9, r9, r6 @ r9 = 0 | g | 0 | b | 0 | r
AND r12,r12,r6 @ r12= 0 | g | 0 | b | 0 | r
AND r14,r14,r6 @ r14= 0 | g | 0 | b | 0 | r
ADD r8, r8, r9
ADD r8, r8, r12
ADD r8, r8, r14
ADD r8, r8, r7 @ r8 = summed pixels + rounding
AND r8, r6, r8, LSR #2 @ r8 = 0 | g | 0 | b | 0 | r
ORR r8, r8, r8, LSR #16 @ r8 = 0 | g | 0 | b | g | r
STRH r8,[r2],#2 @ *dstPtr++
SUBS r11,r11,#2
BGT thin_lp
width_end:
ADD r2,r2,r3 @ dstPtr += dstPitch
ADD r0,r0,r1,LSL #1 @ srcPtr += 2*srcPitch
SUB r0,r0,r4,LSL #1 @ srcPtr -= 2*width
SUBS r5,r5,#2
BGE height_loop
end:
LDMFD r13!,{r4-r11,PC}

3643
graphics/scaler/edge.cpp Normal file

File diff suppressed because it is too large Load Diff

173
graphics/scaler/edge.h Normal file
View File

@@ -0,0 +1,173 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_EDGE_H
#define GRAPHICS_SCALER_EDGE_H
#include "graphics/scalerplugin.h"
class EdgeScaler : public SourceScaler {
public:
EdgeScaler(const Graphics::PixelFormat &format);
uint increaseFactor() override;
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<typename ColorMask>
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<typename ColorMask>
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<typename Pixel>
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<typename Pixel>
int refineDirection(char edge_type, Pixel *pixels, int16 *bptr,
int8 *sim, double angle);
/**
* "Chess Knight" patterns can be mis-detected, fix easy cases.
*/
template<typename Pixel>
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<typename ColorMask>
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<typename ColorMask>
void antiAliasGridClean3x(uint8 *dptr, int dstPitch,
typename ColorMask::PixelType *pixels, int sub_type, int16 *bptr);
/**
* Perform edge detection, draw the new 2x pixels
*/
template<typename ColorMask>
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<typename ColorMask>
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

5103
graphics/scaler/hq.cpp Normal file

File diff suppressed because it is too large Load Diff

54
graphics/scaler/hq.h Normal file
View File

@@ -0,0 +1,54 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_HQ_H
#define GRAPHICS_SCALER_HQ_H
#include "graphics/scalerplugin.h"
#ifdef USE_NASM
struct hqx_parameters;
#endif
class HQScaler : public Scaler {
public:
HQScaler(const Graphics::PixelFormat &format);
~HQScaler();
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
void initLUT(Graphics::PixelFormat format);
inline void HQ2x16(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height);
inline void HQ3x16(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height);
inline void HQ2x32(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height);
inline void HQ3x32(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height);
uint32 *_RGBtoYUV;
#ifdef USE_NASM
hqx_parameters *_hqx_params;
#endif
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

526
graphics/scaler/intern.h Normal file
View File

@@ -0,0 +1,526 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_SCALER_INTERN_H
#define GRAPHICS_SCALER_INTERN_H
#include "common/scummsys.h"
#include "graphics/colormasks.h"
/**
* Interpolate two 16 bit pixel *pairs* at once with equal weights 1.
* In particular, p1 and p2 can contain two pixels each in the upper
* and lower halves.
*
* This also works for 32 bit pixels.
*/
template<typename ColorMask>
static inline uint32 interpolate32_1_1(uint32 p1, uint32 p2) {
// Clear the low bit of each channel,
// divide each channel by 2,
// add the two pixels together,
// add 1 to each channel if the lowbits would have added to 2
return (((p1 & ColorMask::kHighBitsMask) >> 1) +
((p2 & ColorMask::kHighBitsMask) >> 1) +
(p1 & p2 & ColorMask::kLowBitsMask));
}
/**
* Interpolate two 16 bit pixel *pairs* at once with weights 3 resp. 1.
* In particular, p1 and p2 can contain two pixels/each in the upper
* and lower halves.
*
* This also works for 32 bit pixels.
*/
template<typename ColorMask>
static inline uint32 interpolate32_3_1(uint32 p1, uint32 p2) {
// Clear the 2 lowest bits of each channel,
// divide each channel by 4, multiply p1 by 3
// add the two pixels together,
uint32 x = ((p1 & ColorMask::qhighBits) >> 2) * 3 + ((p2 & ColorMask::qhighBits) >> 2);
// Get 2 lowest bits of each channel,
// multiply p1 by 3, add them together, then divide by 4
uint32 y = ((p1 & ColorMask::qlowBits) * 3 + (p2 & ColorMask::qlowBits)) >> 2;
// Use only the low bits of the second result to add to the first result
y &= ColorMask::qlowBits;
return x + y;
}
/**
* Interpolate two 32 bit pixels with weights 2 and 1 and 1, i.e., (2*p1+p2)/3.
*/
template<typename ColorMask>
uint32 interpolate32_2_1(uint32 pixel1, uint32 pixel2) {
uint32 rsum, gsum, bsum, asum;
rsum = ((pixel1 & ColorMask::kRedMask) >> ColorMask::kRedShift) << 1;
rsum += ((pixel2 & ColorMask::kRedMask) >> ColorMask::kRedShift);
rsum /= 3;
rsum <<= ColorMask::kRedShift;
gsum = ((pixel1 & ColorMask::kGreenMask) >> ColorMask::kGreenShift) << 1;
gsum += ((pixel2 & ColorMask::kGreenMask) >> ColorMask::kGreenShift);
gsum /= 3;
gsum <<= ColorMask::kGreenShift;
bsum = ((pixel1 & ColorMask::kBlueMask) >> ColorMask::kBlueShift) << 1;
bsum += ((pixel2 & ColorMask::kBlueMask) >> ColorMask::kBlueShift);
bsum /= 3;
bsum <<= ColorMask::kBlueShift;
asum = ((pixel1 & ColorMask::kAlphaMask) >> ColorMask::kAlphaShift) << 1;
asum += ((pixel2 & ColorMask::kAlphaMask) >> ColorMask::kAlphaShift);
asum /= 3;
asum <<= ColorMask::kAlphaShift;
return (rsum & ColorMask::kRedMask) | (gsum & ColorMask::kGreenMask) | (bsum & ColorMask::kBlueMask) | (asum & ColorMask::kAlphaMask);
}
/**
* Interpolate two 32 bit pixels with weights 5 and 3 and 1, i.e., (5*p1+3*p2)/8.
* @see interpolate_32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_5_3(uint32 p1, uint32 p2) {
uint32 x = ((p1 & ~ColorMask::kLow3Bits) >> 3) * 5 + ((p2 & ~ColorMask::kLow3Bits) >> 3) * 3;
uint32 y = ((p1 & ColorMask::kLow3Bits) * 5 + (p2 & ColorMask::kLow3Bits) * 3) >> 3;
y &= ColorMask::kLow3Bits;
return x + y;
}
/**
* Interpolate two 32 bit pixels with weights 7 and 1, i.e., (7*p1+p2)/8.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_7_1(uint32 p1, uint32 p2) {
uint32 x = ((p1 & ~ColorMask::kLow3Bits) >> 3) * 7 + ((p2 & ~ColorMask::kLow3Bits) >> 3);
uint32 y = ((p1 & ColorMask::kLow3Bits) * 7 + (p2 & ColorMask::kLow3Bits)) >> 3;
y &= ColorMask::kLow3Bits;
return x + y;
}
/**
* Interpolate three 32 bit pixels with weights 2, 1, and 1, i.e., (2*p1+p2+p3)/4.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_2_1_1(uint32 p1, uint32 p2, uint32 p3) {
uint32 x = ((p1 & ColorMask::qhighBits) >> 1)
+ ((p2 & ColorMask::qhighBits) >> 2)
+ ((p3 & ColorMask::qhighBits) >> 2);
uint32 y = ((p1 & ColorMask::qlowBits) << 1)
+ (p2 & ColorMask::qlowBits)
+ (p2 & ColorMask::qlowBits);
y >>= 2;
y &= ColorMask::qlowBits;
return x + y;
}
/**
* Interpolate three 32 bit pixels with weights 5, 2, and 1, i.e., (5*p1+2*p2+p3)/8.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_5_2_1(uint32 p1, uint32 p2, uint32 p3) {
uint32 x = ((p1 & ~ColorMask::kLow3Bits) >> 3) * 5
+ ((p2 & ~ColorMask::kLow3Bits) >> 3) * 2
+ ((p3 & ~ColorMask::kLow3Bits) >> 3);
uint32 y = (p1 & ColorMask::kLow3Bits) * 5
+ (p2 & ColorMask::kLow3Bits) * 2
+ (p2 & ColorMask::kLow3Bits);
y >>= 3;
y &= ColorMask::kLow3Bits;
return x + y;
}
/**
* Interpolate three 32 bit pixels with weights 6, 1, and 1, i.e., (6*p1+p2+p3)/8.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_6_1_1(uint32 p1, uint32 p2, uint32 p3) {
uint32 x = ((p1 & ~ColorMask::kLow3Bits) >> 3) * 6
+ ((p2 & ~ColorMask::kLow3Bits) >> 3)
+ ((p3 & ~ColorMask::kLow3Bits) >> 3);
uint32 y = (p1 & ColorMask::kLow3Bits) * 6
+ (p2 & ColorMask::kLow3Bits)
+ (p2 & ColorMask::kLow3Bits);
y >>= 3;
y &= ColorMask::kLow3Bits;
return x + y;
}
/**
* Interpolate three 32 bit pixels with weights 2, 3, and 3, i.e., (2*p1+3*(p2+p3))/8.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_2_3_3(uint32 p1, uint32 p2, uint32 p3) {
uint32 x = ((p1 & ~ColorMask::kLow3Bits) >> 2)
+ (((p2 & ~ColorMask::kLow3Bits) >> 3)
+ ((p3 & ~ColorMask::kLow3Bits) >> 3)) * 3;
uint32 y = (p1 & ColorMask::kLow3Bits) * 2
+ ((p2 & ColorMask::kLow3Bits)
+ (p2 & ColorMask::kLow3Bits)) * 3;
y >>= 3;
y &= ColorMask::kLow3Bits;
return x + y;
}
/**
* Interpolate three 32 bit pixels with weights 2, 7, and 7, i.e., (2*p1+7*(p2+p3))/16.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
inline uint32 interpolate32_2_7_7(uint32 p1, uint32 p2, uint32 p3) {
uint32 x = ((p1 & ~ColorMask::kLow4Bits) >> 3)
+ (((p2 & ~ColorMask::kLow4Bits) >> 4)
+ ((p3 & ~ColorMask::kLow4Bits) >> 4)) * 7;
uint32 y = (p1 & ColorMask::kLow4Bits) * 2
+ ((p2 & ColorMask::kLow4Bits)
+ (p2 & ColorMask::kLow4Bits)) * 7;
y >>= 4;
y &= ColorMask::kLow4Bits;
return x + y;
}
// Dummy specializations.
template<>
inline uint32 interpolate32_2_7_7<Graphics::ColorMasks<555> >(uint32 p1, uint32 p2, uint32 p3) {
assert(0);
return 0;
}
template<>
inline uint32 interpolate32_2_7_7<Graphics::ColorMasks<565> >(uint32 p1, uint32 p2, uint32 p3) {
assert(0);
return 0;
}
/**
* Interpolate three 32 bit pixels with weights 14, 1, and 1, i.e., (14*p1+p2+p3)/16.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
inline uint32 interpolate32_14_1_1(uint32 p1, uint32 p2, uint32 p3) {
uint32 x = ((p1 & ~ColorMask::kLow4Bits) >> 4) * 14
+ ((p2 & ~ColorMask::kLow4Bits) >> 4)
+ ((p3 & ~ColorMask::kLow4Bits) >> 4);
uint32 y = (p1 & ColorMask::kLow4Bits) * 14
+ (p2 & ColorMask::kLow4Bits)
+ (p2 & ColorMask::kLow4Bits);
y >>= 4;
y &= ColorMask::kLow4Bits;
return x + y;
}
// Dummy specializations.
template<>
inline uint32 interpolate32_14_1_1<Graphics::ColorMasks<555> >(uint32 p1, uint32 p2, uint32 p3) {
assert(0);
return 0;
}
template<>
inline uint32 interpolate32_14_1_1<Graphics::ColorMasks<565> >(uint32 p1, uint32 p2, uint32 p3) {
assert(0);
return 0;
}
/**
* Interpolate three 32 bit pixels with weights 1, 1, and 1, i.e., (p1+p2+p3)/3.
*/
template<typename ColorMask>
uint32 interpolate32_1_1_1(uint32 pixel1, uint32 pixel2, uint32 pixel3) {
uint32 rsum, gsum, bsum;
rsum = ((pixel1 & ColorMask::kRedMask) >> ColorMask::kRedShift);
rsum += ((pixel2 & ColorMask::kRedMask) >> ColorMask::kRedShift);
rsum += ((pixel3 & ColorMask::kRedMask) >> ColorMask::kRedShift);
rsum /= 3;
rsum <<= ColorMask::kRedShift;
gsum = ((pixel1 & ColorMask::kGreenMask) >> ColorMask::kGreenShift);
gsum += ((pixel2 & ColorMask::kGreenMask) >> ColorMask::kGreenShift);
gsum += ((pixel3 & ColorMask::kGreenMask) >> ColorMask::kGreenShift);
gsum /= 3;
gsum <<= ColorMask::kGreenShift;
bsum = ((pixel1 & ColorMask::kBlueMask) >> ColorMask::kBlueShift);
bsum += ((pixel2 & ColorMask::kBlueMask) >> ColorMask::kBlueShift);
bsum += ((pixel3 & ColorMask::kBlueMask) >> ColorMask::kBlueShift);
bsum /= 3;
bsum <<= ColorMask::kBlueShift;
return (rsum & ColorMask::kRedMask) | (gsum & ColorMask::kGreenMask) | (bsum & ColorMask::kBlueMask);
}
/**
* Interpolate four 32 bit pixels with weights 1, 1, 1, and 1, i.e., (p1+p2+p3+p4)/4.
*
* @see interpolate32_3_1 for similar method
*/
template<typename ColorMask>
static inline uint32 interpolate32_1_1_1_1(uint32 p1, uint32 p2, uint32 p3, uint32 p4) {
uint32 x = ((p1 & ~ColorMask::kLow2Bits) >> 2)
+ ((p2 & ~ColorMask::kLow2Bits) >> 2)
+ ((p3 & ~ColorMask::kLow2Bits) >> 2)
+ ((p4 & ~ColorMask::kLow2Bits) >> 2);
uint32 y = (p1 & ColorMask::kLow2Bits)
+ (p2 & ColorMask::kLow2Bits)
+ (p3 & ColorMask::kLow2Bits)
+ (p4 & ColorMask::kLow2Bits);
y >>= 2;
y &= ColorMask::kLow2Bits;
return x + y;
}
/**
* Interpolate two 16 bit pixels with weights 1 and 1, i.e., (p1+p2)/2.
* See <https://www.slack.net/~ant/info/rgb_mixing.html> for details on how this works.
*/
template<typename ColorMask>
static inline unsigned interpolate16_1_1(unsigned p1, unsigned p2) {
const unsigned lowbits = (p1 ^ p2) & ColorMask::kLowBits;
return ((p1 + p2) - lowbits) >> 1;
}
/**
* Interpolate two 16 bit pixels with weights 3 and 1, i.e., (3*p1+p2)/4.
*/
template<typename ColorMask>
static inline unsigned interpolate16_3_1(unsigned p1, unsigned p2) {
const unsigned lowbits = (((p1 & ColorMask::kLowBits) << 1) + (p1 & ColorMask::kLow2Bits)
+ (p2 & ColorMask::kLow2Bits)) & ColorMask::kLow2Bits;
return ((p1*3 + p2) - lowbits) >> 2;
}
/**
* Interpolate two 16 bit pixels with weights 2 and 1, i.e., (2*p1+p2)/3.
*/
template<typename ColorMask>
uint16 interpolate16_2_1(uint16 pixel1, uint16 pixel2) {
uint32 rsum;
uint16 gsum, bsum;
rsum = (pixel1 & ColorMask::kRedMask) << 1;
rsum += (pixel2 & ColorMask::kRedMask);
rsum /= 3;
gsum = (pixel1 & ColorMask::kGreenMask) << 1;
gsum += (pixel2 & ColorMask::kGreenMask);
gsum /= 3;
bsum = (pixel1 & ColorMask::kBlueMask) << 1;
bsum += (pixel2 & ColorMask::kBlueMask);
bsum /= 3;
return (rsum & ColorMask::kRedMask) | (gsum & ColorMask::kGreenMask) | (bsum & ColorMask::kBlueMask);
}
/**
* Interpolate two 16 bit pixels with weights 5 and 3 and 1, i.e., (5*p1+3*p2)/8.
*/
template<typename ColorMask>
static inline unsigned interpolate16_5_3(unsigned p1, unsigned p2) {
const unsigned lowbits = (((p1 & ColorMask::kLowBits) << 2) + (p1 & ColorMask::kLow3Bits)
+ ((p2 & ColorMask::kLow2Bits) << 1) + (p2 & ColorMask::kLow3Bits)) & ColorMask::kLow3Bits;
return ((p1*5 + p2*3) - lowbits) >> 3;
}
/**
* Interpolate two 16 bit pixels with weights 7 and 1, i.e., (7*p1+p2)/8.
*/
template<typename ColorMask>
static inline unsigned interpolate16_7_1(unsigned p1, unsigned p2) {
const unsigned lowbits = (((p1 & ColorMask::kLowBits) << 2) + ((p1 & ColorMask::kLow2Bits) << 1) + (p1 & ColorMask::kLow3Bits)
+ (p2 & ColorMask::kLow3Bits)) & ColorMask::kLow3Bits;
return ((p1*7+p2) - lowbits) >> 3;
}
/**
* Interpolate three 16 bit pixels with weights 2, 1, and 1, i.e., (2*p1+p2+p3)/4.
*/
template<typename ColorMask>
static inline unsigned interpolate16_2_1_1(unsigned p1, unsigned p2, unsigned p3) {
p1<<=1;
const unsigned lowbits = ((p1 & (ColorMask::kLowBits << 1))
+ (p2 & ColorMask::kLow2Bits)
+ (p3 & ColorMask::kLow2Bits)) & ColorMask::kLow2Bits;
return ((p1+p2+p3) - lowbits) >> 2;
}
/**
* Interpolate three 16 bit pixels with weights 5, 2, and 1, i.e., (5*p1+2*p2+p3)/8.
*/
template<typename ColorMask>
static inline unsigned interpolate16_5_2_1(unsigned p1, unsigned p2, unsigned p3) {
p2<<=1;
const unsigned lowbits = (((p1 & ColorMask::kLowBits) << 2) + (p1 & ColorMask::kLow3Bits)
+ (p2 & (ColorMask::kLow2Bits << 1))
+ (p3 & ColorMask::kLow3Bits)) & ColorMask::kLow3Bits;
return ((p1*5 + p2 + p3) - lowbits) >> 3;
}
/**
* Interpolate three 16 bit pixels with weights 6, 1, and 1, i.e., (6*p1+p2+p3)/8.
*/
template<typename ColorMask>
static inline unsigned interpolate16_6_1_1(unsigned p1, unsigned p2, unsigned p3) {
const unsigned lowbits = (((((p1 & ColorMask::kLowBits) << 1) + (p1 & ColorMask::kLow2Bits)) << 1)
+ (p2 & ColorMask::kLow3Bits)
+ (p3 & ColorMask::kLow3Bits)) & ColorMask::kLow3Bits;
return ((p1*6 + p2 + p3) - lowbits) >> 3;
}
/**
* Interpolate three 16 bit pixels with weights 2, 3, and 3, i.e., (2*p1+3*(p2+p3))/8.
*/
template<typename ColorMask>
static inline unsigned interpolate16_2_3_3(unsigned p1, unsigned p2, unsigned p3) {
p1 <<= 1;
const unsigned rb = (p1 & (ColorMask::kRedBlueMask<<1))
+ ((p2 & ColorMask::kRedBlueMask) + (p3 & ColorMask::kRedBlueMask))*3;
const unsigned g = (p1 & (ColorMask::kGreenMask<<1))
+ ((p2 & ColorMask::kGreenMask) + (p3 & ColorMask::kGreenMask))*3;
return ((rb & (ColorMask::kRedBlueMask<<3)) | (g & (ColorMask::kGreenMask<<3))) >> 3;
}
/**
* Interpolate three 16 bit pixels with weights 2, 7, and 7, i.e., (2*p1+7*(p2+p3))/16.
*/
template<typename ColorMask>
static inline unsigned interpolate16_2_7_7(unsigned p1, unsigned p2, unsigned p3) {
p1 <<= 1;
const unsigned rb = (p1 & (ColorMask::kRedBlueMask<<1))
+ ((p2 & ColorMask::kRedBlueMask) + (p3 & ColorMask::kRedBlueMask))*7;
const unsigned g = (p1 & (ColorMask::kGreenMask<<1))
+ ((p2 & ColorMask::kGreenMask) + (p3 & ColorMask::kGreenMask))*7;
return ((rb & (ColorMask::kRedBlueMask<<4)) | (g & (ColorMask::kGreenMask<<4))) >> 4;
}
/**
* Interpolate three 16 bit pixels with weights 14, 1, and 1, i.e., (14*p1+p2+p3)/16.
*/
template<typename ColorMask>
static inline unsigned interpolate16_14_1_1(unsigned p1, unsigned p2, unsigned p3) {
const unsigned rb = (p1&ColorMask::kRedBlueMask)*14
+ (p2&ColorMask::kRedBlueMask)
+ (p3&ColorMask::kRedBlueMask);
const unsigned g = (p1&ColorMask::kGreenMask)*14
+ (p2&ColorMask::kGreenMask) + (p3&ColorMask::kGreenMask);
return ((rb&(ColorMask::kRedBlueMask<<4)) | (g&(ColorMask::kGreenMask<<4))) >> 4;
}
/**
* Interpolate three 16 bit pixels with weights 1, 1, and 1, i.e., (p1+p2+p3)/3.
*/
template<typename ColorMask>
uint16 interpolate16_1_1_1(uint16 pixel1, uint16 pixel2, uint16 pixel3)
{
uint32 rsum;
uint16 gsum, bsum;
rsum = (pixel1 & ColorMask::kRedMask);
rsum += (pixel2 & ColorMask::kRedMask);
rsum += (pixel3 & ColorMask::kRedMask);
rsum /= 3;
gsum = (pixel1 & ColorMask::kGreenMask);
gsum += (pixel2 & ColorMask::kGreenMask);
gsum += (pixel3 & ColorMask::kGreenMask);
gsum /= 3;
bsum = (pixel1 & ColorMask::kBlueMask);
bsum += (pixel2 & ColorMask::kBlueMask);
bsum += (pixel3 & ColorMask::kBlueMask);
bsum /= 3;
return (rsum & ColorMask::kRedMask) | (gsum & ColorMask::kGreenMask) | (bsum & ColorMask::kBlueMask);
}
/**
* Interpolate four 16 bit pixels with weights 1, 1, 1, and 1, i.e., (p1+p2+p3+p4)/4.
*/
template<typename ColorMask>
static inline unsigned interpolate16_1_1_1_1(unsigned p1, unsigned p2, unsigned p3, unsigned p4) {
const unsigned lowbits = ((p1 & ColorMask::kLow2Bits)
+ (p2 & ColorMask::kLow2Bits)
+ (p3 & ColorMask::kLow2Bits)
+ (p4 & ColorMask::kLow2Bits)) & ColorMask::kLow2Bits;
return ((p1+p2+p3+p4) - lowbits) >> 2;
}
/**
* Compare two YUV values (encoded 8-8-8) and check if they differ by more than
* a certain hard coded threshold. Used by the hq scaler family.
*/
static inline bool diffYUV(int yuv1, int yuv2) {
static const int Ymask = 0x00FF0000;
static const int Umask = 0x0000FF00;
static const int Vmask = 0x000000FF;
static const int trY = 0x00300000;
static const int trU = 0x00000700;
static const int trV = 0x00000006;
int diff;
int mask;
diff = ((yuv1 & Umask) - (yuv2 & Umask));
mask = diff >> 31; // -1 if value < 0, 0 otherwise
diff = (diff ^ mask) - mask; // -1: ~value + 1; 0: value
if (diff > trU) return true;
diff = ((yuv1 & Vmask) - (yuv2 & Vmask));
mask = diff >> 31; // -1 if value < 0, 0 otherwise
diff = (diff ^ mask) - mask; // -1: ~value + 1; 0: value
if (diff > trV) return true;
diff = ((yuv1 & Ymask) - (yuv2 & Ymask));
mask = diff >> 31; // -1 if value < 0, 0 otherwise
diff = (diff ^ mask) - mask; // -1: ~value + 1; 0: value
if (diff > trY) return true;
return false;
/*
return
( ( ABS((yuv1 & Ymask) - (yuv2 & Ymask)) > trY ) ||
( ABS((yuv1 & Umask) - (yuv2 & Umask)) > trU ) ||
( ABS((yuv1 & Vmask) - (yuv2 & Vmask)) > trV ) );
*/
}
#endif

316
graphics/scaler/normal.cpp Normal file
View File

@@ -0,0 +1,316 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "graphics/scaler/normal.h"
#ifdef USE_SCALERS
/**
* Trivial nearest-neighbor 2x scaler.
*/
template<typename Pixel>
void Normal2x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
uint8 *r;
const int b = sizeof(Pixel);
assert(IS_ALIGNED(dstPtr, 2));
while (height--) {
r = dstPtr;
for (int i = 0; i < width; ++i, r += b * 2) {
Pixel color = *(((const Pixel*)srcPtr) + i);
*(Pixel *)(r) = color;
*(Pixel *)(r + b) = color;
*(Pixel *)(r + dstPitch) = color;
*(Pixel *)(r + b + dstPitch) = color;
}
srcPtr += srcPitch;
dstPtr += dstPitch << 1;
}
}
#ifdef USE_ARM_SCALER_ASM
extern "C" void Normal2xARM(const uint8 *srcPtr,
uint32 srcPitch,
uint8 *dstPtr,
uint32 dstPitch,
int width,
int height);
#else
/**
* Template Specialization that writes 2 pixels at a time.
*/
template<>
void Normal2x<uint16>(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
uint8 *r;
assert(IS_ALIGNED(dstPtr, 4));
while (height--) {
r = dstPtr;
for (int i = 0; i < width; ++i, r += 4) {
uint32 color = *(((const uint16*)srcPtr) + i);
color |= color << 16;
*(uint32 *)(r) = color;
*(uint32 *)(r + dstPitch) = color;
}
srcPtr += srcPitch;
dstPtr += dstPitch << 1;
}
}
#endif
/**
* Trivial nearest-neighbor 3x scaler.
*/
template<typename Pixel>
void Normal3x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
uint8 *r;
const uint32 dstPitch2 = dstPitch * 2;
const uint32 dstPitch3 = dstPitch * 3;
const int b = sizeof(Pixel);
assert(IS_ALIGNED(dstPtr, 2));
while (height--) {
r = dstPtr;
for (int i = 0; i < width; ++i, r += b * 3) {
Pixel color = *(((const Pixel *)srcPtr) + i);
*(Pixel *)(r + b * 0) = color;
*(Pixel *)(r + b * 1) = color;
*(Pixel *)(r + b * 2) = color;
*(Pixel *)(r + b * 0 + dstPitch) = color;
*(Pixel *)(r + b * 1 + dstPitch) = color;
*(Pixel *)(r + b * 2 + dstPitch) = color;
*(Pixel *)(r + b * 0 + dstPitch2) = color;
*(Pixel *)(r + b * 1 + dstPitch2) = color;
*(Pixel *)(r + b * 2 + dstPitch2) = color;
}
srcPtr += srcPitch;
dstPtr += dstPitch3;
}
}
/**
* Trivial nearest-neighbor 4x scaler.
*/
template<typename Pixel>
void Normal4x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
uint8 *r;
const uint32 dstPitch2 = dstPitch * 2;
const uint32 dstPitch3 = dstPitch * 3;
const uint32 dstPitch4 = dstPitch * 4;
const int b = sizeof(Pixel);
assert(IS_ALIGNED(dstPtr, 2));
while (height--) {
r = dstPtr;
for (int i = 0; i < width; ++i, r += b * 4) {
Pixel color = *(((const Pixel *)srcPtr) + i);
*(Pixel *)(r + b * 0) = color;
*(Pixel *)(r + b * 1) = color;
*(Pixel *)(r + b * 2) = color;
*(Pixel *)(r + b * 3) = color;
*(Pixel *)(r + b * 0 + dstPitch) = color;
*(Pixel *)(r + b * 1 + dstPitch) = color;
*(Pixel *)(r + b * 2 + dstPitch) = color;
*(Pixel *)(r + b * 3 + dstPitch) = color;
*(Pixel *)(r + b * 0 + dstPitch2) = color;
*(Pixel *)(r + b * 1 + dstPitch2) = color;
*(Pixel *)(r + b * 2 + dstPitch2) = color;
*(Pixel *)(r + b * 3 + dstPitch2) = color;
*(Pixel *)(r + b * 0 + dstPitch3) = color;
*(Pixel *)(r + b * 1 + dstPitch3) = color;
*(Pixel *)(r + b * 2 + dstPitch3) = color;
*(Pixel *)(r + b * 3 + dstPitch3) = color;
}
srcPtr += srcPitch;
dstPtr += dstPitch4;
}
}
/**
* Trivial nearest-neighbor 5x scaler.
*/
template<typename Pixel>
void Normal5x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
uint8 *r;
const uint32 dstPitch2 = dstPitch * 2;
const uint32 dstPitch3 = dstPitch * 3;
const uint32 dstPitch4 = dstPitch * 4;
const uint32 dstPitch5 = dstPitch * 5;
const int b = sizeof(Pixel);
assert(IS_ALIGNED(dstPtr, 2));
while (height--) {
r = dstPtr;
for (int i = 0; i < width; ++i, r += b * 5) {
Pixel color = *(((const Pixel *)srcPtr) + i);
*(Pixel *)(r + b * 0) = color;
*(Pixel *)(r + b * 1) = color;
*(Pixel *)(r + b * 2) = color;
*(Pixel *)(r + b * 3) = color;
*(Pixel *)(r + b * 4) = color;
*(Pixel *)(r + b * 0 + dstPitch) = color;
*(Pixel *)(r + b * 1 + dstPitch) = color;
*(Pixel *)(r + b * 2 + dstPitch) = color;
*(Pixel *)(r + b * 3 + dstPitch) = color;
*(Pixel *)(r + b * 4 + dstPitch) = color;
*(Pixel *)(r + b * 0 + dstPitch2) = color;
*(Pixel *)(r + b * 1 + dstPitch2) = color;
*(Pixel *)(r + b * 2 + dstPitch2) = color;
*(Pixel *)(r + b * 3 + dstPitch2) = color;
*(Pixel *)(r + b * 4 + dstPitch2) = color;
*(Pixel *)(r + b * 0 + dstPitch3) = color;
*(Pixel *)(r + b * 1 + dstPitch3) = color;
*(Pixel *)(r + b * 2 + dstPitch3) = color;
*(Pixel *)(r + b * 3 + dstPitch3) = color;
*(Pixel *)(r + b * 4 + dstPitch3) = color;
*(Pixel *)(r + b * 0 + dstPitch4) = color;
*(Pixel *)(r + b * 1 + dstPitch4) = color;
*(Pixel *)(r + b * 2 + dstPitch4) = color;
*(Pixel *)(r + b * 3 + dstPitch4) = color;
*(Pixel *)(r + b * 4 + dstPitch4) = color;
}
srcPtr += srcPitch;
dstPtr += dstPitch5;
}
}
#endif
void NormalScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
#ifdef USE_SCALERS
if (_format.bytesPerPixel == 1) {
switch (_factor) {
case 2:
Normal2x<uint8>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 3:
Normal3x<uint8>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 4:
Normal4x<uint8>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 5:
Normal5x<uint8>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
}
} else if (_format.bytesPerPixel == 2) {
switch (_factor) {
case 2:
#ifdef USE_ARM_SCALER_ASM
Normal2xARM(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
#else
Normal2x<uint16>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
#endif
break;
case 3:
Normal3x<uint16>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 4:
Normal4x<uint16>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 5:
Normal5x<uint16>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
}
} else {
assert(_format.bytesPerPixel == 4);
switch (_factor) {
case 2:
Normal2x<uint32>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 3:
Normal3x<uint32>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 4:
Normal4x<uint32>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
case 5:
Normal5x<uint32>(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
break;
}
}
#endif
}
uint NormalScaler::increaseFactor() {
#ifdef USE_SCALERS
if (_factor < 5)
setFactor(_factor + 1);
#endif
return _factor;
}
uint NormalScaler::decreaseFactor() {
#ifdef USE_SCALERS
if (_factor > 1)
setFactor(_factor - 1);
#endif
return _factor;
}
class NormalPlugin final : public ScalerPluginObject {
public:
NormalPlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return true; }
uint extraPixels() const override { return 0; }
const char *getName() const override;
const char *getPrettyName() const override;
};
NormalPlugin::NormalPlugin() {
_factors.push_back(1);
#ifdef USE_SCALERS
_factors.push_back(2);
_factors.push_back(3);
_factors.push_back(4);
_factors.push_back(5);
#endif
}
Scaler *NormalPlugin::createInstance(const Graphics::PixelFormat &format) const {
return new NormalScaler(format);
}
const char *NormalPlugin::getName() const {
return "normal";
}
const char *NormalPlugin::getPrettyName() const {
return "Normal";
}
REGISTER_PLUGIN_STATIC(NORMAL, PLUGIN_TYPE_SCALER, NormalPlugin);

37
graphics/scaler/normal.h Normal file
View File

@@ -0,0 +1,37 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_NORMAL_H
#define GRAPHICS_SCALER_NORMAL_H
#include "graphics/scalerplugin.h"
class NormalScaler : public Scaler {
public:
NormalScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 1; }
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
};
#endif

241
graphics/scaler/pm.cpp Normal file
View File

@@ -0,0 +1,241 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "graphics/scaler/pm.h"
#include "graphics/scaler/intern.h"
//
// Code taken from Pablo Medina (aka "pm") (pjmedina3@yahoo.com)
// You can find it here: https://web.archive.org/web/20051018114450/http://2xpm.freeservers.com/
// Thanks for his great work
// Implemented and fixed for ScummVM by Johannes Schickel (aka "LordHoto") (lordhoto [at] gmail [dot] com)
//
#define interpolate_1_1(a,b) (ColorMask::kBytesPerPixel == 2 ? interpolate16_1_1<ColorMask>(a,b) : interpolate32_1_1<ColorMask>(a,b))
#define interpolate_3_1(a,b) (ColorMask::kBytesPerPixel == 2 ? interpolate16_3_1<ColorMask>(a,b) : interpolate32_3_1<ColorMask>(a,b))
template<typename ColorMask>
void scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
typedef typename ColorMask::PixelType Pixel;
uint32 nextLineSrc = srcPitch / sizeof(Pixel);
uint32 nextLine = dstPitch / sizeof(Pixel);
uint32 completeLineSrc = nextLineSrc - width;
uint32 completeLine = nextLine - (2*width);
const Pixel *startAddr2 = (const Pixel*)srcPtr;
const Pixel *startAddr1 = startAddr2 - nextLineSrc;
const Pixel *startAddr3 = startAddr2 + nextLineSrc;
Pixel *dstPixel = (Pixel*)dstPtr;
int y = height;
Pixel E[4] = { 0, 0, 0, 0 };
while (y--) {
int x = width;
while (x--) {
//
// pA pB pC
// pD pE pF
// pG pH pI
//
Pixel pB = startAddr1[0];
Pixel pE = startAddr2[0];
Pixel pH = startAddr3[0];
Pixel pA = startAddr1[-1];
Pixel pD = startAddr2[-1];
Pixel pG = startAddr3[-1];
Pixel pC = startAddr1[1];
Pixel pF = startAddr2[1];
Pixel pI = startAddr3[1];
bool doNotReblit = false;
E[0] = E[1] = E[2] = E[3] = pE;
if (!doNotReblit) {
if (pD != pF) {
if ((pE != pD) && (pD == pH) && (pD == pI) && (pE != pG)
&& ((pD != pG) || (pE != pF) || (pA != pD))
&& (!((pD == pA) && (pD == pG) && (pE == pB) && (pE == pF)))) {
E[2] = pH;
E[3] = interpolate_1_1(E[3], pH);
doNotReblit = true;
} else if ((pE != pF) && (pF == pH) && (pF == pG) && (pE != pI)
&& ((pF != pI) || (pE != pD) || (pC != pF))
&& (!((pF == pC) && (pF == pI) && (pE == pB) && (pE == pD)))) {
E[2] = interpolate_1_1(E[2], pH);
E[3] = pH;
doNotReblit = true;
}
}
if (pB != pH) {
if (pE != pB) {
if ((pA != pB) || (pB != pC) || (pE != pH)) {
if ((pB == pD) && (pB == pG) && (pE != pA)
&& (!((pD == pA) && (pD == pC) && (pE == pH) && (pE == pF)))) {
E[0] = interpolate_3_1( pB,E[0]);
E[2] = interpolate_3_1(E[2], pB);
doNotReblit = true;
} else if ((pB == pF) && (pB == pI) && (pE != pC)
&& (!((pF == pC) && (pF == pA) && (pE == pH) && (pE == pD)))) {
E[1] = interpolate_3_1(pB, E[1]);
E[3] = interpolate_3_1(E[3], pB);
doNotReblit = true;
}
}
}
if (pE != pH) {
if ((pG != pH) || (pE != pB) || (pH != pI)) {
if ((pH == pD) && (pH == pA) && (pE != pG)
&& (!((pD == pG) && (pD == pI) && (pE == pB) && (pE == pF)))) {
E[2] = interpolate_3_1( pH,E[2]);
E[0] = interpolate_3_1(E[0], pH);
doNotReblit = true;
} else if ((pH == pF) && (pH == pC) && (pE != pI)
&& (!((pF == pI) && (pF == pG) && (pE == pB) && (pE == pD)))) {
E[3] = interpolate_3_1( pH,E[3]);
E[1] = interpolate_3_1(E[1], pH);
doNotReblit = true;
}
}
}
}
}
if (!doNotReblit) {
if ((pB != pH) && (pD != pF)) {
if ((pB == pD) && (pE != pD)
&& (!((pE == pA) && (pB == pC) && (pE == pF))) // Block
&& (!((pB == pA) && (pB == pG)))
&& (!((pD == pA) && (pD == pC) && (pE == pF) && (pG != pD) && (pG != pE))))
E[0] = interpolate_1_1(E[0], pB);
if ((pB == pF) && (pE != pF)
&& (!((pE == pC) && (pB == pA) && (pE == pD))) // Block
&& (!((pB == pC) && (pB == pI)))
&& (!((pF == pA) && (pF == pC) && (pE == pD) && (pI != pF) && (pI != pE))))
E[1] = interpolate_1_1(E[1], pB);
if ((pH == pD) && ((pE != pG) || (pE != pD))
&& (!((pE == pG) && (pH == pI) && (pE == pF))) // Block
&& (!((pH == pG) && (pH == pA)))
&& (!((pD == pG) && (pD == pI) && (pE == pF) && (pA != pD) && (pA != pE))))
E[2] = interpolate_1_1(E[2], pH);
if ((pH == pF) && ((pE != pI) || (pE != pF))
&& (!((pE == pI) && (pH == pG) && (pE == pD))) // Block
&& (!((pH == pI) && (pH == pC)))
&& (!((pF == pG) && (pF == pI) && (pE == pD) && (pC != pF) && (pI != pE))))
E[3] = interpolate_1_1(E[3], pH);
} else if ((pD == pB) && (pD == pF) && (pD == pH) && (pD != pE)) {
if ((pD == pG) || (pD == pC)) {
E[1] = interpolate_1_1(E[1], pD);
E[2] = E[1];
}
if ((pD == pA) || (pD == pI)) {
E[0] = interpolate_1_1(E[0], pD);
E[3] = E[0];
}
}
}
dstPixel[0] = E[0];
dstPixel[1] = E[1];
dstPixel[nextLine] = E[2];
dstPixel[nextLine + 1] = E[3];
startAddr1++;
startAddr2++;
startAddr3++;
dstPixel += 2;
}
startAddr2 += completeLineSrc;
startAddr1 = startAddr2 - nextLineSrc;
startAddr3 = startAddr2 + nextLineSrc;
dstPixel += completeLine + nextLine;
}
}
void PMScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_format.bytesPerPixel == 2) {
if (_format.gLoss == 2)
::scaleIntern<Graphics::ColorMasks<565> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
::scaleIntern<Graphics::ColorMasks<555> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
} else {
if (_format.aLoss == 0)
::scaleIntern<Graphics::ColorMasks<8888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
::scaleIntern<Graphics::ColorMasks<888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
}
}
uint PMScaler::increaseFactor() {
return _factor;
}
uint PMScaler::decreaseFactor() {
return _factor;
}
class PMPlugin final : public ScalerPluginObject {
public:
PMPlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return false; }
uint extraPixels() const override { return 1; }
const char *getName() const override;
const char *getPrettyName() const override;
};
PMPlugin::PMPlugin() {
_factors.push_back(2);
}
Scaler *PMPlugin::createInstance(const Graphics::PixelFormat &format) const {
return new PMScaler(format);
}
const char *PMPlugin::getName() const {
return "pm";
}
const char *PMPlugin::getPrettyName() const {
return "PM";
}
REGISTER_PLUGIN_STATIC(PM, PLUGIN_TYPE_SCALER, PMPlugin);

36
graphics/scaler/pm.h Normal file
View File

@@ -0,0 +1,36 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_PM_H
#define GRAPHICS_SCALER_PM_H
#include "graphics/scalerplugin.h"
class PMScaler : public Scaler {
public:
PMScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
};
#endif

558
graphics/scaler/sai.cpp Normal file
View File

@@ -0,0 +1,558 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "graphics/scaler/sai.h"
#include "graphics/scaler/intern.h"
static inline int GetResult(uint32 A, uint32 B, uint32 C, uint32 D) {
const bool ac = (A==C);
const bool bc = (B==C);
const int x1 = ac;
const int y1 = (bc & !ac);
const bool ad = (A==D);
const bool bd = (B==D);
const int x2 = ad;
const int y2 = (bd & !ad);
const int x = x1+x2;
const int y = y1+y2;
return (y>>1) - (x>>1);
}
#define interpolate_1_1(a,b) (ColorMask::kBytesPerPixel == 2 ? interpolate16_1_1<ColorMask>(a,b) : interpolate32_1_1<ColorMask>(a,b))
#define interpolate_3_1(a,b) (ColorMask::kBytesPerPixel == 2 ? interpolate16_3_1<ColorMask>(a,b) : interpolate32_3_1<ColorMask>(a,b))
#define interpolate_6_1_1(a,b,c) (ColorMask::kBytesPerPixel == 2 ? interpolate16_6_1_1<ColorMask>(a,b,c) : interpolate32_6_1_1<ColorMask>(a,b,c))
#define interpolate_1_1_1_1(a,b,c,d) (ColorMask::kBytesPerPixel == 2 ? interpolate16_1_1_1_1<ColorMask>(a,b,c,d) : interpolate32_1_1_1_1<ColorMask>(a,b,c,d))
template<typename ColorMask>
void Super2xSaITemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
typedef typename ColorMask::PixelType Pixel;
const Pixel *bP;
Pixel *dP;
const uint32 nextlineSrc = srcPitch / sizeof(Pixel);
while (height--) {
bP = (const Pixel *)srcPtr;
dP = (Pixel *)dstPtr;
for (int i = 0; i < width; ++i) {
unsigned color4, color5, color6;
unsigned color1, color2, color3;
unsigned colorA0, colorA1, colorA2, colorA3;
unsigned colorB0, colorB1, colorB2, colorB3;
unsigned colorS1, colorS2;
unsigned product1a, product1b, product2a, product2b;
//--------------------------------------- B1 B2
// 4 5 6 S2
// 1 2 3 S1
// A1 A2
colorB0 = *(bP - nextlineSrc - 1);
colorB1 = *(bP - nextlineSrc);
colorB2 = *(bP - nextlineSrc + 1);
colorB3 = *(bP - nextlineSrc + 2);
color4 = *(bP - 1);
color5 = *(bP);
color6 = *(bP + 1);
colorS2 = *(bP + 2);
color1 = *(bP + nextlineSrc - 1);
color2 = *(bP + nextlineSrc);
color3 = *(bP + nextlineSrc + 1);
colorS1 = *(bP + nextlineSrc + 2);
colorA0 = *(bP + 2 * nextlineSrc - 1);
colorA1 = *(bP + 2 * nextlineSrc);
colorA2 = *(bP + 2 * nextlineSrc + 1);
colorA3 = *(bP + 2 * nextlineSrc + 2);
//--------------------------------------
if (color2 == color6 && color5 != color3) {
product2b = product1b = color2;
} else if (color5 == color3 && color2 != color6) {
product2b = product1b = color5;
} else if (color5 == color3 && color2 == color6) {
int r = 0;
r += GetResult(color6, color5, color1, colorA1);
r += GetResult(color6, color5, color4, colorB1);
r += GetResult(color6, color5, colorA2, colorS1);
r += GetResult(color6, color5, colorB2, colorS2);
if (r > 0)
product2b = product1b = color6;
else if (r < 0)
product2b = product1b = color5;
else {
product2b = product1b = interpolate_1_1(color5, color6);
}
} else {
if (color6 == color3 && color3 == colorA1 && color2 != colorA2 && color3 != colorA0)
product2b = interpolate_3_1(color3, color2);
else if (color5 == color2 && color2 == colorA2 && colorA1 != color3 && color2 != colorA3)
product2b = interpolate_3_1(color2, color3);
else
product2b = interpolate_1_1(color2, color3);
if (color6 == color3 && color6 == colorB1 && color5 != colorB2 && color6 != colorB0)
product1b = interpolate_3_1(color6, color5);
else if (color5 == color2 && color5 == colorB2 && colorB1 != color6 && color5 != colorB3)
product1b = interpolate_3_1(color5, color6);
else
product1b = interpolate_1_1(color5, color6);
}
if (color5 == color3 && color2 != color6 && color4 == color5 && color5 != colorA2)
product2a = interpolate_1_1(color2, color5);
else if (color5 == color1 && color6 == color5 && color4 != color2 && color5 != colorA0)
product2a = interpolate_1_1(color2, color5);
else
product2a = color2;
if (color2 == color6 && color5 != color3 && color1 == color2 && color2 != colorB2)
product1a = interpolate_1_1(color2, color5);
else if (color4 == color2 && color3 == color2 && color1 != color5 && color2 != colorB0)
product1a = interpolate_1_1(color2, color5);
else
product1a = color5;
*(dP + 0) = (Pixel) product1a;
*(dP + 1) = (Pixel) product1b;
*(dP + dstPitch / sizeof(Pixel) + 0) = (Pixel) product2a;
*(dP + dstPitch / sizeof(Pixel) + 1) = (Pixel) product2b;
bP += 1;
dP += 2;
}
srcPtr += srcPitch;
dstPtr += dstPitch * 2;
}
}
template<typename ColorMask>
void SuperEagleTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
typedef typename ColorMask::PixelType Pixel;
const Pixel *bP;
Pixel *dP;
const uint32 nextlineSrc = srcPitch / sizeof(Pixel);
while (height--) {
bP = (const Pixel *)srcPtr;
dP = (Pixel *)dstPtr;
for (int i = 0; i < width; ++i) {
unsigned color4, color5, color6;
unsigned color1, color2, color3;
unsigned colorA1, colorA2, colorB1, colorB2, colorS1, colorS2;
unsigned product1a, product1b, product2a, product2b;
colorB1 = *(bP - nextlineSrc);
colorB2 = *(bP - nextlineSrc + 1);
color4 = *(bP - 1);
color5 = *(bP);
color6 = *(bP + 1);
colorS2 = *(bP + 2);
color1 = *(bP + nextlineSrc - 1);
color2 = *(bP + nextlineSrc);
color3 = *(bP + nextlineSrc + 1);
colorS1 = *(bP + nextlineSrc + 2);
colorA1 = *(bP + 2 * nextlineSrc);
colorA2 = *(bP + 2 * nextlineSrc + 1);
// --------------------------------------
if (color5 != color3) {
if (color2 == color6) {
product1b = product2a = color2;
if ((color1 == color2) || (color6 == colorB2)) {
product1a = interpolate_3_1(color2, color5);
} else {
product1a = interpolate_1_1(color5, color6);
}
if ((color6 == colorS2) || (color2 == colorA1)) {
product2b = interpolate_3_1(color2, color3);
} else {
product2b = interpolate_1_1(color2, color3);
}
} else {
product2b = interpolate_6_1_1(color3, color2, color6);
product1a = interpolate_6_1_1(color5, color2, color6);
product2a = interpolate_6_1_1(color2, color5, color3);
product1b = interpolate_6_1_1(color6, color5, color3);
}
} else {
if (color2 != color6) {
product2b = product1a = color5;
if ((colorB1 == color5) || (color3 == colorS1)) {
product1b = interpolate_3_1(color5, color6);
} else {
product1b = interpolate_1_1(color5, color6);
}
if ((color3 == colorA2) || (color4 == color5)) {
product2a = interpolate_3_1(color5, color2);
} else {
product2a = interpolate_1_1(color2, color3);
}
} else {
int r = 0;
r += GetResult(color6, color5, color1, colorA1);
r += GetResult(color6, color5, color4, colorB1);
r += GetResult(color6, color5, colorA2, colorS1);
r += GetResult(color6, color5, colorB2, colorS2);
if (r > 0) {
product1b = product2a = color2;
product1a = product2b = interpolate_1_1(color5, color6);
} else if (r < 0) {
product2b = product1a = color5;
product1b = product2a = interpolate_1_1(color5, color6);
} else {
product2b = product1a = color5;
product1b = product2a = color2;
}
}
}
*(dP + 0) = (Pixel) product1a;
*(dP + 1) = (Pixel) product1b;
*(dP + dstPitch / sizeof(Pixel) + 0) = (Pixel) product2a;
*(dP + dstPitch / sizeof(Pixel) + 1) = (Pixel) product2b;
bP += 1;
dP += 2;
}
srcPtr += srcPitch;
dstPtr += dstPitch * 2;
}
}
template<typename ColorMask>
void _2xSaITemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
typedef typename ColorMask::PixelType Pixel;
const Pixel *bP;
Pixel *dP;
const uint32 nextlineSrc = srcPitch / sizeof(Pixel);
while (height--) {
bP = (const Pixel *)srcPtr;
dP = (Pixel *)dstPtr;
for (int i = 0; i < width; ++i) {
unsigned colorA, colorB;
unsigned colorC, colorD,
colorE, colorF, colorG, colorH, colorI, colorJ, colorK, colorL, colorM, colorN, colorO;
unsigned product, product1, product2;
//---------------------------------------
// Map of the pixels: I|E F|J
// G|A B|K
// H|C D|L
// M|N O|P
colorI = *(bP - nextlineSrc - 1);
colorE = *(bP - nextlineSrc);
colorF = *(bP - nextlineSrc + 1);
colorJ = *(bP - nextlineSrc + 2);
colorG = *(bP - 1);
colorA = *(bP);
colorB = *(bP + 1);
colorK = *(bP + 2);
colorH = *(bP + nextlineSrc - 1);
colorC = *(bP + nextlineSrc);
colorD = *(bP + nextlineSrc + 1);
colorL = *(bP + nextlineSrc + 2);
colorM = *(bP + 2 * nextlineSrc - 1);
colorN = *(bP + 2 * nextlineSrc);
colorO = *(bP + 2 * nextlineSrc + 1);
if ((colorA == colorD) && (colorB != colorC)) {
if (((colorA == colorE) && (colorB == colorL)) ||
((colorA == colorC) && (colorA == colorF) && (colorB != colorE) && (colorB == colorJ))) {
product = colorA;
} else {
product = interpolate_1_1(colorA, colorB);
}
if (((colorA == colorG) && (colorC == colorO)) ||
((colorA == colorB) && (colorA == colorH) && (colorG != colorC) && (colorC == colorM))) {
product1 = colorA;
} else {
product1 = interpolate_1_1(colorA, colorC);
}
product2 = colorA;
} else if ((colorB == colorC) && (colorA != colorD)) {
if (((colorB == colorF) && (colorA == colorH)) ||
((colorB == colorE) && (colorB == colorD) && (colorA != colorF) && (colorA == colorI))) {
product = colorB;
} else {
product = interpolate_1_1(colorA, colorB);
}
if (((colorC == colorH) && (colorA == colorF)) ||
((colorC == colorG) && (colorC == colorD) && (colorA != colorH) && (colorA == colorI))) {
product1 = colorC;
} else {
product1 = interpolate_1_1(colorA, colorC);
}
product2 = colorB;
} else if ((colorA == colorD) && (colorB == colorC)) {
if (colorA == colorB) {
product = colorA;
product1 = colorA;
product2 = colorA;
} else {
int r = 0;
product1 = interpolate_1_1(colorA, colorC);
product = interpolate_1_1(colorA, colorB);
r += GetResult(colorA, colorB, colorG, colorE);
r -= GetResult(colorB, colorA, colorK, colorF);
r -= GetResult(colorB, colorA, colorH, colorN);
r += GetResult(colorA, colorB, colorL, colorO);
if (r > 0)
product2 = colorA;
else if (r < 0)
product2 = colorB;
else {
product2 = interpolate_1_1_1_1(colorA, colorB, colorC, colorD);
}
}
} else {
product2 = interpolate_1_1_1_1(colorA, colorB, colorC, colorD);
if ((colorA == colorC) && (colorA == colorF)
&& (colorB != colorE) && (colorB == colorJ)) {
product = colorA;
} else if ((colorB == colorE) && (colorB == colorD)
&& (colorA != colorF) && (colorA == colorI)) {
product = colorB;
} else {
product = interpolate_1_1(colorA, colorB);
}
if ((colorA == colorB) && (colorA == colorH)
&& (colorG != colorC) && (colorC == colorM)) {
product1 = colorA;
} else if ((colorC == colorG) && (colorC == colorD)
&& (colorA != colorH) && (colorA == colorI)) {
product1 = colorC;
} else {
product1 = interpolate_1_1(colorA, colorC);
}
}
*(dP + 0) = (Pixel) colorA;
*(dP + 1) = (Pixel) product;
*(dP + dstPitch / sizeof(Pixel) + 0) = (Pixel) product1;
*(dP + dstPitch / sizeof(Pixel) + 1) = (Pixel) product2;
bP += 1;
dP += 2;
}
srcPtr += srcPitch;
dstPtr += dstPitch * 2;
}
}
// SAI
void SAIScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_format.bytesPerPixel == 2) {
if (_format.gLoss == 2)
_2xSaITemplate<Graphics::ColorMasks<565> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
_2xSaITemplate<Graphics::ColorMasks<555> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
} else {
if (_format.aLoss == 0)
_2xSaITemplate<Graphics::ColorMasks<8888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
_2xSaITemplate<Graphics::ColorMasks<888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
}
}
uint SAIScaler::increaseFactor() {
return _factor;
}
uint SAIScaler::decreaseFactor() {
return _factor;
}
class SAIPlugin final : public ScalerPluginObject {
public:
SAIPlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return false; }
uint extraPixels() const override { return 2; }
const char *getName() const override;
const char *getPrettyName() const override;
};
SAIPlugin::SAIPlugin() {
_factors.push_back(2);
}
Scaler *SAIPlugin::createInstance(const Graphics::PixelFormat &format) const {
return new SAIScaler(format);
}
const char *SAIPlugin::getName() const {
return "sai";
}
const char *SAIPlugin::getPrettyName() const {
return "SAI";
}
REGISTER_PLUGIN_STATIC(SAI, PLUGIN_TYPE_SCALER, SAIPlugin);
// SuperSAI
void SuperSAIScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_format.bytesPerPixel == 2) {
if (_format.gLoss == 2)
Super2xSaITemplate<Graphics::ColorMasks<565> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
Super2xSaITemplate<Graphics::ColorMasks<555> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
} else {
if (_format.aLoss == 0)
Super2xSaITemplate<Graphics::ColorMasks<8888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
Super2xSaITemplate<Graphics::ColorMasks<888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
}
}
uint SuperSAIScaler::increaseFactor() {
return _factor;
}
uint SuperSAIScaler::decreaseFactor() {
return _factor;
}
class SuperSAIPlugin final : public ScalerPluginObject {
public:
SuperSAIPlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return false; }
uint extraPixels() const override { return 2; }
const char *getName() const override;
const char *getPrettyName() const override;
};
SuperSAIPlugin::SuperSAIPlugin() {
_factors.push_back(2);
}
Scaler *SuperSAIPlugin::createInstance(const Graphics::PixelFormat &format) const {
return new SuperSAIScaler(format);
}
const char *SuperSAIPlugin::getName() const {
return "supersai";
}
const char *SuperSAIPlugin::getPrettyName() const {
return "SuperSAI";
}
REGISTER_PLUGIN_STATIC(SUPERSAI, PLUGIN_TYPE_SCALER, SuperSAIPlugin);
// SuperEagle
void SuperEagleScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_format.bytesPerPixel == 2) {
if (_format.gLoss == 2)
SuperEagleTemplate<Graphics::ColorMasks<565> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
SuperEagleTemplate<Graphics::ColorMasks<555> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
} else {
if (_format.aLoss == 0)
SuperEagleTemplate<Graphics::ColorMasks<8888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
SuperEagleTemplate<Graphics::ColorMasks<888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
}
}
uint SuperEagleScaler::increaseFactor() {
return _factor;
}
uint SuperEagleScaler::decreaseFactor() {
return _factor;
}
class SuperEaglePlugin final : public ScalerPluginObject {
public:
SuperEaglePlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return false; }
uint extraPixels() const override { return 2; }
const char *getName() const override;
const char *getPrettyName() const override;
};
SuperEaglePlugin::SuperEaglePlugin() {
_factors.push_back(2);
}
Scaler *SuperEaglePlugin::createInstance(const Graphics::PixelFormat &format) const {
return new SuperEagleScaler(format);
}
const char *SuperEaglePlugin::getName() const {
return "supereagle";
}
const char *SuperEaglePlugin::getPrettyName() const {
return "SuperEagle";
}
REGISTER_PLUGIN_STATIC(SUPEREAGLE, PLUGIN_TYPE_SCALER, SuperEaglePlugin);

56
graphics/scaler/sai.h Normal file
View File

@@ -0,0 +1,56 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_SAI_H
#define GRAPHICS_SCALER_SAI_H
#include "graphics/scalerplugin.h"
class SAIScaler : public Scaler {
public:
SAIScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
};
class SuperSAIScaler : public Scaler {
public:
SuperSAIScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
};
class SuperEagleScaler : public Scaler {
public:
SuperEagleScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
};
#endif

503
graphics/scaler/scale2x.cpp Normal file
View File

@@ -0,0 +1,503 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file contains a C and MMX implementation of the Scale2x effect.
*
* You can find a high-level description of the effect at:
*
* https://www.scale2x.it
*
* Alternatively at the previous license terms, you are allowed to use this
* code in your program with these conditions:
* - the program is not used in commercial activities.
* - the whole source code of the program is released with the binary.
* - derivative works of the program are allowed.
*/
#include "common/scummsys.h"
#include "graphics/scaler/scale2x.h"
/***************************************************************************/
/* Scale2x C implementation */
static inline void scale2x_8_def_single(scale2x_uint8* __restrict__ dst, const scale2x_uint8* __restrict__ src0, const scale2x_uint8* __restrict__ src1, const scale2x_uint8* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0];
dst[1] = src1[1] == src0[0] ? src0[0] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
}
++src0;
++src1;
++src2;
dst += 2;
--count;
}
}
static inline void scale2x_16_def_single(scale2x_uint16* __restrict__ dst, const scale2x_uint16* __restrict__ src0, const scale2x_uint16* __restrict__ src1, const scale2x_uint16* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0];
dst[1] = src1[1] == src0[0] ? src0[0] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
}
++src0;
++src1;
++src2;
dst += 2;
--count;
}
}
static inline void scale2x_32_def_single(scale2x_uint32* __restrict__ dst, const scale2x_uint32* __restrict__ src0, const scale2x_uint32* __restrict__ src1, const scale2x_uint32* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0];
dst[1] = src1[1] == src0[0] ? src0[0] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
}
++src0;
++src1;
++src2;
dst += 2;
--count;
}
}
/**
* Scale by a factor of 2 a row of pixels of 8 bits.
* The function is implemented in C.
* The pixels over the left and right borders are assumed of the same color of
* the pixels on the border.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows.
* It must be at least 2.
* @param dst0 First destination row, double length in pixels.
* @param dst1 Second destination row, double length in pixels.
*/
void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) {
scale2x_8_def_single(dst0, src0, src1, src2, count);
scale2x_8_def_single(dst1, src2, src1, src0, count);
}
/**
* Scale by a factor of 2 a row of pixels of 16 bits.
* This function operates like scale2x_8_def() but for 16 bits pixels.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows.
* It must be at least 2.
* @param dst0 First destination row, double length in pixels.
* @param dst1 Second destination row, double length in pixels.
*/
void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) {
scale2x_16_def_single(dst0, src0, src1, src2, count);
scale2x_16_def_single(dst1, src2, src1, src0, count);
}
/**
* Scale by a factor of 2 a row of pixels of 32 bits.
* This function operates like scale2x_8_def() but for 32 bits pixels.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows.
* It must be at least 2.
* @param dst0 First destination row, double length in pixels.
* @param dst1 Second destination row, double length in pixels.
*/
void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) {
scale2x_32_def_single(dst0, src0, src1, src2, count);
scale2x_32_def_single(dst1, src2, src1, src0, count);
}
/***************************************************************************/
/* Scale2x MMX implementation */
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
/*
* Apply the Scale2x effect at a single row.
* This function must be called only by the other scale2x functions.
*
* Considering the pixel map :
*
* ABC (src0)
* DEF (src1)
* GHI (src2)
*
* this functions compute 2 new pixels in substitution of the source pixel E
* like this map :
*
* ab (dst)
*
* with these variables :
*
* &current -> E
* &current_left -> D
* &current_right -> F
* &current_upper -> B
* &current_lower -> H
*
* %0 -> current_upper
* %1 -> current
* %2 -> current_lower
* %3 -> dst
* %4 -> counter
*
* %mm0 -> *current_left
* %mm1 -> *current_next
* %mm2 -> tmp0
* %mm3 -> tmp1
* %mm4 -> tmp2
* %mm5 -> tmp3
* %mm6 -> *current_upper
* %mm7 -> *current
*/
static inline void scale2x_8_mmx_single(scale2x_uint8* dst, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) {
assert(count >= 16);
assert(count % 8 == 0);
__asm__ __volatile__(
/* central runs */
"shr $3, %4\n"
"jz 1f\n"
"0:\n"
/* set the current, current_pre, current_next registers */
"movq -8(%1), %%mm0\n"
"movq (%1), %%mm7\n"
"movq 8(%1), %%mm1\n"
"psrlq $56, %%mm0\n"
"psllq $56, %%mm1\n"
"movq %%mm7, %%mm2\n"
"movq %%mm7, %%mm3\n"
"psllq $8, %%mm2\n"
"psrlq $8, %%mm3\n"
"por %%mm2, %%mm0\n"
"por %%mm3, %%mm1\n"
/* current_upper */
"movq (%0), %%mm6\n"
/* compute the upper-left pixel for dst on %%mm2 */
/* compute the upper-right pixel for dst on %%mm4 */
"movq %%mm0, %%mm2\n"
"movq %%mm1, %%mm4\n"
"movq %%mm0, %%mm3\n"
"movq %%mm1, %%mm5\n"
"pcmpeqb %%mm6, %%mm2\n"
"pcmpeqb %%mm6, %%mm4\n"
"pcmpeqb (%2), %%mm3\n"
"pcmpeqb (%2), %%mm5\n"
"pandn %%mm2, %%mm3\n"
"pandn %%mm4, %%mm5\n"
"movq %%mm0, %%mm2\n"
"movq %%mm1, %%mm4\n"
"pcmpeqb %%mm1, %%mm2\n"
"pcmpeqb %%mm0, %%mm4\n"
"pandn %%mm3, %%mm2\n"
"pandn %%mm5, %%mm4\n"
"movq %%mm2, %%mm3\n"
"movq %%mm4, %%mm5\n"
"pand %%mm6, %%mm2\n"
"pand %%mm6, %%mm4\n"
"pandn %%mm7, %%mm3\n"
"pandn %%mm7, %%mm5\n"
"por %%mm3, %%mm2\n"
"por %%mm5, %%mm4\n"
/* set *dst */
"movq %%mm2, %%mm3\n"
"punpcklbw %%mm4, %%mm2\n"
"punpckhbw %%mm4, %%mm3\n"
"movq %%mm2, (%3)\n"
"movq %%mm3, 8(%3)\n"
/* next */
"add $8, %0\n"
"add $8, %1\n"
"add $8, %2\n"
"add $16, %3\n"
"dec %4\n"
"jnz 0b\n"
"1:\n"
: "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count)
:
: "cc"
);
}
static inline void scale2x_16_mmx_single(scale2x_uint16* dst, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) {
assert(count >= 8);
assert(count % 4 == 0);
__asm__ __volatile__(
/* central runs */
"shr $2, %4\n"
"jz 1f\n"
"0:\n"
/* set the current, current_pre, current_next registers */
"movq -8(%1), %%mm0\n"
"movq (%1), %%mm7\n"
"movq 8(%1), %%mm1\n"
"psrlq $48, %%mm0\n"
"psllq $48, %%mm1\n"
"movq %%mm7, %%mm2\n"
"movq %%mm7, %%mm3\n"
"psllq $16, %%mm2\n"
"psrlq $16, %%mm3\n"
"por %%mm2, %%mm0\n"
"por %%mm3, %%mm1\n"
/* current_upper */
"movq (%0), %%mm6\n"
/* compute the upper-left pixel for dst on %%mm2 */
/* compute the upper-right pixel for dst on %%mm4 */
"movq %%mm0, %%mm2\n"
"movq %%mm1, %%mm4\n"
"movq %%mm0, %%mm3\n"
"movq %%mm1, %%mm5\n"
"pcmpeqw %%mm6, %%mm2\n"
"pcmpeqw %%mm6, %%mm4\n"
"pcmpeqw (%2), %%mm3\n"
"pcmpeqw (%2), %%mm5\n"
"pandn %%mm2, %%mm3\n"
"pandn %%mm4, %%mm5\n"
"movq %%mm0, %%mm2\n"
"movq %%mm1, %%mm4\n"
"pcmpeqw %%mm1, %%mm2\n"
"pcmpeqw %%mm0, %%mm4\n"
"pandn %%mm3, %%mm2\n"
"pandn %%mm5, %%mm4\n"
"movq %%mm2, %%mm3\n"
"movq %%mm4, %%mm5\n"
"pand %%mm6, %%mm2\n"
"pand %%mm6, %%mm4\n"
"pandn %%mm7, %%mm3\n"
"pandn %%mm7, %%mm5\n"
"por %%mm3, %%mm2\n"
"por %%mm5, %%mm4\n"
/* set *dst */
"movq %%mm2, %%mm3\n"
"punpcklwd %%mm4, %%mm2\n"
"punpckhwd %%mm4, %%mm3\n"
"movq %%mm2, (%3)\n"
"movq %%mm3, 8(%3)\n"
/* next */
"add $8, %0\n"
"add $8, %1\n"
"add $8, %2\n"
"add $16, %3\n"
"dec %4\n"
"jnz 0b\n"
"1:\n"
: "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count)
:
: "cc"
);
}
static inline void scale2x_32_mmx_single(scale2x_uint32* dst, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) {
assert(count >= 4);
assert(count % 2 == 0);
__asm__ __volatile__(
/* central runs */
"shr $1, %4\n"
"jz 1f\n"
"0:\n"
/* set the current, current_pre, current_next registers */
"movq -8(%1), %%mm0\n"
"movq (%1), %%mm7\n"
"movq 8(%1), %%mm1\n"
"psrlq $32, %%mm0\n"
"psllq $32, %%mm1\n"
"movq %%mm7, %%mm2\n"
"movq %%mm7, %%mm3\n"
"psllq $32, %%mm2\n"
"psrlq $32, %%mm3\n"
"por %%mm2, %%mm0\n"
"por %%mm3, %%mm1\n"
/* current_upper */
"movq (%0), %%mm6\n"
/* compute the upper-left pixel for dst on %%mm2 */
/* compute the upper-right pixel for dst on %%mm4 */
"movq %%mm0, %%mm2\n"
"movq %%mm1, %%mm4\n"
"movq %%mm0, %%mm3\n"
"movq %%mm1, %%mm5\n"
"pcmpeqd %%mm6, %%mm2\n"
"pcmpeqd %%mm6, %%mm4\n"
"pcmpeqd (%2), %%mm3\n"
"pcmpeqd (%2), %%mm5\n"
"pandn %%mm2, %%mm3\n"
"pandn %%mm4, %%mm5\n"
"movq %%mm0, %%mm2\n"
"movq %%mm1, %%mm4\n"
"pcmpeqd %%mm1, %%mm2\n"
"pcmpeqd %%mm0, %%mm4\n"
"pandn %%mm3, %%mm2\n"
"pandn %%mm5, %%mm4\n"
"movq %%mm2, %%mm3\n"
"movq %%mm4, %%mm5\n"
"pand %%mm6, %%mm2\n"
"pand %%mm6, %%mm4\n"
"pandn %%mm7, %%mm3\n"
"pandn %%mm7, %%mm5\n"
"por %%mm3, %%mm2\n"
"por %%mm5, %%mm4\n"
/* set *dst */
"movq %%mm2, %%mm3\n"
"punpckldq %%mm4, %%mm2\n"
"punpckhdq %%mm4, %%mm3\n"
"movq %%mm2, (%3)\n"
"movq %%mm3, 8(%3)\n"
/* next */
"add $8, %0\n"
"add $8, %1\n"
"add $8, %2\n"
"add $16, %3\n"
"dec %4\n"
"jnz 0b\n"
"1:\n"
: "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count)
:
: "cc"
);
}
/**
* Scale by a factor of 2 a row of pixels of 8 bits.
* This is a very fast MMX implementation.
* The implementation uses a combination of cmp/and/not operations to
* completly remove the need of conditional jumps. This trick give the
* major speed improvement.
* Also, using the 8 bytes MMX registers more than one pixel are computed
* at the same time.
* Before calling this function you must ensure that the currenct CPU supports
* the MMX instruction set. After calling it you must be sure to call the EMMS
* instruction before any floating-point operation.
* The pixels over the left and right borders are assumed of the same color of
* the pixels on the border.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows. It must
* be at least 16 and a multiple of 8.
* @param dst0 First destination row, double length in pixels.
* @param dst1 Second destination row, double length in pixels.
*/
void scale2x_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) {
if (count % 8 != 0 || count < 16) {
scale2x_8_def(dst0, dst1, src0, src1, src2, count);
} else {
assert(count >= 16);
assert(count % 8 == 0);
scale2x_8_mmx_single(dst0, src0, src1, src2, count);
scale2x_8_mmx_single(dst1, src2, src1, src0, count);
}
}
/**
* Scale by a factor of 2 a row of pixels of 16 bits.
* This function operates like scale2x_8_mmx() but for 16 bits pixels.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows. It must
* be at least 8 and a multiple of 4.
* @param dst0 First destination row, double length in pixels.
* @param dst1 Second destination row, double length in pixels.
*/
void scale2x_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) {
if (count % 4 != 0 || count < 8) {
scale2x_16_def(dst0, dst1, src0, src1, src2, count);
} else {
assert(count >= 8);
assert(count % 4 == 0);
scale2x_16_mmx_single(dst0, src0, src1, src2, count);
scale2x_16_mmx_single(dst1, src2, src1, src0, count);
}
}
/**
* Scale by a factor of 2 a row of pixels of 32 bits.
* This function operates like scale2x_8_mmx() but for 32 bits pixels.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows. It must
* be at least 4 and a multiple of 2.
* @param dst0 First destination row, double length in pixels.
* @param dst1 Second destination row, double length in pixels.
*/
void scale2x_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) {
if (count % 2 != 0 || count < 4) {
scale2x_32_def(dst0, dst1, src0, src1, src2, count);
} else {
assert(count >= 4);
assert(count % 2 == 0);
scale2x_32_mmx_single(dst0, src0, src1, src2, count);
scale2x_32_mmx_single(dst1, src2, src1, src0, count);
}
}
#endif

64
graphics/scaler/scale2x.h Normal file
View File

@@ -0,0 +1,64 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCALER_SCALE2X_H
#define SCALER_SCALE2X_H
#if defined(_MSC_VER)
#define __restrict__
#endif
typedef unsigned char scale2x_uint8;
typedef unsigned short scale2x_uint16;
typedef unsigned scale2x_uint32;
void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count);
void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count);
void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count);
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
void scale2x_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count);
void scale2x_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count);
void scale2x_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count);
/**
* End the use of the MMX instructions.
* This function must be called before using any floating-point operations.
*/
static inline void scale2x_mmx_emms(void)
{
__asm__ __volatile__ (
"emms"
);
}
#endif
#if defined(USE_ARM_SCALER_ASM)
extern "C" void scale2x_8_arm(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count);
extern "C" void scale2x_16_arm(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count);
extern "C" void scale2x_32_arm(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count);
#endif
#endif

View File

@@ -0,0 +1,184 @@
@ 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 3 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, see <http://www.gnu.org/licenses/>.
@
@ @author Robin Watts (robin@wss.co.uk)
.text
.global scale2x_8_arm
.global scale2x_16_arm
.global scale2x_32_arm
@ r0 = dst0
@ r1 = dst1
@ r2 = src
@ r3 = src_prev
@ r4 = src_next
@ r5 = len
@ We hold: r10 B
@ r8 r14 r7 D E F
@ r12 H
.align 2
scale2x_8_arm:
STMFD r13!,{r4-r5,r7-r8,r10-r11,r14}
LDR r4, [r13,#4*7]
LDR r5, [r13,#4*8]
LDRB r14,[r2], #1
CMP r3, #0 @ Set NE
@ Stall on Xscale
MOV r8, r14
B loop8
same8:
STRB r14,[r0], #1
STRB r14,[r0], #1
STRB r14,[r1], #1
STRB r14,[r1], #1
SUBS r5, r5, #1
MOV r8, r14
MOV r14,r7
BLT end8
loop8:
LDRNEB r7, [r3], #1 @ As long as we aren't on the last pixel
LDRB r10,[r2], #1
LDRB r12,[r4], #1
@ Stall on Xscale
CMP r7, r8
CMPNE r10,r12
BEQ same8
CMP r8, r10
STREQB r8, [r0], #1
STRNEB r14,[r0], #1
CMP r10,r7
STREQB r7, [r0], #1
STRNEB r14,[r0], #1
CMP r8, r12
STREQB r8, [r1], #1
STRNEB r14,[r1], #1
CMP r12,r7
STREQB r7, [r1], #1
STRNEB r14,[r1], #1
SUBS r5, r5, #1
MOV r8, r14
MOV r14,r7
BGE loop8
end8:
LDMFD r13!,{r4-r5,r7-r8,r10-r11,PC}
.align 2
scale2x_16_arm:
STMFD r13!,{r4-r5,r7-r8,r10-r11,r14}
LDR r4, [r13,#4*7]
LDR r5, [r13,#4*8]
LDRH r14,[r3], #2
CMP r3, #0 @ Set NE
@ Stall on Xscale
MOV r8, r14
B loop16
same16:
STRH r14,[r0], #2
STRH r14,[r0], #2
STRH r14,[r1], #2
STRH r14,[r1], #2
SUBS r5, r5, #1
MOV r8, r14
MOV r14,r7
BLT end16
loop16:
LDRNEH r7, [r3], #2 @ As long as we aren't on the last pixel
LDRH r10,[r2], #2
LDRH r12,[r4], #2
@ Stall on Xscale
CMP r7, r8
CMPNE r10,r12
BEQ same16
CMP r8, r10
STREQH r8, [r0], #2
STRNEH r14,[r0], #2
CMP r10,r7
STREQH r7, [r0], #2
STRNEH r14,[r0], #2
CMP r8, r12
STREQH r8, [r1], #2
STRNEH r14,[r1], #2
CMP r12,r7
STREQH r7, [r1], #2
STRNEH r14,[r1], #2
SUBS r5, r5, #1
MOV r8, r14
MOV r14,r7
BGE loop16
end16:
LDMFD r13!,{r4-r5,r7-r8,r10-r11,PC}
.align 2
scale2x_32_arm:
STMFD r13!,{r4-r5,r7-r8,r10-r11,r14}
LDR r4, [r13,#4*7]
LDR r5, [r13,#4*8]
LDR r14,[r3], #4
CMP r3, #0 @ Set NE
@ Stall on Xscale
MOV r8, r14
B loop32
same32:
STR r14,[r0], #4
STR r14,[r0], #4
STR r14,[r1], #4
STR r14,[r1], #4
SUBS r5, r5, #1
MOV r8, r14
MOV r14,r7
BLT end32
loop32:
LDRNE r7, [r3], #4 @ As long as we aren't on the last pixel
LDR r10,[r2], #4
LDR r12,[r4], #4
@ Stall on Xscale
CMP r7, r8
CMPNE r10,r12
BEQ same32
CMP r8, r10
STREQ r8, [r0], #4
STRNE r14,[r0], #4
CMP r10,r7
STREQ r7, [r0], #4
STRNE r14,[r0], #4
CMP r8, r12
STREQ r8, [r1], #4
STRNE r14,[r1], #4
CMP r12,r7
STREQ r7, [r1], #4
STRNE r14,[r1], #4
SUBS r5, r5, #1
MOV r8, r14
MOV r14,r7
BGE loop32
end32:
LDMFD r13!,{r4-r5,r7-r8,r10-r11,PC}

223
graphics/scaler/scale3x.cpp Normal file
View File

@@ -0,0 +1,223 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file contains a C and MMX implementation of the Scale2x effect.
*
* You can find a high-level description of the effect at:
*
* https://www.scale2x.it
*
* Alternatively at the previous license terms, you are allowed to use this
* code in your program with these conditions:
* - the program is not used in commercial activities.
* - the whole source code of the program is released with the binary.
* - derivative works of the program are allowed.
*/
#include "common/scummsys.h"
#include "graphics/scaler/scale3x.h"
/***************************************************************************/
/* Scale3x C implementation */
static inline void scale3x_8_def_border(scale3x_uint8* __restrict__ dst, const scale3x_uint8* __restrict__ src0, const scale3x_uint8* __restrict__ src1, const scale3x_uint8* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0];
dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0];
dst[2] = src1[1] == src0[0] ? src1[1] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
dst[2] = src1[0];
}
++src0;
++src1;
++src2;
dst += 3;
--count;
}
}
static inline void scale3x_8_def_center(scale3x_uint8* __restrict__ dst, const scale3x_uint8* __restrict__ src0, const scale3x_uint8* __restrict__ src1, const scale3x_uint8* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0];
dst[1] = src1[0];
dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
dst[2] = src1[0];
}
++src0;
++src1;
++src2;
dst += 3;
--count;
}
}
static inline void scale3x_16_def_border(scale3x_uint16* __restrict__ dst, const scale3x_uint16* __restrict__ src0, const scale3x_uint16* __restrict__ src1, const scale3x_uint16* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0];
dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0];
dst[2] = src1[1] == src0[0] ? src1[1] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
dst[2] = src1[0];
}
++src0;
++src1;
++src2;
dst += 3;
--count;
}
}
static inline void scale3x_16_def_center(scale3x_uint16* __restrict__ dst, const scale3x_uint16* __restrict__ src0, const scale3x_uint16* __restrict__ src1, const scale3x_uint16* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0];
dst[1] = src1[0];
dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
dst[2] = src1[0];
}
++src0;
++src1;
++src2;
dst += 3;
--count;
}
}
static inline void scale3x_32_def_border(scale3x_uint32* __restrict__ dst, const scale3x_uint32* __restrict__ src0, const scale3x_uint32* __restrict__ src1, const scale3x_uint32* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0];
dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0];
dst[2] = src1[1] == src0[0] ? src1[1] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
dst[2] = src1[0];
}
++src0;
++src1;
++src2;
dst += 3;
--count;
}
}
static inline void scale3x_32_def_center(scale3x_uint32* __restrict__ dst, const scale3x_uint32* __restrict__ src0, const scale3x_uint32* __restrict__ src1, const scale3x_uint32* __restrict__ src2, unsigned count) {
/* central pixels */
while (count) {
if (src0[0] != src2[0] && src1[-1] != src1[1]) {
dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0];
dst[1] = src1[0];
dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0];
} else {
dst[0] = src1[0];
dst[1] = src1[0];
dst[2] = src1[0];
}
++src0;
++src1;
++src2;
dst += 3;
--count;
}
}
/**
* Scale by a factor of 3 a row of pixels of 8 bits.
* The function is implemented in C.
* The pixels over the left and right borders are assumed of the same color of
* the pixels on the border.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows.
* It must be at least 2.
* @param dst0 First destination row, triple length in pixels.
* @param dst1 Second destination row, triple length in pixels.
* @param dst2 Third destination row, triple length in pixels.
*/
void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count) {
scale3x_8_def_border(dst0, src0, src1, src2, count);
scale3x_8_def_center(dst1, src0, src1, src2, count);
scale3x_8_def_border(dst2, src2, src1, src0, count);
}
/**
* Scale by a factor of 3 a row of pixels of 16 bits.
* This function operates like scale3x_8_def() but for 16 bits pixels.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows.
* It must be at least 2.
* @param dst0 First destination row, triple length in pixels.
* @param dst1 Second destination row, triple length in pixels.
* @param dst2 Third destination row, triple length in pixels.
*/
void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count) {
scale3x_16_def_border(dst0, src0, src1, src2, count);
scale3x_16_def_center(dst1, src0, src1, src2, count);
scale3x_16_def_border(dst2, src2, src1, src0, count);
}
/**
* Scale by a factor of 3 a row of pixels of 32 bits.
* This function operates like scale3x_8_def() but for 32 bits pixels.
* @param src0 Pointer at the first pixel of the previous row.
* @param src1 Pointer at the first pixel of the current row.
* @param src2 Pointer at the first pixel of the next row.
* @param count Length in pixels of the src0, src1 and src2 rows.
* It must be at least 2.
* @param dst0 First destination row, triple length in pixels.
* @param dst1 Second destination row, triple length in pixels.
* @param dst2 Third destination row, triple length in pixels.
*/
void scale3x_32_def(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count) {
scale3x_32_def_border(dst0, src0, src1, src2, count);
scale3x_32_def_center(dst1, src0, src1, src2, count);
scale3x_32_def_border(dst2, src2, src1, src0, count);
}

37
graphics/scaler/scale3x.h Normal file
View File

@@ -0,0 +1,37 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCALER_SCALE3X_H
#define SCALER_SCALE3X_H
#if defined(_MSC_VER)
#define __restrict__
#endif
typedef unsigned char scale3x_uint8;
typedef unsigned short scale3x_uint16;
typedef unsigned scale3x_uint32;
void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count);
void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count);
void scale3x_32_def(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count);
#endif

View File

@@ -0,0 +1,407 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file contains an example implementation of the Scale effect
* applied to a generic bitmap.
*
* You can find a high-level description of the effect at:
*
* https://www.scale2x.it
*
* Alternatively at the previous license terms, you are allowed to use this
* code in your program with these conditions:
* - the program is not used in commercial activities.
* - the whole source code of the program is released with the binary.
* - derivative works of the program are allowed.
*/
#include "common/scummsys.h"
#include "graphics/scaler/scale2x.h"
#include "graphics/scaler/scale3x.h"
#include "graphics/scaler/scalebit.h"
#define DST(bits, num) (scale2x_uint ## bits *)dst ## num
#define SRC(bits, num) (const scale2x_uint ## bits *)src ## num
/**
* Apply the Scale2x effect on a group of rows. Used internally.
*/
static inline void stage_scale2x(void* dst0, void* dst1, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) {
switch (pixel) {
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
case 1: scale2x_8_mmx( DST( 8,0), DST( 8,1), SRC( 8,0), SRC( 8,1), SRC( 8,2), pixel_per_row); break;
case 2: scale2x_16_mmx(DST(16,0), DST(16,1), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break;
case 4: scale2x_32_mmx(DST(32,0), DST(32,1), SRC(32,0), SRC(32,1), SRC(32,2), pixel_per_row); break;
#elif defined(USE_ARM_SCALER_ASM)
case 1: scale2x_8_arm( DST( 8,0), DST( 8,1), SRC( 8,0), SRC( 8,1), SRC( 8,2), pixel_per_row); break;
case 2: scale2x_16_arm(DST(16,0), DST(16,1), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break;
case 4: scale2x_32_arm(DST(32,0), DST(32,1), SRC(32,0), SRC(32,1), SRC(32,2), pixel_per_row); break;
#else
case 1: scale2x_8_def( DST( 8,0), DST( 8,1), SRC( 8,0), SRC( 8,1), SRC( 8,2), pixel_per_row); break;
case 2: scale2x_16_def(DST(16,0), DST(16,1), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break;
case 4: scale2x_32_def(DST(32,0), DST(32,1), SRC(32,0), SRC(32,1), SRC(32,2), pixel_per_row); break;
#endif
default: break;
}
}
/**
* Apply the Scale3x effect on a group of rows. Used internally.
*/
static inline void stage_scale3x(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) {
switch (pixel) {
case 1: scale3x_8_def( DST( 8,0), DST( 8,1), DST( 8,2), SRC( 8,0), SRC( 8,1), SRC( 8,2), pixel_per_row); break;
case 2: scale3x_16_def(DST(16,0), DST(16,1), DST(16,2), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break;
case 4: scale3x_32_def(DST(32,0), DST(32,1), DST(32,2), SRC(32,0), SRC(32,1), SRC(32,2), pixel_per_row); break;
default: break;
}
}
/**
* Apply the Scale4x effect on a group of rows. Used internally.
*/
static inline void stage_scale4x(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, const void* src3, unsigned pixel, unsigned pixel_per_row) {
stage_scale2x(dst0, dst1, src0, src1, src2, pixel, 2 * pixel_per_row);
stage_scale2x(dst2, dst3, src1, src2, src3, pixel, 2 * pixel_per_row);
}
#define SCDST(i) (dst+(i)*dst_slice)
#define SCSRC(i) (src+(i)*src_slice)
#define SCMID(i) (mid[(i)])
/**
* Apply the Scale2x effect on a bitmap.
* The destination bitmap is filled with the scaled version of the source bitmap.
* The source bitmap isn't modified.
* The destination bitmap must be manually allocated before calling the function,
* note that the resulting size is exactly 2x2 times the size of the source bitmap.
* @param void_dst Pointer at the first pixel of the destination bitmap.
* @param dst_slice Size in bytes of a destination bitmap row.
* @param void_src Pointer at the first pixel of the source bitmap.
* @param src_slice Size in bytes of a source bitmap row.
* @param pixel Bytes per pixel of the source and destination bitmap.
* @param width Horizontal size in pixels of the source bitmap.
* @param height Vertical size in pixels of the source bitmap.
*/
static void scale2x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) {
unsigned char* dst = (unsigned char*)void_dst;
const unsigned char* src = (const unsigned char*)void_src;
unsigned count;
assert(height >= 2);
count = height;
while (count) {
stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width);
dst = SCDST(2);
src = SCSRC(1);
--count;
}
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
scale2x_mmx_emms();
#endif
}
/**
* Apply the Scale3x effect on a bitmap.
* The destination bitmap is filled with the scaled version of the source bitmap.
* The source bitmap isn't modified.
* The destination bitmap must be manually allocated before calling the function,
* note that the resulting size is exactly 3x3 times the size of the source bitmap.
* @param void_dst Pointer at the first pixel of the destination bitmap.
* @param dst_slice Size in bytes of a destination bitmap row.
* @param void_src Pointer at the first pixel of the source bitmap.
* @param src_slice Size in bytes of a source bitmap row.
* @param pixel Bytes per pixel of the source and destination bitmap.
* @param width Horizontal size in pixels of the source bitmap.
* @param height Vertical size in pixels of the source bitmap.
*/
static void scale3x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) {
unsigned char* dst = (unsigned char*)void_dst;
const unsigned char* src = (const unsigned char*)void_src;
unsigned count;
assert(height >= 2);
count = height;
while (count) {
stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width);
dst = SCDST(3);
src = SCSRC(1);
--count;
}
}
/**
* Apply the Scale4x effect on a bitmap.
* The destination bitmap is filled with the scaled version of the source bitmap.
* The source bitmap isn't modified.
* The destination bitmap must be manually allocated before calling the function,
* note that the resulting size is exactly 4x4 times the size of the source bitmap.
* \note This function requires also a small buffer bitmap used internally to store
* intermediate results. This bitmap must have at least a horizontal size in bytes of 2*width*pixel,
* and a vertical size of 6 rows. The memory of this buffer must not be allocated
* in video memory because it's also read and not only written. Generally
* a heap (malloc) or a stack (alloca) buffer is the best choices.
* @param void_dst Pointer at the first pixel of the destination bitmap.
* @param dst_slice Size in bytes of a destination bitmap row.
* @param void_mid Pointer at the first pixel of the buffer bitmap.
* @param mid_slice Size in bytes of a buffer bitmap row.
* @param void_src Pointer at the first pixel of the source bitmap.
* @param src_slice Size in bytes of a source bitmap row.
* @param pixel Bytes per pixel of the source and destination bitmap.
* @param width Horizontal size in pixels of the source bitmap.
* @param height Vertical size in pixels of the source bitmap.
*/
static void scale4x_buf(void* void_dst, unsigned dst_slice, void* void_mid, unsigned mid_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) {
unsigned char* dst = (unsigned char*)void_dst;
const unsigned char* src = (const unsigned char*)void_src;
unsigned count;
unsigned char* mid[6];
assert(height >= 4);
count = height;
/* set the 6 buffer pointers */
mid[0] = (unsigned char*)void_mid;
mid[1] = mid[0] + mid_slice;
mid[2] = mid[1] + mid_slice;
mid[3] = mid[2] + mid_slice;
mid[4] = mid[3] + mid_slice;
mid[5] = mid[4] + mid_slice;
stage_scale2x(SCMID(0), SCMID(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width);
stage_scale2x(SCMID(2), SCMID(3), SCSRC(1), SCSRC(2), SCSRC(3), pixel, width);
while (count) {
unsigned char* tmp;
stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(4), pixel, width);
stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width);
dst = SCDST(4);
src = SCSRC(1);
tmp = SCMID(0); /* shift by 2 position */
SCMID(0) = SCMID(2);
SCMID(2) = SCMID(4);
SCMID(4) = tmp;
tmp = SCMID(1);
SCMID(1) = SCMID(3);
SCMID(3) = SCMID(5);
SCMID(5) = tmp;
--count;
}
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
scale2x_mmx_emms();
#endif
}
/**
* Apply the Scale4x effect on a bitmap.
* The destination bitmap is filled with the scaled version of the source bitmap.
* The source bitmap isn't modified.
* The destination bitmap must be manually allocated before calling the function,
* note that the resulting size is exactly 4x4 times the size of the source bitmap.
* \note This function operates like ::scale4x_buf() but the intermediate buffer is
* automatically allocated in the stack.
* @param void_dst Pointer at the first pixel of the destination bitmap.
* @param dst_slice Size in bytes of a destination bitmap row.
* @param void_src Pointer at the first pixel of the source bitmap.
* @param src_slice Size in bytes of a source bitmap row.
* @param pixel Bytes per pixel of the source and destination bitmap.
* @param width Horizontal size in pixels of the source bitmap.
* @param height Vertical size in pixels of the source bitmap.
*/
static void scale4x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) {
unsigned mid_slice;
void* mid;
mid_slice = 2 * pixel * width; /* required space for 1 row buffer */
mid_slice = (mid_slice + 0x7) & ~0x7; /* align to 8 bytes */
#if defined(HAVE_ALLOCA)
mid = alloca(6 * mid_slice); /* allocate space for 6 row buffers */
assert(mid != 0); /* alloca should never fails */
#else
mid = malloc(6 * mid_slice); /* allocate space for 6 row buffers */
if (!mid)
return;
#endif
scale4x_buf(void_dst, dst_slice, mid, mid_slice, void_src, src_slice, pixel, width, height);
#if !defined(HAVE_ALLOCA)
free(mid);
#endif
}
/**
* Check if the scale implementation is applicable at the given arguments.
* @param scale Scale factor. 2, 3 or 4.
* @param pixel Bytes per pixel of the source and destination bitmap.
* @param width Horizontal size in pixels of the source bitmap.
* @param height Vertical size in pixels of the source bitmap.
* \return
* - -1 on precondition violated.
* - 0 on success.
*/
int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height)
{
if (scale != 2 && scale != 3 && scale != 4)
return -1;
if (pixel != 1 && pixel != 2 && pixel != 4)
return -1;
switch (scale) {
case 2:
case 3:
if (height < 2)
return -1;
break;
case 4:
if (height < 4)
return -1;
break;
default:
break;
}
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
switch (scale) {
case 2:
case 4:
if (width < (16 / pixel))
return -1;
if (width % (8 / pixel) != 0)
return -1;
break;
case 3:
if (width < 2)
return -1;
break;
default:
break;
}
#else
if (width < 2)
return -1;
#endif
return 0;
}
/**
* Apply the Scale effect on a bitmap.
* This function is simply a common interface for ::scale2x(), ::scale3x() and ::scale4x().
* @param scale Scale factor. 2, 3 or 4.
* @param void_dst Pointer at the first pixel of the destination bitmap.
* @param dst_slice Size in bytes of a destination bitmap row.
* @param void_src Pointer at the first pixel of the source bitmap.
* @param src_slice Size in bytes of a source bitmap row.
* @param pixel Bytes per pixel of the source and destination bitmap.
* @param width Horizontal size in pixels of the source bitmap.
* @param height Vertical size in pixels of the source bitmap.
*/
void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height)
{
switch (scale) {
case 2:
scale2x(void_dst, dst_slice, void_src, src_slice, pixel, width, height);
break;
case 3:
scale3x(void_dst, dst_slice, void_src, src_slice, pixel, width, height);
break;
case 4:
scale4x(void_dst, dst_slice, void_src, src_slice, pixel, width, height);
break;
default:
break;
}
}
void AdvMameScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_factor != 4)
::scale(_factor, dstPtr, dstPitch, srcPtr - srcPitch, srcPitch, _format.bytesPerPixel, width, height);
else
::scale(_factor, dstPtr, dstPitch, srcPtr - srcPitch * 2, srcPitch, _format.bytesPerPixel, width, height);
}
uint AdvMameScaler::increaseFactor() {
if (_factor < 4)
setFactor(_factor + 1);
return _factor;
}
uint AdvMameScaler::decreaseFactor() {
if (_factor > 2)
setFactor(_factor - 1);
return _factor;
}
class AdvMamePlugin final : public ScalerPluginObject {
public:
AdvMamePlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return true; }
uint extraPixels() const override { return 4; }
const char *getName() const override;
const char *getPrettyName() const override;
};
AdvMamePlugin::AdvMamePlugin() {
_factors.push_back(2);
_factors.push_back(3);
_factors.push_back(4);
}
Scaler *AdvMamePlugin::createInstance(const Graphics::PixelFormat &format) const {
return new AdvMameScaler(format);
}
const char *AdvMamePlugin::getName() const {
return "advmame";
}
const char *AdvMamePlugin::getPrettyName() const {
return "AdvMame";
}
REGISTER_PLUGIN_STATIC(ADVMAME, PLUGIN_TYPE_SCALER, AdvMamePlugin);

View File

@@ -0,0 +1,55 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file contains an example implementation of the Scale effect
* applied to a generic bitmap.
*
* You can find a high-level description of the effect at:
*
* https://www.scale2x.it
*
* Alternatively at the previous license terms, you are allowed to use this
* code in your program with these conditions:
* - the program is not used in commercial activities.
* - the whole source code of the program is released with the binary.
* - derivative works of the program are allowed.
*/
#ifndef SCALER_SCALEBIT_H
#define SCALER_SCALEBIT_H
#include "graphics/scalerplugin.h"
int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height);
void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height);
class AdvMameScaler : public Scaler {
public:
AdvMameScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
uint increaseFactor() override;
uint decreaseFactor() override;
protected:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
};
#endif

View File

@@ -0,0 +1,304 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "graphics/colormasks.h"
#include "graphics/scaler.h"
#include "graphics/scaler/intern.h"
#include "graphics/paletteman.h"
#include "graphics/managed_surface.h"
template<typename ColorMask>
uint16 quadBlockInterpolate(const uint8 *src, uint32 srcPitch) {
uint16 colorx1y1 = *(((const uint16 *)src));
uint16 colorx2y1 = *(((const uint16 *)src) + 1);
uint16 colorx1y2 = *(((const uint16 *)(src + srcPitch)));
uint16 colorx2y2 = *(((const uint16 *)(src + srcPitch)) + 1);
return interpolate16_1_1_1_1<ColorMask>(colorx1y1, colorx2y1, colorx1y2, colorx2y2);
}
template<typename ColorMask>
void createThumbnail_2(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
// Make sure the width and height is a multiple of 2.
width &= ~1;
height &= ~1;
for (int y = 0; y < height; y += 2) {
for (int x = 0; x < width; x += 2, dstPtr += 2) {
*((uint16 *)dstPtr) = quadBlockInterpolate<ColorMask>(src + 2 * x, srcPitch);
}
dstPtr += (dstPitch - 2 * width / 2);
src += 2 * srcPitch;
}
}
template<typename ColorMask>
void createThumbnail_4(const uint8 *src, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) {
// Make sure the width and height is a multiple of 4
width &= ~3;
height &= ~3;
for (int y = 0; y < height; y += 4) {
for (int x = 0; x < width; x += 4, dstPtr += 2) {
uint16 upleft = quadBlockInterpolate<ColorMask>(src + 2 * x, srcPitch);
uint16 upright = quadBlockInterpolate<ColorMask>(src + 2 * (x + 2), srcPitch);
uint16 downleft = quadBlockInterpolate<ColorMask>(src + srcPitch * 2 + 2 * x, srcPitch);
uint16 downright = quadBlockInterpolate<ColorMask>(src + srcPitch * 2 + 2 * (x + 2), srcPitch);
*((uint16 *)dstPtr) = interpolate16_1_1_1_1<ColorMask>(upleft, upright, downleft, downright);
}
dstPtr += (dstPitch - 2 * width / 4);
src += 4 * srcPitch;
}
}
template<typename ColorMask>
static void scaleThumbnail(Graphics::Surface &in, Graphics::Surface &out) {
while (in.w / out.w >= 4 || in.h / out.h >= 4) {
createThumbnail_4<ColorMask>((const uint8 *)in.getPixels(), in.pitch, (uint8 *)in.getPixels(), in.pitch, in.w, in.h);
in.w /= 4;
in.h /= 4;
}
while (in.w / out.w >= 2 || in.h / out.h >= 2) {
createThumbnail_2<ColorMask>((const uint8 *)in.getPixels(), in.pitch, (uint8 *)in.getPixels(), in.pitch, in.w, in.h);
in.w /= 2;
in.h /= 2;
}
if ((in.w == out.w && in.h < out.h) || (in.w < out.w && in.h == out.h)) {
// In this case we simply center the input surface in the output
uint8 *dst = (uint8 *)out.getBasePtr((out.w - in.w) / 2, (out.h - in.h) / 2);
const uint8 *src = (const uint8 *)in.getPixels();
for (int y = 0; y < in.h; ++y) {
memcpy(dst, src, in.w * in.format.bytesPerPixel);
src += in.pitch;
dst += out.pitch;
}
} else {
// Assure the aspect of the scaled image still matches the original.
int targetWidth = out.w, targetHeight = out.h;
const float inputAspect = (float)in.w / in.h;
const float outputAspect = (float)out.w / out.h;
if (inputAspect > outputAspect) {
targetHeight = int(targetWidth / inputAspect);
} else if (inputAspect < outputAspect) {
targetWidth = int(targetHeight * inputAspect);
}
// Make sure we are still in the bounds of the output
assert(targetWidth <= out.w);
assert(targetHeight <= out.h);
// Center the image on the output surface
byte *dst = (byte *)out.getBasePtr((out.w - targetWidth) / 2, (out.h - targetHeight) / 2);
const uint dstLineIncrease = out.pitch - targetWidth * out.format.bytesPerPixel;
const float scaleFactorX = (float)targetWidth / in.w;
const float scaleFactorY = (float)targetHeight / in.h;
for (int y = 0; y < targetHeight; ++y) {
const float yFrac = (y / scaleFactorY);
const int y1 = (int)yFrac;
const int y2 = (y1 + 1 < in.h) ? (y1 + 1) : (in.h - 1);
for (int x = 0; x < targetWidth; ++x) {
const float xFrac = (x / scaleFactorX);
const int x1 = (int)xFrac;
const int x2 = (x1 + 1 < in.w) ? (x1 + 1) : (in.w - 1);
// Look up colors at the points
uint8 p1R, p1G, p1B;
in.format.colorToRGBT<ColorMask>(READ_UINT16(in.getBasePtr(x1, y1)), p1R, p1G, p1B);
uint8 p2R, p2G, p2B;
in.format.colorToRGBT<ColorMask>(READ_UINT16(in.getBasePtr(x2, y1)), p2R, p2G, p2B);
uint8 p3R, p3G, p3B;
in.format.colorToRGBT<ColorMask>(READ_UINT16(in.getBasePtr(x1, y2)), p3R, p3G, p3B);
uint8 p4R, p4G, p4B;
in.format.colorToRGBT<ColorMask>(READ_UINT16(in.getBasePtr(x2, y2)), p4R, p4G, p4B);
const float xDiff = xFrac - x1;
const float yDiff = yFrac - y1;
uint8 pR = (uint8)((1 - yDiff) * ((1 - xDiff) * p1R + xDiff * p2R) + yDiff * ((1 - xDiff) * p3R + xDiff * p4R));
uint8 pG = (uint8)((1 - yDiff) * ((1 - xDiff) * p1G + xDiff * p2G) + yDiff * ((1 - xDiff) * p3G + xDiff * p4G));
uint8 pB = (uint8)((1 - yDiff) * ((1 - xDiff) * p1B + xDiff * p2B) + yDiff * ((1 - xDiff) * p3B + xDiff * p4B));
WRITE_UINT16(dst, out.format.RGBToColorT<ColorMask>(pR, pG, pB));
dst += 2;
}
// Move to the next line
dst = (byte *)dst + dstLineIncrease;
}
}
}
/**
* Copies the current screen contents to a new surface, using RGB565 format.
* WARNING: surf->free() must be called by the user to avoid leaking.
*
* @param surf the surface to store the data in it
*/
static bool grabScreen565(Graphics::Surface *surf) {
Graphics::Surface *screen = g_system->lockScreen();
if (!screen)
return false;
assert(screen->format.bytesPerPixel == 1 || screen->format.bytesPerPixel == 2
|| screen->format.bytesPerPixel == 4);
assert(screen->getPixels() != 0);
Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
surf->create(screen->w, screen->h, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
byte *palette = 0;
if (screenFormat.bytesPerPixel == 1) {
palette = new byte[256 * 3];
assert(palette);
g_system->getPaletteManager()->grabPalette(palette, 0, 256);
}
for (int y = 0; y < screen->h; ++y) {
for (int x = 0; x < screen->w; ++x) {
byte r = 0, g = 0, b = 0;
if (screenFormat.bytesPerPixel == 1) {
uint8 pixel = *(uint8 *)screen->getBasePtr(x, y);
r = palette[pixel * 3 + 0];
g = palette[pixel * 3 + 1];
b = palette[pixel * 3 + 2];
} else if (screenFormat.bytesPerPixel == 2) {
uint16 col = READ_UINT16(screen->getBasePtr(x, y));
screenFormat.colorToRGB(col, r, g, b);
} else if (screenFormat.bytesPerPixel == 4) {
uint32 col = READ_UINT32(screen->getBasePtr(x, y));
screenFormat.colorToRGB(col, r, g, b);
}
*((uint16 *)surf->getBasePtr(x, y)) = surf->format.RGBToColor(r, g, b);
}
}
delete[] palette;
g_system->unlockScreen();
return true;
}
static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) {
int height;
if ((in.w == 320 && in.h == 200) || (in.w == 640 && in.h == 400)) {
height = kThumbnailHeight1;
} else {
height = kThumbnailHeight2;
}
out.create(kThumbnailWidth, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
assert(out.format == Graphics::createPixelFormat<565>());
scaleThumbnail<Graphics::ColorMasks<565> >(in, out);
in.free();
return true;
}
bool createThumbnailFromScreen(Graphics::Surface *surf) {
assert(surf);
Graphics::Surface screen;
if (!grabScreen565(&screen))
return false;
return createThumbnail(*surf, screen);
}
bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, const uint8 *palette) {
assert(surf);
Graphics::Surface screen;
screen.create(w, h, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
for (int y = 0; y < screen.h; ++y) {
for (int x = 0; x < screen.w; ++x) {
byte r, g, b;
r = palette[pixels[y * w + x] * 3];
g = palette[pixels[y * w + x] * 3 + 1];
b = palette[pixels[y * w + x] * 3 + 2];
*((uint16 *)screen.getBasePtr(x, y)) = screen.format.RGBToColor(r, g, b);
}
}
return createThumbnail(*surf, screen);
}
bool createThumbnail(Graphics::Surface *surf, Graphics::ManagedSurface *in) {
assert(surf);
Graphics::Surface screen;
if (in->hasPalette()) {
uint8 palette[3 * 256];
in->grabPalette(palette, 0, 256);
return createThumbnail(surf, (const uint8 *)in->getPixels(), in->w, in->h, palette);
} else {
screen.convertFrom(in->rawSurface(), Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
return createThumbnail(*surf, screen);
}
}
// this is somewhat awkward, but createScreenShot should logically be in graphics,
// but moving other functions in this file into that namespace breaks several engines
namespace Graphics {
bool createScreenShot(Graphics::Surface &surf) {
Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
//convert surface to 2 bytes pixel format to avoid problems with palette saving and loading
if ((screenFormat.bytesPerPixel == 1) || (screenFormat.bytesPerPixel == 2)) {
return grabScreen565(&surf);
} else {
Graphics::Surface *screen = g_system->lockScreen();
if (!screen) {
return false;
}
surf.create(screen->w, screen->h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
for (int y = 0; y < screen->h; ++y) {
for (int x = 0; x < screen->w; ++x) {
byte r = 0, g = 0, b = 0, a = 0;
uint32 col = READ_UINT32(screen->getBasePtr(x, y));
screenFormat.colorToARGB(col, a, r, g, b);
*((uint32 *)surf.getBasePtr(x, y)) = surf.format.ARGBToColor(a, r, g, b);
}
}
g_system->unlockScreen();
return true;
}
}
} // End of namespace Graphics

112
graphics/scaler/tv.cpp Normal file
View File

@@ -0,0 +1,112 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "graphics/scaler/tv.h"
#include "graphics/scaler.h"
#include "graphics/colormasks.h"
void TVScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) {
if (_format.bytesPerPixel == 2) {
if (_format.gLoss == 2)
scaleIntern<Graphics::ColorMasks<565> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
scaleIntern<Graphics::ColorMasks<555> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
} else {
if (_format.aLoss == 0)
scaleIntern<Graphics::ColorMasks<8888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
else
scaleIntern<Graphics::ColorMasks<888> >(srcPtr, srcPitch, dstPtr, dstPitch, width, height);
}
}
uint TVScaler::increaseFactor() {
return _factor;
}
uint TVScaler::decreaseFactor() {
return _factor;
}
template<typename ColorMask>
void TVScaler::scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch,
int width, int height) {
typedef typename ColorMask::PixelType Pixel;
const uint32 nextlineSrc = srcPitch / sizeof(Pixel);
const Pixel *p = (const Pixel *)srcPtr;
const uint32 nextlineDst = dstPitch / sizeof(Pixel);
Pixel *q = (Pixel *)dstPtr;
while (height--) {
for (int i = 0, j = 0; i < width; ++i, j += 2) {
Pixel p1 = *(p + i);
Pixel pi;
pi = (((p1 & ColorMask::kRedBlueMask) * 7) >> 3) & ColorMask::kRedBlueMask;
pi |= (((p1 & ColorMask::kGreenMask) * 7) >> 3) & ColorMask::kGreenMask;
pi |= p1 & ColorMask::kAlphaMask;
uint8 r, g, b;
_format.colorToRGB(p1,r,g,b);
pi = _format.RGBToColor((r*7)/8, (g*7) / 8, (b*7) / 8);
*(q + j) = p1;
*(q + j + 1) = p1;
*(q + j + nextlineDst) = pi;
*(q + j + nextlineDst + 1) = pi;
}
p += nextlineSrc;
q += nextlineDst << 1;
}
}
class TVPlugin final : public ScalerPluginObject {
public:
TVPlugin();
Scaler *createInstance(const Graphics::PixelFormat &format) const override;
bool canDrawCursor() const override { return false; }
uint extraPixels() const override { return 0; }
const char *getName() const override;
const char *getPrettyName() const override;
};
TVPlugin::TVPlugin() {
_factors.push_back(2);
}
Scaler *TVPlugin::createInstance(const Graphics::PixelFormat &format) const {
return new TVScaler(format);
}
const char *TVPlugin::getName() const {
return "tv";
}
const char *TVPlugin::getPrettyName() const {
return "TV";
}
REGISTER_PLUGIN_STATIC(TV, PLUGIN_TYPE_SCALER, TVPlugin);

40
graphics/scaler/tv.h Normal file
View File

@@ -0,0 +1,40 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRAPHICS_SCALER_TV_H
#define GRAPHICS_SCALER_TV_H
#include "graphics/scalerplugin.h"
class TVScaler : public Scaler {
public:
TVScaler(const Graphics::PixelFormat &format) : Scaler(format) { _factor = 2; }
uint increaseFactor() override;
uint decreaseFactor() override;
private:
virtual void scaleIntern(const uint8 *srcPtr, uint32 srcPitch,
uint8 *dstPtr, uint32 dstPitch, int width, int height, int x, int y) override;
template<typename ColorMask>
void scaleIntern(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr,
uint32 dstPitch, int width, int height);
};
#endif