images (rotated,flipped as the symbol) in symbols
This commit is contained in:
parent
341e85ea08
commit
b978bd90fa
|
|
@ -490,8 +490,8 @@ void remove_symbol(int j)
|
|||
{
|
||||
int i,c;
|
||||
xSymbol save;
|
||||
dbg(1, "remove_symbol(): removing symbol %d\n", j);
|
||||
|
||||
dbg(1,"clearing symbol %s\n", xctx->sym[j].name);
|
||||
my_free(666, &xctx->sym[j].prop_ptr);
|
||||
my_free(667, &xctx->sym[j].templ);
|
||||
my_free(668, &xctx->sym[j].type);
|
||||
|
|
@ -529,6 +529,7 @@ void remove_symbol(int j)
|
|||
if(xctx->sym[j].rect[c][i].prop_ptr != NULL) {
|
||||
my_free(678, &xctx->sym[j].rect[c][i].prop_ptr);
|
||||
}
|
||||
set_rect_extraptr(0, &xctx->sym[j].rect[c][i]);
|
||||
}
|
||||
my_free(679, &xctx->sym[j].rect[c]);
|
||||
xctx->sym[j].rects[c] = 0;
|
||||
|
|
@ -589,30 +590,18 @@ int set_rect_flags(xRect *r)
|
|||
else if(strstr(flags, "graph")) f |= 1;
|
||||
}
|
||||
r->flags = f;
|
||||
dbg(1, "set_rect_flags(): flags=%d\n", f);
|
||||
return f;
|
||||
}
|
||||
|
||||
/* what:
|
||||
* 2: copy: drptr->extraptr <- srptr->extraptr
|
||||
* 1: create
|
||||
* 0: clear
|
||||
*/
|
||||
int set_rect_extraptr(int what, xRect *drptr, xRect *srptr)
|
||||
int set_rect_extraptr(int what, xRect *drptr)
|
||||
{
|
||||
#if HAS_CAIRO==1
|
||||
if(what==2) { /* copy */
|
||||
if(drptr->flags & 1024) { /* embedded image */
|
||||
xEmb_image *d, *s;
|
||||
s = srptr->extraptr;
|
||||
if(s) {
|
||||
d = my_malloc(1478, sizeof(xEmb_image));
|
||||
d->image = NULL;
|
||||
drptr->extraptr = d;
|
||||
} else {
|
||||
drptr->extraptr = NULL;
|
||||
}
|
||||
}
|
||||
} else if(what==1) { /* create */
|
||||
if(what==1) { /* create */
|
||||
if(drptr->flags & 1024) { /* embedded image */
|
||||
if(!drptr->extraptr) {
|
||||
xEmb_image *d;
|
||||
|
|
@ -676,7 +665,7 @@ void clear_drawing(void)
|
|||
for(j=0;j<xctx->rects[i];j++)
|
||||
{
|
||||
my_free(700, &xctx->rect[i][j].prop_ptr);
|
||||
set_rect_extraptr(0, &xctx->rect[i][j], NULL);
|
||||
set_rect_extraptr(0, &xctx->rect[i][j]);
|
||||
}
|
||||
for(j=0;j<xctx->arcs[i];j++)
|
||||
{
|
||||
|
|
|
|||
409
src/draw.c
409
src/draw.c
|
|
@ -351,10 +351,10 @@ void draw_symbol(int what,int c, int n,int layer,short tmp_flip, short rot,
|
|||
double x0,y0,x1,y1,x2,y2;
|
||||
double *x, *y; /* polygon point arrays */
|
||||
short flip;
|
||||
xLine line;
|
||||
xRect rect;
|
||||
xArc arc;
|
||||
xPoly polygon;
|
||||
xLine *line;
|
||||
xRect *rect;
|
||||
xArc *arc;
|
||||
xPoly *polygon;
|
||||
xText text;
|
||||
register xSymbol *symptr;
|
||||
double angle;
|
||||
|
|
@ -421,42 +421,42 @@ void draw_symbol(int what,int c, int n,int layer,short tmp_flip, short rot,
|
|||
if(!hide) {
|
||||
for(j=0;j< symptr->lines[layer];j++)
|
||||
{
|
||||
line = (symptr->line[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,line.x1,line.y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,line.x2,line.y2,x2,y2);
|
||||
line = &(symptr->line[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,line->x1,line->y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,line->x2,line->y2,x2,y2);
|
||||
ORDER(x1,y1,x2,y2);
|
||||
if(line.bus)
|
||||
drawline(c,THICK, x0+x1, y0+y1, x0+x2, y0+y2, line.dash);
|
||||
if(line->bus)
|
||||
drawline(c,THICK, x0+x1, y0+y1, x0+x2, y0+y2, line->dash);
|
||||
else
|
||||
drawline(c,what, x0+x1, y0+y1, x0+x2, y0+y2, line.dash);
|
||||
drawline(c,what, x0+x1, y0+y1, x0+x2, y0+y2, line->dash);
|
||||
}
|
||||
for(j=0;j< symptr->polygons[layer];j++)
|
||||
{
|
||||
polygon = (symptr->poly[layer])[j];
|
||||
x = my_malloc(34, sizeof(double) * polygon.points);
|
||||
y = my_malloc(35, sizeof(double) * polygon.points);
|
||||
for(k=0;k<polygon.points;k++) {
|
||||
ROTATION(rot, flip, 0.0,0.0,polygon.x[k],polygon.y[k],x[k],y[k]);
|
||||
polygon = &(symptr->poly[layer])[j];
|
||||
x = my_malloc(34, sizeof(double) * polygon->points);
|
||||
y = my_malloc(35, sizeof(double) * polygon->points);
|
||||
for(k=0;k<polygon->points;k++) {
|
||||
ROTATION(rot, flip, 0.0,0.0,polygon->x[k],polygon->y[k],x[k],y[k]);
|
||||
x[k]+= x0;
|
||||
y[k] += y0;
|
||||
}
|
||||
drawpolygon(c, NOW, x, y, polygon.points, polygon.fill, polygon.dash); /* added fill */
|
||||
drawpolygon(c, NOW, x, y, polygon->points, polygon->fill, polygon->dash); /* added fill */
|
||||
my_free(718, &x);
|
||||
my_free(719, &y);
|
||||
}
|
||||
for(j=0;j< symptr->arcs[layer];j++)
|
||||
{
|
||||
|
||||
arc = (symptr->arc[layer])[j];
|
||||
arc = &(symptr->arc[layer])[j];
|
||||
if(flip) {
|
||||
angle = 270.*rot+180.-arc.b-arc.a;
|
||||
angle = 270.*rot+180.-arc->b-arc->a;
|
||||
} else {
|
||||
angle = arc.a+rot*270.;
|
||||
angle = arc->a+rot*270.;
|
||||
}
|
||||
angle = fmod(angle, 360.);
|
||||
if(angle<0.) angle+=360.;
|
||||
ROTATION(rot, flip, 0.0,0.0,arc.x,arc.y,x1,y1);
|
||||
drawarc(c,what, x0+x1, y0+y1, arc.r, angle, arc.b, arc.fill, arc.dash);
|
||||
ROTATION(rot, flip, 0.0,0.0,arc->x,arc->y,x1,y1);
|
||||
drawarc(c,what, x0+x1, y0+y1, arc->r, angle, arc->b, arc->fill, arc->dash);
|
||||
}
|
||||
} /* if(!hide) */
|
||||
|
||||
|
|
@ -464,12 +464,23 @@ void draw_symbol(int what,int c, int n,int layer,short tmp_flip, short rot,
|
|||
(hide && layer == PINLAYER && xctx->enable_layer[layer]) ) {
|
||||
for(j=0;j< symptr->rects[layer];j++)
|
||||
{
|
||||
rect = (symptr->rect[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,rect.x1,rect.y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,rect.x2,rect.y2,x2,y2);
|
||||
RECTORDER(x1,y1,x2,y2);
|
||||
drawrect(c,what, x0+x1, y0+y1, x0+x2, y0+y2, rect.dash);
|
||||
filledrect(c,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
||||
rect = &(symptr->rect[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,rect->x1,rect->y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,rect->x2,rect->y2,x2,y2);
|
||||
#if HAS_CAIRO == 1
|
||||
if(layer == GRIDLAYER && rect->flags & 1024) {
|
||||
double xx1 = x0 + x1;
|
||||
double yy1 = y0 + y1;
|
||||
double xx2 = x0 + x2;
|
||||
double yy2 = y0 + y2;
|
||||
draw_image(1, rect, &xx1, &yy1, &xx2, &yy2, rot, flip);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
RECTORDER(x1,y1,x2,y2);
|
||||
drawrect(c,what, x0+x1, y0+y1, x0+x2, y0+y2, rect->dash);
|
||||
filledrect(c,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( (layer==TEXTWIRELAYER && !(xctx->inst[n].flags&2) ) ||
|
||||
|
|
@ -535,10 +546,10 @@ void draw_temp_symbol(int what, GC gc, int n,int layer,short tmp_flip, short rot
|
|||
int j;
|
||||
double x0,y0,x1,y1,x2,y2;
|
||||
short flip;
|
||||
xLine line;
|
||||
xPoly polygon;
|
||||
xRect rect;
|
||||
xArc arc;
|
||||
xLine *line;
|
||||
xPoly *polygon;
|
||||
xRect *rect;
|
||||
xArc *arc;
|
||||
xText text;
|
||||
register xSymbol *symptr;
|
||||
double angle;
|
||||
|
|
@ -582,29 +593,29 @@ void draw_temp_symbol(int what, GC gc, int n,int layer,short tmp_flip, short rot
|
|||
symptr = (xctx->inst[n].ptr+ xctx->sym);
|
||||
for(j=0;j< symptr->lines[layer];j++)
|
||||
{
|
||||
line = (symptr->line[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,line.x1,line.y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,line.x2,line.y2,x2,y2);
|
||||
line = &(symptr->line[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,line->x1,line->y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,line->x2,line->y2,x2,y2);
|
||||
ORDER(x1,y1,x2,y2);
|
||||
if(line.bus)
|
||||
if(line->bus)
|
||||
drawtempline(gc,THICK, x0+x1, y0+y1, x0+x2, y0+y2);
|
||||
else
|
||||
drawtempline(gc,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
||||
}
|
||||
for(j=0;j< symptr->polygons[layer];j++)
|
||||
{
|
||||
polygon = (symptr->poly[layer])[j];
|
||||
polygon = &(symptr->poly[layer])[j];
|
||||
|
||||
{ /* scope block so we declare some auxiliary arrays for coord transforms. 20171115 */
|
||||
int k;
|
||||
double *x = my_malloc(36, sizeof(double) * polygon.points);
|
||||
double *y = my_malloc(37, sizeof(double) * polygon.points);
|
||||
for(k=0;k<polygon.points;k++) {
|
||||
ROTATION(rot, flip, 0.0,0.0,polygon.x[k],polygon.y[k],x[k],y[k]);
|
||||
double *x = my_malloc(36, sizeof(double) * polygon->points);
|
||||
double *y = my_malloc(37, sizeof(double) * polygon->points);
|
||||
for(k=0;k<polygon->points;k++) {
|
||||
ROTATION(rot, flip, 0.0,0.0,polygon->x[k],polygon->y[k],x[k],y[k]);
|
||||
x[k] += x0;
|
||||
y[k] += y0;
|
||||
}
|
||||
drawtemppolygon(gc, NOW, x, y, polygon.points);
|
||||
drawtemppolygon(gc, NOW, x, y, polygon->points);
|
||||
my_free(720, &x);
|
||||
my_free(721, &y);
|
||||
}
|
||||
|
|
@ -612,24 +623,24 @@ void draw_temp_symbol(int what, GC gc, int n,int layer,short tmp_flip, short rot
|
|||
|
||||
for(j=0;j< symptr->rects[layer];j++)
|
||||
{
|
||||
rect = (symptr->rect[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,rect.x1,rect.y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,rect.x2,rect.y2,x2,y2);
|
||||
rect = &(symptr->rect[layer])[j];
|
||||
ROTATION(rot, flip, 0.0,0.0,rect->x1,rect->y1,x1,y1);
|
||||
ROTATION(rot, flip, 0.0,0.0,rect->x2,rect->y2,x2,y2);
|
||||
RECTORDER(x1,y1,x2,y2);
|
||||
drawtemprect(gc,what, x0+x1, y0+y1, x0+x2, y0+y2);
|
||||
}
|
||||
for(j=0;j< symptr->arcs[layer];j++)
|
||||
{
|
||||
arc = (symptr->arc[layer])[j];
|
||||
arc = &(symptr->arc[layer])[j];
|
||||
if(flip) {
|
||||
angle = 270.*rot+180.-arc.b-arc.a;
|
||||
angle = 270.*rot+180.-arc->b-arc->a;
|
||||
} else {
|
||||
angle = arc.a+rot*270.;
|
||||
angle = arc->a+rot*270.;
|
||||
}
|
||||
angle = fmod(angle, 360.);
|
||||
if(angle<0.) angle+=360.;
|
||||
ROTATION(rot, flip, 0.0,0.0,arc.x,arc.y,x1,y1);
|
||||
drawtemparc(gc, what, x0+x1, y0+y1, arc.r, angle, arc.b);
|
||||
ROTATION(rot, flip, 0.0,0.0,arc->x,arc->y,x1,y1);
|
||||
drawtemparc(gc, what, x0+x1, y0+y1, arc->r, angle, arc->b);
|
||||
}
|
||||
|
||||
if(layer==PROPERTYLAYER && xctx->sym_txt)
|
||||
|
|
@ -2413,161 +2424,177 @@ static cairo_status_t png_writer(void *in_closure, const unsigned char *in_data,
|
|||
}
|
||||
#endif
|
||||
|
||||
int draw_images_all(void)
|
||||
|
||||
/* rot and flip for rotated / flipped symbols
|
||||
* draw: 1 draw image
|
||||
* 0 only load image and build base64
|
||||
*/
|
||||
void draw_image(int draw, xRect *r, double *x1, double *y1, double *x2, double *y2, int rot, int flip)
|
||||
{
|
||||
#if HAS_CAIRO==1
|
||||
int ret = 0, i, w, h;
|
||||
#if HAS_CAIRO == 1
|
||||
const char *ptr;
|
||||
int w,h;
|
||||
double x, y, rw, rh;
|
||||
double sx1, sy1, sx2, sy2, alpha;
|
||||
const char *ptr;
|
||||
char filename[PATH_MAX];
|
||||
struct stat buf;
|
||||
const char *attr ;
|
||||
char *filter = NULL;
|
||||
double xx1, yy1, scalex, scaley;
|
||||
png_to_byte_closure_t closure;
|
||||
xEmb_image *emb_ptr;
|
||||
|
||||
xx1 = *x1; yy1 = *y1; /* image anchor point */
|
||||
RECTORDER(*x1, *y1, *x2, *y2);
|
||||
|
||||
/* screen position */
|
||||
sx1=X_TO_SCREEN(*x1);
|
||||
sy1=Y_TO_SCREEN(*y1);
|
||||
sx2=X_TO_SCREEN(*x2);
|
||||
sy2=Y_TO_SCREEN(*y2);
|
||||
if(RECT_OUTSIDE(sx1, sy1, sx2, sy2,
|
||||
xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) return;
|
||||
set_rect_extraptr(1, r); /* create r->extraptr pointing to a xEmb_image struct */
|
||||
emb_ptr = r->extraptr;
|
||||
if(draw) {
|
||||
cairo_save(xctx->cairo_ctx);
|
||||
cairo_save(xctx->cairo_save_ctx);
|
||||
}
|
||||
my_strncpy(filename, get_tok_value(r->prop_ptr, "image", 0), S(filename));
|
||||
my_strdup(1484, &filter, get_tok_value(r->prop_ptr, "filter", 0));
|
||||
|
||||
/* read PNG 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] ) {
|
||||
size_t data_size;
|
||||
if(filter) {
|
||||
size_t filtersize = 0;
|
||||
char *filterdata = NULL;
|
||||
closure.buffer = NULL;
|
||||
filterdata = (char *)base64_decode(attr, strlen(attr), &filtersize);
|
||||
filter_data(filterdata, filtersize, (char **)&closure.buffer, &data_size, filter);
|
||||
my_free(1488, &filterdata);
|
||||
my_free(1485, &filter);
|
||||
} else {
|
||||
closure.buffer = base64_decode(attr, strlen(attr), &data_size);
|
||||
}
|
||||
closure.pos = 0;
|
||||
closure.size = data_size; /* should not be necessary */
|
||||
emb_ptr->image = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
||||
if(closure.buffer == NULL) dbg(0, "draw_images_all(): image creation failed\n");
|
||||
my_free(1467, &closure.buffer);
|
||||
dbg(1, "draw_images_all(): length2 = %d\n", closure.pos);
|
||||
/* ... or read PNG from file (image attribute) */
|
||||
} else if(filename[0] && !stat(filename, &buf)) {
|
||||
char *image_data = NULL;
|
||||
size_t olength;
|
||||
if(filter) {
|
||||
size_t filtersize = 0;
|
||||
char *filterdata = NULL;
|
||||
size_t pngsize = 0;
|
||||
char *pngdata = NULL;
|
||||
FILE *fd;
|
||||
filtersize = buf.st_size;
|
||||
if(filtersize) {
|
||||
fd = fopen(filename, "r");
|
||||
if(fd) {
|
||||
filterdata = my_malloc(1490, filtersize);
|
||||
fread(filterdata, filtersize, 1, fd);
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
filter_data(filterdata, filtersize, &pngdata, &pngsize, filter);
|
||||
closure.buffer = (unsigned char *)pngdata;
|
||||
closure.size = pngsize;
|
||||
closure.pos = 0;
|
||||
emb_ptr->image = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
||||
image_data = base64_encode((unsigned char *)filterdata, filtersize, &olength, 0);
|
||||
my_free(1489, &filterdata);
|
||||
my_free(1487, &filter);
|
||||
} else {
|
||||
closure.buffer = NULL;
|
||||
closure.size = 0;
|
||||
closure.pos = 0;
|
||||
emb_ptr->image = cairo_image_surface_create_from_png(filename);
|
||||
/* write PNG to in-memory buffer */
|
||||
cairo_surface_write_to_png_stream(emb_ptr->image, png_writer, &closure);
|
||||
image_data = base64_encode(closure.buffer, closure.pos, &olength, 0);
|
||||
my_free(1468, &closure.buffer);
|
||||
}
|
||||
/* put base64 encoded data to rect image_data attrinute */
|
||||
my_strdup2(1473, &r->prop_ptr, subst_token(r->prop_ptr, "image_data", image_data));
|
||||
my_free(1474, &image_data);
|
||||
if(cairo_surface_status(emb_ptr->image) != CAIRO_STATUS_SUCCESS) return;
|
||||
dbg(1, "draw_image(): length3 = %d\n", closure.pos);
|
||||
} else return;
|
||||
if(cairo_surface_status(emb_ptr->image) != CAIRO_STATUS_SUCCESS) return;
|
||||
ptr = get_tok_value(r->prop_ptr, "alpha", 0);
|
||||
alpha = 1.0;
|
||||
if(ptr[0]) alpha = atof(ptr);
|
||||
w = cairo_image_surface_get_width (emb_ptr->image);
|
||||
h = cairo_image_surface_get_height (emb_ptr->image);
|
||||
dbg(1, "draw_image() w=%d, h=%d\n", w, h);
|
||||
x = X_TO_SCREEN(xx1);
|
||||
y = Y_TO_SCREEN(yy1);
|
||||
if(r->flags & 2048) { /* resize container rectangle to fit image */
|
||||
*x2 = *x1 + w;
|
||||
*y2 = *y1 + h;
|
||||
scalex = xctx->mooz;
|
||||
scaley = xctx->mooz;
|
||||
} else { /* resize image to fit in rectangle */
|
||||
rw = abs(*x2 - *x1);
|
||||
rh = abs(*y2 - *y1);
|
||||
scalex = rw/w * xctx->mooz;
|
||||
scaley = rh/h * xctx->mooz;
|
||||
}
|
||||
if(draw && xctx->draw_pixmap) {
|
||||
cairo_translate(xctx->cairo_save_ctx, x, y);
|
||||
if(flip && (rot == 0 || rot == 2)) cairo_scale(xctx->cairo_save_ctx, -scalex, scaley);
|
||||
else if(flip && (rot == 1 || rot == 3)) cairo_scale(xctx->cairo_save_ctx, scalex, -scaley);
|
||||
else cairo_scale(xctx->cairo_save_ctx, scalex, scaley);
|
||||
cairo_rotate(xctx->cairo_save_ctx, rot * XSCH_PI * 0.5);
|
||||
cairo_set_source_surface(xctx->cairo_save_ctx, emb_ptr->image, 0. , 0.);
|
||||
cairo_rectangle(xctx->cairo_save_ctx, 0, 0, w , h );
|
||||
/* cairo_fill(xctx->cairo_save_ctx);
|
||||
* cairo_stroke(xctx->cairo_save_ctx); */
|
||||
cairo_clip(xctx->cairo_save_ctx);
|
||||
cairo_paint_with_alpha(xctx->cairo_save_ctx, alpha);
|
||||
}
|
||||
if(draw && xctx->draw_window) {
|
||||
cairo_translate(xctx->cairo_ctx, x, y);
|
||||
if(flip && (rot == 0 || rot == 2)) cairo_scale(xctx->cairo_ctx, -scalex, scaley);
|
||||
else if(flip && (rot == 1 || rot == 3)) cairo_scale(xctx->cairo_ctx, scalex, -scaley);
|
||||
else cairo_scale(xctx->cairo_ctx, scalex, scaley);
|
||||
cairo_rotate(xctx->cairo_ctx, rot * XSCH_PI * 0.5);
|
||||
cairo_set_source_surface(xctx->cairo_ctx, emb_ptr->image, 0. , 0.);
|
||||
cairo_rectangle(xctx->cairo_ctx, 0, 0, w , h );
|
||||
/* cairo_fill(xctx->cairo_ctx);
|
||||
* cairo_stroke(xctx->cairo_ctx); */
|
||||
cairo_clip(xctx->cairo_ctx);
|
||||
cairo_paint_with_alpha(xctx->cairo_ctx, alpha);
|
||||
}
|
||||
if(draw) {
|
||||
cairo_restore(xctx->cairo_ctx);
|
||||
cairo_restore(xctx->cairo_save_ctx);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void draw_images_all(void)
|
||||
{
|
||||
#if HAS_CAIRO==1
|
||||
int i;
|
||||
if(xctx->draw_single_layer==-1 || GRIDLAYER == xctx->draw_single_layer) {
|
||||
if(xctx->enable_layer[GRIDLAYER]) for(i = 0; i < xctx->rects[GRIDLAYER]; i++) {
|
||||
xRect *r = &xctx->rect[GRIDLAYER][i];
|
||||
if(r->flags & 1024) {
|
||||
struct stat buf;
|
||||
const char *attr;
|
||||
double scalex, scaley;
|
||||
char *filter = NULL;
|
||||
png_to_byte_closure_t closure;
|
||||
xEmb_image *emb_ptr;
|
||||
|
||||
my_strncpy(filename, get_tok_value(r->prop_ptr, "image", 0), S(filename));
|
||||
my_strdup(1484, &filter, get_tok_value(r->prop_ptr, "filter", 0));
|
||||
/* screen position */
|
||||
sx1=X_TO_SCREEN(r->x1);
|
||||
sy1=Y_TO_SCREEN(r->y1);
|
||||
sx2=X_TO_SCREEN(r->x2);
|
||||
sy2=Y_TO_SCREEN(r->y2);
|
||||
if(RECT_OUTSIDE(sx1, sy1, sx2, sy2,
|
||||
xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) continue;
|
||||
if(!r->extraptr) {
|
||||
set_rect_extraptr(1, r, NULL);
|
||||
}
|
||||
emb_ptr = r->extraptr;
|
||||
cairo_save(xctx->cairo_ctx);
|
||||
cairo_save(xctx->cairo_save_ctx);
|
||||
|
||||
/* read PNG 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] ) {
|
||||
size_t data_size;
|
||||
if(filter) {
|
||||
size_t filtersize = 0;
|
||||
char *filterdata = NULL;
|
||||
closure.buffer = NULL;
|
||||
filterdata = (char *)base64_decode(attr, strlen(attr), &filtersize);
|
||||
filter_data(filterdata, filtersize, (char **)&closure.buffer, &data_size, filter);
|
||||
my_free(1488, &filterdata);
|
||||
} else {
|
||||
closure.buffer = base64_decode(attr, strlen(attr), &data_size);
|
||||
}
|
||||
closure.pos = 0;
|
||||
closure.size = data_size; /* should not be necessary */
|
||||
emb_ptr->image = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
||||
if(closure.buffer == NULL) dbg(0, "draw_images_all(): image creation failed, n=%d\n", i);
|
||||
my_free(1467, &closure.buffer);
|
||||
dbg(1, "draw_images_all(): length2 = %d\n", closure.pos);
|
||||
/* ... or read PNG from file (image attribute) */
|
||||
} else if(filename[0] && !stat(filename, &buf)) {
|
||||
char *image_data = NULL;
|
||||
size_t olength;
|
||||
if(filter) {
|
||||
size_t filtersize = 0;
|
||||
char *filterdata = NULL;
|
||||
size_t pngsize = 0;
|
||||
char *pngdata = NULL;
|
||||
struct stat st;
|
||||
if(stat(filename, &st) == 0 /* && ( (st.st_mode & S_IFMT) == S_IFREG)*/ ) {
|
||||
FILE *fd;
|
||||
filtersize = st.st_size;
|
||||
if(filtersize) {
|
||||
fd = fopen(filename, "r");
|
||||
if(fd) {
|
||||
filterdata = my_malloc(1490, filtersize);
|
||||
fread(filterdata, filtersize, 1, fd);
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
filter_data(filterdata, filtersize, &pngdata, &pngsize, filter);
|
||||
closure.buffer = (unsigned char *)pngdata;
|
||||
closure.size = pngsize;
|
||||
closure.pos = 0;
|
||||
emb_ptr->image = cairo_image_surface_create_from_png_stream(png_reader, &closure);
|
||||
image_data = base64_encode((unsigned char *)filterdata, filtersize, &olength, 0);
|
||||
my_free(1489, &filterdata);
|
||||
} else {
|
||||
closure.buffer = NULL;
|
||||
closure.size = 0;
|
||||
closure.pos = 0;
|
||||
emb_ptr->image = cairo_image_surface_create_from_png(filename);
|
||||
/* write PNG to in-memory buffer */
|
||||
cairo_surface_write_to_png_stream(emb_ptr->image, png_writer, &closure);
|
||||
image_data = base64_encode(closure.buffer, closure.pos, &olength, 0);
|
||||
}
|
||||
my_free(1468, &closure.buffer);
|
||||
/* put base64 encoded data to rect image_data attrinute */
|
||||
my_strdup2(1473, &r->prop_ptr, subst_token(r->prop_ptr, "image_data", image_data));
|
||||
my_free(1474, &image_data);
|
||||
|
||||
|
||||
if(cairo_surface_status(emb_ptr->image) != CAIRO_STATUS_SUCCESS) {ret = 1; continue;}
|
||||
dbg(1, "draw_images_all(): length3 = %d\n", closure.pos);
|
||||
} else {
|
||||
{ret = 1; continue;}
|
||||
}
|
||||
if(cairo_surface_status(emb_ptr->image) != CAIRO_STATUS_SUCCESS) {ret = 1; continue;}
|
||||
ptr = get_tok_value(r->prop_ptr, "alpha", 0);
|
||||
alpha = 1.0;
|
||||
if(ptr[0]) alpha = atof(ptr);
|
||||
w = cairo_image_surface_get_width (emb_ptr->image);
|
||||
h = cairo_image_surface_get_height (emb_ptr->image);
|
||||
dbg(1, "draw_images_all() w=%d, h=%d\n", w, h);
|
||||
x = X_TO_SCREEN(r->x1);
|
||||
y = Y_TO_SCREEN(r->y1);
|
||||
if(r->flags & 2048) { /* resize container rectangle to fit image */
|
||||
r->x2 = r->x1 + w;
|
||||
r->y2 = r->y1 + h;
|
||||
scalex = xctx->mooz;
|
||||
scaley = xctx->mooz;
|
||||
} else { /* resize image to fit in rectangle */
|
||||
rw = abs(r->x2 - r->x1);
|
||||
rh = abs(r->y2 - r->y1);
|
||||
scalex = rw/w * xctx->mooz;
|
||||
scaley = rh/h * xctx->mooz;
|
||||
}
|
||||
if(xctx->draw_pixmap) {
|
||||
cairo_translate(xctx->cairo_save_ctx, x, y);
|
||||
cairo_scale(xctx->cairo_save_ctx, scalex, scaley);
|
||||
cairo_set_source_surface(xctx->cairo_save_ctx, emb_ptr->image, 0. , 0.);
|
||||
cairo_rectangle(xctx->cairo_save_ctx, 0, 0, w , h );
|
||||
/* cairo_fill(xctx->cairo_save_ctx);
|
||||
* cairo_stroke(xctx->cairo_save_ctx); */
|
||||
cairo_clip(xctx->cairo_save_ctx);
|
||||
cairo_paint_with_alpha(xctx->cairo_save_ctx, alpha);
|
||||
}
|
||||
if(xctx->draw_window) {
|
||||
cairo_translate(xctx->cairo_ctx, x, y);
|
||||
cairo_scale(xctx->cairo_ctx, scalex, scaley);
|
||||
cairo_set_source_surface(xctx->cairo_ctx, emb_ptr->image, 0. , 0.);
|
||||
cairo_rectangle(xctx->cairo_ctx, 0, 0, w , h );
|
||||
/* cairo_fill(xctx->cairo_ctx);
|
||||
* cairo_stroke(xctx->cairo_ctx); */
|
||||
cairo_clip(xctx->cairo_ctx);
|
||||
cairo_paint_with_alpha(xctx->cairo_ctx, alpha);
|
||||
}
|
||||
cairo_restore(xctx->cairo_ctx);
|
||||
cairo_restore(xctx->cairo_save_ctx);
|
||||
my_free(1486, &filter);
|
||||
draw_image(1, r, &r->x1, &r->y1, &r->x2, &r->y2, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
void draw(void)
|
||||
|
|
|
|||
|
|
@ -293,7 +293,8 @@ void *my_calloc(int id, size_t nmemb, size_t size)
|
|||
void *ptr;
|
||||
if(size*nmemb > 0) {
|
||||
ptr=calloc(nmemb, size);
|
||||
if(ptr == NULL) fprintf(errfp,"my_calloc(%d,): allocation failure\n", id);
|
||||
if(ptr == NULL)
|
||||
fprintf(errfp,"my_calloc(%d,): allocation failure %ld * %ld bytes\n", id, nmemb, size);
|
||||
dbg(3, "\nmy_calloc(%d,): allocating %p , %lu bytes\n",
|
||||
id, ptr, (unsigned long) (size*nmemb));
|
||||
}
|
||||
|
|
@ -306,7 +307,7 @@ void *my_malloc(int id, size_t size)
|
|||
void *ptr;
|
||||
if(size>0) {
|
||||
ptr=malloc(size);
|
||||
if(ptr == NULL) fprintf(errfp,"my_malloc(%d,): allocation failure\n", id);
|
||||
if(ptr == NULL) fprintf(errfp,"my_malloc(%d,): allocation failure for %ld bytes\n", id, size);
|
||||
dbg(3, "\nmy_malloc(%d,): allocating %p , %lu bytes\n", id, ptr, (unsigned long) size);
|
||||
}
|
||||
else ptr=NULL;
|
||||
|
|
@ -323,7 +324,7 @@ void my_realloc(int id, void *ptr,size_t size)
|
|||
*(void **)ptr=NULL;
|
||||
} else {
|
||||
*(void **)ptr=realloc(*(void **)ptr,size);
|
||||
if(*(void **)ptr == NULL) fprintf(errfp,"my_realloc(%d,): allocation failure\n", id);
|
||||
if(*(void **)ptr == NULL) fprintf(errfp,"my_realloc(%d,): allocation failure for %ld bytes\n", id, size);
|
||||
dbg(3, "\nmy_realloc(%d,): reallocating %p --> %p to %lu bytes\n",
|
||||
id, a, *(void **)ptr,(unsigned long) size);
|
||||
}
|
||||
|
|
@ -404,9 +405,6 @@ static void edit_rect_property(int x)
|
|||
} else {
|
||||
tclsetvar("retval","");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(x==0) {
|
||||
xctx->semaphore++;
|
||||
tcleval("text_line {Input property:} 0 normal");
|
||||
|
|
@ -435,9 +433,7 @@ static void edit_rect_property(int x)
|
|||
}
|
||||
set_rect_flags(&xctx->rect[c][n]); /* set cached .flags bitmask from on attributes */
|
||||
|
||||
if(xctx->rect[c][n].extraptr) { /* used for images, clear so will be recreated from image attr */
|
||||
set_rect_extraptr(0, &xctx->rect[c][n], NULL);
|
||||
}
|
||||
set_rect_extraptr(0, &xctx->rect[c][n]);
|
||||
dash = get_tok_value(xctx->rect[c][n].prop_ptr,"dash",0);
|
||||
if( strcmp(dash, "") ) {
|
||||
int d = atoi(dash);
|
||||
|
|
@ -450,6 +446,8 @@ static void edit_rect_property(int x)
|
|||
bbox(START,0.0,0.0,0.0,0.0);
|
||||
drw = 1;
|
||||
}
|
||||
draw_image(0, &xctx->rect[c][n],
|
||||
&xctx->rect[c][n].x1, &xctx->rect[c][n].y1, &xctx->rect[c][n].x2, &xctx->rect[c][n].y2, 0, 0);
|
||||
bbox(ADD, xctx->rect[c][n].x1, xctx->rect[c][n].y1, xctx->rect[c][n].x2, xctx->rect[c][n].y2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,10 +306,8 @@ void mem_push_undo(void)
|
|||
for(i = 0;i<xctx->rects[c];i++) {
|
||||
xctx->uslot[slot].bptr[c][i] = xctx->rect[c][i];
|
||||
xctx->uslot[slot].bptr[c][i].prop_ptr = NULL;
|
||||
xctx->uslot[slot].bptr[c][i].extraptr = NULL;
|
||||
my_strdup(185, &xctx->uslot[slot].bptr[c][i].prop_ptr, xctx->rect[c][i].prop_ptr);
|
||||
if(xctx->rect[c][i].extraptr) {
|
||||
set_rect_extraptr(2, &xctx->uslot[slot].bptr[c][i], &xctx->rect[c][i]);
|
||||
}
|
||||
}
|
||||
/* arcs */
|
||||
for(i = 0;i<xctx->arcs[c];i++) {
|
||||
|
|
@ -385,6 +383,7 @@ void mem_push_undo(void)
|
|||
for(j = 0; j < xctx->sym[i].rects[c]; j++) {
|
||||
sym->rect[c][j] = xctx->sym[i].rect[c][j];
|
||||
sym->rect[c][j].prop_ptr = NULL;
|
||||
sym->rect[c][j].extraptr = NULL;
|
||||
my_strdup(1362, &sym->rect[c][j].prop_ptr, xctx->sym[i].rect[c][j].prop_ptr);
|
||||
}
|
||||
/* symbol arcs */
|
||||
|
|
@ -531,10 +530,8 @@ void mem_pop_undo(int redo, int set_modify_status)
|
|||
for(i = 0;i<xctx->rects[c];i++) {
|
||||
xctx->rect[c][i] = xctx->uslot[slot].bptr[c][i];
|
||||
xctx->rect[c][i].prop_ptr = NULL;
|
||||
xctx->rect[c][i].extraptr = NULL;
|
||||
my_strdup(205, &xctx->rect[c][i].prop_ptr, xctx->uslot[slot].bptr[c][i].prop_ptr);
|
||||
if(xctx->uslot[slot].bptr[c][i].extraptr) {
|
||||
set_rect_extraptr(2, &xctx->rect[c][i], &xctx->uslot[slot].bptr[c][i]);
|
||||
}
|
||||
}
|
||||
/* arcs */
|
||||
xctx->maxa[c] = xctx->arcs[c] = xctx->uslot[slot].arcs[c];
|
||||
|
|
@ -621,6 +618,7 @@ void mem_pop_undo(int redo, int set_modify_status)
|
|||
for(j = 0; j < xctx->sym[i].rects[c]; j++) {
|
||||
xctx->sym[i].rect[c][j] = sym->rect[c][j];
|
||||
xctx->sym[i].rect[c][j].prop_ptr = NULL;
|
||||
xctx->sym[i].rect[c][j].extraptr = NULL;
|
||||
my_strdup(1369, & xctx->sym[i].rect[c][j].prop_ptr, sym->rect[c][j].prop_ptr);
|
||||
}
|
||||
/* symbol arcs */
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ void check_collapsing_objects()
|
|||
if(xctx->rect[c][i].x1==xctx->rect[c][i].x2 || xctx->rect[c][i].y1 == xctx->rect[c][i].y2)
|
||||
{
|
||||
my_free(815, &xctx->rect[c][i].prop_ptr);
|
||||
set_rect_extraptr(0, &xctx->rect[c][i], NULL);
|
||||
set_rect_extraptr(0, &xctx->rect[c][i]);
|
||||
found=1;
|
||||
j++;
|
||||
continue;
|
||||
|
|
@ -870,12 +870,10 @@ void copy_objects(int what)
|
|||
RECTORDER(xctx->rx1,xctx->ry1,xctx->rx2,xctx->ry2);
|
||||
xctx->rect[c][n].sel=0;
|
||||
xctx->sel_array[i].n=xctx->rects[c];
|
||||
/* following also clears extraptr */
|
||||
storeobject(-1, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay,
|
||||
xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay,xRECT, c, SELECTED, xctx->rect[c][n].prop_ptr);
|
||||
l = xctx->rects[c] - 1;
|
||||
if(xctx->rect[c][n].extraptr) {
|
||||
set_rect_extraptr(2, &xctx->rect[c][l], &xctx->rect[c][n]);
|
||||
}
|
||||
bbox(ADD, xctx->rect[c][l].x1, xctx->rect[c][l].y1, xctx->rect[c][l].x2, xctx->rect[c][l].y2);
|
||||
break;
|
||||
|
||||
|
|
|
|||
33
src/save.c
33
src/save.c
|
|
@ -37,13 +37,15 @@ int filter_data(const char *din, const size_t ilen,
|
|||
int p2[2]; /* child -> parent, 0: read, 1: write */
|
||||
int ret = 0;
|
||||
pid_t pid;
|
||||
size_t bufsize = 1024, oalloc = 0, n = 0;
|
||||
size_t bufsize = 32768, oalloc = 0, n = 0;
|
||||
|
||||
if(!din || !ilen || !cmd) { /* basic check */
|
||||
*dout = NULL;
|
||||
*olen = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbg(1, "filter_data(): ilen=%ld, cmd=%s\n", ilen, cmd);
|
||||
pipe(p1);
|
||||
pipe(p2);
|
||||
signal(SIGPIPE, SIG_IGN); /* so attempting write/read a broken pipe won't kill program */
|
||||
|
|
@ -73,22 +75,19 @@ int filter_data(const char *din, const size_t ilen,
|
|||
}
|
||||
close(p1[1]);
|
||||
if(!ret) {
|
||||
if((*dout = my_malloc(1480, bufsize))) {
|
||||
oalloc = bufsize + 1; /* add extra space for final '\0' */
|
||||
*olen = 0;
|
||||
while( (n = read(p2[0], *dout + *olen, bufsize)) > 0) {
|
||||
*olen += n;
|
||||
if(*olen + bufsize + 1 >= oalloc) { /* allocate for next read */
|
||||
bufsize = (((bufsize << 2) + bufsize) >> 2); /* bufsize *= 1.25 */
|
||||
oalloc = *olen + bufsize + 1; /* add extra space for final '\0' */
|
||||
my_realloc(1482, dout, oalloc);
|
||||
}
|
||||
oalloc = bufsize + 1; /* add extra space for final '\0' */
|
||||
*dout = my_malloc(1480, oalloc);
|
||||
*olen = 0;
|
||||
while( (n = read(p2[0], *dout + *olen, bufsize)) > 0) {
|
||||
*olen += n;
|
||||
if(*olen + bufsize + 1 >= oalloc) { /* allocate for next read */
|
||||
oalloc = *olen + bufsize + 1; /* add extra space for final '\0' */
|
||||
oalloc = ((oalloc << 2) + oalloc) >> 2; /* size up 1.25x */
|
||||
dbg(1, "filter_data() read %ld bytes, reallocate dout to %ld bytes, bufsize=%ld\n", n, oalloc, bufsize);
|
||||
my_realloc(1482, dout, oalloc);
|
||||
}
|
||||
if(*olen) (*dout)[*olen] = '\0'; /* so (if ascii) it can be used as a string */
|
||||
} else {
|
||||
fprintf(stderr, "filter_data(): malloc error for read buffer\n");
|
||||
ret = 1;
|
||||
}
|
||||
if(*olen) (*dout)[*olen] = '\0'; /* so (if ascii) it can be used as a string */
|
||||
}
|
||||
close(p2[0]);
|
||||
if(n < 0 || !*olen) {
|
||||
|
|
@ -1992,6 +1991,8 @@ static void add_pinlayer_boxes(int *lastr, xRect **bb,
|
|||
my_snprintf(pin_label, save, "name=%s dir=inout ", label);
|
||||
}
|
||||
my_strdup(463, &bb[PINLAYER][i].prop_ptr, pin_label);
|
||||
bb[PINLAYER][i].flags = 0;
|
||||
bb[PINLAYER][i].extraptr = 0;
|
||||
bb[PINLAYER][i].dash = 0;
|
||||
bb[PINLAYER][i].sel = 0;
|
||||
/* add to symbol pins remaining attributes from schematic pins, except name= and lab= */
|
||||
|
|
@ -2416,6 +2417,8 @@ int load_sym_def(const char *name, FILE *embed_fd)
|
|||
} else
|
||||
bb[c][i].dash = 0;
|
||||
bb[c][i].sel = 0;
|
||||
bb[c][i].extraptr = NULL;
|
||||
set_rect_flags(&bb[c][i]);
|
||||
lastr[c]++;
|
||||
break;
|
||||
case 'T':
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ static void del_rect_line_arc_poly(void)
|
|||
j++;
|
||||
bbox(ADD, xctx->rect[c][i].x1, xctx->rect[c][i].y1, xctx->rect[c][i].x2, xctx->rect[c][i].y2);
|
||||
my_free(928, &xctx->rect[c][i].prop_ptr);
|
||||
set_rect_extraptr(0, &xctx->rect[c][i], NULL);
|
||||
set_rect_extraptr(0, &xctx->rect[c][i]);
|
||||
set_modify(1);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1000,6 +1000,7 @@ extern int cli_opt_load_initfile;
|
|||
extern Xschem_ctx *xctx;
|
||||
|
||||
/* FUNCTIONS */
|
||||
extern void draw_image(int draw, 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);
|
||||
extern int embed_rawfile(const char *rawfile);
|
||||
|
|
@ -1007,7 +1008,7 @@ extern int read_rawfile_from_attr(const char *b64s, size_t length);
|
|||
extern int read_embedded_rawfile(void);
|
||||
extern char *base64_from_file(const char *f, size_t *length);
|
||||
extern int set_rect_flags(xRect *r);
|
||||
extern int set_rect_extraptr(int what, xRect *drptr, xRect *srptr);
|
||||
extern int set_rect_extraptr(int what, xRect *drptr);
|
||||
extern unsigned char *base64_decode(const char *data, const size_t input_length, size_t *output_length);
|
||||
extern char *base64_encode(const unsigned char *data, const size_t input_length, size_t *output_length, int brk);
|
||||
extern int get_raw_index(const char *node);
|
||||
|
|
@ -1017,7 +1018,6 @@ extern double get_raw_value(int dataset, int idx, int point);
|
|||
extern int schematic_waves_loaded(void);
|
||||
extern void draw_graph(int i, int flags, Graph_ctx *gr);
|
||||
extern void draw_graph_all(int flags);
|
||||
extern int draw_images_all(void);
|
||||
extern void setup_graph_data(int i, const int flags, int skip, Graph_ctx *gr);
|
||||
extern double timer(int start);
|
||||
extern void enable_layers(void);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
v {xschem version=2.9.8 file_version=1.2}
|
||||
v {xschem version=3.0.0 file_version=1.2 }
|
||||
G {}
|
||||
K {type=subcircuit
|
||||
format="@name @pinlist @symname WN_FB=@WN_FB WP_FB=@WP_FB"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
v {xschem version=2.9.5_RC5 file_version=1.1}
|
||||
v {xschem version=3.0.0 file_version=1.2 }
|
||||
G {}
|
||||
K {}
|
||||
V {}
|
||||
S {}
|
||||
E {}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
v {xschem version=2.9.5_RC5 file_version=1.1}
|
||||
v {xschem version=3.0.0 file_version=1.2}
|
||||
G {type=subcircuit
|
||||
format="@name @pinlist @symname"
|
||||
verilog_stop=true
|
||||
template="name=x1"
|
||||
|
||||
}
|
||||
V {}
|
||||
S {}
|
||||
E {}
|
||||
|
||||
T {@symname} -63 -6 0 0 0.3 0.3 {}
|
||||
T {@name} 135 -22 0 0 0.2 0.2 {}
|
||||
L 4 -130 -10 130 -10 {}
|
||||
L 4 -130 10 130 10 {}
|
||||
L 4 -130 -10 -130 10 {}
|
||||
L 4 130 -10 130 10 {}
|
||||
T {@symname} -58.5 -6 0 0 0.3 0.3 {}
|
||||
T {@name} 135 -22 0 0 0.2 0.2 {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue