If i mirror an image with the canvas multiple times, why does the image lose quality? - image

mirror() {
const mirrorCanvas = document.createElement('canvas') as HTMLCanvasElement;
const clientRect = this.parentRef.nativeElement.getBoundingClientRect();
const image = new Image();
image.src = this.imageBase64;
image.onload = () => {
mirrorCanvas.width = clientRect.width;
mirrorCanvas.height = clientRect.height;
const ctx = mirrorCanvas.getContext('2d');
if (ctx) {
ctx.scale(1, -1);
ctx.drawImage(image, 0, 0, clientRect.width, clientRect.height);
this.imageBase64 = (mirrorCanvas.toDataURL(`image/${this.format}`, 1));
}
}
}
I have this code, which mirrors the image.
However if i keep mirroring this image, it will lose quality, even if i set the quality to 1.
Why is that?

Related

How to set object position to be center of its mass?

Right now, when I upload my model, its center(highlighted by transformControls) is not in the model's center of mass. How can I shift it there?
I tried this:
(model) => {
box = new Box3();
box.expandByObject(model);
const centerOfBox = new Vector3();
box.getCenter(centerOfBox);
const size = new Vector3();
box.getSize(size);
const centerOfWorld = new Vector3(0, size.y/2, 0);
const offset = new Vector3();
offset.subVectors(centerOfWorld, centerOfBox);
const scale = model.scale;
model.translateX(scale.x*offset.x);
model.translateY(scale.y*offset.y);
model.translateZ(scale.z*offset.z);
}
and also this:
(model) => {
model.traverse(function(child) {
const rememberPosition = child.getWorldPosition(new Vector3());
const box = new Box3();
box.expandByObject(child);
const centerOfBox = new Vector3();
box.getCenter(centerOfBox);
const offset = new Vector3();
offset.subVectors(rememberPosition, centerOfBox);
child.traverse(function(ch) {
if (child instanceof Mesh) {
ch.geometry.translate(offset.x, offset.y, offset.z);
ch.translateX(-offset.x);
ch.translateY(-offset.y);
ch.translateZ(-offset.z);
}
});
});
model.position.copy(new Vector3());
}
In the latter version, I tried to shift the geometry to be at the object's position and then shift everything (geometry+position) back to the original place (original place = visibly original place).

Xamarin: Unable to extract central square of photo in Android/iOS

