/ CoreGraphics / CGLPixelSurface.m
CGLPixelSurface.m
  1  #define GL_GLEXT_PROTOTYPES 1
  2  
  3  #import <CoreGraphics/CGLPixelSurface.h>
  4  #import <CoreGraphics/CGWindow.h>
  5  #import <Onyx2D/O2Image.h>
  6  
  7  // this should be fixed upstream
  8  #ifndef DARLING
  9  #import <AppKit/O2Surface_DIBSection.h>
 10  #else
 11  #import <Onyx2D/O2Surface.h>
 12  #endif
 13  
 14  @implementation CGLPixelSurface
 15  
 16  - initWithSize: (O2Size) size {
 17      _width = size.width;
 18      _height = size.height;
 19      _validBuffers = NO;
 20      _numberOfBuffers = 0;
 21      _bufferObjects = NULL;
 22      _readPixels = NULL;
 23      _staticPixels = NULL;
 24      return self;
 25  }
 26  
 27  - (void) dealloc {
 28      [_surface release];
 29      [super dealloc];
 30  }
 31  
 32  - (void) setFrameSize: (O2Size) value {
 33      _width = value.width;
 34      _height = value.height;
 35  
 36      _validBuffers = NO;
 37  }
 38  
 39  - (void) setOpaque: (BOOL) value {
 40      _isOpaque = value;
 41  }
 42  
 43  - (void) validateBuffersIfNeeded {
 44      int i;
 45  
 46      if (_validBuffers)
 47          return;
 48  
 49      // 0's are silently ignored per spec.
 50      if (_numberOfBuffers > 0 &&
 51          _bufferObjects != NULL) // nVidia driver will crash if bufferObjects is
 52                                  // NULL, does not conform to spec.
 53          glDeleteBuffers(_numberOfBuffers, _bufferObjects);
 54  
 55      if (_bufferObjects != NULL)
 56          free(_bufferObjects);
 57  
 58      if (_readPixels != NULL)
 59          free(_readPixels);
 60  
 61      if (_staticPixels != NULL)
 62          free(_staticPixels);
 63  
 64      [_surface release];
 65  
 66      _validBuffers = YES;
 67      _numberOfBuffers = 1;
 68      _rowsPerBuffer = (_height + (_numberOfBuffers - 1)) / _numberOfBuffers;
 69      _bufferObjects = malloc(_numberOfBuffers * sizeof(GLuint));
 70      _readPixels = malloc(_numberOfBuffers * sizeof(void *));
 71      _staticPixels = malloc(_numberOfBuffers * sizeof(void *));
 72  #ifndef DARLING
 73      _surface = [[O2Surface_DIBSection alloc] initWithWidth: _width
 74                                                      height: -_height
 75                                 compatibleWithDeviceContext: nil];
 76  #else
 77      O2ColorSpaceRef colorSpace = O2ColorSpaceCreateDeviceRGB();
 78      _surface =
 79              [[O2Surface alloc] initWithBytes: NULL
 80                                         width: _width
 81                                        height: -_height
 82                              bitsPerComponent: 8
 83                                   bytesPerRow: 0
 84                                    colorSpace: colorSpace
 85                                    bitmapInfo: kO2ImageAlphaPremultipliedFirst |
 86                                                kO2BitmapByteOrder32Little];
 87      O2ColorSpaceRelease(colorSpace);
 88  #endif
 89  
 90      for (i = 0; i < _numberOfBuffers; i++) {
 91          _bufferObjects[i] = 0;
 92          _readPixels[i] = NULL;
 93          _staticPixels[i] = NULL;
 94      }
 95  
 96      // CGLGenBuffers(_numberOfBuffers,_bufferObjects);
 97  
 98      int row = 0, bytesPerRow = _width * 4;
 99  
100      for (i = 0; i < _numberOfBuffers; i++) {
101          _staticPixels[i] =
102                  ((uint8_t *) [_surface pixelBytes]) + row * bytesPerRow;
103  
104          if (_bufferObjects[i] == 0) {
105              _readPixels[i] = _staticPixels[i];
106          } else {
107              _readPixels[i] = NULL;
108              glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, _bufferObjects[i]);
109              glBufferData(GL_PIXEL_PACK_BUFFER_ARB, _width * _rowsPerBuffer * 4,
110                           NULL, GL_STREAM_READ);
111              glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
112          }
113  
114          row += _rowsPerBuffer;
115      }
116  }
117  
118  //#define RGBA_NOT_BGRA 1
119  
120  #ifdef RGBA_NOT_BGRA
121  #define PIXEL_FORMAT GL_RGBA
122  #else
123  #define PIXEL_FORMAT GL_BGRA
124  #endif
125  
126  static inline uint32_t setAlpha255(uint32_t value) {
127  #ifdef RGBA_NOT_BGRA
128      unsigned int a = 0xFF;
129      unsigned int b = (value >> 16) & 0xFF;
130      unsigned int g = (value >> 8) & 0xFF;
131      unsigned int r = (value >> 0) & 0xFF;
132  
133      value = a << 24;
134      value |= r << 16;
135      value |= g << 8;
136      value |= b;
137  
138      return value;
139  #else
140      return value |= 0xFF000000;
141  #endif
142  }
143  
144  static inline uint32_t premultiplyPixel(uint32_t value) {
145  #ifdef RGBA_NOT_BGRA
146      unsigned int a = (value >> 24) & 0xFF;
147      unsigned int b = (value >> 16) & 0xFF;
148      unsigned int g = (value >> 8) & 0xFF;
149      unsigned int r = (value >> 0) & 0xFF;
150  #else
151      unsigned int a = (value >> 24) & 0xFF;
152      unsigned int r = (value >> 16) & 0xFF;
153      unsigned int g = (value >> 8) & 0xFF;
154      unsigned int b = (value >> 0) & 0xFF;
155  #endif
156  
157      value &= 0xFF000000;
158      value |= O2Image_8u_mul_8u_div_255(r, a) << 16;
159      value |= O2Image_8u_mul_8u_div_255(g, a) << 8;
160      value |= O2Image_8u_mul_8u_div_255(b, a);
161  
162      return value;
163  }
164  
165  - (O2Surface *) validSurface {
166      [self validateBuffersIfNeeded];
167      return _surface;
168  }
169  
170  - (void) readBuffer {
171  
172      [self validateBuffersIfNeeded];
173  
174      int bytesPerRow = _width * 4;
175      int i, row = 0;
176  
177      if (glGetError() != GL_NO_ERROR)
178          return;
179  #if 0
180     glPixelStorei(GL_PACK_ALIGNMENT, 4);
181     glPixelStorei(GL_PACK_ROW_LENGTH, 0);
182     glPixelStorei(GL_PACK_SKIP_ROWS, 0);
183     glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
184  #endif
185  
186      // Technically shouldn't need unbind, but to be safe
187      BOOL unbind = NO;
188  
189      for (i = 0; i < _numberOfBuffers; i++) {
190          int rowCount = MIN(_height - row, _rowsPerBuffer);
191  
192          if (_bufferObjects[i] == 0)
193              glReadPixels(0, row, _width, rowCount, PIXEL_FORMAT,
194                           GL_UNSIGNED_BYTE, _readPixels[i]);
195          else {
196              glBindBuffer(GL_PIXEL_PACK_BUFFER, _bufferObjects[i]);
197              unbind = YES;
198  
199              glReadPixels(0, row, _width, rowCount, PIXEL_FORMAT,
200                           GL_UNSIGNED_BYTE, 0);
201          }
202  
203          GLenum error = glGetError();
204          if (error != GL_NO_ERROR) {
205              NSLog(@"glReadPixels error=%d", error);
206          }
207          row += rowCount;
208      }
209  
210      if (unbind)
211          glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
212  
213      row = 0;
214      unbind = NO;
215  
216      for (i = 0; i < _numberOfBuffers; i++) {
217          int r, rowCount = MIN(_height - row, _rowsPerBuffer);
218          unsigned char *inputRow;
219          unsigned char *outputRow = _staticPixels[i];
220  
221          if (_bufferObjects[i] == 0)
222              inputRow = _readPixels[i];
223          else {
224              unbind = YES;
225              glBindBuffer(GL_PIXEL_PACK_BUFFER, _bufferObjects[i]);
226              inputRow =
227                      (GLubyte *) glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
228          }
229  
230          if (_isOpaque) {
231              // Opaque contexts ignore alpha so we set it to 0xFF to get proper
232              // results when blending E.g. application clears context with color
233              // and zero alpha, this will display as the color on OS X reading
234              // back will give us 0 alpha, premultiplying will give us black,
235              // which would be wrong.
236  
237              for (r = 0; r < rowCount;
238                   r++, inputRow += bytesPerRow, outputRow += bytesPerRow) {
239                  int c;
240  
241                  for (c = 0; c < bytesPerRow; c += 4) {
242                      uint32_t pixel = *((uint32_t *) (inputRow + c));
243  
244                      pixel = setAlpha255(pixel);
245  
246                      *((uint32_t *) (outputRow + c)) = pixel;
247                  }
248              }
249          } else {
250              for (r = 0; r < rowCount;
251                   r++, inputRow += bytesPerRow, outputRow += bytesPerRow) {
252                  int c;
253  
254                  for (c = 0; c < bytesPerRow; c += 4) {
255                      uint32_t pixel = *((uint32_t *) (inputRow + c));
256  
257                      pixel = premultiplyPixel(pixel);
258  
259                      *((uint32_t *) (outputRow + c)) = pixel;
260                  }
261              }
262          }
263  
264          if (_bufferObjects[i] != 0) {
265              glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
266          }
267  
268          row += rowCount;
269      }
270  
271      if (unbind)
272          glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
273  
274  #if 0    
275     if(_usePixelBuffer){
276      glBindBuffer(GL_PIXEL_PACK_BUFFER,0);
277      if(inputBytes!=NULL){
278       CGLUnmapBuffer(GL_PIXEL_PACK_BUFFER);
279  }
280     }
281  #endif
282  }
283  
284  - (NSString *) description {
285      return [NSString stringWithFormat: @"<%@ %p:size={  %d %d } surface=%@",
286                                         [self class], self, _width, _height,
287                                         _surface];
288  }
289  
290  @end