first commit

This commit is contained in:
Nick Fisher
2021-09-15 20:07:11 +08:00
commit a0f877be48
292 changed files with 100157 additions and 0 deletions

View File

@@ -0,0 +1,381 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_COLORTRANSFORM_H_
#define IMAGE_COLORTRANSFORM_H_
#include <image/LinearImage.h>
#include <utils/compiler.h>
#include <math/scalar.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/half.h>
#include <algorithm>
#include <memory>
namespace image {
template <typename T>
uint32_t linearToRGB_10_11_11_REV(const T& linear) {
using fp11 = filament::math::fp<0, 5, 6>;
using fp10 = filament::math::fp<0, 5, 5>;
// the max value for a RGB_11_11_10 is {65024, 65024, 64512} : (2 - 2^-M) * 2^(E-1)
// we clamp to the min of that
fp11 r = fp11::fromf(std::min(64512.0f, linear[0]));
fp11 g = fp11::fromf(std::min(64512.0f, linear[1]));
fp10 b = fp10::fromf(std::min(64512.0f, linear[2]));
uint32_t ir = r.bits & 0x7FF;
uint32_t ig = g.bits & 0x7FF;
uint32_t ib = b.bits & 0x3FF;
return (ib << 22) | (ig << 11) | ir;
}
template <typename T>
inline filament::math::float4 linearToRGBM(const T& linear) {
using filament::math::float4;
float4 RGBM(linear[0], linear[1], linear[2], 1.0f);
// Linear to gamma space
RGBM.rgb = sqrt(RGBM.rgb);
// Set the range
RGBM.rgb /= 16.0f;
float maxComponent = std::max(std::max(RGBM.r, RGBM.g), std::max(RGBM.b, 1e-6f));
// Don't let M go below 1 in the [0..16] range
RGBM.a = filament::math::clamp(maxComponent, 1.0f / 16.0f, 1.0f);
RGBM.a = std::ceil(RGBM.a * 255.0f) / 255.0f;
RGBM.rgb = saturate(RGBM.rgb / RGBM.a);
return RGBM;
}
template <typename T>
inline filament::math::float3 RGBMtoLinear(const T& rgbm) {
using filament::math::float3;
float3 linear(rgbm[0], rgbm[1], rgbm[2]);
linear *= rgbm.a * 16.0f;
// Gamma to linear space
return linear * linear;
}
template <typename T>
inline filament::math::float3 linearTosRGB(const T& linear) {
using filament::math::float3;
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 1 / 2.4f;
float3 sRGB;
for (size_t i=0 ; i<3 ; i++) {
if (linear[i] <= 0.0031308f) {
sRGB[i] = linear[i] * 12.92f;
} else {
sRGB[i] = a1 * std::pow(linear[i], p) - a;
}
}
return sRGB;
}
inline float linearTosRGB(float linear) {
if (linear <= 0.0031308f) {
return linear * 12.92f;
} else {
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 1 / 2.4f;
return a1 * std::pow(linear, p) - a;
}
}
template<typename T>
T sRGBToLinear(const T& sRGB);
template<>
inline filament::math::float3 sRGBToLinear(const filament::math::float3& sRGB) {
using filament::math::float3;
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 2.4f;
float3 linear;
for (size_t i=0 ; i<3 ; i++) {
if (sRGB[i] <= 0.04045f) {
linear[i] = sRGB[i] * (1.0f / 12.92f);
} else {
linear[i] = std::pow((sRGB[i] + a) / a1, p);
}
}
return linear;
}
template<>
inline filament::math::float4 sRGBToLinear(const filament::math::float4& sRGB) {
using filament::math::float4;
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
constexpr float p = 2.4f;
float4 linear;
for (size_t i=0 ; i<3 ; i++) {
if (sRGB[i] <= 0.04045f) {
linear[i] = sRGB[i] * (1.0f / 12.92f);
} else {
linear[i] = std::pow((sRGB[i] + a) / a1, p);
}
}
linear[3] = sRGB[3];
return linear;
}
template<typename T>
T linearToSRGB(const T& color);
template<>
inline filament::math::float3 linearToSRGB(const filament::math::float3& color) {
using filament::math::float3;
float3 sRGBColor{color};
#pragma nounroll
for (size_t i = 0; i < sRGBColor.size(); i++) {
sRGBColor[i] = (sRGBColor[i] <= 0.0031308f) ?
sRGBColor[i] * 12.92f : (powf(sRGBColor[i], 1.0f / 2.4f) * 1.055f) - 0.055f;
}
return sRGBColor;
}
// Creates a n-channel sRGB image from a linear floating-point image.
// The source image can have more than N channels, but only the first N are converted to sRGB.
template<typename T, int N = 3>
std::unique_ptr<uint8_t[]> fromLinearTosRGB(const LinearImage& image) {
const size_t w = image.getWidth();
const size_t h = image.getHeight();
const size_t nchan = image.getChannels();
assert(nchan >= N);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * N * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
float const* p = image.getPixelRef(0, y);
for (size_t x = 0; x < w; ++x, p += nchan, d += N) {
for (int n = 0; n < N; n++) {
float source = n < 3 ? linearTosRGB(p[n]) : p[n];
float target = filament::math::saturate(source) * std::numeric_limits<T>::max() + 0.5f;
d[n] = T(target);
}
}
}
return dst;
}
// Creates a N-channel RGB u8 image from a f32 image.
template<typename T, int N = 3>
std::unique_ptr<uint8_t[]> fromLinearToRGB(const LinearImage& image) {
size_t w = image.getWidth();
size_t h = image.getHeight();
size_t channels = image.getChannels();
assert(channels >= N);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * N * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
float const* p = image.getPixelRef(0, y);
for (size_t x = 0; x < w; ++x, p += channels, d += N) {
for (int n = 0; n < N; n++) {
float target = filament::math::saturate(p[n]) * std::numeric_limits<T>::max() + 0.5f;
d[n] = T(target);
}
}
}
return dst;
}
// Creates a 4-channel RGBM u8 image from a f32 image.
// The source image can have three or more channels, but only the first three are honored.
template <typename T>
std::unique_ptr<uint8_t[]> fromLinearToRGBM(const LinearImage& image) {
using namespace filament::math;
size_t w = image.getWidth();
size_t h = image.getHeight();
UTILS_UNUSED_IN_RELEASE size_t channels = image.getChannels();
assert(channels >= 3);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * 4 * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x, d += 4) {
auto src = image.get<float3>((uint32_t) x, (uint32_t) y);
float4 l(linearToRGBM(*src) * std::numeric_limits<T>::max() + 0.5f);
for (size_t i = 0; i < 4; i++) {
d[i] = T(l[i]);
}
}
}
return dst;
}
// Creates a 3-channel RGB_10_11_11_REV image from a f32 image.
// The source image can have three or more channels, but only the first three are honored.
inline std::unique_ptr<uint8_t[]> fromLinearToRGB_10_11_11_REV(const LinearImage& image) {
using namespace filament::math;
size_t w = image.getWidth();
size_t h = image.getHeight();
UTILS_UNUSED_IN_RELEASE size_t channels = image.getChannels();
assert(channels >= 3);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * sizeof(uint32_t)]);
uint8_t* d = dst.get();
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x, d += sizeof(uint32_t)) {
auto src = image.get<float3>((uint32_t)x, (uint32_t)y);
uint32_t v = linearToRGB_10_11_11_REV(*src);
*reinterpret_cast<uint32_t*>(d) = v;
}
}
return dst;
}
// Creates a packed single-channel integer-based image from a floating-point image.
// For example if T is uint8_t, then this performs a transformation from [0,1] to [0,255].
template <typename T>
std::unique_ptr<uint8_t[]> fromLinearToGrayscale(const LinearImage& image) {
const size_t w = image.getWidth();
const size_t h = image.getHeight();
assert(image.getChannels() == 1);
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * sizeof(T)]);
T* d = reinterpret_cast<T*>(dst.get());
for (size_t y = 0; y < h; ++y) {
float const* p = image.getPixelRef(0, y);
for (size_t x = 0; x < w; ++x, ++p, ++d) {
const float gray = filament::math::saturate(*p) * std::numeric_limits<T>::max() + 0.5f;
d[0] = T(gray);
}
}
return dst;
}
// Constructs a 3-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// The "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinear(size_t w, size_t h, size_t bpr,
const uint8_t* src, PROCESS proc, TRANSFORM transform) {
LinearImage result((uint32_t) w, (uint32_t) h, 3);
auto d = result.get< filament::math::float3>();
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 3) {
filament::math::float3 sRGB(proc(p[0]), proc(p[1]), proc(p[2]));
sRGB /= std::numeric_limits<T>::max();
*d++ = transform(sRGB);
}
}
return result;
}
// Constructs a 3-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// The "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinear(size_t w, size_t h, size_t bpr,
const std::unique_ptr<uint8_t[]>& src, PROCESS proc, TRANSFORM transform) {
return toLinear<T>(w, h, bpr, src.get(), proc, transform);
}
// Constructs a 4-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// the "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr,
const uint8_t* src, PROCESS proc, TRANSFORM transform) {
LinearImage result((uint32_t) w, (uint32_t) h, 4);
auto d = result.get< filament::math::float4>();
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 4) {
filament::math::float4 sRGB(proc(p[0]), proc(p[1]), proc(p[2]), proc(p[3]));
sRGB /= std::numeric_limits<T>::max();
*d++ = transform(sRGB);
}
}
return result;
}
// Constructs a 4-channel LinearImage from an untyped data blob.
// The "proc" lambda converts a single color component into a float.
// the "transform" lambda performs an arbitrary float-to-float transformation.
template<typename T, typename PROCESS, typename TRANSFORM>
static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr,
const std::unique_ptr<uint8_t[]>& src, PROCESS proc, TRANSFORM transform) {
return toLinearWithAlpha<T>(w, h, bpr, src.get(), proc, transform);
}
// Constructs a 3-channel LinearImage from RGBM data.
inline LinearImage toLinearFromRGBM( filament::math::float4 const* src, uint32_t w, uint32_t h) {
LinearImage result(w, h, 3);
auto dst = result.get< filament::math::float3>();
for (uint32_t row = 0; row < h; ++row) {
for (uint32_t col = 0; col < w; ++col, ++src, ++dst) {
*dst = RGBMtoLinear(*src);
}
}
return result;
}
inline LinearImage fromLinearToRGBM(const LinearImage& image) {
assert(image.getChannels() == 3);
const uint32_t w = image.getWidth(), h = image.getHeight();
LinearImage result(w, h, 4);
auto src = image.get< filament::math::float3>();
auto dst = result.get< filament::math::float4>();
for (uint32_t row = 0; row < h; ++row) {
for (uint32_t col = 0; col < w; ++col, ++src, ++dst) {
*dst = linearToRGBM(*src);
}
}
return result;
}
template<typename T>
static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, const uint8_t* src) {
LinearImage result(w, h, 4);
filament::math::float4* d = reinterpret_cast<filament::math::float4*>(result.getPixelRef(0, 0));
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 4) {
filament::math::float3 sRGB(p[0], p[1], p[2]);
sRGB /= std::numeric_limits<T>::max();
*d++ = filament::math::float4(sRGBToLinear(sRGB), 1.0f);
}
}
return result;
}
template<typename T>
static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) {
LinearImage result(w, h, 3);
filament::math::float3* d = reinterpret_cast<filament::math::float3*>(result.getPixelRef(0, 0));
for (size_t y = 0; y < h; ++y) {
T const* p = reinterpret_cast<T const*>(src + y * bpr);
for (size_t x = 0; x < w; ++x, p += 3) {
filament::math::float3 sRGB(p[0], p[1], p[2]);
sRGB /= std::numeric_limits<T>::max();
*d++ = sRGBToLinear(sRGB);
}
}
return result;
}
} // namespace Image
#endif // IMAGE_COLORTRANSFORM_H_

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_IMAGEOPS_H
#define IMAGE_IMAGEOPS_H
#include <image/LinearImage.h>
#include <utils/compiler.h>
#include <cstddef>
#include <initializer_list>
namespace image {
// Concatenates images horizontally to create a filmstrip atlas, similar to numpy's hstack.
UTILS_PUBLIC LinearImage horizontalStack(std::initializer_list<LinearImage> images);
UTILS_PUBLIC LinearImage horizontalStack(LinearImage const* img, size_t count);
// Concatenates images vertically to create a filmstrip atlas, similar to numpy's vstack.
UTILS_PUBLIC LinearImage verticalStack(std::initializer_list<LinearImage> images);
UTILS_PUBLIC LinearImage verticalStack(LinearImage const* img, size_t count);
// Horizontally or vertically mirror the given image.
UTILS_PUBLIC LinearImage horizontalFlip(const LinearImage& image);
UTILS_PUBLIC LinearImage verticalFlip(const LinearImage& image);
// Transforms normals (components live in [-1,+1]) into colors (components live in [0,+1]).
UTILS_PUBLIC LinearImage vectorsToColors(const LinearImage& image);
UTILS_PUBLIC LinearImage colorsToVectors(const LinearImage& image);
// Creates a single-channel image by extracting the selected channel.
UTILS_PUBLIC LinearImage extractChannel(const LinearImage& image, uint32_t channel);
// Constructs a multi-channel image by copying data from a sequence of single-channel images.
UTILS_PUBLIC LinearImage combineChannels(std::initializer_list<LinearImage> images);
UTILS_PUBLIC LinearImage combineChannels(LinearImage const* img, size_t count);
// Generates a new image with rows & columns swapped.
UTILS_PUBLIC LinearImage transpose(const LinearImage& image);
// Extracts pixels by specifying a crop window where (0,0) is the top-left corner of the image.
// The boundary is specified as Left Top Right Bottom.
UTILS_PUBLIC
LinearImage cropRegion(const LinearImage& image, uint32_t l, uint32_t t, uint32_t r, uint32_t b);
// Lexicographically compares two images, similar to memcmp.
UTILS_PUBLIC int compare(const LinearImage& a, const LinearImage& b, float epsilon = 0.0f);
// Sets all pixels in all channels to the given value.
UTILS_PUBLIC void clearToValue(LinearImage& img, float value);
// Called by the coordinate field generator to query if a pixel is within the region of interest.
using PresenceCallback = bool(*)(const LinearImage& img, uint32_t col, uint32_t row, void* user);
// Generates a two-channel field of non-normalized coordinates that indicate the nearest pixel
// whose presence function returns true. This is the first step before generating a distance
// field or generalized Voronoi map.
UTILS_PUBLIC
LinearImage computeCoordField(const LinearImage& src, PresenceCallback presence, void* user);
// Generates a single-channel Euclidean distance field with positive values outside the region
// of interest in the source image, and zero values inside. If sqrt is false, the computed
// distances are squared. If signed distance (SDF) is desired, this function can be called a second
// time using an inverted source field.
UTILS_PUBLIC LinearImage edtFromCoordField(const LinearImage& coordField, bool sqrt);
// Dereferences the given coordinate field. Useful for creating Voronoi diagrams or dilated images.
UTILS_PUBLIC
LinearImage voronoiFromCoordField(const LinearImage& coordField, const LinearImage& src);
// Copies content of a source image into a target image. Requires width/height/channels to match.
UTILS_PUBLIC void blitImage(LinearImage& target, const LinearImage& source);
} // namespace image
#endif /* IMAGE_LINEARIMAGE_H */

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_IMAGESAMPLER_H
#define IMAGE_IMAGESAMPLER_H
#include <image/LinearImage.h>
#include <utils/compiler.h>
namespace image {
/**
* Value of a single point sample, allocated according to the number of image channels.
*/
struct UTILS_PUBLIC SingleSample {
float& operator[](int index) { return *(data + index); }
float* data = nullptr;
~SingleSample();
};
/**
* Controls the weighted average used across a window of source samples.
*/
enum class Filter {
DEFAULT, // Selects MITCHELL or LANCZOS dynamically.
BOX, // Computes the un-weighted average over the filter radius.
NEAREST, // Copies the source sample nearest to the center of the filter.
HERMITE, // Also known as "smoothstep", has some nice properties.
GAUSSIAN_SCALARS, // Standard Gaussian filter with sigma = 0.5
GAUSSIAN_NORMALS, // Same as GAUSSIAN_SCALARS, but interpolates unitized vectors.
MITCHELL, // Cubic resampling per Mitchell-Netravali, default for magnification.
LANCZOS, // Popular sinc-based filter, default for minification.
MINIMUM // Takes a min val rather than avg, perhaps useful for depth maps and SDF's.
};
/**
* Defines a viewport inside the texture such that (0,0) is at the top-left corner of the top-left
* pixel, and (1,1) is at the bottom-right corner of the bottom-corner pixel.
*/
struct Region {
float left;
float top;
float right;
float bottom;
};
/**
* Transforms the texel fetching operation when sampling from adjacent images.
*/
enum class Orientation {
STANDARD = 0,
FLIP_X = 1 << 0,
FLIP_Y = 1 << 1,
FLIP_XY = FLIP_X | FLIP_Y
};
/**
* Specifies how to generate samples that lie outside the boundaries of the source region.
*/
struct Boundary {
enum {
EXCLUDE, // Ignore the samples and renormalize the filter. This is probably what you want.
REGION, // Keep samples that are outside sourceRegion if they are still within the image.
CLAMP, // Pretend the edge pixel is repeated forever. Gives edge pixels more weight.
REPEAT, // Resample from the region, wrapping back to the front of the row or column.
MIRROR, // Resample from the region but assume that it has been flipped.
COLOR, // Use the specified constant color.
NEIGHBOR // Sample from an adjacent image.
} mode = EXCLUDE;
SingleSample color; // Used only if mode = COLOR
LinearImage* neighbor = nullptr; // Used only if mode = NEIGHBOR
Orientation orientation; // Used only if mode = NEIGHBOR
};
/**
* Configuration for the resampleImage function. Provides reasonable defaults.
*/
struct ImageSampler {
Filter horizontalFilter = Filter::DEFAULT;
Filter verticalFilter = Filter::DEFAULT;
Region sourceRegion = {0, 0, 1, 1};
float filterRadiusMultiplier = 1;
Boundary east;
Boundary north;
Boundary west;
Boundary south;
};
/**
* Resizes or blurs the given linear image, producing a new linear image with the given dimensions.
*/
UTILS_PUBLIC
LinearImage resampleImage(const LinearImage& source, uint32_t width, uint32_t height,
const ImageSampler& sampler);
/**
* Resizes the given linear image using a simplified API that takes target dimensions and filter.
*/
UTILS_PUBLIC
LinearImage resampleImage(const LinearImage& source, uint32_t width, uint32_t height,
Filter filter = Filter::DEFAULT);
/**
* Computes a single sample for the given texture coordinate and writes the resulting color
* components into the given output holder.
*
* For decent performance, do not call this across the entire image, instead call resampleImage.
* On the first call, pass in a default SingleSample to allocate the result holder. For example:
*
* SingleSample result;
* computeSingleSample(img, 0.5f, 0.5f, &result);
* printf("r g b = %f %f %f\n", result[0], result[1], result[2]);
* computeSingleSample(img, 0.9f, 0.1f, &result);
* printf("r g b = %f %f %f\n", result[0], result[1], result[2]);
*
* The x y coordinates live in "texture space" such that (0.0f, 0.0f) is the upper-left boundary of
* the top-left pixel and (+1.0f, +1.0f) is the lower-right boundary of the bottom-right pixel.
*/
UTILS_PUBLIC
void computeSingleSample(const LinearImage& source, float x, float y, SingleSample* result,
Filter filter = Filter::BOX);
/**
* Generates a sequence of miplevels using the requested filter. To determine the number of mips
* it would take to get down to 1x1, see getMipmapCount.
*
* Source image need not be power-of-two. In the result vector, the half-size image is returned at
* index 0, the quarter-size image is at index 1, etc. Please note that the original-sized image is
* not included.
*/
UTILS_PUBLIC
void generateMipmaps(const LinearImage& source, Filter, LinearImage* result, uint32_t mipCount);
/**
* Returns the number of miplevels it would take to downsample the given image down to 1x1. This
* number does not include the original image (i.e. mip 0).
*/
UTILS_PUBLIC
uint32_t getMipmapCount(const LinearImage& source);
/**
* Given the string name of a filter, converts it to uppercase and returns the corresponding
* enum value. If no corresponding enumerant exists, returns DEFAULT.
*/
UTILS_PUBLIC
Filter filterFromString(const char* name);
} // namespace image
#endif /* IMAGE_IMAGESAMPLER_H */

