struct heif_error rav1e_encode_image()

in libheif/plugins/encoder_rav1e.cc [478:655]


struct heif_error rav1e_encode_image(void* encoder_raw, const struct heif_image* image,
                                     heif_image_input_class input_class)
{
  auto* encoder = (struct encoder_struct_rav1e*) encoder_raw;

  const heif_chroma chroma = heif_image_get_chroma_format(image);

  uint8_t yShift = 0;
  RaChromaSampling chromaSampling;
  RaChromaSamplePosition chromaPosition;
  RaPixelRange rav1eRange;

  if (input_class == heif_image_input_class_alpha) {
    chromaSampling = RA_CHROMA_SAMPLING_CS420; // I can't seem to get RA_CHROMA_SAMPLING_CS400 to work right now, unfortunately
    chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN; // TODO: set to CENTER when AV1 and rav1e supports this
    yShift = 1;
  }
  else {
    switch (chroma) {
      case heif_chroma_444:
        chromaSampling = RA_CHROMA_SAMPLING_CS444;
        chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
        break;
      case heif_chroma_422:
        chromaSampling = RA_CHROMA_SAMPLING_CS422;
        chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
        break;
      case heif_chroma_420:
        chromaSampling = RA_CHROMA_SAMPLING_CS420;
        chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN; // TODO: set to CENTER when AV1 and rav1e supports this
        yShift = 1;
        break;
      default:
        return heif_error_codec_library_error;
    }
  }

  struct heif_color_profile_nclx* nclx = nullptr;
  heif_error err = heif_image_get_nclx_color_profile(image, &nclx);
  if (err.code != heif_error_Ok) {
    nclx = nullptr;
  }

  // make sure NCLX profile is deleted at end of function
  auto nclx_deleter = std::unique_ptr<heif_color_profile_nclx, void (*)(heif_color_profile_nclx*)>(nclx, heif_nclx_color_profile_free);

  rav1eRange = RA_PIXEL_RANGE_FULL;
  if (nclx) {
    rav1eRange = nclx->full_range_flag ? RA_PIXEL_RANGE_FULL : RA_PIXEL_RANGE_LIMITED;
  }

  int bitDepth = heif_image_get_bits_per_pixel(image, heif_channel_Y);

  auto rav1eConfigRaw = rav1e_config_default();
  auto rav1eConfig = std::shared_ptr<RaConfig>(rav1eConfigRaw, [](RaConfig* c) { rav1e_config_unref(c); });

  if (rav1e_config_set_pixel_format(rav1eConfig.get(), (uint8_t) bitDepth, chromaSampling, chromaPosition, rav1eRange) < 0) {
    return heif_error_codec_library_error;
  }

  if (rav1e_config_parse(rav1eConfig.get(), "still_picture", "true") == -1) {
    return heif_error_codec_library_error;
  }
  if (rav1e_config_parse_int(rav1eConfig.get(), "width", heif_image_get_width(image, heif_channel_Y)) == -1) {
    return heif_error_codec_library_error;
  }
  if (rav1e_config_parse_int(rav1eConfig.get(), "height", heif_image_get_height(image, heif_channel_Y)) == -1) {
    return heif_error_codec_library_error;
  }
  if (rav1e_config_parse_int(rav1eConfig.get(), "threads", encoder->threads) == -1) {
    return heif_error_codec_library_error;
  }

  if (nclx &&
      (input_class == heif_image_input_class_normal ||
       input_class == heif_image_input_class_thumbnail)) {
    if (rav1e_config_set_color_description(rav1eConfig.get(),
                                           (RaMatrixCoefficients) nclx->matrix_coefficients,
                                           (RaColorPrimaries) nclx->color_primaries,
                                           (RaTransferCharacteristics) nclx->transfer_characteristics) == -1) {
      return heif_error_codec_library_error;
    }
  }

  if (rav1e_config_parse_int(rav1eConfig.get(), "min_quantizer", encoder->min_q) == -1) {
    return heif_error_codec_library_error;
  }

  int base_quantizer = ((100 - encoder->quality) * 255 + 50) / 100;

  if (rav1e_config_parse_int(rav1eConfig.get(), "quantizer", base_quantizer) == -1) {
    return heif_error_codec_library_error;
  }

  if (encoder->tile_rows != 1) {
    if (rav1e_config_parse_int(rav1eConfig.get(), "tile_rows", encoder->tile_rows) == -1) {
      return heif_error_codec_library_error;
    }
  }
  if (encoder->tile_cols != 1) {
    if (rav1e_config_parse_int(rav1eConfig.get(), "tile_cols", encoder->tile_cols) == -1) {
      return heif_error_codec_library_error;
    }
  }
  /*if (encoder->speed != -1)*/ {
    if (rav1e_config_parse_int(rav1eConfig.get(), "speed", encoder->speed) == -1) {
      return heif_error_codec_library_error;
    }
  }

  if (nclx) {
    rav1e_config_set_color_description(rav1eConfig.get(),
                                       (RaMatrixCoefficients) nclx->matrix_coefficients,
                                       (RaColorPrimaries) nclx->color_primaries,
                                       (RaTransferCharacteristics) nclx->transfer_characteristics);
  }

  RaContext* rav1eContextRaw = rav1e_context_new(rav1eConfig.get());
  if (!rav1eContextRaw) {
    return heif_error_codec_library_error;
  }
  auto rav1eContext = std::shared_ptr<RaContext>(rav1eContextRaw, [](RaContext* ctx) { rav1e_context_unref(ctx); });


  // --- copy libheif image to rav1e image

  auto rav1eFrameRaw = rav1e_frame_new(rav1eContext.get());
  auto rav1eFrame = std::shared_ptr<RaFrame>(rav1eFrameRaw, [](RaFrame* frm) { rav1e_frame_unref(frm); });

  int byteWidth = (bitDepth > 8) ? 2 : 1;
  // if (input_class == heif_image_input_class_alpha) {
  //} else
  {
    int strideY;
    const uint8_t* Y = heif_image_get_plane_readonly(image, heif_channel_Y, &strideY);
    int strideCb;
    const uint8_t* Cb = heif_image_get_plane_readonly(image, heif_channel_Cb, &strideCb);
    int strideCr;
    const uint8_t* Cr = heif_image_get_plane_readonly(image, heif_channel_Cr, &strideCr);


    uint32_t height = heif_image_get_height(image, heif_channel_Y);

    uint32_t uvHeight = (height + yShift) >> yShift;
    rav1e_frame_fill_plane(rav1eFrame.get(), 0, Y, strideY * height, strideY, byteWidth);
    rav1e_frame_fill_plane(rav1eFrame.get(), 1, Cb, strideCb * uvHeight, strideCb, byteWidth);
    rav1e_frame_fill_plane(rav1eFrame.get(), 2, Cr, strideCr * uvHeight, strideCr, byteWidth);
  }

  RaEncoderStatus encoderStatus = rav1e_send_frame(rav1eContext.get(), rav1eFrame.get());
  if (encoderStatus != 0) {
    return heif_error_codec_library_error;
  }

  // flush encoder
  encoderStatus = rav1e_send_frame(rav1eContext.get(), nullptr);
  if (encoderStatus != 0) {
    return heif_error_codec_library_error;
  }

  RaPacket* pkt = nullptr;
  encoderStatus = rav1e_receive_packet(rav1eContext.get(), &pkt);
  if (encoderStatus != 0) {
    return heif_error_codec_library_error;
  }

  if (pkt && pkt->data && (pkt->len > 0)) {
    encoder->compressed_data.resize(pkt->len);
    memcpy(encoder->compressed_data.data(), pkt->data, pkt->len);
    encoder->data_read = false;
  }

  if (pkt) {
    rav1e_packet_unref(pkt);
  }

  return heif_error_ok;
}