TransformGestureEvent zoom on image but keep image size Flex - image

I'm having problems finding the exact answer I'm looking for. I'm building an application that currently allows pinch zooming on an image. This is being built in Flash Builder 4.5. What I want specifically is to keep the image the same dimensions while zooming in and out. So the image still increases and decreases but stays within an invisible box.
Does that explanation make sense?
Right now I can zoom in and out but it changes the image size and covers the other content. Eventually other features will be added including pan and rotate. The image will become a 3D model of a mouth.
If tutorials or sources are supplied that would be great.
Here's my code for the zoom gesture which is using the TransformGestureEvent:
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.TransformGestureEvent;
import flash.ui.Multitouch;
import flash.ui.MultitouchInputMode;
private function init():void
{
img.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
}
private function onZoom(event:TransformGestureEvent):void
{
var mySprite:Sprite = img as Sprite;
if(mySprite.scaleY.valueOf() >= 1)
{
mySprite.scaleX *= event.scaleX;
mySprite.scaleY *= event.scaleY;
}
else
{
mySprite.scaleX= 1;
mySprite.scaleY = 1;
}
}

If i understand you question correctly.
I think you may want to look at scrollRect
http://gskinner.com/blog/archives/2006/11/understanding_d.html
DisplayObject.scrollRect lets you specify a Rectangle object that defines the region of the DisplayObject (Sprite, MovieClip, etc) to display. The masked region is always displayed at the origin of the display object, which lets you scroll the region by changing the x and y properties of the rectangle.

Related

Is it possible to save a generated image in Codename One?

My question is related to this previous question. What I want to achieve is to stack images (they have transparency), write a string on top, and save the photomontage / photocollage with full resolution.
#Override
protected void beforeMain(Form f) {
Image photoBase = fetchResourceFile().getImage("Voiture_4_3.jpg");
Image watermark = fetchResourceFile().getImage("Watermark.png");
f.setLayout(new LayeredLayout());
final Label drawing = new Label();
f.addComponent(drawing);
// Image mutable dans laquelle on va dessiner (fond blanc)
Image mutableImage = Image.createImage(photoBase.getWidth(), photoBase.getHeight());
drawing.getUnselectedStyle().setBgImage(mutableImage);
drawing.getUnselectedStyle().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
// Paint all the stuff
paints(mutableImage.getGraphics(), photoBase, watermark, photoBase.getWidth(), photoBase.getHeight());
// Save the collage
Image screenshot = Image.createImage(photoBase.getWidth(), photoBase.getHeight());
f.revalidate();
f.setVisible(true);
drawing.paintComponent(screenshot.getGraphics(), true);
String imageFile = FileSystemStorage.getInstance().getAppHomePath() + "screenshot.png";
try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(imageFile)) {
ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1);
} catch(IOException err) {
err.printStackTrace();
}
}
public void paints(Graphics g, Image background, Image watermark, int width, int height) {
g.drawImage(background, 0, 0);
g.drawImage(watermark, 0, 0);
g.setColor(0xFF0000);
// Upper left corner
g.fillRect(0, 0, 10, 10);
// Lower right corner
g.setColor(0x00FF00);
g.fillRect(width - 10, height - 10, 10, 10);
g.setColor(0xFF0000);
Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(220, Font.STYLE_BOLD);
g.setFont(f);
// Draw a string right below the M from Mercedes on the car windscreen (measured in Gimp)
g.drawString("HelloWorld",
(int) (848 ),
(int) (610)
);
}
This is the saved screenshot I get if I use the Iphone6 skin (the payload image is smaller than the original one and is centered). If I use the Xoom skin this is what I get (the payload image is still smaller than the original image but it has moved to the left).
So to sum it all up : why is the saved screenshot with Xoom skin different from the one I get with Iphone skin ? Is there anyway to directly save the graphics on which I paint in the paints method so that the saved image would have the original dimensions ?
Thanks a lot to anyone that could help me :-)!
Cheers,
You can save an image in Codename one using the ImageIO class. Notice that you can draw a container hierarchy into a mutable image using the paintComponent(Graphics) method.
You can do both approaches with draw image on mutable or via layouts. Personally I always prefer layouts as I like the abstraction but I wouldn't say the mutable image approach is right/wrong.
Notice that if you change/repaint a lot then mutable images are slower (this will not be noticeable for regular code or on the simulator) as they are forced to use the software renderer and can't use the GPU fully.
In the previous question it seems you placed the image with a "FIT" style which naturally drew it smaller than the containing container and then drew the image on top of it manually... This is problematic.
One solution is to draw everything manually but then you will need to do the "fit" aspect of drawing yourself. If you use layouts you should position everything based on the layouts including your drawing/text.

Create an image-overlay mask in javafx

