I am trying to crop an image. Inorder to select the crop area I am using an rectangle element.
The initial width and height of the rectangle are set to 100 respectively. On pinch the size of the rectangle will be increased. How can i obtain the size of this rectangle, which is enlarged?
The code I am using is as follows:
private void GestureListener_PinchDelta(object sender, PinchGestureEventArgs e)
{
ImageTransformation.ScaleX = _initialScale * e.DistanceRatio;
ImageTransformation.ScaleY = ImageTransformation.ScaleX;
cropRectangle.Width = cropRectangle.Width + e.DistanceRatio;
cropRectangle.Height = cropRectangle.Height + e.DistanceRatio;
}
I am unable to obtain the size of the enlarged rectangle
RenderTransforms won't change Width & Height of your Control because it affects only the rendering. Not sure there is a better way but you can simply calculate the render size by using the original size and the scale factor.
Here is a very basic example
xaml
<Canvas x:Name="cnv" Grid.Row="1">
<Rectangle Canvas.Top="150" Canvas.Left="150"
Width="200" Height="200" x:Name="myrect"
Fill="AliceBlue"
ManipulationStarted="myrect_ManipulationStarted"
ManipulationDelta="myrect_ManipulationDelta"
ManipulationCompleted="myrect_ManipulationCompleted">
</Rectangle>
</Canvas>
code
public MainPage()
{
InitializeComponent();
transformGroup = new TransformGroup();
translation = new TranslateTransform();
scale = new ScaleTransform();
transformGroup.Children.Add(scale);
transformGroup.Children.Add(translation);
myrect.RenderTransform = transformGroup;
}
private TransformGroup transformGroup;
private TranslateTransform translation;
private ScaleTransform scale;
private void myrect_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
//change the color of the Rectangle to a half transparent Red
myrect.Fill = new SolidColorBrush(Color.FromArgb(127, 255, 0, 0));
}
private void myrect_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
translation.X += e.DeltaManipulation.Translation.X;
translation.Y += e.DeltaManipulation.Translation.Y;
//Scale the Rectangle
if (e.DeltaManipulation.Scale.X != 0)
scale.ScaleX *= e.DeltaManipulation.Scale.X;
if (e.DeltaManipulation.Scale.Y != 0)
scale.ScaleY *= e.DeltaManipulation.Scale.Y;
}
private void myrect_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
myrect.Fill = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
Debug.WriteLine(myrect.Width * scale.ScaleX);
Debug.WriteLine(myrect.Height * scale.ScaleY);
}
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());
while trying to implement logic that show current user location i encountered an issue.
<Maps:Pushpin Location="{Binding MyLocation}" Canvas.ZIndex="1000" PositionOrigin="Center" >
<Maps:Pushpin.Template>
<ControlTemplate>
<Grid>
<Ellipse Width="{Binding MyAccuracyViewSize}" Height="{Binding MyAccuracyViewSize}"
Fill="#60008000" Stroke="Green" StrokeThickness="3"/>
<Ellipse Width="18" Height="18" Fill="#A0FF4500" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</Maps:Pushpin.Template>
</Maps:Pushpin>
Bigger green circle shows accuracy area. Its size in pixels varies depending on zoom. If zoom level is big - it becomes rather big (> 480 pixels). At that point it gets cropped by screen resolution. AFAIK WP7 restriction is 2000x2000 px for control size.
Seems that this is a kind of a map control restriction.
Any ideas how to remove this restriction to show ellipse of size up to 2000x2000 px?
Thanks!
How about MapPolygon?
void OnPositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
SecondsCounter = 0; //reset counter
double accuracy = e.Position.Location.HorizontalAccuracy;
if (accuracy < e.Position.Location.VerticalAccuracy)
{
accuracy = e.Position.Location.VerticalAccuracy;
}
if (PolyCircle == null)
{
PolyCircle = new MapPolygon();
PolyCircle.Opacity = 0.7;
//Set the polygon properties
PolyCircle.Fill = new SolidColorBrush(Colors.Orange);
PolyCircle.Stroke = new SolidColorBrush(Colors.Purple);
PolyCircle.StrokeThickness = 4;
map1.Children.Add(PolyCircle);
}
PolyCircle.Locations = CreateCircle(e.Position.Location, accuracy);
map1.Center = e.Position.Location;
}
public static double ToRadian(double degrees)
{
return degrees * (Math.PI / 180);
}
public static double ToDegrees(double radians)
{
return radians * (180 / Math.PI);
}
public static LocationCollection CreateCircle(GeoCoordinate center, double radius)
{
var earthRadius = 6367000; // radius in meters
var lat = ToRadian(center.Latitude); //radians
var lng = ToRadian(center.Longitude); //radians
var d = radius / earthRadius; // d = angular distance covered on earth's surface
var locations = new LocationCollection();
for (var x = 0; x <= 360; x++)
{
var brng = ToRadian(x);
var latRadians = Math.Asin(Math.Sin(lat) * Math.Cos(d) + Math.Cos(lat) * Math.Sin(d) * Math.Cos(brng));
var lngRadians = lng + Math.Atan2(Math.Sin(brng) * Math.Sin(d) * Math.Cos(lat), Math.Cos(d) - Math.Sin(lat) * Math.Sin(latRadians));
locations.Add(new GeoCoordinate(ToDegrees(latRadians), ToDegrees(lngRadians)));
}
return locations;
}
More here, in My Map position example. Good luck!
I have a byte[] that stores my image.
Now I want to be able to scale it and store it in another byte[].
I know how to scale Bitmaps, and can convert my from byte[] to Bitmap.
But I cant get the scaled Bitmap back into a byte[].
Blackberries
EncodedImage thumbnail = image.scaleImageToFill(50, 50); does nothing.
Im trying to create a thumbnail of 50x50. doesnt have to be exact. That is stored in a byte[].
How do I resize an image stored in a byte[], and keep it in a new byte[].
byte[] imageTaken;
//Create thumbnail from image taken
EncodedImage image = EncodedImage.createEncodedImage(imageTaken, 0, -1);
image.getBitmap();
EncodedImage thumbnail = image.scaleImageToFill(50, 50);
byte[] thumbArray = thumbnail.getData();
try this code
bitmap = resizeImage(your image, 50,50);
private static EncodedImage resizeImage(EncodedImage image, int width, int height) {
if (image == null) {
return image;
}
//return if image does not need a resizing
if (image.getWidth() <= width && image.getHeight() <= height) {
return image;
}
double scaleHeight, scaleWidth;
if (image.getWidth() > width && image.getHeight() > height) { //actual image is bigger than scale size
if (image.getWidth() > image.getHeight()) { //actual image width is more that height then scale with width
scaleWidth = width;
scaleHeight = (double)width / image.getWidth() * image.getHeight();
} else { //scale with height
scaleHeight = height;
scaleWidth = (double)height / image.getHeight() * image.getWidth();
}
} else if (width < image.getWidth()) { //scale with scale width or height
scaleWidth = width;
scaleHeight = (double)width / image.getWidth() * image.getHeight();
} else {
scaleHeight = height;
scaleWidth = (double)height / image.getHeight() * image.getWidth();
}
int w = Fixed32.div(Fixed32.toFP(image.getWidth()), Fixed32.toFP((int)scaleWidth));
int h = Fixed32.div(Fixed32.toFP(image.getHeight()), Fixed32.toFP((int)scaleHeight));
return image.scaleImage32(w, h);
}
Then convert the bitmap to Bytes -
JPEGEncodedImage encoder=JPEGEncodedImage.encode(bitmap,100);
byte[] array=encoder.getData();
int length=array.length;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(length);
Base64OutputStream base64OutputStream = new Base64OutputStream( byteArrayOutputStream );
try{
base64OutputStream.write( array, 0, length );
base64OutputStream.flush();
base64OutputStream.close();
}
catch (IOException ioe){
//Dialog.alert("Error in encodeBase64() : "+ioe.toString());
System.out.println("Error in encodeBase64() : "+ioe.toString());
}
try {
byte[] data = Base64InputStream.decode(byteArrayOutputStream.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I have quadratic images (arround 2000x2000). I want to show them fullcreen on a page with the possibility to zoom in with "pintch to zoom". So the initial image would have a sice of 768 x 768.
<Image Name="displayImage" VerticalAlignment="Center" MinHeight="768" HorizontalAlignment="Center" Source="{Binding Image}" Stretch="UniformToFill" CacheMode="BitmapCache" ImageOpened="OnImageOpened">
<Image.RenderTransform>
<CompositeTransform x:Name="transform" ScaleX="1" ScaleY="1" />
</Image.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener PinchDelta="OnPinchDelta" PinchStarted="OnPinchStarted" DragDelta="OnDragDelta" />
</toolkit:GestureService.GestureListener>
</Image>
.
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
var image = sender as Image;
if (image == null) return;
initialScale = transform.ScaleX;
Point firstTouch = e.GetPosition(image, 0);
Point secondTouch = e.GetPosition(image, 1);
center = new Point(firstTouch.X + (secondTouch.X - firstTouch.X)/2.0,
firstTouch.Y + (secondTouch.Y - firstTouch.Y)/2.0);
}
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
var image = sender as Image;
if (image == null) return;
if (initialScale*e.DistanceRatio > 4 || (initialScale != 1 && e.DistanceRatio == 1) ||
initialScale*e.DistanceRatio < 1)
return;
if (e.DistanceRatio <= 1.08)
{
transform.CenterY = 0;
transform.CenterY = 0;
transform.TranslateX = 0;
transform.TranslateY = 0;
}
transform.CenterX = center.X;
transform.CenterY = center.Y;
transform.ScaleX = (Orientation == PageOrientation.Landscape)
? initialScale*(1 + (e.DistanceRatio - 1)*2)
: initialScale*e.DistanceRatio;
transform.ScaleY = transform.ScaleX;
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
var image = sender as Image;
if (image == null || transform.ScaleX <= 1.1) return;
double centerX = transform.CenterX;
double centerY = transform.CenterY;
double translateX = transform.TranslateX;
double translateY = transform.TranslateY;
double scale = transform.ScaleX;
double width = image.ActualWidth;
double height = image.ActualHeight;
if (centerX - scale*centerX + translateX + e.HorizontalChange < 0 &&
centerX + scale*(width - centerX) + translateX + e.HorizontalChange > width)
{
transform.TranslateX += e.HorizontalChange;
}
if (centerY - scale*centerY + translateY + e.VerticalChange < 0 &&
centerY + scale*(height - centerY) + translateY + e.VerticalChange > height)
{
transform.TranslateY += e.VerticalChange;
}
return;
}
Basically what I have now is a picture, with no borders (fullscreen) and I can zoom in. But the problem is, that the images is quadratic, so on the left and right there is something missing and I can't "scroll" (or better move the picture) to the left or right. Moving the picture arround only works when I have zoomed in, but I never see the missing parts of the image. Any ideas how to solve this?
I am not sure if it solves your problem. But is your image on a GRID or on a CANVAS?When it is on a grid the content is clipped. A Canvas can be much larger.
I have a class that extends from ButtonField :
class BitmapButtonField extends ButtonField
{
private Bitmap _bitmap;
private int _buttonWidth;
private int _buttonHeight;
BitmapButtonField(Bitmap bitmap, int buttonWidth, int buttonHeight, long style)
{
super(style);
_buttonWidth = buttonWidth;
_buttonHeight = buttonHeight;
_bitmap = bitmap;
}
public int getPreferredHeight()
{
return _buttonHeight;
}
public int getPreferredWidth()
{
return _buttonWidth;
}
protected void layout(int width, int height)
{
setExtent(Math.min( width, getPreferredWidth()), Math.min( height, getPreferredHeight()));
}
protected void paint(Graphics graphics)
{
// THIS IS NOT CENTERED
int x = (getPreferredWidth() - _bitmap.getWidth()) >> 1;
graphics.drawBitmap(x, 0, _bitmap.getWidth(), _bitmap.getHeight(), _bitmap, 0, 0);
// THIS IS NOT LEFTMOST, TOPMOST
graphics.drawBitmap(0, 0, _bitmap.getWidth(), _bitmap.getHeight(), _bitmap, 0, 0);
}
}
If you could see from my comments on the paint method, clearly the ButtonField 0,0 position is not exactly in leftmost and topmost corner of the button, somehow it is padded with unknown offset, so this makes centering the image is difficult.
But if I extend from a Field class, the problem goes away, but I need to keep extending from ButtonField since I need the ButtonField border and focus style (blue-white rounded rectangle), I just need to display centered image on a ButtonField, and still retaining all of the standard ButtonField attributes.
Is there a way to eliminate the padded offset in paint method on a ButtonField? I've tried setPadding and setMargin but no such luck. Thanks a lot!
In paint() to define x offset for image there is a code:
int x = (getPreferredWidth() - _bitmap.getWidth()) >> 1;
It is ok, still button can have other size, because of:
protected void layout(int width, int height)
{
setExtent(Math.min( width, getPreferredWidth()),
Math.min( height, getPreferredHeight()));
}
Try to use
protected void layout(int width, int height)
{
setExtent(getPreferredWidth(), getPreferredHeight());
}