适用于绘图应用的高分辨率内容在iPad设备上使用OpenGL ES

时间:2012-03-13 12:55:53

标签: objective-c ios xcode ipad opengl-es

我正在开发适用于iPhone和iPad的绘画应用[参考GLPaint应用]。在这个应用程序中,我根据用户触摸的位置通过屏幕上的图纸线填充油漆图像中的颜色。应用程序适用于iPhone。在iPad中没有缩放在绘制视图上的线条是正确的[无像素失真],但在缩放后的缩放线条上的像素失真,即OpenGL ES的内容不是高分辨率。

我正在使用以下代码初始化绘制视图:

-(id)initWithCoder:(NSCoder*)coder {
   CGImageRef      brushImage;
   CGContextRef    brushContext;
   GLubyte         *brushData;
   size_t          width, height;
   CGFloat         components[3];

   if ((self = [super initWithCoder:coder])) {
      CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
      eaglLayer.opaque = NO;
      eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
      context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

      if (!context || ![EAGLContext setCurrentContext:context]) {
         return nil;
      }

      if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
      {
         brushImage = [UIImage imageNamed:@"circle 64.png"].CGImage;
      }
      else {
         brushImage = [UIImage imageNamed:@"flower 128.png"].CGImage;
      }

      // Get the width and height of the image
      width = CGImageGetWidth(brushImage)   ;
      height = CGImageGetHeight(brushImage) ;

      if(brushImage) {
         // Allocate  memory needed for the bitmap context
         brushData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));

         // Use  the bitmatp creation function provided by the Core Graphics framework. 
         brushContext = CGBitmapContextCreate(brushData, width, height, 8, width * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast);

         // After you create the context, you can draw the  image to the context.
         CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), brushImage);

         // You don't need the context at this point, so you need to release it to avoid memory leaks.
         CGContextRelease(brushContext);

         // Use OpenGL ES to generate a name for the texture.
         glGenTextures(1, &brushTexture);

         // Bind the texture name. 
         glBindTexture(GL_TEXTURE_2D, brushTexture);

         // Set the texture parameters to use a minifying filter and a linear filer (weighted average)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

         // Specify a 2D texture image, providing the a pointer to the image data in memory
         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);

         // Release  the image data; it's no longer needed
         free(brushData);
      }

      CGFloat scale;


      if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
      {
         NSLog(@"IPAd");
         self.contentScaleFactor=1.0;
         scale = self.contentScaleFactor;
      }

      else {
         // NSLog(@"IPHone");
         self.contentScaleFactor=2.0;
      }

      //scale = 2.000000;

      // Setup OpenGL states
      glMatrixMode(GL_PROJECTION);
      CGRect frame = self.bounds;
      NSLog(@"Scale %f", scale);
      glOrthof(0, (frame.size.width) * scale, 0, (frame.size.height) * scale, -1, 1);
      glViewport(0, 0, (frame.size.width) * scale, (frame.size.height) * scale);
      glMatrixMode(GL_MODELVIEW);
      glDisable(GL_DITHER);
      glEnable(GL_BLEND);
      glEnable(GL_TEXTURE_2D);
      glEnableClientState(GL_VERTEX_ARRAY);
      glEnable(GL_BLEND);

      // Set a blending function appropriate for premultiplied alpha pixel data
      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      glEnable(GL_POINT_SPRITE_OES);
      glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
      glPointSize(width / kBrushScale);

      // Make sure to start with a cleared buffer
      needsErase = YES;

      // Define a starting color 
      HSL2RGB((CGFloat) 0.0 / (CGFloat)kPaletteSize, kSaturation, kLuminosity, &components[0], &components[1], &components[2]);
      [self setBrushColorWithRed:245.0f green:245.0f blue:0.0f];
      boolEraser=NO;
   }

   return self;
}

创建框架缓冲区

-(BOOL)createFramebuffer {
   // Generate IDs for a framebuffer object and a color renderbuffer
   glGenFramebuffersOES(1, &viewFramebuffer);
   glGenRenderbuffersOES(1, &viewRenderbuffer);

   glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
   glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

   // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)
   // allowing us to draw into a buffer that will later be rendered to screen wherever the layer is (which corresponds with our view).
   [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
   glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

   // Get the size of the backing CAEAGLLayer
   glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
   glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

   // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
   glGenRenderbuffersOES(1, &depthRenderbuffer);
   glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
   glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
   glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);

   if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
   {
      NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
      return NO;
   }

   return YES;
}

