Objective C: Using UIImage for Stroking - iphone

I am trying to apply a texture for my brush but i'm really having a hard time figuring how it is done.
Here's the image of my output.
I used an UIImage that just follows the touch on the screen but when i swipe it faster the result is on the right side "W", and on the left side that's the result when i swipe it slow.
i tried using the CGContextMoveToPoint and CGContextAddLineToPoint i don't know how apply the texture.
Is it possible to use UIImage for the stroke texture?
Here's my code
UIImage * brushImageTexture = [UIImage imageNamed:#"brushImage.png"];
[brushImagetexture drawAtPoint:CGPointMake(touchCurrentPosition.x, touchVurrentPosition.y) blendMode:blendMode alpha:1.0f];

You need to manually draw the image at each point along the line from the previous point to the current point.
CGPoint vector = CGPointMake(currentPosition.x - lastPosition.x, currentPosition.y - lastPosition.y);
CGFloat distance = hypotf(vector.x, vector.y);
vector.x /= distance;
vector.y /= distance;
for (CGFloat i = 0; i < distance; i += 1.0f) {
CGPoint p = CGPointMake(lastPosition.x + i * vector.x, lastPosition.y + i * vector.y);
[brushImageTexture drawAtPoint:p blendMode:blendMode alpha:1.0f];
}

Related

Drawing a circle with cocos2d?

I am trying to draw a circle in my scene with :
- (void) draw
{
ccDrawColor4F(100, 100, 100, 255);
CGPoint center = ccp(winSize.width/2, winSize.height/2);
CGFloat radius = 10.f;
CGFloat angle = 0.f;
NSInteger segments = 10;
BOOL drawLineToCenter = YES;
ccDrawCircle(center, radius, angle, segments, drawLineToCenter);
}
Well, I get some white,small circle, not filled, with some line from the center to the circle (like a clock).
So, first why are the colors are not as requested, how can I fill it with my own color? whats the line in the middle?
And, what if i want to change the color after a second? whats the best way to do that? Set a timer with a global variable?
#import <OpenGLES/ES1/gl.h>
- (void) draw
{
glLineWidth(5*CC_CONTENT_SCALE_FACTOR()); //set the thickness to 5 pixels for example
ccDrawColor4F(0.4,0.4,0.4, 1);
ccDrawLine([self anchorPoint], _peg.position);
CGPoint center = ccp(winSize.width/2, winSize.height/2);
CGFloat radius = 10.0;
CGFloat angle = 360;
NSInteger segments = 360;
BOOL drawLineToCenter = NO;
ccDrawCircle(center, radius, angle, segments, drawLineToCenter);
}
If you want to update the circle's color every second, schedule the update method and implement it as follows :
-(void) update:(float)dt
{
if(startTime == 0){
startTime = [NSDate timeIntervalSinceReferenceDate];
}
double elapsedTime = [NSDate timeIntervalSinceReferenceDate] - startTime;
if(elapsedTime > 1){
//update ccDrawColor4F here, for example just changing the Red component:
ccDrawColor4F((float)arc4random() / UINT_MAX,0.4,0.4, 1);
startTime = [NSDate timeIntervalSinceReferenceDate];
}
}
This should fix your issue. Hope it helps.
The colour values are floats not bytes, so you need to pass in something more like
ccDrawColor4F(0.4f, 0.4f, 0.4f, 1.f);
You may also want to reset the colour back to white after you've called your draw methods just for the sake of consistency.
To get a filled circle, use this function instead
void ccDrawSolidCircle( CGPoint center, float r, float a, NSUInteger segs, ccColor4F color);
The circle is always drawn from the middle, so presumably if you choose 4 segments, you'll get 4 lines drawn from the middle. Although I'm not too sure on that, I don't use primitives very much at all. As for the colour change, yes I would opt for some form of timer to change the values passed into the ccDrawColor4F method. But bear in mind that you want as little non-drawing code in the draw method as possible, so perhaps work out the colour change in another update related function.

How can i get rotate UIImageView with changable angle?

I have created one UIImageView
Here's code
capView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:kARROW_CAP_NAME]];
capView.frame = CGRectMake(0, 0, kARROW_H, kARROW_H);
[self addSubview:capView];
Now I have two points which I need to move around View, So, they are Updating..
PointA, PointB
I got the angle between them:
CGFloat angleBetweenPoints(CGPoint first, CGPoint second)
{
CGFloat height = second.y - first.y;
CGFloat width = first.x - second.x;
CGFloat rads = atan(height/width);
return RADIANS_TO_DEGREES(rads);
}
When I apply this angle to my UIImageView, it continuously change,
My used code here:
capView.transform = CGAffineTransformMakeRotation(arrowAngle);
or
capView.transform = CGAffineTransformRotate(self.transform, arrowAngle);
Arrow Angle is the value of above function which i write above..
Please take a look and help me.
Got the answer, So simple..
But still no one give me answer..
capView.transform = CGAffineTransformMakeRotation(RADIANS(M_PI_2 - arrowAngle))
And RADINAS is macro define here,
#define RADIANS(degrees) ((degrees * M_PI) / 180.0)
Thanks to all who be a part of it..

