shithub: npe

ref: 979ae53cfee913c2d303ab0f95aaa65dace1c7b5
dir: /libnpe_sdl3/surface.c/

View raw version
#include "_sdl.h"
#include <SDL3/SDL_iostream.h>
#include <bio.h>

static void
setformat(SDL_Surface *s, ulong chan, int bpp)
{
	if((s->format = calloc(1, sizeof(SDL_PixelFormat))) == nil)
		sysfatal("setformat: %r");
	s->format->BytesPerPixel = bpp / 8;
	s->format->format = chan2pixel(chan);
	if(chan == CMAP8){
		if((s->format->palette = calloc(1, sizeof(SDL_Palette))) == nil)
			sysfatal("setformat: %r");
		s->format->palette->ncolors = 256;
		if((s->format->palette->colors = calloc(1, sizeof(SDL_Color) * 256)) == nil)
			sysfatal("setformat: %r");
	}
}

static SDL_Surface *
sfrommem(Memimage *i)
{
	int bpp;
	SDL_Surface *s;

	if((s = mallocz(sizeof *s, 1)) == nil)
		return nil;
	s->i = i;
	s->w = Dx(i->r);
	s->h = Dy(i->r);
	bpp = i->depth;
	s->pitch = s->w * (bpp / 8);
	s->clip_rect.x = 0;
	s->clip_rect.y = 0;
	s->clip_rect.w = s->w;
	s->clip_rect.h = s->h;
	s->n = s->pitch * s->h;
	if(i->chan == CMAP8)
		s->pixels = calloc(1, s->n);
	else
		s->pixels = i->data->bdata;
	if(s->pixels == nil)
		return nil;
	setformat(s, i->chan, bpp);
	return s;
}

