I am trying to learn NME with haxe to create a small game. I have setup NME 3.5.5 with Haxe 2.10 in FlashDevelop. To draw the game background, I'm using
// Class level variable
var background : nme.display.Bitmap;
public function initResources() : Void
{
background = new Bitmap(Assets.getBitmapData("img/back.png"));
}
And in the render loop, I'm rendering like this.
g.clear();
g.beginBitmapFill(background.bitmapData, true, true);
g.drawRect(0, 0, 640, 480);
g.endFill();
This is drawing the image across the view and I need to resize it to fit the screen.
EDIT:
Here is the function i'm using to scale the bitmap. It doesn't work and nothing is rendered on the screen.
public static function resize( source:Bitmap, width:Int, height:Int ) : Bitmap
{
var scaleX:Int = Std.int(width / source.bitmapData.width);
var scaleY:Int = Std.int(height / source.bitmapData.height);
var data:BitmapData = new BitmapData(width, height, true);
var matrix:Matrix = new Matrix();
matrix.scale(scaleX, scaleY);
data.draw(source.bitmapData, matrix);
return new Bitmap(data);
}
Thanks.
EDIT 2:
Finally made it. I was unnecessarily casting it to int. Here's the solution.
public static function resize( source:Bitmap, width:Int, height:Int ) : Bitmap
{
var scaleX:Float = width / source.bitmapData.width;
var scaleY:Float = height / source.bitmapData.height;
var data:BitmapData = new BitmapData(width, height, true);
var matrix:Matrix = new Matrix();
matrix.scale(scaleX, scaleY);
data.draw(source.bitmapData, matrix);
return new Bitmap(data);
}
Look this Resizing BitmapData in ActionScript 3
You should use BitmapData.draw and scaled matrix.
Related
I am developing an application in Flutter where I am using CustomPainter to draw an image which the user picks from gallery/camera. In addition to this the use can draw lines as well as change the stroke value, opacity colour and colour on it its own. For this I have created 2 classes DrawEditor and DrawingPainter the code for those two classes can be found below. Once the user picks an image
the image is passed to the DrawingPainter class where paint() is called and I draw my lines and image. The issue is in _paintBackgroundImage() in this method I draw the image by using canvas.drawImage(paintedImage, Offset.zero, Paint()); which does not scale the image.
Earlier I tried a different approach instead of drawing the image with canvas.drawImage(paintedImage, Offset.zero, Paint()) I used canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint()); as can be seen below. However with this approach the draw picture Is pixelated so I prefer canvas.drawImage(paintedImage, Offset.zero, Paint()) as this does not damage the picture.
Any help with scaling the image will be greatly appreciated.
//Example 1 : Code with canvas.drawImageRect but image pixelated
final UI.Rect rect = UI.Offset.zero & _canvasSize;
final Size imageSize =Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, _canvasSize);
final Rect inputSubRect =
Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubRect =
Alignment.center.inscribe(sizes.destination, rect);
canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint());
//Example 2 : Code with canvas.drawImageRect but image pixelated
canvas.drawRect(Rect.fromPoints(blurStartOffset, blurIndicatorOffset),
blurPaintSettings)
class DrawingPainter extends CustomPainter {
static int blurColor = 0xFFB3E5FC;
UI.Image paintedImage;
List<DrawingPoints> pointsList;
List<DrawingPoints> blurPointsList;
List<Offset> offsetPoints = List();
Size _canvasSize;
Offset blurIndicatorOffset;
Offset blurStartOffset;
bool isBlur;
List<BlurIndicatorOffsetWrapper> wrapperList = new List();
/// To blur an image we need a [MaskFilter]
Paint blurPaintSettings = new Paint()
..style = PaintingStyle.fill
..color = Color(blurColor)
..maskFilter = MaskFilter.blur(BlurStyle.normal, 3.0);
DrawingPainter(
{this.pointsList,
this.paintedImage,
this.blurPointsList,
this.blurIndicatorOffset,
this.blurStartOffset}) {
isBlur = blurIndicatorOffset != null;
}
#override
void paint(Canvas canvas, Size size) {
_canvasSize = size;
_paintBackgroundImage(canvas);
_drawPoints(canvas);
_drawBlurIndicator(canvas);
}
/// Paints the image onto the canvas
void _paintBackgroundImage(Canvas canvas) {
if (paintedImage == null) {
return;
}
final UI.Rect rect = UI.Offset.zero & _canvasSize;
final Size imageSize =
Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, _canvasSize);
final Rect inputSubRect =
Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubRect =
Alignment.center.inscribe(sizes.destination, rect);
canvas.drawImageRect(paintedImage, inputSubRect, outputSubRect, Paint());
}
/// Paints the lines onto the canvas
void _drawPoints(Canvas canvas) {
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null && pointsList[i + 1] != null) {
canvas.drawLine(pointsList[i].points, pointsList[i + 1].points,
pointsList[i].paint);
}
}
}
/// Paints the blur indicator onto the canvas
void _drawBlurIndicator(Canvas canvas) {
if (blurStartOffset != null && blurIndicatorOffset != null) {
canvas.drawRect(Rect.fromPoints(blurStartOffset, blurIndicatorOffset),
blurPaintSettings);
}
}
void setBlurIndicator(Offset localOffset) {
blurIndicatorOffset = localOffset;
}
#override
bool shouldRepaint(DrawingPainter oldDelegate) {
return true;
}
Future<Uint8List> save() async {
//Create canvas
// Set PictureRecorder on the canvas and start recording
UI.PictureRecorder recorder = UI.PictureRecorder();
Canvas canvas = Canvas(recorder);
//Draw image on new canvas
if (paintedImage != null) {
final Size imageSize = Size(paintedImage.width.toDouble(), paintedImage.height.toDouble());
//Here image is the problem
canvas.drawImage(paintedImage, Offset.zero, Paint());
}
//Draw points on new canvas
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null && pointsList[i + 1] != null) {
canvas.drawLine(
pointsList[i].points,
pointsList[i + 1].points,
pointsList[i].paint,
);
}
}
//End recording
final resultImage = await recorder.endRecording().toImage(
_canvasSize.width.floor(),
_canvasSize.height.floor(),
);
final imageBytes =
await resultImage.toByteData(format: UI.ImageByteFormat.png);
return imageBytes.buffer.asUint8List();
}
}
class DrawingPoints {
Paint paint;
Offset points;
DrawingPoints({this.points, this.paint});
}
enum SelectedMode { StrokeWidth, Opacity, Color, Blur }
I had a very similar requirement and the comment about using paintImage was exactly what I was looking for, so I figured I'd share what I ended up with.
I needed to scale down an image and draw overlays on top of that image. image is my original (unscaled) Image object.
var recorder = ui.PictureRecorder();
var imageCanvas = new Canvas(recorder);
var painter = _MarkupPainter(_overlays);
//Paint the image into a rectangle that matches the requested width/height.
//This will handle rescaling the image into the rectangle so that it will not be clipped.
paintImage(
canvas: imageCanvas,
rect: Rect.fromLTWH(0, 0, scaledWidth, scaledHeight),
image: image,
fit: BoxFit.scaleDown,
repeat: ImageRepeat.noRepeat,
scale: 1.0,
alignment: Alignment.center,
flipHorizontally: false,
filterQuality: FilterQuality.high
);
//Add the markup overlays.
painter.paint(imageCanvas, Size(scaledWidth, scaledHeight));
var picture = recorder.endRecording();
return picture.toImage(scaledWidth.toInt(), scaledHeight.toInt());
I retrieve a rect from openSeadragonSelection:
viewer:
this.viewer = OpenSeadragon(this.config);
this.selection = this.viewer.selection({
showConfirmDenyButtons: true,
styleConfirmDenyButtons: true,
returnPixelCoordinates: true,
onSelection: rect => console.log(rect)
});
this.selection.enable();
rect by onSelection:
t.SelectionRect {x: 3502, y: 2265, width: 1122, height: 887, rotation:0, degrees: 0, …}
I have no idea how to get the canvas by rect from my viewer instance.
this.viewer.open(new OpenSeadragon.ImageTileSource(this.getTile(this.src)));
A self implemented imageViewer returned the canvas of the selected area. So I could get the blob and post it to the server:
onSave(canvas){
let source = canvas.toDataURL();
this.setState({source:source, crop: false, angle: 0});
save(this.dataURItoBlob(source), source.match(new RegExp("\/(.*);"))1]);
}
dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
How can I get the image of the viewer by rect. Rotation should be considered as well.
#iangilman:
Thank's alot for your advice. I created another canvas which I crop and after that put it back into the viewer. I was not sure if something similar was supported by your library yet:
const viewportRect = self.viewer.viewport.imageToViewportRectangle(rect);
const webRect = self.viewer.viewport.viewportToViewerElementRectangle(viewportRect);
const { x, y, width, height } = webRect || {};
const { canvas } = self.viewer.drawer;
let source = canvas.toDataURL();
const img = new Image();
img.onload = function () {
let croppedCanvas = document.createElement('canvas');
let ctx = croppedCanvas.getContext('2d');
croppedCanvas.width = width;
croppedCanvas.height = height;
ctx.drawImage(img, x, y, width, height, 0, 0, width, height);
let croppedSrc = croppedCanvas.toDataURL();
//update viewer with cropped image
self.tile = self.getTile(croppedSrc);
self.ImageTileSource = new OpenSeadragon.ImageTileSource(self.tile);
self.viewer.open(self.ImageTileSource);
}
img.src = source;
Rotation hasn't been considered yet.
I imagine you'll need to convert the rectangle into the proper coordinates, then create a second canvas and copy the appropriate bit out of the OSD canvas into the second one.
Looks like maybe the selection rectangle is in image coordinates? The OSD canvas will be in web coordinates, or maybe double that on an HDPI display. OSD has a number of conversion functions, for instance:
var viewportRect = viewer.viewport.imageToViewportRectangle(imageRect);
var webRect = viewer.viewport.viewportToViewerElementRectangle(viewportRect);
You can find out the pixel density via OpenSeadragon.pixelDensityRatio.
Once you have the appropriate rectangle it should be easy to copy out of the one canvas into another. I'm not sure how you incorporate rotation, but it might be as simple as adding a rotation call to one of the canvas contexts.
Sorry this is kind of vague, but I hope it helps!
I am trying my hand at easeljs and animating a spritesheet. This is the first time I am working with sprites and as such am not knowledgeable about them.
My simple easeljs code to show this specific animation is;
var stage;
function init() {
// create a new stage and point it at our canvas:
stage = new createjs.Stage(document.getElementById("demoCanvas"));
var data = {
images: ["http://i.imgur.com/g5WtL7v.png"],
frames: {width:256, height:256},
animations: {
run:[0,4]
}
};
var spriteSheet = new createjs.SpriteSheet(data);
var animation = new createjs.BitmapAnimation(spriteSheet);
animation.gotoAndPlay("run");
}
But the sprite doesn't shows up on the canvas at all. WHat am I doing wrong?
Additional question;
defining frames in easeljs can be done by
frames: [ // x, y, width, height, imageIndex, regX, regY
While I do understand width and height, what are x, y, imageIndex and regX, regY. The documentaion explains how I can use these parameters but for someone who is working with sprites for the 1st time in my life, I just dont know what these terms mean.
EDIT: I have also tried changing the code as such;
var stage;
function init() {
// create a new stage and point it at our canvas:
stage = new createjs.Stage("demoCanvas");
var data = {
images: ["http://i.imgur.com/g5WtL7v.png"],
frames: {width:256, height:256, count:8},
animations: {
run:[0,4, true]
}
};
var ss = new createjs.SpriteSheet(data);
var animation = new createjs.BitmapAnimation(ss);
animation.x = 100;
animation.y = 100;
stage.addChild(animation);
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", stage);
}
But I am still seeing a blank canvas...
You are missing a frame-count in the frames-object:
frames: {width:...,height:...,count:4} // or whatever number of frames your sprite holds
And just in case: The width and height is the width and height of 1 frame, not the entire image.
See more information and examples here: http://www.createjs.com/Docs/EaselJS/classes/SpriteSheet.html
ok i got things to work by combining both the codes that I have listed above;
var stage;
function init() {
// create a new stage and point it at our canvas:
stage = new createjs.Stage("demoCanvas");
var data = {
images: ["http://i.imgur.com/g5WtL7v.png"],
frames: {width:256, height:256, count:8},
animations: {
run:[0,4, true]
}
};
var ss = new createjs.SpriteSheet(data);
var animation = new createjs.BitmapAnimation(ss);
animation.x = 100;
animation.y = 100;
stage.addChild(animation);
animation.gotoAndPlay("run");
createjs.Ticker.setFPS(10);
createjs.Ticker.addEventListener("tick", stage);
}
Now I have some questions;
If I need animation.gotoAndPlay("run"); to animate the sprite how come the code at https://github.com/CreateJS/EaselJS/blob/master/examples/SpriteSheet_simple.html doesn't needs it?
whats the difference between new createjs.Sprite(ss, "run"); and new createjs.BitmapAnimation(spriteSheet); . I am unable to find any documentation of the former.
tried searching for something like this, but I've had no luck. I'm trying to open a new tab with a screenshot of the current state of my webgl image. Basically, it's a 3d model, with the ability to change which objects are displayed, the color of those objects, and the background color. Currently, I am using the following:
var screenShot = window.open(renderer.domElement.toDataURL("image/png"), 'DNA_Screen');
This line succeeds in opening a new tab with a current image of my model, but does not display the current background color. It also does not properly display the tab name. Instead, the tab name is always "PNG 1024x768".
Is there a way to change my window.open such that the background color is shown? The proper tab name would be great as well, but the background color is my biggest concern.
If you open the window with no URL you can access it's entire DOM directly from the JavaScript that opened the window.
var w = window.open('', '');
You can then set or add anything you want
w.document.title = "DNA_screen";
w.document.body.style.backgroundColor = "red";
And add the screenshot
var img = new Image();
img.src = someCanvas.toDataURL();
w.document.body.appendChild(img);
Well it is much longer than your one liner but you can change the background color of the rectangle of the context.
printCanvas (renderer.domElement.toDataURL ("image/png"), width, height,
function (url) { window.open (url, '_blank'); });
// from THREEx.screenshot.js
function printCanvas (srcUrl, dstW, dstH, callback)
{
// to compute the width/height while keeping aspect
var cpuScaleAspect = function (maxW, maxH, curW, curH)
{
var ratio = curH / curW;
if (curW >= maxW && ratio <= 1)
{
curW = maxW;
curH = maxW * ratio;
}
else if (curH >= maxH)
{
curH = maxH;
curW = maxH / ratio;
}
return { width: curW, height: curH };
}
// callback once the image is loaded
var onLoad = function ()
{
// init the canvas
var canvas = document.createElement ('canvas');
canvas.width = dstW;
canvas.height = dstH;
var context = canvas.getContext ('2d');
context.fillStyle = "black";
context.fillRect (0, 0, canvas.width, canvas.height);
// scale the image while preserving the aspect
var scaled = cpuScaleAspect (canvas.width, canvas.height, image.width, image.height);
// actually draw the image on canvas
var offsetX = (canvas.width - scaled.width ) / 2;
var offsetY = (canvas.height - scaled.height) / 2;
context.drawImage (image, offsetX, offsetY, scaled.width, scaled.height);
// notify the url to the caller
callback && callback (canvas.toDataURL ("image/png")); // dump the canvas to an URL
}
// Create new Image object
var image = new Image();
image.onload = onLoad;
image.src = srcUrl;
}
I have a project where i'm drawing to a stageSized bitmap by using BitmapData.draw
using the 'show update regions' feature of the debug player, i can see that the player always updates the whole stage although i changed only a small part of the bitmapdata.
Can I somehow make the player only update that part of the screen where there were changes made to the bitmap data?
Would it be possible to use copyPixels instead? It only allows copying from another BitmapData, but most of the time that should be fine (you could always cache animations to bitmapdata once, and then use the cached data instead of the Draw-method). Flash player should be able to redraw only the altered parts when you use copyPixels.
However, the real question; is this really an issue? Don't optimize these things unless you actually need to :)
If you're only redrawing part of the bitmap, then only that part should get updated by Flash Player.
You can see it in action here:
public class Test extends Sprite
{
public function Test()
{
stage.align = "topLeft";
stage.scaleMode = "noScale";
var bitmap:Bitmap = new Bitmap();
addChild(bitmap);
var bd:BitmapData = new BitmapData(400, 400);
bitmap.bitmapData = bd;
// One-second timer.
var timer:Timer = new Timer(1000);
timer.start();
timer.addEventListener("timer", timerHandler);
}
private function timerHandler(event:Event):void
{
var bitmap:Bitmap = Bitmap(getChildAt(0));
var bd:BitmapData = bitmap.bitmapData;
// Draw 100x100 square in random location, with random color.
var xPos:Number = Math.random() * 300;
var yPos:Number = Math.random() * 300;
var matrix:Matrix = new Matrix(1, 0, 0, 1, xPos, yPos);
var color:uint = Math.round(Math.random() * 0xFFFFFF) | 0xFF000000;
var colorTransform:ColorTransform = new ColorTransform();
colorTransform.color = color;
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.beginFill(0xFFFFFF);
g.drawRect(0, 0, 100, 100);
g.endFill();
bd.draw(shape, matrix, colorTransform);
}
}
Try this with "Show Redraw Regions" in the standalone Flash Player, and you'll see it updates only the newly drawn part on each tick.