static gboolean stop_load()

in gdk-pixbuf/pixbufloader-heif.c [66:191]


static gboolean stop_load(gpointer context, GError** error)
{
  HeifPixbufCtx* hpc;
  struct heif_error err;
  struct heif_context* hc = NULL;
  struct heif_image_handle* hdl = NULL;
  struct heif_image* img = NULL;
  int width, height, stride;
  int requested_width, requested_height;
  const uint8_t* data;
  GdkPixbuf* pixbuf;
  gboolean result;

  result = FALSE;
  hpc = (HeifPixbufCtx*) context;

  err = heif_init(NULL);
  if (err.code != heif_error_Ok) {
    g_warning("%s", err.message);
    goto cleanup;
  }

  hc = heif_context_alloc();
  if (!hc) {
    g_warning("cannot allocate heif_context");
    goto cleanup;
  }

  err = heif_context_read_from_memory_without_copy(hc, hpc->data->data, hpc->data->len, NULL);
  if (err.code != heif_error_Ok) {
    g_warning("%s", err.message);
    goto cleanup;
  }

  err = heif_context_get_primary_image_handle(hc, &hdl);
  if (err.code != heif_error_Ok) {
    g_warning("%s", err.message);
    goto cleanup;
  }

  int has_alpha = heif_image_handle_has_alpha_channel(hdl);

  err = heif_decode_image(hdl, &img, heif_colorspace_RGB,
                          has_alpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB,
                          NULL);
  if (err.code != heif_error_Ok) {
    g_warning("%s", err.message);
    goto cleanup;
  }

  width = heif_image_get_width(img, heif_channel_interleaved);
  height = heif_image_get_height(img, heif_channel_interleaved);
  requested_width = width;
  requested_height = height;

  if (hpc->size_func) {
    (*hpc->size_func)(&requested_width, &requested_height, hpc->user_data);
  }

  if (requested_width > 0 && requested_height > 0 && (width != requested_width || height != requested_height)) {
    struct heif_image* resized;
    heif_image_scale_image(img, &resized, requested_width, requested_height, NULL);
    heif_image_release(img);
    width = requested_width;
    height = requested_height;
    img = resized;
  }

  data = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);

  pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, has_alpha, 8, width, height, stride, release_heif_image,
                                    img);

  size_t profile_size = heif_image_handle_get_raw_color_profile_size(hdl);
  if(profile_size) {
    guchar *profile_data = (guchar *)g_malloc0(profile_size);

    err = heif_image_handle_get_raw_color_profile(hdl, profile_data);
    if (err.code == heif_error_Ok) {
      gchar *profile_base64 = g_base64_encode(profile_data, profile_size);
      gdk_pixbuf_set_option(pixbuf, "icc-profile", profile_base64);
      g_free(profile_base64);
    }
    else {
      // Having no ICC profile is perfectly fine. Do not show any warning because of that.
    }

    g_free(profile_data);
  }
  
  if (hpc->prepare_func) {
    (*hpc->prepare_func)(pixbuf, NULL, hpc->user_data);
  }

  if (hpc->update_func != NULL) {
    (*hpc->update_func)(pixbuf, 0, 0, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), hpc->user_data);
  }

  g_clear_object(&pixbuf);

  result = TRUE;

  cleanup:
  if (img) {
    // Do not free the image here when we pass it to gdk-pixbuf, as its memory will still be used by gdk-pixbuf.

    if (!result) {
      heif_image_release(img);
    }
  }

  if (hdl) {
    heif_image_handle_release(hdl);
  }

  if (hc) {
    heif_context_free(hc);
  }

  g_byte_array_free(hpc->data, TRUE);
  g_free(hpc);

  heif_deinit();

  return result;
}