Classes/GLLevelMeter.m

/*
 
<codex>
 
*/
 
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
 
#import "GLLevelMeter.h"
 
 
@implementation GLLevelMeter
 
+ (Class) layerClass
{
    return [CAEAGLLayer class];
}
 
- (BOOL)_createFramebuffer
{
    glGenFramebuffersOES(1, &_viewFramebuffer);
    glGenRenderbuffersOES(1, &_viewRenderbuffer);
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer);
    [_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _viewRenderbuffer);
    
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &_backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &_backingHeight);
    
    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }
    
    return YES;
}
 
- (void)_destroyFramebuffer
{
    glDeleteFramebuffersOES(1, &_viewFramebuffer);
    _viewFramebuffer = 0;
    glDeleteRenderbuffersOES(1, &_viewRenderbuffer);
    _viewRenderbuffer = 0;
    
}
 
- (void)_setupView
{
    
    // Sets up matrices and transforms for OpenGL ES
    glViewport(0, 0, _backingWidth, _backingHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(0, _backingWidth, 0, _backingHeight, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    
    // Clears the view with black
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    ///glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
}
 
- (void)_performInit
{
    _level = 0.;
    _numLights = 0;
    _numColorThresholds = 3;
    _variableLightIntensity = YES;
    
    _bgColor = [[UIColor alloc] initWithRed:0. green:0. blue:0. alpha:0.6];
    _borderColor = [[UIColor alloc] initWithRed:0. green:0. blue:0. alpha:1.];
        
    _colorThresholds = (LevelMeterColorThreshold*)malloc(3 * sizeof(LevelMeterColorThreshold));
    _colorThresholds[0].maxValue = 0.6;
    _colorThresholds[0].color = [[UIColor alloc] initWithRed:0. green:1. blue:0. alpha:1.];
    _colorThresholds[1].maxValue = 0.9;
    _colorThresholds[1].color = [[UIColor alloc] initWithRed:1. green:1. blue:0. alpha:1.];
    _colorThresholds[2].maxValue = 1.;
    _colorThresholds[2].color = [[UIColor alloc] initWithRed:1. green:0. blue:0. alpha:1.];
    _vertical = ([self frame].size.width < [self frame].size.height) ? YES : NO;
    
    if ([self respondsToSelector:@selector(setContentScaleFactor:)]){
        _scaleFactor = self.contentScaleFactor = [[UIScreen mainScreen] scale];
    } else {
        _scaleFactor = 1.0;
    }
    
    CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
    
    eaglLayer.opaque = YES;
    
    eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
    
    _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
    
    if(!_context || ![EAGLContext setCurrentContext:_context] || ![self _createFramebuffer]) {
        [self release];
        return;
    }
    
    [self _setupView];
}
 
 
- (void)_drawView
{   
    if (!_viewFramebuffer) return;
    
    // Make sure that you are drawing to the current context
    [EAGLContext setCurrentContext:_context];
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer);
    
    CGColorRef bgc = self.bgColor.CGColor;
 
    if (CGColorGetNumberOfComponents(bgc) != 4) goto bail;
    
    const CGFloat *rgba;
    
    rgba = CGColorGetComponents(bgc);
    
    glClearColor(rgba[0], rgba[1], rgba[2], 1.);
    //glClearColor(0., 0., 0., 1.);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glPushMatrix();
    
    CGRect bds;
    
    if (_vertical)
    {
        glScalef(1., -1., 1.);
        bds = CGRectMake(0., -1., [self bounds].size.width * _scaleFactor, [self bounds].size.height * _scaleFactor);
    } else {
        glTranslatef(0., [self bounds].size.height * _scaleFactor, 0.);
        glRotatef(-90., 0., 0., 1.);
        bds = CGRectMake(0., 1., [self bounds].size.height * _scaleFactor, [self bounds].size.width * _scaleFactor);
    }
    
    if (_numLights == 0)
    {
        int i;
        CGFloat currentTop = 0.;
        
        for (i=0; i<_numColorThresholds; i++)
        {
            LevelMeterColorThreshold thisThresh = _colorThresholds[i];
            CGFloat val = MIN(thisThresh.maxValue, _level);
                        
            CGRect rect = CGRectMake(
                                     0, 
                                     (bds.size.height) * currentTop, 
                                     bds.size.width, 
                                     (bds.size.height) * (val - currentTop)
                                     );
            
            NSLog(@"Drawing rect (%0.2f, %0.2f, %0.2f, %0.2f)", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
            
            
            GLfloat vertices[] = {
                CGRectGetMinX(rect), CGRectGetMinY(rect),  
                CGRectGetMaxX(rect), CGRectGetMinY(rect),  
                CGRectGetMinX(rect), CGRectGetMaxY(rect),  
                CGRectGetMaxX(rect), CGRectGetMaxY(rect),  
            };
            
            CGColorRef clr = thisThresh.color.CGColor;
            if (CGColorGetNumberOfComponents(clr) != 4) goto bail;
            const CGFloat *rgba;
            rgba = CGColorGetComponents(clr);
            glColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
            
            
            glVertexPointer(2, GL_FLOAT, 0, vertices);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            
            
            if (_level < thisThresh.maxValue) break;
            
            currentTop = val;
        }
    }
    else
    {
        int light_i;
        CGFloat lightMinVal = 0.;
        CGFloat insetAmount, lightVSpace;
        lightVSpace = bds.size.height / (CGFloat)_numLights;
        if (lightVSpace < 4.) insetAmount = 0.;
        else if (lightVSpace < 8.) insetAmount = 0.5;
        else insetAmount = 1.;
        
        int peakLight = -1;
        if (_peakLevel > 0.)
        {
            peakLight = _peakLevel * _numLights;
            if (peakLight >= _numLights) peakLight = _numLights - 1;
        }
        
        for (light_i=0; light_i<_numLights; light_i++)
        {
            CGFloat lightMaxVal = (CGFloat)(light_i + 1) / (CGFloat)_numLights;
            CGFloat lightIntensity;
            CGRect lightRect;
            UIColor *lightColor;
            
            if (light_i == peakLight)
            {
                lightIntensity = 1.;
            } else {
                lightIntensity = (_level - lightMinVal) / (lightMaxVal - lightMinVal);
                lightIntensity = LEVELMETER_CLAMP(0., lightIntensity, 1.);
                if ((!_variableLightIntensity) && (lightIntensity > 0.)) lightIntensity = 1.;
            }
            
            lightColor = _colorThresholds[0].color;
            int color_i;
            for (color_i=0; color_i<(_numColorThresholds-1); color_i++)
            {
                LevelMeterColorThreshold thisThresh = _colorThresholds[color_i];
                LevelMeterColorThreshold nextThresh = _colorThresholds[color_i + 1];
                if (thisThresh.maxValue <= lightMaxVal) lightColor = nextThresh.color;
            }
            
            lightRect = CGRectMake(
                                   0., 
                                   bds.origin.y * (bds.size.height * ((CGFloat)(light_i) / (CGFloat)_numLights)), 
                                   bds.size.width,
                                   bds.size.height * (1. / (CGFloat)_numLights)
                                   );
            lightRect = CGRectInset(lightRect, insetAmount, insetAmount);
 
            GLfloat vertices[] = {
                CGRectGetMinX(lightRect), CGRectGetMinY(lightRect),  
                CGRectGetMaxX(lightRect), CGRectGetMinY(lightRect),  
                CGRectGetMinX(lightRect), CGRectGetMaxY(lightRect),  
                CGRectGetMaxX(lightRect), CGRectGetMaxY(lightRect),  
            };          
            
            glVertexPointer(2, GL_FLOAT, 0, vertices);
            
            glColor4f(1., 0., 0., 1.);
            
            if (lightIntensity == 1.)
            {
                //[lightColor set];
                CGColorRef clr = lightColor.CGColor;
                if (CGColorGetNumberOfComponents(clr) != 4) goto bail;
                const CGFloat *rgba;
                rgba = CGColorGetComponents(clr);
                glColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
                glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            } else if (lightIntensity > 0.) {
                //CGColorRef clr = CGColorCreateCopyWithAlpha([lightColor CGColor], lightIntensity);
                //CGContextSetFillColorWithColor(cxt, clr);
                CGColorRef clr = lightColor.CGColor;
                if (CGColorGetNumberOfComponents(clr) != 4) goto bail;
                const CGFloat *rgba;
                rgba = CGColorGetComponents(clr);
                glColor4f(rgba[0], rgba[1], rgba[2], lightIntensity);
                glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
                //CGColorRelease(clr);
            }
            
            lightMinVal = lightMaxVal;
        }
        
        
    }
    
    
    
bail:   
    glPopMatrix();
    
    glFlush();
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer);
    [_context presentRenderbuffer:GL_RENDERBUFFER_OES];
}   
 
 
- (void)layoutSubviews
{
    [EAGLContext setCurrentContext:_context];
    [self _destroyFramebuffer];
    [self _createFramebuffer];
    [self _drawView];
}
 
 
 
- (void)drawRect:(CGRect)rect
{
    [self _drawView];
}
 
- (void)setNeedsDisplay
{
    [self _drawView];
}
 
 
- (void)dealloc
{
    if([EAGLContext currentContext] == _context) {
        [EAGLContext setCurrentContext:nil];
    }
    
    [_context release];
    _context = nil;
    
    
    [super dealloc];
}
 
 
 
 
@end