[Solved] PNG blitting problem

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Mr.Modem
Posts: 28
Joined: Wed Sep 21, 2005 4:43 am

[Solved] PNG blitting problem

Post by Mr.Modem »

I am working on a little program where I need to load large PNG files and blit them on the screen. It seemed like a simple task but somehow screwed up anyway. I had some old code from another project that where supposed to do what I wanted but it turned out it didn't work as expected. The code can successfully load and blit small png images but when the width of the image is larger than 512 the image get distorted. I would be very glad if someone could point if what I have done wrong.

Image

Code: Select all

#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <png.h>
#include <zlib.h>

#include <pspctrl.h>
#include <pspgu.h>
#include <psprtc.h>

PSP_MODULE_INFO&#40;"ImageViewer", 0, 1, 1&#41;;
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER&#41;;

static unsigned int __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; list&#91;262144&#93;;

#define BUF_WIDTH &#40;512&#41;
#define SCR_WIDTH &#40;480&#41;
#define SCR_HEIGHT &#40;272&#41;

typedef struct 
&#123;
int Width;
int Height;
int power2height;
int power2width;
unsigned short *ImageData;
&#125;Image;

//static unsigned short __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; pixels&#91;BUF_WIDTH*SCR_HEIGHT&#93;;
//static unsigned short __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; swizzled_pixels&#91;BUF_WIDTH*SCR_HEIGHT&#93;;

static int exitRequest = 0;
static unsigned int staticOffset = 0;

static unsigned int getMemorySize&#40;unsigned int width, unsigned int height, unsigned int psm&#41;
&#123;
	switch &#40;psm&#41;
	&#123;
		case GU_PSM_T4&#58;
			return &#40;width * height&#41; >> 1;

		case GU_PSM_T8&#58;
			return width * height;

		case GU_PSM_5650&#58;
		case GU_PSM_5551&#58;
		case GU_PSM_4444&#58;
		case GU_PSM_T16&#58;
			return 2 * width * height;

		case GU_PSM_8888&#58;
		case GU_PSM_T32&#58;
			return 4 * width * height;

		default&#58;
			return 0;
	&#125;
&#125;

void* getStaticVramBuffer&#40;unsigned int width, unsigned int height, unsigned int psm&#41;
&#123;
	unsigned int memSize = getMemorySize&#40;width,height,psm&#41;;
	void* result = &#40;void*&#41;staticOffset;
	staticOffset += memSize;

	return result;
&#125;

int running&#40;&#41;
&#123;
	return !exitRequest;
&#125;

int exitCallback&#40;int arg1, int arg2, void *common&#41;
&#123;
	exitRequest = 1;
	return 0;
&#125;

int callbackThread&#40;SceSize args, void *argp&#41;
&#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exitCallback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;

	sceKernelSleepThreadCB&#40;&#41;;

	return 0;
&#125;

int setupCallbacks&#40;void&#41;
&#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", callbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41;
	&#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

void user_warning_fn&#40;png_structp png_ptr, png_const_charp warning_msg&#41;
&#123;
	// ignore PNG warnings
&#125;

int PowerOfTwo&#40;int value&#41;
&#123;
int poweroftwo = 1;
    while &#40;poweroftwo < value &#41;&#123;
		poweroftwo <<= 1;
	&#125;
	return poweroftwo;
&#125;

unsigned short Color8888To5551&#40;unsigned int Color32&#41;
&#123;
unsigned char Red = &#40;Color32&#41;;
unsigned short Red16 = Red>>3;
unsigned char Green = &#40;Color32>>8&#41;;
unsigned short Green16 = &#40;Green>>3&#41;<<5;
unsigned char Blue = &#40;Color32>>16&#41;;
unsigned short Blue16 = &#40;Blue>>3&#41;<<10;
unsigned char Alpha = &#40;Color32>>24&#41;;
unsigned short Alpha16 = &#40;Alpha>>3&#41;<<15;
unsigned short Color16 = Red16 | Green16 | Blue16 | Alpha16;
return Color16;
&#125;

