How to create a CGGradient for my UIView subclass? - objective-c

Does anyone know how to create a CGGradient that will fill my view,
my current code is this and it fill the UIView with a red rectangle I want to have a gradient (from black to grey for instance) instead of the rect :
- (void)drawRect:(CGRect)rect {
CGContextRef context=UIGraphicsGetCurrentContext();
CGRect r;
r.origin.x=0.;
r.origin.y=0.;
r.size.width=rect.size.width;
r.size.height=rect.size.height;
CGContextSetRGBFillColor(context, 1., 0., 0., 1.);
CGContextFillRect (context,r);
}

In my answer to this question, I provide code for drawing a gloss gradient within a UIView. The colors and drawing positions can be modified from that to form whatever linear gradient you need.

this is a subclass where you can chose the colors
.h file
#import <UIKit/UIKit.h>
#interface GradientView : UIView {
CGGradientRef gradient;
}
#property(nonatomic, assign) CGGradientRef gradient;
- (id)initWithGradient:(CGGradientRef)gradient;
- (id)initWithColor:(UIColor*)top bottom:(UIColor*)bot;
- (void)setGradientWithColor:(UIColor*)top bottom:(UIColor*)bot;
- (void)getRGBA:(CGFloat*)buffer;
#end
.m file
#import "GradientView.h"
#implementation GradientView
#synthesize gradient;
- (id)initWithGradient:(CGGradientRef)grad {
self = [super init];
if(self){
[self setGradient:grad];
}
return self;
}
- (id)initWithColor:(UIColor*)top bottom:(UIColor*)bot {
self = [super init];
if(self){
[self setGradientWithColor:top bottom:bot];
}
return self;
}
- (void)setGradient:(CGGradientRef)g {
if(gradient != NULL && g != gradient){
CGGradientRelease(gradient);
}
if(g != gradient){
CGGradientRetain(g);
}
gradient = g;
[self setNeedsDisplay];
}
- (void)setGradientWithColor:(UIColor*)top bottom:(UIColor*)bot {
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat clr[8];
[top getRGBA:clr];
[bot getRGBA:clr+4] ;
CGGradientRef grad = CGGradientCreateWithColorComponents(rgb, clr, NULL, sizeof(clr)/(sizeof(clr[0])*4));
[self setGradient:grad];
CGColorSpaceRelease(rgb);
CGGradientRelease(grad);
}
- (void)getRGBA:(CGFloat*)buffer {
CGColorRef clr = [self CGColor];
NSInteger n = CGColorGetNumberOfComponents(clr);
const CGFloat *colors = CGColorGetComponents(clr);
// TODO: add other ColorSpaces support
switch (n) {
case 2:
for(int i = 0; i<3; ++i){
buffer[i] = colors[0];
}
buffer[3] = CGColorGetAlpha(clr);
break;
case 3:
for(int i = 0; i<3; ++i){
buffer[i] = colors[i];
}
buffer[3] = 1.0;
break;
case 4:
for(int i = 0; i<4; ++i){
buffer[i] = colors[i];
}
break;
default:
break;
}
}
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextDrawLinearGradient(c, gradient, CGPointMake(0, 0), CGPointMake(0, rect.size.height), 0);
}
- (void)dealloc {
CGGradientRelease(gradient);
[super dealloc];
}
#end

Use CGGradientCreateWithColorComponents or CGGradientCreateWithColors to create the gradient. (The latter takes CGColor objects.) Then, use CGContextDrawLinearGradient or CGContextDrawRadialGradient to draw it.
A linear gradient will extend infinitely in at least the two directions perpendicular to the line of the gradient. A radial gradient extends infinitely in every direction. To prevent the gradient from spilling outside your view, you'll probably need to add the view's bounds to the clipping path using CGContextClipToRect.

Related

Filling a grid with buttons

