Skip to content

Image rescaling for IMAGE() #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 144 additions & 22 deletions src/ui/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
//
// Copyright(C) 2002-2019 Chris Warren-Smith.

#include "common/sberr.h"
#include "common/sys.h"
#include "common/messages.h"
#include "common/pproc.h"
#include "common/fs_socket_client.h"
#include "include/var.h"
#include "lib/maapi.h"
#include "lib/lodepng/lodepng.h"
#include "ui/image.h"
#include "ui/system.h"
#include "ui/rgb.h"
#include <cstdint>

#define IMG_X "x"
#define IMG_Y "y"
Expand Down Expand Up @@ -211,9 +214,47 @@ ImageBuffer *get_image(unsigned bid) {
return result;
}

void scaleImage(ImageBuffer* image, var_num_t scaling) {
if (scaling == 1.0 || scaling <= 0.0) {
return;
}

uint16_t xx, yy, w, h;
uint32_t offsetImage, offsetScaledImage;

w = round((var_num_t)image->_width * scaling);
h = round((var_num_t)image->_height * scaling);

uint8_t* scaledImage = (uint8_t *)malloc(w * h * 4);
if (!scaledImage) {
err_throw(ERR_IMAGE_LOAD, "Failed to allocate RAM");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sets up for SmallBASIC try/catch handling but isn't itself a throw like with java etc.
So you still need to avoid invoking the code below. So either use else { ... or return here.
In this case a simple return is the simplest solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I didn't know that. I changed it.

return;
}

uint32_t* image32bit = (uint32_t*)image->_image;
uint32_t* scaledImage32bit = (uint32_t*)scaledImage;

for (yy = 0; yy < h; yy++) {
for (xx = 0; xx < w; xx++) {
offsetScaledImage = yy * w + xx;
offsetImage = floor((var_num_t)yy / scaling) * image->_width + floor((var_num_t)xx / scaling);
scaledImage32bit[offsetScaledImage] = image32bit[offsetImage];
}
}

free(image->_image);
image->_width = w;
image->_height = h;
image->_image = scaledImage;
}

//
// Copy from screen
//
ImageBuffer *load_image(var_int_t x) {
var_int_t y, w, h;
int count = par_massget("iii", &y, &w, &h);
var_num_t scaling = 1.0;
int count = par_massget("iiif", &y, &w, &h, &scaling);
int width = g_system->getOutput()->getWidth();
int height = g_system->getOutput()->getHeight();
ImageBuffer *result = nullptr;
Expand All @@ -238,49 +279,92 @@ ImageBuffer *load_image(var_int_t x) {
result->_height = h;
result->_filename = nullptr;
result->_image = image;
scaleImage(result, scaling);
buffers.add(result);
}
}
return result;
}

//
// share image buffer from another image variable
//
ImageBuffer *load_image(var_t *var) {
ImageBuffer *result = nullptr;
var_num_t scaling = 1.0;

if (code_peek() == kwTYPE_SEP) {
code_skipsep();
scaling = par_getnum();
}

if (var->type == V_MAP) {
int bid = map_get_int(var, IMG_BID, -1);
if (bid != -1) {
result = get_image((unsigned)bid);
if (scaling == 1.0 || scaling <= 0.0) {
result = get_image((unsigned)bid);
} else {
ImageBuffer *inputImage = nullptr;
inputImage = get_image((unsigned)bid);
uint8_t* imageData = (uint8_t *)malloc(inputImage->_width * inputImage->_height * 4);
if (!imageData) {
err_throw(ERR_IMAGE_LOAD, "Failed to allocate RAM");
return result;
}
result = new ImageBuffer;
result->_bid = ++nextId;
result->_width = inputImage->_width;
result->_height = inputImage->_height;
result->_filename = nullptr;
memcpy(imageData, inputImage->_image, inputImage->_width * inputImage->_height * 4);
result->_image = imageData;
scaleImage(result, scaling);
buffers.add(result);
}
}
} else if (var->type == V_ARRAY && v_maxdim(var) == 2) {
int h = ABS(v_ubound(var, 0) - v_lbound(var, 0)) + 1;
int w = ABS(v_ubound(var, 1) - v_lbound(var, 1)) + 1;
int size = w * h * 4;
auto image = (uint8_t *)malloc(size);
auto imageData = (uint8_t *)malloc(size);
if (!imageData) {
err_throw(ERR_IMAGE_LOAD, "Failed to allocate RAM");
return result;
}
for (int y = 0; y < h; y++) {
int yoffs = (y * w * 4);
for (int x = 0; x < w; x++) {
int pos = y * w + x;
uint8_t a, r, g, b;
v_get_argb(v_getint(v_elem(var, pos)), a, r, g, b);
SET_IMAGE_ARGB(image, yoffs + (x * 4), a, r, g, b);
SET_IMAGE_ARGB(imageData, yoffs + (x * 4), a, r, g, b);
}
}
result = new ImageBuffer();
result->_bid = ++nextId;
result->_width = w;
result->_height = h;
result->_filename = nullptr;
result->_image = image;
result->_image = imageData;
scaleImage(result, scaling);
buffers.add(result);
}
return result;
}

