Files
LibretroAdmin cd38818e52 Common: pointer and size instead of spans, loops instead of STL algorithms
SHA1, HMAC, hex dumps, PNG loading, file search, DHCP options, HTTP
posts and DirectIOFile reads and writes take pointer plus size; callers
pass their buffers straight through. Contains.h is gone; lookups are
plain loops. File search and FAT FST sorting are in-place heap sorts;
min/max/clamp/copy/fill become ternaries, memcpy and memset. Watch
string import and export had no callers and are removed.
2026-11-07 13:37:26 +01:00

166 lines
4.5 KiB
C++

// Copyright 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "InputCommon/ImageOperations.h"
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include "Common/IOFile.h"
#include "Common/Image.h"
namespace InputCommon
{
namespace
{
Pixel SampleNearest(const ImagePixelData& src, double u, double v)
{
const u32 x = std::clamp(static_cast<u32>(u * src.width), 0u, src.width - 1);
const u32 y = std::clamp(static_cast<u32>(v * src.height), 0u, src.height - 1);
return src.pixels[x + y * src.width];
}
} // namespace
void CopyImageRegion(const ImagePixelData& src, ImagePixelData& dst, const Rect& src_region,
const Rect& dst_region)
{
if (src_region.GetWidth() != dst_region.GetWidth() ||
src_region.GetHeight() != dst_region.GetHeight())
{
return;
}
for (u32 x = 0; x < dst_region.GetWidth(); x++)
{
for (u32 y = 0; y < dst_region.GetHeight(); y++)
{
dst.pixels[(y + dst_region.top) * dst.width + x + dst_region.left] =
src.pixels[(y + src_region.top) * src.width + x + src_region.left];
}
}
}
std::optional<ImagePixelData> LoadImage(const std::string& path)
{
File::IOFile file;
if (!file.Open(path, "rb"))
return std::nullopt;
Common::UniqueBuffer<u8> buffer(file.GetSize());
if (!file.ReadBytes(buffer.data(), file.GetSize()))
return std::nullopt;
ImagePixelData image;
Common::UniqueBuffer<u8> data;
if (!Common::LoadPNG(buffer.data(), buffer.size(), &data, &image.width, &image.height))
return std::nullopt;
image.pixels.resize(image.width * image.height);
for (u32 x = 0; x < image.width; x++)
{
for (u32 y = 0; y < image.height; y++)
{
const u32 index = y * image.width + x;
const auto pixel =
Pixel{data[index * 4], data[index * 4 + 1], data[index * 4 + 2], data[index * 4 + 3]};
image.pixels[index] = pixel;
}
}
return image;
}
bool WriteImage(const std::string& path, const ImagePixelData& image)
{
Common::UniqueBuffer<u8> buffer;
buffer.reset(image.width * image.height * 4);
std::size_t buffer_index = 0;
for (u32 y = 0; y < image.height; ++y)
{
for (u32 x = 0; x < image.width; ++x)
{
const auto index = x + y * image.width;
const auto pixel = image.pixels[index];
buffer[buffer_index++] = pixel.r;
buffer[buffer_index++] = pixel.g;
buffer[buffer_index++] = pixel.b;
buffer[buffer_index++] = pixel.a;
}
}
return Common::SavePNG(path, buffer.data(), Common::ImageByteFormat::RGBA, image.width,
image.height, image.width * 4);
}
ImagePixelData Resize(ResizeMode mode, const ImagePixelData& src, u32 new_width, u32 new_height)
{
ImagePixelData result(new_width, new_height);
for (u32 x = 0; x < new_width; x++)
{
const double u = x / static_cast<double>(new_width - 1);
for (u32 y = 0; y < new_height; y++)
{
const double v = y / static_cast<double>(new_height - 1);
switch (mode)
{
case ResizeMode::Nearest:
result.pixels[y * new_width + x] = SampleNearest(src, u, v);
break;
}
}
}
return result;
}
ImagePixelData ResizeKeepAspectRatio(ResizeMode mode, const ImagePixelData& src, u32 new_width,
u32 new_height, const Pixel& background_color)
{
ImagePixelData result(new_width, new_height, background_color);
const double corrected_height = new_width * (src.height / static_cast<double>(src.width));
const double corrected_width = new_height * (src.width / static_cast<double>(src.height));
// initially no borders
u32 top = 0;
u32 left = 0;
ImagePixelData resized;
if (corrected_height <= new_height)
{
// Handle vertical padding
const int diff = new_height - std::trunc(corrected_height);
top = diff / 2;
if (diff % 2 != 0)
{
// If the difference is odd, we need to have one side be slightly larger
top += 1;
}
resized = Resize(mode, src, new_width, corrected_height);
}
else
{
// Handle horizontal padding
const int diff = new_width - std::trunc(corrected_width);
left = diff / 2;
if (diff % 2 != 0)
{
// If the difference is odd, we need to have one side be slightly larger
left += 1;
}
resized = Resize(mode, src, corrected_width, new_height);
}
CopyImageRegion(resized, result, Rect{0, 0, resized.width, resized.height},
Rect{left, top, left + resized.width, top + resized.height});
return result;
}
} // namespace InputCommon