Using Core Plot to show the selected Time Period in circle

I am developing an application in which I have to show the selected time in my clock image.
Description:-
What I exactly want to say it I am having a image of circle. Now according to the selected time I want that portion to be filled in some other color on my circle image.
Please help me in doing this.
I am stuck here.
I have attached the image that will describe more what I exactly want to do.
I also tried it with doing it with pie-chart but area showing not giving proper time.
Please please help me.
In this image it shows time 1:am to 6:am
Any help would be highly appreciated.
For what you want above (not using coreplot). Place this code in the relevant drawRect/drawInContext method.
Not at mac so not 100% sure it will compile. Should be close. Also might be out by 90
#include <math.h>
CGFloat radius = 100;
//CGFloat pi = 3.1415927; //comes for free in math.h
//draw underlying circle;
UIBezierPath *basecircle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,radius*2,radius*2)];
[[UIColor blackColor] set];
[basecircle fill];
CGFloat starttime = pi/6; //1 pm = 1/6 rad
GFloat endtime = pi; //6 pm = 1 rad
//draw arc
CGPoint center = CGPointMake(radius,radius);
UIBezier *arc = [UIBezierPath bezierPath]; //empty path
[arc moveToPoint:center];
CGPoint next;
next.x = radius + radius * sin(starttime);
next.y = radius + radius * cos(starttime);
[arc moveToPoint:next]; //go one end of arc
[arc addArcWithCenter:center radius:radius startAngle:starttime endAngle:endtime clockwise:YES]; //add the arc
[arc moveToPoint:center]; //back to center
[[UIColor yellowColor] set];
[arc fill];

iPhone Circular Progress Indicator. CGContextRef. Draw on Demand

