text: 
  #import "GPUImageHueFilter.h"
  
  // Adapted from http://stackoverflow.com/questions/9234724/how-to-change-hue-of-a-texture-with-glsl - see for code and discussion
  #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
  NSString *const kGPUImageHueFragmentShaderString = SHADER_STRING
  (
   precision highp float;
   varying highp vec2 textureCoordinate;
   
   uniform sampler2D inputImageTexture;
   uniform mediump float hueAdjust;
   const highp  vec4  kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
   const highp  vec4  kRGBToI     = vec4 (0.595716, -0.274453, -0.321263, 0.0);
   const highp  vec4  kRGBToQ     = vec4 (0.211456, -0.522591, 0.31135, 0.0);
   
   const highp  vec4  kYIQToR   = vec4 (1.0, 0.9563, 0.6210, 0.0);
   const highp  vec4  kYIQToG   = vec4 (1.0, -0.2721, -0.6474, 0.0);
   const highp  vec4  kYIQToB   = vec4 (1.0, -1.1070, 1.7046, 0.0);
   
   void main ()
   {
       // Sample the input pixel
       highp vec4 color   = texture2D(inputImageTexture, textureCoordinate);
       
       // Convert to YIQ
       highp float   YPrime  = dot (color, kRGBToYPrime);
       highp float   I      = dot (color, kRGBToI);
       highp float   Q      = dot (color, kRGBToQ);
       
       // Calculate the hue and chroma
       highp float   hue     = atan (Q, I);
       highp float   chroma  = sqrt (I * I + Q * Q);
       
       // Make the user's adjustments
       hue += (-hueAdjust); //why negative rotation?
       
       // Convert back to YIQ
       Q = chroma * sin (hue);
       I = chroma * cos (hue);
       
       // Convert back to RGB
       highp vec4    yIQ   = vec4 (YPrime, I, Q, 0.0);
       color.r = dot (yIQ, kYIQToR);
       color.g = dot (yIQ, kYIQToG);
       color.b = dot (yIQ, kYIQToB);
       
       // Save the result
       gl_FragColor = color;
   }
  );
  #else
  NSString *const kGPUImageHueFragmentShaderString = SHADER_STRING
  (
   varying vec2 textureCoordinate;
   
   uniform sampler2D inputImageTexture;
   uniform float hueAdjust;
   const vec4  kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
   const vec4  kRGBToI     = vec4 (0.595716, -0.274453, -0.321263, 0.0);
   const vec4  kRGBToQ     = vec4 (0.211456, -0.522591, 0.31135, 0.0);
   
   const vec4  kYIQToR   = vec4 (1.0, 0.9563, 0.6210, 0.0);
   const vec4  kYIQToG   = vec4 (1.0, -0.2721, -0.6474, 0.0);
   const vec4  kYIQToB   = vec4 (1.0, -1.1070, 1.7046, 0.0);
   
   void main ()
   {
       // Sample the input pixel
       vec4 color   = texture2D(inputImageTexture, textureCoordinate);
       
       // Convert to YIQ
       float   YPrime  = dot (color, kRGBToYPrime);
       float   I      = dot (color, kRGBToI);
       float   Q      = dot (color, kRGBToQ);
       
       // Calculate the hue and chroma
       float   hue     = atan (Q, I);
       float   chroma  = sqrt (I * I + Q * Q);
       
       // Make the user's adjustments
       hue += (-hueAdjust); //why negative rotation?
       
       // Convert back to YIQ
       Q = chroma * sin (hue);
       I = chroma * cos (hue);
       
       // Convert back to RGB
       vec4    yIQ   = vec4 (YPrime, I, Q, 0.0);
       color.r = dot (yIQ, kYIQToR);
       color.g = dot (yIQ, kYIQToG);
       color.b = dot (yIQ, kYIQToB);
       
       // Save the result
       gl_FragColor = color;
   }
  );
  #endif
  
  @implementation GPUImageHueFilter
  @synthesize hue;
  
  - (id)init
  {
      if(! (self = [super initWithFragmentShaderFromString:kGPUImageHueFragmentShaderString]) )
      {
          return nil;
      }
      
      hueAdjustUniform = [filterProgram uniformIndex:@"hueAdjust"];
      self.hue = 90;
      
      return self;
  }
  
  - (void)setHue:(CGFloat)newHue
  {
      // Convert degrees to radians for hue rotation
      hue = fmodf(newHue, 360.0) * M_PI/180;
      [self setFloat:hue forUniform:hueAdjustUniform program:filterProgram];
  }
  
  @end
  