RetroArch/gfx/video_texture_image.c

316 lines
7.5 KiB
C
Raw Normal View History

2012-04-21 23:13:50 +02:00
/* RetroArch - A frontend for libretro.
2014-01-01 01:50:59 +01:00
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
2016-01-10 04:41:52 +01:00
* Copyright (C) 2011-2016 - Daniel De Matteis
2011-05-18 20:22:27 +02:00
*
2012-04-21 23:13:50 +02:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2011-05-18 20:22:27 +02:00
* 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.
*
2012-04-21 23:13:50 +02:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2011-05-18 20:22:27 +02:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 23:31:57 +02:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2011-05-18 20:22:27 +02:00
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
2011-05-18 21:49:23 +02:00
#ifdef HAVE_CONFIG_H
2014-02-25 21:24:15 +01:00
#include "../../config.h"
2011-05-18 21:49:23 +02:00
#endif
2011-05-18 20:22:27 +02:00
#include <boolean.h>
2015-02-22 04:56:18 +01:00
#include <formats/image.h>
2016-05-07 03:35:31 +02:00
#include <file/nbio.h>
2011-05-18 21:49:23 +02:00
2016-05-07 03:31:24 +02:00
enum video_image_format
{
IMAGE_FORMAT_NONE = 0,
IMAGE_FORMAT_TGA,
2016-05-07 06:20:17 +02:00
IMAGE_FORMAT_PNG,
IMAGE_FORMAT_JPEG,
IMAGE_FORMAT_BMP
2016-05-07 03:31:24 +02:00
};
static bool video_texture_image_supports_rgba = false;
void video_texture_image_set_rgba(void)
{
video_texture_image_supports_rgba = true;
}
void video_texture_image_unset_rgba(void)
{
video_texture_image_supports_rgba = false;
}
2016-01-30 03:51:24 +01:00
bool video_texture_image_set_color_shifts(
unsigned *r_shift, unsigned *g_shift, unsigned *b_shift,
unsigned *a_shift)
2015-05-04 10:13:53 +02:00
{
2015-09-28 17:18:48 +02:00
*a_shift = 24;
*r_shift = 16;
2015-09-28 17:18:48 +02:00
*g_shift = 8;
*b_shift = 0;
2015-05-04 10:13:53 +02:00
if (video_texture_image_supports_rgba)
{
*r_shift = 0;
*b_shift = 16;
return true;
}
return false;
2015-05-04 10:13:53 +02:00
}
bool video_texture_image_color_convert(unsigned r_shift,
2015-05-04 10:16:47 +02:00
unsigned g_shift, unsigned b_shift, unsigned a_shift,
struct texture_image *out_img)
2013-01-12 14:05:05 +01:00
{
2015-01-26 19:26:06 +01:00
/* This is quite uncommon. */
if (a_shift != 24 || r_shift != 16 || g_shift != 8 || b_shift != 0)
{
uint32_t i;
uint32_t num_pixels = out_img->width * out_img->height;
2015-03-22 04:15:30 +01:00
uint32_t *pixels = (uint32_t*)out_img->pixels;
2013-01-12 14:05:05 +01:00
2015-01-26 19:26:06 +01:00
for (i = 0; i < num_pixels; i++)
2013-01-12 14:05:05 +01:00
{
2015-01-26 19:26:06 +01:00
uint32_t col = pixels[i];
2015-09-28 17:18:48 +02:00
uint8_t a = (uint8_t)(col >> 24);
uint8_t r = (uint8_t)(col >> 16);
uint8_t g = (uint8_t)(col >> 8);
uint8_t b = (uint8_t)(col >> 0);
pixels[i] = (a << a_shift) |
2015-01-26 19:26:06 +01:00
(r << r_shift) | (g << g_shift) | (b << b_shift);
2013-01-12 14:05:05 +01:00
}
2015-05-04 10:16:47 +02:00
return true;
2013-01-12 14:05:05 +01:00
}
2015-05-04 10:16:47 +02:00
return false;
}
#ifdef GEKKO
#define GX_BLIT_LINE_32(off) \
{ \
2015-04-10 07:46:54 +02:00
unsigned x; \
const uint16_t *tmp_src = src; \
2015-04-10 07:46:54 +02:00
uint16_t *tmp_dst = dst; \
for (x = 0; x < width2 >> 3; x++, tmp_src += 8, tmp_dst += 32) \
{ \
tmp_dst[ 0 + off] = tmp_src[0]; \
tmp_dst[ 16 + off] = tmp_src[1]; \
tmp_dst[ 1 + off] = tmp_src[2]; \
tmp_dst[ 17 + off] = tmp_src[3]; \
tmp_dst[ 2 + off] = tmp_src[4]; \
tmp_dst[ 18 + off] = tmp_src[5]; \
tmp_dst[ 3 + off] = tmp_src[6]; \
tmp_dst[ 19 + off] = tmp_src[7]; \
} \
src += tmp_pitch; \
}
2016-05-17 16:15:37 +02:00
static bool video_texture_image_internal_gx_convert_texture32(
2016-01-30 03:51:24 +01:00
struct texture_image *image)
{
unsigned tmp_pitch, width2, i;
2015-03-22 04:15:30 +01:00
const uint16_t *src = NULL;
uint16_t *dst = NULL;
2014-09-09 06:59:17 +02:00
/* Memory allocation in libogc is extremely primitive so try
* to avoid gaps in memory when converting by copying over to
* a temporary buffer first, then converting over into
* main buffer again. */
2016-01-30 03:51:24 +01:00
void *tmp = malloc(image->width
* image->height * sizeof(uint32_t));
if (!tmp)
return false;
2016-01-30 03:51:24 +01:00
memcpy(tmp, image->pixels, image->width
* image->height * sizeof(uint32_t));
tmp_pitch = (image->width * sizeof(uint32_t)) >> 1;
2015-03-22 04:15:30 +01:00
image->width &= ~3;
image->height &= ~3;
width2 = image->width << 1;
src = (uint16_t*)tmp;
dst = (uint16_t*)image->pixels;
for (i = 0; i < image->height; i += 4, dst += 4 * width2)
{
GX_BLIT_LINE_32(0)
GX_BLIT_LINE_32(4)
GX_BLIT_LINE_32(8)
GX_BLIT_LINE_32(12)
}
free(tmp);
return true;
}
#endif
static bool video_texture_image_load_internal(
enum image_type_enum type,
void *ptr,
2016-01-30 03:30:56 +01:00
struct texture_image *out_img,
unsigned a_shift, unsigned r_shift,
unsigned g_shift, unsigned b_shift)
{
int ret;
2016-05-07 04:07:27 +02:00
bool success = false;
2016-05-18 12:17:51 +02:00
void *img = image_transfer_new(type);
if (!img)
2016-05-07 04:07:27 +02:00
goto end;
image_transfer_set_buffer_ptr(img, type, (uint8_t*)ptr);
if (!image_transfer_start(img, type))
2016-05-07 04:07:27 +02:00
goto end;
while (image_transfer_iterate(img, type));
if (!image_transfer_is_valid(img, type))
2016-05-07 04:07:27 +02:00
goto end;
do
2016-01-30 03:30:56 +01:00
{
ret = image_transfer_process(img, type,
(uint32_t**)&out_img->pixels, 0, &out_img->width,
&out_img->height);
}while(ret == IMAGE_PROCESS_NEXT);
if (ret == IMAGE_PROCESS_ERROR || ret == IMAGE_PROCESS_ERROR_END)
2016-05-07 04:07:27 +02:00
goto end;
2016-01-30 03:30:56 +01:00
video_texture_image_color_convert(r_shift, g_shift, b_shift,
a_shift, out_img);
#ifdef GEKKO
2016-05-17 16:15:37 +02:00
if (!video_texture_image_internal_gx_convert_texture32(out_img))
2016-01-30 03:30:56 +01:00
{
2016-05-07 03:35:31 +02:00
video_texture_image_free(out_img);
2016-05-07 04:07:27 +02:00
goto end;
2016-01-30 03:30:56 +01:00
}
#endif
2016-05-07 04:07:27 +02:00
success = true;
2016-05-07 04:07:27 +02:00
end:
if (img)
image_transfer_free(img, type);
2016-05-07 04:07:27 +02:00
return success;
2016-01-30 03:30:56 +01:00
}
void video_texture_image_free(struct texture_image *img)
{
if (!img)
return;
2015-02-22 01:28:54 +01:00
if (img->pixels)
free(img->pixels);
memset(img, 0, sizeof(*img));
}
static enum video_image_format video_texture_image_get_type(const char *path)
2016-05-07 03:31:24 +02:00
{
2016-05-18 12:17:51 +02:00
#ifdef HAVE_RTGA
2016-05-07 03:31:24 +02:00
if (strstr(path, ".tga"))
return IMAGE_FORMAT_TGA;
2016-05-18 12:17:51 +02:00
#endif
#ifdef HAVE_RPNG
2016-05-07 03:31:24 +02:00
if (strstr(path, ".png"))
return IMAGE_FORMAT_PNG;
2016-05-18 12:17:51 +02:00
#endif
#ifdef HAVE_RJPEG
2016-05-07 06:20:17 +02:00
if (strstr(path, ".jpg") || strstr(path, ".jpeg"))
return IMAGE_FORMAT_JPEG;
2016-05-18 12:17:51 +02:00
#endif
if (strstr(path, ".bmp"))
return IMAGE_FORMAT_BMP;
2016-05-07 03:31:24 +02:00
return IMAGE_FORMAT_NONE;
}
2016-01-30 02:48:43 +01:00
static enum image_type_enum video_texture_image_convert_fmt_to_type(enum video_image_format fmt)
{
switch (fmt)
{
2016-05-18 12:17:51 +02:00
#ifdef HAVE_RPNG
case IMAGE_FORMAT_PNG:
return IMAGE_TYPE_PNG;
2016-05-18 12:17:51 +02:00
#endif
#ifdef HAVE_RJPEG
case IMAGE_FORMAT_JPEG:
return IMAGE_TYPE_JPEG;
2016-05-18 12:17:51 +02:00
#endif
case IMAGE_FORMAT_BMP:
return IMAGE_TYPE_BMP;
2016-05-18 12:17:51 +02:00
#ifdef HAVE_RTGA
case IMAGE_FORMAT_TGA:
return IMAGE_TYPE_TGA;
2016-05-18 12:17:51 +02:00
#endif
case IMAGE_FORMAT_NONE:
default:
break;
}
return IMAGE_TYPE_NONE;
}
2016-01-30 03:51:24 +01:00
bool video_texture_image_load(struct texture_image *out_img,
const char *path)
{
2015-05-04 10:13:53 +02:00
unsigned r_shift, g_shift, b_shift, a_shift;
size_t file_len = 0;
struct nbio_t *handle = NULL;
void *ptr = NULL;
2016-05-07 03:31:24 +02:00
enum video_image_format fmt = video_texture_image_get_type(path);
video_texture_image_set_color_shifts(&r_shift, &g_shift, &b_shift,
2015-05-04 10:13:53 +02:00
&a_shift);
if (fmt != IMAGE_FORMAT_NONE)
{
handle = (struct nbio_t*)nbio_open(path, NBIO_READ);
if (!handle)
goto error;
nbio_begin_read(handle);
while (!nbio_iterate(handle));
ptr = nbio_get_ptr(handle, &file_len);
if (!ptr)
goto error;
if (video_texture_image_load_internal(
video_texture_image_convert_fmt_to_type(fmt),
ptr,out_img,
a_shift, r_shift, g_shift, b_shift))
goto success;
2016-05-07 03:31:24 +02:00
}
2015-04-19 16:55:03 +02:00
error:
2016-05-07 04:07:27 +02:00
out_img->pixels = NULL;
out_img->width = 0;
out_img->height = 0;
if (handle)
nbio_free(handle);
return false;
success:
if (handle)
nbio_free(handle);
return true;
}