325 lines
7.7 KiB
C
325 lines
7.7 KiB
C
|
/*
|
||
|
* libtilemcore - Graphing calculator emulation library
|
||
|
*
|
||
|
* Copyright (C) 2010-2011 Benjamin Moody
|
||
|
*
|
||
|
* This library 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 2.1 of
|
||
|
* the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library 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 library; if not, see
|
||
|
* <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
# include <config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include "tilem.h"
|
||
|
|
||
|
/* Scale the input buffer, multiply by F * INCOUNT, and add to the
|
||
|
output buffer (which must be an exact multiple of the size of the
|
||
|
input buffer.) */
|
||
|
static inline void add_scale1d_exact(const byte * restrict in, int incount,
|
||
|
unsigned int * restrict out,
|
||
|
int outcount, int f)
|
||
|
{
|
||
|
int i, j;
|
||
|
|
||
|
for (i = 0; i < incount; i++) {
|
||
|
for (j = 0; j < outcount / incount; j++) {
|
||
|
*out += *in * f * incount;
|
||
|
out++;
|
||
|
}
|
||
|
in++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Scale a 1-dimensional buffer, multiply by F * INCOUNT, and add to
|
||
|
the output buffer. */
|
||
|
static inline void add_scale1d_smooth(const byte * restrict in, int incount,
|
||
|
unsigned int * restrict out,
|
||
|
int outcount, int f)
|
||
|
{
|
||
|
int in_rem, out_rem;
|
||
|
unsigned int outv;
|
||
|
int i;
|
||
|
|
||
|
in_rem = outcount;
|
||
|
out_rem = incount;
|
||
|
outv = 0;
|
||
|
i = outcount;
|
||
|
while (i > 0) {
|
||
|
if (in_rem < out_rem) {
|
||
|
out_rem -= in_rem;
|
||
|
outv += in_rem * *in * f;
|
||
|
in++;
|
||
|
in_rem = outcount;
|
||
|
}
|
||
|
else {
|
||
|
in_rem -= out_rem;
|
||
|
outv += out_rem * *in * f;
|
||
|
*out += outv;
|
||
|
outv = 0;
|
||
|
out++;
|
||
|
out_rem = incount;
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Scale a 2-dimensional buffer, multiply by INWIDTH * INHEIGHT, and
|
||
|
store in the output buffer. */
|
||
|
static void scale2d_smooth(const byte * restrict in,
|
||
|
int inwidth, int inheight, int inrowstride,
|
||
|
unsigned int * restrict out,
|
||
|
int outwidth, int outheight, int outrowstride)
|
||
|
{
|
||
|
int in_rem, out_rem;
|
||
|
int i;
|
||
|
|
||
|
memset(out, 0, outrowstride * outheight * sizeof(int));
|
||
|
|
||
|
in_rem = outheight;
|
||
|
out_rem = inheight;
|
||
|
i = outheight;
|
||
|
while (i > 0) {
|
||
|
if (in_rem < out_rem) {
|
||
|
if (in_rem) {
|
||
|
if (outwidth % inwidth)
|
||
|
add_scale1d_smooth(in, inwidth, out,
|
||
|
outwidth, in_rem);
|
||
|
else
|
||
|
add_scale1d_exact(in, inwidth, out,
|
||
|
outwidth, in_rem);
|
||
|
}
|
||
|
out_rem -= in_rem;
|
||
|
in += inrowstride;
|
||
|
in_rem = outheight;
|
||
|
}
|
||
|
else {
|
||
|
in_rem -= out_rem;
|
||
|
if (outwidth % inwidth)
|
||
|
add_scale1d_smooth(in, inwidth, out, outwidth,
|
||
|
out_rem);
|
||
|
else
|
||
|
add_scale1d_exact(in, inwidth, out, outwidth,
|
||
|
out_rem);
|
||
|
out += outrowstride;
|
||
|
out_rem = inheight;
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Quickly scale a 1-dimensional buffer and store in the output
|
||
|
buffer. */
|
||
|
static inline void scale1d_fast(const byte * restrict in, int incount,
|
||
|
byte * restrict out, int outcount)
|
||
|
{
|
||
|
int i, e;
|
||
|
|
||
|
e = outcount - incount / 2;
|
||
|
i = outcount;
|
||
|
while (i > 0) {
|
||
|
if (e >= 0) {
|
||
|
*out = *in;
|
||
|
out++;
|
||
|
e -= incount;
|
||
|
i--;
|
||
|
}
|
||
|
else {
|
||
|
e += outcount;
|
||
|
in++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Quickly scale a 2-dimensional buffer and store in the output
|
||
|
buffer. */
|
||
|
static void scale2d_fast(const byte * restrict in,
|
||
|
int inwidth, int inheight, int inrowstride,
|
||
|
byte * restrict out,
|
||
|
int outwidth, int outheight, int outrowstride)
|
||
|
{
|
||
|
int i, e;
|
||
|
|
||
|
e = outheight - inheight / 2;
|
||
|
i = outheight;
|
||
|
while (i > 0) {
|
||
|
if (e >= 0) {
|
||
|
scale1d_fast(in, inwidth, out, outwidth);
|
||
|
out += outrowstride;
|
||
|
e -= inheight;
|
||
|
i--;
|
||
|
}
|
||
|
else {
|
||
|
e += outheight;
|
||
|
in += inrowstride;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Determine range of linear pixel values corresponding to a given
|
||
|
contrast level. */
|
||
|
static void get_contrast_settings(unsigned int contrast,
|
||
|
int *cbase, int *cfact)
|
||
|
{
|
||
|
if (contrast < 32) {
|
||
|
*cbase = 0;
|
||
|
*cfact = contrast * 8;
|
||
|
}
|
||
|
else {
|
||
|
*cbase = (contrast - 32) * 8;
|
||
|
*cfact = 255 - *cbase;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define GETSCALEBUF(ttt, www, hhh) \
|
||
|
((ttt *) alloc_scalebuf(buf, (www) * (hhh) * sizeof(ttt)))
|
||
|
|
||
|
static void* alloc_scalebuf(TilemLCDBuffer *buf, unsigned int size)
|
||
|
{
|
||
|
if (TILEM_UNLIKELY(size > buf->tmpbufsize)) {
|
||
|
buf->tmpbufsize = size;
|
||
|
tilem_free(buf->tmpbuf);
|
||
|
buf->tmpbuf = tilem_malloc_atomic(size);
|
||
|
}
|
||
|
|
||
|
return buf->tmpbuf;
|
||
|
}
|
||
|
|
||
|
void tilem_draw_lcd_image_indexed(TilemLCDBuffer * restrict buf,
|
||
|
byte * restrict buffer,
|
||
|
int imgwidth, int imgheight,
|
||
|
int rowstride, int scaletype)
|
||
|
{
|
||
|
int dwidth = buf->width;
|
||
|
int dheight = buf->height;
|
||
|
int i, j, v;
|
||
|
unsigned int * restrict ibuf;
|
||
|
int cbase, cfact;
|
||
|
byte cindex[129];
|
||
|
|
||
|
if (dwidth == 0 || dheight == 0 || buf->contrast == 0) {
|
||
|
for (i = 0; i < imgheight; i++) {
|
||
|
for (j = 0; j < imgwidth; j++)
|
||
|
buffer[j] = 0;
|
||
|
buffer += rowstride;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
get_contrast_settings(buf->contrast, &cbase, &cfact);
|
||
|
|
||
|
for (i = 0; i <= 128; i++)
|
||
|
cindex[i] = ((i * cfact) >> 7) + cbase;
|
||
|
|
||
|
if (scaletype == TILEM_SCALE_FAST
|
||
|
|| (imgwidth % dwidth == 0 && imgheight % dheight == 0)) {
|
||
|
scale2d_fast(buf->data, dwidth, dheight, buf->rowstride,
|
||
|
buffer, imgwidth, imgheight, rowstride);
|
||
|
|
||
|
for (i = 0; i < imgwidth * imgheight; i++)
|
||
|
buffer[i] = cindex[buffer[i]];
|
||
|
}
|
||
|
else {
|
||
|
ibuf = GETSCALEBUF(unsigned int, imgwidth, imgheight);
|
||
|
|
||
|
scale2d_smooth(buf->data, dwidth, dheight, buf->rowstride,
|
||
|
ibuf, imgwidth, imgheight, imgwidth);
|
||
|
|
||
|
for (i = 0; i < imgheight; i++) {
|
||
|
for (j = 0; j < imgwidth; j++) {
|
||
|
v = ibuf[j] / (dwidth * dheight);
|
||
|
buffer[j] = cindex[v];
|
||
|
}
|
||
|
ibuf += imgwidth;
|
||
|
buffer += rowstride;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void tilem_draw_lcd_image_rgb(TilemLCDBuffer * restrict buf,
|
||
|
byte * restrict buffer,
|
||
|
int imgwidth, int imgheight, int rowstride,
|
||
|
int pixbytes, const dword * restrict palette,
|
||
|
int scaletype)
|
||
|
{
|
||
|
int dwidth = buf->width;
|
||
|
int dheight = buf->height;
|
||
|
int i, j, v;
|
||
|
int padbytes = rowstride - (imgwidth * pixbytes);
|
||
|
byte * restrict bbuf;
|
||
|
unsigned int * restrict ibuf;
|
||
|
int cbase, cfact;
|
||
|
dword cpalette[129];
|
||
|
|
||
|
if (dwidth == 0 || dheight == 0 || buf->contrast == 0) {
|
||
|
for (i = 0; i < imgheight; i++) {
|
||
|
for (j = 0; j < imgwidth; j++) {
|
||
|
buffer[0] = palette[0] >> 16;
|
||
|
buffer[1] = palette[0] >> 8;
|
||
|
buffer[2] = palette[0];
|
||
|
buffer += pixbytes;
|
||
|
}
|
||
|
buffer += padbytes;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
get_contrast_settings(buf->contrast, &cbase, &cfact);
|
||
|
|
||
|
for (i = 0; i <= 128; i++) {
|
||
|
v = ((i * cfact) >> 7) + cbase;
|
||
|
cpalette[i] = palette[v];
|
||
|
}
|
||
|
|
||
|
if (scaletype == TILEM_SCALE_FAST
|
||
|
|| (imgwidth % dwidth == 0 && imgheight % dheight == 0)) {
|
||
|
bbuf = GETSCALEBUF(byte, imgwidth, imgheight);
|
||
|
|
||
|
scale2d_fast(buf->data, dwidth, dheight, buf->rowstride,
|
||
|
bbuf, imgwidth, imgheight, imgwidth);
|
||
|
|
||
|
for (i = 0; i < imgheight; i++) {
|
||
|
for (j = 0; j < imgwidth; j++) {
|
||
|
v = bbuf[j];
|
||
|
buffer[0] = cpalette[v] >> 16;
|
||
|
buffer[1] = cpalette[v] >> 8;
|
||
|
buffer[2] = cpalette[v];
|
||
|
buffer += pixbytes;
|
||
|
}
|
||
|
bbuf += imgwidth;
|
||
|
buffer += padbytes;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ibuf = GETSCALEBUF(unsigned int, imgwidth, imgheight);
|
||
|
|
||
|
scale2d_smooth(buf->data, dwidth, dheight, buf->rowstride,
|
||
|
ibuf, imgwidth, imgheight, imgwidth);
|
||
|
|
||
|
for (i = 0; i < imgheight; i++) {
|
||
|
for (j = 0; j < imgwidth; j++) {
|
||
|
v = (ibuf[j] / (dwidth * dheight));
|
||
|
buffer[0] = cpalette[v] >> 16;
|
||
|
buffer[1] = cpalette[v] >> 8;
|
||
|
buffer[2] = cpalette[v];
|
||
|
buffer += pixbytes;
|
||
|
}
|
||
|
ibuf += imgwidth;
|
||
|
buffer += padbytes;
|
||
|
}
|
||
|
}
|
||
|
}
|