first commit
This commit is contained in:
381
ios/include/image/ColorTransform.h
Normal file
381
ios/include/image/ColorTransform.h
Normal 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_
|
||||
91
ios/include/image/ImageOps.h
Normal file
91
ios/include/image/ImageOps.h
Normal 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 */
|
||||
164
ios/include/image/ImageSampler.h
Normal file
164
ios/include/image/ImageSampler.h
Normal 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 */
|
||||
279
ios/include/image/KtxBundle.h
Normal file
279
ios/include/image/KtxBundle.h
Normal 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 */
|
||||
367
ios/include/image/KtxUtility.h
Normal file
367
ios/include/image/KtxUtility.h
Normal 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
|
||||
121
ios/include/image/LinearImage.h
Normal file
121
ios/include/image/LinearImage.h
Normal 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 */
|
||||
Reference in New Issue
Block a user