s/picture: use bilinear scaling

This commit is contained in:
rr- 2021-11-30 01:12:17 +01:00 committed by Marcin Kurczewski
parent d3e28825fa
commit 0882d6371f
5 changed files with 138 additions and 24 deletions

View file

@ -3,6 +3,8 @@
#include "memory.h"
#include "specific/s_picture.h"
#include <assert.h>
PICTURE *Picture_Create()
{
PICTURE *picture = Memory_Alloc(sizeof(picture));
@ -25,6 +27,15 @@ PICTURE *Picture_CreateFromFile(const char *file_path)
return picture;
}
bool Picture_Scale(
PICTURE *target_pic, const PICTURE *source_pic, size_t target_width,
size_t target_height)
{
assert(target_pic);
assert(source_pic);
return S_Picture_Scale(target_pic, source_pic, target_width, target_height);
}
void Picture_Free(PICTURE *picture)
{
if (picture) {

View file

@ -9,4 +9,8 @@ PICTURE *Picture_Create();
PICTURE *Picture_CreateFromFile(const char *file_path);
void Picture_Free(PICTURE *picture);
bool Picture_Scale(
PICTURE *target_pic, const PICTURE *source_pic, size_t target_width,
size_t target_height);
#endif

View file

@ -142,10 +142,17 @@ void S_AnimateTextures(int32_t ticks)
void S_DisplayPicture(const char *filename)
{
PICTURE *picture = Picture_CreateFromFile(filename);
if (picture) {
HWR_DownloadPicture(picture);
Picture_Free(picture);
PICTURE *orig_pic = Picture_CreateFromFile(filename);
if (orig_pic) {
PICTURE *scaled_pic = Picture_Create();
if (scaled_pic) {
Picture_Scale(
scaled_pic, orig_pic, g_DDrawSurfaceWidth,
g_DDrawSurfaceHeight);
HWR_DownloadPicture(scaled_pic);
Picture_Free(scaled_pic);
}
Picture_Free(orig_pic);
}
}

View file

@ -4,13 +4,17 @@
#include "log.h"
#include "memory.h"
#include <assert.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
bool S_Picture_LoadFromFile(PICTURE *picture, const char *file_path)
bool S_Picture_LoadFromFile(PICTURE *target_pic, const char *file_path)
{
assert(target_pic);
assert(!target_pic->data);
int32_t error_code;
char *full_path = NULL;
AVFormatContext *format_ctx = NULL;
@ -20,9 +24,9 @@ bool S_Picture_LoadFromFile(PICTURE *picture, const char *file_path)
struct SwsContext *sws_ctx = NULL;
AVPacket *packet = NULL;
picture->width = 0;
picture->height = 0;
picture->data = NULL;
target_pic->width = 0;
target_pic->height = 0;
target_pic->data = NULL;
File_GetFullPath(file_path, &full_path);
@ -95,12 +99,13 @@ bool S_Picture_LoadFromFile(PICTURE *picture, const char *file_path)
goto fail;
}
picture->width = frame->width;
picture->height = frame->height;
target_pic->width = frame->width;
target_pic->height = frame->height;
sws_ctx = sws_getContext(
codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, picture->width,
picture->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);
codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
target_pic->width, target_pic->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,
NULL, NULL, NULL);
if (!sws_ctx) {
LOG_ERROR("Failed to get SWS context");
@ -111,7 +116,7 @@ bool S_Picture_LoadFromFile(PICTURE *picture, const char *file_path)
uint8_t *dst_data[4];
int dst_linesize[4];
error_code = av_image_alloc(
dst_data, dst_linesize, picture->width, picture->height,
dst_data, dst_linesize, target_pic->width, target_pic->height,
AV_PIX_FMT_RGB24, 1);
if (error_code < 0) {
goto fail;
@ -121,14 +126,14 @@ bool S_Picture_LoadFromFile(PICTURE *picture, const char *file_path)
sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0,
frame->height, dst_data, dst_linesize);
picture->data =
Memory_Alloc(picture->height * picture->width * sizeof(RGB888));
target_pic->data =
Memory_Alloc(target_pic->height * target_pic->width * sizeof(RGB888));
av_image_copy_to_buffer(
(uint8_t *)picture->data,
picture->width * picture->height * sizeof(RGB888),
(uint8_t *)target_pic->data,
target_pic->width * target_pic->height * sizeof(RGB888),
(const uint8_t *const *)dst_data, dst_linesize, AV_PIX_FMT_RGB24,
picture->width, picture->height, 1);
target_pic->width, target_pic->height, 1);
return true;
@ -137,11 +142,11 @@ fail:
"Error while opening picture %s: %s", full_path,
av_err2str(error_code));
picture->width = 0;
picture->height = 0;
if (picture->data) {
Memory_Free(picture->data);
picture->data = NULL;
target_pic->width = 0;
target_pic->height = 0;
if (target_pic->data) {
Memory_Free(target_pic->data);
target_pic->data = NULL;
}
if (sws_ctx) {
@ -172,3 +177,87 @@ fail:
return false;
}
bool S_Picture_Scale(
PICTURE *target_pic, const PICTURE *source_pic, int target_width,
int target_height)
{
assert(source_pic);
assert(source_pic->data);
assert(target_pic);
assert(!target_pic->data);
int source_width = source_pic->width;
int source_height = source_pic->height;
// keep aspect ratio and fit inside, adding black bars on the sides
const float source_ratio = source_width / (float)source_height;
const float target_ratio = target_width / (float)target_height;
{
int new_width = source_ratio < target_ratio
? target_height * source_ratio
: target_width;
int new_height = source_ratio < target_ratio
? target_height
: target_width / source_ratio;
target_width = new_width;
target_height = new_height;
}
bool ret = false;
struct SwsContext *sws_ctx = sws_getContext(
source_width, source_height, AV_PIX_FMT_RGB24, target_width,
target_height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL);
if (!sws_ctx) {
LOG_ERROR("Failed to get SWS context");
goto cleanup;
}
target_pic->width = target_width;
target_pic->height = target_height;
target_pic->data =
Memory_Alloc(target_height * target_width * sizeof(RGB888));
uint8_t *src_planes[4];
uint8_t *dst_planes[4];
int src_linesize[4];
int dst_linesize[4];
av_image_fill_arrays(
src_planes, src_linesize, (const uint8_t *)source_pic->data,
AV_PIX_FMT_RGB24, source_width, source_height, 1);
av_image_fill_arrays(
dst_planes, dst_linesize, (const uint8_t *)target_pic->data,
AV_PIX_FMT_RGB24, target_pic->width, target_pic->height, 1);
for (int i = 0; i < 4; i++) {
LOG_DEBUG("%p %d", src_planes[i], src_linesize[i]);
}
for (int i = 0; i < 4; i++) {
LOG_DEBUG("%p %d", dst_planes[i], dst_linesize[i]);
}
sws_scale(
sws_ctx, (const uint8_t *const *)src_planes, src_linesize, 0,
source_height, (uint8_t *const *)dst_planes, dst_linesize);
ret = true;
cleanup:
if (sws_ctx) {
sws_freeContext(sws_ctx);
}
if (!ret) {
if (target_pic) {
Memory_Free(target_pic->data);
target_pic->width = 0;
target_pic->height = 0;
target_pic->data = NULL;
}
}
return ret;
}

View file

@ -5,6 +5,9 @@
#include <stdbool.h>
bool S_Picture_LoadFromFile(PICTURE *picture, const char *file_path);
bool S_Picture_LoadFromFile(PICTURE *target_pic, const char *file_path);
bool S_Picture_Scale(
PICTURE *target_pic, const PICTURE *source_pic, int target_width,
int target_height);
#endif