unsigned short Color8888To4444&#40;unsigned int Color32&#41;
&#123;
unsigned char Red = &#40;Color32&#41;;
unsigned short Red16 = Red>>4;
unsigned char Green = &#40;Color32>>8&#41;;
unsigned short Green16 = &#40;Green>>4&#41;<<4;
unsigned char Blue = &#40;Color32>>16&#41;;
unsigned short Blue16 = &#40;Blue>>4&#41;<<8;
unsigned char Alpha = &#40;Color32>>24&#41;;
unsigned short Alpha16 = &#40;Alpha>>4&#41;<<12;
unsigned short Color16 = Red16 | Green16 | Blue16 | Alpha16;
return Color16;
&#125;

unsigned short Color8888To5650&#40;unsigned int Color32&#41;
&#123;
unsigned char Red = &#40;Color32&#41;;
unsigned short Red16 = Red>>3;
unsigned char Green = &#40;Color32>>8&#41;;
unsigned short Green16 = &#40;Green>>2&#41;<<5;
unsigned char Blue = &#40;Color32>>16&#41;;
unsigned short Blue16 = &#40;Blue>>3&#41;<<11;
//unsigned char Alpha = &#40;Color32>>24&#41;;
unsigned short Color16 = Red16 | Green16 | Blue16;
return Color16;
&#125;

struct Vertex
&#123;
	unsigned short u, v;
	unsigned short color;
	short x, y, z;
&#125;;

void advancedBlit&#40;int sx, int sy, int sw, int sh, int dx, int dy, int slice&#41;
&#123;
	int start, end;

	// blit maximizing the use of the texture-cache

	for &#40;start = sx, end = sx+sw; start < end; start += slice, dx += slice&#41;
	&#123;
		struct Vertex* vertices = &#40;struct Vertex*&#41;sceGuGetMemory&#40;2 * sizeof&#40;struct Vertex&#41;&#41;;
		int width = &#40;start + slice&#41; < end ? slice &#58; end-start;

		vertices&#91;0&#93;.u = start; vertices&#91;0&#93;.v = sy;
		vertices&#91;0&#93;.color = 0;
		vertices&#91;0&#93;.x = dx; vertices&#91;0&#93;.y = dy; vertices&#91;0&#93;.z = 0;

		vertices&#91;1&#93;.u = start + width; vertices&#91;1&#93;.v = sy + sh;
		vertices&#91;1&#93;.color = 0;
		vertices&#91;1&#93;.x = dx + width; vertices&#91;1&#93;.y = dy + sh; vertices&#91;1&#93;.z = 0;

		sceGuDrawArray&#40;GU_SPRITES,GU_TEXTURE_16BIT|GU_COLOR_4444|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,vertices&#41;;
	&#125;
&#125;

void swizzle_fast&#40;u8* out, const u8* in, unsigned int width, unsigned int height&#41;
&#123;
   unsigned int blockx, blocky;
   unsigned int j;
 
   unsigned int width_blocks = &#40;width / 16&#41;;
   unsigned int height_blocks = &#40;height / 8&#41;;
 
   unsigned int src_pitch = &#40;width-16&#41;/4;
   unsigned int src_row = width * 8;
 
   const u8* ysrc = in;
   u32* dst = &#40;u32*&#41;out;
 
   for &#40;blocky = 0; blocky < height_blocks; ++blocky&#41;
   &#123;
      const u8* xsrc = ysrc;
      for &#40;blockx = 0; blockx < width_blocks; ++blockx&#41;
      &#123;
         const u32* src = &#40;u32*&#41;xsrc;
         for &#40;j = 0; j < 8; ++j&#41;
         &#123;
            *&#40;dst++&#41; = *&#40;src++&#41;;
            *&#40;dst++&#41; = *&#40;src++&#41;;
            *&#40;dst++&#41; = *&#40;src++&#41;;
            *&#40;dst++&#41; = *&#40;src++&#41;;
            src += src_pitch;
         &#125;
         xsrc += 16;
     &#125;
     ysrc += src_row;
   &#125;
&#125;

