Created
March 12, 2012 04:09
-
-
Save Shilo/2019733 to your computer and use it in GitHub Desktop.
A category for Sparrow that allows blend modes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SHBlendModes.h | |
// | |
// Created by Shilo White on 3/11/12. | |
// Copyright (c) 2012 Shilocity Productions. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "SPDisplayObject.h" | |
#import <OpenGLES/ES1/gl.h> | |
typedef enum { | |
SHBlendModeAuto, | |
SHBlendModeNormal, | |
SHBlendModeInverseMask, | |
SHBlendModeErase, | |
SHBlendModeCustom | |
} SHBlendMode; | |
@interface SPDisplayObject (BlendModes) | |
@property (nonatomic, assign) SHBlendMode blendMode; | |
@property (nonatomic, readonly) NSString *blendModeName; | |
@property (nonatomic, assign) GLenum blendModeSourceFactor; | |
@property (nonatomic, assign) GLenum blendModeDestinationFactor; | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor destinationFactor:(GLenum)blendModeDestinationFactor; | |
- (void)renderAndBlend:(SPRenderSupport *)support; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SHBlendModes.m | |
// | |
// Created by Shilo White on 3/11/12. | |
// Copyright (c) 2012 Shilocity Productions. All rights reserved. | |
// | |
#import "SHBlendModes.h" | |
#import <objc/runtime.h> | |
#import <OpenGLES/EAGL.h> | |
#import <OpenGLES/ES1/glext.h> | |
#import "SPView.h" | |
#import "SPStage.h" | |
#import "SPDisplayObjectContainer.h" | |
#import "SPQuad.h" | |
#import "SPImage.h" | |
#import "SPTextField.h" | |
#import "SPRenderTexture.h" | |
@interface SPDisplayObject () | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor destinationFactor:(GLenum)blendModeDestinationFactor isBlendModeCustom:(BOOL)isBlendModeCustom; | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor isBlendModeCustom:(BOOL)isBlendModeCustom; | |
- (void)setBlendModeDestinationFactor:(GLenum)blendModeDestinationFactor isBlendModeCustom:(BOOL)isBlendModeCustom; | |
- (void)blend; | |
@end | |
@interface SPTextField () | |
- (void)redrawContents; | |
@end | |
@interface SPRenderTexture () | |
- (void)renderToFramebuffer:(SPDrawingBlock)block; | |
@end | |
@implementation SPDisplayObject (BlendModes) | |
- (void)setBlendMode:(SHBlendMode)blendMode { | |
switch (blendMode) { | |
case SHBlendModeNormal: | |
[self setBlendModeSourceFactor:GL_SRC_ALPHA destinationFactor:GL_ONE_MINUS_SRC_ALPHA isBlendModeCustom:NO]; | |
break; | |
case SHBlendModeInverseMask: | |
[self setBlendModeSourceFactor:GL_ONE destinationFactor:GL_ONE_MINUS_SRC_ALPHA isBlendModeCustom:NO]; | |
break; | |
case SHBlendModeErase: | |
[self setBlendModeSourceFactor:GL_ZERO destinationFactor:GL_ONE_MINUS_SRC_ALPHA isBlendModeCustom:NO]; | |
break; | |
default: | |
break; | |
} | |
objc_setAssociatedObject(self, @"blendMode", [NSNumber numberWithInt:blendMode], OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
} | |
- (SHBlendMode)blendMode { | |
return [objc_getAssociatedObject(self, @"blendMode") intValue]; | |
} | |
- (NSString *)blendModeName { | |
switch (self.blendMode) { | |
case SHBlendModeAuto: | |
return @"auto"; | |
case SHBlendModeNormal: | |
return @"normal"; | |
case SHBlendModeInverseMask: | |
return @"inverse mask"; | |
case SHBlendModeErase: | |
return @"erase"; | |
case SHBlendModeCustom: | |
return @"custom"; | |
} | |
} | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor destinationFactor:(GLenum)blendModeDestinationFactor { | |
[self setBlendModeSourceFactor:blendModeSourceFactor]; | |
[self setBlendModeDestinationFactor:blendModeDestinationFactor]; | |
} | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor destinationFactor:(GLenum)blendModeDestinationFactor isBlendModeCustom:(BOOL)isBlendModeCustom { | |
[self setBlendModeSourceFactor:blendModeSourceFactor isBlendModeCustom:isBlendModeCustom]; | |
[self setBlendModeDestinationFactor:blendModeDestinationFactor isBlendModeCustom:isBlendModeCustom]; | |
} | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor { | |
[self setBlendModeSourceFactor:blendModeSourceFactor isBlendModeCustom:YES]; | |
} | |
- (void)setBlendModeSourceFactor:(GLenum)blendModeSourceFactor isBlendModeCustom:(BOOL)isBlendModeCustom { | |
if (isBlendModeCustom) self.blendMode = SHBlendModeCustom; | |
objc_setAssociatedObject(self, @"blendModeSourceFactor", [NSNumber numberWithInt:blendModeSourceFactor], OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
} | |
- (GLenum)blendModeSourceFactor { | |
return (GLenum)[objc_getAssociatedObject(self, @"blendModeSourceFactor") intValue]; | |
} | |
- (void)setBlendModeDestinationFactor:(GLenum)blendModeDestinationFactor { | |
[self setBlendModeDestinationFactor:blendModeDestinationFactor isBlendModeCustom:YES]; | |
} | |
- (void)setBlendModeDestinationFactor:(GLenum)blendModeDestinationFactor isBlendModeCustom:(BOOL)isBlendModeCustom { | |
if (isBlendModeCustom) self.blendMode = SHBlendModeCustom; | |
objc_setAssociatedObject(self, @"blendModeDestinationFactor", [NSNumber numberWithInt:blendModeDestinationFactor], OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
} | |
- (GLenum)blendModeDestinationFactor { | |
return (GLenum)[objc_getAssociatedObject(self, @"blendModeDestinationFactor") intValue]; | |
} | |
- (void)blend | |
{ | |
if (self.blendMode == SHBlendModeAuto) return; | |
glBlendFunc(self.blendModeSourceFactor, self.blendModeDestinationFactor); | |
} | |
- (void)renderAndBlend:(SPRenderSupport*)support | |
{ | |
// override in subclass | |
} | |
@end | |
@implementation SPView (BlendModes) | |
- (void)renderStage | |
{ | |
if (mFramebuffer == 0 || mRenderbuffer == 0) | |
return; // buffers not yet initialized | |
[EAGLContext setCurrentContext:mContext]; | |
glBindFramebufferOES(GL_FRAMEBUFFER_OES, mFramebuffer); | |
glViewport(0, 0, mWidth, mHeight); | |
[mRenderSupport reset]; | |
[mStage renderAndBlend:mRenderSupport]; | |
glBindRenderbufferOES(GL_RENDERBUFFER_OES, mRenderbuffer); | |
[mContext presentRenderbuffer:GL_RENDERBUFFER_OES]; | |
} | |
@end | |
@implementation SPStage (BlendModes) | |
- (void)renderAndBlend:(SPRenderSupport *)support | |
{ | |
[SPRenderSupport clearWithColor:mColor alpha:1.0f]; | |
[SPRenderSupport setupOrthographicRenderingWithLeft:0 right:mWidth bottom:mHeight top:0]; | |
[super renderAndBlend:support]; | |
#if DEBUG | |
[SPRenderSupport checkForOpenGLError]; | |
#endif | |
} | |
@end | |
@implementation SPDisplayObjectContainer (BlendModes) | |
- (void)renderAndBlend:(SPRenderSupport *)support | |
{ | |
float alpha = self.alpha; | |
for (SPDisplayObject *child in mChildren) | |
{ | |
float childAlpha = child.alpha; | |
if (childAlpha != 0.0f && child.visible) | |
{ | |
glPushMatrix(); | |
[SPRenderSupport transformMatrixForObject:child]; | |
child.alpha *= alpha; | |
[child renderAndBlend:support]; | |
child.alpha = childAlpha; | |
glPopMatrix(); | |
} | |
} | |
} | |
@end | |
@implementation SPQuad (BlendModes) | |
- (void)renderAndBlend:(SPRenderSupport *)support | |
{ | |
static uint colors[4]; | |
float alpha = self.alpha; | |
[support bindTexture:nil]; | |
[self blend]; | |
for (int i=0; i<4; ++i) | |
colors[i] = [support convertColor:mVertexColors[i] alpha:alpha]; | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glEnableClientState(GL_COLOR_ARRAY); | |
glVertexPointer(2, GL_FLOAT, 0, mVertexCoords); | |
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
glDisableClientState(GL_VERTEX_ARRAY); | |
glDisableClientState(GL_COLOR_ARRAY); | |
} | |
@end | |
@implementation SPImage (BlendModes) | |
- (void)renderAndBlend:(SPRenderSupport *)support | |
{ | |
static float texCoords[8]; | |
static uint colors[4]; | |
float alpha = self.alpha; | |
[support bindTexture:mTexture]; | |
[self blend]; | |
[mTexture adjustTextureCoordinates:mTexCoords saveAtTarget:texCoords numVertices:4]; | |
for (int i=0; i<4; ++i) | |
colors[i] = [support convertColor:mVertexColors[i] alpha:alpha]; | |
SPRectangle *frame = mTexture.frame; | |
if (frame) | |
{ | |
glTranslatef(-frame.x, -frame.y, 0.0f); | |
glScalef(mTexture.width / frame.width, mTexture.height / frame.height, 1.0f); | |
} | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glEnableClientState(GL_COLOR_ARRAY); | |
glTexCoordPointer(2, GL_FLOAT, 0, texCoords); | |
glVertexPointer(2, GL_FLOAT, 0, mVertexCoords); | |
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
glDisableClientState(GL_TEXTURE_COORD_ARRAY); | |
glDisableClientState(GL_VERTEX_ARRAY); | |
glDisableClientState(GL_COLOR_ARRAY); | |
// Rendering was tested with vertex buffers, too -- but for simple quads and images like these, | |
// the overhead seems to outweigh the benefit. The "glDrawArrays"-approach is faster here. | |
} | |
@end | |
@implementation SPTextField (BlendModes) | |
- (void)renderAndBlend:(SPRenderSupport *)support | |
{ | |
if (mRequiresRedraw) [self redrawContents]; | |
[super renderAndBlend:support]; | |
} | |
@end | |
@implementation SPRenderTexture (BlendModes) | |
- (void)drawObject:(SPDisplayObject *)object | |
{ | |
[self renderToFramebuffer:^ | |
{ | |
glPushMatrix(); | |
[SPRenderSupport transformMatrixForObject:object]; | |
[object renderAndBlend:mRenderSupport]; | |
glPopMatrix(); | |
}]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had to alter
renderStage
so that it also had the extra timestamp information for animations. Also, for some reason it doesn't just alter the blend mode of the target sprite, but randomly some other objects in the same display chain also. Do you know why this might be?