I'm trying to do a simple thing. I have a binary image and all I want is to overlay the binary image on a color image, but the white pixels in the binary image should be red, and the black transparent.
I'm quite used to JavaFx but I'm stuck with this one. I know I could achieve it by iterating through all pixels with a PixelReader, but I'm sure there is an easier way. I tried to use some sort of Blend-effect but no luck so far.
I think it should be similar to this:
How to Blend two Image in javaFX
I came up with this:
Image image = new Image("/circle.jpg", false);
ImageView iv = new ImageView(image);
Image mask = new Image("/mask.jpg", false);
ImageView ivMask = new ImageView(mask);
Rectangle r = new Rectangle(mask.getWidth(), mask.getHeight());
r.setFill(Color.RED);
r.setBlendMode(BlendMode.MULTIPLY); // sets the white area red
Group g = new Group(ivMask, r); // sets the white area red
// this is not working as expected
iv.setBlendMode(BlendMode.DIFFERENCE);
Group g2 = new Group(iv, g);
Thanks for any suggestions!
If you think, processing pixel-wise is faster than just creating an overlay, please let me know.
Solution by pixel-reader would be:
Pane root = new Pane();
// read the underlaying image
root.getChildren().add(new ImageView(new Image("/src.jpg")));
Image mask = new Image("/mask.jpg");
PixelReader pixelReader = mask.getPixelReader();
Canvas resultCanvas = new Canvas();
root.getChildren().add(resultCanvas);
GraphicsContext resultLayer = resultCanvas.getGraphicsContext2D();
for (int y = 0; y < mask.getHeight(); y++) {
for (int x = 0; x < mask.getWidth(); x++) {
if( pixelReader.getColor(x, y).equals(Color.WHITE) ){
resultLayer.fillRect(x, y, 1.0, 1.0);
}
}
}
Cheers!
What you are Doing Wrong
The difference operator isn't a binary difference based on whether a pixel is set instead it is a difference in the RGB components, so instead of a solid red overlay, you will get a multi-colored overlay because the difference in the RGB components of the blended images differs between pixels.
Background
You are trying to do something similar to a masked bit-blit operation with blend modes (basically, an OR then an AND of pixel data based on a white on black mask). It is possible though a little tricky with the built-in blends in JavaFX 8.
You could create a feature request for additional support in the blend API for bit-blt style basics as well as exposing a full porter duff compositing implementation like Swing has so that the underlying blend engine has a bit more power and is possibly a little easier to use.
Alternatives
The preferred thing to do would be to pre-process your mask in an image editor like photoshop to convert the black part to an alpha channel - then you can just layer your mask on top of your original and the default compositing mode will take of it.
To make your alpha enabled mask red, you could just use mask.setBlendMode(BlendMode.RED) (or you could pre-color the mask in an image editor before using it in your program).
Another alternative is the PixelReader solution you have in your question (which I think is fine if you are unable to pre-convert your mask to use alpha).
The blend operations can be hardware accelerated on appropriate hardware. So potentially using a blend could be faster if you are doing it very often (but you would have to have many blends being run very quickly on large images to really notice any kind of performance difference).
Sample Solution Using Blend Operations
Sample Output
Input Images
original.jpg
stencil.jpg
Code
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Blended extends Application {
#Override
public void start(Stage stage) {
Image original = new Image(
getClass().getResourceAsStream("original.jpg")
);
Image stencil = new Image(
getClass().getResourceAsStream("stencil.jpg")
);
// first invert the stencil so that it is black on white rather than white on black.
Rectangle whiteRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
whiteRect.setFill(Color.WHITE);
whiteRect.setBlendMode(BlendMode.DIFFERENCE);
Group inverted = new Group(
new ImageView(stencil),
whiteRect
);
// overlay the black portions of the inverted mask onto the image.
inverted.setBlendMode(BlendMode.MULTIPLY);
Group overlaidBlack = new Group(
new ImageView(original),
inverted
);
// create a new mask with a red tint (red on black).
Rectangle redRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
redRect.setFill(Color.RED);
redRect.setBlendMode(BlendMode.MULTIPLY);
Group redStencil = new Group(
new ImageView(stencil),
redRect
);
// overlay the red mask on to the image.
redStencil.setBlendMode(BlendMode.ADD);
Group overlaidRed = new Group(
overlaidBlack,
redStencil
);
// display the original, composite image and stencil.
HBox layout = new HBox(10);
layout.getChildren().addAll(
new ImageView(original),
overlaidRed,
new ImageView(stencil)
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}

What make scaled stage cost so much?

set the flash object's size to 100% on web page,and the stage scale to show_all
stage.scaleMode = StageScaleMode.SHOW_ALL;
load the page on browser, change the browser window size, so the flash size also change
give an example program to keep it redraw every frame in the whole stage area.
on some low spec pc, run this flash would cost 90% or higher cpu resource
package
{
import flash.display.BlendMode;
import flash.display.GradientType;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
[SWF(width="1200", height="600", frameRate="24")]
public class Test5 extends Sprite
{
public function Test5()
{
var g:Graphics = this.graphics;
g.beginFill(0x4000);
g.drawRect(0,0,1200,600);
g.endFill();
var sp:Sprite = new Sprite();
sp.alpha = 0.5;
sp.blendMode = BlendMode.LAYER;
sp.cacheAsBitmap = false;
this.addChild(sp);
g = sp.graphics;
var matrix:Matrix = new Matrix();
matrix.createGradientBox(1200,600,0,0,0);
var count:int = 0;
this.addEventListener(Event.ENTER_FRAME,function(e:Event):void{
g.clear();
g.beginGradientFill(GradientType.RADIAL,[0xff,0],[1,0.3],[count++%256,255],matrix);
g.drawRect(0,0,1200,600);
});
}
}
}
if shrink the window's size ,the cost will come down significantly.if enlarge it to 1920x1080,the fps will go down.
same thing happened for Bitmap too,like moving a stage size cloud picture from one side to another side.
my question is why does view size cost so much?since the stagewidth and stageheight didn't change,so i suppose the flash player only scale the final result and bitblt.but maybe i was wrong, can anyone explain it for me?
It has to render a lot more if you scale up the window?
Rendering graphics is probably the most processor intensive thing that Flash does, so increasing the amount of content it needs to draw onto the screen is going to hurt performance.

Starling TouchEvent on Sprite

I have some images/sprites/buttons (i tried them all :)) scrolling/moving on the stage. The user has to tap them to remove them.
The problem is that the touchevent does not match the position of the image.
The faster the touchable sprites move, the further the distance between the touch and the actual image. i.e.:
a image is moving across the screen with 20px/frame:
Touching the image triggers nothing, touching 20 before it triggers a touch on the image.
when image is not moving, this doesn't happen. It seems that the image is already moved internally, but not yet drawn to the screen, but thats just a wild guess. The example below uses a button, but the same goes for an image. I"ll provide a short example of the code, but i guess its pretty straightforward what i'm trying to do.
private var _image:Button;
protected function init():void {
//create pickup image
_image = new Button(assets.getTexture("button"));
_image.scaleWhenDown = 1;
_image.addEventListener(Event.TRIGGERED, onClick_image);
addChild(_image);
//listen to touch event (not used, but example for touch on image instead of button
//touchable = true;
//addEventListener(TouchEvent.TOUCH, onTouch_image);
}
private function onEnter_frame(e:Event):void {
_image.x -= 20;
}

Zoom toward mouse (eg. Google maps)

I've written a home-brew view_port class for a 2D strategy game. The panning (with arrow keys) and zooming (with mouse wheel) work fine, but I'd like the view to also home towards wherever the cursor is placed, as in Google Maps or Supreme Commander
I'll spare you the specifics of how the zoom is implemented and even what language I'm using: this is all irrelevant. What's important is the zoom function, which modifies the rectangle structure (x,y,w,h) that represents the view. So far the code looks like this:
void zoom(float delta, float mouse_x, float mouse_y)
{
zoom += delta;
view.w = window.w/zoom;
view.h = window.h/zoom;
// view.x = ???
// view.y = ???
}
Before somebody suggests it, the following will not work:
view.x = mouse_x - view.w/2;
view.y = mouse_y - view.h/2;
This picture illustrates why, as I attempt to zoom towards the smiley face:
As you can see when the object underneath the mouse is placed in the centre of the screen it stops being under the mouse, so we stop zooming towards it!
If you've got a head for maths (you'll need one) any help on this would be most appreciated!
I managed to figure out the solution, thanks to a lot of head-scratching a lot of little picture: I'll post the algorithm here in case anybody else needs it.
Vect2f mouse_true(mouse_position.x/zoom + view.x, mouse_position.y/zoom + view.y);
Vect2f mouse_relative(window_size.x/mouse_pos.x, window_size.y/mouse_pos.y);
view.x = mouse_true.x - view.w/mouse_relative.x;
view.y = mouse_true.y - view.h/mouse_relative.y;
This ensures that objects placed under the mouse stay under the mouse. You can check out the code over on github, and I also made a showcase demo for youtube.
In my concept there is a camera and a screen.
The camera is the moving part. The screen is the scalable part.
I made an example script including a live demo.
The problem is reduced to only one dimension in order to keep it simple.
https://www.khanacademy.org/cs/cam-positioning/4772921545326592
var a = (mouse.x + camera.x) / zoom;
// now increase the zoom e.g.: like that:
zoom = zoom + 1;
var newPosition = a * zoom - mouse.x;
camera.setX(newPosition);
screen.setWidth(originalWidth * zoom);
For a 2D example you can simply add the same code for the height and y positions.

Resources