I am trying to get an image using the camera. The image is to be 256x256 and I want it to come from the centre of a photo taken using the camera on a phone. I found this code at: https://forums.xamarin.com/discussion/37647/cross-platform-crop-image-view
I am using this code for Android...
public byte[] CropPhoto(byte[] photoToCropBytes, Rectangle rectangleToCrop, double outputWidth, double outputHeight)
{
using (var photoOutputStream = new MemoryStream())
{
// Load the bitmap
var inSampleSize = CalculateInSampleSize((int)rectangleToCrop.Width, (int)rectangleToCrop.Height, (int)outputWidth, (int)outputHeight);
var options = new BitmapFactory.Options();
options.InSampleSize = inSampleSize;
//options.InPurgeable = true; see http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
using (var photoToCropBitmap = BitmapFactory.DecodeByteArray(photoToCropBytes, 0, photoToCropBytes.Length, options))
{
var matrix = new Matrix();
var martixScale = outputWidth / rectangleToCrop.Width * inSampleSize;
matrix.PostScale((float)martixScale, (float)martixScale);
using (var photoCroppedBitmap = Bitmap.CreateBitmap(photoToCropBitmap, (int)(rectangleToCrop.X / inSampleSize), (int)(rectangleToCrop.Y / inSampleSize), (int)(rectangleToCrop.Width / inSampleSize), (int)(rectangleToCrop.Height / inSampleSize), matrix, true))
{
photoCroppedBitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, photoOutputStream);
}
}
return photoOutputStream.ToArray();
}
}
public static int CalculateInSampleSize(int inputWidth, int inputHeight, int outputWidth, int outputHeight)
{
//see http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
int inSampleSize = 1; //default
if (inputHeight > outputHeight || inputWidth > outputWidth) {
int halfHeight = inputHeight / 2;
int halfWidth = inputWidth / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > outputHeight && (halfWidth / inSampleSize) > outputWidth)
{
inSampleSize *= 2;
}
}
return inSampleSize;
}
and this code for iOS...
public byte[] CropPhoto(byte[] photoToCropBytes, Xamarin.Forms.Rectangle
rectangleToCrop, double outputWidth, double outputHeight)
{
byte[] photoOutputBytes;
using (var data = NSData.FromArray(photoToCropBytes))
{
using (var photoToCropCGImage = UIImage.LoadFromData(data).CGImage)
{
//crop image
using (var photoCroppedCGImage = photoToCropCGImage.WithImageInRect(new CGRect((nfloat)rectangleToCrop.X, (nfloat)rectangleToCrop.Y, (nfloat)rectangleToCrop.Width, (nfloat)rectangleToCrop.Height)))
{
using (var photoCroppedUIImage = UIImage.FromImage(photoCroppedCGImage))
{
//create a 24bit RGB image to the output size
using (var cGBitmapContext = new CGBitmapContext(IntPtr.Zero, (int)outputWidth, (int)outputHeight, 8, (int)(4 * outputWidth), CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedFirst))
{
var photoOutputRectangleF = new RectangleF(0f, 0f, (float)outputWidth, (float)outputHeight);
// draw the cropped photo resized
cGBitmapContext.DrawImage(photoOutputRectangleF, photoCroppedUIImage.CGImage);
//get cropped resized photo
var photoOutputUIImage = UIKit.UIImage.FromImage(cGBitmapContext.ToImage());
//convert cropped resized photo to bytes and then stream
using (var photoOutputNsData = photoOutputUIImage.AsJPEG())
{
photoOutputBytes = new Byte[photoOutputNsData.Length];
System.Runtime.InteropServices.Marshal.Copy(photoOutputNsData.Bytes, photoOutputBytes, 0, Convert.ToInt32(photoOutputNsData.Length));
}
}
}
}
}
}
return photoOutputBytes;
}
I am struggling to work out exactly what the parameters are to call the function.
Currently, I am doing the following:
double cropSize = Math.Min(DeviceDisplay.MainDisplayInfo.Width, DeviceDisplay.MainDisplayInfo.Height);
double left = (DeviceDisplay.MainDisplayInfo.Width - cropSize) / 2.0;
double top = (DeviceDisplay.MainDisplayInfo.Height - cropSize) / 2.0;
// Get a square resized and cropped from the top image as a byte[]
_imageData = mediaService.CropPhoto(_imageData, new Rectangle(left, top, cropSize, cropSize), 256, 256);
I was expecting this to crop the image to the central square (in portrait mode side length would be the width of the photo) and then scale it down to a 256x256 image. But it never picks the centre of the image.
Has anyone ever used this code and can tell me what I need to pass in for the 'rectangleToCrop' parameter?
Note: Both Android and iOS give the same image, just not the central part that I was expecting.
Here are the two routines I used:
Android:
public byte[] ResizeImageAndCropToSquare(byte[] rawPhoto, int outputSize)
{
// Create object of bitmapfactory's option method for further option use
BitmapFactory.Options options = new BitmapFactory.Options();
// InPurgeable is used to free up memory while required
options.InPurgeable = true;
// Get the original image
using (var originalImage = BitmapFactory.DecodeByteArray(rawPhoto, 0, rawPhoto.Length, options))
{
// The shortest edge will determine the size of the square image
int cropSize = Math.Min(originalImage.Width, originalImage.Height);
int left = (originalImage.Width - cropSize) / 2;
int top = (originalImage.Height - cropSize) / 2;
using (var squareImage = Bitmap.CreateBitmap(originalImage, left, top, cropSize, cropSize))
{
// Resize the square image to the correct size of an Avatar
using (var resizedImage = Bitmap.CreateScaledBitmap(squareImage, outputSize, outputSize, true))
{
// Return the raw data of the resized image
using (MemoryStream resizedImageStream = new MemoryStream())
{
// Resize the image maintaining 100% quality
resizedImage.Compress(Bitmap.CompressFormat.Png, 100, resizedImageStream);
return resizedImageStream.ToArray();
}
}
}
}
}
iOS:
private const int BitsPerComponent = 8;
public byte[] ResizeImageAndCropToSquare(byte[] rawPhoto, int outputSize)
{
using (var data = NSData.FromArray(rawPhoto))
{
using (var photoToCrop = UIImage.LoadFromData(data).CGImage)
{
nint photoWidth = photoToCrop.Width;
nint photoHeight = photoToCrop.Height;
nint cropSize = photoWidth < photoHeight ? photoWidth : photoHeight;
nint left = (photoWidth - cropSize) / 2;
nint top = (photoHeight - cropSize) / 2;
// Crop image
using (var photoCropped = photoToCrop.WithImageInRect(new CGRect(left, top, cropSize, cropSize)))
{
using (var photoCroppedUIImage = UIImage.FromImage(photoCropped))
{
// Create a 24bit RGB image of output size
using (var cGBitmapContext = new CGBitmapContext(IntPtr.Zero, outputSize, outputSize, BitsPerComponent, outputSize << 2, CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedFirst))
{
var photoOutputRectangleF = new RectangleF(0f, 0f, outputSize, outputSize);
// Draw the cropped photo resized
cGBitmapContext.DrawImage(photoOutputRectangleF, photoCroppedUIImage.CGImage);
// Get cropped resized photo
var photoOutputUIImage = UIImage.FromImage(cGBitmapContext.ToImage());
// Convert cropped resized photo to bytes and then stream
using (var photoOutputNsData = photoOutputUIImage.AsPNG())
{
var rawOutput = new byte[photoOutputNsData.Length];
Marshal.Copy(photoOutputNsData.Bytes, rawOutput, 0, Convert.ToInt32(photoOutputNsData.Length));
return rawOutput;
}
}
}
}
}
}
}

