/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see .
*/
#include "scaler.h"
#include "scaler_int.h"
#include "filter.h"
#include "pixconv.h"
#include
#include
#include
#include
#include "../../libretro.h"
#include "../../performance.h"
// In case aligned allocs are needed later ...
void *scaler_alloc(size_t elem_size, size_t size)
{
return calloc(elem_size, size);
}
void scaler_free(void *ptr)
{
free(ptr);
}
static bool allocate_frames(struct scaler_ctx *ctx)
{
ctx->scaled.stride = ((ctx->out_width + 7) & ~7) * sizeof(uint64_t);
ctx->scaled.width = ctx->out_width;
ctx->scaled.height = ctx->in_height;
ctx->scaled.frame = (uint64_t*)scaler_alloc(sizeof(uint64_t), (ctx->scaled.stride * ctx->scaled.height) >> 3);
if (!ctx->scaled.frame)
return false;
if (ctx->in_fmt != SCALER_FMT_ARGB8888)
{
ctx->input.stride = ((ctx->in_width + 7) & ~7) * sizeof(uint32_t);
ctx->input.frame = (uint32_t*)scaler_alloc(sizeof(uint32_t), (ctx->input.stride * ctx->in_height) >> 2);
if (!ctx->input.frame)
return false;
}
if (ctx->out_fmt != SCALER_FMT_ARGB8888)
{
ctx->output.stride = ((ctx->out_width + 7) & ~7) * sizeof(uint32_t);
ctx->output.frame = (uint32_t*)scaler_alloc(sizeof(uint32_t), (ctx->output.stride * ctx->out_height) >> 2);
if (!ctx->output.frame)
return false;
}
return true;
}
static bool set_direct_pix_conv(struct scaler_ctx *ctx)
{
if (ctx->in_fmt == ctx->out_fmt)
ctx->direct_pixconv = conv_copy;
else if (ctx->in_fmt == SCALER_FMT_0RGB1555 && ctx->out_fmt == SCALER_FMT_ARGB8888)
ctx->direct_pixconv = conv_0rgb1555_argb8888;
else if (ctx->in_fmt == SCALER_FMT_RGB565 && ctx->out_fmt == SCALER_FMT_ARGB8888)
ctx->direct_pixconv = conv_rgb565_argb8888;
else if (ctx->in_fmt == SCALER_FMT_RGB565 && ctx->out_fmt == SCALER_FMT_BGR24)
ctx->direct_pixconv = conv_rgb565_bgr24;
else if (ctx->in_fmt == SCALER_FMT_0RGB1555 && ctx->out_fmt == SCALER_FMT_RGB565)
ctx->direct_pixconv = conv_0rgb1555_rgb565;
else if (ctx->in_fmt == SCALER_FMT_RGB565 && ctx->out_fmt == SCALER_FMT_0RGB1555)
ctx->direct_pixconv = conv_rgb565_0rgb1555;
else if (ctx->in_fmt == SCALER_FMT_BGR24 && ctx->out_fmt == SCALER_FMT_ARGB8888)
ctx->direct_pixconv = conv_bgr24_argb8888;
else if (ctx->in_fmt == SCALER_FMT_ARGB8888 && ctx->out_fmt == SCALER_FMT_0RGB1555)
ctx->direct_pixconv = conv_argb8888_0rgb1555;
else if (ctx->in_fmt == SCALER_FMT_ARGB8888 && ctx->out_fmt == SCALER_FMT_BGR24)
ctx->direct_pixconv = conv_argb8888_bgr24;
else if (ctx->in_fmt == SCALER_FMT_0RGB1555 && ctx->out_fmt == SCALER_FMT_BGR24)
ctx->direct_pixconv = conv_0rgb1555_bgr24;
else if (ctx->in_fmt == SCALER_FMT_ARGB8888 && ctx->out_fmt == SCALER_FMT_ABGR8888)
ctx->direct_pixconv = conv_argb8888_abgr8888;
else if (ctx->in_fmt == SCALER_FMT_YUYV && ctx->out_fmt == SCALER_FMT_ARGB8888)
ctx->direct_pixconv = conv_yuyv_argb8888;
else if (ctx->in_fmt == SCALER_FMT_RGBA4444 && ctx->out_fmt == SCALER_FMT_ARGB8888)
ctx->direct_pixconv = conv_rgba4444_argb8888;
else
return false;
return true;
}
static bool set_pix_conv(struct scaler_ctx *ctx)
{
switch (ctx->in_fmt)
{
case SCALER_FMT_ARGB8888:
// No need to convert :D
break;
case SCALER_FMT_0RGB1555:
ctx->in_pixconv = conv_0rgb1555_argb8888;
break;
case SCALER_FMT_RGB565:
ctx->in_pixconv = conv_rgb565_argb8888;
break;
case SCALER_FMT_BGR24:
ctx->in_pixconv = conv_bgr24_argb8888;
break;
case SCALER_FMT_RGBA4444:
ctx->in_pixconv = conv_rgba4444_argb8888;
break;
default:
return false;
}
switch (ctx->out_fmt)
{
case SCALER_FMT_ARGB8888:
// No need to convert :D
break;
case SCALER_FMT_0RGB1555:
ctx->out_pixconv = conv_argb8888_0rgb1555;
break;
case SCALER_FMT_BGR24:
ctx->out_pixconv = conv_argb8888_bgr24;
break;
default:
return false;
}
return true;
}
bool scaler_ctx_gen_filter(struct scaler_ctx *ctx)
{
scaler_ctx_gen_reset(ctx);
if (ctx->in_width == ctx->out_width && ctx->in_height == ctx->out_height)
ctx->unscaled = true; // Only pixel format conversion ...
else
{
ctx->scaler_horiz = scaler_argb8888_horiz;
ctx->scaler_vert = scaler_argb8888_vert;
ctx->unscaled = false;
}
ctx->scaler_special = NULL;
if (!allocate_frames(ctx))
return false;
if (ctx->unscaled)
{
if (!set_direct_pix_conv(ctx))
return false;
}
else
{
if (!set_pix_conv(ctx))
return false;
}
if (!ctx->unscaled && !scaler_gen_filter(ctx))
return false;
return true;
}
void scaler_ctx_gen_reset(struct scaler_ctx *ctx)
{
scaler_free(ctx->horiz.filter);
scaler_free(ctx->horiz.filter_pos);
scaler_free(ctx->vert.filter);
scaler_free(ctx->vert.filter_pos);
scaler_free(ctx->scaled.frame);
scaler_free(ctx->input.frame);
scaler_free(ctx->output.frame);
memset(&ctx->horiz, 0, sizeof(ctx->horiz));
memset(&ctx->vert, 0, sizeof(ctx->vert));
memset(&ctx->scaled, 0, sizeof(ctx->scaled));
memset(&ctx->input, 0, sizeof(ctx->input));
memset(&ctx->output, 0, sizeof(ctx->output));
}
void scaler_ctx_scale(struct scaler_ctx *ctx,
void *output, const void *input)
{
if (ctx->unscaled) // Just perform straight pixel conversion.
{
ctx->direct_pixconv(output, input,
ctx->out_width, ctx->out_height,
ctx->out_stride, ctx->in_stride);
}
else if (ctx->scaler_special) // Take some special, and (hopefully) more optimized path.
{
const void *inp = input;
int in_stride = ctx->in_stride;
if (ctx->in_fmt != SCALER_FMT_ARGB8888)
{
ctx->in_pixconv(ctx->input.frame, input,
ctx->in_width, ctx->in_height,
ctx->input.stride, ctx->in_stride);
inp = ctx->input.frame;
in_stride = ctx->input.stride;
}
bool conv_out = ctx->out_fmt != SCALER_FMT_ARGB8888;
void *outp = output;
int out_stride = ctx->out_stride;
if (conv_out)
{
outp = ctx->output.frame;
out_stride = ctx->output.stride;
}
ctx->scaler_special(ctx, outp, inp,
ctx->out_width, ctx->out_height,
ctx->in_width, ctx->in_height,
out_stride, in_stride);
if (conv_out)
{
ctx->out_pixconv(output, ctx->output.frame,
ctx->out_width, ctx->out_height,
ctx->out_stride, ctx->output.stride);
}
}
else // Take generic filter path.
{
if (ctx->in_fmt != SCALER_FMT_ARGB8888)
{
ctx->in_pixconv(ctx->input.frame, input,
ctx->in_width, ctx->in_height,
ctx->input.stride, ctx->in_stride);
ctx->scaler_horiz(ctx, ctx->input.frame, ctx->input.stride);
}
else
ctx->scaler_horiz(ctx, input, ctx->in_stride);
if (ctx->out_fmt != SCALER_FMT_ARGB8888)
{
ctx->scaler_vert(ctx, ctx->output.frame, ctx->output.stride);
ctx->out_pixconv(output, ctx->output.frame,
ctx->out_width, ctx->out_height,
ctx->out_stride, ctx->output.stride);
}
else
ctx->scaler_vert(ctx, output, ctx->out_stride);
}
}