View File

@@ -0,0 +1,279 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_KTXBUNDLE_H
#define IMAGE_KTXBUNDLE_H
#include <math/vec3.h>
#include <utils/compiler.h>
#include <cstdint>
#include <memory>
namespace image {
struct KtxInfo {
uint32_t endianness;
uint32_t glType;
uint32_t glTypeSize;
uint32_t glFormat;
uint32_t glInternalFormat;
uint32_t glBaseInternalFormat;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
};
struct KtxBlobIndex {
uint32_t mipLevel;
uint32_t arrayIndex;
uint32_t cubeFace;
};
struct KtxBlobList;
struct KtxMetadata;
/**
* KtxBundle is a structured set of opaque data blobs that can be passed straight to the GPU, such
* that a single bundle corresponds to a single texture object. It is well suited for storing
* block-compressed texture data.
*
* One bundle may be comprised of several mipmap levels, cubemap faces, and array elements. The
* number of blobs is immutable, and is determined as follows.
*
* blob_count = mip_count * array_length * (cubemap ? 6 : 1)
*
* Bundles can be quickly serialized to a certain file format (see below link), but this class lives
* in the image lib rather than imageio because it has no dependencies, and does not support CPU
* decoding.
*
* https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
*/
class UTILS_PUBLIC KtxBundle {
public:
~KtxBundle();
/**
* Creates a hierarchy of empty texture blobs, to be filled later via setBlob().
*/
KtxBundle(uint32_t numMipLevels, uint32_t arrayLength, bool isCubemap);
/**
* Creates a new bundle by deserializing the given data.
*
* Typically, this constructor is used to consume the contents of a KTX file.
*/
KtxBundle(uint8_t const* bytes, uint32_t nbytes);
/**
* Serializes the bundle into the given target memory. Returns false if there's not enough
* memory.
*
* Typically, this method is used to write out the contents of a KTX file.
*/
bool serialize(uint8_t* destination, uint32_t numBytes) const;
/**
* Computes the size (in bytes) of the serialized bundle.
*/
uint32_t getSerializedLength() const;
/**
* Gets or sets information about the texture object, such as format and type.
*/
KtxInfo const& getInfo() const { return mInfo; }
KtxInfo& info() { return mInfo; }
/**
* Gets or sets key/value metadata.
*/
const char* getMetadata(const char* key, size_t* valueSize = nullptr) const;
void setMetadata(const char* key, const char* value);
/**
* Parses the key="sh" metadata and returns 3 bands of data.
*
* Assumes 3 bands for a total of 9 RGB coefficients.
* Returns true if successful.
*/
bool getSphericalHarmonics(filament::math::float3* result);
/**
* Gets the number of miplevels (this is never zero).
*/
uint32_t getNumMipLevels() const { return mNumMipLevels; }
/**
* Gets the number of array elements (this is never zero).
*/
uint32_t getArrayLength() const { return mArrayLength; }
/**
* Returns whether or not this is a cubemap.
*/
bool isCubemap() const { return mNumCubeFaces > 1; }
/**
* Retrieves a weak reference to a given data blob. Returns false if the given blob index is out
* of bounds, or if the blob at the given index is empty.
*/
bool getBlob(KtxBlobIndex index, uint8_t** data, uint32_t* size) const;
/**
* Copies the given data into the blob at the given index, replacing whatever is already there.
* Returns false if the given blob index is out of bounds.
*/
bool setBlob(KtxBlobIndex index, uint8_t const* data, uint32_t size);
/**
* Allocates the blob at the given index to the given number of bytes. This allows subsequent
* calls to setBlob to be thread-safe.
*/
bool allocateBlob(KtxBlobIndex index, uint32_t size);
// The following constants help clients populate the "info" struct. Most of them have corollary
// constants in the OpenGL headers.
static constexpr uint32_t R8 = 0x8229;
static constexpr uint32_t R8_SNORM = 0x8F94;
static constexpr uint32_t R8UI = 0x8232;
static constexpr uint32_t R8I = 0x8231;
static constexpr uint32_t STENCIL_INDEX8 = 0x8D48;
static constexpr uint32_t R16F = 0x822D;
static constexpr uint32_t R16UI = 0x8234;
static constexpr uint32_t R16I = 0x8233;
static constexpr uint32_t RG8 = 0x822B;
static constexpr uint32_t RG8_SNORM = 0x8F95;
static constexpr uint32_t RG8UI = 0x8238;
static constexpr uint32_t RG8I = 0x8237;
static constexpr uint32_t RGB565 = 0x8D62;
static constexpr uint32_t RGB5_A1 = 0x8057;
static constexpr uint32_t RGBA4 = 0x8056;
static constexpr uint32_t DEPTH_COMPONENT16 = 0x81A5;
static constexpr uint32_t RGB8 = 0x8051;
static constexpr uint32_t SRGB8 = 0x8C41;
static constexpr uint32_t RGB8_SNORM = 0x8F96;
static constexpr uint32_t RGB8UI = 0x8D7D;
static constexpr uint32_t RGB8I = 0x8D8F;
static constexpr uint32_t DEPTH_COMPONENT24 = 0x81A6;
static constexpr uint32_t R32F = 0x822E;
static constexpr uint32_t R32UI = 0x8236;
static constexpr uint32_t R32I = 0x8235;
static constexpr uint32_t RG16F = 0x822F;
static constexpr uint32_t RG16UI = 0x823A;
static constexpr uint32_t RG16I = 0x8239;
static constexpr uint32_t R11F_G11F_B10F = 0x8C3A;
static constexpr uint32_t RGB9_E5 = 0x8C3D;
static constexpr uint32_t RGBA8 = 0x8058;
static constexpr uint32_t SRGB8_ALPHA8 = 0x8C43;
static constexpr uint32_t RGBA8_SNORM = 0x8F97;
static constexpr uint32_t RGB10_A2 = 0x8059;
static constexpr uint32_t RGBA8UI = 0x8D7C;
static constexpr uint32_t RGBA8I = 0x8D8E;
static constexpr uint32_t DEPTH_COMPONENT32F = 0x8CAC;
static constexpr uint32_t DEPTH24_STENCIL8 = 0x88F0;
static constexpr uint32_t DEPTH32F_STENCIL8 = 0x8CAD;
static constexpr uint32_t RGB16F = 0x881B;
static constexpr uint32_t RGB16UI = 0x8D77;
static constexpr uint32_t RGB16I = 0x8D89;
static constexpr uint32_t RG32F = 0x8230;
static constexpr uint32_t RG32UI = 0x823C;
static constexpr uint32_t RG32I = 0x823B;
static constexpr uint32_t RGBA16F = 0x881A;
static constexpr uint32_t RGBA16UI = 0x8D76;
static constexpr uint32_t RGBA16I = 0x8D88;
static constexpr uint32_t RGB32F = 0x8815;
static constexpr uint32_t RGB32UI = 0x8D71;
static constexpr uint32_t RGB32I = 0x8D83;
static constexpr uint32_t RGBA32F = 0x8814;
static constexpr uint32_t RGBA32UI = 0x8D70;
static constexpr uint32_t RGBA32I = 0x8D82;
static constexpr uint32_t RED = 0x1903;
static constexpr uint32_t RG = 0x8227;
static constexpr uint32_t RGB = 0x1907;
static constexpr uint32_t RGBA = 0x1908;
static constexpr uint32_t BGR = 0x80E0;
static constexpr uint32_t BGRA = 0x80E1;
static constexpr uint32_t LUMINANCE = 0x1909;
static constexpr uint32_t LUMINANCE_ALPHA = 0x190A;
static constexpr uint32_t UNSIGNED_BYTE = 0x1401;
static constexpr uint32_t UNSIGNED_SHORT = 0x1403;
static constexpr uint32_t HALF_FLOAT = 0x140B;
static constexpr uint32_t FLOAT = 0x1406;
static constexpr uint32_t ENDIAN_DEFAULT = 0x04030201;
static constexpr uint32_t RGB_S3TC_DXT1 = 0x83F0;
static constexpr uint32_t RGBA_S3TC_DXT1 = 0x83F1;
static constexpr uint32_t RGBA_S3TC_DXT3 = 0x83F2;
static constexpr uint32_t RGBA_S3TC_DXT5 = 0x83F3;
static constexpr uint32_t RGBA_ASTC_4x4 = 0x93B0;
static constexpr uint32_t RGBA_ASTC_5x4 = 0x93B1;
static constexpr uint32_t RGBA_ASTC_5x5 = 0x93B2;
static constexpr uint32_t RGBA_ASTC_6x5 = 0x93B3;
static constexpr uint32_t RGBA_ASTC_6x6 = 0x93B4;
static constexpr uint32_t RGBA_ASTC_8x5 = 0x93B5;
static constexpr uint32_t RGBA_ASTC_8x6 = 0x93B6;
static constexpr uint32_t RGBA_ASTC_8x8 = 0x93B7;
static constexpr uint32_t RGBA_ASTC_10x5 = 0x93B8;
static constexpr uint32_t RGBA_ASTC_10x6 = 0x93B9;
static constexpr uint32_t RGBA_ASTC_10x8 = 0x93BA;
static constexpr uint32_t RGBA_ASTC_10x10 = 0x93BB;
static constexpr uint32_t RGBA_ASTC_12x10 = 0x93BC;
static constexpr uint32_t RGBA_ASTC_12x12 = 0x93BD;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_4x4 = 0x93D0;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_5x4 = 0x93D1;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_5x5 = 0x93D2;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_6x5 = 0x93D3;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_6x6 = 0x93D4;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x5 = 0x93D5;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x6 = 0x93D6;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x8 = 0x93D7;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x5 = 0x93D8;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x6 = 0x93D9;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x8 = 0x93DA;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x10 = 0x93DB;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_12x10 = 0x93DC;
static constexpr uint32_t SRGB8_ALPHA8_ASTC_12x12 = 0x93DD;
static constexpr uint32_t R11_EAC = 0x9270;
static constexpr uint32_t SIGNED_R11_EAC = 0x9271;
static constexpr uint32_t RG11_EAC = 0x9272;
static constexpr uint32_t SIGNED_RG11_EAC = 0x9273;
static constexpr uint32_t RGB8_ETC2 = 0x9274;
static constexpr uint32_t SRGB8_ETC2 = 0x9275;
static constexpr uint32_t RGB8_ALPHA1_ETC2 = 0x9276;
static constexpr uint32_t SRGB8_ALPHA1_ETC = 0x9277;
static constexpr uint32_t RGBA8_ETC2_EAC = 0x9278;
static constexpr uint32_t SRGB8_ALPHA8_ETC2_EAC = 0x9279;
private:
image::KtxInfo mInfo = {};
uint32_t mNumMipLevels;
uint32_t mArrayLength;
uint32_t mNumCubeFaces;
std::unique_ptr<KtxBlobList> mBlobs;
std::unique_ptr<KtxMetadata> mMetadata;
};
} // namespace image
#endif /* IMAGE_KTXBUNDLE_H */