Image* LoadPng&#40;const char* filename,int ColorMode,int Swizzle,int Vram&#41;
&#123;
    unsigned short *Buffer;
	unsigned short *swizzled_pixels = NULL;
	Image *Image1;
	png_structp png_ptr;
	png_infop info_ptr;
	unsigned int sig_read = 0;
	png_uint_32 width, height;
	int bit_depth, color_type, interlace_type, x, y;
	unsigned int* line;
	FILE *fp;
	int OutBytesPerPixel;
	int Power2Width = 0;
	int Power2Height = 0;
	if&#40;ColorMode == GU_PSM_4444 || ColorMode == GU_PSM_5650 || ColorMode == GU_PSM_5551&#41;OutBytesPerPixel = 2;
	else OutBytesPerPixel = 4;

	if &#40;&#40;fp = fopen&#40;filename, "rb"&#41;&#41; == NULL&#41;&#123;
            printf&#40;"Can't open file %s\n",filename&#41;;
            return NULL; 
    &#125;
	png_ptr = png_create_read_struct&#40;PNG_LIBPNG_VER_STRING, NULL, NULL, NULL&#41;;
	if &#40;png_ptr == NULL&#41; &#123;
		fclose&#40;fp&#41;;
		return NULL;
	&#125;
	png_set_error_fn&#40;png_ptr, &#40;png_voidp&#41; NULL, &#40;png_error_ptr&#41; NULL, user_warning_fn&#41;;
	info_ptr = png_create_info_struct&#40;png_ptr&#41;;
	if &#40;info_ptr == NULL&#41; &#123;
		fclose&#40;fp&#41;;
		png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;;
		return NULL;
	&#125;
	png_init_io&#40;png_ptr, fp&#41;;
	png_set_sig_bytes&#40;png_ptr, sig_read&#41;;
	png_read_info&#40;png_ptr, info_ptr&#41;;
	png_get_IHDR&#40;png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL&#41;;
	png_set_strip_16&#40;png_ptr&#41;;
	png_set_packing&#40;png_ptr&#41;;
	if &#40;color_type == PNG_COLOR_TYPE_PALETTE&#41; png_set_palette_to_rgb&#40;png_ptr&#41;;
	if &#40;color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8&#41; png_set_gray_1_2_4_to_8&#40;png_ptr&#41;;
	if &#40;png_get_valid&#40;png_ptr, info_ptr, PNG_INFO_tRNS&#41;&#41; png_set_tRNS_to_alpha&#40;png_ptr&#41;;
	png_set_filler&#40;png_ptr, 0xff, PNG_FILLER_AFTER&#41;;
	line = &#40;unsigned int*&#41; malloc&#40;width * 4&#41;;
	if &#40;!line&#41; &#123;
		fclose&#40;fp&#41;;
		png_destroy_read_struct&#40;&png_ptr, png_infopp_NULL, png_infopp_NULL&#41;;
		return NULL;
	&#125;
	Power2Width = PowerOfTwo&#40;width&#41;;
	Power2Height = PowerOfTwo&#40;height&#41;;
	Buffer = memalign&#40;16,Power2Width*Power2Height*OutBytesPerPixel&#41;;
	for &#40;y = 0; y < height; y++&#41; &#123;
		png_read_row&#40;png_ptr, &#40;unsigned char*&#41; line, png_bytep_NULL&#41;;
		for &#40;x = 0; x < width; x++&#41; &#123;
			unsigned int *Buffer32 = &#40;unsigned int*&#41;Buffer;
			unsigned int color32 = line&#91;x&#93;;
			unsigned short color16;
			//printf&#40;"%d&#58;%d %u ",y,x,color32&#41;;
			if&#40;ColorMode == GU_PSM_5551&#41;&#123;
				color16 = Color8888To5551&#40;color32&#41;;
				Buffer&#91;y*Power2Width+x&#93; = color16;
			&#125;
			else if&#40;ColorMode == GU_PSM_4444&#41;&#123;
				color16 = Color8888To4444&#40;color32&#41;;
				Buffer&#91;y*Power2Width+x&#93; = color16;
			&#125;
			else if&#40;ColorMode == GU_PSM_5650&#41;&#123;
				color16 = Color8888To5650&#40;color32&#41;;
				Buffer&#91;y*Power2Width+x&#93; = color16;
			&#125;
			else &#123;
				Buffer32&#91;y*Power2Width+x&#93; = color32;
			&#125;
			//printf&#40;"%u \n",Buffer&#91;y*width+x&#93;&#41;;
			//printf&#40;"%u \n",color16&#41;;
		&#125;
	&#125;
	free&#40;line&#41;;
	png_read_end&#40;png_ptr, info_ptr&#41;;
	png_destroy_read_struct&#40;&png_ptr, &info_ptr, png_infopp_NULL&#41;;
	fclose&#40;fp&#41;;	