//
// Load from file
//
ImageBuffer *load_image(const unsigned char *buffer, int32_t size) {
ImageBuffer *result = nullptr;
unsigned w, h;
unsigned char *image;
var_num_t scaling = 1.0;

if (code_peek() == kwTYPE_SEP) {
code_skipsep();
scaling = par_getnum();
}

unsigned error = decode_png(&image, &w, &h, buffer, size);
if (!error) {
Expand All @@ -290,26 +374,38 @@ ImageBuffer *load_image(const unsigned char *buffer, int32_t size) {
result->_height = h;
result->_filename = nullptr;
result->_image = image;
scaleImage(result, scaling);
buffers.add(result);
} else {
err_throw(ERR_IMAGE_LOAD, lodepng_error_text(error));
}
return result;
}

//
// Load from file
//
ImageBuffer *load_image(dev_file_t *filep) {
ImageBuffer *result = nullptr;
List_each(ImageBuffer *, it, buffers) {
ImageBuffer *next = (*it);
if (next->_filename != nullptr && strcmp(next->_filename, filep->name) == 0) {
result = next;
break;
}
var_num_t scaling = 1.0;

if (code_peek() == kwTYPE_SEP) {
code_skipsep();
scaling = par_getnum();
}

if (scaling == 1.0 || scaling <= 0.0) {
List_each(ImageBuffer *, it, buffers) {
ImageBuffer *next = (*it);
if (next->_filename != nullptr && strcmp(next->_filename, filep->name) == 0) {
result = next;
break;
}
}
}
if (result == nullptr) {
unsigned w, h;
unsigned char *image;
unsigned char *imageData;
unsigned error = 0;
unsigned network_error = 0;
var_t *var_p;
Expand All @@ -322,13 +418,13 @@ ImageBuffer *load_image(dev_file_t *filep) {
} else {
var_p = v_new();
http_read(filep, var_p);
error = decode_png(&image, &w, &h, (unsigned char *)var_p->v.p.ptr, var_p->v.p.length);
error = decode_png(&imageData, &w, &h, (unsigned char *)var_p->v.p.ptr, var_p->v.p.length);
v_free(var_p);
v_detach(var_p);
}
break;
case ft_stream:
error = decode_png_file(&image, &w, &h, filep->name);
error = decode_png_file(&imageData, &w, &h, filep->name);
break;
default:
error = 1;
Expand All @@ -344,25 +440,37 @@ ImageBuffer *load_image(dev_file_t *filep) {
result->_width = w;
result->_height = h;
result->_filename = strdup(filep->name);
result->_image = image;
result->_image = imageData;
scaleImage(result, scaling);
buffers.add(result);
}
}
return result;
}

//
// Create from XPM data
//
ImageBuffer *load_xpm_image(char **data) {
unsigned w, h;
unsigned char *image;
unsigned error = xpm_decode32(&image, &w, &h, data);
ImageBuffer *result = nullptr;
var_num_t scaling = 1.0;

if (code_peek() == kwTYPE_SEP) {
code_skipsep();
scaling = par_getnum();
}

if (!error) {
result = new ImageBuffer();
result->_bid = ++nextId;
result->_width = w;
result->_height = h;
result->_filename = nullptr;
result->_image = image;
scaleImage(result, scaling);
buffers.add(result);
} else {
err_throw(ERR_IMAGE_LOAD, ERR_XPM_IMAGE);
Expand Down Expand Up @@ -480,7 +588,7 @@ void cmd_image_save(var_s *self, var_s *) {
if (!prog_error &&
!encode_png_file(str.v.p.ptr, image->_image, w, h)) {
saved = true;
}
}
v_free(&str);
break;
default:
Expand All @@ -493,7 +601,7 @@ void cmd_image_save(var_s *self, var_s *) {
uint32_t offsetTop = map_get_int(self, IMG_OFFSET_TOP, 0);
uint32_t wClip = map_get_int(self, IMG_WIDTH, w);
uint32_t hClip = map_get_int(self, IMG_HEIGHT, h);

if (offsetTop < h && offsetLeft < w) {
if (offsetTop + hClip > h) {
hClip = h - offsetTop;
Expand All @@ -512,15 +620,15 @@ void cmd_image_save(var_s *self, var_s *) {
uint8_t a, r, g, b;
GET_IMAGE_ARGB(image->_image, yoffs + (x * 4), a, r, g, b);
pixel_t px = v_get_argb_px(a, r, g, b);
unsigned pos = (y - offsetTop ) * wClip + (x - offsetLeft);
unsigned pos = (y - offsetTop ) * wClip + (x - offsetLeft);
v_setint(v_elem(var, pos), px);
}
}
} else {
v_tomatrix(var, hClip, wClip);
}
saved = true;
}
}
}
}
if (!saved) {
Expand Down Expand Up @@ -646,6 +754,16 @@ void screen_dump() {
}
}

/*
* I = Image(file [,scale])
* I = Image(image [,scale])
* I = Image(x1,y1,x2,y2 [,scale])
* I = Image(pixmap [,scale])
* I = Image(array [,scale])
* scale > 1: upscale
* scale < 1: downscale
* scale <=0: don't scale
*/
extern "C" void v_create_image(var_p_t var) {
var_t arg;
ImageBuffer *image = nullptr;
Expand All @@ -659,22 +777,23 @@ extern "C" void v_create_image(var_p_t var) {
image = load_image(filep);
}
break;

case kwTYPE_LINE:
case kwTYPE_EOC:
break;

default:
v_init(&arg);
eval(&arg);

if (arg.type == V_STR && !prog_error) {
// Img = Image(FileName)
dev_file_t file;
strlcpy(file.name, arg.v.p.ptr, sizeof(file.name));
file.type = ft_stream;
image = load_image(&file);
} else if (arg.type == V_ARRAY && v_asize(&arg) > 0 && !prog_error) {
var_p_t elem0 = v_elem(&arg, 0);
if (elem0->type == V_STR) {
// Img = Image(PixmapData)
char **data = new char*[v_asize(&arg)];
for (unsigned i = 0; i < v_asize(&arg); i++) {
var_p_t elem = v_elem(&arg, i);
Expand All @@ -683,9 +802,10 @@ extern "C" void v_create_image(var_p_t var) {
image = load_xpm_image(data);
delete [] data;
} else if (v_maxdim(&arg) == 2) {
// load from 2d array
// Img = Image(Array2D)
image = load_image(&arg);
} else if (elem0->type == V_INT) {
// Create from buffer?
auto *data = new unsigned char[v_asize(&arg)];
for (unsigned i = 0; i < v_asize(&arg); i++) {
var_p_t elem = v_elem(&arg, i);
Expand All @@ -695,8 +815,10 @@ extern "C" void v_create_image(var_p_t var) {
delete [] data;
}
} else if (arg.type == V_INT && !prog_error) {
// Copy from screen
image = load_image(arg.v.i);
} else {
// Img2 = image(Img1) -> Img2 is pointer to existing buffer Img1
image = load_image(&arg);
}
v_free(&arg);
Expand Down