View File

@@ -0,0 +1,367 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_KTXUTILITY_H
#define IMAGE_KTXUTILITY_H
#include <filament/Engine.h>
#include <filament/Texture.h>
#include <image/KtxBundle.h>
namespace image {
/**
* Allows clients to create Filament textures from KtxBundle objects.
*
* Note that libimage does not have a dependency on libfilament, so for simplicity this is a
* header-only library with inlined functions.
*/
namespace ktx {
using Texture = filament::Texture;
using Engine = filament::Engine;
using TextureFormat = Texture::InternalFormat;
using CompressedPixelDataType = Texture::CompressedType;
using PixelDataType = Texture::Type;
using PixelDataFormat = Texture::Format;
using PixelBufferDescriptor = Texture::PixelBufferDescriptor;
using Callback = void(*)(void* userdata);
CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info);
PixelDataType toPixelDataType(const KtxInfo& info);
PixelDataFormat toPixelDataFormat(const KtxInfo& info);
bool isCompressed(const KtxInfo& info);
TextureFormat toTextureFormat(const KtxInfo& info);
TextureFormat toSrgbTextureFormat(TextureFormat tex);
/**
* Creates a Texture object from a KTX file and populates all of its faces and miplevels.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Forces the KTX-specified format into an SRGB format if possible
* @param callback Gets called after all texture data has been uploaded to the GPU
* @param userdata Passed into the callback
*/
inline Texture* createTexture(Engine* engine, const KtxBundle& ktx, bool srgb,
Callback callback, void* userdata) {
using Sampler = Texture::Sampler;
const auto& ktxinfo = ktx.getInfo();
const uint32_t nmips = ktx.getNumMipLevels();
const auto cdatatype = toCompressedPixelDataType(ktxinfo);
const auto datatype = toPixelDataType(ktxinfo);
const auto dataformat = toPixelDataFormat(ktxinfo);
auto texformat = toTextureFormat(ktxinfo);
if (srgb) {
texformat = toSrgbTextureFormat(texformat);
}
Texture* texture = Texture::Builder()
.width(ktxinfo.pixelWidth)
.height(ktxinfo.pixelHeight)
.levels(static_cast<uint8_t>(nmips))
.sampler(ktx.isCubemap() ? Sampler::SAMPLER_CUBEMAP : Sampler::SAMPLER_2D)
.format(texformat)
.build(*engine);
struct Userdata {
uint32_t remainingBuffers;
Callback callback;
void* userdata;
};
Userdata* cbuser = new Userdata({nmips, callback, userdata});
PixelBufferDescriptor::Callback cb = [](void*, size_t, void* cbuserptr) {
Userdata* cbuser = (Userdata*) cbuserptr;
if (--cbuser->remainingBuffers == 0) {
if (cbuser->callback) {
cbuser->callback(cbuser->userdata);
}
delete cbuser;
}
};
uint8_t* data;
uint32_t size;
if (isCompressed(ktxinfo)) {
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, cdatatype, size, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
}
return texture;
}
if (ktx.isCubemap()) {
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size * 6, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size));
}
return texture;
}
for (uint32_t level = 0; level < nmips; ++level) {
ktx.getBlob({level, 0, 0}, &data, &size);
PixelBufferDescriptor pbd(data, size, dataformat, datatype, cb, cbuser);
texture->setImage(*engine, level, std::move(pbd));
}
return texture;
}
/**
* Creates a Texture object from a KTX bundle, populates all of its faces and miplevels,
* and automatically destroys the bundle after all the texture data has been uploaded.
*
* @param engine Used to create the Filament Texture
* @param ktx In-memory representation of a KTX file
* @param srgb Forces the KTX-specified format into an SRGB format if possible
*/
inline Texture* createTexture(Engine* engine, KtxBundle* ktx, bool srgb) {
auto freeKtx = [] (void* userdata) {
KtxBundle* ktx = (KtxBundle*) userdata;
delete ktx;
};
return createTexture(engine, *ktx, srgb, freeKtx, ktx);
}
template<typename T>
T toCompressedFilamentEnum(uint32_t format) {
switch (format) {
case KtxBundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
case KtxBundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
case KtxBundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
case KtxBundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
case KtxBundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
case KtxBundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
case KtxBundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
case KtxBundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
case KtxBundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
case KtxBundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
case KtxBundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
case KtxBundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
case KtxBundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
case KtxBundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
case KtxBundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
case KtxBundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
case KtxBundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
case KtxBundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
case KtxBundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
case KtxBundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
case KtxBundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
case KtxBundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
case KtxBundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
case KtxBundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
case KtxBundle::R11_EAC: return T::EAC_R11;
case KtxBundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
case KtxBundle::RG11_EAC: return T::EAC_RG11;
case KtxBundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
case KtxBundle::RGB8_ETC2: return T::ETC2_RGB8;
case KtxBundle::SRGB8_ETC2: return T::ETC2_SRGB8;
case KtxBundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
case KtxBundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
case KtxBundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
case KtxBundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
}
return (T) 0xffff;
}
inline CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info) {
return toCompressedFilamentEnum<CompressedPixelDataType>(info.glInternalFormat);
}
inline PixelDataType toPixelDataType(const KtxInfo& info) {
switch (info.glType) {
case KtxBundle::UNSIGNED_BYTE: return PixelDataType::UBYTE;
case KtxBundle::UNSIGNED_SHORT: return PixelDataType::USHORT;
case KtxBundle::HALF_FLOAT: return PixelDataType::HALF;
case KtxBundle::FLOAT: return PixelDataType::FLOAT;
case KtxBundle::R11F_G11F_B10F: return PixelDataType::UINT_10F_11F_11F_REV;
}
return (PixelDataType) 0xff;
}
inline PixelDataFormat toPixelDataFormat(const KtxInfo& info) {
switch (info.glFormat) {
case KtxBundle::LUMINANCE:
case KtxBundle::RED: return PixelDataFormat::R;
case KtxBundle::RG: return PixelDataFormat::RG;
case KtxBundle::RGB: return PixelDataFormat::RGB;
case KtxBundle::RGBA: return PixelDataFormat::RGBA;
// glFormat should NOT be a sized format according to the spec
// however cmgen was generating incorrect files until after Filament 1.8.0
// so we keep this line here to preserve compatibility with older assets
case KtxBundle::R11F_G11F_B10F: return PixelDataFormat::RGB;
}
return (PixelDataFormat) 0xff;
}
inline bool isCompressed(const KtxInfo& info) {
return info.glFormat == 0;
}
inline TextureFormat toSrgbTextureFormat(TextureFormat format) {
switch(format) {
// Non-compressed
case Texture::InternalFormat::RGB8:
return Texture::InternalFormat::SRGB8;
case Texture::InternalFormat::RGBA8:
return Texture::InternalFormat::SRGB8_A8;
// ASTC
case Texture::InternalFormat::RGBA_ASTC_4x4:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_4x4;
case Texture::InternalFormat::RGBA_ASTC_5x4:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x4;
case Texture::InternalFormat::RGBA_ASTC_5x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x5;
case Texture::InternalFormat::RGBA_ASTC_6x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x5;
case Texture::InternalFormat::RGBA_ASTC_6x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x6;
case Texture::InternalFormat::RGBA_ASTC_8x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x5;
case Texture::InternalFormat::RGBA_ASTC_8x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x6;
case Texture::InternalFormat::RGBA_ASTC_8x8:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x8;
case Texture::InternalFormat::RGBA_ASTC_10x5:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x5;
case Texture::InternalFormat::RGBA_ASTC_10x6:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x6;
case Texture::InternalFormat::RGBA_ASTC_10x8:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x8;
case Texture::InternalFormat::RGBA_ASTC_10x10:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x10;
case Texture::InternalFormat::RGBA_ASTC_12x10:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x10;
case Texture::InternalFormat::RGBA_ASTC_12x12:
return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x12;
// ETC2
case Texture::InternalFormat::ETC2_RGB8:
return Texture::InternalFormat::ETC2_SRGB8;
case Texture::InternalFormat::ETC2_RGB8_A1:
return Texture::InternalFormat::ETC2_SRGB8_A1;
case Texture::InternalFormat::ETC2_EAC_RGBA8:
return Texture::InternalFormat::ETC2_EAC_SRGBA8;
// DXT
case Texture::InternalFormat::DXT1_RGB:
return Texture::InternalFormat::DXT1_SRGB;
case Texture::InternalFormat::DXT1_RGBA:
return Texture::InternalFormat::DXT1_SRGBA;
case Texture::InternalFormat::DXT3_RGBA:
return Texture::InternalFormat::DXT3_SRGBA;
case Texture::InternalFormat::DXT5_RGBA:
return Texture::InternalFormat::DXT5_SRGBA;
default:
return format;
}
}
inline TextureFormat toTextureFormat(const KtxInfo& info) {
switch (info.glInternalFormat) {
case KtxBundle::RED: return TextureFormat::R8;
case KtxBundle::RG: return TextureFormat::RG8;
case KtxBundle::RGB: return TextureFormat::RGB8;
case KtxBundle::RGBA: return TextureFormat::RGBA8;
case KtxBundle::LUMINANCE: return TextureFormat::R8;
case KtxBundle::LUMINANCE_ALPHA: return TextureFormat::RG8;
case KtxBundle::R8: return TextureFormat::R8;
case KtxBundle::R8_SNORM: return TextureFormat::R8_SNORM;
case KtxBundle::R8UI: return TextureFormat::R8UI;
case KtxBundle::R8I: return TextureFormat::R8I;
case KtxBundle::STENCIL_INDEX8: return TextureFormat::STENCIL8;
case KtxBundle::R16F: return TextureFormat::R16F;
case KtxBundle::R16UI: return TextureFormat::R16UI;
case KtxBundle::R16I: return TextureFormat::R16I;
case KtxBundle::RG8: return TextureFormat::RG8;
case KtxBundle::RG8_SNORM: return TextureFormat::RG8_SNORM;
case KtxBundle::RG8UI: return TextureFormat::RG8UI;
case KtxBundle::RG8I: return TextureFormat::RG8I;
case KtxBundle::RGB565: return TextureFormat::RGB565;
case KtxBundle::RGB9_E5: return TextureFormat::RGB9_E5;
case KtxBundle::RGB5_A1: return TextureFormat::RGB5_A1;
case KtxBundle::RGBA4: return TextureFormat::RGBA4;
case KtxBundle::DEPTH_COMPONENT16: return TextureFormat::DEPTH16;
case KtxBundle::RGB8: return TextureFormat::RGB8;
case KtxBundle::SRGB8: return TextureFormat::SRGB8;
case KtxBundle::RGB8_SNORM: return TextureFormat::RGB8_SNORM;
case KtxBundle::RGB8UI: return TextureFormat::RGB8UI;
case KtxBundle::RGB8I: return TextureFormat::RGB8I;
case KtxBundle::R32F: return TextureFormat::R32F;
case KtxBundle::R32UI: return TextureFormat::R32UI;
case KtxBundle::R32I: return TextureFormat::R32I;
case KtxBundle::RG16F: return TextureFormat::RG16F;
case KtxBundle::RG16UI: return TextureFormat::RG16UI;
case KtxBundle::RG16I: return TextureFormat::RG16I;
case KtxBundle::R11F_G11F_B10F: return TextureFormat::R11F_G11F_B10F;
case KtxBundle::RGBA8: return TextureFormat::RGBA8;
case KtxBundle::SRGB8_ALPHA8: return TextureFormat::SRGB8_A8;
case KtxBundle::RGBA8_SNORM: return TextureFormat::RGBA8_SNORM;
case KtxBundle::RGB10_A2: return TextureFormat::RGB10_A2;
case KtxBundle::RGBA8UI: return TextureFormat::RGBA8UI;
case KtxBundle::RGBA8I: return TextureFormat::RGBA8I;
case KtxBundle::DEPTH24_STENCIL8: return TextureFormat::DEPTH24_STENCIL8;
case KtxBundle::DEPTH32F_STENCIL8: return TextureFormat::DEPTH32F_STENCIL8;
case KtxBundle::RGB16F: return TextureFormat::RGB16F;
case KtxBundle::RGB16UI: return TextureFormat::RGB16UI;
case KtxBundle::RGB16I: return TextureFormat::RGB16I;
case KtxBundle::RG32F: return TextureFormat::RG32F;
case KtxBundle::RG32UI: return TextureFormat::RG32UI;
case KtxBundle::RG32I: return TextureFormat::RG32I;
case KtxBundle::RGBA16F: return TextureFormat::RGBA16F;
case KtxBundle::RGBA16UI: return TextureFormat::RGBA16UI;
case KtxBundle::RGBA16I: return TextureFormat::RGBA16I;
case KtxBundle::RGB32F: return TextureFormat::RGB32F;
case KtxBundle::RGB32UI: return TextureFormat::RGB32UI;
case KtxBundle::RGB32I: return TextureFormat::RGB32I;
case KtxBundle::RGBA32F: return TextureFormat::RGBA32F;
case KtxBundle::RGBA32UI: return TextureFormat::RGBA32UI;
case KtxBundle::RGBA32I: return TextureFormat::RGBA32I;
}
return toCompressedFilamentEnum<TextureFormat>(info.glInternalFormat);
}
} // namespace ktx
} // namespace image
#endif

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_LINEARIMAGE_H
#define IMAGE_LINEARIMAGE_H
#include <utils/compiler.h>
#include <cstdint>
/**
* Types and free functions for the Filament core imaging library, primarily used for offline tools,
* but with minimal dependencies to support potential use by the renderer.
*/
namespace image {
/**
* LinearImage is a handle to packed floating point data arranged into a row-major grid.
*
* We use this object as input/output for core algorithms that wish to be agnostic of source and
* destination formats. The number of channels is arbitrary (1 or more) but we often use 3-channel
* images to represent color data.
*
* The underlying pixel data has shared ownership semantics to allow clients to easily pass around
* the image object without incurring a deep copy. Shared access to pixels is not thread safe.
*
* By convention, we do not use channel major order (i.e. planar). However we provide a free
* function in ImageOps to combine planar data. Pixels are stored such that the row stride is simply
* width * channels * sizeof(float).
*/
class UTILS_PUBLIC LinearImage {
public:
~LinearImage();
/**
* Allocates a zeroed-out image.
*/
LinearImage(uint32_t width, uint32_t height, uint32_t channels);
/**
* Makes a shallow copy with shared pixel data.
*/
LinearImage(const LinearImage& that);
LinearImage& operator=(const LinearImage& that);
/**
* Creates an empty (invalid) image.
*/
LinearImage() : mDataRef(nullptr), mData(nullptr), mWidth(0), mHeight(0), mChannels(0) {}
operator bool() const { return mData != nullptr; }
/**
* Gets a pointer to the underlying pixel data.
*/
float* getPixelRef() { return mData; }
template<typename T> T* get() { return reinterpret_cast<T*>(mData); }
/**
* Gets a pointer to immutable pixel data.
*/
float const* getPixelRef() const { return mData; }
template<typename T> T const* get() const { return reinterpret_cast<T const*>(mData); }
/**
* Gets a pointer to the pixel data at the given column and row. (not bounds checked)
*/
float* getPixelRef(uint32_t column, uint32_t row) {
return mData + (column + row * mWidth) * mChannels;
}
template<typename T>
T* get(uint32_t column, uint32_t row) {
return reinterpret_cast<T*>(getPixelRef(column, row));
}
/**
* Gets a pointer to the immutable pixel data at the given column and row. (not bounds checked)
*/
float const* getPixelRef(uint32_t column, uint32_t row) const {
return mData + (column + row * mWidth) * mChannels;
}
template<typename T>
T const* get(uint32_t column, uint32_t row) const {
return reinterpret_cast<T const*>(getPixelRef(column, row));
}
uint32_t getWidth() const { return mWidth; }
uint32_t getHeight() const { return mHeight; }
uint32_t getChannels() const { return mChannels; }
void reset() { *this = LinearImage(); }
bool isValid() const { return mData; }
private:
struct SharedReference;
SharedReference* mDataRef = nullptr;
float* mData;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mChannels;
};
} // namespace image
#endif /* IMAGE_LINEARIMAGE_H */