Как сжать в память с помощью libjpeg-turbo, используя jpeg_mem_dest

Я пытался следовать другому ответу, но просто не могу понять это правильно. У меня есть около 8 МБ растрового изображения RBGX для преобразования в формат JPEG в памяти с помощью libjpeg-turbo. Если я использую jpeg_stdio_dest Я могу записать все это в файл, затем прочитать файл обратно, и все в порядке. Тем не менее, пытаясь использовать jpeg_mem_dest был загадкой Я все равно настроил как jpeg_stdio_dest, но используя mem кажется, только сделать одно выделение 4 КБ, а затем никогда не выделять больше места.

Я не могу найти документы с дальнейшими инструкциями по использованию jpeg_mem_destи действительно мог бы использовать какое-то направление.

void compress(std::vector<unsigned char>& input) {
  jpeg_compress_struct cinfo{};
  jpeg_error_mgr err{};

  cinfo.err = jpeg_std_error(&err);
  jpeg_create_compress(&cinfo);
#if 0 // using this with an open FILE* out works
  jpeg_stdio_dest(&cinfo, out);
#endif


  cinfo.image_width = kWidth; // constants defined somewhere
  cinfo.image_height = kHeight;
  cinfo.input_components = 4;
  cinfo.in_color_space = JCS_EXT_RGBX;

  // what's wrong with this?
  unsigned char* buf{};
  unsigned long buf_sz{};
  jpeg_mem_dest(&cinfo, &buf, &buf_sz);

  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, 70, true);

  jpeg_start_compress(&cinfo, true);   
  while (cinfo.next_scanline < cinfo.image_height) {
    auto row = static_cast<JSAMPROW>(&input[cinfo.next_scanline  * 4  * kWidth]);
    jpeg_write_scanlines(&cinfo, &row, 1);
    // Always prints 4096, and buf never changes 
    std::cout << "buf_sz: " << buf_sz 
              << " buf: " << static_cast<void*>(buf) << '\n';
  }
  jpeg_finish_compress(&cinfo);

  // ...
  // in reality, return the compressed data
}

1 ответ

Решение

Да, это совсем не интуитивно понятно. Программист, предложивший твик jpeg_mem_dest(), не имел большого выбора, расширять существующий API-интерфейс не так просто, когда он изначально не предназначен для поддержки функции. Что совершенно неочевидно, так это то, что ваши переменные не обновляются до тех пор, пока не будет вызван jpeg_finish_compress(). Соответствующий код в библиотеке:

METHODDEF(void)
term_mem_destination (j_compress_ptr cinfo)
{
  my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest;

  *dest->outbuffer = dest->buffer;
  *dest->outsize = (unsigned long)(dest->bufsize - dest->pub.free_in_buffer);
}

Обратите внимание на слово "термин". Эта функция вызывается косвенно через указатель на функцию:

GLOBAL(void)
jpeg_finish_compress (j_compress_ptr cinfo)
{
  //...
  /* Write EOI, do final cleanup */
  (*cinfo->marker->write_file_trailer) (cinfo);
  (*cinfo->dest->term_destination) (cinfo);
  //...
}

Вы ничего не можете с этим поделать. Просто настройте код std::cout, переместите его после цикла, чтобы приспособить работу библиотеки.

Остерегайтесь других мелких деталей этой функции, также не очевидных. Вы должны освободить () буфер, который он создал. Видно в предоставленном примере программы cjpeg.c, конец main().

Другие вопросы по тегам