CGGlyphs wrong position - objective-c

I have been trying to draw single glyph with core text, but the x position of letter is little bit different. The red rectangle show the correct position.
CGContextRef main = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetAllowsAntialiasing(main, false);
CGContextSetFont(main, font);
CGContextSetFontSize(main, 200);
CGContextSetTextPosition(main, 0, 0);
glyphs[0] = CGFontGetGlyphWithGlyphName(font, CFSTR("E"));
points[0] = CGPointMake(100, 100);
CGContextSetRGBFillColor(main, 0, 1, 0, 1);
CGContextShowGlyphsAtPositions(main, glyphs, points, 1);
CGRect *r = malloc(sizeof(CGRect)*1);
CGFontGetGlyphBBoxes(font, glyphs, 1, r);
float t = roundf(r[0].size.width/CGFontGetUnitsPerEm(font)*200);
float t2 = roundf(r[0].size.height/CGFontGetUnitsPerEm(font)*200);
CGRect r2 = CGRectMake(points[0].x, points[0].y-1, t, t2+2);
CGContextSetRGBStrokeColor(main, 1, 0, 0, 1);
CGContextStrokeRect(main, r2);
Here is screenshot:

You're assuming the bounding box's origin is at zero. It isn't. You need to add in its offset. Something like (following your patterns):
float cornerX = roundf(r[0].origin.x/CGFontGetUnitsPerEm(font)*200);
float cornerY = roundf(r[0].origin.y/CGFontGetUnitsPerEm(font)*200);
CGRect r2 = CGRectMake(points[0].x+cornerX, points[0].y-1+cornerY, t, t2+2);

Related

How do I create a custom NSCursor with alpha mask?

When I create a custom NSCursor in Objective-C the alpha channel mask appears to XOR the screen below. I am expecting an alpha channel value of zero to be transparent, not XOR the graphics below. I am certain my mask data is correct ARGB where A=0 for transparent and A=255 for opaque. What am I doing wrong?
static void maincustomcursor(bigmap *thedata, point hotspot)
{
NSPoint thepoint;
NSImage *newimage;
NSCursor *thecursor;
CGImageRef theimage;
CGBitmapInfo theinfo;
CGContextRef thecont;
CGColorSpaceRef thecolor;
int width, height, across;
width = (*thedata).width;
height = (*thedata).height;
if (width != smalliconsize || height != smalliconsize) return;
if (hotspot.h < 0) hotspot.h = 0;
if (hotspot.h >= smalliconsize) hotspot.h = smalliconsize - 1;
if (hotspot.v < 0) hotspot.v = 0;
if (hotspot.v >= smalliconsize) hotspot.v = smalliconsize - 1;
thepoint = NSMakePoint(hotspot.h, hotspot.v);
across = (*thedata).rowbytes;
thecolor = CGColorSpaceCreateDeviceRGB();
theinfo = (CGBitmapInfo)kCGImageAlphaPremultipliedFirst;
thecont = CGBitmapContextCreate((*thedata).baseaddr, width, height, 8, across, thecolor, theinfo);
theimage = CGBitmapContextCreateImage(thecont);
newimage = [[NSImage alloc] initWithCGImage:theimage size:NSZeroSize];
thecursor = [[NSCursor alloc] initWithImage:newimage hotSpot:thepoint];
[thecursor set];
[thecursor release];
[newimage release];
CGImageRelease(theimage);
CGContextRelease(thecont);
CGColorSpaceRelease(thecolor);
}
OK I figured this out. When the alpha channel = 0 you need to be sure that red, green, and blue are also zero. If the alpha channel is zero and red, green, and blue are 255, then I am seeing XOR for the pixel. This may be related to the ancient way that cursors were implemented, with clear, white, black, and xor depending on the bit masks.

OpenGL Picking not work in NSOpenGLView

