blob: 688bf88a42e2decce1d04cb154bdb17950108c84 [file] [log] [blame]
/*
* Copyright (C) 2014 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.
*/
#include "gles/texture_codecs.h"
#include <GLES/gl.h>
#ifndef _WIN32
#include <netinet/in.h> // for __BYTE_ORDER
#else
// Windows is little-endian only
#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN 4321
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
#include "common/alog.h"
#include "gles/debug.h"
#if __APPLE__
#define __LITTLE_ENDIAN 1
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
namespace {
// Static (compile time) count of one bits in an integer constant.
// StaticPopulationCount<0>::kValue == 0
// StaticPopulationCount<1>::kValue == 1
// StaticPopulationCount<2>::kValue == 1
// StaticPopulationCount<3>::kValue == 2
// StaticPopulationCount<4>::kValue == 1
// StaticPopulationCount<5>::kValue == 2
// StaticPopulationCount<6>::kValue == 2
// StaticPopulationCount<7>::kValue == 3
// (etc)
template <size_t N> struct StaticPopulationCount {
static const size_t kValue = 1 + StaticPopulationCount<(N & (N - 1))>::kValue;
};
template <> struct StaticPopulationCount<0> {
static const size_t kValue = 0;
};
// Static (compile time) leading zero bit count of an integer constant.
// StaticLeadingZeroCount<0>::kValue == 32
// StaticLeadingZeroCount<1>::kValue == 0
// StaticLeadingZeroCount<2>::kValue == 1
// StaticLeadingZeroCount<3>::kValue == 0
// StaticLeadingZeroCount<4>::kValue == 2
// StaticLeadingZeroCount<5>::kValue == 0
// StaticLeadingZeroCount<6>::kValue == 1
// StaticLeadingZeroCount<7>::kValue == 0
// (etc)
template <uint32_t N> struct StaticLeadingZeroCount {
static const uint32_t _0 = N;
static const uint32_t _1 = _0 | (_0 << 1);
static const uint32_t _2 = _1 | (_1 << 2);
static const uint32_t _3 = _2 | (_2 << 4);
static const uint32_t _4 = _3 | (_3 << 8);
static const uint32_t _5 = _4 | (_4 << 16);
static const size_t kValue = StaticPopulationCount<~_5>::kValue;
};
// Broadcast(component, bits) fills in missing bits in the lower bits of an
// eight bit value, with the goal of expanding a value to the full range of an
// eight bit integer.
//
// The input 'component' should be the value to fully expand to eight bits, and
// should have its most significant bits set correctly, and the remaining bits
// should be zero. 'bits' indicates how many bits at the top are set.
//
// For example, for a one bit value, the topmost bit is just used to fill the
// entire byte:
//
// Broadcast(0x00, 1) -> 0x00
// Broadcast(0x80, 1) -> 0xff
//
// For a four bit value, there are sixteen expected inputs, with the lower four
// bits just set identically to the upper four bits on output:
//
// Broadcast(0x00, 4) -> 0x00
// Broadcast(0x10, 4) -> 0x11
// Broadcast(0x20, 4) -> 0x22
// . . .
// Broadcast(0xf0, 4) -> 0xff
uint8_t Broadcast(uint8_t component, int bits) {
if (bits < 8)
component |= component >> (bits * 1);
if (bits < 4)
component |= component >> (bits * 2);
if (bits < 2)
component |= component >> (bits * 4);
return component;
}
// Bitwise shift helper function
//
// In C/C++, the right hand argument to the ">>" operator is expected to be
// positive, otherwise the behavior is undefined.
//
// This function chooses between left and right shifts
uint32_t ShiftRight(uint32_t n, int cnt) {
return (cnt >= 0) ? (n >> cnt) : (n << -cnt);
}
// Represents a single bit-packed component.
//
// Defines functionality to extract/unpacked the component from the packed value
// as a uint8_t value, or to take a uint8_t value and form the packed value
// using it.
template <uint32_t kMask, uint8_t kDefault> struct PackedComponent {
static const int kMaskShift = StaticLeadingZeroCount<kMask>::kValue;
static const int kMaskWidth = StaticPopulationCount<kMask>::kValue;
// Compute the (possibly negative) right shift amount such that the unpacked
// component will be in the most significant bits of a byte.
// This shift amount is also used to pack up the component again.
static const int kUnpackShift = kMaskWidth + kMaskShift - 8;
static const int kPackShift = -kUnpackShift;
static uint8_t Unpack(uint32_t pv) {
uint8_t masked = static_cast<uint8_t>(ShiftRight(pv & kMask, kUnpackShift));
return Broadcast(masked, kMaskWidth);
}
static uint32_t Pack(uint8_t v) {
uint32_t shifted = ShiftRight(v, kPackShift);
uint32_t packed = shifted & kMask;
return packed;
}
};
// This specialization handles missing color and alpha components, by using the
// value kDefault when unpacking. When packing a value, nothing is written.
// See es_full_spec1.1.12.pdf section 3.6 "Final Expansion to RGBA"
template <uint8_t kDefault> struct PackedComponent<0, kDefault> {
static uint8_t Unpack(uint32_t pv) {
return kDefault;
}
static uint32_t Pack(uint8_t v) {
return 0;
}
};
// Defines the order of bytes in an N byte integer for a Little Endian (LE), Big
// Endian (BE), the current (Native) architecture.
//
// Also defines SwapBE to ensure an integer value is in Big Endian order.
struct Memory2ByteFormatLE {
typedef PackedComponent<0x0000ff, 0> C0;
typedef PackedComponent<0x00ff00, 0> C1;
};
struct Memory2ByteFormatBE {
typedef PackedComponent<0x00ff00, 0> C0;
typedef PackedComponent<0x0000ff, 0> C1;
};
uint32_t SwapEndian(uint32_t v) {
uint32_t result = 0;
result |= (v >> 24) & 0x000000ff;
result |= (v >> 8) & 0x0000ff00;
result |= (v << 8) & 0x00ff0000;
result |= (v << 24) & 0xff000000;
return result;
}
#ifdef __BYTE_ORDER
# if __BYTE_ORDER == __LITTLE_ENDIAN
typedef Memory2ByteFormatLE Memory2ByteFormatNative;
uint32_t SwapBE(uint32_t v) {
return SwapEndian(v);
}
# elif __BYTE_ORDER == __BIG_ENDIAN
typedef Memory2ByteFormatBE Memory2ByteFormatNative;
uint32_t SwapBE(uint32_t v) {
return v;
}
# else
# error Unknown endian
# endif
#else
# error Unknown endian
#endif /* __BYTE_ORDER */
template <typename StorageInt, uint32_t kRedMask, uint32_t kGreenMask,
uint32_t kBlueMask, uint32_t kAlphaMask>
struct PackedFormat {
typedef StorageInt StorageType;
typedef PackedComponent<kRedMask, 0> Red;
typedef PackedComponent<kGreenMask, 0> Green;
typedef PackedComponent<kBlueMask, 0> Blue;
typedef PackedComponent<kAlphaMask, 255> Alpha;
};
bool IsAligned(const void* ptr, int align) {
if (align == 1)
return true;
if (align == 3)
return false;
return ((reinterpret_cast<intptr_t>(ptr) & (align - 1)) == 0);
};
// Defines the intermediate format used when encoding and decoding all other
// formats.
//
// This format allow the data conversion to any format by only supporting N
// conversions to/from this intermediate format rather than the N*N conversions
// directly between every possible format.
//
// All memory access using this format are expected to be aligned.
typedef PackedFormat<uint32_t, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff>
IntermediateFormat;
template <typename SrcF, typename DstF>
struct Convert {
static typename DstF::StorageType Apply(typename SrcF::StorageType v) {
typename DstF::StorageType result = 0;
result |= DstF::Red::Pack(SrcF::Red::Unpack(v));
result |= DstF::Green::Pack(SrcF::Green::Unpack(v));
result |= DstF::Blue::Pack(SrcF::Blue::Unpack(v));
result |= DstF::Alpha::Pack(SrcF::Alpha::Unpack(v));
return result;
}
};
template <typename P, GLenum F, GLenum T>
struct PackedShortCodec {
static const size_t kSize = 2;
static const GLenum kFormat = F;
static const GLenum kType = T;
static uint32_t ReadAligned(const uint8_t* p) {
return Convert<P, IntermediateFormat>::Apply(
*reinterpret_cast<const uint16_t*>(p));
}
static void WriteAligned(uint8_t* p, uint32_t v) {
*reinterpret_cast<uint16_t*>(p) = Convert<IntermediateFormat, P>::Apply(v);
}
static uint32_t ReadUnaligned(const uint8_t* p) {
uint16_t pv = Memory2ByteFormatNative::C0::Pack(*p++);
pv |= Memory2ByteFormatNative::C1::Pack(*p++);
return Convert<P, IntermediateFormat>::Apply(pv);
}
static void WriteUnaligned(uint8_t* p, uint32_t v) {
uint16_t pv = Convert<IntermediateFormat, P>::Apply(v);
*p++ = Memory2ByteFormatNative::C0::Unpack(pv);
*p++ = Memory2ByteFormatNative::C1::Unpack(pv);
}
};
// In addition to the packed pixel types, OpenGL has a notion of storing the
// pixels as a simple sequence of bytes in memory. To read it, you just read the
// bytes in sequence out of memory. The first byte read is the first component,
// and so on.
//
// The number and meaning of each component is defined by format parameter. For
// example, if the format is RGBA, then there are for components, and the first
// component is R (Red), and the last is A.
//
// Note there are no padding/alignment bytes. For the RGB format for example,
// the data for the R component of the next pixel follows immediately after the
// B component for the previous pixel.
template <bool kHasRed, bool kHasGreen, bool kHasBlue, bool kHasLuminance,
bool kHasAlpha, GLenum F>
struct BytestreamCodec {
static const size_t kSize =
kHasRed + kHasGreen + kHasBlue + kHasLuminance + kHasAlpha;
static const GLenum kFormat = F;
static const GLenum kType = GL_UNSIGNED_BYTE;
static uint32_t ReadUnaligned(const uint8_t* p) {
// Byte stream is always aligned.
return ReadAligned(p);
}
static void WriteUnaligned(uint8_t* p, uint32_t v) {
// Byte stream is always aligned.
WriteAligned(p, v);
}
static uint32_t ReadAligned(const uint8_t* p) {
uint32_t v = 0;
if (kHasRed)
v |= IntermediateFormat::Red::Pack(*p++);
if (kHasGreen)
v |= IntermediateFormat::Green::Pack(*p++);
if (kHasBlue)
v |= IntermediateFormat::Blue::Pack(*p++);
if (kHasLuminance) {
// Note: See es_full_spec_1.1.12.pdf section 3.6, "Conversion to
// RGB".
uint8_t luminance = *p++;
v |= IntermediateFormat::Red::Pack(luminance);
v |= IntermediateFormat::Blue::Pack(luminance);
v |= IntermediateFormat::Green::Pack(luminance);
}
if (kHasAlpha)
v |= IntermediateFormat::Alpha::Pack(*p++);
else
v |= IntermediateFormat::Alpha::Pack(255);
return v;
}
static void WriteAligned(uint8_t* p, uint32_t v) {
if (kHasRed)
*p++ = IntermediateFormat::Red::Unpack(v);
if (kHasGreen)
*p++ = IntermediateFormat::Green::Unpack(v);
if (kHasBlue)
*p++ = IntermediateFormat::Blue::Unpack(v);
if (kHasLuminance)
// Note: es_full_spec_1.1.12.pdf section 3.7.1 table 3.8. gles uses red
// as luminance.
*p++ = IntermediateFormat::Red::Unpack(v);
if (kHasAlpha)
*p++ = IntermediateFormat::Alpha::Unpack(v);
}
};
// As a special optimization for RGBA byte streams, allow them to be
// read and written directly out of memory as a stream of uint32_t instead.
// However this may mean that the bytes need to be swapped to match, and it also
// requires the memory to be aligned.
//
// Note: This code assumes that the IntermediateFormat is equivalent to this
// format after correcting for any endian differences.
struct CodecRgba {
static const size_t kSize = 4;
static const GLenum kFormat = GL_RGBA;
static const GLenum kType = GL_UNSIGNED_BYTE;
// This allows us to use the BytestreamCodec functionality for the unaligned
// case, while specializing for the aligned case.
typedef BytestreamCodec<true, true, true, false, true, GL_RGBA> Unaligned;
static uint32_t ReadUnaligned(const uint8_t* p) {
return Unaligned::ReadUnaligned(p);
}
static void WriteUnaligned(uint8_t* p, uint32_t v) {
Unaligned::WriteUnaligned(p, v);
}
static uint32_t ReadAligned(const uint8_t* p) {
uint32_t v = SwapBE(*reinterpret_cast<const uint32_t*>(p));
return v;
}
static void WriteAligned(uint8_t* p, uint32_t v) {
*reinterpret_cast<uint32_t*>(p) = SwapBE(v);
}
};
// Core GLES1 only defines four simple packed formats.
// See es_full_spec1.1.12.pdf section 3.6 table 3.4 where "Type" is
// UNSIGNED_BYTE.
typedef BytestreamCodec<true, true, true, false, false, GL_RGB>
CodecRgb;
typedef BytestreamCodec<false, false, false, true, true, GL_LUMINANCE_ALPHA>
CodecLuminanceAlpha;
typedef BytestreamCodec<false, false, false, true, false, GL_LUMINANCE>
CodecLuminance;
typedef BytestreamCodec<false, false, false, false, true, GL_ALPHA>
CodecAlpha;
// For the packed pixel types, the components are always ordered from the most
// significant bits to the least significant bits.
//
// For example, if the type is UNSIGNED_SHORT_4_4_4_4, then the first component
// will be in the top four bits, and the last will be in the bottom four bits.
//
// The meaning of each component is defined by format parameter. For example, if
// the format is RGBA, then the first component is R (Red), and the last is A
// (Alpha).
//
// Core GLES1 only defines three simple packed formats.
//
// See es_full_spec1.1.12.pdf section 3.6 table 3.6
typedef PackedFormat<uint16_t, 0xf000, 0x0f00, 0x00f0, 0x000f>
PackedShort4444Rgba;
typedef PackedFormat<uint16_t, 0xf800, 0x07c0, 0x003e, 0x0001>
PackedShort5551Rgba;
typedef PackedFormat<uint16_t, 0xf800, 0x07e0, 0x001f, 0>
PackedShort565Rgb;
typedef PackedShortCodec<PackedShort4444Rgba, GL_RGBA,
GL_UNSIGNED_SHORT_4_4_4_4> CodecRgba4444;
typedef PackedShortCodec<PackedShort5551Rgba, GL_RGBA,
GL_UNSIGNED_SHORT_5_5_5_1> CodecRgba5551;
typedef PackedShortCodec<PackedShort565Rgb, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5> CodecRgb565;
} // end anonymous namespace
class ConverterBase {
public:
ConverterBase(size_t src_bpp, size_t dst_bpp)
: src_bpp_(src_bpp),
dst_bpp_(dst_bpp) {
}
virtual ~ConverterBase() {
}
virtual void* Convert(size_t width, size_t height, size_t alignment,
const void* __restrict__ src, void* __restrict__ dst) const {
ALOG_ASSERT(alignment == 1 || alignment == 2 ||
alignment == 4 || alignment == 8);
ALOG_ASSERT(src);
ALOG_ASSERT(dst);
ALOG_ASSERT(width != 0);
ALOG_ASSERT(height != 0);
size_t src_stride =
(width * src_bpp_ + alignment - 1) & ~(alignment - 1);
// Make output buffer 4 bytes aligned.
size_t dst_stride = (width * dst_bpp_ + 3) & ~3;
if (alignment >= src_bpp_ || IsAligned(src, src_bpp_)) {
ConvertAligned(width, height,
src_stride, static_cast<const uint8_t*>(src),
dst_stride, static_cast<uint8_t*>(dst));
} else {
ConvertUnaligned(width, height,
src_stride, static_cast<const uint8_t*>(src),
dst_stride, static_cast<uint8_t*>(dst));
}
return dst;
}
protected:
virtual void ConvertAligned(size_t width, size_t height,
size_t src_stride, const uint8_t* __restrict__ src,
size_t dst_stride, uint8_t* __restrict__ dst) const = 0;
virtual void ConvertUnaligned(size_t width, size_t height,
size_t src_stride, const uint8_t* __restrict__ src,
size_t dst_stride, uint8_t* __restrict__ dst) const = 0;
size_t src_bpp_;
size_t dst_bpp_;
};
template <typename SrcF, typename DstF>
class OptimizedConverter : public ConverterBase {
public:
OptimizedConverter() : ConverterBase(SrcF::kSize, DstF::kSize) {}
protected:
virtual void ConvertAligned(size_t width, size_t height,
size_t src_stride, const uint8_t* __restrict__ src,
size_t dst_stride, uint8_t* __restrict__ dst) const {
for (size_t y = 0; y < height; y++) {
const uint8_t* src_next = src + src_stride;
uint8_t* dst_next = dst + dst_stride;
for (size_t x = 0; x < width; x++) {
uint32_t v = SrcF::ReadAligned(src);
DstF::WriteAligned(dst, v);
src += SrcF::kSize;
dst += DstF::kSize;
}
src = src_next;
dst = dst_next;
}
}
virtual void ConvertUnaligned(size_t width, size_t height,
size_t src_stride, const uint8_t* __restrict__ src,
size_t dst_stride, uint8_t* __restrict__ dst) const {
for (size_t y = 0; y < height; y++) {
const uint8_t* src_next = src + src_stride;
uint8_t* dst_next = dst + dst_stride;
for (size_t x = 0; x < width; x++) {
uint32_t v = SrcF::ReadUnaligned(src);
DstF::WriteAligned(dst, v);
src += SrcF::kSize;
dst += DstF::kSize;
}
src = src_next;
dst = dst_next;
}
}
};
class GeneralConverter : public ConverterBase {
public:
typedef uint32_t (*ReadFunc)(const uint8_t *);
typedef void (*WriteFunc)(uint8_t*, uint32_t);
GeneralConverter(
size_t src_bpp, ReadFunc read_aligned_cb, ReadFunc read_unaligned_cb,
size_t dst_bpp, WriteFunc write_aligned_cb)
: ConverterBase(src_bpp, dst_bpp),
read_aligned_cb_(read_aligned_cb),
read_unaligned_cb_(read_unaligned_cb),
write_aligned_cb_(write_aligned_cb) {
ALOG_ASSERT(read_aligned_cb_);
ALOG_ASSERT(read_unaligned_cb_);
ALOG_ASSERT(write_aligned_cb_);
}
protected:
virtual void ConvertAligned(size_t width, size_t height,
size_t src_stride, const uint8_t* __restrict__ src,
size_t dst_stride, uint8_t* __restrict__ dst) const {
for (size_t y = 0; y < height; y++) {
const uint8_t* src_next = src + src_stride;
uint8_t* dst_next = dst + dst_stride;
for (size_t x = 0; x < width; x++) {
uint32_t v = read_aligned_cb_(src);
write_aligned_cb_(dst, v);
src += src_bpp_;
dst += dst_bpp_;
}
src = src_next;
dst = dst_next;
}
}
virtual void ConvertUnaligned(size_t width, size_t height,
size_t src_stride, const uint8_t* __restrict__ src,
size_t dst_stride, uint8_t* __restrict__ dst) const {
for (size_t y = 0; y < height; y++) {
const uint8_t* src_next = src + src_stride;
uint8_t* dst_next = dst + dst_stride;
for (size_t x = 0; x < width; x++) {
uint32_t v = read_unaligned_cb_(src);
write_aligned_cb_(dst, v);
src += src_bpp_;
dst += dst_bpp_;
}
src = src_next;
dst = dst_next;
}
}
private:
ReadFunc read_aligned_cb_;
ReadFunc read_unaligned_cb_;
WriteFunc write_aligned_cb_;
};
struct Codec {
GLenum format;
GLenum type;
size_t bpp;
GeneralConverter::ReadFunc read_aligned_cb;
GeneralConverter::ReadFunc read_unaligned_cb;
GeneralConverter::WriteFunc write_aligned_cb;
};
#define CODEC_ENTRY(C) \
{ \
C::kFormat, \
C::kType, \
C::kSize, \
C::ReadAligned, \
C::ReadUnaligned, \
C::WriteAligned, \
}
const Codec codecs[] = {
CODEC_ENTRY(CodecRgb),
CODEC_ENTRY(CodecRgba),
CODEC_ENTRY(CodecRgba4444),
CODEC_ENTRY(CodecRgba5551),
CODEC_ENTRY(CodecRgb565),
CODEC_ENTRY(CodecLuminance),
CODEC_ENTRY(CodecLuminanceAlpha),
CODEC_ENTRY(CodecAlpha),
};
TextureConverter::TextureConverter(GLenum src_format, GLenum src_type,
GLenum dst_format, GLenum dst_type)
: converter_(NULL),
src_format_(src_format),
src_type_(src_type),
dst_format_(dst_format),
dst_type_(dst_type) {
InitializeConverter();
}
TextureConverter::~TextureConverter() {
delete converter_;
}
bool TextureConverter::IsValid() const {
return converter_ != NULL;
}
void TextureConverter::InitializeConverter() {
// Generate optimized converters for frequently used conversions.
#define CONVERTER_ENTRY(S, D) \
if (src_format_ == S::kFormat && src_type_ == S::kType && \
dst_format_ == D::kFormat && dst_type_ == D::kType) { \
converter_ = new OptimizedConverter<S, D>(); \
return; \
}
CONVERTER_ENTRY(CodecRgb565, CodecRgb);
CONVERTER_ENTRY(CodecRgba4444, CodecRgba);
CONVERTER_ENTRY(CodecRgba5551, CodecRgba);
CONVERTER_ENTRY(CodecRgba, CodecRgb);
CONVERTER_ENTRY(CodecRgba4444, CodecRgb);
CONVERTER_ENTRY(CodecRgba5551, CodecRgb);
#undef CONVERTER_ENTRY
const Codec* src = NULL;
const Codec* dst = NULL;
for (size_t i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++) {
if (!src && codecs[i].format == src_format_ && codecs[i].type == src_type_)
src = &codecs[i];
if (!dst && codecs[i].format == dst_format_ && codecs[i].type == dst_type_)
dst = &codecs[i];
if (src && dst)
break;
}
if (src && dst) {
ALOGV("(%s, %s) to (%s, %s) conversion is not optimized.",
GetEnumString(src_format_), GetEnumString(src_type_),
GetEnumString(dst_format_), GetEnumString(dst_type_));
converter_ = new GeneralConverter(src->bpp, src->read_aligned_cb,
src->read_unaligned_cb, dst->bpp,
dst->write_aligned_cb);
}
}
void* TextureConverter::Convert(size_t width, size_t height, size_t alignment,
const void* __restrict__ src,
void* __restrict__ dst) const {
return converter_->Convert(width, height, alignment, src, dst);
}