Add a texture on a sphere by Urhosharp

I'm using Xamarin.Forms + Urhosharp, I've got a problem to set texture from an image on a sphere. The problem is that texture.Load or texture.SetData always returns false. I did try different methods like SetData, Load, resize texture and image (to a power of 2 number) and ... but none of them worked. Here is my code:
private async void CreateScene()
{
Input.SubscribeToTouchEnd(OnTouched);
_scene = new Scene();
_octree = _scene.CreateComponent<Octree>();
_plotNode = _scene.CreateChild();
var baseNode = _plotNode.CreateChild().CreateChild();
var plane = baseNode.CreateComponent<StaticModel>();
plane.Model = CoreAssets.Models.Sphere;
var cameraNode = _scene.CreateChild();
_camera = cameraNode.CreateComponent<Camera>();
cameraNode.Position = new Vector3(10, 15, 10) / 1.75f;
cameraNode.Rotation = new Quaternion(-0.121f, 0.878f, -0.305f, -0.35f);
Node lightNode = cameraNode.CreateChild();
var light = lightNode.CreateComponent<Light>();
light.LightType = LightType.Point;
light.Range = 100;
light.Brightness = 1.3f;
int size = 3;
baseNode.Scale = new Vector3(size * 1.5f, 1, size * 1.5f);
var imageStream = await new HttpClient().GetStreamAsync("some 512 * 512 jpg image");
var ms = new MemoryStream();
imageStream.CopyTo(ms);
var image = new Image();
var isLoaded = image.Load(new MemoryBuffer(ms));
if (!isLoaded)
{
throw new Exception();
}
var texture = new Texture2D();
//var isTextureLoaded = texture.Load(new MemoryBuffer(ms.ToArray()));
var isTextureLoaded = texture.SetData(image);
if (!isTextureLoaded)
{
throw new Exception();
}
var material = new Material();
material.SetTexture(TextureUnit.Diffuse, texture);
material.SetTechnique(0, CoreAssets.Techniques.Diff, 0, 0);
plane.SetMaterial(material);
try
{
await _plotNode.RunActionsAsync(new EaseBackOut(new RotateBy(2f, 0, 360, 0)));
}
catch (OperationCanceledException) { }
}
Please help!
To Create a material from a 2D Texture, you can use Material.FromImage .
Refer following documentation for more detail.
model.SetMaterial(Material.FromImage("earth.jpg"));
https://developer.xamarin.com/api/type/Urho.Material/
https://developer.xamarin.com/api/member/Urho.Material.FromImage/p/System.String/
private async void CreateScene()
{
_scene = new Scene();
_octree = _scene.CreateComponent<Octree>();
_plotNode = _scene.CreateChild();
var baseNode = _plotNode.CreateChild().CreateChild();
var plane = _plotNode.CreateComponent<StaticModel>();
plane.Model = CoreAssets.Models.Sphere;
plane.SetMaterial(Material.FromImage("earth.jpg"));
var cameraNode = _scene.CreateChild();
_camera = cameraNode.CreateComponent<Camera>();
cameraNode.Position = new Vector3(10, 15, 10) / 1.75f;
cameraNode.Rotation = new Quaternion(-0.121f, 0.878f, -0.305f, -0.35f);
Node lightNode = cameraNode.CreateChild();
var light = lightNode.CreateComponent<Light>();
light.LightType = LightType.Point;
light.Range = 100;
light.Brightness = 1.3f;
int size = 3;
baseNode.Scale = new Vector3(size * 1.5f, 1, size * 1.5f);
Renderer.SetViewport(0, new Viewport(_scene, _camera, null));
try
{
await _plotNode.RunActionsAsync(new EaseBackOut(new RotateBy(2f, 0, 360, 0)));
}
catch (OperationCanceledException) { }
}

Resizing dynamically loaded images Flash AS3

I have a series of banners (standard sizes) which all need to load the same corresponding image for each slide. I can load them fine but I want the image to match the size of the container MC that the image is being loaded to, is that possible? Either that or to set the height/width manually...
Everything I have tried doesnt work, here is the code for the working state (where it just loads the image which stretches across the stage)
Code:
var myImage:String = dynamicContent.Profile[0].propImage.Url;
function myImageLoader(file:String, x:Number, y:Number):StudioLoader{
var myImageLoader:StudioLoader = new StudioLoader();
var request:URLRequest = new URLRequest(file);
myImageLoader.load(request);
myImageLoader.x = -52;
myImageLoader.y =-30;
return myImageLoader;
}
propImage1.addChild(loadImage(enabler.getUrl(myImage),-20,0));
You can resize your loaded image after the Event.COMPLETE on the LoaderInfo (contentLoaderInfo) of your URLLoader is fired, so you can do like this :
var request:URLRequest = new URLRequest('http://www.example.com/image.jpg');
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_loadComplete);
function on_loadComplete(e:Event):void {
var image:DisplayObject = loader.content;
image.x = 100;
image.y = 100;
image.width = 300;
image.height = 200;
addChild(image);
}
loader.load(request);
Edit :
load_image('http://www.example.com/image.jpg');
function load_image(url){
var request:URLRequest = new URLRequest(url);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_loadComplete);
function on_loadComplete(e:Event):void {
add_image(loader.content);
}
loader.load(request);
}
function add_image(image:DisplayObject, _x:int = 0, _y:int = 0, _width:int = 100, _height:int = 100){
image.x = _x;
image.y = _y;
image.width = _width;
image.height = _height;
addChild(image);
}
Hope that can help.

Windows Phone - update live tile from background agent with custom image

