Files
filament/third_party/astcenc/Source/astc_stb_tga.cpp
Philip Rideout ec31a55094 Add astcenc from ARM to third_party.
This was designed as a standalone tool rather than a library, so we are
renaming their "main" function to "standalone_main", which is described
in the tnt README.

We'll also be adding a wrapper to our imageio library.
2018-09-28 09:42:09 -07:00

596 lines
17 KiB
C++

/*----------------------------------------------------------------------------*/
/**
* This confidential and proprietary software may be used only as
* authorised by a licensing agreement from ARM Limited
* (C) COPYRIGHT 2011-2012 ARM Limited
* ALL RIGHTS RESERVED
*
* The entire notice above must be reproduced on all authorised
* copies and copies may only be made to the extent permitted
* by a licensing agreement from ARM Limited.
*
* @brief Functions for loading/storing TGA files and the file types
* accessible through STB.
*/
/*----------------------------------------------------------------------------*/
#include "astc_codec_internals.h"
#include "softfloat.h"
#include <stdint.h>
#include <stdio.h>
#define STBI_HEADER_FILE_ONLY
#include <stb_image.h>
astc_codec_image * load_image_with_stb(const char *filename, int padding, int *result)
{
int xsize, ysize;
int components;
int y_flip = 1;
int x, y;
astc_codec_image *astc_img = NULL;
if (stbi_is_hdr(filename))
{
float *image = stbi_loadf(filename, &xsize, &ysize, &components, STBI_rgb_alpha);
if (image != NULL)
{
astc_img = allocate_image(16, xsize, ysize, 1, padding);
for (y = 0; y < ysize; y++)
{
int y_dst = y + padding;
int y_src = y_flip ? (ysize - y - 1) : y;
float *src = image + 4 * xsize * y_src;
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata16[0][y_dst][4 * x_dst] = float_to_sf16(src[4 * x], SF_NEARESTEVEN);
astc_img->imagedata16[0][y_dst][4 * x_dst + 1] = float_to_sf16(src[4 * x + 1], SF_NEARESTEVEN);
astc_img->imagedata16[0][y_dst][4 * x_dst + 2] = float_to_sf16(src[4 * x + 2], SF_NEARESTEVEN);
astc_img->imagedata16[0][y_dst][4 * x_dst + 3] = float_to_sf16(src[4 * x + 3], SF_NEARESTEVEN);
}
}
stbi_image_free(image);
fill_image_padding_area(astc_img);
*result = components + 0x80;
return astc_img;
}
}
else
{
stbi_uc *image = stbi_load(filename, &xsize, &ysize, &components, STBI_rgb_alpha);
uint8_t *imageptr = (uint8_t *) image;
if (image != NULL)
{
astc_img = allocate_image(8, xsize, ysize, 1, padding);
for (y = 0; y < ysize; y++)
{
int y_dst = y + padding;
int y_src = y_flip ? (ysize - y - 1) : y;
uint8_t *src = imageptr + 4 * xsize * y_src;
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata8[0][y_dst][4 * x_dst] = src[4 * x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 1] = src[4 * x + 1];
astc_img->imagedata8[0][y_dst][4 * x_dst + 2] = src[4 * x + 2];
astc_img->imagedata8[0][y_dst][4 * x_dst + 3] = src[4 * x + 3];
}
}
stbi_image_free(image);
fill_image_padding_area(astc_img);
*result = components;
return astc_img;
}
}
// if we haven't returned, it's because we failed to load the file.
printf("Failed to load image %s\nReason: %s\n", filename, stbi_failure_reason());
*result = -1;
return NULL;
}
/*
given a TGA filename, read in a TGA file and create test-vectors from it.
*/
struct tga_header
{
uint8_t identsize;
uint8_t colormaptype;
uint8_t imagetype;
uint8_t dummied[5];
uint16_t xstart;
uint16_t ystart;
uint16_t xsize;
uint16_t ysize;
uint8_t bitsperpixel;
uint8_t descriptor;
};
enum tga_descriptor
{
TGA_DESCRIPTOR_XFLIP = 0x10,
TGA_DESCRIPTOR_YFLIP = 0x20,
};
enum tga_type
{
TGA_COLORMAP_NONE = 0x00,
/* other color map type codes are reserved */
TGA_IMAGETYPE_NONE = 0, /* no image data */
TGA_IMAGETYPE_PSEUDOCOLOR = 1, /* color-mapped image */
TGA_IMAGETYPE_TRUECOLOR = 2, /* true-color image */
TGA_IMAGETYPE_GREYSCALE = 3, /* true-color single channel */
TGA_IMAGETYPE_RLE_PSEUDOCOLOR = 9, /* RLE color-mapped image */
TGA_IMAGETYPE_RLE_TRUECOLOR = 10, /* RLE true color */
TGA_IMAGETYPE_RLE_GREYSCALE = 11, /* RLE true grey */
HTGA_IMAGETYPE_TRUECOLOR = 0x82,
HTGA_IMAGETYPE_GREYSCALE = 0x83,
};
enum tga_errors
{
TGA_ERROR_OPEN = -1, /* error opening file */
TGA_ERROR_READ = -2, /* error reading file */
TGA_ERROR_COLORMAP = -3, /* file has a colormap (not supported) */
TGA_ERROR_RLE = -4, /* file is run-length encoded (not supported) */
TGA_ERROR_FORMAT = -5, /* file has an unsupported pixel format */
TGA_ERROR_LAYOUT = -6 /* file layout unsupported (right-to-left flipped) */
};
/*
return: if positive number, then the number is #components in the image
1=Grayscale 2=Grayscale+Alpha 3=RGB 4=RGB+Alpha
add 0x80 if the file was in fact a HTGA file with HDR content.
if negative number, then what went wrong
-1=failed to open file
-2=failed to read data
-3=failed to load image because it has a colormap
-4=failed to load image because it is RLE-encoded
-5=failed to load image because it has an unsupported pixel type
-6=failed to load image because it is flipped in the x dimension
*/
astc_codec_image *load_tga_image(const char *tga_filename, int padding, int *result)
{
int x, y;
int i;
int y_flip;
FILE *f = fopen(tga_filename, "rb");
if (!f)
{
*result = TGA_ERROR_OPEN;
return NULL;
}
tga_header hdr;
size_t bytes_read = fread(&hdr, 1, 18, f);
if (bytes_read != 18)
{
fclose(f);
*result = TGA_ERROR_READ;
return NULL;
}
if (hdr.colormaptype != 0)
{
fclose(f);
*result = TGA_ERROR_COLORMAP;
return NULL;
}
// do a quick test for RLE-pictures so that we reject them
if (hdr.imagetype == TGA_IMAGETYPE_RLE_TRUECOLOR || hdr.imagetype == TGA_IMAGETYPE_RLE_PSEUDOCOLOR || hdr.imagetype == TGA_IMAGETYPE_RLE_GREYSCALE)
{
fclose(f);
printf("TGA image %s is RLE-encoded; only uncompressed TGAs are supported.\n", tga_filename);
*result = TGA_ERROR_RLE;
return NULL;
}
// Check for x flip (rare, unsupported) and y flip (supported)
if (hdr.descriptor & TGA_DESCRIPTOR_XFLIP)
{
fclose(f);
*result = TGA_ERROR_LAYOUT;
return NULL;
}
if (hdr.descriptor & TGA_DESCRIPTOR_YFLIP)
y_flip = 1;
else
y_flip = 0;
// support 4 formats (non-RLE only):
// 8-bit grayscale
// 8-bit grayscale + 8-bit alpha
// RGB 8:8:8
// RGBA 8:8:8:8
if (!(hdr.imagetype == TGA_IMAGETYPE_TRUECOLOR && hdr.bitsperpixel == 32)
&& !(hdr.imagetype == TGA_IMAGETYPE_TRUECOLOR && hdr.bitsperpixel == 24)
&& !(hdr.imagetype == TGA_IMAGETYPE_GREYSCALE && hdr.bitsperpixel == 16)
&& !(hdr.imagetype == TGA_IMAGETYPE_GREYSCALE && hdr.bitsperpixel == 8)
&& !(hdr.imagetype == HTGA_IMAGETYPE_TRUECOLOR && hdr.bitsperpixel == 64)
&& !(hdr.imagetype == HTGA_IMAGETYPE_TRUECOLOR && hdr.bitsperpixel == 48)
&& !(hdr.imagetype == HTGA_IMAGETYPE_GREYSCALE && hdr.bitsperpixel == 32) && !(hdr.imagetype == HTGA_IMAGETYPE_GREYSCALE && hdr.bitsperpixel == 16))
{
fclose(f);
*result = TGA_ERROR_FORMAT;
return NULL;
}
if (hdr.identsize != 0) // skip ID field if it present.
fseek(f, hdr.identsize, SEEK_CUR);
int bytesperpixel = hdr.bitsperpixel / 8;
int bitness = (hdr.imagetype >= 0x80) ? 16 : 8;
// OK, it seems we have a legit TGA or HTGA file of a format we understand.
// Now, let's read it.
size_t bytestoread = 0;
uint8_t **row_pointers8 = NULL;
uint16_t **row_pointers16 = NULL;
if (bitness == 8)
{
row_pointers8 = new uint8_t *[hdr.ysize];
row_pointers8[0] = new uint8_t[hdr.xsize * hdr.ysize * bytesperpixel];
for (i = 1; i < hdr.ysize; i++)
row_pointers8[i] = row_pointers8[0] + hdr.xsize * bytesperpixel * i;
bytestoread = hdr.xsize * hdr.ysize * bytesperpixel;
bytes_read = fread(row_pointers8[0], 1, bytestoread, f);
}
else if (bitness == 16)
{
row_pointers16 = new uint16_t *[hdr.ysize];
row_pointers16[0] = new uint16_t[hdr.xsize * hdr.ysize * (bytesperpixel / 2)];
for (i = 1; i < hdr.ysize; i++)
row_pointers16[i] = row_pointers16[0] + hdr.xsize * (bytesperpixel / 2) * i;
bytestoread = hdr.xsize * hdr.ysize * bytesperpixel;
bytes_read = fread(row_pointers16[0], 1, bytestoread, f);
}
fclose(f);
if (bytes_read != bytestoread)
{
if (row_pointers8)
{
delete[]row_pointers8[0];
delete[]row_pointers8;
}
if (row_pointers16)
{
delete[]row_pointers16[0];
delete[]row_pointers16;
}
*result = -2;
return NULL;
}
// OK, at this point, we can expand the image data to RGBA.
int ysize = hdr.ysize;
int xsize = hdr.xsize;
astc_codec_image *astc_img = allocate_image(bitness, xsize, ysize, 1, padding);
int retval;
if (bitness == 8)
{
for (y = 0; y < ysize; y++)
{
int y_dst = y + padding;
int y_src = y_flip ? (ysize - y - 1) : y;
switch (bytesperpixel)
{
case 1: // single-component, treated as Luminance
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata8[0][y_dst][4 * x_dst] = row_pointers8[y_src][x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 1] = row_pointers8[y_src][x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 2] = row_pointers8[y_src][x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 3] = 0xFF;
}
break;
case 2: // two-component, treated as Luminance-Alpha
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata8[0][y_dst][4 * x_dst] = row_pointers8[y_src][2 * x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 1] = row_pointers8[y_src][2 * x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 2] = row_pointers8[y_src][2 * x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 3] = row_pointers8[y_src][2 * x + 1];
}
break;
case 3: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata8[0][y_dst][4 * x_dst] = row_pointers8[y_src][3 * x + 2]; // TGA uses BGR, we use RGB
astc_img->imagedata8[0][y_dst][4 * x_dst + 1] = row_pointers8[y_src][3 * x + 1];
astc_img->imagedata8[0][y_dst][4 * x_dst + 2] = row_pointers8[y_src][3 * x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 3] = 0xFF;
}
break;
case 4: // four-component, treated as RGBA
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata8[0][y_dst][4 * x_dst] = row_pointers8[y_src][4 * x + 2]; // TGA uses BGR, we use RGB
astc_img->imagedata8[0][y_dst][4 * x_dst + 1] = row_pointers8[y_src][4 * x + 1];
astc_img->imagedata8[0][y_dst][4 * x_dst + 2] = row_pointers8[y_src][4 * x];
astc_img->imagedata8[0][y_dst][4 * x_dst + 3] = row_pointers8[y_src][4 * x + 3];
}
break;
}
}
delete[]row_pointers8[0];
delete[]row_pointers8;
retval = bytesperpixel;
}
else // if( bitness == 16 )
{
for (y = 0; y < ysize; y++)
{
int y_dst = y + padding;
int y_src = y_flip ? (ysize - y - 1) : y;
switch (bytesperpixel)
{
case 2: // single-component, treated as Luminance
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata16[0][y_dst][4 * x_dst] = row_pointers16[y_src][x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 1] = row_pointers16[y_src][x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 2] = row_pointers16[y_src][x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 3] = 0x3C00;
}
break;
case 4: // two-component, treated as Luminance-Alpha
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata16[0][y_dst][4 * x_dst] = row_pointers16[y_src][2 * x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 1] = row_pointers16[y_src][2 * x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 2] = row_pointers16[y_src][2 * x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 3] = row_pointers16[y_src][2 * x + 1];
}
break;
case 6: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata16[0][y_dst][4 * x_dst] = row_pointers16[y_src][3 * x + 2]; // TGA uses BGR, we use RGB
astc_img->imagedata16[0][y_dst][4 * x_dst + 1] = row_pointers16[y_src][3 * x + 1];
astc_img->imagedata16[0][y_dst][4 * x_dst + 2] = row_pointers16[y_src][3 * x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 3] = 0x3C00;
}
break;
case 8: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
int x_dst = x + padding;
astc_img->imagedata16[0][y_dst][4 * x_dst] = row_pointers16[y_src][4 * x + 2]; // TGA uses BGR, we use RGB
astc_img->imagedata16[0][y_dst][4 * x_dst + 1] = row_pointers16[y_src][4 * x + 1];
astc_img->imagedata16[0][y_dst][4 * x_dst + 2] = row_pointers16[y_src][4 * x];
astc_img->imagedata16[0][y_dst][4 * x_dst + 3] = row_pointers16[y_src][4 * x + 3];
}
break;
}
}
delete[]row_pointers16[0];
delete[]row_pointers16;
retval = (bytesperpixel / 2) + 0x80;
}
fill_image_padding_area(astc_img);
*result = retval;
return astc_img;
}
/*
returns -1 if any problems arose when writing the file, else the number of color channels it chose to write.
*/
int store_tga_image(const astc_codec_image * img, const char *tga_filename, int bitness)
{
int x, y;
int i;
int xsize = img->xsize;
int ysize = img->ysize;
// first scan through the image data
// to determine how many color channels the image has.
int image_channels = determine_image_channels(img);
// construct a header
tga_header hdr;
hdr.identsize = 0;
hdr.colormaptype = 0;
hdr.imagetype = image_channels >= 3 ? 2 : 3;
if (bitness == 16)
hdr.imagetype |= 0x80;
for (i = 0; i < 5; i++)
hdr.dummied[i] = 0;
hdr.xstart = 0;
hdr.ystart = 0;
hdr.xsize = xsize;
hdr.ysize = ysize;
hdr.bitsperpixel = image_channels * bitness;
hdr.descriptor = 0;
int bytesperpixel = image_channels;
// construct image data to write
uint8_t **row_pointers8 = NULL;
uint16_t **row_pointers16 = NULL;
if (bitness == 8)
{
row_pointers8 = new uint8_t *[hdr.ysize];
row_pointers8[0] = new uint8_t[hdr.xsize * hdr.ysize * bytesperpixel];
for (i = 1; i < hdr.ysize; i++)
row_pointers8[i] = row_pointers8[0] + hdr.xsize * bytesperpixel * i;
for (y = 0; y < ysize; y++)
{
switch (bytesperpixel)
{
case 1: // single-component, treated as Luminance
for (x = 0; x < xsize; x++)
{
row_pointers8[y][x] = img->imagedata8[0][y][4 * x];
}
break;
case 2: // two-component, treated as Luminance-Alpha
for (x = 0; x < xsize; x++)
{
row_pointers8[y][2 * x] = img->imagedata8[0][y][4 * x];
row_pointers8[y][2 * x + 1] = img->imagedata8[0][y][4 * x + 3];
}
break;
case 3: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
row_pointers8[y][3 * x + 2] = img->imagedata8[0][y][4 * x];
row_pointers8[y][3 * x + 1] = img->imagedata8[0][y][4 * x + 1];
row_pointers8[y][3 * x] = img->imagedata8[0][y][4 * x + 2];
}
break;
case 4: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
row_pointers8[y][4 * x + 2] = img->imagedata8[0][y][4 * x];
row_pointers8[y][4 * x + 1] = img->imagedata8[0][y][4 * x + 1];
row_pointers8[y][4 * x] = img->imagedata8[0][y][4 * x + 2];
row_pointers8[y][4 * x + 3] = img->imagedata8[0][y][4 * x + 3];
}
break;
}
}
}
else // if bitness == 16
{
row_pointers16 = new uint16_t *[hdr.ysize];
row_pointers16[0] = new uint16_t[hdr.xsize * hdr.ysize * bytesperpixel];
for (i = 1; i < hdr.ysize; i++)
row_pointers16[i] = row_pointers16[0] + hdr.xsize * bytesperpixel * i;
for (y = 0; y < ysize; y++)
{
switch (bytesperpixel)
{
case 1: // single-component, treated as Luminance
for (x = 0; x < xsize; x++)
{
row_pointers16[y][x] = img->imagedata16[0][y][4 * x];
}
break;
case 2: // two-component, treated as Luminance-Alpha
for (x = 0; x < xsize; x++)
{
row_pointers16[y][2 * x] = img->imagedata16[0][y][4 * x];
row_pointers16[y][2 * x + 1] = img->imagedata16[0][y][4 * x + 3];
}
break;
case 3: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
row_pointers16[y][3 * x + 2] = img->imagedata16[0][y][4 * x];
row_pointers16[y][3 * x + 1] = img->imagedata16[0][y][4 * x + 1];
row_pointers16[y][3 * x] = img->imagedata16[0][y][4 * x + 2];
}
break;
case 4: // three-component, treated as RGB
for (x = 0; x < xsize; x++)
{
row_pointers16[y][4 * x + 2] = img->imagedata16[0][y][4 * x];
row_pointers16[y][4 * x + 1] = img->imagedata16[0][y][4 * x + 1];
row_pointers16[y][4 * x] = img->imagedata16[0][y][4 * x + 2];
row_pointers16[y][4 * x + 3] = img->imagedata16[0][y][4 * x + 3];
}
break;
}
}
}
int retval = image_channels;
// then try writing it all to file.
FILE *wf = fopen(tga_filename, "wb");
if (wf)
{
if (bitness == 8)
{
size_t expected_bytes_written = 18 + bytesperpixel * xsize * ysize;
size_t hdr_bytes_written = fwrite(&hdr, 1, 18, wf);
size_t data_bytes_written = fwrite(row_pointers8[0], 1, bytesperpixel * xsize * ysize, wf);
fclose(wf);
if (hdr_bytes_written + data_bytes_written != expected_bytes_written)
retval = -1;
}
else
{
size_t expected_bytes_written = 18 + bytesperpixel * xsize * ysize * sizeof(uint16_t);
size_t hdr_bytes_written = fwrite(&hdr, 1, 18, wf);
size_t data_bytes_written = fwrite(row_pointers16[0], 1, bytesperpixel * xsize * ysize * sizeof(uint16_t), wf);
fclose(wf);
if (hdr_bytes_written + data_bytes_written != expected_bytes_written)
retval = -1;
}
}
else
{
retval = -1;
}
if (row_pointers8)
{
delete[]row_pointers8[0];
delete[]row_pointers8;
}
if (row_pointers16)
{
delete[]row_pointers16[0];
delete[]row_pointers16;
}
return retval;
}