I am new to objective c and trying the learn the basics of creating a UI. In my UIView class, I have created a grid along with buttons, yet those buttons don't actually exist (as far as I can tell). Ideally, when I click a button, the image is supposed to change, but that doesn't happen. Where should I be looking at to fix?
- (id)initWithFrame:(CGRect)frame {
if( self = [super init]){
tiles_ = [NSMutableArray array];
tileClosed = NO;
}
return self = [super initWithFrame:frame];
}
- (void) initTile : (Tile *) sender
{
int MINE_COUNT = 16;
for(int i = 0; i < MINE_COUNT; i++){
while(1){
int rand = random() % [tiles_ count];
Tile * tile = [tiles_ objectAtIndex:rand];
if(tile != sender && !tile.isMine){
tile.isMine = YES;
break;
}
}
}
tileClosed = YES;
}
- (void)drawRect:(CGRect)rect
{
NSLog( #"drawRect:" );
CGContextRef context = UIGraphicsGetCurrentContext();
// shrink into upper left quadrant
CGRect bounds = [self bounds]; // get view's location and size
CGFloat w = CGRectGetWidth( bounds ); // w = width of view (in points)
CGFloat h = CGRectGetHeight ( bounds ); // h = height of view (in points)
dw = w/16.0f; // dw = width of cell (in points)
dh = h/16.0f; // dh = height of cell (in points)
NSLog( #"view (width,height) = (%g,%g)", w, h );
NSLog( #"cell (width,height) = (%g,%g)", dw, dh );
// draw lines to form a 16x16 cell grid
CGContextBeginPath( context ); // begin collecting drawing operations
for ( int i = 1; i < 16; ++i )
{
// draw horizontal grid line
CGContextMoveToPoint( context, 0, i*dh );
CGContextAddLineToPoint( context, w, i*dh );
}
for ( int i = 1; i < 16; ++i )
{
// draw vertical grid line
CGContextMoveToPoint( context, i*dw, 0 );
CGContextAddLineToPoint( context, i*dw, h );
}
for(int x=1; x<16;x++){
for(int y=1;y<16;y++){
Tile * tile = [[Tile alloc] init];
[tile setFrame:CGRectMake(x * 16.0f, y * 16.0f, 16.0f, 16.0f)];
[tile addTarget:self action:#selector(clickCell:) forControlEvents:UIControlEventTouchUpInside];
[tiles_ addObject: tile]; }
}
[[UIColor grayColor] setStroke]; // use gray as stroke color
CGContextDrawPath( context, kCGPathStroke ); // execute collected drawing ops
}
- (void) clickCell : (Tile *) sender
{
if(! tileClosed) [self initTile: sender];
[sender open];
}
Your initwithFrame: method is broken: the return line should just be return self;.
What you're doing at the moment in effect clobbers the tiles_ array, so it ends up as nil, hence attempting to store tiles in it does nothing (and hence they aren't retained).
Your init method is incorrect and you can change it simply to
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame]
if( self)
{
tiles_ = [NSMutableArray array];
tileClosed = NO;
}
return self;
}
and for the buttons to be appearing you are not adding it to the subview the
[self.view addSubview:tile];
is missing.

UITextview: how to indent the text?

Ok i'm trying to create a ruled notebook view.
The code to create this is below
#interface QuickNoteNoteTextView ()
#property (nonatomic) CGPoint startPoint;
#property (nonatomic) CGPoint endPoint;
#property (nonatomic, retain) UIColor *lineColor;
#end
#implementation QuickNoteNoteTextView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// Get the graphics context
CGContextRef ctx = UIGraphicsGetCurrentContext();
[super drawRect:rect];
// Get the height of a single text line
NSString *alpha = #"ABCD";
CGSize textSize = [alpha sizeWithFont:self.font constrainedToSize:self.contentSize lineBreakMode:NSLineBreakByWordWrapping ];
NSUInteger height = textSize.height;
// Get the height of the view or contents of the view whichever is bigger
textSize = [self.text sizeWithFont:self.font constrainedToSize:self.contentSize lineBreakMode:NSLineBreakByWordWrapping ];
NSUInteger contentHeight = (rect.size.height > textSize.height) ? (NSUInteger)rect.size.height : textSize.height;
NSUInteger offset = 6 + height; // MAGIC Number 6 to offset from 0 to get first line OK ???
contentHeight += offset;
// Draw ruled lines
CGContextSetRGBStrokeColor(ctx, 0, 0, 0, 1);
for(int i=offset;i < contentHeight;i+=height) {
CGPoint lpoints[2] = { CGPointMake(0, i), CGPointMake(rect.size.width, i) };
CGContextStrokeLineSegments(ctx, lpoints, 2);
}
//vertical line
self.startPoint = CGPointMake(22.0, self.frame.size.height * -1);
self.endPoint = CGPointMake(22.0, self.frame.size.height * 2);
self.lineColor = [UIColor redColor];
CGContextSetShouldAntialias(ctx, NO);
CGContextSetStrokeColorWithColor(ctx, [self.lineColor CGColor]);
CGContextSetLineWidth(ctx, 1);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, self.startPoint.x, self.startPoint.y);
CGContextAddLineToPoint(ctx, self.endPoint.x, self.endPoint.y);
CGContextMoveToPoint(ctx, self.startPoint.x + 2.0f, self.startPoint.y);
CGContextAddLineToPoint(ctx, self.endPoint.x + 2.0f, self.endPoint.y);
CGContextDrawPath(ctx, kCGPathStroke);
}
This code correctly draws horizontal ruled lines in a UITextview and a vertical margin line on the left.
The last thing i'm trying to do is shift the text in my textview right so that it sits to the right of the vertical line.
Any ideas?
OK I solved the problem by using contentInset to push the text to the right by 20 pixels, then amended my drawRect to extend the horizontal line by -20 to account for the contentInset

