mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 10:05:46 -05:00
IntHalfbandFilters: set workInterpolateLowerHalf and UpperHalf optimized methods as defaults
This commit is contained in:
parent
172de27047
commit
137647e2ca
@ -287,7 +287,7 @@ public:
|
||||
}
|
||||
|
||||
// upsample by 2, from lower half of original spectrum
|
||||
bool workInterpolateLowerHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
bool workInterpolateLowerHalfZeroStuffing(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
@ -371,6 +371,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Optimized upsampler by 2 not calculating FIR with inserted null samples */
|
||||
bool workInterpolateLowerHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // imag
|
||||
sampleOut->setImag(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // - real
|
||||
m_state = 1; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
case 1:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(-s.real());
|
||||
sampleOut->setImag(-s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 2; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
|
||||
case 2:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // - imag
|
||||
sampleOut->setImag(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // real
|
||||
m_state = 3; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
default:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(s.real());
|
||||
sampleOut->setImag(s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 0; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
}
|
||||
}
|
||||
|
||||
// downsample by 2, return upper half of original spectrum
|
||||
bool workDecimateUpperHalf(Sample* sample)
|
||||
{
|
||||
@ -441,7 +508,7 @@ public:
|
||||
}
|
||||
|
||||
// upsample by 2, move original spectrum to upper half
|
||||
bool workInterpolateUpperHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
bool workInterpolateUpperHalfZeroStuffing(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
@ -525,6 +592,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Optimized upsampler by 2 not calculating FIR with inserted null samples */
|
||||
bool workInterpolateUpperHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // - imag
|
||||
sampleOut->setImag(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // + real
|
||||
m_state = 1; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
case 1:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(-s.real());
|
||||
sampleOut->setImag(-s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 2; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
|
||||
case 2:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // + imag
|
||||
sampleOut->setImag(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // - real
|
||||
m_state = 3; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
default:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(s.real());
|
||||
sampleOut->setImag(s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 0; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
}
|
||||
}
|
||||
|
||||
void myDecimate(const Sample* sample1, Sample* sample2)
|
||||
{
|
||||
m_samples[m_ptr][0] = sample1->real();
|
||||
|
@ -203,11 +203,78 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// upsample by 2, from lower half of original spectrum - double buffer variant
|
||||
/** Optimized upsampler by 2 not calculating FIR with inserted null samples */
|
||||
bool workInterpolateLowerHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // imag
|
||||
sampleOut->setImag(-m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // - real
|
||||
m_state = 1; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
case 1:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(-s.real());
|
||||
sampleOut->setImag(-s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samplesDB[m_ptr][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr][1] = sampleIn->imag();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 2; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
|
||||
case 2:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(-m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // - imag
|
||||
sampleOut->setImag(m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // real
|
||||
m_state = 3; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
default:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(s.real());
|
||||
sampleOut->setImag(s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samplesDB[m_ptr][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr][1] = sampleIn->imag();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 0; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
}
|
||||
}
|
||||
|
||||
// upsample by 2, from lower half of original spectrum - double buffer variant
|
||||
bool workInterpolateLowerHalfZeroStuffing(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
@ -335,11 +402,78 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// upsample by 2, move original spectrum to upper half - double buffer variant
|
||||
/** Optimized upsampler by 2 not calculating FIR with inserted null samples */
|
||||
bool workInterpolateUpperHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(-m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // - imag
|
||||
sampleOut->setImag(m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // + real
|
||||
m_state = 1; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
case 1:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(-s.real());
|
||||
sampleOut->setImag(-s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samplesDB[m_ptr][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr][1] = sampleIn->imag();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 2; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
|
||||
case 2:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // + imag
|
||||
sampleOut->setImag(-m_samplesDB[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // - real
|
||||
m_state = 3; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
default:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(s.real());
|
||||
sampleOut->setImag(s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samplesDB[m_ptr][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr][1] = sampleIn->imag();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samplesDB[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 0; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
}
|
||||
}
|
||||
|
||||
// upsample by 2, move original spectrum to upper half - double buffer variant
|
||||
bool workInterpolateUpperHalfZeroStuffing(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
|
@ -207,7 +207,7 @@ public:
|
||||
}
|
||||
|
||||
// upsample by 2, from lower half of original spectrum - double buffer variant
|
||||
bool workInterpolateLowerHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
bool workInterpolateLowerHalfZeroStuffing(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
@ -287,6 +287,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Optimized upsampler by 2 not calculating FIR with inserted null samples */
|
||||
bool workInterpolateLowerHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // imag
|
||||
sampleOut->setImag(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // - real
|
||||
m_state = 1; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
case 1:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(-s.real());
|
||||
sampleOut->setImag(-s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 2; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
|
||||
case 2:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // - imag
|
||||
sampleOut->setImag(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // real
|
||||
m_state = 3; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
default:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(s.real());
|
||||
sampleOut->setImag(s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 0; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
}
|
||||
}
|
||||
|
||||
// downsample by 2, return upper half of original spectrum
|
||||
bool workDecimateUpperHalf(Sample* sample)
|
||||
{
|
||||
@ -339,7 +406,7 @@ public:
|
||||
}
|
||||
|
||||
// upsample by 2, move original spectrum to upper half - double buffer variant
|
||||
bool workInterpolateUpperHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
bool workInterpolateUpperHalfZeroStuffing(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
@ -419,6 +486,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Optimized upsampler by 2 not calculating FIR with inserted null samples */
|
||||
bool workInterpolateUpperHalf(Sample* sampleIn, Sample *sampleOut)
|
||||
{
|
||||
Sample s;
|
||||
|
||||
switch(m_state)
|
||||
{
|
||||
case 0:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // - imag
|
||||
sampleOut->setImag(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // + real
|
||||
m_state = 1; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
case 1:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(-s.real());
|
||||
sampleOut->setImag(-s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 2; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
|
||||
case 2:
|
||||
// return the middle peak
|
||||
sampleOut->setReal(m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][1]); // + imag
|
||||
sampleOut->setImag(-m_samples[m_ptr + (HBFIRFilterTraits<HBFilterOrder>::hbOrder/4) - 1][0]); // - real
|
||||
m_state = 3; // next state
|
||||
return false; // tell caller we didn't consume the sample
|
||||
|
||||
default:
|
||||
// calculate with non null samples
|
||||
doInterpolateFIR(&s);
|
||||
sampleOut->setReal(s.real());
|
||||
sampleOut->setImag(s.imag());
|
||||
|
||||
// insert sample into ring double buffer
|
||||
m_samples[m_ptr][0] = sampleIn->real();
|
||||
m_samples[m_ptr][1] = sampleIn->imag();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][0] = sampleIn->real();
|
||||
m_samples[m_ptr + HBFIRFilterTraits<HBFilterOrder>::hbOrder/2][1] = sampleIn->imag();
|
||||
|
||||
// advance pointer
|
||||
if (m_ptr < (HBFIRFilterTraits<HBFilterOrder>::hbOrder/2) - 1) {
|
||||
m_ptr++;
|
||||
} else {
|
||||
m_ptr = 0;
|
||||
}
|
||||
|
||||
m_state = 0; // next state
|
||||
return true; // tell caller we consumed the sample
|
||||
}
|
||||
}
|
||||
|
||||
void myDecimate(const Sample* sample1, Sample* sample2)
|
||||
{
|
||||
storeSample((FixReal) sample1->real(), (FixReal) sample1->imag());
|
||||
|
Loading…
Reference in New Issue
Block a user