mgba-ps3/src/util/convolve.c
2021-04-15 22:10:58 -07:00

183 lines
4.4 KiB
C

/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/convolve.h>
void ConvolutionKernelCreate(struct ConvolutionKernel* kernel, size_t rank, size_t* dims) {
kernel->rank = rank;
kernel->dims = malloc(sizeof(kernel->dims[0]) * rank);
size_t ksize = 1;
size_t i;
for (i = 0; i < rank; ++i) {
kernel->dims[i] = dims[i];
ksize *= dims[i];
}
kernel->kernel = calloc(ksize, sizeof(float));
}
void ConvolutionKernelDestroy(struct ConvolutionKernel* kernel) {
free(kernel->kernel);
free(kernel->dims);
kernel->kernel = NULL;
kernel->dims = NULL;
kernel->rank = 0;
}
void ConvolutionKernelFillRadial(struct ConvolutionKernel* kernel, bool normalize) {
if (kernel->rank != 2) {
return;
}
float support;
if (normalize) {
support = 12.f / (M_PI * (kernel->dims[0] - 1) * (kernel->dims[1] - 1));
} else {
support = 1.f;
}
float wr = (kernel->dims[0] - 1) / 2.f;
float hr = (kernel->dims[1] - 1) / 2.f;
float* elem = kernel->kernel;
size_t y;
for (y = 0; y < kernel->dims[1]; ++y) {
size_t x;
for (x = 0; x < kernel->dims[0]; ++x) {
float r = (1.f - hypotf((x - wr) / wr, (y - hr) / hr)) * support;
*elem = fmaxf(0, r);
++elem;
}
}
}
void ConvolutionKernelFillCircle(struct ConvolutionKernel* kernel, bool normalize) {
if (kernel->rank != 2) {
return;
}
float support;
if (normalize) {
support = 4.f / (M_PI * (kernel->dims[0] - 1) * (kernel->dims[1] - 1));
} else {
support = 1.f;
}
float wr = (kernel->dims[0] - 1) / 2.f;
float hr = (kernel->dims[1] - 1) / 2.f;
float* elem = kernel->kernel;
size_t y;
for (y = 0; y < kernel->dims[1]; ++y) {
size_t x;
for (x = 0; x < kernel->dims[0]; ++x) {
float r = hypotf((x - wr) / wr, (y - hr) / hr);
*elem = r <= 1.f ? support : 0.f;
++elem;
}
}
}
void Convolve1DPad0PackedS32(const int32_t* restrict src, int32_t* restrict dst, size_t length, const struct ConvolutionKernel* restrict kernel) {
if (kernel->rank != 1) {
return;
}
size_t kx2 = kernel->dims[0] / 2;
size_t x;
for (x = 0; x < length; ++x) {
float sum = 0.f;
size_t kx;
for (kx = 0; kx < kernel->dims[0]; ++kx) {
if (x + kx <= kx2) {
continue;
}
size_t cx = x + kx - kx2;
if (cx >= length) {
continue;
}
sum += src[cx] * kernel->kernel[kx];
}
*dst = sum;
++dst;
}
}
void Convolve2DClampPacked8(const uint8_t* restrict src, uint8_t* restrict dst, size_t width, size_t height, size_t stride, const struct ConvolutionKernel* restrict kernel) {
if (kernel->rank != 2) {
return;
}
size_t kx2 = kernel->dims[0] / 2;
size_t ky2 = kernel->dims[1] / 2;
size_t y;
for (y = 0; y < height; ++y) {
uint8_t* orow = &dst[y * stride];
size_t x;
for (x = 0; x < width; ++x) {
float sum = 0.f;
size_t ky;
for (ky = 0; ky < kernel->dims[1]; ++ky) {
size_t cy = 0;
if (y + ky > ky2) {
cy = y + ky - ky2;
}
if (cy >= height) {
cy = height - 1;
}
const uint8_t* irow = &src[cy * stride];
size_t kx;
for (kx = 0; kx < kernel->dims[0]; ++kx) {
size_t cx = 0;
if (x + kx > kx2) {
cx = x + kx - kx2;
}
if (cx >= width) {
cx = width - 1;
}
sum += irow[cx] * kernel->kernel[ky * kernel->dims[0] + kx];
}
}
*orow = sum;
++orow;
}
}
}
void Convolve2DClampChannels8(const uint8_t* restrict src, uint8_t* restrict dst, size_t width, size_t height, size_t stride, size_t channels, const struct ConvolutionKernel* restrict kernel) {
if (kernel->rank != 2) {
return;
}
size_t kx2 = kernel->dims[0] / 2;
size_t ky2 = kernel->dims[1] / 2;
size_t y;
for (y = 0; y < height; ++y) {
uint8_t* orow = &dst[y * stride];
size_t x;
for (x = 0; x < width; ++x) {
size_t c;
for (c = 0; c < channels; ++c) {
float sum = 0.f;
size_t ky;
for (ky = 0; ky < kernel->dims[1]; ++ky) {
size_t cy = 0;
if (y + ky > ky2) {
cy = y + ky - ky2;
}
if (cy >= height) {
cy = height - 1;
}
const uint8_t* irow = &src[cy * stride];
size_t kx;
for (kx = 0; kx < kernel->dims[0]; ++kx) {
size_t cx = 0;
if (x + kx > kx2) {
cx = x + kx - kx2;
}
if (cx >= width) {
cx = width - 1;
}
cx *= channels;
sum += irow[cx + c] * kernel->kernel[ky * kernel->dims[0] + kx];
}
}
*orow = sum;
++orow;
}
}
}
}