Skip to content

Instantly share code, notes, and snippets.

@Shilo
Created March 12, 2012 04:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Shilo/2019733 to your computer and use it in GitHub Desktop.
Save Shilo/2019733 to your computer and use it in GitHub Desktop.
A category for Sparrow that allows blend modes.
//
// 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
//
// 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
@jowie
Copy link

jowie commented Sep 25, 2013

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment