Skip to content

Commit

Permalink
Optimized how Dead Space models are loaded, since some were really sl…
Browse files Browse the repository at this point in the history
…ow to load.
  • Loading branch information
MeltyPlayer committed May 6, 2024
1 parent 7f81995 commit 6d5ff77
Show file tree
Hide file tree
Showing 36 changed files with 358 additions and 131 deletions.
3 changes: 2 additions & 1 deletion FinModelUtility/Fin/Fin/Fin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
<PackageReference Include="SubstreamSharp" Version="1.0.3" />
<PackageReference Include="System.Drawing.Common" Version="8.0.4" />
<PackageReference Include="System.IO.Abstractions" Version="21.0.2" />
<PackageReference Include="schema" Version="0.5.0" />
<PackageReference Include="schema" Version="0.5.1" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="ZString" Version="2.6.0" />
</ItemGroup>
</Project>
7 changes: 2 additions & 5 deletions FinModelUtility/Fin/Fin/src/image/formats/BImage.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System;
using System.Drawing;
using System.IO;

using fin.util.hash;
using System.IO.Hashing;

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
Expand Down Expand Up @@ -111,9 +110,7 @@ public override unsafe int GetHashCode() {
using var fastLock = this.Lock();
var span = fastLock.Bytes;

var hash = FluentHash.Start();
hash.With(span);

var hash = (int) Crc32.HashToUInt32(span);
this.cachedHash_ = hash;
return hash;
}
Expand Down
50 changes: 50 additions & 0 deletions FinModelUtility/Fin/Fin/src/image/io/image/Dxt1ImageReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;

using fin.image.io.tile;

using schema.binary;

using SixLabors.ImageSharp.PixelFormats;

