flickering twin images in memory game - image

I have a problem of flickering twin images in https://github.com/dzenanr/educ_memory_game.
In the Board class of view/board.dart, the following code in the _imageBox method creates and loads images:
var imagePath = 'images/${cell.image}';
ImageElement image = new Element.tag('img');
image.src = imagePath;
image.onLoad.listen((event) {
context.drawImageToRect(image, new Rect(x, y, boxSize, boxSize));
});
A click on a board cell shows an image for a second. When the same 2 images are discovered, those twin images stay displayed , but they flicker every second (a timer refresh interval).
In order to solve the flickering problem, I create those images in a constructor of the Board class:
for (var cell in memory.cells) {
ImageElement image = new Element.tag('img');
image.src = 'images/${cell.image}';
imageMap[cell.image] = image;
}
Then, I get an image from the map. However neither of the following two works:
ImageElement image = imageMap[cell.image];
image.onLoad.listen((event) {
context.drawImageToRect(image, new Rect(x, y, boxSize, boxSize));
});
or
ImageElement image = imageMap[cell.image];
context.drawImageToRect(image, new Rect(x, y, boxSize, boxSize));

Changing the src attribute of the image imply a network access and is not good, you have to used the cached images.
To display the image correctly, just change the _imageBox function a little bit.
void _imageBox(Cell cell) {
var x = cell.column * boxSize;
var y = cell.row * boxSize;
context.beginPath();
// Moved at the beginning otherwise it is drawn above the image.
context.rect(x, y, boxSize, boxSize);
context.fill();
context.stroke();
context.closePath();
if (cell.hidden ) {
context.fillStyle = HIDDEN_CELL_COLOR_CODE;
var centerX = cell.column * boxSize + boxSize / 2;
var centerY = cell.row * boxSize + boxSize / 2;
var radius = 4;
context.arc(centerX, centerY, radius, 0, 2 * PI, false);
} else {
ImageElement image = imageMap[cell.image]; // if decomment, comment the above 3 lines
context.drawImageToRect(image, new Rect(x, y, boxSize, boxSize));
}
}

Related

Skiasharp how to get coordinates from touch location after scaling and transform

