Skip to content

Instantly share code, notes, and snippets.

@ste2425
Last active October 23, 2025 08:20
Show Gist options
  • Select an option

  • Save ste2425/ed48f1c0bb07cb39762fdde4a84ce556 to your computer and use it in GitHub Desktop.

Select an option

Save ste2425/ed48f1c0bb07cb39762fdde4a84ce556 to your computer and use it in GitHub Desktop.
glib2d prx issue repro
# Define the exports for the prx
PSP_BEGIN_EXPORTS
# These four lines are mandatory (although you can add other functions like module_stop)
# syslib is a psynonym for the single mandatory export.
PSP_EXPORT_START(syslib, 0, 0x8000)
PSP_EXPORT_FUNC(module_start)
PSP_EXPORT_FUNC(module_stop)
PSP_EXPORT_VAR(module_info)
PSP_EXPORT_END
PSP_END_EXPORTS
/*
* gLib2D - A simple, fast, light-weight 2D graphics library.
*
* Copyright 2012 Clément Guérin <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "glib2d.h"
#include <pspdisplay.h>
#include <pspkernel.h>
#include <pspgu.h>
#include <vram.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
/* Defines */
#define DLIST_SIZE (524288)
#define LINE_SIZE (512)
#define PIXEL_SIZE (4)
#define FRAMEBUFFER_SIZE (LINE_SIZE * G2D_SCR_H * PIXEL_SIZE)
#define MALLOC_STEP (128)
#define TSTACK_MAX (64)
#define SLICE_WIDTH (64.f)
#define M_180_PI (57.29578f)
#define M_PI_180 (0.017453292f)
#define DEFAULT_SIZE (10)
#define DEFAULT_COORD_MODE (G2D_UP_LEFT)
#define DEFAULT_X (0.f)
#define DEFAULT_Y (0.f)
#define DEFAULT_Z (0.f)
#define DEFAULT_COLOR (WHITE)
#define DEFAULT_ALPHA (0xFF)
#define OBJ rctx.obj[rctx.n - 1]
#define OBJ_I rctx.obj[i]
#define TRANSFORM tstack[tstack_size - 1]
/* Enumerations */
typedef enum {
RECTS, LINES, QUADS, POINTS
} Obj_Type;
/* Structures */
typedef struct {
float x, y, z;
float rot, rot_sin, rot_cos;
float scale_w, scale_h;
} Transform;
typedef struct {
float x, y, z;
float rot_x, rot_y; // Rotation center
float rot, rot_sin, rot_cos;
int crop_x, crop_y;
int crop_w, crop_h;
float scale_w, scale_h;
g2dColor color;
g2dAlpha alpha;
} Object;
typedef struct {
Object *obj;
Object cur_obj;
unsigned int n;
Obj_Type type;
g2dTexture *tex;
bool use_strip;
bool use_z;
bool use_vert_color;
bool use_rot;
bool use_tex_linear;
bool use_tex_repeat;
bool use_int;
unsigned int color_count;
g2dCoord_Mode coord_mode;
} RenderContext;
/* Local variables */
static int *dlist;
static RenderContext rctx;
static Transform tstack[TSTACK_MAX];
static unsigned int tstack_size;
static bool init = false;
static bool start = false;
static bool begin = false;
static bool zclear = true;
static bool scissor = false;
static float global_scale;
/* Global variables */
g2dTexture g2d_draw_buffer = {
512, 512,
G2D_SCR_W, G2D_SCR_H,
(float)G2D_SCR_W/G2D_SCR_H,
false,
(g2dColor *)FRAMEBUFFER_SIZE
};
g2dTexture g2d_disp_buffer = {
512, 512,
G2D_SCR_W, G2D_SCR_H,
(float)G2D_SCR_W/G2D_SCR_H,
false,
(g2dColor *)0
};
/* Internal functions */
static void _g2dStart(void) {
if (!init)
g2dInit();
sceKernelDcacheWritebackRange(dlist, DLIST_SIZE);
sceGuStart(GU_DIRECT, dlist);
start = true;
}
static void *_g2dSetVertex(void *vp, int i, float vx, float vy) {
// Vertex order: [texture uv] [color] [coord]
short *vp_short;
g2dColor *vp_color;
float *vp_float;
// Texture coordinates
vp_short = (short *)vp;
if (rctx.tex != NULL) {
*(vp_short++) = OBJ_I.crop_x + vx * OBJ_I.crop_w;
*(vp_short++) = OBJ_I.crop_y + vy * OBJ_I.crop_h;
}
// Color
vp_color = (g2dColor*)vp_short;
if (rctx.use_vert_color)
*(vp_color++) = OBJ_I.color;
// Coordinates
vp_float = (float *)vp_color;
vp_float[0] = OBJ_I.x;
vp_float[1] = OBJ_I.y;
if (rctx.type == RECTS) {
vp_float[0] += vx * OBJ_I.scale_w;
vp_float[1] += vy * OBJ_I.scale_h;
if (rctx.use_rot) {// Apply a rotation
float tx = vp_float[0] - OBJ_I.rot_x;
float ty = vp_float[1] - OBJ_I.rot_y;
vp_float[0] = OBJ_I.rot_x - OBJ_I.rot_sin*ty + OBJ_I.rot_cos*tx,
vp_float[1] = OBJ_I.rot_y + OBJ_I.rot_cos*ty + OBJ_I.rot_sin*tx;
}
}
if (rctx.use_int) {// Pixel perfect
vp_float[0] = floorf(vp_float[0]);
vp_float[1] = floorf(vp_float[1]);
}
vp_float[2] = OBJ_I.z;
return (void*)(vp_float + 3);
}
void vfpu_sincosf(float x, float *s, float *c) {
__asm__ volatile (
"mtv %2, s000\n" // s000 = x
"vcst.s s001, VFPU_2_PI\n" // s001 = 2/pi
"vmul.s s000, s000, s001\n" // s000 = s000*s001
"vrot.p c010, s000, [s, c]\n" // s010 = sinf(s000), s011 = cosf(s000)
"mfv %0, s010\n" // *s = s010
"mfv %1, S011\n" // *c = s011
: "=r"(*s), "=r"(*c) : "r"(x)
);
}
/* Main functions */
void g2dInit(void) {
if (init)
return;
// Display list allocation
dlist = malloc(DLIST_SIZE);
// Setup GU
sceGuInit();
sceGuStart(GU_DIRECT, dlist);
sceGuDrawBuffer(GU_PSM_8888, g2d_draw_buffer.data, LINE_SIZE);
sceGuDispBuffer(G2D_SCR_W, G2D_SCR_H, g2d_disp_buffer.data, LINE_SIZE);
sceGuDepthBuffer((void *)(FRAMEBUFFER_SIZE * 2), LINE_SIZE);
sceGuOffset(2048 - G2D_SCR_W / 2, 2048 - G2D_SCR_H / 2);
sceGuViewport(2048, 2048, G2D_SCR_W, G2D_SCR_H);
g2d_draw_buffer.data = vabsptr(g2d_draw_buffer.data);
g2d_disp_buffer.data = vabsptr(g2d_disp_buffer.data);
sceGuDepthRange(65535, 0);
sceGuClearDepth(65535);
sceGuAlphaFunc(GU_GREATER, 0, 255);
sceGuDepthFunc(GU_LEQUAL);
sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
sceGuShadeModel(GU_SMOOTH);
sceGuDisable(GU_CULL_FACE);
sceGuDisable(GU_CLIP_PLANES);
sceGuDisable(GU_DITHER);
sceGuEnable(GU_ALPHA_TEST);
sceGuEnable(GU_SCISSOR_TEST);
sceGuEnable(GU_BLEND);
g2dResetGlobalScale();
g2dResetScissor();
sceGuFinish();
sceGuSync(GU_SYNC_FINISH, GU_SYNC_WHAT_DONE);
sceDisplayWaitVblankStart();
sceGuDisplay(GU_TRUE);
init = true;
}
void g2dTerm(void) {
if (!init)
return;
sceGuTerm();
free(dlist);
init = false;
}
void g2dClear(g2dColor color) {
if (!start)
_g2dStart();
sceGuClearColor(color);
sceGuClear(GU_COLOR_BUFFER_BIT | GU_FAST_CLEAR_BIT | (zclear ? GU_DEPTH_BUFFER_BIT : 0));
zclear = false;
}
void g2dClearZ(void) {
if (!start)
_g2dStart();
sceGuClear(GU_DEPTH_BUFFER_BIT | GU_FAST_CLEAR_BIT);
zclear = true;
}
static void _g2dBeginCommon(Obj_Type type, g2dTexture *tex) {
if (begin)
return;
if (!start)
_g2dStart();
// Reset render context
rctx.obj = realloc(rctx.obj, MALLOC_STEP * sizeof(Object));
rctx.n = 0;
rctx.type = type;
rctx.tex = tex;
rctx.use_strip = false;
rctx.use_z = false;
rctx.use_vert_color = false;
rctx.use_rot = false;
rctx.use_tex_linear = true;
rctx.use_tex_repeat = false;
rctx.use_int = false;
rctx.color_count = 0;
rctx.coord_mode = DEFAULT_COORD_MODE;
// Reset current object
g2dReset();
begin = true;
}
void g2dBeginRects(g2dTexture *tex) {
_g2dBeginCommon(RECTS, tex);
}
void g2dBeginLines(g2dLine_Mode mode) {
_g2dBeginCommon(LINES, NULL);
rctx.use_strip = (mode & G2D_STRIP);
}
void g2dBeginQuads(g2dTexture *tex) {
_g2dBeginCommon(QUADS, tex);
}
void g2dBeginPoints(void) {
_g2dBeginCommon(POINTS, NULL);
}
static void _g2dEndRects(void) {
// Define vertices properties
int v_prim = (rctx.use_rot ? GU_TRIANGLES : GU_SPRITES);
int v_obj_nbr = (rctx.use_rot ? 6 : 2);
int v_nbr;
int v_coord_size = 3;
int v_tex_size = (rctx.tex != NULL ? 2 : 0);
int v_color_size = (rctx.use_vert_color ? 1 : 0);
int v_size = v_tex_size * sizeof(short) + v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
int i;
if (rctx.tex != NULL)
v_type |= GU_TEXTURE_16BIT;
if (rctx.use_vert_color)
v_type |= GU_COLOR_8888;
// Count how many vertices to allocate.
if (rctx.tex == NULL || rctx.use_rot) // No slicing
v_nbr = v_obj_nbr * rctx.n;
else { // Can use texture slicing for tremendous performance :)
v_nbr = 0;
for (i = 0; i < rctx.n; i++)
v_nbr += v_obj_nbr * ceilf(OBJ_I.crop_w/SLICE_WIDTH);
}
// Allocate vertex list memory
void *v = sceGuGetMemory(v_nbr * v_size);
void *vi = v;
// Build the vertex list
for (i = 0; i < rctx.n; i += 1) {
if (rctx.use_rot) { // Two triangles per object
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
vi = _g2dSetVertex(vi, i, 1.f, 0.f);
vi = _g2dSetVertex(vi, i, 0.f, 1.f);
vi = _g2dSetVertex(vi, i, 0.f, 1.f);
vi = _g2dSetVertex(vi, i, 1.f, 0.f);
vi = _g2dSetVertex(vi, i, 1.f, 1.f);
}
else if (rctx.tex == NULL) { // One sprite per object
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
vi = _g2dSetVertex(vi, i, 1.f, 1.f);
}
else { // Several sprites per object for a better texture cache use
float step = SLICE_WIDTH/OBJ_I.crop_w;
float u;
for (u = 0.f; u < 1.f; u += step) {
vi = _g2dSetVertex(vi, i, u, 0.f);
vi = _g2dSetVertex(vi, i, (u + step > 1.f ? 1.f : u+step), 1.f);
}
}
}
// Then put it in the display list.
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
}
static void _g2dEndLines(void) {
// Define vertices properties
int v_prim = (rctx.use_strip ? GU_LINE_STRIP : GU_LINES);
int v_obj_nbr = (rctx.use_strip ? 1 : 2);
int v_nbr = v_obj_nbr * (rctx.use_strip ? rctx.n : rctx.n/2);
int v_coord_size = 3;
int v_color_size = (rctx.use_vert_color ? 1 : 0);
int v_size = v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
int i;
if (rctx.use_vert_color)
v_type |= GU_COLOR_8888;
// Allocate vertex list memory
void *v = sceGuGetMemory(v_nbr * v_size);
void *vi = v;
// Build the vertex list
if (rctx.use_strip) {
vi = _g2dSetVertex(vi, 0, 0.f, 0.f);
for (i = 1; i < rctx.n; i += 1)
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
}
else
{
for (i = 0; i + 1 < rctx.n; i += 2) {
vi = _g2dSetVertex(vi, i , 0.f, 0.f);
vi = _g2dSetVertex(vi, i + 1, 0.f, 0.f);
}
}
// Then put it in the display list.
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
}
static void _g2dEndQuads(void) {
// Define vertices properties
int v_prim = GU_TRIANGLES;
int v_obj_nbr = 6;
int v_nbr = v_obj_nbr * (rctx.n / 4);
int v_coord_size = 3;
int v_tex_size = (rctx.tex != NULL ? 2 : 0);
int v_color_size = (rctx.use_vert_color ? 1 : 0);
int v_size = v_tex_size * sizeof(short) + v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
int i;
if (rctx.tex != NULL)
v_type |= GU_TEXTURE_16BIT;
if (rctx.use_vert_color)
v_type |= GU_COLOR_8888;
// Allocate vertex list memory
void *v = sceGuGetMemory(v_nbr * v_size);
void *vi = v;
// Build the vertex list
for (i = 0; i + 3 < rctx.n; i += 4) {
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
vi = _g2dSetVertex(vi, i + 1, 1.f, 0.f);
vi = _g2dSetVertex(vi, i + 3, 0.f, 1.f);
vi = _g2dSetVertex(vi, i + 3, 0.f, 1.f);
vi = _g2dSetVertex(vi, i + 1, 1.f, 0.f);
vi = _g2dSetVertex(vi, i + 2, 1.f, 1.f);
}
// Then put it in the display list.
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
}
static void _g2dEndPoints(void) {
// Define vertices properties
int v_prim = GU_POINTS;
int v_obj_nbr = 1;
int v_nbr = v_obj_nbr * rctx.n;
int v_coord_size = 3;
int v_color_size = (rctx.use_vert_color ? 1 : 0);
int v_size = v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
int i;
if (rctx.use_vert_color)
v_type |= GU_COLOR_8888;
// Allocate vertex list memory
void *v = sceGuGetMemory(v_nbr * v_size);
void *vi = v;
// Build the vertex list
for (i = 0; i < rctx.n; i += 1)
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
// Then put it in the display list.
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
}
void g2dEnd(void) {
if (!begin || rctx.n == 0) {
begin = false;
return;
}
// Manage pspgu extensions
if (rctx.use_z)
sceGuEnable(GU_DEPTH_TEST);
else
sceGuDisable(GU_DEPTH_TEST);
if (rctx.use_vert_color)
sceGuColor(WHITE);
else
sceGuColor(rctx.cur_obj.color);
if (rctx.tex == NULL)
sceGuDisable(GU_TEXTURE_2D);
else {
sceGuEnable(GU_TEXTURE_2D);
if (rctx.use_tex_linear)
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
else
sceGuTexFilter(GU_NEAREST, GU_NEAREST);
if (rctx.use_tex_repeat)
sceGuTexWrap(GU_REPEAT, GU_REPEAT);
else
sceGuTexWrap(GU_CLAMP, GU_CLAMP);
// Load texture
sceGuTexMode(GU_PSM_8888, 0, 0, rctx.tex->swizzled);
sceGuTexImage(0, rctx.tex->tw, rctx.tex->th, rctx.tex->tw, rctx.tex->data);
}
switch (rctx.type) {
case RECTS:
_g2dEndRects();
break;
case LINES:
_g2dEndLines();
break;
case QUADS:
_g2dEndQuads();
break;
case POINTS:
_g2dEndPoints();
break;
}
sceGuColor(WHITE);
if (rctx.use_z)
zclear = true;
begin = false;
}
void g2dReset(void) {
g2dResetCoord();
g2dResetScale();
g2dResetColor();
g2dResetAlpha();
g2dResetRotation();
g2dResetCrop();
g2dResetTex();
}
void g2dFlip(g2dFlip_Mode mode) {
if (scissor)
g2dResetScissor();
sceGuFinish();
sceGuSync(GU_SYNC_FINISH, GU_SYNC_WHAT_DONE);
if (mode & G2D_VSYNC)
sceDisplayWaitVblankStart();
g2d_disp_buffer.data = g2d_draw_buffer.data;
g2d_draw_buffer.data = vabsptr(sceGuSwapBuffers());
start = false;
}
void g2dAdd(void) {
if (!begin || rctx.cur_obj.scale_w == 0.f || rctx.cur_obj.scale_h == 0.f)
return;
if (rctx.n % MALLOC_STEP == 0)
rctx.obj = realloc(rctx.obj, (rctx.n+MALLOC_STEP) * sizeof(Object));
rctx.n++;
OBJ = rctx.cur_obj;
// Coordinate mode stuff
OBJ.rot_x = OBJ.x;
OBJ.rot_y = OBJ.y;
switch (rctx.coord_mode) {
case G2D_UP_RIGHT:
OBJ.x -= OBJ.scale_w;
break;
case G2D_DOWN_RIGHT:
OBJ.x -= OBJ.scale_w;
OBJ.y -= OBJ.scale_h;
break;
case G2D_DOWN_LEFT:
OBJ.y -= OBJ.scale_h;
break;
case G2D_CENTER:
OBJ.x -= OBJ.scale_w / 2.f;
OBJ.y -= OBJ.scale_h / 2.f;
break;
case G2D_UP_LEFT:
default:
break;
};
// Alpha stuff
OBJ.color = G2D_MODULATE(OBJ.color, 255, rctx.cur_obj.alpha);
}
void g2dPush(void) {
if (tstack_size >= TSTACK_MAX)
return;
tstack_size++;
TRANSFORM.x = rctx.cur_obj.x;
TRANSFORM.y = rctx.cur_obj.y;
TRANSFORM.z = rctx.cur_obj.z;
TRANSFORM.rot = rctx.cur_obj.rot;
TRANSFORM.rot_sin = rctx.cur_obj.rot_sin;
TRANSFORM.rot_cos = rctx.cur_obj.rot_cos;
TRANSFORM.scale_w = rctx.cur_obj.scale_w;
TRANSFORM.scale_h = rctx.cur_obj.scale_h;
}
void g2dPop(void) {
if (tstack_size <= 0)
return;
rctx.cur_obj.x = TRANSFORM.x;
rctx.cur_obj.y = TRANSFORM.y;
rctx.cur_obj.z = TRANSFORM.z;
rctx.cur_obj.rot = TRANSFORM.rot;
rctx.cur_obj.rot_sin = TRANSFORM.rot_sin;
rctx.cur_obj.rot_cos = TRANSFORM.rot_cos;
rctx.cur_obj.scale_w = TRANSFORM.scale_w;
rctx.cur_obj.scale_h = TRANSFORM.scale_h;
tstack_size--;
if (rctx.cur_obj.rot != 0.f)
rctx.use_rot = true;
if (rctx.cur_obj.z != 0.f)
rctx.use_z = true;
}
/* Coord functions */
void g2dResetCoord(void) {
rctx.cur_obj.x = DEFAULT_X;
rctx.cur_obj.y = DEFAULT_Y;
rctx.cur_obj.z = DEFAULT_Z;
}
void g2dSetCoordMode(g2dCoord_Mode mode) {
if (mode > G2D_CENTER)
return;
rctx.coord_mode = mode;
}
void g2dGetCoordXYZ(float *x, float *y, float *z) {
if (x != NULL) *x = rctx.cur_obj.x;
if (y != NULL) *y = rctx.cur_obj.y;
if (z != NULL) *z = rctx.cur_obj.z;
}
void g2dSetCoordXY(float x, float y) {
rctx.cur_obj.x = x * global_scale;
rctx.cur_obj.y = y * global_scale;
rctx.cur_obj.z = 0.f;
}
void g2dSetCoordXYZ(float x, float y, float z) {
rctx.cur_obj.x = x * global_scale;
rctx.cur_obj.y = y * global_scale;
rctx.cur_obj.z = z * global_scale;
if (z != 0.f)
rctx.use_z = true;
}
void g2dSetCoordXYRelative(float x, float y) {
float inc_x = x;
float inc_y = y;
if (rctx.cur_obj.rot_cos != 1.f) {
inc_x = -rctx.cur_obj.rot_sin*y + rctx.cur_obj.rot_cos*x;
inc_y = rctx.cur_obj.rot_cos*y + rctx.cur_obj.rot_sin*x;
}
rctx.cur_obj.x += inc_x * global_scale;
rctx.cur_obj.y += inc_y * global_scale;
}
void g2dSetCoordXYZRelative(float x, float y, float z) {
g2dSetCoordXYRelative(x, y);
rctx.cur_obj.z += z * global_scale;
if (z != 0.f)
rctx.use_z = true;
}
void g2dSetCoordInteger(bool use) {
rctx.use_int = use;
}
/* Scale functions */
void g2dResetGlobalScale(void) {
global_scale = 1.f;
}
void g2dResetScale(void) {
if (rctx.tex == NULL) {
rctx.cur_obj.scale_w = DEFAULT_SIZE;
rctx.cur_obj.scale_h = DEFAULT_SIZE;
}
else {
rctx.cur_obj.scale_w = rctx.tex->w;
rctx.cur_obj.scale_h = rctx.tex->h;
}
rctx.cur_obj.scale_w *= global_scale;
rctx.cur_obj.scale_h *= global_scale;
}
void g2dGetGlobalScale(float *scale) {
if (scale != NULL)
*scale = global_scale;
}
void g2dGetScaleWH(float *w, float *h) {
if (w != NULL)
*w = rctx.cur_obj.scale_w;
if (h != NULL)
*h = rctx.cur_obj.scale_h;
}
void g2dSetGlobalScale(float scale) {
global_scale = scale;
}
void g2dSetScale(float w, float h) {
g2dResetScale();
g2dSetScaleRelative(w, h);
}
void g2dSetScaleWH(float w, float h) {
rctx.cur_obj.scale_w = w * global_scale;
rctx.cur_obj.scale_h = h * global_scale;
// A trick to prevent an unexpected behavior when mirroring with GU_SPRITES.
if (rctx.cur_obj.scale_w < 0 || rctx.cur_obj.scale_h < 0)
rctx.use_rot = true;
}
void g2dSetScaleRelative(float w, float h) {
rctx.cur_obj.scale_w *= w;
rctx.cur_obj.scale_h *= h;
if (rctx.cur_obj.scale_w < 0 || rctx.cur_obj.scale_h < 0)
rctx.use_rot = true;
}
void g2dSetScaleWHRelative(float w, float h) {
rctx.cur_obj.scale_w += w * global_scale;
rctx.cur_obj.scale_h += h * global_scale;
if (rctx.cur_obj.scale_w < 0 || rctx.cur_obj.scale_h < 0)
rctx.use_rot = true;
}
/* Color functions */
void g2dResetColor(void) {
rctx.cur_obj.color = DEFAULT_COLOR;
}
void g2dResetAlpha(void) {
rctx.cur_obj.alpha = DEFAULT_ALPHA;
}
void g2dGetAlpha(g2dAlpha *alpha) {
if (alpha != NULL)
*alpha = rctx.cur_obj.alpha;
}
void g2dSetColor(g2dColor color) {
rctx.cur_obj.color = color;
if (++rctx.color_count > 1)
rctx.use_vert_color = true;
}
void g2dSetAlpha(g2dAlpha alpha) {
if (alpha < 0)
alpha = 0;
if (alpha > 255)
alpha = 255;
rctx.cur_obj.alpha = alpha;
if (++rctx.color_count > 1)
rctx.use_vert_color = true;
}
void g2dSetAlphaRelative(int alpha) {
g2dSetAlpha(rctx.cur_obj.alpha + alpha);
}
/* Rotation functions */
void g2dResetRotation(void) {
rctx.cur_obj.rot = 0.f;
rctx.cur_obj.rot_sin = 0.f;
rctx.cur_obj.rot_cos = 1.f;
}
void g2dGetRotationRad(float *radians) {
if (radians != NULL)
*radians = rctx.cur_obj.rot;
}
void g2dGetRotation(float *degrees) {
if (degrees != NULL)
*degrees = rctx.cur_obj.rot * M_180_PI;
}
void g2dSetRotationRad(float radians) {
if (radians == rctx.cur_obj.rot)
return;
rctx.cur_obj.rot = radians;
vfpu_sincosf(radians, &rctx.cur_obj.rot_sin, &rctx.cur_obj.rot_cos);
if (radians != 0.f)
rctx.use_rot = true;
}
void g2dSetRotation(float degrees) {
g2dSetRotationRad(degrees * M_PI_180);
}
void g2dSetRotationRadRelative(float radians) {
g2dSetRotationRad(rctx.cur_obj.rot + radians);
}
void g2dSetRotationRelative(float degrees) {
g2dSetRotationRadRelative(degrees * M_PI_180);
}
/* Crop functions */
void g2dResetCrop(void) {
if (rctx.tex == NULL)
return;
rctx.cur_obj.crop_x = 0;
rctx.cur_obj.crop_y = 0;
rctx.cur_obj.crop_w = rctx.tex->w;
rctx.cur_obj.crop_h = rctx.tex->h;
}
void g2dGetCropXY(int *x, int *y) {
if (rctx.tex == NULL)
return;
if (x != NULL)
*x = rctx.cur_obj.crop_x;
if (y != NULL)
*y = rctx.cur_obj.crop_y;
}
void g2dGetCropWH(int *w, int *h) {
if (rctx.tex == NULL)
return;
if (w != NULL)
*w = rctx.cur_obj.crop_w;
if (h != NULL)
*h = rctx.cur_obj.crop_h;
}
void g2dSetCropXY(int x, int y) {
if (rctx.tex == NULL)
return;
rctx.cur_obj.crop_x = x;
rctx.cur_obj.crop_y = y;
}
void g2dSetCropWH(int w, int h) {
if (rctx.tex == NULL)
return;
rctx.cur_obj.crop_w = w;
rctx.cur_obj.crop_h = h;
}
void g2dSetCropXYRelative(int x, int y) {
if (rctx.tex == NULL)
return;
g2dSetCropXY(rctx.cur_obj.crop_x + x, rctx.cur_obj.crop_y + y);
}
void g2dSetCropWHRelative(int w, int h) {
if (rctx.tex == NULL)
return;
g2dSetCropWH(rctx.cur_obj.crop_w + w, rctx.cur_obj.crop_h + h);
}
/* Texture functions */
void g2dResetTex(void) {
if (rctx.tex == NULL)
return;
rctx.use_tex_repeat = false;
rctx.use_tex_linear = true;
}
void g2dSetTexRepeat(bool use) {
if (rctx.tex == NULL)
return;
rctx.use_tex_repeat = use;
}
void g2dSetTexLinear(bool use) {
if (rctx.tex == NULL)
return;
rctx.use_tex_linear = use;
}
/* Texture management */
static unsigned int _getNextPower2(unsigned int n) {
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
static void _swizzle(unsigned char *dest, unsigned char *source, int width, int height) {
int i, j;
int rowblocks = (width / 16);
int rowblocks_add = (rowblocks-1) * 128;
unsigned int block_address = 0;
unsigned int *img = (unsigned int*)source;
for (j = 0; j < height; j++) {
unsigned int *block = (unsigned int *)(dest + block_address);
for (i = 0; i < rowblocks; i++) {
*block++ = *img++;
*block++ = *img++;
*block++ = *img++;
*block++ = *img++;
block += 28;
}
if ((j & 0x7) == 0x7)
block_address += rowblocks_add;
block_address += 16;
}
}
g2dTexture *g2dTexCreate(int w, int h) {
g2dTexture *tex = malloc(sizeof(g2dTexture));
if (!tex)
return NULL;
tex->tw = _getNextPower2(w);
tex->th = _getNextPower2(h);
tex->w = w;
tex->h = h;
tex->ratio = (float)w / h;
tex->swizzled = false;
tex->data = malloc(tex->tw * tex->th * sizeof(g2dColor));
if (!tex->data) {
free(tex);
return NULL;
}
return tex;
}
void g2dTexFree(g2dTexture **tex) {
if (tex == NULL)
return;
if (*tex == NULL)
return;
free((*tex)->data);
free((*tex));
*tex = NULL;
}
static g2dTexture *_g2dTexLoadData(void *data, int width, int height) {
g2dTexture *tex = g2dTexCreate(width, height);
if (!tex)
return NULL;
g2dColor *src = data;
for (u32 y = 0; y < tex->h; ++y) {
for (u32 x = 0; x < tex->w; ++x) {
tex->data[y * tex->tw + x] = src[y * tex->w + x];
}
}
return tex;
}
g2dTexture *g2dTexLoad(void *data, int width, int height, g2dTex_Mode mode) {
if (!data)
return NULL;
g2dTexture *tex = _g2dTexLoadData(data, width, height);
if (!tex)
return NULL;
if (tex->w > 512 || tex->h > 512)
goto error;
u32 tex_size = tex->tw * tex->th * PIXEL_SIZE;
if ((mode & G2D_SWIZZLE) && (tex->w >= 16 || tex->h >= 16)) {
u8 *tmp = malloc(tex_size);
if (!tmp)
goto error;
_swizzle(tmp, (u8*)tex->data, tex->tw * PIXEL_SIZE, tex->th);
free(tex->data);
tex->data = (g2dColor*)tmp;
tex->swizzled = true;
}
sceKernelDcacheWritebackRange(tex->data, tex_size);
return tex;
error:
g2dTexFree(&tex);
return NULL;
}
/* Scissor functions */
void g2dResetScissor(void) {
g2dSetScissor(0, 0, G2D_SCR_W, G2D_SCR_H);
scissor = false;
}
void g2dSetScissor(int x, int y, int w, int h) {
sceGuScissor(x, y, x+w, y+h);
scissor = true;
}
// EOF
/** \mainpage gLib2D Documentation
*
* \section intro Introduction
*
* gLib2D by Geecko - A simple, fast, light-weight 2D graphics library. \n\n
* This library has been designed to replace the old graphics.c library
* and to simplify the use of pspgu.\n
* The goals : keep it simple, keep it small, keep it fast.
*
* \section limits Known limitations
*
* - Draw & display buffers can't actually be used as real textures. Just a way
* to get the vram pointer.
* - No support for multiples contexts (e.g. sharing coordinates beetween
* textures using some gBegin calls at a time).
* - Manipulating textures (clear, get pixel info...) is not possible.
* - When some 512*512 rotated, colorized and scaled textures are rendered
* at a time, the framerate *could* go under 60 fps.
*
* \section install Installation
*
* - Simply put glib2d.c and glib2d.h in your source directory. \n
* - Then add glib2d.o and link "-lpng -ljpeg -lz -lpspgu -lm -lpspvram"
* in your Makefile.
* - You're done !
*
* \section copyright License
*
* This work is licensed under the LGPLv3 License. \n
* See the LICENSE file for more details. \n
* You can support the library by marking your homebrew with
* "Using gLib2D by Geecko".
*
* \section contact Contact
*
* Please report bugs or submit ideas at : \n [email protected] \n\n
* Get the full documentation on : \n http://geecko.dev.free.fr \n\n
* Also stay tuned on... \n
* https://github.com/GeeckoDev (contributors would be a plus!) \n
* http://twitter.com/GeeckoDev
*/
/**
* \file glib2d.h
* \brief gLib2D Header
* \version Beta 5
*/
#ifndef GLIB2D_H
#define GLIB2D_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
/**
* \def G2D_SCR_W
* \brief Screen width constant, in pixels.
*/
/**
* \def G2D_SCR_H
* \brief Screen height constant, in pixels.
*/
#define G2D_SCR_W (480)
#define G2D_SCR_H (272)
/**
* \def G2D_RGBA(r,g,b,a)
* \brief Create a g2dColor.
*
* This macro creates a g2dColor from 4 values, red, green, blue and alpha.
* Input range is from 0 to 255.
*/
#define G2D_RGBA(r, g, b, a) ((r)|((g)<<8)|((b)<<16)|((a)<<24))
/**
* \def G2D_GET_R(color)
* \brief Get red channel value from a g2dColor.
*/
/**
* \def G2D_GET_G(color)
* \brief Get green channel value from a g2dColor.
*/
/**
* \def G2D_GET_B(color)
* \brief Get blue channel value from a g2dColor.
*/
/**
* \def G2D_GET_A(color)
* \brief Get alpha channel value from a g2dColor.
*/
#define G2D_GET_R(color) (((color) ) & 0xFF)
#define G2D_GET_G(color) (((color) >> 8) & 0xFF)
#define G2D_GET_B(color) (((color) >> 16) & 0xFF)
#define G2D_GET_A(color) (((color) >> 24) & 0xFF)
/**
* \def G2D_MODULATE(color,luminance,alpha)
* \brief g2dColor modulation.
*
* This macro modulates the luminance & alpha of a g2dColor.
* Input range is from 0 to 255.
*/
#define G2D_MODULATE(color,luminance,alpha) \
G2D_RGBA((int)(luminance) * G2D_GET_R(color) / 255, \
(int)(luminance) * G2D_GET_G(color) / 255, \
(int)(luminance) * G2D_GET_B(color) / 255, \
(int)(alpha ) * G2D_GET_A(color) / 255)
/**
* \enum g2dColors
* \brief Colors enumeration.
*
* Primary, secondary, tertiary and grayscale colors are defined.
*/
enum g2dColors {
// Primary colors
RED = 0xFF0000FF,
GREEN = 0xFF00FF00,
BLUE = 0xFFFF0000,
// Secondary colors
CYAN = 0xFFFFFF00,
MAGENTA = 0xFFFF00FF,
YELLOW = 0xFF00FFFF,
// Tertiary colors
AZURE = 0xFFFF7F00,
VIOLET = 0xFFFF007F,
ROSE = 0xFF7F00FF,
ORANGE = 0xFF007FFF,
CHARTREUSE = 0xFF00FF7F,
SPRING_GREEN = 0xFF7FFF00,
// Grayscale
WHITE = 0xFFFFFFFF,
LITEGRAY = 0xFFBFBFBF,
GRAY = 0xFF7F7F7F,
DARKGRAY = 0xFF3F3F3F,
BLACK = 0xFF000000
};
/**
* \enum g2dCoord_Mode
* \brief Coordinates modes enumeration.
*
* Choose where the coordinates correspond in the object.
* Can only be used with g2dSetCoordMode.
*/
/**
* \enum g2dLine_Mode
* \brief Line modes enumeration.
*
* Change line draw properties.
* Can only be used with g2dBeginLines.
*/
/**
* \enum g2dFlip_Mode
* \brief Flip modes enumeration.
*
* Change flip properties.
* Can only be used with g2dFlip.
*/
/**
* \enum g2dTex_Mode
* \brief Texture modes enumeration.
*
* Change texture properties.
* Can only be used with g2dTexLoad.
*/
typedef enum {
G2D_UP_LEFT,
G2D_UP_RIGHT,
G2D_DOWN_RIGHT,
G2D_DOWN_LEFT,
G2D_CENTER
} g2dCoord_Mode;
typedef enum {
G2D_STRIP = 1 /**< Make a line strip. */
} g2dLine_Mode;
typedef enum {
G2D_VSYNC = 1 /**< Limit the FPS to 60 (synchronized with the screen).
Better quality and less power consumption. */
} g2dFlip_Mode;
typedef enum {
G2D_SWIZZLE = 1 /**< Recommended. Use it to speedup rendering. */
} g2dTex_Mode;
/**
* \var g2dAlpha
* \brief Alpha type.
*/
/**
* \var g2dColor
* \brief Color type.
*/
typedef int g2dAlpha;
typedef unsigned int g2dColor;
/**
* \struct g2dTexture
* \brief Texture structure.
*/
typedef struct {
int tw; /**< Real texture width. A power of two. */
int th; /**< Real texture height. A power of two. */
int w; /**< Texture width, as seen when drawing. */
int h; /**< Texture height, as seen when drawing. */
float ratio; /**< Width/height ratio. */
bool swizzled; /**< Is the texture swizzled ? */
g2dColor *data; /**< Pointer to raw data. */
} g2dTexture;
/**
* \var g2d_draw_buffer
* \brief The current draw buffer as a texture.
*/
/**
* \var g2d_disp_buffer
* \brief The current display buffer as a texture.
*/
extern g2dTexture g2d_draw_buffer;
extern g2dTexture g2d_disp_buffer;
/**
* \brief Initializes the library.
*
* This function will create a GU context and setup the display buffers.
* Automatically called by the other functions.
*/
void g2dInit(void);
/**
* \brief Shutdowns the library.
*
* This function will destroy the GU context.
*/
void g2dTerm(void);
/**
* \brief Clears screen & depth buffer.
* @param color Screen clear color
*
* This function clears the screen, and clears the zbuffer if depth coordinate
* is used in the loop. Will automatically init the GU if needed.
*/
void g2dClear(g2dColor color);
/**
* \brief Clears depth buffer.
*
* This function clears the zbuffer to zero (z range 0-65535).
* Will automatically init the GU if needed.
*/
void g2dClearZ(void);
/**
* \brief Begins rectangles rendering.
* @param tex Pointer to a texture, pass NULL to get a colored rectangle.
*
* This function begins object rendering. Resets all properties.
* One g2dAdd() call per object.
* Only one texture can be used, but multiple objects can be rendered at a time.
* g2dBegin*() / g2dEnd() couple can be called multiple times in the loop,
* to render multiple textures.
*/
void g2dBeginRects(g2dTexture *tex);
/**
* \brief Begins lines rendering.
* @param line_mode A g2dLine_Mode constant.
*
* This function begins object rendering. Calls g2dReset().
* Two g2dAdd() calls per object.
* Pass G2D_LINE_STRIP to make a line strip (two calls, then one per object).
*/
void g2dBeginLines(g2dLine_Mode mode);
/**
* \brief Begins quads rendering.
* @param tex Pointer to a texture, pass NULL to get a colored quad.
*
* This function begins object rendering. Resets all properties.
* Four g2dAdd() calls per object, first for the up left corner, then clockwise.
* Only one texture can be used, but multiple objects can be rendered at a time.
* g2dBegin*() / g2dEnd() couple can be called multiple times in the loop,
* to render multiple textures.
*/
void g2dBeginQuads(g2dTexture *tex);
/**
* \brief Begins points rendering.
*
* This function begins object rendering. Resets all properties.
* One g2dAdd() call per object.
*/
void g2dBeginPoints(void);
/**
* \brief Ends object rendering.
*
* This function ends object rendering. Must be called after g2dBegin*() to add
* objects to the display list. Automatically adapts pspgu functionnalities
* to get the best performance possible.
*/
void g2dEnd(void);
/**
* \brief Resets current transformation and attribution.
*
* This function must be called during object rendering.
* Calls g2dResetCoord(), g2dResetRotation(), g2dResetScale(),
* g2dResetColor(), g2dResetAlpha(), g2dResetCrop() and g2dResetTex().
*/
void g2dReset(void);
/**
* \brief Flips the screen.
* @param flip_mode A g2dFlip_Mode constant.
*
* This function must be called at the end of the loop.
* Renders the whole display list to the draw buffer.
* Inverts framebuffers to display the whole thing.
*/
void g2dFlip(g2dFlip_Mode mode);
/**
* \brief Pushes the current transformation & attribution to a new object.
*
* This function must be called during object rendering.
*/
void g2dAdd(void);
/**
* \brief Saves the current transformation to stack.
*
* This function must be called during object rendering.
* The stack is 64 saves high.
* Use it like the OpenGL one.
*/
void g2dPush(void);
/**
* \brief Restore the current transformation from stack.
*
* This function must be called during object rendering.
* The stack is 64 saves high.
* Use it like the OpenGL one.
*/
void g2dPop(void);
/**
* \brief Creates a new blank texture.
* @param w Width of the texture.
* @param h Height of the texture.
*
* This function returns NULL on allocation fail.
*/
g2dTexture* g2dTexCreate(int w, int h);
/**
* \brief Frees a texture & set its pointer to NULL.
* @param tex Pointer to the variable which contains the texture pointer.
*
* This function is used to gain memory when a texture is useless.
* Must pass the pointer to the variable which contains the pointer,
* to set it to NULL (passing NULL to a g2dBegin* function is safe).
*/
void g2dTexFree(g2dTexture **tex);
/**
* \brief Loads an image.
* @param data RGBA texture buffer.
* @param width Texture width.
* @param height Texture height.
* @param tex_mode A g2dTex_Mode constant.
* @returns Pointer to the generated texture.
*
* Swizzling is enabled only for 16*16+ textures (useless on small textures), pass G2D_SWIZZLE to enable it.
* Texture supported up to 512*512 in size only (hardware limitation).
*/
g2dTexture *g2dTexLoad(void *data, int width, int height, g2dTex_Mode mode);
/**
* \brief Resets the current coordinates.
*
* This function must be called during object rendering.
* Sets g2dSetCoordMode() to G2D_UP_LEFT and g2dSetCoordXYZ() to (0,0,0).
*/
void g2dResetCoord(void);
/**
* \brief Set coordinate mode.
* @param coord_mode A gCoord_Mode.
*
* This function must be called during object rendering.
* Defines where the coordinates correspond in the object.
*/
void g2dSetCoordMode(g2dCoord_Mode mode);
/**
* \brief Gets the current position.
* @param x Pointer to save the current x (in pixels).
* @param y Pointer to save the current y (in pixels).
* @param z Pointer to save the current z (in pixels).
*
* This function must be called during object rendering.
* Parameters are pointers to float, not int !
* Pass NULL if not needed.
*/
void g2dGetCoordXYZ(float *x, float *y, float *z);
/**
* \brief Sets the new position.
* @param x New x, in pixels.
* @param y New y, in pixels.
*
* This function must be called during object rendering.
*/
void g2dSetCoordXY(float x, float y);
/**
* \brief Sets the new position, with depth support.
* @param x New x, in pixels.
* @param y New y, in pixels.
* @param z New z, in pixels. (front 0-65535 back)
*
* This function must be called during object rendering.
*/
void g2dSetCoordXYZ(float x, float y, float z);
/**
* \brief Sets the new position, relative to the current.
* @param x New x increment, in pixels.
* @param y New y increment, in pixels.
*
* This function must be called during object rendering.
*/
void g2dSetCoordXYRelative(float x, float y);
/**
* \brief Sets the new position, with depth support, relative to the current.
* @param x New x increment, in pixels.
* @param y New y increment, in pixels.
* @param z New z increment, in pixels.
*
* This function must be called during object rendering.
*/
void g2dSetCoordXYZRelative(float x, float y, float z);
/**
* \brief Use integer coordinates.
* @param use false to desactivate (better look, by default),
true to activate (can be useful when you have glitches).
*
* This function must be called during object rendering.
*/
void g2dSetCoordInteger(bool use);
/**
* \brief Resets the global scale.
*
* This function resets the global scale to 1.f.
* Translations and scales are multiplied by this factor.
*/
void g2dResetGlobalScale(void);
/**
* \brief Resets the current scale.
*
* This function must be called during object rendering.
* Sets the scale to the current texture size or (10,10).
*/
void g2dResetScale(void);
/**
* \brief Gets the global scale.
* @param scale Pointer to save the global scale (factor).
*
* Pass NULL if not needed.
*/
void g2dGetGlobalScale(float *scale);
/**
* \brief Gets the current scale.
* @param w Pointer to save the current width (in pixels).
* @param h Pointer to save the current height (in pixels).
*
* This function must be called during object rendering.
* Parameters are pointers to float, not int !
* Pass NULL if not needed.
*/
void g2dGetScaleWH(float *w, float *h);
/**
* \brief Sets the global scale.
*
* Translations and scales are multiplied by this factor.
*/
void g2dSetGlobalScale(float scale);
/**
* \brief Sets the new scale.
* @param w Width scale factor.
* @param h Height scale factor.
*
* This function must be called during object rendering.
* g2dResetScale() is called, then width & height scale are
* multiplied by these values.
* Negative values can be passed to invert the texture.
*/
void g2dSetScale(float w, float h);
/**
* \brief Sets the new scale, in pixels.
* @param w New width, in pixels.
* @param h New height, in pixels.
*
* This function must be called during object rendering.
* Negative values can be passed to invert the texture.
*/
void g2dSetScaleWH(float w, float h);
/**
* \brief Sets the new scale, relative to the current.
* @param w Width scale factor.
* @param h Height scale factor.
*
* This function must be called during object rendering.
* Current width & height scale are multiplied by these values.
* Negative values can be passed to invert the texture.
*/
void g2dSetScaleRelative(float w, float h);
/**
* \brief Sets the new scale, in pixels, relative to the current.
* @param w New width to increment, in pixels.
* @param h New height to increment, in pixels.
*
* This function must be called during object rendering.
* Negative values can be passed to invert the texture.
*/
void g2dSetScaleWHRelative(float w, float h);
/**
* \brief Resets the current color.
*
* This function must be called during object rendering.
* Sets g2dSetColor() to WHITE.
*/
void g2dResetColor(void);
/**
* \brief Resets the current alpha.
*
* This function must be called during object rendering.
* Sets g2dSetAlpha() to 255.
*/
void g2dResetAlpha(void);
/**
* \brief Gets the current alpha.
* @param alpha Pointer to save the current alpha (0-255).
*
* This function must be called during object rendering.
* Pass NULL if not needed.
*/
void g2dGetAlpha(g2dAlpha *alpha);
/**
* \brief Sets the new color.
* @param color The new color.
*
* This function must be called during object rendering.
* Can be used to colorize any object.
*/
void g2dSetColor(g2dColor color);
/**
* \brief Sets the new alpha.
* @param alpha The new alpha (0-255).
*
* This function must be called during object rendering.
* Can be used to make any object transparent.
*/
void g2dSetAlpha(g2dAlpha alpha);
/**
* \brief Sets the new alpha, relative to the current alpha.
* @param alpha The new alpha increment.
*
* This function must be called during object rendering.
* Can be used to make any object transparent.
*/
void g2dSetAlphaRelative(int alpha);
/**
* \brief Resets the current rotation.
*
* This function must be called during object rendering.
* Sets g2dSetRotation() to 0°.
*/
void g2dResetRotation(void);
/**
* \brief Gets the current rotation, in radians.
* @param radians Pointer to save the current rotation.
*
* This function must be called during object rendering.
* Pass NULL if not needed.
*/
void g2dGetRotationRad(float *radians);
/**
* \brief Gets the current rotation, in degrees.
* @param degrees Pointer to save the current rotation.
*
* This function must be called during object rendering.
* Pass NULL if not needed.
*/
void g2dGetRotation(float *degrees);
/**
* \brief Sets the new rotation, in radians.
* @param radians The new angle.
*
* This function must be called during object rendering.
* The rotation center is the actual coordinates.
*/
void g2dSetRotationRad(float radians);
/**
* \brief Sets the new rotation, in degrees.
* @param degrees The new angle.
*
* This function must be called during object rendering.
* The rotation center is the actual coordinates.
*/
void g2dSetRotation(float degrees);
/**
* \brief Sets the new rotation, relative to the current, in radians.
* @param radians The new angle increment.
*
* This function must be called during object rendering.
* The rotation center is the actual coordinates.
*/
void g2dSetRotationRadRelative(float radians);
/**
* \brief Sets the new rotation, relative to the current, in degrees.
* @param degrees The new angle increment.
*
* This function must be called during object rendering.
* The rotation center is the actual coordinates.
*/
void g2dSetRotationRelative(float degrees);
/**
* \brief Resets the current crop.
*
* This function must be called during object rendering.
* Sets g2dSetCropXY() to (0;0) and g2dSetCropWH() to (tex->w,tex->h).
*/
void g2dResetCrop(void);
/**
* \brief Gets the current crop position.
* @param x Pointer to save the current crop x.
* @param y Pointer to save the current crop y.
*
* This function must be called during object rendering.
* Pass NULL if not needed.
*/
void g2dGetCropXY(int *x, int *y);
/**
* \brief Gets the current crop scale.
* @param w Pointer to save the current crop width.
* @param h Pointer to save the current crop height.
*
* This function must be called during object rendering.
* Pass NULL if not needed.
*/
void g2dGetCropWH(int *w, int *h);
/**
* \brief Sets the new crop position.
* @param x New x, in pixels.
* @param y New y, in pixels.
*
* This function must be called during object rendering. Defines crop position.
* If the rectangle is larger or next to the texture, it will be repeated
* when g2dSetTexRepeat is enabled. Useful for a tileset.
*/
void g2dSetCropXY(int x, int y);
/**
* \brief Sets the new crop size.
* @param w New width, in pixels.
* @param h New height, in pixels.
*
* This function must be called during object rendering. Defines crop size.
* If the rectangle is larger or next to the texture, it will be repeated
* when g2dSetTexRepeat is enabled. Useful for a tileset.
*/
void g2dSetCropWH(int w, int h);
/**
* \brief Sets the new crop position, relative to the current.
* @param x New x increment, in pixels.
* @param y New y increment, in pixels.
*
* This function must be called during object rendering. Defines crop position.
* If the rectangle is larger or next to the texture, texture will be repeated
* when g2dSetTexRepeat is enabled. Useful for a tileset.
*/
void g2dSetCropXYRelative(int x, int y);
/**
* \brief Sets the new crop size, relative to the current.
* @param w New width increment, in pixels.
* @param h New height increment, in pixels.
*
* This function must be called during object rendering. Defines crop size.
* If the rectangle is larger or next to the texture, texture will be repeated
* when g2dSetTexRepeat is enabled. Useful for a tileset.
*/
void g2dSetCropWHRelative(int w, int h);
/**
* \brief Resets texture properties.
*
* This function must be called during object rendering.
*/
void g2dResetTex(void);
/**
* \brief Set texture wrap.
* @param use true to repeat, false to clamp (by default).
*
* This function must be called during object rendering.
*/
void g2dSetTexRepeat(bool use);
/**
* \brief Use the bilinear filter with the texture.
* @param use true to activate (better look, by default).
false to desactivate (better performance).
*
* This function must be called during object rendering.
* Only useful when scaling.
*/
void g2dSetTexLinear(bool use);
/**
* \brief Resets the draw zone to the entire screen.
*
* This function can be called everywhere in the loop.
*/
void g2dResetScissor(void);
/**
* \brief Sets the draw zone.
* @param x New x position.
* @param y New y position.
* @param w New width.
* @param h New height.
*
* This function can be called everywhere in the loop.
* Pixel draw will be skipped outside this rectangle.
*/
void g2dSetScissor(int x, int y, int w, int h);
#ifdef __cplusplus
}
#endif
#endif
#include <pspdisplay.h>
#include <pspsdk.h>
#include <psptypes.h>
#include <pspkerror.h>
#include <pspkerneltypes.h>
#include <pspthreadman.h>
#include <pspiofilemgr.h>
#include <stdbool.h>
#include <inttypes.h>
#include <glib2d.h>
#include <string.h>
#define MODULE_NAME "test"
#define MAJOR_VER 1
#define MINOR_VER 1
#define MODULE_OK 0
#define MODULE_ERROR 1
SceUID g_uiThreadId = -1;
uint8_t g_running = 1;
#define LOG_PATH "ms0:/SEPLUGINS/debug2.log"
//
// PSP SDK
//
// We are building a kernel mode prx plugin
PSP_MODULE_INFO(MODULE_NAME, PSP_MODULE_KERNEL, MAJOR_VER, MINOR_VER);
// We don't allocate any heap memory, so set this to 0.
PSP_HEAP_SIZE_KB(0);
// We don't need a main thread since we only do basic setup during module start and won't stall module loading.
// This will make us be called from the module loader thread directly, instead of a secondary kernel thread.
PSP_NO_CREATE_MAIN_THREAD();
// We don't need any of the newlib features since we're not calling into stdio or stdlib etc
PSP_DISABLE_NEWLIB();
int write(const char *filename, const char *text) {
SceUID fd = sceIoOpen(filename, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_APPEND, 0777);
if (fd < 0) {
return fd; // error opening file
}
int result = sceIoWrite(fd, text, strlen(text));
sceIoClose(fd);
return result; // return number of bytes written or error code
}
void DrawRect(float x, float y, float width, float height, g2dColor colour) {
g2dBeginRects(nullptr); {
g2dSetColor(colour);
g2dSetScaleWH(width, height);
g2dSetCoordXY(x, y);
g2dAdd();
}
g2dEnd();
}
static
int gui_thread(SceSize args, void *argp)
{
while( g_running) {
if (sceDisplayWaitVblankStart() < 0)
break;
DrawRect(30, 30, 200, 200, G2D_RGBA(255, 255, 255, 255));
}
return 0;
}
static
int start_GUI_thread(void)
{
int result;
SceUID thid;
// name, entry, initPriority, stackSize, PspThreadAttributes, SceKernelThreadOptParam
thid = sceKernelCreateThread(MODULE_NAME "GUIThread", gui_thread, 0x11, 0x800, 0, 0);
if (thid >= 0) {
result = sceKernelStartThread(thid, 0, 0);
g_uiThreadId = thid;
}
else {
result = thid;
}
return result;
}
static
int stop_gui_thread(void)
{
int result = 0;
SceUID thid = g_uiThreadId;
g_running = 0;
if(thid >= 0) {
// Wait for the main thread to clean up and exit
result = sceKernelWaitThreadEnd(thid, NULL);
if(result < 0) {
result = sceKernelTerminateDeleteThread(thid);
if(result >= 0) {
g_uiThreadId = -1;
}
}
else {
// Thead stopped cleanly, delete it
result = sceKernelDeleteThread(thid);
if(result >= 0) {
g_uiThreadId = -1;
}
}
}
return result;
}
// Called during module init
int module_start(SceSize args, void *argp)
{
write(LOG_PATH, "I'm in\n"); // never logs
// ive while (true) here too expecting it to lock the PSP if it runs
// yet it doesnt lock the PSP so i assume is not running
start_GUI_thread();
return MODULE_OK;
}
// Called during module deinit
int module_stop(SceSize args, void *argp)
{
stop_gui_thread();
return MODULE_OK;
}
TARGET = testg2d
OBJS = glib2d.o main.o exports.o
BUILD_PRX = 1
PRX_EXPORTS = exports.exp
# Use the kernel's small inbuilt libc
#USE_KERNEL_LIBC = 1
# Use only kernel libraries
#USE_KERNEL_LIBS = 1
CFLAGS = -Os -G0 -Wall -fno-builtin-printf
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
LIBS = -lz -lpspgu -lm -lpspvram -lpsphprm_driver -lpspdebug -lpspdisplay
#LDFLAGS = -nostartfiles
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build_prx.mak
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment