Skip to content

Commit 70172ff

Browse files
vtfpp: improve regenerateImageData to reduce data loss when resizing VTF by reusing previous mip data
1 parent e19998b commit 70172ff

File tree

1 file changed

+33
-14
lines changed

1 file changed

+33
-14
lines changed

src/vtfpp/VTF.cpp

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,12 +1481,12 @@ void VTF::removeResourceInternal(Resource::Type type) {
14811481
}
14821482

14831483
void VTF::regenerateImageData(ImageFormat newFormat, uint16_t newWidth, uint16_t newHeight, uint8_t newMipCount, uint16_t newFrameCount, uint8_t newFaceCount, uint16_t newDepth, ImageConversion::ResizeFilter filter, float quality) {
1484-
if (!newWidth) { newWidth = 1; }
1485-
if (!newHeight) { newHeight = 1; }
1486-
if (!newMipCount) { newMipCount = 1; }
1487-
if (!newFrameCount) { newFrameCount = 1; }
1488-
if (!newFaceCount) { newFaceCount = 1; }
1489-
if (!newDepth) { newDepth = 1; }
1484+
if (!newWidth) newWidth = 1;
1485+
if (!newHeight) newHeight = 1;
1486+
if (!newMipCount) newMipCount = 1;
1487+
if (!newFrameCount) newFrameCount = 1;
1488+
if (!newFaceCount) newFaceCount = 1;
1489+
if (!newDepth) newDepth = 1;
14901490

14911491
if (newMipCount > 1) {
14921492
// Valve code doesn't like it when you have compressed mips with padding unless they're lower than 4 on a given dimension!
@@ -1512,19 +1512,38 @@ void VTF::regenerateImageData(ImageFormat newFormat, uint16_t newWidth, uint16_t
15121512
} else {
15131513
newImageData.resize(ImageFormatDetails::getDataLength(this->format, newMipCount, newFrameCount, newFaceCount, newWidth, newHeight, newDepth));
15141514
for (int i = newMipCount - 1; i >= 0; i--) {
1515-
const auto [mipWidth, mipHeight] = ImageDimensions::getMipDims(i, this->width, this->height);
15161515
const auto [newMipWidth, newMipHeight, newMipDepth] = ImageDimensions::getMipDims(i, newWidth, newHeight, newDepth);
1516+
1517+
int sourceMipIndex = 0;
1518+
for (int mip = 0, bestScore = std::numeric_limits<int>::max(); mip < this->mipCount; mip++) {
1519+
const auto [mipWidth, mipHeight] = ImageDimensions::getMipDims(mip, this->width, this->height, false);
1520+
if (mipWidth == newMipWidth && mipHeight == newMipHeight) {
1521+
break;
1522+
}
1523+
const auto widthDiff = static_cast<int>(mipWidth) - static_cast<int>(newMipWidth);
1524+
const auto heightDiff = static_cast<int>(mipHeight) - static_cast<int>(newMipHeight);
1525+
if (widthDiff < 0 || heightDiff < 0) {
1526+
continue;
1527+
}
1528+
if (const auto score = widthDiff + heightDiff; score < bestScore) {
1529+
bestScore = score;
1530+
sourceMipIndex = mip;
1531+
}
1532+
}
1533+
const auto [sourceMipWidth, sourceMipHeight] = ImageDimensions::getMipDims(sourceMipIndex, this->width, this->height);
1534+
15171535
for (int j = 0; j < newFrameCount; j++) {
15181536
for (int k = 0; k < newFaceCount; k++) {
15191537
for (int l = 0; l < newMipDepth; l++) {
1520-
if (i < this->mipCount && j < this->frameCount && k < faceCount && l < this->depth) {
1521-
auto imageSpan = this->getImageDataRaw(i, j, k, l);
1522-
std::vector<std::byte> image{imageSpan.begin(), imageSpan.end()};
1523-
if (this->width != newWidth || this->height != newHeight) {
1524-
image = ImageConversion::resizeImageData(image, this->format, mipWidth, newMipWidth, mipHeight, newMipHeight, this->isSRGB(), filter);
1538+
if (j < this->frameCount && k < faceCount && l < this->depth) {
1539+
auto imageSpan = this->getImageDataRaw(sourceMipIndex, j, k, l);
1540+
std::vector<std::byte> imageBacking;
1541+
if (sourceMipWidth != newMipWidth || sourceMipHeight != newMipHeight) {
1542+
imageBacking = ImageConversion::resizeImageData(imageSpan, this->format, sourceMipWidth, newMipWidth, sourceMipHeight, newMipHeight, this->isSRGB(), filter);
1543+
imageSpan = imageBacking;
15251544
}
1526-
if (uint32_t offset, length; ImageFormatDetails::getDataPosition(offset, length, this->format, i, newMipCount, j, newFrameCount, k, newFaceCount, newWidth, newHeight, l, newDepth) && image.size() == length) {
1527-
std::memcpy(newImageData.data() + offset, image.data(), length);
1545+
if (uint32_t offset, length; ImageFormatDetails::getDataPosition(offset, length, this->format, i, newMipCount, j, newFrameCount, k, newFaceCount, newWidth, newHeight, l, newDepth) && imageSpan.size() == length) {
1546+
std::memcpy(newImageData.data() + offset, imageSpan.data(), length);
15281547
}
15291548
}
15301549
}

0 commit comments

Comments
 (0)