I have an Xamarin project where I am using Skiasharp. I am relatively new to the drawing utility. Ive spent a few days trying to figure out this issue with no luck. After scaling and transforming the canvas, when I touch the skcanvas view on the phone screen and look at the 'location' point in the touch event, its not the same location that the canvas drew something. I need the exact location I drew the rectangle.
Its a lot of code below and granted its not all the code, but its the important parts. I am absolutely baffled why I draw in one (X,Y) location yet when I touch the screen the touch event for the canvas gives me a completely different location than what than the (X,Y) the widget was drawn at.
'''
public static void DrawLayout(SKImageInfo info, SKCanvas canvas, SKSvg svg,
SetupViewModel vm)
var layout = vm.SelectedReticleLayout;
float yRatio;
float xRatio;
float widgetHeight = 75;
float widgetWidth = 170;
float availableWidth = 720;
float availableHeight = 1280;
var currentZoomScale = getScale();
canvas.Translate(info.Width / 2f, info.Height / 2f);
SKRect bounds = svg.ViewBox;
xRatio = (info.Width / bounds.Width) + ((info.Width / bounds.Width) * currentZoomScale);
yRatio = (info.Height / bounds.Height) + ((info.Height / bounds.Height) *
currentZoomScale);
float ratio = Math.Min(xRatio, yRatio);
canvas.Scale(ratio);
canvas.Translate(-bounds.MidX, -bounds.MidY);
canvas.DrawPicture(svg.Picture, new SKPaint { Color = SKColors.White, Style =
SKPaintStyle.Fill });
// now set the X,Y and Width and Height of the large Red Rectangle
float imageCenter = canvas.LocalClipBounds.Width / 2;
layout.RedBorderXOffSet = imageCenter - (imageCenter / 2.0f) +
canvas.LocalClipBounds.Left;
float redBorderYOffSet = (float)(svg.Picture.CullRect.Top +
Math.Ceiling(.0654450261780105f * svg.Picture.CullRect.Bottom));
layout.RedBorderYOffSet = (float)(canvas.LocalClipBounds.Top +
Math.Ceiling(.0654450261780105f * canvas.LocalClipBounds.Bottom));
layout.RedBorderWidth = canvas.LocalClipBounds.Width / 2.0f;
layout.RedBorderWidthXOffSet = layout.RedBorderWidth + layout.RedBorderXOffSet;
layout.RedBorderHeight = (float)(canvas.LocalClipBounds.Bottom -
Math.Ceiling(.0654450261780105f * canvas.LocalClipBounds.Bottom * 2)) -
canvas.LocalClipBounds.Top;
layout.RedBorderHeightYOffSet = layout.RedBorderYOffSet + layout.RedBorderHeight;
// draw the large red rectangle
canvas.DrawRect(layout.RedBorderXOffSet, layout.RedBorderYOffSet, layout.RedBorderWidth,
layout.RedBorderHeight, RedBorderPaint);
// clear the tracked widgets, tracked widgets are updated every time we draw the widgets
// base widgets contain the default size and location relative to the scope. base line
widgets
// will need to be multiplied by the node scale height and width
layout.TrackedWidgets.Clear();
var widget = new widget
{
X = layout.RedBorderXOffSet + 5;
Y = layout.RedBorderYOffSet + layout.TrackedReticleWidgets[0].Height + 15;
Height = layout.RedBorderHeight * (widgetHeight / availableHeight);
Width = layout.RedBorderWdith * (widgetWidth / availableWidth);
}
// define colors for text and border colors for small rectangles (widgets)
public static SKPaint SelectedWidgetColor => new SKPaint { Color = SKColors.LightPink,
Style = SKPaintStyle.StrokeAndFill, StrokeWidth = 3 };
public static SKPaint EmptyWidgetBorder => new SKPaint { Color = SKColors.DarkGray,
Style = SKPaintStyle.Stroke, StrokeWidth = 3 };
public static SKPaint EmptyWidgetText => new SKPaint { Color = SKColors.Black, TextSize
= 10, FakeBoldText = false, Style = SKPaintStyle.Stroke, Typeface =
SKTypeface.FromFamilyName("Arial") };
public static SKPaint DefinedWidgetText => new SKPaint { Color = SKColors.DarkRed,
FakeBoldText = false, Style = SKPaintStyle.Stroke };
// create small rectangle (widget) and draw the widget
var widgetRectangle = SKRect.Create(widget.X, widget.Y, widget.Width, widget.Height);
canvas.DrawRect(widgetRectangle, widget.IsSelected ? SelectedWidgetColor :
EmptyWidgetBorder);
// now lets create the text to draw in the widget
string text = EnumUtility.GetDescription(widget.WidgetDataType);
float textWidth = EmptyWidgetText.MeasureText(text);
EmptyWidgetText.TextSize = widget.Width * GetUnscaledWidgetWith(widget) *
EmptyWidgetText.TextSize / textWidth;
SKRect textBounds = new SKRect();
EmptyWidgetText.MeasureText(text, ref textBounds);
float xText = widgetRectangle.MidX - textBounds.MidX;
float yText = widgetRectangle.MidY - textBounds.MidY;
canvas.DrawText(text, xText, yText, EmptyWidgetText);
'''

How to have scalable image with viewport in p5js?