namespace fin.image.io.image {
public class Dxt1ImageReader(
int width,
int height,
int subTileCountInAxis = 2,
int subTileSizeInAxis = 4,
bool flipBlocksHorizontally = true)
: IImageReader<IImage<Rgb24>> {
private readonly Dxt1TileReader tileReader_
= new(subTileCountInAxis, subTileSizeInAxis, flipBlocksHorizontally);

public IImage<Rgb24> ReadImage(IBinaryReader br) {
Span<ushort> shortBuffer = stackalloc ushort[2];
Span<Rgb24> paletteBuffer = stackalloc Rgb24[4];
Span<byte> indicesBuffer = stackalloc byte[4];

var image = this.tileReader_.CreateImage(width, height);
using var imageLock = image.Lock();
var scan0 = imageLock.Pixels;

var tileXCount
= (int) Math.Ceiling(1f * width / this.tileReader_.TileWidth);
var tileYCount = height / this.tileReader_.TileHeight;

for (var tileY = 0; tileY < tileYCount; ++tileY) {
for (var tileX = 0; tileX < tileXCount; ++tileX) {
this.tileReader_.Decode(br,
scan0,
tileX,
tileY,
width,
height,
shortBuffer,
paletteBuffer,
indicesBuffer);
}
}

return image;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,37 @@
using SixLabors.ImageSharp.PixelFormats;

namespace fin.image.io.image {
public class CmprImageReader : IImageReader<IImage<Rgba32>> {
private readonly CmprTileReader tileReader_ = new();
private readonly int width_;
private readonly int height_;

public CmprImageReader(int width, int height) {
this.width_ = width;
this.height_ = height;
}
public class Dxt1aImageReader(
int width,
int height,
int subTileCountInAxis = 2,
int subTileSizeInAxis = 4,
bool flipBlocksHorizontally = true)
: IImageReader<IImage<Rgba32>> {
private readonly Dxt1aTileReader tileReader_
= new(subTileCountInAxis, subTileSizeInAxis, flipBlocksHorizontally);

public IImage<Rgba32> ReadImage(IBinaryReader br) {
Span<ushort> shortBuffer = stackalloc ushort[2];
Span<Rgba32> paletteBuffer = stackalloc Rgba32[4];
Span<byte> indicesBuffer = stackalloc byte[4];

var image = this.tileReader_.CreateImage(this.width_, this.height_);
var image = this.tileReader_.CreateImage(width, height);
using var imageLock = image.Lock();
var scan0 = imageLock.Pixels;

var tileXCount = (int) Math.Ceiling(1f * this.width_ / this.tileReader_.TileWidth);
var tileYCount = this.height_ / this.tileReader_.TileHeight;
var tileXCount
= (int) Math.Ceiling(1f * width / this.tileReader_.TileWidth);
var tileYCount = height / this.tileReader_.TileHeight;

for (var tileY = 0; tileY < tileYCount; ++tileY) {
for (var tileX = 0; tileX < tileXCount; ++tileX) {
this.tileReader_.Decode(br,
scan0,
tileX,
tileY,
this.width_,
this.height_,
width,
height,
shortBuffer,
paletteBuffer,
indicesBuffer);
Expand Down
142 changes: 142 additions & 0 deletions FinModelUtility/Fin/Fin/src/image/io/tile/Dxt1TileReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using System.Runtime.CompilerServices;

using fin.image.formats;
using fin.util.color;

using schema.binary;

using SixLabors.ImageSharp.PixelFormats;

namespace fin.image.io.tile {
public readonly struct Dxt1TileReader(
int subTileCountInAxis = 2,
int subTileSizeInAxis = 4,
bool flipBlocksHorizontally = true)
: ITileReader<Rgb24> {
public IImage<Rgb24> CreateImage(int width, int height)
=> new Rgb24Image(PixelFormat.DXT1, width, height);

private readonly int tileSizeInAxis_
= subTileCountInAxis * subTileSizeInAxis;

public int TileWidth => this.tileSizeInAxis_;
public int TileHeight => this.tileSizeInAxis_;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Decode(IBinaryReader br,
Span<Rgb24> scan0,
int tileX,
int tileY,
int imageWidth,
int imageHeight) {
Span<ushort> shortBuffer = stackalloc ushort[2];
Span<Rgb24> paletteBuffer = stackalloc Rgb24[4];
Span<byte> indicesBuffer = stackalloc byte[4];
this.Decode(br,
scan0,
tileX,
tileY,
imageWidth,
imageHeight,
shortBuffer,
paletteBuffer,
indicesBuffer);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Decode(IBinaryReader br,
Span<Rgb24> scan0,
int tileX,
int tileY,
int imageWidth,
int imageHeight,
Span<ushort> shortBuffer,
Span<Rgb24> paletteBuffer,
Span<byte> indicesBuffer) {
for (var j = 0; j < subTileCountInAxis; ++j) {
for (var i = 0; i < subTileCountInAxis; ++i) {
DecodeSubblock_(
br,
shortBuffer,
scan0,
paletteBuffer,
indicesBuffer,
tileX * TileWidth + i * subTileSizeInAxis,
tileY * TileHeight + j * subTileSizeInAxis,
imageWidth,
imageHeight);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void DecodeSubblock_(
IBinaryReader br,
Span<ushort> shortBuffer,
Span<Rgb24> scan0,
Span<Rgb24> paletteBuffer,
Span<byte> indicesBuffer,
int imageX,
int imageY,
int imageWidth,
int imageHeight) {
br.ReadUInt16s(shortBuffer);
DecodePalette_(shortBuffer, paletteBuffer);

br.ReadBytes(indicesBuffer);
for (var j = 0; j < subTileSizeInAxis; ++j) {
if (imageY + j >= imageHeight) {
break;
}

var indices = indicesBuffer[j];
var scan0Offset = (imageY + j) * imageWidth + imageX;

for (var i = 0; i < subTileSizeInAxis; ++i) {
if (imageX + i >= imageWidth) {
break;
}

var shiftIndexAmount = !flipBlocksHorizontally ? i : 3 - i;
var index = (indices >> (2 * shiftIndexAmount)) & 0b11;
scan0[scan0Offset + i] = paletteBuffer[index];
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodePalette_(
ReadOnlySpan<ushort> colorValues,
Span<Rgb24> palette) {
var color1Value = colorValues[0];
var color2Value = colorValues[1];

ColorUtil.SplitRgb565(color1Value, out var r1, out var g1, out var b1);
ColorUtil.SplitRgb565(color2Value, out var r2, out var g2, out var b2);

palette[0] = new Rgb24(r1, g1, b1);
palette[1] = new Rgb24(r2, g2, b2);

if (color1Value > color2Value) {
// 3rd color in palette is 1/3 from 1st to 2nd.
palette[2] = new Rgb24(
(byte) (((r1 << 1) + r2) / 3),
(byte) (((g1 << 1) + g2) / 3),
(byte) (((b1 << 1) + b2) / 3));
// 4th color in palette is 2/3 from 1st to 2nd.
palette[3] = new Rgb24(
(byte) ((r1 + (r2 << 1)) / 3),
(byte) ((g1 + (g2 << 1)) / 3),
(byte) ((b1 + (b2 << 1)) / 3));
} else {
// 3rd color in palette is halfway between 1st and 2nd.
palette[2] = new Rgb24((byte) ((r1 + r2) >> 1),
(byte) ((g1 + g2) >> 1),
(byte) ((b1 + b2) >> 1));
// 4th color in palette is black.
palette[3] = new Rgb24(0, 0, 0);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,19 @@
using SixLabors.ImageSharp.PixelFormats;

namespace fin.image.io.tile {
/// <summary>
/// Seems EERILY similar to the DXT1 format--they might actually be exactly
/// the same.
/// </summary>
public readonly struct CmprTileReader : ITileReader<Rgba32> {
public readonly struct Dxt1aTileReader(
int subTileCountInAxis = 2,
int subTileSizeInAxis = 4,
bool flipBlocksHorizontally = true)
: ITileReader<Rgba32> {
public IImage<Rgba32> CreateImage(int width, int height)
=> new Rgba32Image(PixelFormat.DXT1, width, height);
=> new Rgba32Image(PixelFormat.DXT1A, width, height);

private const int SUB_TILE_COUNT_IN_AXIS = 2;
private const int SUB_TILE_SIZE_IN_AXIS = 4;
private readonly int tileSizeInAxis_
= subTileCountInAxis * subTileSizeInAxis;

private const int TILE_SIZE_IN_AXIS =
CmprTileReader.SUB_TILE_COUNT_IN_AXIS *
CmprTileReader.SUB_TILE_SIZE_IN_AXIS;

public int TileWidth => CmprTileReader.TILE_SIZE_IN_AXIS;
public int TileHeight => CmprTileReader.TILE_SIZE_IN_AXIS;
public int TileWidth => this.tileSizeInAxis_;
public int TileHeight => this.tileSizeInAxis_;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Decode(IBinaryReader br,
Expand All @@ -50,32 +46,32 @@ public unsafe void Decode(IBinaryReader br,

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Decode(IBinaryReader br,
Span<Rgba32> scan0,
int tileX,
int tileY,
int imageWidth,
int imageHeight,
Span<ushort> shortBuffer,
Span<Rgba32> paletteBuffer,
Span<byte> indicesBuffer) {
for (var j = 0; j < SUB_TILE_COUNT_IN_AXIS; ++j) {
for (var i = 0; i < SUB_TILE_COUNT_IN_AXIS; ++i) {
DecodeCmprSubblock_(
Span<Rgba32> scan0,
int tileX,
int tileY,
int imageWidth,
int imageHeight,
Span<ushort> shortBuffer,
Span<Rgba32> paletteBuffer,
Span<byte> indicesBuffer) {
for (var j = 0; j < subTileCountInAxis; ++j) {
for (var i = 0; i < subTileCountInAxis; ++i) {
DecodeSubblock_(
br,
shortBuffer,
scan0,
paletteBuffer,
indicesBuffer,
tileX * TileWidth + i * SUB_TILE_SIZE_IN_AXIS,
tileY * TileHeight + j * SUB_TILE_SIZE_IN_AXIS,
tileX * TileWidth + i * subTileSizeInAxis,
tileY * TileHeight + j * subTileSizeInAxis,
imageWidth,
imageHeight);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeCmprSubblock_(
private void DecodeSubblock_(
IBinaryReader br,
Span<ushort> shortBuffer,
Span<Rgba32> scan0,
Expand All @@ -86,30 +82,31 @@ private static void DecodeCmprSubblock_(
int imageWidth,
int imageHeight) {
br.ReadUInt16s(shortBuffer);
DecodeCmprPalette_(shortBuffer, paletteBuffer);
DecodePalette_(shortBuffer, paletteBuffer);

br.ReadBytes(indicesBuffer);
for (var j = 0; j < 4; ++j) {
for (var j = 0; j < subTileSizeInAxis; ++j) {
if (imageY + j >= imageHeight) {
break;
}

var indices = indicesBuffer[j];
var scan0Offset = (imageY + j) * imageWidth + imageX;

for (var i = 0; i < 4; ++i) {
for (var i = 0; i < subTileSizeInAxis; ++i) {
if (imageX + i >= imageWidth) {
break;
}

var index = (indices >> (2 * (3 - i))) & 0b11;
var shiftIndexAmount = !flipBlocksHorizontally ? i : 3 - i;
var index = (indices >> (2 * shiftIndexAmount)) & 0b11;
scan0[scan0Offset + i] = paletteBuffer[index];
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeCmprPalette_(
private static void DecodePalette_(
ReadOnlySpan<ushort> colorValues,
Span<Rgba32> palette) {
var color1Value = colorValues[0];
Expand Down
Loading

0 comments on commit 6d5ff77

Please sign in to comment.