AnimTestbed/3rdparty/ozz-animation/samples/framework/image.cc

223 lines
9.2 KiB
C++
Raw Normal View History

2021-11-11 21:22:24 +01:00
//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#include "framework/image.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
#include "ozz/base/memory/allocator.h"
namespace ozz {
namespace sample {
namespace image {
bool HasAlpha(Format::Value _format) { return _format >= Format::kRGBA; }
int Stride(Format::Value _format) { return _format <= Format::kBGR ? 3 : 4; }
#define PUSH_PIXEL_RGB(_buffer, _size, _repetition, _pixel, _mapping) \
_buffer[_size + 0] = 0x80 | ((_repetition - 1) & 0xff); \
_buffer[_size + 1] = _pixel.c[_mapping[0]]; \
_buffer[_size + 2] = _pixel.c[_mapping[1]]; \
_buffer[_size + 3] = _pixel.c[_mapping[2]]; \
_size += 4;
#define PUSH_PIXEL_RGBA(_buffer, _size, _repetition, _pixel, _mapping) \
_buffer[_size + 0] = 0x80 | ((_repetition - 1) & 0xff); \
_buffer[_size + 1] = _pixel.c[_mapping[0]]; \
_buffer[_size + 2] = _pixel.c[_mapping[1]]; \
_buffer[_size + 3] = _pixel.c[_mapping[2]]; \
_buffer[_size + 4] = _pixel.c[_mapping[3]]; \
_size += 5;
bool WriteTGA(const char* _filename, int _width, int _height,
Format::Value _src_format, const uint8_t* _src_buffer,
bool _write_alpha) {
union Pixel {
uint8_t c[4];
uint32_t p;
};
assert(_filename && _src_buffer);
ozz::log::LogV() << "Write image to TGA file \"" << _filename << "\"."
<< std::endl;
// Opens output file.
ozz::io::File file(_filename, "wb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open file \"" << _filename
<< "\" for writing." << std::endl;
return false;
}
// Builds and writes tga header.
const uint8_t header[] = {
0, // ID length
0, // Color map type
10, // Image type (RLE true-color)
0,
0,
0,
0,
0, // Color map specification (no color map)
0,
0, // X-origin (2 bytes little-endian)
0,
0, // Y-origin (2 bytes little-endian)
static_cast<uint8_t>(_width & 0xff), // Width (2 bytes little-endian)
static_cast<uint8_t>((_width >> 8) & 0xff),
static_cast<uint8_t>(_height & 0xff), // Height (2 bytes little-endian)
static_cast<uint8_t>((_height >> 8) & 0xff),
static_cast<uint8_t>(_write_alpha ? 32 : 24), // Pixel depth
0}; // Image descriptor
static_assert(sizeof(header) == 18, "Expects 18 bytes structure.");
file.Write(header, sizeof(header));
// Early out if no pixel to write.
if (!_width || !_height) {
return true;
}
// Writes pixels, with RLE compression.
// Prepares component mappings from src to TARGA format.
const uint8_t mappings[4][4] = {
{2, 1, 0, 0}, {0, 1, 2, 0}, {2, 1, 0, 3}, {0, 1, 2, 3}};
const uint8_t* mapping = mappings[_src_format];
// Allocates enough space to store RLE packets for the worst case scenario.
uint8_t* dest_buffer =
reinterpret_cast<uint8_t*>(ozz::memory::default_allocator()->Allocate(
(1 + (_write_alpha ? 4 : 3)) * _width * _height, 4));
size_t dest_size = 0;
if (HasAlpha(_src_format)) {
assert(Stride(_src_format) == 4);
const int src_pitch = _width * 4;
const int src_size = _height * src_pitch;
if (_write_alpha) {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], _src_buffer[line + 3]}};
int count = 1;
for (int p = line + 4; p < line + _width * 4; p += 4, count++) {
const Pixel next = {{_src_buffer[p + 0], _src_buffer[p + 1],
_src_buffer[p + 2], _src_buffer[p + 3]}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
}
} else {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], 0}};
int count = 1;
for (int p = line + 4; p < line + _width * 4; p += 4, count++) {
const Pixel next = {
{_src_buffer[p + 0], _src_buffer[p + 1], _src_buffer[p + 2], 0}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
}
}
} else { // Source has no alpha channel.
assert(Stride(_src_format) == 3);
const int src_pitch = _width * 3;
const int src_size = _height * src_pitch;
if (_write_alpha) {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], 255}};
int count = 1;
for (int p = line + 3; p < line + _width * 3; p += 3, count++) {
const Pixel next = {{_src_buffer[p + 0], _src_buffer[p + 1],
_src_buffer[p + 2], 255}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGBA(dest_buffer, dest_size, count, current, mapping);
}
} else {
for (int line = 0; line < src_size; line += src_pitch) {
Pixel current = {{_src_buffer[line + 0], _src_buffer[line + 1],
_src_buffer[line + 2], 0}};
int count = 1;
for (int p = line + 3; p < line + _width * 3; p += 3, count++) {
const Pixel next = {
{_src_buffer[p + 0], _src_buffer[p + 1], _src_buffer[p + 2], 0}};
if (current.p != next.p || count == 128) {
// Writes current packet.
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
// Starts new RLE packet.
current.p = next.p;
count = 0;
}
}
// Finishes the line.
assert(count > 0 && count <= 128);
PUSH_PIXEL_RGB(dest_buffer, dest_size, count, current, mapping);
}
}
}
// Writes all the RLE packets buffer at once.
file.Write(dest_buffer, dest_size);
ozz::memory::default_allocator()->Deallocate(dest_buffer);
return true;
}
#undef PUSH_PIXEL_RGB
#undef PUSH_PIXEL_RGBA
} // namespace image
} // namespace sample
} // namespace ozz