1. ファイルの変換方法
[準備]
パスを追加
$ vi ~/.bash_profile
export PATH=$PATH:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
[利用方法]
$ texturetool --bits-per-pixels-2 --channel-weighting-linear ¥
-e PVRTC -fPVR -o output.pvr input.png
--bits-per-pixels-2 2bit(サイズ1/16)
--bits-per-pixels-4 4bit(サイズ1/8)
--channel-weighting-linear 誤差をRGBに均等に分散
--channel-weighting-perceptual 緑を優先に
-m ミップマップをつける(ファイルサイズが 1.3倍になる)
[変換サイズ例]
input.png 328kB(パレットカラー) 非圧縮だと4MB
output-2.pvr 256kB
output-4.pvr 512kB
2. テクスチャとして読み込む方法
const char PVRIdentifier[4] = { 'P', 'V', 'R', '!' };
enum Constants {
PVR_FLAG_TYPE_PVRTC_2 = 24,
PVR_FLAG_TYPE_PVRTC_4 = 25,
PVR_FLAG_TYPE_MASK = 0xff,
PVR_MAX_SURFACES = 16
};
struct Header {
uint32_t headerSize;
uint32_t height;
uint32_t width;
uint32_t numMipmaps;
uint32_t flags;
uint32_t dataSize;
uint32_t bpp;
uint32_t bitmaskRed;
uint32_t bitmaskGreen;
uint32_t bitmaskBlue;
uint32_t bitmaskAlpha;
uint32_t tag;
uint32_t numSurfaces;
};
struct Surface {
GLuint size;
const void* bits;
};
static void _loadCompressedTexture( const char* fname,
uint *texture, uint *_width, uint *_height) {
char *buf;
GLuint width, height;
GLenum format;
//bool hasAlpha;
GLuint numSurfaces;
Surface surfaces[PVR_MAX_SURFACES];
NSString* fileLocation = [[NSBundle mainBundle]
pathForResource:[NSString stringWithUTF8String:fname] ofType:@""];
std::ifstream f;
f.open([fileLocation UTF8String]);
if(!f.good())
return;
f.seekg(0, std::ios::end);
int size = f.tellg();
f.seekg(0, std::ios::beg);
buf = new char[size];
f.read(buf, size);
const Header& header = *reinterpret_cast(buf);
uint32_t tag = OSSwapLittleToHostInt32(header.tag);
if(PVRIdentifier[0] != ((tag >> 0) & 0xff) ||
PVRIdentifier[1] != ((tag >> 8) & 0xff) ||
PVRIdentifier[2] != ((tag >> 16) & 0xff) ||
PVRIdentifier[3] != ((tag >> 24) & 0xff)) {
return;
}
uint32_t flags = OSSwapLittleToHostInt32(header.flags);
uint32_t formatFlags = flags & PVR_FLAG_TYPE_MASK;
if(formatFlags == PVR_FLAG_TYPE_PVRTC_4
|| formatFlags == PVR_FLAG_TYPE_PVRTC_2) {
if(formatFlags == PVR_FLAG_TYPE_PVRTC_4)
format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
else if(formatFlags == PVR_FLAG_TYPE_PVRTC_2)
format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
else
return; // false;
width = OSSwapLittleToHostInt32(header.width);
height = OSSwapLittleToHostInt32(header.height);
// hasAlpha = OSSwapLittleToHostInt32(header.bitmaskAlpha) ? true : false;
numSurfaces = 0;
GLuint w = width;
GLuint h = height;
GLuint offset = 0;
GLuint size = OSSwapLittleToHostInt32(header.dataSize);
const uint8_t* pBytes = reinterpret_cast(buf) + sizeof(header);
while(offset < size && numSurfaces < PVR_MAX_SURFACES) {
GLuint blockSize, widthBlocks, heightBlocks, bpp;
Surface& surface = surfaces[numSurfaces++];
if (formatFlags == PVR_FLAG_TYPE_PVRTC_4) {
blockSize = 4 * 4;
widthBlocks = w / 4;
heightBlocks = h / 4;
bpp = 4;
} else {
blockSize = 8 * 4;
widthBlocks = w / 8;
heightBlocks = h / 4;
bpp = 2;
}
if (widthBlocks < 2)
widthBlocks = 2;
if (heightBlocks < 2)
heightBlocks = 2;
surface.size = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
surface.bits = &pBytes[offset];
(w >>= 1) || (w = 1);
(h >>= 1) || (h = 1);
offset += surface.size;
}
if(numSurfaces <= 0) return;
// テクスチャに登録
w = width;
h = height;
(*_width) = width;
(*_height) = height;
for(GLuint i = 0 ; i < numSurfaces ; ++i)
{
const Surface& surface = surfaces[i];
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, (*texture) );
// テクスチャの設定を行う
// glEnable(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glCompressedTexImage2D(GL_TEXTURE_2D,
i,
format,
w,
h,
0,
surface.size,
surface.bits);
(w >>= 1) || (w = 1);
(h >>= 1) || (h = 1);
}
glBindTexture(GL_TEXTURE_2D, 0 );
free(buf);
}
}
3. 結果
圧縮画像を使用するとメモリ使用量が50%以下に低減した(1/8, 1/16まで小さくはならなかった)。2bit/4bitの圧縮はイラストだと線の描画でジャギーが目立つ。
自然画像を多用する場合は、iPhoneに限りPVTRCを利用することを検討するのが良いと思う。

