cleanups in draw_image(), added command `xschem image` for various operations on images

This commit is contained in:
stefan schippers 2024-03-17 01:32:10 +01:00
parent 6416df9733
commit d5f7c5c88c
4 changed files with 187 additions and 93 deletions

View File

@ -543,7 +543,6 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
<li><kbd> abort_operation</kbd></li><pre>
@ -862,6 +861,13 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
'inst' can be an instance name or number </pre>
<li><kbd> hilight_netname net</kbd></li><pre>
Highlight net name 'net' </pre>
<li><kbd> image [invert|white_transp|black_transp|transp_white|transp_black|write_back]</kbd></li><pre>
Apply required changes to selected images
invert: invert colors
white_transp: transform white to transparent color (alpha=0) after invert.
black_transp: transform black to transparent color (alpha=0) after invert.
transp_white: transform white to transparent color (alpha=0) after invert.
transp_black: transform black to transparent color (alpha=0) after invert.</pre>
<li><kbd> instance sym_name x y rot flip [prop] [n]</kbd></li><pre>
Place a new instance of symbol 'sym_name' at position x,y,
rotation and flip set to 'rot', 'flip'
@ -1592,6 +1598,8 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"

View File

@ -3880,15 +3880,34 @@ cairo_status_t png_writer(void *in_closure, const unsigned char *in_data, unsign
#endif
#if HAS_CAIRO==1
void inspect_image(cairo_surface_t **surface)
/* what:
* 1: invert colors
* 2: set white to transparent
* 4: set black to transparent
* 8: set transparent to white
* 16: set transparent to black
* 256: write back into `image_data` attribute
*/
int edit_image(int what, xRect *r)
{
cairo_t *ct;
unsigned char *data;
cairo_surface_t *newsfc;
cairo_format_t format;
int size_x, size_y, stride, x, y;
int jpg, size_x, size_y, stride, x, y;
xEmb_image *emb_ptr = r->extraptr;
cairo_surface_t **surface = &emb_ptr->image;
const char *attr = get_tok_value(r->prop_ptr, "image_data", 0);
cairo_surface_flush(*surface);
if(attr[0]) {
if(!strncmp(attr, "/9j/", 4)) jpg = 1;
else if(!strncmp(attr, "iVBOR", 5)) jpg = 0;
else jpg = -1; /* some invalid data */
} else {
jpg = -1;
}
if(jpg == -1) return 0;
format = cairo_image_surface_get_format(*surface);
size_x = cairo_image_surface_get_width(*surface);
size_y = cairo_image_surface_get_height(*surface);
@ -3908,43 +3927,121 @@ void inspect_image(cairo_surface_t **surface)
for(x = 0; x < size_x; x++) {
for(y = 0; y < size_y; y++) {
unsigned char *ptr = data + y * stride + x * 4;
unsigned int a = ptr[3];
unsigned int r = ptr[2];
unsigned int g = ptr[1];
unsigned int b = ptr[0];
unsigned char a = ptr[3];
unsigned char r = ptr[2];
unsigned char g = ptr[1];
unsigned char b = ptr[0];
if(0 && x == 100) {
dbg(0, "R=%d G=%d B=%d A=%d\n", r, g, b, a);
/* invert colors */
if(what & 1) {
r = a - r;
g = a - g;
b = a - b;
}
#if 1 /* premultiplied data invert*/
r = a - r;
g = a - g;
b = a - b;
#endif
/* set white to transparent */
if(what & 2) {
if(r > 242 && g > 242 && b >242) {r = g = b = a = 0;}
}
#if 1 /* set (almost) white to transparent */
if(r > 245 && g > 245 && b >245) {r = g = b = a = 0;}
#endif
/* set black to transparent */
if(what & 4) {
if(r < 13 && g < 13 && b < 13) {r = g = b = a = 0;}
}
#if 0 /* set (almost) black to transparent */
if(r < 10 && g < 10 && b < 10) {r = g = b = a = 0;}
#endif
/* set transparent to white */
if(what & 8) {
if(a == 0) {r = g = b = a = 0xff;}
}
/* set transparent to black */
if(what & 16) {
if(a == 0) {r = g = b = 0x00; a = 0xff;}
}
/* write result back */
#if 1
ptr[3] = (unsigned char) a;
ptr[2] = (unsigned char) r;
ptr[1] = (unsigned char) g;
ptr[0] = (unsigned char) b;
#endif
ptr[3] = a;
ptr[2] = r;
ptr[1] = g;
ptr[0] = b;
}
}
cairo_surface_mark_dirty(*surface);
/* write back modified image to image_data attribute */
if(what & 256) {
char *encoded_data = NULL;
size_t olength;
png_to_byte_closure_t closure;
if(jpg == 0) {
/* write PNG to in-memory buffer */
closure.buffer = NULL;
closure.size = 0;
closure.pos = 0;
cairo_surface_write_to_png_stream(emb_ptr->image, png_writer, &closure);
closure.size = closure.pos;
} else if(jpg == 1) {
/* write JPG to in-memory buffer */
#if defined(HAS_LIBJPEG)
int jpeg_quality;
const char *ptr;
ptr = get_tok_value(r->prop_ptr, "jpeg_quality", 0);
jpeg_quality = 75;
if(ptr[0]) jpeg_quality = atoi(ptr);
closure.buffer = NULL;
closure.size = 0;
closure.pos = 0;
cairo_image_surface_write_to_jpeg_mem(emb_ptr->image, &closure.buffer, &closure.pos, jpeg_quality);
closure.size = closure.pos;
#endif
}
/* put base64 encoded data to rect image_data attribute */
encoded_data = base64_encode((unsigned char *)closure.buffer, closure.size, &olength, 0);
my_strdup2(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "image_data", encoded_data));
my_free(_ALLOC_ID_, &closure.buffer);
my_free(_ALLOC_ID_, &encoded_data);
}
dbg(1, "size_x = %d, size_y = %d, stride = %d\n", size_x, size_y, stride);
return 1;
}
#endif
static cairo_surface_t *get_surface_from_b64data(const char *attr)
{
int jpg = -1;
png_to_byte_closure_t closure;
size_t data_size;
cairo_surface_t *surface = NULL;
if(!strncmp(attr, "/9j/", 4)) jpg = 1;
else if(!strncmp(attr, "iVBOR", 5)) jpg = 0;
else jpg = -1; /* some invalid data */
closure.buffer = base64_decode(attr, strlen(attr), &data_size);
closure.pos = 0;
closure.size = data_size; /* should not be necessary */
if(closure.buffer == NULL) {
dbg(0, "draw_image(): image creation failed\n");
return NULL;
}
if(jpg == 0) {
surface = cairo_image_surface_create_from_png_stream(png_reader, &closure);
} else if(jpg == 1) {
#if defined(HAS_LIBJPEG)
surface = cairo_image_surface_create_from_jpeg_mem(closure.buffer, closure.size);
#endif
}
if(!surface || cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
if(jpg != 1)
dbg(0, "draw_image(): failure creating image surface from \"image_data\" attribute\n");
if(surface) cairo_surface_destroy(surface);
surface = NULL;
}
my_free(_ALLOC_ID_, &closure.buffer);
return surface;
}
/* rot and flip for rotated / flipped symbols
* dr: 1 draw image
* 0 only load image and build base64
@ -3953,9 +4050,6 @@ int draw_image(int dr, xRect *r, double *x1, double *y1, double *x2, double *y2,
{
#if HAS_CAIRO==1
#if defined(HAS_LIBJPEG)
#ifdef INSPECT_IMAGE
int jpeg_quality=75;
#endif
#endif
const char *ptr;
int w,h;
@ -3986,46 +4080,17 @@ int draw_image(int dr, xRect *r, double *x1, double *y1, double *x2, double *y2,
cairo_save(xctx->cairo_save_ctx);
}
my_strncpy(filename, get_tok_value(r->prop_ptr, "image", 0), S(filename));
#if defined(HAS_LIBJPEG)
#ifdef INSPECT_IMAGE
ptr = get_tok_value(r->prop_ptr, "quality", 0);
jpeg_quality = 75;
if(ptr[0]) jpeg_quality = atoi(ptr);
#endif
#endif
/******* read image from in-memory buffer ... *******/
if(emb_ptr && emb_ptr->image) {
/* nothing to do, image is already created */
/******* ... or read PNG from image_data attribute *******/
} else if( (attr = get_tok_value(r->prop_ptr, "image_data", 0))[0] ) {
png_to_byte_closure_t closure;
size_t data_size;
if(!strncmp(attr, "/9j/", 4)) jpg = 1;
else if(!strncmp(attr, "iVBOR", 5)) jpg = 0;
else jpg = -1; /* some invalid data */
closure.buffer = base64_decode(attr, strlen(attr), &data_size);
closure.pos = 0;
closure.size = data_size; /* should not be necessary */
if(closure.buffer == NULL) {
dbg(0, "draw_image(): image creation failed\n");
return 0;
}
if(jpg == 0) {
emb_ptr->image = cairo_image_surface_create_from_png_stream(png_reader, &closure);
} else if(jpg == 1) {
#if defined(HAS_LIBJPEG)
emb_ptr->image = cairo_image_surface_create_from_jpeg_mem(closure.buffer, closure.size);
#endif
}
if(!emb_ptr->image || cairo_surface_status(emb_ptr->image) != CAIRO_STATUS_SUCCESS) {
} else if( (attr = get_tok_value(r->prop_ptr, "image_data", 0))[0] && strlen(attr) > 5) {
emb_ptr->image = get_surface_from_b64data(attr);
if(!emb_ptr->image) {
if(jpg != 1)
dbg(0, "draw_image(): failure creating image surface from \"image_data\" attribute\n");
my_free(_ALLOC_ID_, &closure.buffer);
return 0;
}
my_free(_ALLOC_ID_, &closure.buffer);
dbg(1, "draw_image(): length2 = %d\n", closure.pos);
/******* ... or read PNG from file (image attribute) *******/
} else if(filename[0] && !stat(filename, &buf)) {
png_to_byte_closure_t closure;
@ -4090,37 +4155,12 @@ int draw_image(int dr, xRect *r, double *x1, double *y1, double *x2, double *y2,
my_free(_ALLOC_ID_, &closure.buffer);
return 0;
}
/* this code can be used to transfer an image surface back to a memory buffer
* closure.buffer will hold the data, closure.pos wil be set to the size */
#ifdef INSPECT_IMAGE
inspect_image(&emb_ptr->image);
if(jpg == 0) {
/* write PNG to in-memory buffer */
my_free(_ALLOC_ID_, &closure.buffer);
closure.size = 0;
closure.pos = 0;
cairo_surface_write_to_png_stream(emb_ptr->image, png_writer, &closure);
closure.size = closure.pos;
} else if(jpg == 1) {
/* write JPG to in-memory buffer */
#if defined(HAS_LIBJPEG)
my_free(_ALLOC_ID_, &closure.buffer);
closure.size = 0;
closure.pos = 0;
cairo_image_surface_write_to_jpeg_mem(emb_ptr->image, &closure.buffer, &closure.pos, jpeg_quality);
closure.size = closure.pos;
#endif
}
#endif
/* put base64 encoded data to rect image_data attribute */
encoded_data = base64_encode((unsigned char *)closure.buffer, closure.size, &olength, 0);
my_strdup2(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "image_data", encoded_data));
my_free(_ALLOC_ID_, &encoded_data);
my_free(_ALLOC_ID_, &closure.buffer);
} else {
} else { /* no emb_ptr->image and no "image_data" attribute */
return 0;
}
ptr = get_tok_value(r->prop_ptr, "alpha", 0);

View File

@ -2032,6 +2032,53 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
else { cmd_found = 0;}
break;
case 'i': /*----------------------------------------------*/
/* image [invert|white_transp|black_transp|transp_white|transp_black|write_back]
* Apply required changes to selected images
* invert: invert colors
* white_transp: transform white to transparent color (alpha=0) after invert.
* black_transp: transform black to transparent color (alpha=0) after invert.
* transp_white: transform white to transparent color (alpha=0) after invert.
* transp_black: transform black to transparent color (alpha=0) after invert.
*/
if(!strcmp(argv[1], "image"))
{
int n, i, c;
int what = 0;
xRect *r;
if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;}
if(argc < 3) {
Tcl_SetResult(interp, "Missing arguments", TCL_STATIC);
return TCL_ERROR;
}
if(!strcmp(argv[2], "help")) {
Tcl_SetResult(interp,
"xschem image [invert|white_transp|black_transp|transp_white|transp_black|write_back]",
TCL_STATIC);
return TCL_OK;
}
for(i = 2; i < argc; i++) {
if(!strcmp(argv[i], "invert")) what |= 1;
if(!strcmp(argv[i], "white_transp")) what |= 2;
if(!strcmp(argv[i], "black_transp")) what |= 4;
if(!strcmp(argv[i], "transp_white")) what |= 8;
if(!strcmp(argv[i], "transp_black")) what |= 16;
if(!strcmp(argv[i], "write_back")) what |= 256;
}
rebuild_selected_array();
for(n=0; n < xctx->lastsel; ++n) {
if(xctx->sel_array[n].type == xRECT) {
i = xctx->sel_array[n].n;
c = xctx->sel_array[n].col;
r = &xctx->rect[c][i];
if(c == GRIDLAYER && r->flags & 1024) {
edit_image(what, &xctx->rect[c][i]);
}
}
}
draw();
Tcl_ResetResult(interp);
}
/* instance sym_name x y rot flip [prop] [n]
* Place a new instance of symbol 'sym_name' at position x,y,
* rotation and flip set to 'rot', 'flip'
@ -2040,7 +2087,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
* if 'n' is given it must be 0 on first call
* and non zero on following calls
* It is used only for efficiency reasons if placing multiple instances */
if(!strcmp(argv[1], "instance"))
else if(!strcmp(argv[1], "instance"))
{
if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;}
if(argc==7) {
@ -5330,13 +5377,11 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
del_object_table();
Tcl_ResetResult(interp);
}
/* test 2 inst text_n */
else if(argc > 2 && atoi(argv[2]) == 2) {
dbg(0, "graph_flags=%d\n", xctx->graph_flags);
Tcl_ResetResult(interp);
}
else if(argc > 2 && atoi(argv[2]) == 3) {
else if(argc > 3 && atoi(argv[2]) == 3) {
Tcl_ResetResult(interp);
}
}

View File

@ -1227,6 +1227,7 @@ extern int cli_opt_load_initfile;
extern Xschem_ctx *xctx;
/* FUNCTIONS */
extern int edit_image(int what, xRect *r);
extern int draw_image(int dr, xRect *r, double *x1, double *y1, double *x2, double *y2, int rot, int flip);
extern int filter_data(const char *din, const size_t ilen,
char **dout, size_t *olen, const char *cmd);