SDL_Surface *
SDL_CreateRGBSurface(Uint32, int w, int h, int bpp, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
{
	SDL_Surface *s;
	int n;
	ulong chan;

	rm = rm ? rm : npe_sdl.defmask.r;
	gm = gm ? gm : npe_sdl.defmask.g;
	bm = bm ? bm : npe_sdl.defmask.b;
	if((chan = mask2chan(bpp, rm, gm, bm, am)) == 0){
		werrstr("bad bpp and/or mask");
		return nil;
	}
	n = w*h*bpp/8;
	if((s = calloc(1, sizeof(*s))) == nil){
		werrstr("SDL_CreateRGBSurface: memory");
		return nil;
	}
	if(chan == CMAP8){
		s->i = allocmemimage(Rect(0,0,w,h), screen->chan);
		s->format->palette = calloc(1, sizeof(SDL_Palette));
		s->format->palette->ncolors = 256;
		s->format->palette->colors = calloc(1, sizeof(SDL_Color) * 256);
		s->pixels = calloc(1, s->n);
	}else{
		s->i = allocmemimage(Rect(0,0,s->w,s->h), chan);
		s->pixels = ((Memimage*)s->i)->data->bdata;
	}
	
	s->w = w;
	s->h = h;
	s->pitch = w*bpp/8;
	s->clip_rect.x = 0;
	s->clip_rect.y = 0;
	s->clip_rect.w = w;
	s->clip_rect.h = h;
	s->n = n;

	return s;
}

SDL_Surface *
SDL_CreateRGBSurfaceFrom(void *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
{
	SDL_Surface *s;
	u8int *p;
	int n, y;

	if((s = SDL_CreateRGBSurface(0, w, h, bpp, rm, gm, bm, am)) == nil)
		return nil;

	n = w*bpp/8;
	for(y = 0, p = pixels; y < h; y++, p += pitch)
		memmove(s->pixels + y*n, p, n);

	return s;
}

SDL_Surface *
SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int w, int h, int fbpp, Uint32 fmt)
{
	SDL_Surface *s;
	int bpp;
	ulong chan;
	Uint32 rm, gm, bm, am;

	if((chan = pixel2chan(fmt)) == 0){
		werrstr("SDL_CreateRGBSurfaceWithFormat: FIXME format %8ux not implemented", fmt);
		return nil;
	}
	chan2mask(chan, &bpp, &rm, &gm, &bm, &am);
	if(bpp != fbpp && fbpp != 0)
		sysfatal("FIXME SDL_CreateRGBSurfaceWithFormat passes wrong bpp for format: %d not %d", fbpp, bpp);
	if((s = SDL_CreateRGBSurface(flags, w, h, bpp, rm, bm, gm, am)) == nil)
		return nil;
	return s;
}

Uint32
SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
{
	return SDL_MapRGB(surface->format, r, g, b);
}

bool
SDL_SetSurfacePalette(SDL_Surface *s, SDL_Palette *palette)
{
	s->format->palette = palette;
	return true;
}

bool
SDL_SetSurfaceColorKey(SDL_Surface *s, bool enable, Uint32 key)
{
	s->keyset = enable;
	s->key = key;
	return true;
}

static void
syncpalette(SDL_Surface *s)
{
	SDL_Color *c;
	Uint8 *to;
	int j;

	to = ((Memimage*)s->i)->data->bdata;
	for(j = 0; j < s->n; j++){
		c = s->format->palette->colors + s->pixels[j];
		*to++ = c->b;
		*to++ = c->g;
		*to++ = c->r;
		*to++ = c->a;
	}
}

static void
synctopalette(SDL_Surface *s)
{
	SDL_Color c, *f;
	Uint32 *from;
	int j, k;
	Memimage *i;
	SDL_PixelFormat fmt;

	i = s->i;
	fmt.format = chan2pixel(screen->chan);
	from = (void*)i->data->bdata;
	for(j = 0; j < s->n; j++){
		SDL_GetRGB(from[j], &fmt, &c.r, &c.g, &c.b);
		for(k = 0; k < s->format->palette->ncolors; k++){
			f = s->format->palette->colors + k;
			if(c.r == f->r && c.g == f->g && c.b == f->b)
				break;
		}
		if(k == s->format->palette->ncolors)
			s->pixels[j] = 0; /* FIXME */
		else
			s->pixels[j] = k;
	}
}

bool
SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
{
	Rectangle r, r2;

	r = srcrect == nil ? Rect(0, 0, src->w, src->h) : Rect(srcrect->x, srcrect->y, srcrect->x+srcrect->w, srcrect->y+srcrect->h);
	r2 = dstrect == nil ? Rect(0, 0, dst->w, dst->h) : Rect(dstrect->x, dstrect->y, dstrect->x+dstrect->w, dstrect->y+dstrect->h);

	if(src->format->format == SDL_PIXELFORMAT_INDEX8)
		syncpalette(src);

	memimagedraw(dst->i, r2, src->i, ZP, nil, ZP, S);

	if(dst->format->format == SDL_PIXELFORMAT_INDEX8)
		synctopalette(dst);
	return true;
}

bool
SDL_SetSurfaceRLE(SDL_Surface *, int)
{
	/* nothing to do here */
	return true;
}

bool
SDL_SetSurfaceBlendMode(SDL_Surface *, SDL_BlendMode blendMode)
{
	if(blendMode != SDL_BLENDMODE_NONE){
		werrstr("SDL_SetSurfaceBlendMode: only SDL_BLENDMODE_NONE is supported");
		return false;
	}
	return true;
}

bool
SDL_LockSurface(SDL_Surface *)
{
	/* nothing to do here */
	return true;
}

bool
SDL_UnlockSurface(SDL_Surface *)
{
	/* nothing to do here */
	return true;
}

// https://wiki.libsdl.org/SDL3/SDL_LowerBlit
bool
SDL_LowerBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
{
	return SDL_BlitSurface(src, srcrect, dst, dstrect);
}

bool
SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)
{
	Rectangle r, r2;
	Memimage *rowimg;
	int w, h;
	int scale;
	ulong *s, *d, *e;
	ulong *out;
	int i, y;

	r = srcrect == nil ? Rect(0, 0, src->w, src->h) : Rect(srcrect->x, srcrect->y, srcrect->x+srcrect->w, srcrect->y+srcrect->h);
	r2 = dstrect == nil ? Rect(0, 0, dst->w, dst->h) : Rect(dstrect->x, dstrect->y, dstrect->x+dstrect->w, dstrect->y+dstrect->h);

	w = Dx(r);
	h = Dy(r);

	scale = Dx(r2)/w;
	if(scale <= 0)
		scale = 1;
	else if(scale > 12)
		scale = 12;

	rowimg = allocmemimage(Rect(0, 0, scale*w, 1), ((Memimage*)src->i)->chan);

	assert(dst->format->format != SDL_PIXELFORMAT_INDEX8);
	if(src->format->format == SDL_PIXELFORMAT_INDEX8)
		syncpalette(src);

	for(y = 0; y < h; y++){
		s = wordaddr(src->i, Pt(0, y));
		d = (ulong*)rowimg->data->bdata;
		e = s + w;
		for(; s < e; s++){
			switch(scale){
			case 12:
				*d++ = *s;
			case 11:
				*d++ = *s;
			case 10:
				*d++ = *s;
			case 9:
				*d++ = *s;
			case 8:
				*d++ = *s;
			case 7:
				*d++ = *s;
			case 6:
				*d++ = *s;
			case 5:
				*d++ = *s;
			case 4:
				*d++ = *s;
			case 3:
				*d++ = *s;
			case 2:
				*d++ = *s;
			case 1:
				*d++ = *s;
			}
		}
		d = (ulong*)rowimg->data->bdata;
		for(i = 0; i < scale; i++){
			out = wordaddr(dst->i, Pt(0, y*scale + i));
			memcpy(out, d, scale*w*4);
		}
	}
	freememimage(rowimg);
	return true;
}

