/* * gLib2D - A simple, fast, light-weight 2D graphics library. * * Copyright 2012 Clément Guérin * * 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 . */ #include "glib2d.h" #include #include #include #include #include #include #include /* 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(0, 0); 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(0, 0); 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 == NULL) 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 == NULL) { free(tex); return NULL; } memset(tex->data, 0, tex->tw * tex->th * sizeof(g2dColor)); 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 = NULL; g2dColor *line = NULL; u32 row = 0, col = 0; line = data; tex = g2dTexCreate(width, height); for (row = 0; row < tex->w; row++) { for (col = 0; col < tex->h; col++) tex->data[row + col * tex->tw] = line[(row + col * tex->w)]; } return tex; } g2dTexture *g2dTexLoad(void *data, int width, int height, g2dTex_Mode mode) { g2dTexture *tex = NULL; if (data == NULL) return NULL; tex = _g2dTexLoadData(data, width, height); if (tex == NULL) goto error; // The PSP can't draw 512*512+ textures. if (tex->w > 512 || tex->h > 512) goto error; // Swizzling is useless with small textures. if ((mode & G2D_SWIZZLE) && (tex->w >= 16 || tex->h >= 16)) { u8 *tmp = malloc(tex->tw*tex->th*PIXEL_SIZE); _swizzle(tmp, (u8*)tex->data, tex->tw*PIXEL_SIZE, tex->th); free(tex->data); tex->data = (g2dColor*)tmp; tex->swizzled = true; } else tex->swizzled = false; sceKernelDcacheWritebackRange(tex->data, tex->tw * tex->th * PIXEL_SIZE); return tex; // Load failure... abort 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