SDL/src/render/nds/SDL_ndsrender.c
Sam Lantinga 41dcf2a16e Updated Nintendo DS support
Frank Zago

This patch updates the DS port:
- do not use the now removed compat layer.
- integrate parts of libgl2D since I got permission from the author, and thus
removed an external dependancy,
- a few bugs fixes.

Now, the textures should be completely supported, except reading from them
which doesn't makes sense to have on the DS. Sound is still not supported.

If someone else wants to work on the missing pieces, feel free.
2012-02-12 21:04:01 -05:00

419 lines
11 KiB
C
Executable File

/*
Simple DirectMedia Layer
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_config.h"
#if SDL_VIDEO_RENDER_NDS
#include <stdio.h>
#include <stdlib.h>
#include <nds.h>
#include "SDL_libgl2D.h"
#include "SDL_video.h"
#include "../../video/SDL_sysvideo.h"
#include "SDL_render.h"
#include "../SDL_sysrender.h"
#include "SDL_log.h"
/* Draws a partial sprite. Based on glSprite. */
static void glSpritePartial(const SDL_Rect * srcrect, int x, int y, int flipmode, const glImage *spr)
{
int x1 = x;
int y1 = y;
int x2 = x + srcrect->w;
int y2 = y + srcrect->h;
int u1 = srcrect->x + ((flipmode & GL_FLIP_H) ? spr->width-1 : 0);
int u2 = srcrect->x + ((flipmode & GL_FLIP_H) ? 0 : srcrect->h);
int v1 = srcrect->y + ((flipmode & GL_FLIP_V) ? spr->height-1 : 0);
int v2 = srcrect->y + ((flipmode & GL_FLIP_V) ? 0 : srcrect->h);
if (spr->textureID != gCurrentTexture) {
glBindTexture(GL_TEXTURE_2D, spr->textureID);
gCurrentTexture = spr->textureID;
}
glBegin(GL_QUADS);
gxTexcoord2i(u1, v1); gxVertex3i(x1, y1, g_depth);
gxTexcoord2i(u1, v2); gxVertex2i(x1, y2);
gxTexcoord2i(u2, v2); gxVertex2i(x2, y2);
gxTexcoord2i(u2, v1); gxVertex2i(x2, y1);
glEnd();
g_depth++;
}
/* SDL NDS renderer implementation */
extern SDL_RenderDriver NDS_RenderDriver;
typedef struct
{
/* Whether current 3D engine is on the main or sub screen. */
int is_sub;
} NDS_RenderData;
typedef struct
{
glImage image[1];
} NDS_TextureData;
static int NDS_UpdateViewport(SDL_Renderer *renderer)
{
/* Nothing to do. */
return 0;
}
static int
NDS_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_Rect * dstrect)
{
NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
int dest_y;
if (data->is_sub) {
dest_y = dstrect->y;
} else {
dest_y = dstrect->y-SCREEN_HEIGHT;
}
if (srcrect->w == dstrect->w && srcrect->h == dstrect->h) {
/* No scaling */
glSpritePartial(srcrect, dstrect->x, dest_y, GL_FLIP_NONE, txdat->image);
} else {
/* Convert the scaling proportion into a 20.12 value. */
s32 scale_w = divf32(dstrect->w << 12, texture->w << 12);
s32 scale_h = divf32(dstrect->h << 12, texture->h << 12);
glSpriteScaleXY(dstrect->x, dest_y, scale_w, scale_h, GL_FLIP_NONE, txdat->image);
}
return 0;
}
static int NDS_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
{
NDS_TextureData *txdat = NULL;
int i;
SDL_Log("NDS_CreateTexture: NDS_CreateTexture.\n");
/* Sanity checks. */
for (i=0; i<NDS_RenderDriver.info.num_texture_formats; i++) {
if (texture->format == NDS_RenderDriver.info.texture_formats[i])
break;
}
if (i == NDS_RenderDriver.info.num_texture_formats) {
SDL_SetError("Unsupported texture format (%x)", texture->format);
return -1;
}
if (texture->w > NDS_RenderDriver.info.max_texture_width) {
SDL_SetError("Texture too large (%d)", texture->w);
return -1;
}
if (texture->h > NDS_RenderDriver.info.max_texture_height) {
SDL_SetError("Texture too tall (%d)", texture->h);
return -1;
}
texture->driverdata = SDL_calloc(1, sizeof(NDS_TextureData));
txdat = (NDS_TextureData *) texture->driverdata;
if (!txdat) {
SDL_OutOfMemory();
return -1;
}
return 0;
}
static void
NDS_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
NDS_TextureData *txdat = texture->driverdata;
/* free anything else allocated for texture */
SDL_free(txdat);
}
/* size is no more than 512. */
static int get_gltexture_size(unsigned int size)
{
if (size > 256)
return TEXTURE_SIZE_512;
else if (size > 128)
return TEXTURE_SIZE_256;
else if (size > 64)
return TEXTURE_SIZE_128;
else if (size > 32)
return TEXTURE_SIZE_64;
else if (size > 16)
return TEXTURE_SIZE_32;
else if (size > 8)
return TEXTURE_SIZE_16;
else
return TEXTURE_SIZE_8;
}
static int NDS_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels, int pitch)
{
NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
char *new_pixels = NULL;
const int gl_w = get_gltexture_size(rect->w);
const int gl_h = get_gltexture_size(rect->h);
const int w = 1 << (3+gl_w); /* Texture sizes must be a power of 2. */
const int h = 1 << (3+gl_h); /* Texture sizes must be a power of 2. */
if (w != rect->w || h != rect->h) {
/* Allocate a temporary surface and copy pixels into it while
* enlarging the pitch. */
const char *src;
char *dst;
int new_pitch = 2 * w;
int i;
new_pixels = malloc(2 * w * h);
if (!new_pixels)
return SDL_ENOMEM;
src = pixels;
dst = new_pixels;
for (i=0; i<rect->h; i++) {
memcpy(dst, src, pitch);
src += pitch;
dst += new_pitch;
}
}
glLoadTile(txdat->image,
gl_w, gl_h,
rect->w, rect->h,
texture->format == SDL_PIXELFORMAT_ABGR1555 ? GL_RGBA : GL_RGB,
TEXGEN_OFF, 0, NULL,
new_pixels? new_pixels : pixels);
if (new_pixels)
free(new_pixels);
return 0;
}
static int NDS_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
const SDL_Rect *rect, void **pixels, int *pitch)
{
SDL_Log("enter %s (todo)\n", __func__);
return 0;
}
static void NDS_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
{
SDL_Log("enter %s\n", __func__);
/* stub! */
}
static int NDS_RenderClear(SDL_Renderer *renderer)
{
glClearColor(renderer->r >> 3,
renderer->g >> 3,
renderer->b >> 3,
renderer->a >> 3);
return 0;
}
static void NDS_RenderPresent(SDL_Renderer * renderer)
{
NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
glEnd2D();
glFlush(0);
swiWaitForVBlank();
/* wait for capture unit to be ready */
while(REG_DISPCAPCNT & DCAP_ENABLE);
/* 3D engine can only work on one screen at a time. */
data->is_sub = !data->is_sub;
if (data->is_sub) {
lcdMainOnBottom();
vramSetBankC(VRAM_C_LCD);
vramSetBankD(VRAM_D_SUB_SPRITE);
REG_DISPCAPCNT = DCAP_BANK(2) | DCAP_ENABLE | DCAP_SIZE(3);
} else {
lcdMainOnTop();
vramSetBankD(VRAM_D_LCD);
vramSetBankC(VRAM_C_SUB_BG);
REG_DISPCAPCNT = DCAP_BANK(3) | DCAP_ENABLE | DCAP_SIZE(3);
}
glBegin2D();
}
static int NDS_RenderDrawPoints(SDL_Renderer *renderer, const SDL_Point *points,
int count)
{
NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
int i;
int color = RGB15(renderer->r >> 3,
renderer->g >> 3,
renderer->b >> 3);
for (i=0; i < count; i++) {
if (data->is_sub) {
glPutPixel(points[i].x, points[i].y, color);
} else {
glPutPixel(points[i].x, points[i].y - SCREEN_HEIGHT, color);
}
}
return 0;
}
static int NDS_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points,
int count)
{
NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
int i;
int color = RGB15(renderer->r >> 3,
renderer->g >> 3,
renderer->b >> 3);
for (i=0; i < count-1; i++) {
if (data->is_sub) {
glLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color);
} else {
glLine(points[i].x, points[i].y - SCREEN_HEIGHT,
points[i+1].x, points[i+1].y - SCREEN_HEIGHT, color);
}
}
return 0;
}
static int NDS_RenderFillRects(SDL_Renderer *renderer, const SDL_Rect *rects,
int count)
{
NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
int i;
int color = RGB15(renderer->r >> 3,
renderer->g >> 3,
renderer->b >> 3);
for (i=0; i<count; i++) {
if (data->is_sub) {
glBoxFilled(rects[i].x, rects[i].y,
rects[i].x + rects[i].w,
rects[i].y + rects[i].h, color);
} else {
glBoxFilled(rects[i].x, rects[i].y - SCREEN_HEIGHT,
rects[i].x + rects[i].w,
rects[i].y + rects[i].h - SCREEN_HEIGHT,
color);
}
}
return 0;
}
static SDL_Renderer *
NDS_CreateRenderer(SDL_Window * window, Uint32 flags)
{
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayMode *displayMode = &display->current_mode;
SDL_Renderer *renderer;
NDS_RenderData *data;
int bpp;
Uint32 Rmask, Gmask, Bmask, Amask;
if (displayMode->format != SDL_PIXELFORMAT_ABGR1555) {
SDL_SetError("Unsupported pixel format (%x)", displayMode->format);
return NULL;
}
if (!SDL_PixelFormatEnumToMasks(displayMode->format, &bpp,
&Rmask, &Gmask, &Bmask, &Amask)) {
SDL_SetError("Unknown display format");
return NULL;
}
renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
if (!renderer) {
SDL_OutOfMemory();
return NULL;
}
data = (NDS_RenderData *) SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_free(renderer);
SDL_OutOfMemory();
return NULL;
}
renderer->info = NDS_RenderDriver.info;
renderer->info.flags = SDL_RENDERER_ACCELERATED;
renderer->CreateTexture = NDS_CreateTexture;
renderer->UpdateTexture = NDS_UpdateTexture;
renderer->LockTexture = NDS_LockTexture;
renderer->UnlockTexture = NDS_UnlockTexture;
renderer->UpdateViewport = NDS_UpdateViewport;
renderer->RenderClear = NDS_RenderClear;
renderer->RenderDrawPoints = NDS_RenderDrawPoints;
renderer->RenderDrawLines = NDS_RenderDrawLines;
renderer->RenderFillRects = NDS_RenderFillRects;
renderer->RenderCopy = NDS_RenderCopy;
/* renderer->RenderReadPixels = NDS_RenderReadPixels; - todo ? */
renderer->RenderPresent = NDS_RenderPresent;
renderer->DestroyTexture = NDS_DestroyTexture;
/* renderer->DestroyRenderer = NDS_DestroyRenderer; - todo ? */
renderer->driverdata = data;
return renderer;
}
SDL_RenderDriver NDS_RenderDriver = {
.CreateRenderer = NDS_CreateRenderer,
.info = {
.name = "nds",
.flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC,
.num_texture_formats = 2,
.texture_formats = { [0] = SDL_PIXELFORMAT_ABGR1555,
[1] = SDL_PIXELFORMAT_BGR555,
},
.max_texture_width = 512,
.max_texture_height = 512,
}
};
#endif /* SDL_VIDEO_RENDER_NDS */
/* vi: set ts=4 sw=4 expandtab: */