I want to draw an image that would effectively be a circular progress indicator on a UIButton. Because the image is supposed to represent progress of a task, I do not think I should handle the drawing code in the view's drawrect method.
I have a thread that is performing some tasks. After each task, it calls a method on the main thread. The called method is supposed to update the image on the button.
In the button update method, I create a CGContextRef by using CGBitmapContextCreate.
Then I use the button's frame to create a CGRect.
Then I attempt to draw into using the context I created.
Lastly I set NeedsDisplay and clean up.
But none of this is inside the view's drawrect method.
I would like to know if anyone has used CGContext to draw on / in a view on-demand in a view while the view is being displayed.
I would like to get some ideas regarding an approach to doing this.
Here is an encapsulated version of what I am doing now:
CGContextRef xContext = nil;
CGColorSpaceRef xColorSpace;
CGRect xRect;
void* xBitmapData;
int iBMPByteCount;
int iBMPBytesPerRow;
float fBMPWidth = 20.0f;
float fBMPHeight = 20.0f;
float fPI = 3.14159;
float fRadius = 25.0f;
iBMPBytesPerRow = (fBMPWidth * 4);
iBMPByteCount = (iBMPBytesPerRow * fBMPHeight);
xColorSpace = CGColorSpaceCreateDeviceRGB();
xBitmapData = malloc(iBMPByteCount);
xContext = CGBitmapContextCreate(xBitmapData, fBMPWidth, fBMPHeight, 8, iBMPBytesPerRow, xColorSpace, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(xColorSpace);
UIGraphicsPushContext(xContext);
xRect = CGRectMake(30.0f, 400.0f, 50.0f, 50.0f);
float fWidth = xRect.size.width;
float fHeight = xRect.size.height;
CGContextClearRect(xContext, xRect);
CGContextSetRGBStrokeColor(xContext, 0.5f, 0.6f, 0.7f, 1.0f);
CGContextSetLineWidth(xContext, 1.0f);
float fArcBegin = 45.0f * fPI / 180.0f;
float fArcEnd = 90.0f * fPI / 180.0f;
CGContextSetFillColor(xContext, CGColorGetComponents( [[UIColor greenColor] CGColor]));
CGContextMoveToPoint(xContext, fWidth, fHeight);
CGContextAddArc(xContext, fWidth, fHeight, fRadius, fArcBegin, fArcEnd, 0);
CGContextClosePath(xContext);
CGContextFillPath(xContext);
UIGraphicsPopContext;
CGContextRelease(xContext);
[self.view setNeedsDisplay];
// [self.view setNeedsDisplayInRect: xRect];
The above is a little bit wonky because I've tried different tweaks. However, I think it communicates what I am trying to do.
Alternative approach:
You could create a series of images that represent the progress updates and then replace the UIButton currentImage property with the setImage:forState: method at each step of the process. This doesn't require drawing in the existing view and this approach has worked well for me to show simple "animation" of images (buttons or other).
Would this approach work for you? If not, why not?
Bart
This was really bugging me so after dealing with a series of silly, but necessary issues regarding the project I want this functionality for, I played around with it.
The end result is that I can now arbitrarily draw an arc representing the progress of a particular background task to a button.
The goal was to draw something like the little indicator in the lower right hand corner of the XCode windows while a project is being cleaned or compiled.
I created a function that will draw and fill an arc and return it as a UIImage.
The worker thread calls method (PerformSelectorOnMainThread) with the current values and a button identifier. In the called method, I call the arc image function with the percentage filled and such.
example call:
oImg = [self ArcImageCreate:100.0f fWidth:100.0f
fPercentFilled: 0.45f fAngleStart: 0.0f xFillColor:[UIColor blueColor]];
Then set the background image of the button:
[oBtn setBackgroundImage: oImg forState: UIControlStateNormal];
Here is the function:
It is not finished, but it works well enough to illustrate how I am doing this.
/**
ArcImageCreate
#ingroup UngroupedFunctions
#brief Create a filled or unfilled solid arc and return it as a UIImage.
Allows for dynamic / arbitrary update of an object that allows a UIImage to be drawn on it. \
This can be used for some sort of pie chart or progress indicator by Image Flipping.
#param fHeight The height of the created UIImage.
#param fWidth The width of the created UIImage.
#param fPercentFilled A percentage of the circle to be filled by the arc. 0.0 to 1.0.
#param AngleStart The angle where the arc should start. 0 to 360. Clock Reference.
#param xFillColor The color of the filled area.
#return Pointer to a UIImage.
#todo [] Validate object creation at each step.
#todo [] Convert PercentFilled (0.0 to 1.0) to appropriate radian(?) (-3.15 to +3.15)
#todo [] Background Image Support. Allow for the arc to be drawn on top of an image \
and the whole thing returned.
#todo [] Background Image Reduction. Background images will have to be resized to fit the specfied size. \
Do not want to return a 65KB object because the background is 60K or whatever.
#todo [] UIColor RGBA Components. Determine a decent method of extracting RGVA values \
from a UIColor*. Check out arstechnica.com/apple/guides/2009/02/iphone-development-accessing-uicolor-components.ars \
for an idea.
*/
- (UIImage*) ArcImageCreate: (float)fHeight fWidth:(float)fWidth fPercentFilled:(float)fPercentFilled fAngleStart:(float)fAngleStart xFillColor:(UIColor*)xFillColor
{
UIImage* fnRez = nil;
float fArcBegin = 0.0f;
float fArcEnd = 0.0f;
float fArcPercent = 0.0f;
UIColor* xArcColor = nil;
float fArcImageWidth = 0.0f;
float fArcImageHeight = 0.0f;
CGRect xArcImageRect;
CGContextRef xContext = nil;
CGColorSpaceRef xColorSpace;
void* xBitmapData;
int iBMPByteCount;
int iBMPBytesPerRow;
float fPI = 3.14159;
float fRadius = 25.0f;
// #todo Force default of 100x100 px if out of bounds. \
// Check max image dimensions for iPhone. \
// If negative, flip values *if* values are 'reasonable'. \
// Determine minimum useable pixel dimensions. 10x10 px is too small. Or is it?
fArcImageWidth = fHeight;
fArcImageHeight = fWidth;
// Get the passed target percentage and clip it between 0.0 and 1.0
fArcPercent = (fPercentFilled 1.0f) ? 1.0f : fPercentFilled;
fArcPercent = (fArcPercent > 1.0f) ? 1.0f : fArcPercent;
// Get the passed start angle and clip it between 0.0 to 360.0
fArcBegin = (fAngleStart 359.0f) ? 0.0f : fAngleStart;
fArcBegin = (fArcBegin > 359.0f) ? 0.0f : fArcBegin;
fArcBegin = (fArcBegin * fPI) / 180.0f;
fArcEnd = ((360.0f * fArcPercent) * fPI) / 180.0f;
//
if (xFillColor == nil) {
// random color
} else {
xArcColor = xFillColor;
}
// Calculate memory required for image.
iBMPBytesPerRow = (fArcImageWidth * 4);
iBMPByteCount = (iBMPBytesPerRow * fArcImageHeight);
xBitmapData = malloc(iBMPByteCount);
// Create a color space. Behavior changes at OSXv10.4. Do not rely on it for consistency across devices.
xColorSpace = CGColorSpaceCreateDeviceRGB();
// Set the system to draw. Behavior changes at OSXv10.3.
// Both of these work. Not sure which is better.
// xContext = CGBitmapContextCreate(xBitmapData, fArcImageWidth, fArcImageHeight, 8, iBMPBytesPerRow, xColorSpace, kCGImageAlphaPremultipliedFirst);
xContext = CGBitmapContextCreate(NULL, fArcImageWidth, fArcImageHeight, 8, iBMPBytesPerRow, xColorSpace, kCGImageAlphaPremultipliedFirst);
// Let the system know the colorspace reference is no longer required.
CGColorSpaceRelease(xColorSpace);
// Set the created context as the current context.
// UIGraphicsPushContext(xContext);
// Define the image's box.
xArcImageRect = CGRectMake(0.0f, 0.0f, fArcImageWidth, fArcImageHeight);
// Clear the image's box.
// CGContextClearRect(xContext, xRect);
// Draw the ArcImage's background image.
// CGContextDrawImage(xContext, xArcImageRect, [oBackgroundImage CGImage]);
// Set Us Up The Transparent Drawing Area.
CGContextBeginTransparencyLayer(xContext, nil);
// Set the fill and stroke colors
// #todo [] Determine why SetFilColor does not. Use alternative method.
// CGContextSetFillColor(xContext, CGColorGetComponents([xArcColor CGColor]));
// CGContextSetFillColorWithColor(xContext, CGColorGetComponents([xArcColor CGColor]));
// Test Colors
CGContextSetRGBFillColor(xContext, 0.3f, 0.4f, 0.5f, 1.0f);
CGContextSetRGBStrokeColor(xContext, 0.5f, 0.6f, 0.7f, 1.0f);
CGContextSetLineWidth(xContext, 1.0f);
// Something like this to reverse drawing?
// CGContextTranslateCTM(xContext, TranslateXValue, TranslateYValue);
// CGContextScaleCTM(xContext, -1.0f, 1.0f); or CGContextScaleCTM(xContext, 1.0f, -1.0f);
// Test Vals
// fArcBegin = 45.0f * fPI / 180.0f; // 0.785397
// fArcEnd = 90.0f * fPI / 180.0f; // 1.570795
// Move to the start point and draw the arc.
CGContextMoveToPoint(xContext, fArcImageWidth/2.0f, fArcImageHeight/2.0f);
CGContextAddArc(xContext, fArcImageWidth/2.0f, fArcImageHeight/2.0f, fRadius, fArcBegin, fArcEnd, 0);
// Ask the OS to close the arc (current point to starting point).
CGContextClosePath(xContext);
// Fill 'er up. Implicit path closure.
CGContextFillPath(xContext);
// CGContextEOFillPath(context);
// Close Transparency drawing area.
CGContextEndTransparencyLayer(xContext);
// Create an ImageReference and create a UIImage from it.
CGImageRef xCGImageTemp = CGBitmapContextCreateImage(xContext);
CGContextRelease(xContext);
fnRez = [UIImage imageWithCGImage: xCGImageTemp];
CGImageRelease(xCGImageTemp);
// UIGraphicsPopContext;
return fnRez;
}

Copy part of scaled UIImage

I want to be able to scale and position an image, and then save just part of that image. I currently have a UIImageView inside a UIScrollView. After zooming and positioning the image I hit a button and then have the following code.
// imageScale = current scale of UIView
// get the new width and height of the scaled UIView
float scaledImageWidth = [scrollView viewWithTag:1].frame.size.width;
float scaledImageHeight = [scrollView viewWithTag:1].frame.size.height;
// get starting X and Y coords of target in relation to UIView
// target is a box in the middle of the screen, 210x255px
float imageY = scaledImageHeight / 2 - (scrollView.contentOffset.y * imageScale);
imageY = (imageY < 0) ? (140 * imageScale) + ((imageY * -1) + (scrollView.contentOffset.y *imageScale)) : (140 * imageScale) - imageY;
float imageX = scaledImageWidth / 2 - (scrollView.contentOffset.x * imageScale);
imageX = (imageX < 0) ? (56 * imageScale) + ((imageX * -1) + (scrollView.contentOffset.x *imageScale)) : (56 * imageScale) - imageX;
// image = original unscaled UIImage
// create new UIImage that matches the size of the scaled UIView we have been working with
UIGraphicsBeginImageContext( CGSizeMake(scaledImageWidth, scaledImageHeight) );
[image drawInRect:CGRectMake(0,0,scaledImageWidth, scaledImageHeight)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// now with the UIImage that is the size we want, copy a piece of the image
CGImageRef imageRef = CGImageCreateWithImageInRect([newImage CGImage], CGRectMake(imageX,imageY,210, 255));
UIImage* myThumbnail = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
This seems to work mostly ok. The problem I see is that when I make the final copy of just a piece of the image, the copy (myThumbnail) isn't scaled. However, the source (newImage) appears to scale without problems. Does anyone know what I am missing, or if there would be a different approach to this problem?
Edit:
Ok, I was a little off. The copy is scaling. The problem I'm having is its position is off. So if the image position is too far in one direction, the new copy wont be in the right position. For example, if I move the image so that I'm cropping the bottom left, it might give me a sliver on the right instead of that bottom left portion of the image.