scummvm/common/dct.cpp
David Fioramonti cf99bb0a5e COMMON: Update RDFT and DCT cos/sin table constructor usage
When the constructor for the cos/sin table was changed from
number of bits to number of points all usages thoughout the
code should of been changed, but this was missed in RDFT and DCT.

Fixes Trac#10683.
2018-08-25 23:50:07 +02:00

208 lines
4.2 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
// Based on eos' (I)RDFT code which is in turn
// Based upon the (I)DCT code in FFmpeg
// Copyright (c) 2009 Peter Ross <pross@xvid.org>
// Copyright (c) 2010 Alex Converse <alex.converse@gmail.com>
// Copyright (c) 2010 Vitor Sessak
#include "common/dct.h"
namespace Common {
DCT::DCT(int bits, TransformType trans) : _bits(bits), _cos(1 << (_bits + 2) ), _trans(trans), _rdft(nullptr) {
int n = 1 << _bits;
_tCos = _cos.getTable();
_csc2 = new float[n / 2];
_rdft = new RDFT(_bits, (_trans == DCT_III) ? RDFT::IDFT_C2R : RDFT::DFT_R2C);
for (int i = 0; i < (n / 2); i++)
_csc2[i] = 0.5 / sin((M_PI / (2 * n) * (2 * i + 1)));
}
DCT::~DCT() {
delete _rdft;
delete[] _csc2;
}
void DCT::calc(float *data) {
switch (_trans) {
case DCT_I:
calcDCTI(data);
break;
case DCT_II:
calcDCTII(data);
break;
case DCT_III:
calcDCTIII(data);
break;
case DST_I:
calcDSTI(data);
break;
}
}
/* sin((M_PI * x / (2*n)) */
#define SIN(n,x) (_tCos[(n) - (x)])
/* cos((M_PI * x / (2*n)) */
#define COS(n,x) (_tCos[x])
void DCT::calcDCTI(float *data) {
int n = 1 << _bits;
float next = -0.5f * (data[0] - data[n]);
for (int i = 0; i < (n / 2); i++) {
float tmp1 = data[i ];
float tmp2 = data[n - i];
float s = SIN(n, 2 * i);
float c = COS(n, 2 * i);
c *= tmp1 - tmp2;
s *= tmp1 - tmp2;
next += c;
tmp1 = (tmp1 + tmp2) * 0.5f;
data[i ] = tmp1 - s;
data[n - i] = tmp1 + s;
}
_rdft->calc(data);
data[n] = data[1];
data[1] = next;
for (int i = 3; i <= n; i += 2)
data[i] = data[i - 2] - data[i];
}
void DCT::calcDCTII(float *data) {
int n = 1 << _bits;
for (int i = 0; i < (n / 2); i++) {
float tmp1 = data[i ];
float tmp2 = data[n - i - 1];
float s = SIN(n, 2 * i + 1);
s *= tmp1 - tmp2;
tmp1 = (tmp1 + tmp2) * 0.5f;
data[i ] = tmp1 + s;
data[n - i - 1] = tmp1 - s;
}
_rdft->calc(data);
float next = data[1] * 0.5f;
data[1] *= -1;
for (int i = n - 2; i >= 0; i -= 2) {
float inr = data[i ];
float ini = data[i + 1];
float c = COS(n, i);
float s = SIN(n, i);
data[i ] = c * inr + s * ini;
data[i + 1] = next;
next += s * inr - c * ini;
}
}
void DCT::calcDCTIII(float *data) {
int n = 1 << _bits;
float next = data[n - 1];
float inv_n = 1.0f / n;
for (int i = n - 2; i >= 2; i -= 2) {
float val1 = data[i ];
float val2 = data[i - 1] - data[i + 1];
float c = COS(n, i);
float s = SIN(n, i);
data[i ] = c * val1 + s * val2;
data[i + 1] = s * val1 - c * val2;
}
data[1] = 2 * next;
_rdft->calc(data);
for (int i = 0; i < (n / 2); i++) {
float tmp1 = data[i ] * inv_n;
float tmp2 = data[n - i - 1] * inv_n;
float csc = _csc2[i] * (tmp1 - tmp2);
tmp1 += tmp2;
data[i ] = tmp1 + csc;
data[n - i - 1] = tmp1 - csc;
}
}
void DCT::calcDSTI(float *data) {
int n = 1 << _bits;
data[0] = 0;
for (int i = 1; i < (n / 2); i++) {
float tmp1 = data[i ];
float tmp2 = data[n - i];
float s = SIN(n, 2 * i);
s *= tmp1 + tmp2;
tmp1 = (tmp1 - tmp2) * 0.5f;
data[i ] = s + tmp1;
data[n - i] = s - tmp1;
}
data[n / 2] *= 2;
_rdft->calc(data);
data[0] *= 0.5f;
for (int i = 1; i < (n - 2); i += 2) {
data[i + 1] += data[i - 1];
data[i ] = -data[i + 2];
}
data[n - 1] = 0;
}
} // End of namespace Common