@@ -227,26 +227,71 @@ static unsigned int hap_texture_format_identifier_for_format_constant(unsigned i
227
227
}
228
228
}
229
229
230
- unsigned long HapMaxEncodedLength (unsigned long inputBytes )
230
+ // Returns the length of a decode instructions container of chunk_count chunks
231
+ // not including the section header
232
+ static size_t hap_decode_instructions_length (unsigned int chunk_count )
231
233
{
232
234
/*
233
- Actually our max encoded length is inputBytes + 8U but snappy may produce longer output
234
- and the only way we can find out is by trying with a suitably-sized buffer
235
+ Calculate the size of our Decode Instructions Section
236
+ = Second-Stage Compressor Table + Chunk Size Table + headers for both sections
237
+ = chunk_count + (4 * chunk_count) + 4 + 4
235
238
*/
236
- unsigned long compressedLength = snappy_max_compressed_length (inputBytes );
237
- if (compressedLength < inputBytes ) compressedLength = inputBytes ;
238
- return compressedLength + 8U ;
239
+ size_t length = (5 * chunk_count ) + 8 ;
240
+
241
+ return length ;
242
+ }
243
+
244
+ static unsigned int hap_limited_chunk_count_for_frame (size_t input_bytes , unsigned int texture_format , unsigned int chunk_count )
245
+ {
246
+ // This is a hard limit due to the 4-byte headers we use for the decode instruction container
247
+ // (0xFFFFFF == count + (4 x count) + 20)
248
+ if (chunk_count > 3355431 )
249
+ {
250
+ chunk_count = 3355431 ;
251
+ }
252
+ // Divide frame equally on DXT block boundries (8 or 16 bytes)
253
+ unsigned long dxt_block_count = input_bytes / (texture_format == HapTextureFormat_RGB_DXT1 ? 8 : 16 );
254
+ while (dxt_block_count % chunk_count != 0 ) {
255
+ chunk_count -- ;
256
+ }
257
+
258
+ return chunk_count ;
259
+ }
260
+
261
+ static size_t hap_max_encoded_length (size_t input_bytes , unsigned int texture_format , unsigned int compressor , unsigned int chunk_count )
262
+ {
263
+ size_t decode_instructions_length , max_compressed_length ;
264
+
265
+ chunk_count = hap_limited_chunk_count_for_frame (input_bytes , texture_format , chunk_count );
266
+
267
+ decode_instructions_length = hap_decode_instructions_length (chunk_count );
268
+
269
+ if (compressor == HapCompressorSnappy )
270
+ {
271
+ size_t chunk_size = input_bytes / chunk_count ;
272
+ max_compressed_length = snappy_max_compressed_length (chunk_size ) * chunk_count ;
273
+ }
274
+ else
275
+ {
276
+ max_compressed_length = input_bytes ;
277
+ }
278
+
279
+ // top section header + decode instructions section header + decode instructions + compressed data
280
+ return max_compressed_length + 8U + decode_instructions_length + 4U ;
281
+ }
282
+
283
+ unsigned long HapMaxEncodedLength (unsigned long inputBytes , unsigned int textureFormat , unsigned int chunkCount )
284
+ {
285
+ // Assume snappy, the worst case
286
+ return hap_max_encoded_length (inputBytes , textureFormat , HapCompressorSnappy , chunkCount );
239
287
}
240
288
241
289
unsigned int HapEncode (const void * inputBuffer , unsigned long inputBufferBytes , unsigned int textureFormat ,
242
- unsigned int compressor , void * outputBuffer , unsigned long outputBufferBytes ,
243
- unsigned long * outputBufferBytesUsed )
290
+ unsigned int compressor , unsigned int chunkCount , void * outputBuffer ,
291
+ unsigned long outputBufferBytes , unsigned long * outputBufferBytesUsed )
244
292
{
245
- size_t maxCompressedLength ;
246
- size_t maxOutputBufferLength ;
247
- size_t headerLength ;
248
- void * compressedStart ;
249
- size_t storedLength ;
293
+ size_t top_section_header_length ;
294
+ size_t top_section_length ;
250
295
unsigned int storedCompressor ;
251
296
unsigned int storedFormat ;
252
297
@@ -262,16 +307,14 @@ unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes,
262
307
|| (compressor != HapCompressorNone
263
308
&& compressor != HapCompressorSnappy
264
309
)
310
+ || outputBuffer == NULL
265
311
)
266
312
{
267
313
return HapResult_Bad_Arguments ;
268
314
}
269
-
270
- maxCompressedLength = compressor == HapCompressorSnappy ? snappy_max_compressed_length (inputBufferBytes ) : inputBufferBytes ;
271
- if (maxCompressedLength < inputBufferBytes )
315
+ else if (outputBufferBytes < hap_max_encoded_length (inputBufferBytes , textureFormat , compressor , chunkCount ))
272
316
{
273
- // Sanity check in case a future Snappy promises to always compress
274
- maxCompressedLength = inputBufferBytes ;
317
+ return HapResult_Buffer_Too_Small ;
275
318
}
276
319
277
320
/*
@@ -283,56 +326,110 @@ unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes,
283
326
*/
284
327
if (inputBufferBytes > kHapUInt24Max )
285
328
{
286
- headerLength = 8U ;
329
+ top_section_header_length = 8U ;
287
330
}
288
331
else
289
332
{
290
- headerLength = 4U ;
291
- }
292
-
293
- maxOutputBufferLength = maxCompressedLength + headerLength ;
294
- if (outputBufferBytes < maxOutputBufferLength
295
- || outputBuffer == NULL )
296
- {
297
- return HapResult_Buffer_Too_Small ;
333
+ top_section_header_length = 4U ;
298
334
}
299
- compressedStart = ((uint8_t * )outputBuffer ) + headerLength ;
300
-
335
+
301
336
if (compressor == HapCompressorSnappy )
302
337
{
303
- snappy_status result ;
304
- storedLength = outputBufferBytes ;
305
- result = snappy_compress ((const char * )inputBuffer , inputBufferBytes , (char * )compressedStart , & storedLength );
306
- if (result != SNAPPY_OK )
338
+ /*
339
+ We attempt to chunk as requested, and if resulting frame is larger than it is uncompressed then
340
+ store frame uncompressed
341
+ */
342
+
343
+ size_t decode_instructions_length ;
344
+ size_t chunk_size , compress_buffer_remaining ;
345
+ uint8_t * second_stage_compressor_table ;
346
+ void * chunk_size_table ;
347
+ char * compressed_data ;
348
+ unsigned int i ;
349
+
350
+ chunkCount = hap_limited_chunk_count_for_frame (inputBufferBytes , textureFormat , chunkCount );
351
+ decode_instructions_length = hap_decode_instructions_length (chunkCount );
352
+
353
+ // Check we have space for the Decode Instructions Container
354
+ if ((inputBufferBytes + decode_instructions_length + 4 ) > kHapUInt24Max )
307
355
{
308
- return HapResult_Internal_Error ;
356
+ top_section_header_length = 8U ;
357
+ }
358
+
359
+ second_stage_compressor_table = ((uint8_t * )outputBuffer ) + top_section_header_length + 4 + 4 ;
360
+ chunk_size_table = ((uint8_t * )outputBuffer ) + top_section_header_length + 4 + 4 + chunkCount + 4 ;
361
+
362
+ chunk_size = inputBufferBytes / chunkCount ;
363
+
364
+ // write the Decode Instructions section header
365
+ hap_write_section_header (((uint8_t * )outputBuffer ) + top_section_header_length , 4U , decode_instructions_length , kHapSectionDecodeInstructionsContainer );
366
+ // write the Second Stage Compressor Table section header
367
+ hap_write_section_header (((uint8_t * )outputBuffer ) + top_section_header_length + 4U , 4U , chunkCount , kHapSectionChunkSecondStageCompressorTable );
368
+ // write the Chunk Size Table section header
369
+ hap_write_section_header (((uint8_t * )outputBuffer ) + top_section_header_length + 4U + 4U + chunkCount , 4U , chunkCount * 4U , kHapSectionChunkSizeTable );
370
+
371
+ compressed_data = (char * )(((uint8_t * )outputBuffer ) + top_section_header_length + 4 + decode_instructions_length );
372
+
373
+ compress_buffer_remaining = outputBufferBytes - top_section_header_length - 4 - decode_instructions_length ;
374
+
375
+ top_section_length = 4 + decode_instructions_length ;
376
+
377
+ for (i = 0 ; i < chunkCount ; i ++ ) {
378
+ size_t chunk_packed_length = compress_buffer_remaining ;
379
+ const char * chunk_input_start = (const char * )(((uint8_t * )inputBuffer ) + (chunk_size * i ));
380
+ if (compressor == HapCompressorSnappy )
381
+ {
382
+ snappy_status result = snappy_compress (chunk_input_start , chunk_size , (char * )compressed_data , & chunk_packed_length );
383
+ if (result != SNAPPY_OK )
384
+ {
385
+ return HapResult_Internal_Error ;
386
+ }
387
+ }
388
+
389
+ if (compressor == HapCompressorNone || chunk_packed_length >= chunk_size )
390
+ {
391
+ // store the chunk uncompressed
392
+ memcpy (compressed_data , chunk_input_start , chunk_size );
393
+ chunk_packed_length = chunk_size ;
394
+ second_stage_compressor_table [i ] = kHapCompressorNone ;
395
+ }
396
+ else
397
+ {
398
+ // ie we used snappy and saved some space
399
+ second_stage_compressor_table [i ] = kHapCompressorSnappy ;
400
+ }
401
+ hap_write_4_byte_uint (((uint8_t * )chunk_size_table ) + (i * 4 ), chunk_packed_length );
402
+ compressed_data += chunk_packed_length ;
403
+ top_section_length += chunk_packed_length ;
404
+ compress_buffer_remaining -= chunk_packed_length ;
405
+ }
406
+
407
+ if (top_section_length < inputBufferBytes + top_section_header_length )
408
+ {
409
+ // use the complex storage because snappy compression saved space
410
+ storedCompressor = kHapCompressorComplex ;
411
+ }
412
+ else
413
+ {
414
+ // Signal to store the frame uncompressed
415
+ compressor = HapCompressorNone ;
309
416
}
310
- storedCompressor = kHapCompressorSnappy ;
311
- }
312
- else
313
- {
314
- // HapCompressorNone
315
- // Setting storedLength to 0 causes the frame to be used uncompressed
316
- storedLength = 0 ;
317
417
}
318
-
319
- /*
320
- If our "compressed" frame is no smaller than our input frame then store the input uncompressed.
321
- */
322
- if (storedLength == 0 || storedLength >= inputBufferBytes )
418
+
419
+ if (compressor == HapCompressorNone )
323
420
{
324
- memcpy (compressedStart , inputBuffer , inputBufferBytes );
325
- storedLength = inputBufferBytes ;
421
+ memcpy ((( uint8_t * ) outputBuffer ) + top_section_header_length , inputBuffer , inputBufferBytes );
422
+ top_section_length = inputBufferBytes ;
326
423
storedCompressor = kHapCompressorNone ;
327
424
}
328
425
329
426
storedFormat = hap_texture_format_identifier_for_format_constant (textureFormat );
330
427
331
- hap_write_section_header (outputBuffer , headerLength , storedLength , hap_4_bit_packed_byte (storedCompressor , storedFormat ));
428
+ hap_write_section_header (outputBuffer , top_section_header_length , top_section_length , hap_4_bit_packed_byte (storedCompressor , storedFormat ));
332
429
333
430
if (outputBufferBytesUsed != NULL )
334
431
{
335
- * outputBufferBytesUsed = storedLength + headerLength ;
432
+ * outputBufferBytesUsed = top_section_length + top_section_header_length ;
336
433
}
337
434
338
435
return HapResult_No_Error ;
@@ -591,7 +688,17 @@ unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes,
591
688
*/
592
689
bytesUsed = running_uncompressed_chunk_size ;
593
690
594
- callback ((HapDecodeWorkFunction )hap_decode_chunk , chunk_info , chunk_count , info );
691
+ if (chunk_count == 1 )
692
+ {
693
+ /*
694
+ We don't invoke the callback for one chunk, just decode it directly
695
+ */
696
+ hap_decode_chunk (chunk_info , 0 );
697
+ }
698
+ else
699
+ {
700
+ callback ((HapDecodeWorkFunction )hap_decode_chunk , chunk_info , chunk_count , info );
701
+ }
595
702
596
703
/*
597
704
Check to see if we encountered any errors and report one of them
0 commit comments