Image1 = malloc&#40;sizeof&#40;Image&#41;&#41;;

Image1->Width = width;
Image1->Height = height;
Image1->power2width = Power2Width;
Image1->power2height = Power2Height;

swizzled_pixels = memalign&#40;16,Image1->power2height*Image1->power2width*OutBytesPerPixel&#41;;
swizzle_fast&#40;&#40;u8*&#41;swizzled_pixels,&#40;const u8*&#41;Buffer,Image1->power2width*OutBytesPerPixel,Image1->power2height&#41;; 
// 512*2 because swizzle operates in bytes, and each pixel in a 16-bit texture is 2 bytes
sceKernelDcacheWritebackAll&#40;&#41;;

Image1->ImageData = swizzled_pixels;
free&#40;Buffer&#41;;

sceGuFinish&#40;&#41;;
sceGuSync&#40;0,0&#41;;

return Image1;	
&#125;

int main&#40;int argc, char* argv&#91;&#93;&#41;
&#123;
	unsigned int i = 0,png = 0;
	
	Image *Image1;

	pspDebugScreenInit&#40;&#41;;
	setupCallbacks&#40;&#41;;

	// Setup GU

	void* fbp0 = getStaticVramBuffer&#40;BUF_WIDTH,SCR_HEIGHT,GU_PSM_8888&#41;;
	void* fbp1 = getStaticVramBuffer&#40;BUF_WIDTH,SCR_HEIGHT,GU_PSM_8888&#41;;
	void* zbp = getStaticVramBuffer&#40;BUF_WIDTH,SCR_HEIGHT,GU_PSM_4444&#41;;

	sceGuInit&#40;&#41;;

	sceGuStart&#40;GU_DIRECT,list&#41;;
	sceGuDrawBuffer&#40;GU_PSM_8888,fbp0,BUF_WIDTH&#41;;
	sceGuDispBuffer&#40;SCR_WIDTH,SCR_HEIGHT,fbp1,BUF_WIDTH&#41;;
	sceGuDepthBuffer&#40;zbp,BUF_WIDTH&#41;;
	sceGuOffset&#40;2048 - &#40;SCR_WIDTH/2&#41;,2048 - &#40;SCR_HEIGHT/2&#41;&#41;;
	sceGuViewport&#40;2048,2048,SCR_WIDTH,SCR_HEIGHT&#41;;
	sceGuDepthRange&#40;65535,0&#41;;
	sceGuScissor&#40;0,0,SCR_WIDTH,SCR_HEIGHT&#41;;
	sceGuEnable&#40;GU_SCISSOR_TEST&#41;;
	sceGuFrontFace&#40;GU_CW&#41;;
	sceGuEnable&#40;GU_TEXTURE_2D&#41;;
	sceGuClear&#40;GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT&#41;;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0,0&#41;;

	sceDisplayWaitVblankStart&#40;&#41;;
	sceGuDisplay&#40;1&#41;;
	
	if&#40;&#40;Image1 = LoadPng&#40;"2.png",GU_PSM_4444,1,0&#41;&#41; != NULL&#41;png = 1;

	float curr_ms = 1.0f;
	SceCtrlData oldPad;
	oldPad.Buttons = 0;

	sceCtrlSetSamplingCycle&#40;0&#41;;
	sceCtrlSetSamplingMode&#40;0&#41;;

	u64 last_tick;
	sceRtcGetCurrentTick&#40;&last_tick&#41;;
	u32 tick_frequency = sceRtcGetTickResolution&#40;&#41;;
	int frame_count = 0;

	while&#40;running&#40;&#41;&#41;
	&#123;
		SceCtrlData pad;

		sceGuStart&#40;GU_DIRECT,list&#41;;

		// switch methods if requested

		if&#40;sceCtrlPeekBufferPositive&#40;&pad, 1&#41;&#41;
		&#123;
			if &#40;pad.Buttons != oldPad.Buttons&#41;
			&#123;
				if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;;
				if&#40;pad.Buttons & PSP_CTRL_CIRCLE&#41;;
			&#125;
			oldPad = pad;
		&#125;

		sceGuTexMode&#40;GU_PSM_4444,0,0,1&#41;; // 16-bit RGBA
		sceGuTexImage&#40;0,Image1->power2width,Image1->power2height,Image1->power2width,Image1->ImageData&#41;; // setup texture as a 512x512 texture, even though the buffer is only 512x272 &#40;480 visible&#41;
		sceGuTexFunc&#40;GU_TFX_REPLACE,GU_TCC_RGBA&#41;; // don't get influenced by any vertex colors
		sceGuTexFilter&#40;GU_NEAREST,GU_NEAREST&#41;; // point-filtered sampling

		advancedBlit&#40;0,0,Image1->Width,Image1->Height,0,0,32&#41;;

		sceGuFinish&#40;&#41;;
		sceGuSync&#40;0,0&#41;;

		float curr_fps = 1.0f / curr_ms;

		pspDebugScreenSetOffset&#40;&#40;int&#41;fbp0&#41;;
		pspDebugScreenSetXY&#40;0,0&#41;;
		pspDebugScreenPrintf&#40;"fps&#58; %d.%03d width&#58; %d %d height&#58; %d %d %d %u",&#40;int&#41;curr_fps,&#40;int&#41;&#40;&#40;curr_fps-&#40;int&#41;curr_fps&#41; * 1000.0f&#41;,Image1->Width,Image1->power2width,Image1->Height,Image1->power2height,png,Image1->ImageData&#91;100+i&#93;&#41;;
		