UIImage Resize drawRect Filling Background with Black

I have a class called MNTRectangle which is a subclass of UIImage. I have overridden the drawRect method of this class to draw a border on the image (using its frame). I have it so when the user starts a panning/dragging gesture on a class called DocumentView (subclass of UIView) it adds an instance of MNTRectangle as a subview to the instance of DocumentView. As the user continues to drag the MNTRectangle is resized. The problem is that the MNTRectangle is appearing as solid black in the end, I have tried clearing the graphics context as well as saving the context before drawing the border and restoring the context after drawing the border. No matter what I seem to try I cannot get the MNTRectangle to clear and just show the border on resize.
Here is the code for my MNTRectangle class:
#implementation MNTRectangle
- (id)init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setup];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setup];
}
return self;
}
- (void)setup
{
[self setBackgroundColor:[UIColor clearColor]];
}
- (void)drawRect:(CGRect)rect
{
// Get graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
// CGContextSaveGState(context);
// CGContextClearRect(context, rect);
// Draw border
CGContextSetLineWidth(context, 4.0);
[[UIColor blackColor] setStroke];
CGContextStrokeRect(context, rect);
// CGContextRestoreGState(context);
}
Here is the code in DocumentView for the panning/dragging handling on the UIView:
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
}
return self;
}
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
if ([sender state] == UIGestureRecognizerStateBegan)
{
_startPanPoint = [sender locationInView:self];
MNTRectangle *rectangle = [[MNTRectangle alloc] initWithFrame:CGRectMake(_startPanPoint.x, _startPanPoint.y, 1, 1)];
[_objects addObject:rectangle];
_currentPanObject = rectangle;
[self addSubview:rectangle];
}
else if ([sender state] == UIGestureRecognizerStateChanged)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
}
else if ([sender state] == UIGestureRecognizerStateEnded)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
}
}
Any help with this would be greatly appreciated.
I finally figured it out. In my handlePan: method after I set the frame of the rectangle I was missing [rectangle setNeedsDisplay];.
Now my DocumentView code looks like this:
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
}
return self;
}
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
if ([sender state] == UIGestureRecognizerStateBegan)
{
_startPanPoint = [sender locationInView:self];
MNTRectangle *rectangle = [[MNTRectangle alloc] initWithFrame:CGRectMake(_startPanPoint.x, _startPanPoint.y, 1, 1)];
[_objects addObject:rectangle];
_currentPanObject = rectangle;
[self addSubview:rectangle];
}
else if ([sender state] == UIGestureRecognizerStateChanged)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
[rectangle setNeedsDisplay];
}
else if ([sender state] == UIGestureRecognizerStateEnded)
{
CGPoint endPanPoint = [sender locationInView:self];
float height = fabsf(endPanPoint.y - _startPanPoint.y);
float width = fabsf(endPanPoint.x - _startPanPoint.x);
float x = MIN(_startPanPoint.x, endPanPoint.x);
float y = MIN(_startPanPoint.y, endPanPoint.y);
MNTRectangle *rectangle = (MNTRectangle *)_currentPanObject;
[rectangle setFrame:CGRectMake(x, y, width, height)];
[rectangle setNeedsDisplay];
}
}
You're gonna want to call [super drawRect:rect]
To clarify for anyone reading, calling [rectangle setNeedsDisplay]; is needed because you're overriding drawRect: and thus this subclass of UIView, MNTRectangle, needs to be told to manually redraw itself after it's internal variables have been changed.
This function is called automatically on standard objects that inherit from UIView, but since you've created a custom object, you'll need to call it manually if you have some special behavior in MNTRectangle that requires a redraw.
Further documentation here.

Making a circular NSSlide look and behave like in GarageBand