I Am trying to implement picking in a NSOpenGLView , but not works, this is the code.
I render only the objects that I need pick with no lights and I render the scene as same in normal render.
- (void)drawSeleccion
{
NSSize size = [self bounds].size;
GLuint selectBuf[16 * 4] = {0};
GLint hits;
glClearColor (0.0, 0.0, 0.0, 0.0);
glColor4f(1.0, 1.0, 1.0, 1.0);
glSelectBuffer (16 * 4, selectBuf);
glRenderMode(GL_SELECT);
/// *** Start ***
glInitNames();
glPushName(0);
// view port.
glViewport(0, 0, size.width, size.height);
// projection matrix.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float dist = 534;
aspectRatio = size.width / size.height;
nearDist = MAX(10, dist - 360.0);
farDist = dist + 360.0;
GLKMatrix4 m4 = GLKMatrix4MakePerspective(zoom, aspectRatio, nearDist, farDist);
glMultMatrixf(m4.m);
// Model view.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// I look at.
GLKMatrix4 glm = GLKMatrix4MakeLookAt(0, dist, 0, 0, 0, 0, 0, 0, 1);
glMultMatrixf(glm.m);
// rotate viewPort.
glRotated(rotate_x, 1, 0, 0);
glRotated(rotate_z, 0, 0, 1);
glTranslated(translate_x - frente * 0.5, fondo * -0.5, translate_z - alto * 0.5);
/// render model....
glPushMatrix();
for (int volNo = 0; volNo < [self.modeloOptimo.arr_volumenes count]; volNo++) {
VolumenOptimo *volOp = self.modeloOptimo.arr_volumenes[volNo];
glLoadName(volNo);
volOp->verProblemas = false;
[volOp drawVolumen];
}
glPopName();
glPopMatrix();
// Flush view.
glFlush();
hits = glRenderMode(GL_RENDER);
processHits (hits, selectBuf);
} // Fin de drawRect.
Always hits = 0, and selectBuf is empty.
Any idea. thanks

UIView drawRect() : fill everything except polygon

I need to fill my UIView in drawRect() method with 'reverted polygon' - everything in view is filled with some color except polygon itself.
I've got this code to draw a simple polygon:
CGContextBeginPath(context);
for(int i = 0; i < corners.count; ++i)
{
CGPoint cur = [self cornerAt:i], next = [self cornerAt:(i + 1) % corners.count];
if(i == 0)
CGContextMoveToPoint(context, cur.x, cur.y);
CGContextAddLineToPoint(context, next.x, next.y);
}
CGContextClosePath(context);
CGContextFillPath(context);
I found a similar question, but in C#, not Obj-C : c# fill everything but GraphicsPath
Probably the fastest way is to set a clip:
// create your path as posted
// but don't fill it (remove the last line)
CGContextAddRect(context, self.bounds);
CGContextEOClip(context);
CGContextSetRGBFillColor(context, 1, 1, 0, 1);
CGContextFillRect(context, self.bounds);
Both the other answers suggest to first fill a rect, then draw the shape with clear color on top. Both omit the necessary blend mode. Here's a working version:
CGContextSetRGBFillColor(context, 1, 1, 0, 1);
CGContextFillRect(context, self.bounds);
CGContextSetBlendMode(context, kCGBlendModeClear);
// create and fill your path as posted
Edit: Both approaches require the backgroundColor to be the clearColor and opaque to be set to NO.
Second Edit: The original question was about Core Graphics. Of course there are other ways of masking part of a view. Most notably CALayer's mask property.
You can set this property to an instance of a CAPathLayer that contains your clip path to create a stencil effect.
in drawRect u can set the background color of ur view with the color u want
self.backgroundColor = [UIcolor redColor]; //set ur color
and then draw a polygon the way u r doing.
CGContextBeginPath(context);
for(int i = 0; i < corners.count; ++i)
{
CGPoint cur = [self cornerAt:i], next = [self cornerAt:(i + 1) % corners.count];
if(i == 0)
CGContextMoveToPoint(context, cur.x, cur.y);
CGContextAddLineToPoint(context, next.x, next.y);
}
CGContextClosePath(context);
CGContextFillPath(context);
hope it helps.. happy coding :)
Create a new CGLayer, fill it with your outside color, then draw your polygon using a clear color.
layer1 = CGLayerCreateWithContext(context, self.bounds.size, NULL);
context1 = CGLayerGetContext(layer1);
[... fill entire layer ...]
CGContextSetFillColorWithColor(self.context1, [[UIColor clearColor] CGColor]);
[... draw your polygon ...]
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawLayerAtPoint(context, CGPointZero, layer1);

CGContextAddEllipse - overlapping get's clipped - Quartz

