shithub: cstory

ref: b821d86c911e230575d3d3d9e45f1690de5636a1
dir: /src/Backends/Rendering/9front.cpp/

View raw version
// Released under the MIT licence.
// See LICENCE.txt for details.

#include "../Rendering.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <draw.h>
#include <memdraw.h>
#include <thread.h>
#include <mouse.h>

#include "../Misc.h"
#include "Window/Software.h"
#include "../../Attributes.h"

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

static Channel *resizec;
static Mousectl *mctl;

typedef struct RenderBackend_Surface
{
	Image *i;
	Image *mask;
	int dirty;
} RenderBackend_Surface;

typedef struct RenderBackend_GlyphAtlas
{
	Memimage *m;
	Image *color;
	int dirty;
	Image *cache;
} RenderBackend_GlyphAtlas;

static RenderBackend_Surface framebuffer;

static RenderBackend_GlyphAtlas *glyph_atlas;
static RenderBackend_Surface *glyph_destination_surface;

enum {
	scale = 2,
};

static void RenderBackend_Mouseproc(void*)
{
	enum { Amouse, Aresize, Aout, Aend };
	static int needgetwin;

	Alt a[] = {
		[Amouse] {  mctl->c, nil, CHANRCV },
		[Aresize] { mctl->resizec, nil, CHANRCV },
		[Aout] { resizec, &needgetwin, CHANSND },
		[Aend] { nil, nil, CHANEND },
	};

	threadsetname("mouseproc");
	for(;;){
		switch(alt(a)){
		case Aresize:
			needgetwin = 1;
			break;
		case Aout:
			needgetwin = 0;
			break;
		}
	}
}

RenderBackend_Surface* RenderBackend_Init(const char *window_title, size_t width, size_t height, int fullscreen)
{
	memimageinit();
	if(initdraw(nil, nil, "cstory") < 0)
		sysfatal("initdraw: %r");
	resizec = chancreate(sizeof(int), 1);
	mctl = initmouse(nil, screen);
	if(mctl == nil)
		sysfatal("initmouse: %r");
	proccreate(RenderBackend_Mouseproc, nil, 8192);

	framebuffer.i = allocimage(display, Rect(0, 0, width*scale, height*scale), screen->chan, 0, DBlue);
	if(framebuffer.i == nil)
		sysfatal("could not alloc screen");

	return &framebuffer;
}

void RenderBackend_Deinit(void)
{
	freeimage(framebuffer.i);
}

void RenderBackend_DrawScreen(void)
{
	int needgetwin;

	recv(resizec, &needgetwin);
	if(needgetwin)
		getwindow(display, Refnone);
	draw(screen, screen->r, framebuffer.i, nil, ZP);
	flushimage(display, 1);
}

RenderBackend_Surface* RenderBackend_CreateSurface(size_t width, size_t height, int render_target)
{
	(void)render_target;

	RenderBackend_Surface *surface = mallocz(sizeof(RenderBackend_Surface), 1);

	if (surface == NULL)
		return NULL;

	surface->i = allocimage(display, Rect(0, 0, width*scale, height*scale), screen->chan, 0, DGreen);

	return surface;
}

void RenderBackend_FreeSurface(RenderBackend_Surface *surface)
{
	freeimage(surface->i);
	freeimage(surface->mask);
	free(surface);
}

int RenderBackend_IsSurfaceLost(RenderBackend_Surface *surface)
{
	(void)surface;

	return 0;
}

void RenderBackend_RestoreSurface(RenderBackend_Surface *surface)
{
	(void)surface;
}

void RenderBackend_UploadSurfaceScale(RenderBackend_Surface *surface, const unsigned char *pixels, size_t width, size_t height)
{
	Image *row;
	Rectangle r, r2;
	int i, j, k, l;
	uchar *buf, *d;

	r = Rect(0, 0, width*scale, 1);
	r2 = Rect(0, 0, width*scale, scale);
	row = allocimage(display, r, BGR24, 1, DYellow);
	buf = malloc(width*scale*3);

	for(i = 0; i < height; i++){
		d = buf;
		for(j = 0; j < width; j++)
		for(k = 0; k < scale; k++){
			*d++ = pixels[(i*width*3) + j*3 + 0];
			*d++ = pixels[(i*width*3) + j*3 + 1];
			*d++ = pixels[(i*width*3) + j*3 + 2];
		}

		loadimage(row, r, buf, width*scale*3);
		draw(surface->i, r2, row, nil, ZP);
		r2.min.y += scale;
		r2.max.y += scale;
	}

	freeimage(row);
	free(buf);
	surface->dirty = 1;
}

void RenderBackend_UploadSurface(RenderBackend_Surface *surface, const unsigned char *pixels, size_t width, size_t height)
{
	Image *i;
	Rectangle r;

	if(scale != 1){
		RenderBackend_UploadSurfaceScale(surface, pixels, width, height);
		return;
	}

	r = Rect(0, 0, width, height);
	i = allocimage(display, r, BGR24, 0, DYellow);
	loadimage(i, r, pixels, width*height*3);
	draw(surface->i, r, i, nil, ZP);
	freeimage(i);
	surface->dirty = 1;
}

void RenderBackend_CalcMask(RenderBackend_Surface *s)
{
	Memimage *m;
	Rectangle r;
	int x, y, w, h;
	ulong *lp;
	ulong mask;

	switch(s->i->chan){
	case XRGB32:
	case ARGB32:
	case XBGR32:
	case ABGR32:
		mask = 0xFFFFFF;
		break;
	case RGBA32:
		mask = 0xFFFFFF00;
		break;
	default:
		/* TODO? */
		sysfatal("< 32 bit screen color channel");
		return;
	}
	r = s->i->r;
	w = Dx(r);
	h = Dy(r);
	m = allocmemimage(r, s->i->chan);
	unloadimage(s->i, r, m->data->bdata, h*w*(s->i->depth/8));
	for(y = 0; y < h; y++)
	for(x = 0; x < w; x++){
		lp = m->data->base + y*w + x;
		if((*lp & mask) == 0)
			*lp = 0x00;
		else
			*lp = 0xFFFFFFFF;
	}
	freeimage(s->mask);
	s->mask = allocimage(display, r, screen->chan, 0, DBlue);
	loadimage(s->mask, r, m->data->bdata, h*w*(s->i->depth/8));
	freememimage(m);
	s->dirty = 0;
}

void RenderBackend_Blit(RenderBackend_Surface *source_surface, const RenderBackend_Rect *rect, RenderBackend_Surface *destination_surface, long x, long y, int colour_key)
{
	Rectangle r, r2;

	r.min = (Point){rect->left*scale, rect->top*scale};
	r.max = (Point){rect->right*scale, rect->bottom*scale};

	r2.min = (Point){x*scale, y*scale};
	r2.max = (Point){x*scale+Dx(r), y*scale+Dy(r)};

	if(colour_key && source_surface->dirty)
		RenderBackend_CalcMask(source_surface);

	draw(destination_surface->i, r2, source_surface->i, colour_key ? source_surface->mask : nil, r.min);
	destination_surface->dirty = 1;
}

void RenderBackend_ColourFill(RenderBackend_Surface *surface, const RenderBackend_Rect *rect, unsigned char red, unsigned char green, unsigned char blue)
{
	Rectangle r;
	Image *color;

	r = Rect(rect->left*scale, rect->top*scale, rect->right*scale, rect->bottom*scale);
	color = allocimage(display, Rect(0, 0, 1, 1), BGR24, 1, (red<<24)|(green<<16)|(blue<<8)|0xFF);
	draw(surface->i, r, color, nil, ZP);
	freeimage(color);
	surface->dirty = 1;
}

RenderBackend_GlyphAtlas* RenderBackend_CreateGlyphAtlas(size_t width, size_t height)
{
	RenderBackend_GlyphAtlas *atlas = mallocz(sizeof(RenderBackend_GlyphAtlas), 1);
	atlas->m = allocmemimage(Rect(0, 0, width*scale, height*scale), screen->chan);
	atlas->dirty = 1;
	return atlas;
}

void RenderBackend_DestroyGlyphAtlas(RenderBackend_GlyphAtlas *atlas)
{
	freememimage(atlas->m);
	freeimage(atlas->cache);
	free(atlas);
}

void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height, size_t pitch)
{
	uchar *s, *d, *od;
	ulong dw;
	int ix, iy, k;

	dw = Dx(atlas->m->r);
	for(iy = 0; iy < height; iy++, y++){
		s = pixels + iy*pitch;
		od = d = byteaddr(atlas->m, Pt(x*scale, y*scale));

		for(ix = 0; ix < width; ix++, s++)
		for(k = 0; k < scale; k++){
			*d++ = *s;
			*d++ = *s;
			*d++ = *s;
			*d++ = 0xFF;
		}

		for(k = 1; k < scale; k++)
			memcpy(byteaddr(atlas->m, Pt(x*scale, y*scale+k)), od, d - od);
	}
	atlas->dirty = 1;
}

void RenderBackend_PrepareToDrawGlyphs(RenderBackend_GlyphAtlas *atlas, RenderBackend_Surface *destination_surface, unsigned char red, unsigned char green, unsigned char blue)
{
	int x, y, w;

	glyph_atlas = atlas;
	glyph_destination_surface = destination_surface;

	freeimage(atlas->color);
	atlas->color = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (red<<24)|(green<<16)|(blue<<8)|0xFF);
}

void RenderBackend_DrawGlyph(long x, long y, size_t glyph_x, size_t glyph_y, size_t glyph_width, size_t glyph_height)
{
	Rectangle r, cr;
	Point p;

	r = Rect(x*scale, y*scale, x*scale+glyph_width*scale, y*scale+glyph_height*scale);
	p = Pt(glyph_x*scale, glyph_y*scale);
	if(glyph_atlas->dirty){
		cr = glyph_atlas->m->r;
		freeimage(glyph_atlas->cache);
		glyph_atlas->cache = allocimage(display, cr, screen->chan, 0, DYellow);
		loadimage(glyph_atlas->cache, cr, glyph_atlas->m->data->bdata, Dx(cr)*Dy(cr)*(glyph_atlas->m->depth/8));
		glyph_atlas->dirty = 0;
	}
	draw(glyph_destination_surface->i, r, glyph_atlas->color, glyph_atlas->cache, p);
	glyph_destination_surface->dirty = 1;
}

void RenderBackend_HandleRenderTargetLoss(void)
{
}

void RenderBackend_HandleWindowResize(size_t width, size_t height)
{
}