I have two play two videos simultaneously on a view .Both videos would be same.
Now, my concern is the video on right is actually to be flipped horizontally along x-axis and then saved in photo library.I have tried googling a lot and found that CGAFFineRotateTransform can help but I am not able to use that in my code.Kindly help me to flip the video on right horizontally while keeping the scale and move same.
Any help or guidance in this direction would be appreciable .Thanks in advance!
Check the difference between video on left and right,video on left is complete but video on right is showing half video only
To just flip (mirror) the video horizontally, use a negative x-value for scaling:
CGAffineTransform scale = CGAffineTransformMakeScale( -1.0, 1.0);
Edit: Regarding your more general question on how to position tracks: For each video track involved:
build a bounding rect based on the original size of the track - this is the source rect
ask yourself where the track should end up, in terms of origin and size in the resulting video - this gives you the destination rect
You can then derive the corresponding affine transform with a function like:
CGAffineTransform NHB_CGAffineTransformMakeRectToRect( CGRect srcRect, CGRect dstRect)
{
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformTranslate( t, dstRect.origin.x - fmin( 0., dstRect.size.width), dstRect.origin.y - fmin( 0., dstRect.size.height));
t = CGAffineTransformScale( t, dstRect.size.width / srcRect.size.width, dstRect.size.height / srcRect.size.height);
return t;
}
To mirror, provide a negative size for the corresponding axis (the - fmin(,) part compensates the offset).
Given a video track and assuming, for example, the track should go mirrored to the right half of a 640x480 video, you can get the corresponding transform with:
CGSize srcSize = videoTrack.naturalSize;
CGRect srcRect = CGRectMake( 0, 0, srcSize.width, srcSize.height);
CGRect dstRect = CGRectMake( 320, 0, -320, 480);
CGAffineTransform t = NHB_CGAffineTransformMakeRectToRect(srcRect, dstRect);
Of course this may stretch the video track; to keep the aspect ratio, you'll have to take the source size into account when calculating the destination rect.
Some remarks:
note that in NHB_CGAffineTransformMakeRectToRect I deliberately chose to start with the identity matrix, and then add the required transforms one by one. This way much more complex transforms can be build, including rotation. As I said, try to get a grasp on affine transforms, they're really powerful
AVAssetTracks naturalSize sometimes returns confusing results for some videos with complex SAR/PAR definitions. To make this bullet proof, you'll have to derive the size from the dimensions in the corresponding format descriptions, but that's a whole new topic...
Related
I have a SCNNode - sceneNode - which is a child of rootNode and contains all of my child nodes. Upon the user tapping a button, I want to rotate the scene around a certain point on the y-axis. For example, the camera's point of view is known, and I want to rotate everything by 90º around the camera. The camera is no longer at 0, 0, 0.
I've tried playing around with the SCNMatrix4MakeTranslation function, and then changing the y value on the euler angles, but I've not been able to get it to work expectedly.
Your question is rather ambiguous, if you were to rotate the entire scene 90degrees “around the camera” the scene would end up next to the camera and you wouldn’t see it.
To rotate a SCNNode around a point other than its own pivot, as the question in the title, create a translation matrix that moves it to that point, multiply with a rotation matrix to the SCNNode, and then multiply with the inverse of the translation matrix.
However, what you probably want to do instead is do basically the same process with the camera node instead of the sceneNode. That will make the camera move around the sceneNode, giving the appearance the scene is rotating in place.
For example:
//get the current camera's transform and store it in cammat
GLKMatrix4 cammat = SCNMatrix4ToGLKMatrix4(self.myView.pointOfView.transform);
//create a rotation matrix of 90 degrees over axis Y
GLKQuaternion quaty = GLKQuaternionMakeWithAngleAndAxis(M_PI/2, 0, 1, 0);
GLKMatrix4 rotMat = GLKMatrix4MakeWithQuaternion(quaty);
//set the camera transform to rotMat * cammat, which basically rotates the camera first, and then moves it back to the same offset it was.
self.myView.pointOfView.transform = SCNMatrix4FromGLKMatrix4(GLKMatrix4Multiply(rotMat, cammat));
For dimension consideration, I resize the opengl view to 2.0 scale than origin, like this:
NSInteger Dimension = 2;
self.glView = [[WQPaintGLView alloc] initWithFrame:CGRectMake(0, 0, width*Dimension, height*Dimension)];
CGAffineTransform tScale = CGAffineTransformMakeScale((float)1/Dimension, (float)1/Dimension);
CGAffineTransform tTranslate = CGAffineTransformTranslate(tScale, -width, -height);
self.glView.transform = tTranslate;
[self.canvasContainerView addSubview:self.glView];
But get a strange issue, see:
I can only draw stuff in the left bottom 1/4 area.
What did I wrong?
The UIView transform and openGL are not very compatible. Also the view resizing after the openGL initialization could be troublesome and in most cases a new render buffer must be created from the view.
Anyway since you scaled the view to have a larger surface you should check for following calls:
glViewport should define what part of the buffer you are writing at. Usually it is set like (0, 0, viewWidth, viewHeight). In your case it must include the scale as well.
glOrtho (or glFrustum) define your coordinate system if used. Those should most likely be the same no matter the view scale.
Any other matrix usage or scissors that may be defined by the view's frame.
By all means if possible remove the transform on the view and try to find a better solution.
Let's say you have an original image that is
200 high, 100 wide
Let's say you want to draw only a square of it. Let's say, just the bottom square.
Let's say you want to draw it on to a new small image that is
20 high, 20 wide
Of course, you simply do this:
CGRect imageRect = CGRectMake( -10,0, 20,20);
.. begin graphics context ..
[originalImage drawInRect:imageRect];
With drawRect, you supply a rectangle the same full shape (same proportions) of the original image, but expressed in the size of the new canvas. No problem.
BUT:
in the example, you are drawing THE WHOLE ORIGINAL IMAGE -- THE WHOLE 200 HEIGHT on to the new small square.
(Of course the "top half" misses the new canvas, and you only get the bottom half on the new canvas -- which is what you wanted.)
My impression is iOS renders or calculates the "whole" original image, and it only "puts on" the bottom half (in the example) on to the new canvas.
This seems very wasteful.
IS THERE A FASTER WAY TO DO THIS?
It seems like there should be a command, something like this:
drawThisPartOfTheOriginalImage: (0,100 to 100,200)
ontoThisPartOfTheNewCanvas: (0,20 to 20,20)
What's the situation? Is there a more efficient command than drawRect when you are only drawing a small part of the original image? Cheers
CGContextClipToRect approach...(doesn't work!)
.
I experimented with CGContextClipToRect as Peter suggested below.
CGContextClipToRect indeed sets the area you will draw to on your "result" canvas. I simply set it to the size of that result canvas (it would be 20.20 in the example above). To repeat the aim here being to have iOS save time by avoiding pointlessly drawing the, err, not-drawn part of the original.
This example is for an original image 2000.2000 drawing on to a 500.500 (ie, only drawing the top left quarter of the original on to the result).
In fact notice it is slightly slower when you include the CGContextClipToRect, again suggesting iOS "knows when to stop" anyways.
// no need to "overdraw"... quickener turned OFF
//CGContextRef c = UIGraphicsGetCurrentContext();
//CGContextClipToRect(c, CGRectMake(0, 0, resultSize.width,resultSize.height));
//Execution Time .................................. 0.443669
// no need to "overdraw"... quickener turned ON
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextClipToRect(c, CGRectMake(0, 0, resultSize.width,resultSize.height));
//Execution Time .................................. 0.461845
As you can see it's a hair slower, actually, adding the CGContextClipToRect trick.
For the record, here is the exact routine used to crop an image:
-(UIImage *)simplishTopCrop:(UIImage *)fromImage
{
// check for zero fromImage.size.width etc etc
CGSize resultSize = CGSizeMake(640,640);
CGFloat scale = MAX(
resultSize.width/fromImage.size.width,
resultSize.height/fromImage.size.height);
CGFloat width = fromImage.size.width * scale;
CGFloat height = fromImage.size.height * scale;
CGRect imageRect = CGRectMake(0,0, width,height);
UIGraphicsBeginImageContextWithOptions(resultSize, NO, 0);
// INSERT 'CGContextClipToRect' TRICK ABOVE, RIGHT HERE
[fromImage drawInRect:imageRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
This is where clipping comes in. Clip to your dirty rect, then draw the whole image into your bounds. The clipping path will keep the rest of the image at least from appearing, and hopefully from being composited or sampled at all.
If your profiling in Instruments finds that that is not efficient enough, you might try cropping the image itself, using CGImageCreateWithImageInRect, and then drawing that image into your dirty rect. You may want to keep your cropped image around and only throw it away when the rect changes. One way or the other, cropping the image may be more efficient—but don't forget to profile both before and after to prove that.
When you have to display a series of visual components (sprites) within the context of a game each taking a literal height and width that needs to be relative to the height & width of the Viewport (not necessarily aspect ratio) of the target device:
Is there a scaling class to help come up with scaling ratio in a dynamic fashion based on current device viewport size?
Will I need to roll my own scaling ratio algorithm?
Any cross platform issues I should be aware of?
This is not a question relating to the loading of assets based on target device nor is it a question of how to perform the scaling of the sprite (which is described here: http://msdn.microsoft.com/en-us/library/bb194913.aspx), rather a question of how to determine the scale of sprites based on view port size.
You can always create your own implementation of scaling.
For example, the default target viewport dimensions are:
const int defaultWidth = 1280, defaultHeight = 720;
And your current screen dimensions are 800×600, which gives you a (let's use a Vector2 instead of two floats):
int currentWidth = GraphicsDevice.Viewport.Width,
currentHeight = GraphicsDevice.Viewport.Height;
Vector2 scale = new Vector2(currentWidth / defaultWidth,
currentHeight / defaultHeight);
This gives you a {0.625; 0.83333}. You can now use this in a handy SpriteBatch.Draw() overload that takes a Vector2 scaling variable:
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effects,
float layerDepth
)
Alternatively, you can draw all your stuff to a RenderTarget2D and copy the resulting image from there to a stretched texture on the main screen, but that will still require the above SpriteBatch.Draw() overload, though it might save you time if you have lots of draw calls.
Another Option to generate the scale would be to leverage:
var scaleMatrix = Matrix.CreateScale(
(float)GraphicsDevice.Viewport.Width / View.Width,
(float)GraphicsDevice.Viewport.Width / View.Width, 1f);
http://msdn.microsoft.com/en-gb/library/bb195692.aspx.
But this did not meet my needs, as I would then have to roll my own transform to map touch input location to the 'transformed' sprites (which respond to user touch input by knowing their own position and size).
In the end I used a percentage based approach.
I basically got the viewport height and width...
GraphicsDevice.Viewport.Width
GraphicsDevice.Viewport.Height
...then calculated the Height and Width of my sprites (Note: "as mentioned in question they take a literal height and width") based on their relative size to the screen myself using percentages.
//I want the buttons height and width to be 20% of the viewport
var x, y = GraphicsDevice.Viewport.Width * 0.2f; //20% of screen width
var btnsize = new Vector(x,y);
var button = new GameButton(btnsize);
Then once I have the size of the button I am able to calculate the position on the screen to render the button based of the size of the button and the available viewport size, against working in relative position based in percentages.
Finally, I have a reason to ask something, instead of scouring endless hours of the joys of Stack Overflow.
Here's my situation: I have an UIImageView with one UIImage inside it. I'm transforming the entire UIImageView via CGAffineTranforms, to scale it height-wise and keep it at a specific angle.
I'm feeding it this transform data through two CGPoints, so it's essentially just calculating the angle and scale between these two points and transforming.
Now, the transforming is working like a charm, but I recently came across the UIImage method resizableImageWithCapInsets, which works just fine if you set the frame of the image manually (ie. scale using a frame), but it seems that using transforms overrides this, which I guess is sort of to be expected since it's Core Graphics doing it's thing.
My question is, how would I go about either a) adding cap insets after transforming the image or b) doing the angle & scaling via a frame?
Note that the two points providing the data are touch points, so they can differ very much, which is why creating a scaled rectangle at a specific angle is tricky at best.
To keep you code hungry geniuses happy, here's a snippet of the current way I'm handling scaling (only doing cap insets when creating the UIImage):
float xDiff = (PointB.x - PointA.x) / 2;
float yDiff = (PointB.y - PointA.y) / 2;
float angle = [self getRotatingAngle:PointA secondPoint:PointB];
CGPoint pDiff = CGPointMake(PointA.x + xDiff, PointA.y + yDiff);
self.center = pDiff;
// Setup a new transform
// Set it up with a scale and an angle
double distance = sqrt(pow((PointB.x - PointA.x), 2.0) + pow((PointB.y - PointA.y), 2.0));
float scale = 1.0 * (distance / self.image.size.height);
CGAffineTransform transformer = self.transform;
transformer = CGAffineTransformConcat(CGAffineTransformMakeScale(1.0, scale), CGAffineTransformMakeRotation(angle));
// Apply the transformer
self.transform = transformer;
Adding a proper answer to this. The answer to the problem can be found here.