I like to draw a glass with a few Elements
- Top Ellipse
- Bottom Ellipse
- and the Lines Inbetween
Next, it should be filled with a Gradient. The Elements work, but at the point, where the middle of the glass comes in touch with the top or bottom ellipse the area get's clipped.
- (void)drawRect:(CGRect)rect
{
CGPoint c = self.center;
// Drawing code
CGContextRef cx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(cx, 1.0);
[[UIColor whiteColor] setStroke];
// DrawTheShapeOfTheGlass
CGContextBeginPath(cx);
// Top and Bottom Ellipse
CGContextAddEllipseInRect(cx, CGRectMake(0, 0, 100, 20));
CGContextAddEllipseInRect(cx, CGRectMake(10, 90, 80, 20));
// Define the points for the Area inbetween
CGPoint points[] = { {0.0,10.0},{10.0,100.0},{90.0,100.0},{100.0,10.0} };
CGContextAddLines(cx, points, 4);
CGContextClosePath(cx);
// Clip, that's only the Clipped-Area wil be filled with the Gradient
CGContextClip(cx);
// CreateAndDraw the Gradient
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colorSpace[] = {1.0f, 1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f };
CGFloat locations[] = { 0.0, 1.0 };
CGGradientRef myGradient = CGGradientCreateWithColorComponents(rgbColorSpace, colorSpace, locations, 2);
CGPoint s = CGPointMake(0, 0);
CGPoint e = CGPointMake(100, 100);
CGContextDrawLinearGradient(cx, myGradient, s, e, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CGColorSpaceRelease(rgbColorSpace);
CGGradientRelease(myGradient);
}
Here how it looks like:
Is there any possibility to "fill" the whole ellipse? I played around with BlendModes but it didn't help.
Thanks
Try replacing the points[] initialization code with the following...
CGPoint points[] = {{0.0,10.0},{100.0,10.0},{90.0,100.0},{10.0,100.0}};
CoreGraphics uses the non-zero winding count rule to determine how to fill a path. Since ellipses are drawn clockwise and your trapezoid was drawn counter clockwise, the overlapping regions were not filled. Changing the drawing order of the trapezoid to clockwise will result in an object that is completely filled.

Objective-C Draw a Circle with a Ring shape

I wrote this class that draws a animated progress with a circle (it draws a circular sector based on a float progress)
#implementation MXMProgressView
#synthesize progress;
- (id)initWithDefaultSize {
int circleOffset = 45.0f;
self = [super initWithFrame:CGRectMake(0.0f,
0.0f,
135.0f + circleOffset,
135.0f + circleOffset)];
self.backgroundColor = [UIColor clearColor];
return self;
}
- (void)drawRect:(CGRect)rect {
CGRect allRect = self.bounds;
CGRect circleRect = CGRectMake(allRect.origin.x + 2, allRect.origin.y + 2, allRect.size.width - 4,
allRect.size.height - 4);
CGContextRef context = UIGraphicsGetCurrentContext();
// background image
//UIImage *image = [UIImage imageNamed:#"loader_disc_hover.png"];
//[image drawInRect:circleRect];
// Orange: E27006
CGContextSetRGBFillColor(context,
((CGFloat)0xE2/(CGFloat)0xFF),
((CGFloat)0x70/(CGFloat)0xFF),
((CGFloat)0x06/(CGFloat)0xFF),
0.01f); // fill
//CGContextSetLineWidth(context, 2.0);
CGContextFillEllipseInRect(context, circleRect);
//CGContextStrokeEllipseInRect(context, circleRect);
// Draw progress
float x = (allRect.size.width / 2);
float y = (allRect.size.height / 2);
// Orange: E27006
CGContextSetRGBFillColor(context,
((CGFloat)0xE2/(CGFloat)0xFF),
((CGFloat)0x70/(CGFloat)0xFF),
((CGFloat)0x06/(CGFloat)0xFF),
1.0f); // progress
CGContextMoveToPoint(context, x, y);
CGContextAddArc(context, x, y, (allRect.size.width - 4) / 2, -M_PI_2, (self.progress * 2 * M_PI) - M_PI_2, 0);
CGContextClosePath(context);
CGContextFillPath(context);
}
#end
Now what I want to do I to draw a ring shape with the same progress animation, instead of filling the full circle, so a circular sector again not starting from the center of the circle.
I tried with CGContextAddEllipseInRect and the CGContextEOFillPath(context);
with no success.
I think you'll need to construct a more complex path, something like:
// Move to start point of outer arc (which might not be required)
CGContextMoveToPoint(context, x+outerRadius*cos(startAngle), y+outerRadius*sin(startAngle));
// Add outer arc to path (counterclockwise)
CGContextAddArc(context, x, y, outerRadius, startAngle, endAngle, 0);
// move *inward* to start point of inner arc
CGContextMoveToPoint(context, x+innerRadius*cos(endAngle), y+innerRadius*sin(endAngle));
// Add inner arc to path (clockwise)
CGContextAddArc(context, x, y, innerRadius, endAngle, StartAngle, 1);
// Close the path from end of inner arc to start of outer arc
CGContextClosePath(context);
Note: I haven't tried the above code myself
Cheap and nasty solution:
Draw a solid circle that is smaller than the original circle by the thickness of the ring you want to draw.
Draw this circle on top of the original circle, all that you will see animating is the ring.