I'm making a website for my design studio and I would like to set image sizes with the p5js language.
Here, I made a function that allows each mouse click to display an image. The problem is that once the image is displayed, it appears at the indicated dimensions, adapt to the screen size but the ratio is not good. The image is either too wide or too extended.
so what do you need to do to display an image with the right original width/height ratio
All your feedback is appreciated.
Thanks in advance !
let works = []
function preload() {
for (let i = 0; i < 6; i++) {
works[i] = loadImage("img/work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++
image(works[counter%6], mouseX, mouseY, windowWidth/2, windowHeight/2);
}
The image function can take 3, 5, 7, or 9 parameters, and in each case it has different behavior:
image(img, x, y) - Draw the entire image with it's original size at the specified location
image(img, x, y, width, height) - Draw the entire image, scaled to the specified width and height without preserving aspect ratio, at the specified position.
image(img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) - Draw a specific part of the image, scaled to the specified width and height without preserving aspect ratio, at the specified position.
The function of the dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight parameters is explained by this diagram:
In order to draw an image scaled relative to the window size constrained to the aspect ratio of the window, you will need to determine how best to crop your images.
let works = []
function preload() {
works[0] = loadImage("https://www.paulwheeler.us/files/no_idea_why.jpg");
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++
let img = works[counter % works.length];
let windowAspect = width / height;
let imageAspect = img.width / img.height;
let w, h;
// This code naively crops the bottom or right edge of the image as necessary. Obviously there are other ways to limit the image size.
if (windowAspect >= imageAspect) {
// Our window is wider than our image, we need to constrain the height of the image
w = img.width;
h = w / windowAspect;
} else {
// Our window is narrower than or image, we need to constrain the width of the image
h = img.height;
w = h * windowAspect;
}
image(img, mouseX, mouseY, windowWidth / 2, windowHeight / 2, 0, 0, w, h);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
</body>
</html>

HTML5 Canvas rotate trouble

I have a canvas element on which I am drawing a number of images and overlaying them with text. Unfortunately the problem requires that some of these images and corresponding text be rotated. Added to this the problem that there must be a corresponding background color on some of the images (images are simple outlines of desks for a floorplan)
Here is the function I have built to handle adding a single desk to the plan. The problem I am having is that when I use the rotate neither the text nor the background colors show up, while the appear correctly if I do not rotate the image, except that they are not rotated and the background fillRect() is oriented 90 degrees off.
function redrawDesk(desk, ctx, color) {
var rotate = desk.rotation == 90 || desk.rotation == 270;
if (rotate) {
ctx.save();
ctx.rotate(Math.PI / 2);
ctx.clearRect(desk.left, desk.top, desk.width, desk.height);
ctx.restore()
}
var img = $("#desk_" + desk.rowID)[0];
ctx.drawImage(img, desk.left, desk.top, desk.height, desk.width);
var x = desk.left;
var y = desk.top;
var h = desk.height;
var w = desk.width;
if (rotate) {
//ctx.save()
ctx.rotate(Math.PI / 2);
var tmp=x;
x=y;
y=tmp;
tmp=h;
h=w;
w=tmp;
}
ctx.textAlign = "center";
ctx.fillText(desk.deskID, x + w / 2,y + h/ 2);
if (color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
}
//ctx.restore();
if (rotate) {
ctx.rotate(Math.PI / -2);
}
}
Thank you
The main problem is that you are defining the desk and text as absolute coordinates.
Define objects in there local coordinate system. Eg the desk has a height and width but not a position. Its draw relative to its self (around 0,0)
const desk = {
w : 10, h : 10,
color : "blue",
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(-this.w / 2, -this.h / 2, this.w, this.h);
}
};
You can then position the desk into the world coordinate system (the canvas) by defining where its center will be.
function drawObj(obj, x, y) { // what to draw and where
ctx.setTransform(1,0,0,1,x,y); // Same as ctx.translate if 2D API is in default context
// The means you do not have to use ctx.save and
// ctx.restore in this function
obj.draw(); // draw desk
}
For a full transform its much the same
function drawObj(obj, x, y, scale, rotate) { // rotate is in radians
ctx.setTransform(scale, 0, 0, scale, x, y);
ctx.rotate(rotate);
obj.draw();
}
To add text you can add it as an object to the desk and draw it to its own local coordinate system
desk.name = {
text : "Desk",
color : "black",
font : "bold " + 20 + "px Calibri",
draw() {
ctx.font = this.font;
ctx.textAlign = "center";
ctx.fillStyle = this.color;
ctx.fillText(this.text, 0,0);
}
};
You can now draw the desk and name using the draw object function
drawObj(desk,200, 200, 1, Math.PI / 2); // Draw at 200,200 rotated 90deg CW
drawObj(desk.name, 200, 200, 1, Math.PI / 2); // draw the text rotated same and centered over desk
// Or if the text should be above and not rotated
drawObj(desk.name, 200, 200 - 30, 1, 0);
As the above functions use setTransform you may need to restore the transform. There are two ways to do this.
ctx.resetTransform(); // Check browser support for this call
ctx.setTransform(1,0,0,1,0,0); // same as above just does it manaly
In my code I tested to see if there is a rotation needed. If so I set a translate on the canvas to give me a new start point: ctx.translate(x, y); This allowed me to simplify my location settings for placing text and the background colors, which means they are showing up correctly. Here is the changed code to compare with the original:
if (rotate) {
ctx.save();
tmp = h;
h = w;
w = tmp;
ctx.translate(x, y);
}
if (color) {
ctx.fillStyle = color;
ctx.fillRect(0, 0, w, h);
}
ctx.font = "bold " + w / 2 + "px Calibri";
ctx.textAlign = "center";
ctx.fillStyle = "#000";
var c=ctx.canvas;
ctx.rotate(Math.PI / -2);
ctx.fillText(desk.deskID, 0-h/2, w/2); //x + w / 2, y + h / 2);
ctx.restore();

Cannot get context.rotate to show image properly

I need to rotate the image 180 degrees, but when I add in the rotate code, the image becomes a square.
var moulding_matte_canvas_height = [],
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
width = 5.171363636363637,
opening_i = 0,
rotate = 180 * Math.PI / 180,
i = 2;
moulding_matte_canvas_height[0] = 225;
canvas.width = 285;
canvas.height = 335;
img = new Image();
img.src = 'http://www.asa.tframes.org:1881/system/components/compimg/80ac89fad42cf14561b641df241bf406/pattern';
function moulding_get_segment_width(img_width, opening_i)
{
return 175;
}
//
$(img).on("load", function() {
ctx.save();
ctx.translate(width, moulding_matte_canvas_height[opening_i]);
//ctx.rotate(rotate);
//ctx.rotate(Math.PI);
ctx.drawImage(img, 0, 0, moulding_get_segment_width(img.width, i), width);
ctx.restore();
//
});
$("#mattes").append(canvas); /*appending the canvas*/
http://fiddle.jshell.net/onpa628e/1/
(Whitespace is intentional due to other elements that will be drawn and the red next to the image is just for clarity)
Without rotate:
With rotate:
Expected:
Your image looks like a square, because it's only showing a tiny portion on the canvas. This happens because translate is only moving ~ 5px (your "width" variable) on the x axis.
Here's your example modified with a higher x value.

Dynamic videos from user inputs - How does it work?

Is there a way to generate a video frame's content by code?
For example :
I want to make a program that takes some string variable as input and then gives an output video of same text but now with special effects on the text itself.
It came to my mind the idea after seeing some projects that Facebook has done in their website. They have a database of pictures, comments, friends, events, likes and so on, that are related to a User. With all this information they do videos like for example Friend's day, a video that is completely related to the user that wants to post it.
How does these things work?
Is there some sort of software I can use? Can someone give me a place to start with?
I want to make a program that takes some string variable as input and then gives an output video of same text but now with special effects on the text itself.
You can render the text using canvas and record the result using the new Media Recorder API.
The effects can be made various ways of course, and the text content itself can be used against a database, or in conjunction with machine learning to structure/obtain some data relative to the text. This has a wide scope though, wider than what fits in here.
In any case, the generation of the video itself is shown below. It is a simple example where you can enter some text, it gets animated and recorded as video. When you click stop the actual video will show instead (which you can allow user to download). You can extend with other effects, storyboard features so you can record scenes and develop it to make an entire movie.
Do notice that all animation happens on the canvas in real-time. Typical video is recorded at 30 frames per second and not 60 frames per second, which is the typical frame rate for canvas. This is also something to consider in regard to smoothness. However, you can use this to your advantage by targeting 30 FPS to give your code a little more time to process. The API is suppose to support step-recording by not giving a frame rate as argument, but I have not been able to make this work as of yet.
There are currently limits with support of the API as not all browsers supports it yet.
Example
// un-refined code for demo use only
// set up globals, get key elements
var div = document.querySelector("div");
var btn = document.querySelector("button");
var txt = document.querySelector("input");
var c = document.querySelector("canvas");
// make 2d context without alpha channel
var ctx = c.getContext("2d", {alpha: false});
var isRecording = false;
// stream vars
var rec, stream, tref;
// if clicked, lets go
btn.onclick = setup;
function setup() {
// toggle button text/status for demo
if (isRecording) {
this.disabled = true;
this.innerHTML = "Making video...";
stop();
return
}
else {
isRecording = true;
this.innerHTML = "Click to stop & show video";
}
// Setup canvas for text rendering
var ct1 = document.createElement("canvas");
var ct2 = document.createElement("canvas");
var ctxText1 = ct1.getContext("2d");
var ctxText2 = ct2.getContext("2d");
var w, h;
w = ct1.width = ct2.width = c.width;
h = ct1.height = ct2.height = c.height;
setupCtx(ctxText1, "#333", "#FF9F05");
setupCtx(ctxText2, "#FF9F05", "#000");
function setupCtx(ctxText, bg, fg) {
ctxText.textAlign = "center";
ctxText.textBaseline = "middle";
ctxText.font = "92px sans-serif";
ctxText.fillStyle = bg;
ctxText.fillRect(0, 0, w, h);
ctxText.fillStyle = fg;
ctxText.translate(w/2, h/2);
ctxText.rotate(-0.5);
ctxText.fillText(txt.value.toUpperCase(), 0, 0);
}
// populate grid (see Square object below which handles the tiles)
var cols = 18,rows = 11,
cellWidth = (c.width / cols)|0,
cellHeight = (c.height / rows)|0,
grid = [],
len = cols * rows, y = 0, x,
index, hasActive = true;
for (; y < rows; y++) {
for (x = 0; x < cols; x++) {
grid.push(new Square(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, ct1, ct2, 0.01));
}
}
x = 0;
// start recording canvas to video
record();
//animation loop (refactor at will)
function loop() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
ctx.clearRect(0, 0, w, h);
// trigger cells
for (y = 0; y < rows; y++) {
var gx = (x | 0) - y;
if (gx >= 0 && gx < cols) {
index = y * cols + gx;
grid[index].trigger();
}
}
x += 0.25;
// update all
for (var i = 0; i < grid.length; i++) grid[i].update();
tref = requestAnimationFrame(loop);
}
// setup media recorder to record canvas # 30 FPS (note: canvas = 60 FPS by def.)
function record() {
stream = c.captureStream(30);
rec = new MediaRecorder(stream);
rec.addEventListener('dataavailable', done);
rec.start();
requestAnimationFrame(loop);
}
}
// stop recorder and trigger dataavailable event
function stop() {
rec.stop();
cancelAnimationFrame(tref); // stop loop as well
}
// finish up, show new shiny video instead of canvas
function done(e) {
var blob = new Blob([e.data], {"type": "video/webm"});
var video = document.createElement("video");
document.body.removeChild(c);
document.body.appendChild(video);
// play, Don
video.autoplay = video.loop = video.controls = true;
video.src = URL.createObjectURL(blob);
video.onplay = function() {
div.innerHTML = "Playing resulting video below:";
};
}
// stolen from a previous example I made (CC3.0-attr btw as always)
// this is included for the sole purpose of some animation.
function Square(ctx, x, y, w, h, image, image2, speed) {
this.ctx = ctx;
this.x = x;
this.y = y;
this.height = h;
this.width = w;
this.image = image;
this.image2 = image2;
this.first = true;
this.alpha = 0; // current alpha for this instance
this.speed = speed; // increment for alpha per frame
this.triggered = false; // is running
this.done = false; // has finished
}
Square.prototype = {
trigger: function () { // start this rectangle
this.triggered = true
},
update: function () {
if (this.triggered && !this.done) { // only if active
this.alpha += this.speed; // update alpha
if (this.alpha <= 0 || this.alpha >= 1)
this.speed = -this.speed;
}
var t = Math.sin(Math.PI * this.alpha);
if (t <= 0) this.first = !this.first;
this.ctx.fillStyle = this.color; // render this instance
this.ctx.globalAlpha = Math.max(0, Math.min(1, t));
var cx = this.x + this.width * 0.5, // center position
cy = this.y + this.width * 0.5;
this.ctx.setTransform(t*0.5+0.5, 0, 0, t*0.5+0.5, cx, cy); // scale and translate
this.ctx.rotate(-Math.PI * (1 - t)); // rotate, 90° <- alpha
this.ctx.translate(-cx, -cy); // translate back
this.ctx.drawImage(this.first ? this.image : this.image2, this.x, this.y, this.width, this.height, this.x, this.y, this.width, this.height);
}
};
body {background: #555;color:#ddd;font:16px sans-serif}
<div>
<label>Enter text to animate: <input value="U R AWESOME"></label>
<button>Click to start animation + recording</button>
</div>
<canvas width=640 height=400></canvas>

Resources