void
SDL_DestroySurface(SDL_Surface *surface)
{
	freememimage(surface->i);
	memset(surface, 0, sizeof(surface));
	free(surface);
}

bool
SDL_SaveBMP(SDL_Surface *s, const char *file)
{
	u8int h[54];
	Biobuf *f;
	int sz, i;

	if(s->format->format != SDL_PIXELFORMAT_RGB24){
		werrstr("SDL_SaveBMP: not rgb24");
		return false;
	}
	if((f = Bopen(file, OWRITE|OTRUNC)) == nil)
		return false;
	sz = sizeof(h) + s->n;
	memset(h, 0, sizeof(h));
	h[0] = 'B';
	h[1] = 'M';
	h[0x02+0] = sz;
	h[0x02+1] = sz>>8;
	h[0x02+2] = sz>>16;
	h[0x02+3] = sz>>24;
	h[0x0a] = sizeof(h);
	h[0x0e] = sizeof(h) - 14;
	h[0x12+0] = s->w;
	h[0x12+1] = s->w>>8;
	h[0x12+2] = s->w>>16;
	h[0x12+3] = s->w>>24;
	h[0x16+0] = s->h;
	h[0x16+1] = s->h>>8;
	h[0x16+2] = s->h>>16;
	h[0x16+3] = s->h>>24;
	h[0x1a] = 1;
	h[0x1c] = 24;
	Bwrite(f, h, sizeof(h));
	memset(h, 0, 4);
	for(i = s->h-1; i >= 0; i--){
		Bwrite(f, s->pixels+i*s->w*3, s->w*3);
		if(s->w & 3)
			Bwrite(f, h, 4-(s->w&3));
	}
	Bterm(f);
	return true;
}

/* fight me */
SDL_Surface*
SDL_LoadBMP_IO(SDL_IOStream *o, bool closeio)
{
	int n, pfd[2];
	uchar buf[8192];
	Memimage *i, *i2;

	if(pipe(pfd) < 0)
		sysfatal("SDL_LoadBMP_IO: %r");
	switch(fork()){
	case -1: sysfatal("SDL_LoadBMP_IO: %r");
	case 0:
		dup(pfd[0], 0);
		dup(pfd[0], 1);
		close(pfd[0]);
		close(pfd[1]);
		execl("/bin/bmp", "bmp", "-9tv", nil);
		sysfatal("SDL_LoadBMP_IO: %r");
	default:
		close(pfd[0]);
	}
	while((n = SDL_ReadIO(o, buf, sizeof buf)) > 0)
		if(write(pfd[1], buf, n) != n)
			sysfatal("SDL_LoadBMP_IO: %r");
	if(closeio)
		SDL_CloseIO(o);
	write(pfd[1], buf, 0);
	if((i = readmemimage(pfd[1])) == nil)
		sysfatal("SDL_LoadBMP_IO: %r");
	close(pfd[1]);
	/* bmp(1) doesn't do RGBA, SDL3 only does ARGB1555, RGB24 and ARGB32 */
	if((i2 = allocmemimage(i->r, ARGB32)) == nil){
		freememimage(i);
		return nil;
	}
	memimagedraw(i2, i2->r, i, ZP, nil, ZP, S);
	freememimage(i);
	return sfrommem(i2);
}