@lwx228 asked How do add text to a cell that has an image loaded? This idea is pretty simple: put the picture in a frame box, use a graphic script to decorate, take a new picture. The details to get a nice round-trip image are a bit more complicated. The frame needs to be sized and scaled, and the picture positioned, and the final image cropped.
// open the original
original = New Image(
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Oroblanco_%28sweetie%29_fruits.jpg/320px-Oroblanco_%28sweetie%29_fruits.jpg"
);
{xSize, ySize} = original << size;
// use a frame to do the drawing, make the original fit the frame exactly
gb = Graph Box(
<<backgroundcolor( "orange" ), // to see errors better
FrameSize( xSize + 1, ySize + 1 ), // the +1 is required, 4 times
X Scale( 0, xSize + 1 ),
Y Scale( 0, ySize + 1 ),
// apparently x and y are different. this is correct in 17EA.
<<Add Image( image( original ), bounds( top( 0 ), Left( 1 ), bottom( ySize ), Right( xSize + 1 ) ) ),
// draw something on top of the image
Text( Center Justified, {xSize / 2, 10}, "https://en.wikipedia.org/wiki/Oroblanco" );
Pen Color( "blue" );
Pen Size( 3 );
Circle( {1 * xsize / 4, ysize / 2}, 10, 20, 30, 40, 50 );
Circle( {3 * xsize / 4, ysize / 2}, 10, 20, 30, 40, 50 );
);
// capture an updated image
updated = gb[framebox( 1 )] << getpicture;
// crop added border. "updated" is the final image.
updated << crop( Left( 1 ), top( 1 ), Right( xSize + 1 ), bottom( ySize + 1 ) );
// the remainder is just to verify the image...
originalMatrix = original << getpixels;
newMatrix = updated << getpixels;
diff = (originalMatrix - newMatrix != 0);
New Window( "bitmaps",
V List Box(
gb,
Spacer Box( size( 1, 10 ) ),
Button Box( "picture credit: Ivar Leidus, Creative Commons Attribution-Share Alike 4.0 International",
Web( "https://en.wikipedia.org/wiki/File:Oroblanco_(sweetie)_fruits.jpg" ),
<<underlinestyle
),
Spacer Box( size( 1, 10 ) ),
H List Box(
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "original" ), original ),
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "updated" ), updated ),
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "diff" ), New Image( diff ) ),
Spacer Box( size( 2, 2 ) ),
)
)
);
The results, below, shows the image filling the frame box with the decorations, and three images below. The left image is the original, the center is modified, and the right is the difference. The difference is mostly black, no change, which is good. The text in the difference is fuzzy because the anti-aliasing made subtle changes at the edge of the text.
Hello Craige! I wonder if JSL can superimpose a small image at the specified position of the image? I searched the help manual and couldn't find the answer.
// open the original
original = New Image(
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Oroblanco_%28sweetie%29_fruits.jpg/320px-Oroblanco_%28sweetie%29_fruits.jpg"
);
{xSize, ySize} = original << size;
overlay1 = new image("https://community.jmp.com/t5/image/serverpage/image-id/35680iA0171FFF13D53E06");// cat
{xOverlay1Size, yOverlay1Size} = overlay1 << size;
dt = open("$sample_data/big class families.jmp","invisible");
overlay2 = dt:picture[1];// katie
overlay2<<scale(2); // make katie as big as the cat
close(dt,nosave);
{xOverlay2Size, yOverlay2Size} = overlay2 << size;
// use a frame to do the drawing, make the original fit the frame exactly
gb = Graph Box(
<<backgroundcolor( "orange" ), // to see errors better
FrameSize( xSize + 1, ySize + 1 ), // the +1 is required, 4 times
X Scale( 0, xSize + 1 ),
Y Scale( 0, ySize + 1 ),
// apparently x and y are different. this is correct in 17EA.
// for this demo, comment out here and add later.
// <<Add Image( image( overlay ), bounds( top( 50 ), Left( 20 ), bottom( 50+yOverlaySize ), Right( 20+xOverlaySize + 1 ) ) ),
// <<Add Image( image( original ), bounds( top( 0 ), Left( 1 ), bottom( ySize ), Right( xSize + 1 ) ) ),
// draw something on top of the image
Text( Center Justified, {xSize / 2, 10}, "https://en.wikipedia.org/wiki/Oroblanco" );
Pen Color( "light orange" );
Pen Size( 3 );
Circle( {3 * xsize / 4 -10, ysize / 2+5}, 60 );
);
// you could use a loop to add a bunch of images to the framebox
// <<AddImage adds at the FRONT of the display list. The first
// image added will be drawn last, on top of the 2nd image added.
gb[framebox(1)]<<Add Image( image( overlay2 ), bounds( top( 50 ), Left( 155 ), bottom( 50+yOverlay2Size ), Right( 155+xOverlay2Size + 1 ) ) );
gb[framebox(1)]<<Add Image( image( overlay1 ), bounds( top( 50 ), Left( 20 ), bottom( 50+yOverlay1Size ), Right( 20+xOverlay1Size + 1 ) ) );
gb[framebox(1)]<<Add Image( image( original ), bounds( top( 0 ), Left( 1 ), bottom( ySize ), Right( xSize + 1 ) ) );
// capture an updated image
updated = gb[framebox( 1 )] << getpicture;
// crop added border. "updated" is the final image.
updated << crop( Left( 1 ), top( 1 ), Right( xSize + 1 ), bottom( ySize + 1 ) );
// the remainder is just to verify the image...
originalMatrix = original << getpixels;
newMatrix = updated << getpixels;
diff = (originalMatrix - newMatrix != 0);
New Window( "bitmaps",
V List Box(
gb,
Spacer Box( size( 1, 10 ) ),
Button Box( "picture credit: Ivar Leidus, Creative Commons Attribution-Share Alike 4.0 International",
Web( "https://en.wikipedia.org/wiki/File:Oroblanco_(sweetie)_fruits.jpg" ),
<<underlinestyle
),
Spacer Box( size( 1, 10 ) ),
H List Box(
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "original" ), original ),
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "updated" ), updated ),
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "diff" ), New Image( diff ) ),
Spacer Box( size( 2, 2 ) ),
)
)
);
Pictures added over another picture.
The graphic script can't add the picture. Use the <<AddImage message instead.
The GraphBox(...) function takes three kinds of arguments: parameters, messages, and scripts. Best practice is to put the parameters and messages first, and a single script at the end. The parameters and messages are separated by commas. The script uses semicolons to separate statements.
Many, many graph box scripts have been written with accidental commas rather than semicolons. Each comma causes a new script to begin, separated from the previous script. It is a bit of extra overhead for JMP to run multiple scripts when one would do.
// open the original
original = New Image(
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Oroblanco_%28sweetie%29_fruits.jpg/320px-Oroblanco_%28sweetie%29_fruits.jpg"
);
{xSize, ySize} = original << size;
overlay1 = new image("https://community.jmp.com/t5/image/serverpage/image-id/35680iA0171FFF13D53E06");// cat
{xOverlay1Size, yOverlay1Size} = overlay1 << size;
dt = open("$sample_data/big class families.jmp","invisible");
overlay2 = dt:picture[1];// katie
// adjusting transparency. Katie already has a transparent background, alpha=0, and a mostly opaque
// foreground, alpha=1. Katie probably has some antialiasing around the edge of the shape, 0 < alpha < 1.
// rather than set all the alpha for the entire square to .5, multiply the existing alpha by .5.
// this will leave the alpha=0 at 0, and modify the others...
{r,g,b,a} = overlay2<<getpixels("rgba");
a = a * 0.5; // matrix multiply, just the alpha channel
overlay2 = newimage("rgba",{r,g,b,a});
overlay2<<scale(2); // make katie as big as the cat
close(dt,nosave);
{xOverlay2Size, yOverlay2Size} = overlay2 << size;
// use a frame to do the drawing, make the original fit the frame exactly
gb = Graph Box(
<<backgroundcolor( "orange" ), // to see errors better
FrameSize( xSize + 1, ySize + 1 ), // the +1 is required, 4 times
X Scale( 0, xSize + 1 ),
Y Scale( 0, ySize + 1 ),
// apparently x and y are different. this is correct in 17EA.
// for this demo, comment out here and add later.
// <<Add Image( image( overlay ), bounds( top( 50 ), Left( 20 ), bottom( 50+yOverlaySize ), Right( 20+xOverlaySize + 1 ) ) ),
// <<Add Image( image( original ), bounds( top( 0 ), Left( 1 ), bottom( ySize ), Right( xSize + 1 ) ) ),
// draw something on top of the image
Text( Center Justified, {xSize / 2, 10}, "https://en.wikipedia.org/wiki/Oroblanco" );
Pen Color( "light orange" );
Pen Size( 3 );
Circle( {3 * xsize / 4 -10, ysize / 2+5}, 60 );
);
// you could use a loop to add a bunch of images to the framebox
// <<AddImage adds at the FRONT of the display list. The first
// image added will be drawn last, on top of the 2nd image added.
gb[framebox(1)]<<Add Image( image( overlay2 ), bounds( top( 50 ), Left( 155 ), bottom( 50+yOverlay2Size ), Right( 155+xOverlay2Size + 1 ) ) );
gb[framebox(1)]<<Add Image( image( overlay1 ), bounds( top( 50 ), Left( 20 ), bottom( 50+yOverlay1Size ), Right( 20+xOverlay1Size + 1 ) ) );
gb[framebox(1)]<<Add Image( image( original ), bounds( top( 0 ), Left( 1 ), bottom( ySize ), Right( xSize + 1 ) ) );
// capture an updated image
updated = gb[framebox( 1 )] << getpicture;
// crop added border. "updated" is the final image.
updated << crop( Left( 1 ), top( 1 ), Right( xSize + 1 ), bottom( ySize + 1 ) );
// the remainder is just to verify the image...
originalMatrix = original << getpixels;
newMatrix = updated << getpixels;
diff = (originalMatrix - newMatrix != 0);
New Window( "bitmaps",
V List Box(
gb,
Spacer Box( size( 1, 10 ) ),
Button Box( "picture credit: Ivar Leidus, Creative Commons Attribution-Share Alike 4.0 International",
Web( "https://en.wikipedia.org/wiki/File:Oroblanco_(sweetie)_fruits.jpg" ),
<<underlinestyle
),
Spacer Box( size( 1, 10 ) ),
H List Box(
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "original" ), original ),
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "updated" ), updated ),
Spacer Box( size( 2, 2 ) ),
V List Box( Text Box( "diff" ), New Image( diff ) ),
Spacer Box( size( 2, 2 ) ),
)
)
);
which would ignore the existing values by jamming 0.5 in where it was previously 0.0.
The cut-away parts are no longer completely transparent.
The [0,0] makes a matrix assignment rather than changing a to a scalar. The light gray square tells me the transparent cut away pixels are probably white, and putting them in front of the darker gray with 50% causes a lighter gray.
One way to get a transparent background is to save an SVG then edit the SVG to remove the background rectangle(s). You can do that with an editor like Inkscape or you can write JSL to do it.
dt = Open( "$sample_data/big class.jmp" );
gb = dt << Graph Builder(
Show Control Panel( 0 ),
Variables( X( :height ), Y( :weight ), Overlay( :sex ) ),
Elements( Points( X, Y, Legend( 1 ) ), Smoother( X, Y, Legend( 2 ) ) ),
SendToReport(
Dispatch( {}, "Graph Builder", FrameBox,
// I chose fedcba to be unique, used in patmatch below
{Background Color( -Hex To Number( "fedcba" ) )}
)
)
);
gb << savepicture( "$temp/temp.svg", "svg" );
txt = Load Text File( "$temp/temp.svg" );
Write( txt ); // to log, for debugging
/* remove something like this:
<g fill="#FEDCBA">
<rect stroke="none" x="75.5" y="33.5" width="509" height="420"/>
</g>
*/
rc = Pat Match( txt, "<g fill=\!"#FEDCBA\!">" + Pat Arb() >> BadFillPattern + "</g>" );
If( !rc,
Throw( "fill pattern not found" )
);
// the BadFillPattern occurs twice, for two different colors. remove cases of
// <rect stroke="none" x="75.5" y="33.5" width="509" height="420"/>
// (the svg command that puts a rectangle of color behind the graph's data.)
nfound = 0;
While( Pat Match( txt, BadFillPattern, "" ), nfound += 1 );
If( nfound != 2,
Throw( "nfound=" || Char( nfound ) )
);
Save Text File( "$temp/temp.svg", txt );
// I don't know how you'll load this file on windows to prove it is transparent.
Open( "$temp/temp.svg" ); // this *does* load it, but there is nothing behind it to show through.
// you can open in FireFox and fiddle with the background color to observe the transparency
FireFox background shows through the transparency.
I know there are Linux tools for converting the SVG to a transparent PNG. Those tools probably exist on Win as well.
Rotating a bitmap generally makes the resulting image a little blurry because the output image is resampled from the input image...usually four input pixels contribute to each output pixel. Starting with an extra large input can help.
You might prefer the SVG output; the vectors can be rotated without loss. I like Inkscape for an SVG editor.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.