Estou salvando um Cairo::ImageSurface
como parte de um arquivo binário. Escrever pixels de surface->get_data()
funciona bem, no entanto, produz um arquivo muito maior comparado a quando escrevo com surface->write_to_png(std::string name)
. Então, estou tentando usar surface->write_to_png_stream(const SlotWriteFunc &write_func);
e Cairo::ImageSurface::create_from_png_stream(const SlotReadFunc &read_func)
;
Inicialmente, o callback Cairo::Surface::SlotWriteFunc fez sentido para mim, ele é chamado várias vezes ao escrever a superfície e fornece parâmetros const unsigned char*
como dados e unsigned int
como comprimento, mas Cairo::Surface::SlotReadFunc me confunde, pois ele também fornece um unsigned int
que determina quantos dados ele quer ler. Por que não é um unsigned int&
, para que eu possa dizer o comprimento com base no arquivo? Isso me leva a uma conclusão que eu não entendo nem SlotWriteFunc
um nem outro.
Por favor, explique como usar SlotWriteFunc
e SlotReadFunc
.
Obrigado pela ajuda.
O exemplo a seguir grava no arquivo corretamente, mas gera um erro quando eu chamo create_from_png_stream
:
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted
#include <fstream>
#include <iostream>
#include <cairomm/cairomm.h>
class PNGStream {
public:
std::fstream *file;
Cairo::ErrorStatus write(const unsigned char *data, unsigned int length) {
std::cout << length << std::endl;
file->write(reinterpret_cast<char *>(&length), sizeof(length));
file->write(reinterpret_cast<const char*>(data), length);
return Cairo::ErrorStatus::CAIRO_STATUS_SUCCESS;
}
Cairo::ErrorStatus read(unsigned char *data, unsigned int length) {
std::cout << length << std::endl;
// I'm not sure, what comes here
return Cairo::ErrorStatus::CAIRO_STATUS_SUCCESS;
}
};
int main() {
std::fstream file;
PNGStream png_stream;
png_stream.file = &file;
auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, 1920, 1080);
auto cr = Cairo::Context::create(surface);
cr->set_source_rgb(0, 0, 0);
cr->paint();
file.open("test.png", std::ios::binary | std::ios::out);
surface->write_to_png_stream(sigc::mem_fun(png_stream, &PNGStream::write));
file.close();
file.open("test.png", std::ios::binary | std::ios::in);
auto surface2 = Cairo::ImageSurface::create_from_png_stream(sigc::mem_fun(png_stream, &PNGStream::read)); //error
file.close();
return 0;
}
Os arquivos PNG têm um formato conhecido, que consiste em blocos, com cada bloco prefixado por seu comprimento. O Cairo pode lê-lo bloco por bloco, até que a leitura falhe, indicando o fim do arquivo.
Quanto ao código que você escreve, você deve apenas ler
length
bytes do fluxo. Se a leitura não for bem-sucedida, o fluxo do arquivo terá o sinalizador de erro definido.Isso funcionará se você tiver um fluxo de arquivo de tamanho exatamente igual ao de um png, mas se você tiver qualquer outra coisa "compactada" no mesmo arquivo (como recursos incorporados), será necessário contar quantos bytes foram lidos e limitar manualmente os bytes lidos, ou seja: falhará se mais bytes forem solicitados.
o lado escritor deveria apenas escrever em vez de ler