I am new to drawing with Cocoa, and I am making some software which will have sliders similar to these found in GarageBand:
GB Sliders http://img33.imageshack.us/img33/2668/schermafbeelding2010061r.png
These look beautiful and can be controld by moving the mouse up and down.
Can you help me with customizing NSSliders by subclassing them, so I can make them look and behave exactly as in GarageBand? Thanks.
I have one image for the knob which should be rotated as they do not need to be in 3D .
The simplest way is to create a NSView subclass that handles both the mouse management and the drawing.
There is a sample code that can help you to start named "TLayer". It is part of the Examples of the XCode 3.1.4. It contains a circular custom view that controls the offset and the radius of the shadow drawn for layers. It is easy to understand and easy to extend.
Note: as it does not seems to be available on the Apple website, so I have pasted the sources below.
ShadowOffsetView.h
#import <AppKit/AppKit.h>
extern NSString *ShadowOffsetChanged;
#interface ShadowOffsetView : NSView
{
CGSize _offset;
float _scale;
}
- (float)scale;
- (void)setScale:(float)scale;
- (CGSize)offset;
- (void)setOffset:(CGSize)offset;
#end
ShadowOffsetView.m
#import "ShadowOffsetView.h"
NSString *ShadowOffsetChanged = #"ShadowOffsetChanged";
#interface ShadowOffsetView (Internal)
- (NSCell *)cell;
#end
#implementation ShadowOffsetView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self == nil)
return nil;
_offset = CGSizeZero;
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (float)scale
{
return _scale;
}
- (void)setScale:(float)scale
{
_scale = scale;
}
- (CGSize)offset
{
return CGSizeMake(_offset.width * _scale, _offset.height * _scale);
}
- (void)setOffset:(CGSize)offset
{
offset = CGSizeMake(offset.width / _scale, offset.height / _scale);
if (!CGSizeEqualToSize(_offset, offset)) {
_offset = offset;
[self setNeedsDisplay:YES];
}
}
- (BOOL)isOpaque
{
return NO;
}
- (void)setOffsetFromPoint:(NSPoint)point
{
float radius;
CGSize offset;
NSRect bounds;
bounds = [self bounds];
offset.width = (point.x - NSMidX(bounds)) / (NSWidth(bounds) / 2);
offset.height = (point.y - NSMidY(bounds)) / (NSHeight(bounds) / 2);
radius = sqrt(offset.width * offset.width + offset.height * offset.height);
if (radius > 1) {
offset.width /= radius;
offset.height /= radius;
}
if (!CGSizeEqualToSize(_offset, offset)) {
_offset = offset;
[self setNeedsDisplay:YES];
[(NSNotificationCenter *)[NSNotificationCenter defaultCenter]
postNotificationName:ShadowOffsetChanged object:self];
}
}
- (void)mouseDown:(NSEvent *)event
{
NSPoint point;
point = [self convertPoint:[event locationInWindow] fromView:nil];
[self setOffsetFromPoint:point];
}
- (void)mouseDragged:(NSEvent *)event
{
NSPoint point;
point = [self convertPoint:[event locationInWindow] fromView:nil];
[self setOffsetFromPoint:point];
}
- (void)drawRect:(NSRect)rect
{
NSRect bounds;
CGContextRef context;
float x, y, w, h, r;
bounds = [self bounds];
x = NSMinX(bounds);
y = NSMinY(bounds);
w = NSWidth(bounds);
h = NSHeight(bounds);
r = MIN(w / 2, h / 2);
context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextTranslateCTM(context, x + w/2, y + h/2);
CGContextAddArc(context, 0, 0, r, 0, 2*M_PI, true);
CGContextClip(context);
CGContextSetGrayFillColor(context, 0.910, 1);
CGContextFillRect(context, CGRectMake(-w/2, -h/2, w, h));
CGContextAddArc(context, 0, 0, r, 0, 2*M_PI, true);
CGContextSetGrayStrokeColor(context, 0.616, 1);
CGContextStrokePath(context);
CGContextAddArc(context, 0, -2, r, 0, 2*M_PI, true);
CGContextSetGrayStrokeColor(context, 0.784, 1);
CGContextStrokePath(context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, r * _offset.width, r * _offset.height);
CGContextSetLineWidth(context, 2);
CGContextSetGrayStrokeColor(context, 0.33, 1);
CGContextStrokePath(context);
}
#end
Well, for the actual drawing you'd either have to have images for each rotation angle of the knob (easier to implement) and then just draw the proper one.
(While for a real realistic 3d look—even if possible—programmatic drawing wouldn't be worth its time, I guess.)
Or draw the knob by code. This article should give you an idea I think:
http://katidev.com/blog/2008/03/07/how-to-create-a-custom-control-with-nsview/
(For both, the mouse event handling and basic NSBezerPath drawing of circular and rotating knob-like elements)

Fragment shaders on a texture

I am trying to add some post-processing capabilities to a program. The rendering is done using openGL. I just want to allow the program to load some home made fragment shader and use them on the video stream.
I wrote a little piece of shader using "OpenGL Shader Builder" that just turns a texture in grayscale. The shaders works well in the shader builder but I can't make it work in the main program. The screens stays all black.
Here is the setup :
#implementation PluginGLView
- (id) initWithCoder: (NSCoder *) coder
{
const GLubyte * strExt;
if ((self = [super initWithCoder:coder]) == nil)
return nil;
glLock = [[NSLock alloc] init];
if (nil == glLock) {
[self release];
return nil;
}
// Init pixel format attribs
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFANoRecovery,
NSOpenGLPFADoubleBuffer,
0
};
// Get pixel format from OpenGL
NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
if (!pixFmt)
{
NSLog(#"No Accelerated OpenGL pixel format found\n");
NSOpenGLPixelFormatAttribute attrs2[] =
{
NSOpenGLPFANoRecovery,
0
};
// Get pixel format from OpenGL
pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs2];
if (!pixFmt) {
NSLog(#"No OpenGL pixel format found!\n");
[self release];
return nil;
}
}
[self setPixelFormat:[pixFmt autorelease]];
/*
long swapInterval = 1 ;
[[self openGLContext]
setValues:&swapInterval
forParameter:NSOpenGLCPSwapInterval];
*/
[glLock lock];
[[self openGLContext] makeCurrentContext];
// Init object members
strExt = glGetString (GL_EXTENSIONS);
texture_range = gluCheckExtension ((const unsigned char *)"GL_APPLE_texture_range", strExt) ? GL_TRUE : GL_FALSE;
texture_hint = GL_STORAGE_SHARED_APPLE ;
client_storage = gluCheckExtension ((const unsigned char *)"GL_APPLE_client_storage", strExt) ? GL_TRUE : GL_FALSE;
rect_texture = gluCheckExtension((const unsigned char *)"GL_EXT_texture_rectangle", strExt) ? GL_TRUE : GL_FALSE;
// Setup some basic OpenGL stuff
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Loads the shaders
shader=LoadShader(GL_FRAGMENT_SHADER,"/Users/alexandremathieu/fragment.fs");
program=glCreateProgram();
glAttachShader(program, shader);
glLinkProgram(program);
glUseProgram(program);
[NSOpenGLContext clearCurrentContext];
[glLock unlock];
image_width = 1024;
image_height = 512;
image_depth = 16;
image_type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
image_base = (GLubyte *) calloc(((IMAGE_COUNT * image_width * image_height) / 3) * 4, image_depth >> 3);
if (image_base == nil) {
[self release];
return nil;
}
// Create and load textures for the first time
[self loadTextures:GL_TRUE];
// Init fps timer
//gettimeofday(&cycle_time, NULL);
drawBG = YES;
// Call for a redisplay
noDisplay = YES;
PSXDisplay.Disabled = 1;
[self setNeedsDisplay:true];
return self;
}
And here is the "render screen" function which basically...renders the screen.
- (void)renderScreen
{
int bufferIndex = whichImage;
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, bufferIndex+1);
glUseProgram(program);
int loc=glGetUniformLocation(program, "texture");
glUniform1i(loc,bufferIndex+1);
glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, image_width, image_height, GL_BGRA, image_type, image[bufferIndex]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-1.0f, 1.0f);
glTexCoord2f(0.0f, image_height);
glVertex2f(-1.0f, -1.0f);
glTexCoord2f(image_width, image_height);
glVertex2f(1.0f, -1.0f);
glTexCoord2f(image_width, 0.0f);
glVertex2f(1.0f, 1.0f);
glEnd();
[[self openGLContext] flushBuffer];
[NSOpenGLContext clearCurrentContext];
//[glLock unlock];
}
and finally here's the shader.
uniform sampler2DRect texture;
void main() {
vec4 color, texel;
color = gl_Color;
texel = texture2DRect(texture, gl_TexCoord[0].xy);
color *= texel;
// Begin Shader
float gray=0.0;
gray+=(color.r + color.g + color.b)/3.0;
color=vec4(gray,gray,gray,color.a);
// End Shader
gl_FragColor = color;
}
The loading and using of shaders works since I am able to turn the screen all red with this shader
void main(){
gl_FragColor=vec4(1.0,0.0,0.0,1.0);
}
If the shader contains a syntax error I get an error message from the LoadShader function etc. If I remove the use of the shader, everything works normally.
I think the problem comes from the "passing the texture as a uniform parameter" thing. But these are my very firsts step with openGL and I can't be sure of anything.
Texture samplers have to be set to the number of the active texture unit. So for example with glActiveTexture(GL_TEXTURE3) the sampler must be set to 3 as well. In your case the number should be 0.