/* Simple image viewer that opens an image using SDL2_image and presents it * to the screen. * * License: CC0 / Public Domain */ #include #include "common.h" #include "window.h" #include #include #include // Static configuration, done in the file to keep things simple static const char *icc_profile = ""; // path to ICC profile static const char *lut_file = ""; // path to .cube lut // Program state static pl_log logger; static struct window *win; // For rendering static pl_tex img_tex; static pl_tex osd_tex; static struct pl_plane img_plane; static struct pl_plane osd_plane; static pl_renderer renderer; static struct pl_custom_lut *lut; struct file { void *data; size_t size; }; static struct file icc_file; static bool open_file(const char *path, struct file *out) { if (!path || !path[0]) { *out = (struct file) {0}; return true; } FILE *fp = NULL; bool success = false; fp = fopen(path, "rb"); if (!fp) goto done; if (fseeko(fp, 0, SEEK_END)) goto done; off_t size = ftello(fp); if (size < 0) goto done; if (fseeko(fp, 0, SEEK_SET)) goto done; void *data = malloc(size); if (!fread(data, size, 1, fp)) goto done; *out = (struct file) { .data = data, .size = size, }; success = true; done: if (fp) fclose(fp); return success; } static void close_file(struct file *file) { if (!file->data) return; free(file->data); *file = (struct file) {0}; } SDL_NORETURN static void uninit(int ret) { pl_renderer_destroy(&renderer); pl_tex_destroy(win->gpu, &img_tex); pl_tex_destroy(win->gpu, &osd_tex); close_file(&icc_file); pl_lut_free(&lut); window_destroy(&win); pl_log_destroy(&logger); exit(ret); } static bool upload_plane(const SDL_Surface *img, pl_tex *tex, struct pl_plane *plane) { if (!img) return false; SDL_Surface *fixed = NULL; const SDL_PixelFormat *fmt = img->format; if (SDL_ISPIXELFORMAT_INDEXED(fmt->format)) { // libplacebo doesn't handle indexed formats yet fixed = SDL_CreateRGBSurfaceWithFormat(0, img->w, img->h, 32, SDL_PIXELFORMAT_ABGR8888); SDL_BlitSurface((SDL_Surface *) img, NULL, fixed, NULL); img = fixed; fmt = img->format; } struct pl_plane_data data = { .type = PL_FMT_UNORM, .width = img->w, .height = img->h, .pixel_stride = fmt->BytesPerPixel, .row_stride = img->pitch, .pixels = img->pixels, }; uint64_t masks[4] = { fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask }; pl_plane_data_from_mask(&data, masks); bool ok = pl_upload_plane(win->gpu, plane, tex, &data); SDL_FreeSurface(fixed); return ok; } static bool render_frame(const struct pl_swapchain_frame *frame) { pl_tex img = img_plane.texture; struct pl_frame image = { .num_planes = 1, .planes = { img_plane }, .repr = pl_color_repr_unknown, .color = pl_color_space_unknown, .crop = {0, 0, img->params.w, img->params.h}, }; // This seems to be the case for SDL2_image image.repr.alpha = PL_ALPHA_INDEPENDENT; struct pl_frame target; pl_frame_from_swapchain(&target, frame); target.profile = (struct pl_icc_profile) { .data = icc_file.data, .len = icc_file.size, }; image.rotation = PL_ROTATION_0; // for testing pl_rect2df_aspect_copy_rot(&target.crop, &image.crop, 0.0, image.rotation); struct pl_overlay osd; struct pl_overlay_part osd_part; if (osd_tex) { osd_part = (struct pl_overlay_part) { .src = { 0, 0, osd_tex->params.w, osd_tex->params.h }, .dst = { 0, 0, osd_tex->params.w, osd_tex->params.h }, }; osd = (struct pl_overlay) { .tex = osd_tex, .mode = PL_OVERLAY_NORMAL, .repr = image.repr, .color = image.color, .coords = PL_OVERLAY_COORDS_DST_FRAME, .parts = &osd_part, .num_parts = 1, }; target.overlays = &osd; target.num_overlays = 1; } // Use the heaviest preset purely for demonstration/testing purposes struct pl_render_params params = pl_render_high_quality_params; params.lut = lut; return pl_render_image(renderer, &image, &target, ¶ms); } int main(int argc, char **argv) { if (argc < 2 || argc > 3) { fprintf(stderr, "Usage: %s []\n", argv[0]); return 255; } const char *file = argv[1]; const char *overlay = argc > 2 ? argv[2] : NULL; logger = pl_log_create(PL_API_VER, pl_log_params( .log_cb = pl_log_color, .log_level = PL_LOG_INFO, )); // Load image, do this first so we can use it for the window size SDL_Surface *img = IMG_Load(file); if (!img) { fprintf(stderr, "Failed loading '%s': %s\n", file, SDL_GetError()); uninit(1); } // Create window unsigned int start = SDL_GetTicks(); win = window_create(logger, &(struct window_params) { .title = "SDL2_image demo", .width = img->w, .height = img->h, }); if (!win) uninit(1); // Initialize rendering state if (!upload_plane(img, &img_tex, &img_plane)) { fprintf(stderr, "Failed uploading image plane!\n"); uninit(2); } SDL_FreeSurface(img); if (overlay) { SDL_Surface *osd = IMG_Load(overlay); if (!upload_plane(osd, &osd_tex, &osd_plane)) fprintf(stderr, "Failed uploading OSD plane.. continuing anyway\n"); SDL_FreeSurface(osd); } if (!open_file(icc_profile, &icc_file)) fprintf(stderr, "Failed opening ICC profile.. continuing anyway\n"); struct file lutf; if (open_file(lut_file, &lutf) && lutf.size) { if (!(lut = pl_lut_parse_cube(logger, lutf.data, lutf.size))) fprintf(stderr, "Failed parsing LUT.. continuing anyway\n"); close_file(&lutf); } renderer = pl_renderer_create(logger, win->gpu); unsigned int last = SDL_GetTicks(), frames = 0; printf("Took %u ms for initialization\n", last - start); // Render loop while (!win->window_lost) { struct pl_swapchain_frame frame; bool ok = pl_swapchain_start_frame(win->swapchain, &frame); if (!ok) { window_poll(win, true); continue; } if (!render_frame(&frame)) { fprintf(stderr, "libplacebo: Failed rendering frame!\n"); uninit(3); } ok = pl_swapchain_submit_frame(win->swapchain); if (!ok) { fprintf(stderr, "libplacebo: Failed submitting frame!\n"); uninit(3); } pl_swapchain_swap_buffers(win->swapchain); frames++; unsigned int now = SDL_GetTicks(); if (now - last > 5000) { printf("%u frames in %u ms = %f FPS\n", frames, now - last, 1000.0f * frames / (now - last)); last = now; frames = 0; } window_poll(win, false); } uninit(0); }