diff --git a/Runtime/API/C_ImageProcessing.lua b/Runtime/API/C_ImageProcessing.lua index 6f7952d43..7283c5030 100644 --- a/Runtime/API/C_ImageProcessing.lua +++ b/Runtime/API/C_ImageProcessing.lua @@ -121,4 +121,32 @@ function C_ImageProcessing.EncodeJPG(rgbaPixelArray, imageWidthInPixels, imageHe return tostring(outputBuffer) end +function C_ImageProcessing.EncodeTGA(rgbaPixelArray, imageWidthInPixels, imageHeightInPixels) + if type(rgbaPixelArray) == "userdata" then + rgbaPixelArray = tostring(rgbaPixelArray) + end + + validateString(rgbaPixelArray, "rgbaPixelArray") + validateNumber(imageWidthInPixels, "imageWidthInPixels") + validateNumber(imageHeightInPixels, "imageHeightInPixels") + + local rgbaPixelBuffer = buffer.new(#rgbaPixelArray):put(rgbaPixelArray) + + local image = ffi_new("stbi_image_t") + image.width = imageWidthInPixels + image.height = imageHeightInPixels + image.data = rgbaPixelBuffer + image.channels = 4 + + local requiredBufferSize = stbi.bindings.stbi_get_required_tga_size(image) + local outputBuffer = buffer.new() + local startPointer, reservedBufferSize = outputBuffer:reserve(requiredBufferSize) + + local numBytesWritten = stbi.bindings.stbi_encode_tga(image, startPointer, reservedBufferSize) + assert(numBytesWritten > 0, "Failed to encode TGA image (preallocated buffer too small?)") + outputBuffer:commit(numBytesWritten) + + return tostring(outputBuffer) +end + return C_ImageProcessing diff --git a/Tests/BDD/imageprocessing-namespace.spec.lua b/Tests/BDD/imageprocessing-namespace.spec.lua index 5c0597f97..cbe9d1026 100644 --- a/Tests/BDD/imageprocessing-namespace.spec.lua +++ b/Tests/BDD/imageprocessing-namespace.spec.lua @@ -3,9 +3,11 @@ local EXAMPLE_IMAGE_BUFFER = buffer.new():put(EXAMPLE_IMAGE_DATA) local EXAMPLE_BMP_BYTES = C_FileSystem.ReadFile(path.join("Tests", "Fixtures", "rgba-pixels.bmp")) local EXAMPLE_PNG_BYTES = C_FileSystem.ReadFile(path.join("Tests", "Fixtures", "rgba-pixels.png")) local EXAMPLE_JPG_BYTES = C_FileSystem.ReadFile(path.join("Tests", "Fixtures", "rgba-pixels.jpg")) +local EXAMPLE_TGA_BYTES = C_FileSystem.ReadFile(path.join("Tests", "Fixtures", "rgba-pixels.tga")) local EXAMPLE_BMP_BUFFER = buffer.new():put(EXAMPLE_BMP_BYTES) local EXAMPLE_PNG_BUFFER = buffer.new():put(EXAMPLE_PNG_BYTES) local EXAMPLE_JPG_BUFFER = buffer.new():put(EXAMPLE_JPG_BYTES) +local EXAMPLE_TGA_BUFFER = buffer.new():put(EXAMPLE_TGA_BYTES) describe("C_ImageProcessing", function() describe("DecodeFileContents", function() @@ -93,6 +95,22 @@ describe("C_ImageProcessing", function() assertEquals(imageHeightInPixels, 2) end) + it("should be able to decode TGA file contents from a string", function() + local rgbaPixelArray, imageWidthInPixels, imageHeightInPixels = + C_ImageProcessing.DecodeFileContents(EXAMPLE_TGA_BYTES) + assertEquals(rgbaPixelArray, EXAMPLE_IMAGE_DATA) + assertEquals(imageWidthInPixels, 2) + assertEquals(imageHeightInPixels, 2) + end) + + it("should be able to decode TGA file contents from a string buffer", function() + local rgbaPixelArray, imageWidthInPixels, imageHeightInPixels = + C_ImageProcessing.DecodeFileContents(EXAMPLE_TGA_BUFFER) + assertEquals(rgbaPixelArray, EXAMPLE_IMAGE_DATA) + assertEquals(imageWidthInPixels, 2) + assertEquals(imageHeightInPixels, 2) + end) + it("should throw if garbage bytes are passed as the file contents", function() local function attemptToDecodeInvalidFile() C_ImageProcessing.DecodeFileContents("Not a valid image file") @@ -227,4 +245,43 @@ describe("C_ImageProcessing", function() assertThrows(attemptToEncodeInvalidFile, expectedErrorMessage) end) end) + + describe("EncodeTGA", function() + it("should be able to encode pixel data given as a string", function() + local bmpFileContents = C_ImageProcessing.EncodeTGA(EXAMPLE_IMAGE_DATA, 2, 2) + assertEquals(bmpFileContents, EXAMPLE_TGA_BYTES) + end) + + it("should be able to encode pixel data given as a string buffer", function() + local bmpFileContents = C_ImageProcessing.EncodeTGA(EXAMPLE_IMAGE_BUFFER, 2, 2) + assertEquals(bmpFileContents, EXAMPLE_TGA_BYTES) + end) + + it("should throw if a non-string type was passed as the pixel buffer", function() + local function attemptToEncodeInvalidFile() + C_ImageProcessing.EncodeTGA(123, 2, 2) + end + local expectedErrorMessage = + "Expected argument rgbaPixelArray to be a string value, but received a number value instead" + assertThrows(attemptToEncodeInvalidFile, expectedErrorMessage) + end) + + it("should throw if a non-number type was passed as the image width", function() + local function attemptToEncodeInvalidFile() + C_ImageProcessing.EncodeTGA(EXAMPLE_IMAGE_DATA, "2", 2) + end + local expectedErrorMessage = + "Expected argument imageWidthInPixels to be a number value, but received a string value instead" + assertThrows(attemptToEncodeInvalidFile, expectedErrorMessage) + end) + + it("should throw if a non-number type was passed as the image height", function() + local function attemptToEncodeInvalidFile() + C_ImageProcessing.EncodeTGA(EXAMPLE_IMAGE_DATA, 2, "2") + end + local expectedErrorMessage = + "Expected argument imageHeightInPixels to be a number value, but received a string value instead" + assertThrows(attemptToEncodeInvalidFile, expectedErrorMessage) + end) + end) end) diff --git a/Tests/Fixtures/rgba-pixels.tga b/Tests/Fixtures/rgba-pixels.tga new file mode 100644 index 000000000..50d802648 Binary files /dev/null and b/Tests/Fixtures/rgba-pixels.tga differ