使用以下代码绘制线条

-(void)renderLineFromPoint:(CGPoint)start toPoint:(CGPoint)end {
   static GLfloat*     vertexBuffer = NULL;
   static NSUInteger   vertexMax = 64;
   NSUInteger          vertexCount = 0,
                       count,
                       i;

   [EAGLContext setCurrentContext:context];
   glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

   // Convert locations from Points to Pixels
   //CGFloat scale = self.contentScaleFactor;
   CGFloat scale;
   scale=self.contentScaleFactor;
   NSLog(@"Scale %f",scale);

   start.x *= scale;
   start.y *= scale;
   end.x *= scale;
   end.y *= scale;

   float dx = end.x - start.x;
   float dy = end.y - start.y;
   float dist = (sqrtf(dx * dx + dy * dy)/ kBrushPixelStep);

   // Allocate vertex array buffer
   if(vertexBuffer == NULL)
      //  vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));
      vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));
      count = MAX(ceilf(dist), 1);

      //NSLog(@"count %d",count);

      for(i = 0; i < count; ++i) {
         if (vertexCount == vertexMax) {
            vertexMax = 2 * vertexMax;
            vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
            //  NSLog(@"if loop");
         }

         vertexBuffer[2 * vertexCount + 0] = start.x + (dx) * ((GLfloat)i / (GLfloat)count);
         vertexBuffer[2 * vertexCount + 1] = start.y + (dy) * ((GLfloat)i / (GLfloat)count);

         vertexCount += 1;
      }

   // Render the vertex array
   glVertexPointer(2, GL_FLOAT, 0, vertexBuffer);
   glDrawArrays(GL_POINTS, 0, vertexCount);
   glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
   [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

对于ipad设备,绘画视图的内容是正常的 - 高分辨率对于普通视图但是在缩放后我没有得到高分辨率内容的绘画视图像素的线条看起来扭曲。

我尝试更改ContentScaleFactor以及上面代码的缩放参数以查看差异,但没有按预期工作。 IPad支持1.0&amp;的contentScaleFactor。 1.5,当我设置contentScaleFactor = 2时,Paint视图无法显示行显示奇怪的虚线。

有没有办法让OpenGL的内容高分辨率?

2 个答案:

答案 0 :(得分:1)

我不确定你的意思是高分辨率。 opengl是一个带有位图支持渲染系统的矢量库。后备存储将具有用于创建渲染缓冲区的图层的大小(以内容比例因子为单位):

- (BOOL)renderbufferStorage:(NSUInteger)target fromDrawable:(id<EAGLDrawable>)drawable

一旦创建就无法改变分辨率,通常这样做也没有意义,每个屏幕像素的一个渲染缓冲像素最有意义。

很难确切地知道你想要解决的问题,而不知道你正在谈论什么缩放。我假设您在CAEAGLLayer中设置了UIScrollView,并且您看到像素工件。这是不可避免的,它还有什么用呢?

如果你想让你的线条平滑,你需要使用边缘有alpha混合的三角形条纹网格来实现它们,这将提供抗锯齿。而不是缩放图层本身,您只需通过缩放顶点“缩放”内容,但保持CAEAGLLayer相同的大小。这将消除像素化并给出purdy alpha混合边缘。

答案 1 :(得分:1)

简短回答是,您可以拥有“高分辨率”内容。

但在解决问题之前,你必须清楚地理解这个问题。这是一个很长的答案:

您使用的画笔具有特定大小(64或128)。只要您的虚拟纸张(您绘制的区域)将显示大于1个屏幕像素的像素,您就会开始看到“失真”。例如,在您最喜欢的图片浏览器中,如果您打开其中一个画笔并放大图片也会失真。你不能避免这种情况,除非使用vertor-brushes(这不是这个答案的范围,而且要复杂得多)。

最快的方法是使用更多的细长刷子,但是如果你想要缩放它就像软糖一样,纹理看起来也会变形。

您还可以使用glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_ MAG _FILTER,GL_LINEAR)添加放大滤镜; 。您在样本中使用了MIN,添加此项将使纹理平滑