unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat, unsigned int compressor, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed) { size_t maxCompressedLength; size_t maxOutputBufferLength; size_t headerLength; void *compressedStart; size_t storedLength; unsigned int storedCompressor; unsigned int storedFormat; /* Check arguments */ if (inputBuffer == NULL || inputBufferBytes == 0 || (textureFormat != HapTextureFormat_RGB_DXT1 && textureFormat != HapTextureFormat_RGBA_DXT5 && textureFormat != HapTextureFormat_YCoCg_DXT5 ) || (compressor != HapCompressorNone && compressor != HapCompressorSnappy ) ) { return HapResult_Bad_Arguments; } maxCompressedLength = compressor == HapCompressorSnappy ? snappy_max_compressed_length(inputBufferBytes) : inputBufferBytes; if (maxCompressedLength < inputBufferBytes) { // Sanity check in case a future Snappy promises to always compress maxCompressedLength = inputBufferBytes; } /* To store frames of length greater than can be expressed in three bytes, we use an eight byte header (the last four bytes are the frame size). We don't know the compressed size until we have performed compression, but we know the worst-case size (the uncompressed size), so choose header-length based on that. A simpler encoder could always use the eight-byte header variation. */ if (inputBufferBytes > kHapUInt24Max) { headerLength = 8U; } else { headerLength = 4U; } maxOutputBufferLength = maxCompressedLength + headerLength; if (outputBufferBytes < maxOutputBufferLength || outputBuffer == NULL) { return HapResult_Buffer_Too_Small; } compressedStart = ((uint8_t *)outputBuffer) + headerLength; if (compressor == HapCompressorSnappy) { snappy_status result; storedLength = outputBufferBytes; result = snappy_compress((const char *)inputBuffer, inputBufferBytes, (char *)compressedStart, &storedLength); if (result != SNAPPY_OK) { return HapResult_Internal_Error; } storedCompressor = kHapCompressorSnappy; } else { // HapCompressorNone // Setting storedLength to 0 causes the frame to be used uncompressed storedLength = 0; } /* If our "compressed" frame is no smaller than our input frame then store the input uncompressed. */ if (storedLength == 0 || storedLength >= inputBufferBytes) { memcpy(compressedStart, inputBuffer, inputBufferBytes); storedLength = inputBufferBytes; storedCompressor = kHapCompressorNone; } storedFormat = hap_texture_format_identifier_for_format_constant(textureFormat); hap_write_section_header(outputBuffer, headerLength, storedLength, hap_4_bit_packed_byte(storedCompressor, storedFormat)); if (outputBufferBytesUsed != NULL) { *outputBufferBytesUsed = storedLength + headerLength; } return HapResult_No_Error; }
static unsigned int hap_encode_texture(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat, unsigned int compressor, unsigned int chunkCount, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed) { size_t top_section_header_length; size_t top_section_length; unsigned int storedCompressor; unsigned int storedFormat; /* Check arguments */ if (inputBuffer == NULL || inputBufferBytes == 0 || (textureFormat != HapTextureFormat_RGB_DXT1 && textureFormat != HapTextureFormat_RGBA_DXT5 && textureFormat != HapTextureFormat_YCoCg_DXT5 && textureFormat != HapTextureFormat_A_RGTC1 ) || (compressor != HapCompressorNone && compressor != HapCompressorSnappy ) || outputBuffer == NULL || outputBufferBytesUsed == NULL ) { return HapResult_Bad_Arguments; } else if (outputBufferBytes < hap_max_encoded_length(inputBufferBytes, textureFormat, compressor, chunkCount)) { return HapResult_Buffer_Too_Small; } /* To store frames of length greater than can be expressed in three bytes, we use an eight byte header (the last four bytes are the frame size). We don't know the compressed size until we have performed compression, but we know the worst-case size (the uncompressed size), so choose header-length based on that. A simpler encoder could always use the eight-byte header variation. */ if (inputBufferBytes > kHapUInt24Max) { top_section_header_length = 8U; } else { top_section_header_length = 4U; } if (compressor == HapCompressorSnappy) { /* We attempt to chunk as requested, and if resulting frame is larger than it is uncompressed then store frame uncompressed */ size_t decode_instructions_length; size_t chunk_size, compress_buffer_remaining; uint8_t *second_stage_compressor_table; void *chunk_size_table; char *compressed_data; unsigned int i; chunkCount = hap_limited_chunk_count_for_frame(inputBufferBytes, textureFormat, chunkCount); decode_instructions_length = hap_decode_instructions_length(chunkCount); // Check we have space for the Decode Instructions Container if ((inputBufferBytes + decode_instructions_length + 4) > kHapUInt24Max) { top_section_header_length = 8U; } second_stage_compressor_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4; chunk_size_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4 + chunkCount + 4; chunk_size = inputBufferBytes / chunkCount; // write the Decode Instructions section header hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length, 4U, decode_instructions_length, kHapSectionDecodeInstructionsContainer); // write the Second Stage Compressor Table section header hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U, 4U, chunkCount, kHapSectionChunkSecondStageCompressorTable); // write the Chunk Size Table section header hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U + 4U + chunkCount, 4U, chunkCount * 4U, kHapSectionChunkSizeTable); compressed_data = (char *)(((uint8_t *)outputBuffer) + top_section_header_length + 4 + decode_instructions_length); compress_buffer_remaining = outputBufferBytes - top_section_header_length - 4 - decode_instructions_length; top_section_length = 4 + decode_instructions_length; for (i = 0; i < chunkCount; i++) { size_t chunk_packed_length = compress_buffer_remaining; const char *chunk_input_start = (const char *)(((uint8_t *)inputBuffer) + (chunk_size * i)); if (compressor == HapCompressorSnappy) { snappy_status result = snappy_compress(chunk_input_start, chunk_size, (char *)compressed_data, &chunk_packed_length); if (result != SNAPPY_OK) { return HapResult_Internal_Error; } } if (compressor == HapCompressorNone || chunk_packed_length >= chunk_size) { // store the chunk uncompressed memcpy(compressed_data, chunk_input_start, chunk_size); chunk_packed_length = chunk_size; second_stage_compressor_table[i] = kHapCompressorNone; } else { // ie we used snappy and saved some space second_stage_compressor_table[i] = kHapCompressorSnappy; } hap_write_4_byte_uint(((uint8_t *)chunk_size_table) + (i * 4), chunk_packed_length); compressed_data += chunk_packed_length; top_section_length += chunk_packed_length; compress_buffer_remaining -= chunk_packed_length; } if (top_section_length < inputBufferBytes + top_section_header_length) { // use the complex storage because snappy compression saved space storedCompressor = kHapCompressorComplex; } else { // Signal to store the frame uncompressed compressor = HapCompressorNone; } } if (compressor == HapCompressorNone) { memcpy(((uint8_t *)outputBuffer) + top_section_header_length, inputBuffer, inputBufferBytes); top_section_length = inputBufferBytes; storedCompressor = kHapCompressorNone; } storedFormat = hap_texture_format_identifier_for_format_constant(textureFormat); hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, hap_4_bit_packed_byte(storedCompressor, storedFormat)); *outputBufferBytesUsed = top_section_length + top_section_header_length; return HapResult_No_Error; }