199 lines
6.7 KiB
Text
199 lines
6.7 KiB
Text
// Copyright 2009-2021 Intel Corporation
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "texture2d.isph"
|
|
|
|
|
|
// Low-level texel accessors
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// TODO blocking
|
|
|
|
inline Vec4f getTexel_RGBA8(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
assert(self);
|
|
const uint32 c = ((const uniform uint32 *uniform)self->data)[i.y*self->size.x + i.x];
|
|
const uint32 r = c & 0xff;
|
|
const uint32 g = (c >> 8) & 0xff;
|
|
const uint32 b = (c >> 16) & 0xff;
|
|
const uint32 a = c >> 24;
|
|
return make_Vec4f((float)r, (float)g, (float)b, (float)a)*(1.f/255.f);
|
|
}
|
|
|
|
inline Vec4f getTexel_RGB8(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
assert(self);
|
|
const uniform uint8 *uniform texel = (const uniform uint8 *uniform)self->data;
|
|
const uint32 texelOfs = 3*(i.y*self->size.x + i.x);
|
|
const uint32 r = texel[texelOfs];
|
|
const uint32 g = texel[texelOfs+1];
|
|
const uint32 b = texel[texelOfs+2];
|
|
return make_Vec4f(make_Vec3f((float)r, (float)g, (float)b)*(1.f/255.f), 1.f);
|
|
}
|
|
|
|
inline Vec4f getTexel_R8(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
assert(self);
|
|
const uint8 c = ((const uniform uint8 *uniform)self->data)[i.y*self->size.x + i.x];
|
|
return make_Vec4f(c*(1.f/255.f), 0.0f, 0.0f, 1.f);
|
|
}
|
|
|
|
inline Vec4f getTexel_SRGBA(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
return srgba_to_linear(getTexel_RGBA8(self, i));
|
|
}
|
|
|
|
inline Vec4f getTexel_SRGB(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
return srgba_to_linear(getTexel_RGB8(self, i));
|
|
}
|
|
|
|
inline Vec4f getTexel_RGBA32F(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
assert(self);
|
|
return ((const uniform Vec4f *uniform)self->data)[i.y*self->size.x + i.x];
|
|
}
|
|
|
|
inline Vec4f getTexel_RGB32F(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
assert(self);
|
|
Vec3f v = ((const uniform Vec3f*uniform )self->data)[i.y*self->size.x + i.x];
|
|
return make_Vec4f(v, 1.f);
|
|
}
|
|
|
|
inline Vec4f getTexel_R32F(const uniform Texture2D *uniform self, const Vec2i i)
|
|
{
|
|
assert(self);
|
|
float v = ((const uniform float*uniform)self->data)[i.y*self->size.x + i.x];
|
|
return make_Vec4f(v, 0.f, 0.f, 1.f);
|
|
}
|
|
|
|
|
|
// Texture coordinate utilities
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline Vec2i nearest_coords(const uniform Texture2D *uniform self, const Vec2f p)
|
|
{
|
|
// repeat: get remainder within [0..1] parameter space
|
|
Vec2f tc = frac(p);
|
|
tc = max(tc, make_Vec2f(0.0f)); // filter out inf/NaN
|
|
|
|
// scale by texture size
|
|
tc = tc * self->sizef;
|
|
|
|
// nearest
|
|
return make_Vec2i(tc);
|
|
}
|
|
|
|
struct BilinCoords {
|
|
Vec2i st0;
|
|
Vec2i st1;
|
|
Vec2f frac;
|
|
};
|
|
|
|
inline BilinCoords bilinear_coords(const uniform Texture2D *uniform self, const Vec2f p)
|
|
{
|
|
BilinCoords coords;
|
|
|
|
// repeat: get remainder within [0..1] parameter space
|
|
// lower sample shifted by half a texel
|
|
Vec2f tc = frac(p - self->halfTexel);
|
|
tc = max(tc, make_Vec2f(0.0f)); // filter out inf/NaN
|
|
|
|
// scale by texture size
|
|
tc = tc * self->sizef;
|
|
coords.frac = frac(tc);
|
|
|
|
coords.st0 = make_Vec2i(tc);
|
|
coords.st1 = coords.st0 + 1;
|
|
// handle border cases
|
|
if (coords.st1.x >= self->size.x)
|
|
coords.st1.x = 0;
|
|
if (coords.st1.y >= self->size.y)
|
|
coords.st1.y = 0;
|
|
|
|
return coords;
|
|
}
|
|
|
|
inline Vec4f bilerp(const Vec2f frac, const Vec4f c00, const Vec4f c01, const Vec4f c10, const Vec4f c11)
|
|
{
|
|
return lerp(frac.y,
|
|
lerp(frac.x, c00, c01),
|
|
lerp(frac.x, c10, c11));
|
|
}
|
|
|
|
|
|
// Implementations of Texture2D_get for different formats and filter modi
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define __define_tex_get(FMT) \
|
|
\
|
|
static Vec4f Texture2D_nearest_##FMT(const uniform Texture2D *uniform self, \
|
|
const Vec2f &p) \
|
|
{ \
|
|
return getTexel_##FMT(self, nearest_coords(self, p)); \
|
|
} \
|
|
\
|
|
static Vec4f Texture2D_bilinear_##FMT(const uniform Texture2D *uniform self, \
|
|
const Vec2f &p) \
|
|
{ \
|
|
BilinCoords cs = bilinear_coords(self, p); \
|
|
\
|
|
const Vec4f c00 = getTexel_##FMT(self, make_Vec2i(cs.st0.x, cs.st0.y)); \
|
|
const Vec4f c01 = getTexel_##FMT(self, make_Vec2i(cs.st1.x, cs.st0.y)); \
|
|
const Vec4f c10 = getTexel_##FMT(self, make_Vec2i(cs.st0.x, cs.st1.y)); \
|
|
const Vec4f c11 = getTexel_##FMT(self, make_Vec2i(cs.st1.x, cs.st1.y)); \
|
|
\
|
|
return bilerp(cs.frac, c00, c01, c10, c11); \
|
|
}
|
|
|
|
#define __define_tex_get_case(FMT) \
|
|
case TEXTURE_##FMT: return filter_nearest ? &Texture2D_nearest_##FMT : \
|
|
&Texture2D_bilinear_##FMT;
|
|
|
|
#define __foreach_fetcher(FCT) \
|
|
FCT(RGBA8) \
|
|
FCT(SRGBA) \
|
|
FCT(RGBA32F) \
|
|
FCT(RGB8) \
|
|
FCT(SRGB) \
|
|
FCT(RGB32F) \
|
|
FCT(R8) \
|
|
FCT(R32F)
|
|
|
|
__foreach_fetcher(__define_tex_get)
|
|
|
|
static uniform Texture2D_get Texture2D_get_addr(const uniform uint32 type,
|
|
const uniform bool filter_nearest)
|
|
{
|
|
switch (type) {
|
|
__foreach_fetcher(__define_tex_get_case)
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
#undef __define_tex_get
|
|
#undef __define_tex_get_addr
|
|
#undef __foreach_fetcher
|
|
|
|
|
|
// Exports (called from C++)
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
export void *uniform Texture2D_create(uniform Vec2i &size, void *uniform data,
|
|
uniform uint32 type, uniform uint32 flags)
|
|
{
|
|
uniform Texture2D *uniform self = uniform new uniform Texture2D;
|
|
self->size = size;
|
|
|
|
// Due to float rounding frac(x) can be exactly 1.0f (e.g. for very small
|
|
// negative x), although it should be strictly smaller than 1.0f. We handle
|
|
// this case by having sizef slightly smaller than size, such that
|
|
// frac(x)*sizef is always < size.
|
|
self->sizef = make_Vec2f(nextafter((float)size.x, -1.0f), nextafter((float)size.y, -1.0f));
|
|
self->halfTexel = make_Vec2f(0.5f/size.x, 0.5f/size.y);
|
|
self->data = data;
|
|
self->get = Texture2D_get_addr(type, flags & TEXTURE_FILTER_NEAREST);
|
|
|
|
return self;
|
|
}
|