GLTexture.m 4.38 KB
Newer Older
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
1 2
#import "GLTexture.h"
#import "RCTLog.h"
3
#import "RCTUtils.h"
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
4

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
GLImageData* genPixelsEmpty (int width, int height)
{
  GLubyte* data = (GLubyte *) malloc(width*height*4*sizeof(GLubyte));
  for (int i = 0; i < width * height * 4; i+=4) {
    data[i] = data[i+1] = data[i+2] = 0;
    data[i+3] = 0;
  }
  return [[GLImageData alloc] initWithData:data withWidth:width withHeight:height];
}

GLImageData* genPixelsRandom (int width, int height)
{
  GLubyte* data = (GLubyte *) malloc(width*height*4*sizeof(GLubyte));
  for (int i = 0; i < width * height * 4; i+=4) {
    data[i] = rand() % 255;
    data[i+1] = rand() % 255;
    data[i+2] = rand() % 255;
    data[i+3] = 255;
  }
  return [[GLImageData alloc] initWithData:data withWidth:width withHeight:height];
}

GLImageData* genPixelsWithImage (UIImage *image)
{
  int width = image.size.width;
  int height = image.size.height;
  if (width == 0 || height == 0) {
    RCTLogError(@"The image must be loaded in setPixelsWithImage call");
    return nil;
  }
  GLubyte* data = malloc(width * height * 4);
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef ctx = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
  if (ctx == NULL) {
    RCTLogError(@"unable to create the bitmap context");
    CGColorSpaceRelease(colorSpace);
    free(data);
    return nil;
  }
  CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, height);
  CGContextConcatCTM(ctx, flipVertical);
  
  CGRect rect = CGRectMake(0.0, 0.0, width, height);
  CGContextClearRect(ctx, rect);
  CGContextDrawImage(ctx, rect, image.CGImage);
  CGColorSpaceRelease(colorSpace);
  CGContextRelease(ctx);
  return [[GLImageData alloc] initWithData:data withWidth:width withHeight:height];
}

GLImageData* genPixelsWithView (UIView *view)
{
57 58
  float width = RCTScreenScale() * view.bounds.size.width;
  float height = RCTScreenScale() * view.bounds.size.height;
59 60 61 62
  GLubyte *data = (GLubyte *)malloc(4 * width * height);
  CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef ctx = CGBitmapContextCreate(data, width, height, 8, 4 * width, colourSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
  CGColorSpaceRelease(colourSpace);
63 64
  CGContextClearRect(ctx, CGRectMake(0.0, 0.0, width, height));
  CGContextScaleCTM(ctx, RCTScreenScale(), RCTScreenScale());
65 66 67 68 69
  [view.layer renderInContext:ctx];
  CGContextRelease(ctx);
  return [[GLImageData alloc] initWithData:data withWidth:width withHeight:height];
}

Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
70 71
@implementation GLTexture
{
72 73
  GLuint _handle; // The identifier of the gl texture
  GLImageData* dataCurrentlyUploaded; // The last set data (cache)
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
74 75
}

76 77
GLImageData *EMPTY_PIXELS;

Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
78 79
- (instancetype)init
{
80 81 82
  if (!EMPTY_PIXELS) {
    EMPTY_PIXELS = genPixelsEmpty(2, 2);
  }
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
83 84 85 86 87 88 89 90 91
  self = [super init];
  if (self) {
    [self makeTexture];
  }
  return self;
}

- (void)dealloc
{
92
  glDeleteTextures(1, &_handle);
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
93 94 95 96 97
  dataCurrentlyUploaded = nil;
}

- (void) makeTexture
{
98 99
  glGenTextures(1, &_handle);
  glBindTexture(GL_TEXTURE_2D, _handle);
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
100 101 102 103 104 105 106 107 108
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

- (int)bind: (int)unit
{
  glActiveTexture(GL_TEXTURE0 + unit);
109
  glBindTexture(GL_TEXTURE_2D, _handle);
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
110 111 112
  return unit;
}

113 114 115 116 117 118 119 120 121 122 123 124
- (void)bind
{
  glBindTexture(GL_TEXTURE_2D, _handle);
}

- (void)setShapeWithWidth:(float)width withHeight:(float)height
{
  [self bind];
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}

- (void)setPixels: (GLImageData *)data
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
125 126 127
{
  if (data != dataCurrentlyUploaded) {
    dataCurrentlyUploaded = data;
128
    [self bind];
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
129 130 131 132 133 134
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data.width, data.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data);
  }
}

- (void)setPixelsEmpty
{
135
  [self setPixels:EMPTY_PIXELS];
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
136 137 138 139
}

- (void)setPixelsRandom: (int)width withHeight:(int)height // for testing
{
140
  GLImageData* data = genPixelsRandom(width, height);
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
141 142 143 144 145
  [self setPixels:data];
}

- (void)setPixelsWithImage: (UIImage *)image
{
146
  GLImageData *data = genPixelsWithImage(image);
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
147 148 149 150 151 152
  if (!data) return;
  [self setPixels:data];
}

- (void)setPixelsWithView: (UIView *)view
{
153
  GLImageData *data = genPixelsWithView(view);
Gaëtan Renaudeau's avatar
Gaëtan Renaudeau committed
154 155 156 157 158
  [self setPixels:data];
}


@end