//		sceDisplayWaitVblankStart&#40;&#41;;
		fbp0 = sceGuSwapBuffers&#40;&#41;;
		
		if&#40;i>1000&#41;i=0;
		else i++;

		// simple frame rate counter

		++frame_count;
		u64 curr_tick;
		sceRtcGetCurrentTick&#40;&curr_tick&#41;;
		if &#40;&#40;curr_tick-last_tick&#41; >= tick_frequency&#41;
		&#123;
			float time_span = &#40;&#40;int&#41;&#40;curr_tick-last_tick&#41;&#41; / &#40;float&#41;tick_frequency;
			curr_ms = time_span / frame_count;

			frame_count = 0;
			sceRtcGetCurrentTick&#40;&last_tick&#41;;
		&#125;
	&#125;

	sceGuTerm&#40;&#41;;

	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;
EDIT: As you may see the code is based on the sdk blit sample
Last edited by Mr.Modem on Tue Dec 05, 2006 1:09 am, edited 1 time in total.
Insert_witty_name
Posts: 376
Joined: Wed May 10, 2006 11:31 pm

Post by Insert_witty_name »

Maximum texture size is 512x512.
chp
Posts: 313
Joined: Wed Jun 23, 2004 7:16 am

Post by chp »

If you want to blit textures larger than 512 pixels you have to offset into the texture and setup proper texture block widths to skip the unseen data, since the hardware (and sceGu) does not handle texture larger than this. If you want to address the first 512x512 block of a 1024x1024 texture you set something like this:

Code: Select all

sceGuTexImage&#40;0,512,512,1024,texture&#41;;
GE Dominator
Mr.Modem
Posts: 28
Joined: Wed Sep 21, 2005 4:43 am

Post by Mr.Modem »

Thanks guys!
Post Reply