Files
FreeJ/xcode/GLString.m
xant 81beebc0b1 textlayer now works properly again (gui controls are still to be fixed though)
solved the blurry problem when rendering the string with certain fonts
and sizes
2010-03-26 23:59:21 +01:00

367 lines
10 KiB
Objective-C
Executable File

/* FreeJ
* (c) Copyright 2009 Andrea Guzzo <xant@dyne.org>
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
// Abstract: Uses Quartz to draw a string into an OpenGL texture.
// Imported from apple "CocoaGL" example and extended
// to fit freej integration.
#import <GLString.h>
// The following is a NSBezierPath category to allow
// for rounded corners of the border
#pragma mark -
#pragma mark NSBezierPath Category
@implementation NSBezierPath (RoundRect)
+ (NSBezierPath *)bezierPathWithRoundedRect:(NSRect)rect cornerRadius:(float)radius {
NSBezierPath *result = [NSBezierPath bezierPath];
[result appendBezierPathWithRoundedRect:rect cornerRadius:radius];
return result;
}
- (void)appendBezierPathWithRoundedRect:(NSRect)rect cornerRadius:(float)radius {
if (!NSIsEmptyRect(rect)) {
if (radius > 0.0) {
// Clamp radius to be no larger than half the rect's width or height.
float clampedRadius = MIN(radius, 0.5 * MIN(rect.size.width, rect.size.height));
NSPoint topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
NSPoint topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
[self moveToPoint:NSMakePoint(NSMidX(rect), NSMaxY(rect))];
[self appendBezierPathWithArcFromPoint:topLeft toPoint:rect.origin radius:clampedRadius];
[self appendBezierPathWithArcFromPoint:rect.origin toPoint:bottomRight radius:clampedRadius];
[self appendBezierPathWithArcFromPoint:bottomRight toPoint:topRight radius:clampedRadius];
[self appendBezierPathWithArcFromPoint:topRight toPoint:topLeft radius:clampedRadius];
[self closePath];
} else {
// When radius == 0.0, this degenerates to the simple case of a plain rectangle.
[self appendBezierPathWithRect:rect];
}
}
}
@end
#pragma mark -
#pragma mark GLString
// GLString follows
@implementation GLString
#pragma mark -
#pragma mark Deallocs
- (void) deleteTexture
{
if (texName && cgl_ctx) {
(*cgl_ctx->disp.delete_textures)(cgl_ctx->rend, 1, &texName);
texName = 0; // ensure it is zeroed for failure cases
cgl_ctx = 0;
}
}
- (void) dealloc
{
[self deleteTexture];
[textColor release];
[boxColor release];
[borderColor release];
[string release];
[super dealloc];
}
#pragma mark -
#pragma mark Initializers
// designated initializer
- (id) initWithAttributedString:(NSAttributedString *)attributedString
{
[super init];
cgl_ctx = NULL;
texName = 0;
texSize.width = 0.0f;
texSize.height = 0.0f;
if (string)
[string release];
string = [attributedString retain];
staticFrame = NO;
antialias = YES;
marginSize.width = 4.0f; // standard margins
marginSize.height = 2.0f;
cRadius = 4.0f;
requiresUpdate = YES;
// all other variables 0 or NULL
return self;
}
- (id) initWithString:(NSString *)aString withFont:font withTextColor:(NSColor *)text BoxColor:(NSColor *)box BorderColor:(NSColor *)border
{
NSMutableDictionary *attribs = [NSMutableDictionary dictionary];
[attribs
setObject:font
forKey:NSFontAttributeName
];
[attribs
setObject:text
forKey:NSForegroundColorAttributeName
];
[attribs
setObject:box
forKey:NSBackgroundColorAttributeName
];
// XXX - how to use bordercolor now?
return [self initWithAttributedString:[[[NSAttributedString alloc] initWithString:aString attributes:attribs] autorelease]];
}
- (id) initWithString:(NSString *)aString withAttributes:(NSDictionary *)attribs
{
return [self initWithAttributedString:[[[NSAttributedString alloc] initWithString:aString attributes:attribs] autorelease]];
}
- (void) genImage
{
if (image)
[image release];
if (bitmap)
[bitmap release];
if ((NO == staticFrame)) { // find frame size if we have not already found it
frameSize = [string size]; // current string size
frameSize.width += marginSize.width * 2.0f; // add padding
frameSize.height += marginSize.height * 2.0f;
}
image = [[NSImage alloc] initWithSize:frameSize];
[image lockFocus];
[[NSGraphicsContext currentContext] setShouldAntialias:antialias];
if ([boxColor alphaComponent]) { // this should be == 0.0f but need to make sure
[boxColor set];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(NSMakeRect (0.0f, 0.0f, frameSize.width, frameSize.height) , 0.5, 0.5)
cornerRadius:cRadius];
[path fill];
}
if ([borderColor alphaComponent]) {
[borderColor set];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(NSMakeRect (0.0f, 0.0f, frameSize.width, frameSize.height), 0.5, 0.5)
cornerRadius:cRadius];
[path setLineWidth:1.0f];
[path stroke];
}
[textColor set];
[string drawAtPoint:NSMakePoint (marginSize.width, marginSize.height)]; // draw at offset position
bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, frameSize.width, frameSize.height)];
[image unlockFocus];
}
#pragma mark -
#pragma mark Accessors
- (GLuint) texName
{
return texName;
}
- (NSSize) texSize
{
return texSize;
}
#pragma mark Text Color
- (void) setTextColor:(NSColor *)color // set default text color
{
[color retain];
[textColor release];
textColor = color;
requiresUpdate = YES;
}
- (NSColor *) textColor
{
return textColor;
}
#pragma mark Box Color
- (void) setBoxColor:(NSColor *)color // set default text color
{
[color retain];
[boxColor release];
boxColor = color;
requiresUpdate = YES;
}
- (NSColor *) boxColor
{
return boxColor;
}
#pragma mark Border Color
- (void) setBorderColor:(NSColor *)color // set default text color
{
[color retain];
[borderColor release];
borderColor = color;
requiresUpdate = YES;
}
- (NSColor *) borderColor
{
return borderColor;
}
#pragma mark Margin Size
// these will force the texture to be regenerated at the next draw
- (void) setMargins:(NSSize)size // set offset size and size to fit with offset
{
marginSize = size;
if (NO == staticFrame) { // ensure dynamic frame sizes will be recalculated
frameSize.width = 0.0f;
frameSize.height = 0.0f;
}
requiresUpdate = YES;
}
- (NSSize) marginSize
{
return marginSize;
}
#pragma mark Antialiasing
- (BOOL) antialias
{
return antialias;
}
- (void) setAntialias:(bool)request
{
antialias = request;
requiresUpdate = YES;
}
#pragma mark Frame
- (NSSize) frameSize
{
if ((NO == staticFrame) && (0.0f == frameSize.width) && (0.0f == frameSize.height)) { // find frame size if we have not already found it
frameSize = [string size]; // current string size
frameSize.width += marginSize.width * 2.0f; // add padding
frameSize.height += marginSize.height * 2.0f;
}
return frameSize;
}
- (BOOL) staticFrame
{
return staticFrame;
}
- (void) useStaticFrame:(NSSize)size // set static frame size and size to frame
{
frameSize = size;
staticFrame = YES;
requiresUpdate = YES;
}
- (void) useDynamicFrame
{
if (staticFrame) { // set to dynamic frame and set to regen texture
staticFrame = NO;
frameSize.width = 0.0f; // ensure frame sizes will be recalculated
frameSize.height = 0.0f;
requiresUpdate = YES;
}
}
#pragma mark String
- (void) setString:(NSAttributedString *)attributedString // set string after initial creation
{
[attributedString retain];
[string release];
string = attributedString;
if (NO == staticFrame) { // ensure dynamic frame sizes will be recalculated
frameSize.width = 0.0f;
frameSize.height = 0.0f;
}
requiresUpdate = YES;
}
- (void) setString:(NSString *)aString withAttributes:(NSDictionary *)attribs; // set string after initial creation
{
[self setString:[[[NSAttributedString alloc] initWithString:aString attributes:attribs] autorelease]];
}
#pragma mark -
#pragma mark Drawing
// generates the texture (requires a current opengl context)
- (CVPixelBufferRef) drawOnBuffer:(CVPixelBufferRef)pixelBuffer
{
[self genImage];
int pxWidth = CVPixelBufferGetWidth(pixelBuffer);
int pxHeight = CVPixelBufferGetHeight(pixelBuffer);
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
void *rasterData = CVPixelBufferGetBaseAddress(pixelBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
// note that we are most likely going to write on a small area of the pixelbuffer
// and memory could have been reused so we need to ensure emptying the entire frame
// before actually drawing on it.
// XXX - there could be a proper apple api to do this
memset(rasterData, 0, bytesPerRow*pxHeight);
// context to draw in, set to pixel buffer's address
size_t bitsPerComponent = 8; // *not* CGImageGetBitsPerComponent(image);
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGContextRef ctxt = CGBitmapContextCreate(rasterData, pxWidth, pxHeight, bitsPerComponent, bytesPerRow, cs, kCGImageAlphaNoneSkipFirst);
if(ctxt == NULL){
NSLog(@"could not create context");
return NULL;
}
// draw at the center of the provided pixel buffer
NSGraphicsContext *nsctxt = [NSGraphicsContext graphicsContextWithGraphicsPort:ctxt flipped:NO];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:nsctxt];
[image compositeToPoint:NSMakePoint(round((pxWidth-frameSize.width)/2),
round((pxHeight-frameSize.height)/2))
operation:NSCompositeCopy];
[NSGraphicsContext restoreGraphicsState];
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
CFRelease(ctxt);
CFRelease(cs);
return pixelBuffer;
}
@end