Files
UnrealEngine/Engine/Plugins/Mutable/Source/MutableRuntime/Private/MuR/OpImageCrop.cpp
2025-05-18 13:04:45 +08:00

101 lines
3.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MuR/ImagePrivate.h"
#include "MuR/MutableMath.h"
#include "MuR/Platform.h"
#include "Math/IntRect.h"
namespace mu
{
bool FImageOperator::ImageCrop(TSharedPtr<FImage>& OutCropped, int32 CompressionQuality, const FImage* InBase, const box< FIntVector2 >& Rect)
{
check(InBase);
TSharedPtr<FImage> BaseReformat;
TSharedPtr<FImage> Cropped = OutCropped;
EImageFormat BaseFormat = InBase->GetFormat();
EImageFormat UncompressedFormat = GetUncompressedFormat(BaseFormat);
if (BaseFormat != UncompressedFormat)
{
// Compressed formats need decompression + compression after crop
// \TODO: This may use some additional untracked memory locally in this function.
BaseReformat = ImagePixelFormat(CompressionQuality, InBase, UncompressedFormat);
InBase = BaseReformat.Get();
Cropped = CreateImage(OutCropped->GetSizeX(), OutCropped->GetSizeY(), OutCropped->GetLODCount(), UncompressedFormat, EInitializationType::NotInitialized);
}
// In case the base is smaller than the crop extension, make it as if the base extends indefinitely
// with black pixels.
if (Rect.min.X + Rect.size.X > InBase->GetSizeX() || Rect.min.Y + Rect.size.Y > InBase->GetSizeY())
{
Cropped->InitToBlack();
}
const FImageFormatData& finfo = GetImageFormatData(UncompressedFormat);
check(Cropped);
check(Cropped->GetSizeX() == Rect.size[0]);
check(Cropped->GetSizeY() == Rect.size[1]);
// TODO: better error control. This happens if some layouts are corrupt.
bool bCorrect =
(Rect.min[0] >= 0 && Rect.min[1] >= 0) &&
(Rect.size[0] >= 0 && Rect.size[1] >= 0) &&
(Rect.min[0] + Rect.size[0] <= InBase->GetSizeX()) &&
(Rect.min[1] + Rect.size[1] <= InBase->GetSizeY());
if (!bCorrect)
{
return false;
}
// Block images are not supported for now
check(finfo.PixelsPerBlockX == 1);
check(finfo.PixelsPerBlockY == 1);
checkf(Rect.min[0] % finfo.PixelsPerBlockX == 0, TEXT("Rect must snap to blocks."));
checkf(Rect.min[1] % finfo.PixelsPerBlockY == 0, TEXT("Rect must snap to blocks."));
checkf(Rect.size[0] % finfo.PixelsPerBlockX == 0, TEXT("Rect must snap to blocks."));
checkf(Rect.size[1] % finfo.PixelsPerBlockY == 0, TEXT("Rect must snap to blocks."));
const uint32 BytesPerPixel = finfo.BytesPerBlock;
const uint8* BaseBuf = InBase->GetLODData(0);
uint8* CropBuf = Cropped->GetLODData(0);
FImageSize BaseSize = InBase->GetSize();
FImageSize CroppedSize = Cropped->GetSize();
FIntRect BaseRect = FIntRect(
FMath::Min<int32>(BaseSize.X, Rect.min[0]),
FMath::Min<int32>(BaseSize.Y, Rect.min[1]),
FMath::Min<int32>(BaseSize.X, Rect.min[0] + Rect.size[0]),
FMath::Min<int32>(BaseSize.Y, Rect.min[1] + Rect.size[1]));
const uint32 NumBytesPerRow = FMath::Max<uint32>(0, (BaseRect.Max.X - BaseRect.Min.X) * BytesPerPixel);
for (int32 Y = 0; Y < BaseRect.Max.Y - BaseRect.Min.Y; ++Y)
{
FMemory::Memcpy(
CropBuf + CroppedSize.X*Y*BytesPerPixel,
BaseBuf + (BaseSize.X*(Y + BaseRect.Min.Y) + BaseRect.Min.X)*BytesPerPixel,
NumBytesPerRow);
}
if (BaseFormat != UncompressedFormat)
{
ReleaseImage(BaseReformat);
bool bSuccess = false;
ImagePixelFormat(bSuccess, CompressionQuality, OutCropped.Get(), Cropped.Get());
check(bSuccess);
ReleaseImage(Cropped);
}
return true;
}
}