I'm translating the libheif to pascal code using a dll, for this Delphi library. And I have been able to translate most needed functions, with the sole exception of writing a heif image to a byte array.
Reading a heif image as so is very simple to do, with the help of heif_context_read_from_memory
. But there is no equivalent heif_context_write_to_memory
function.
According to this issue. The library should be able to write a heif image using heif_context_write
and providing a writing callback. And I have been able to successfully add such a implementation. Which is as follows:
const
InputFile = 'C:\Test\sample.heic';
OutputFile = 'output.heic';
var
databytes: PByte;
writer: THeifWriter;
var
ctx: PHeifContext;
imageHandle: PHeifImageHandle;
encoder: PHeifEncoder;
FImage: PHeifImage;
FDataStride: integer;
begin
// Allocate context
ctx := heif_context_alloc();
try
// Make new memory instance
heif_context_read_from_file(ctx, @AnsiString(InputFile)[1], nil).ErrRaise;
// Get primary image
heif_context_get_primary_image_handle(ctx, imageHandle).ErrRaise;
try
// Decode image
heif_decode_image(imageHandle, FImage, THeifColorspace.colorspace_RGB, THeifChroma.chroma_interleaved_RGB, nil).ErrRaise;
// Read plane
heif_image_get_plane(FImage, THeifChannel.channel_interleaved, FDataStride);
finally
heif_image_handle_release(imageHandle);
end;
finally
heif_context_free( ctx );
end;
// Encode
ctx := heif_context_alloc();
try
// Crete encoder
heif_context_get_encoder_for_format(ctx, THeifCompressionFormat.compression_HEVC, encoder).ErrRaise;
try
// Set quality
heif_encoder_set_lossless(encoder, true).ErrRaise;
// Encode
heif_context_encode_image(ctx, FImage, encoder, nil, nil).ErrRaise;
finally
heif_encoder_release(encoder);
end;
// Write
databytes := AllocMem(sizeof(byte));
databytes^ := 123;
writer.writer_api_version := 1;
writer.write := writer_write;
heif_context_write(ctx, writer, databytes);
finally
heif_context_free(ctx);
end;
And the callback procedure:
function TForm1.writer_write(ctx: PHeifContext; const data: Pointer;
size: cardinal; userdata: Pointer): THeifError;
var
value: int64;
MemoryZone: PByte;
begin
// Allocate memory
MemoryZone := AllocMem(size);
try
Move(data, MemoryZone, size);
finally
FreeMem(MemoryZone, size);
end;
// Result
Result := THeifError.Create(THeifErrorNum.heif_error_Invalid_input);
end;
But the Move() procedure in the callback gives me an error. And the size cardinal always seems to be a bit random.
And It's not the encoder of the file. They are both read correctly. If I use heif_context_write_to_file(ctx, @AnsiString(OutputFile)[1])
instead of heif_context_write
, the image is saved correctly.
I've scoured many examples and looked at the documentation. But everything I find is a bit lacking in explications on how that callback procedure even works.
And now I'm out of clues on what to do next.
The function DoWrite
should be declared as class function DoWrite(ctx: PHeifContext; const data: Pointer; size: cardinal; userdata: Pointer): THeifError; static;
If it is a regular method, then Delphi includes a hidden parameter that refers to the Delphi class instance. When declared as a static function this hidden parameter is omitted.
In theory, you could just skip the ctx parameter in the callback function, but then you have the risk that you will crash if this function refers to anything in the class. So the class static function is a cleaner solution.