223 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			223 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								//----------------------------------------------------------------------------//
							 | 
						||
| 
								 | 
							
								//                                                                            //
							 | 
						||
| 
								 | 
							
								// 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
							 |