I am trying to add cloud image to album cover if the cover is loaded from internet. I am trying to do this in Background Audio agent and I think I almost got it. The problem is that I have black image in tile. Few times when testing I got cover image with my cloud image in it but mostly I get black image (and sometimes black image with cloud in it).
Can anyone help me find the problem? Thanks
private void UpdateAppTile()
{
var apptile = ShellTile.ActiveTiles.First();
if (apptile != null && _playList != null && _playList.Any())
{
var track = _playList[currentTrackNumber];
var size = 360;
Uri coverUrl;
if (track.AlbumArt.OriginalString.StartsWith("http"))
{
BitmapImage img = null;
using (AutoResetEvent are = new AutoResetEvent(false))
{
string filename = Path.GetFileNameWithoutExtension(track.AlbumArt.OriginalString);
var urlToNewCover = String.Format("http://.../{0}/{1}", filename, size);
coverUrl = new Uri(urlToNewCover, UriKind.Absolute);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
img = new BitmapImage(coverUrl);
are.Set();
});
are.WaitOne();
var wbmp = CreateTileImageWithCloud(img);
SaveTileImage(wbmp, "/shared/shellcontent/test.jpg");
coverUrl = new Uri("isostore:/shared/shellcontent/test.jpg", UriKind.RelativeOrAbsolute);
}
}
else
{
var coverId = track.Tag.Split(',')[1];
var urlToNewCover = String.Format("http://.../{0}/{1}", coverId, size);
coverUrl = new Uri(urlToNewCover, UriKind.Absolute);
}
var appTileData = new FlipTileData
{
BackgroundImage = coverUrl,
WideBackgroundImage = coverUrl,
...
}
apptile.Update(appTileData);
}
}
public static BitmapImage LoadBitmap(string iFilename)
{
Uri imgUri = new Uri(iFilename, UriKind.Relative);
StreamResourceInfo imageResource = Application.GetResourceStream(imgUri);
BitmapImage image = new BitmapImage();
image.SetSource(imageResource.Stream);
return image;
}
private void SaveTileImage(WriteableBitmap wbmp, string filename)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.FileExists(filename))
store.DeleteFile(filename);
var stream = store.OpenFile(filename, FileMode.OpenOrCreate);
wbmp.SaveJpeg(stream, wbmp.PixelWidth, wbmp.PixelHeight, 100, 100);
stream.Close();
}
}
private WriteableBitmap CreateTileImageWithCloud(BitmapImage img)
{
Image image = null;
WriteableBitmap wbmp = null;
using (AutoResetEvent are = new AutoResetEvent(false))
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
image = new Image { Source = img };
Canvas.SetLeft(image, 0);
Canvas.SetTop(image, 0);
var cloud = new BitmapImage(new Uri("Assets/Images/Other/Cloud_no.png", UriKind.Relative));
var cloudImg = new Image { Source = cloud };
Canvas.SetLeft(cloudImg, 125);
Canvas.SetTop(cloudImg, 10);
var canvas = new Canvas
{
Height = 176,
Width = 176
};
canvas.Children.Add(image);
canvas.Children.Add(cloudImg);
wbmp = new WriteableBitmap(176, 176);
wbmp.Render(canvas, null);
wbmp.Invalidate();
are.Set();
});
are.WaitOne();
}
return wbmp;
}
Edit
I found little pattern in which this is working and in which not. When application is running and I called this twice (in TrackReady and SkipNext) then I very often get cover image with cloud. When I am running just background agent (without running app) I get always black image. And often first UpdateAppTile call is just black image and second it's black image with cloud. That black color is default canvas background so I guess I have problems with delay when loading cover image from url. But I am not sure how in my case use ImageOpened event and if it help.
I think that you should call Measure and Arrange after adding elements to canvas and before rendering canvas (as with other UIElements):
canvas.Measure( new Size( Width, Height ) );
canvas.Arrange( new Rect( 0, 0, Width, Height ) );

Resources