Bug 656185 - Part 2 - Handle odd crop offsets correctly with OpenGl. r=derf
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 27 Jun 2011 14:32:16 +1200
changeset 71817 f67ea1c7da3696b66a8f94ef0302cfc458a7b4d2
parent 71816 f18032387a590fb2b54a449fd70566996930208e
child 71818 5841deff345436bf66f81c849835348985b0ae35
push idunknown
push userunknown
push dateunknown
reviewersderf
bugs656185
milestone7.0a1
Bug 656185 - Part 2 - Handle odd crop offsets correctly with OpenGl. r=derf
gfx/layers/ImageLayers.h
gfx/layers/Layers.cpp
gfx/layers/basic/BasicImages.cpp
gfx/layers/basic/BasicLayers.cpp
gfx/layers/ipc/PLayers.ipdl
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/thebes/GLContext.cpp
gfx/ycbcr/README
gfx/ycbcr/TypeFromSize.patch
gfx/ycbcr/update.sh
gfx/ycbcr/yuv_convert.cpp
gfx/ycbcr/yuv_convert.h
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -402,16 +402,22 @@ public:
     PRUint8* mCrChannel;
     PRInt32 mCbCrStride;
     gfxIntSize mCbCrSize;
     // Picture region
     PRUint32 mPicX;
     PRUint32 mPicY;
     gfxIntSize mPicSize;
     StereoMode mStereoMode;
+
+    nsIntRect GetPictureRect() const {
+      return nsIntRect(mPicX, mPicY,
+                       mPicSize.width,
+                       mPicSize.height);
+    }
   };
 
   enum {
     MAX_DIMENSION = 16384
   };
 
   /**
    * This makes a copy of the data buffers.
@@ -428,16 +434,30 @@ public:
    */
   virtual void SetDelayedConversion(PRBool aDelayed) { }
 
   /**
    * Grab the original YUV data. This is optional.
    */
   virtual const Data* GetData() { return nsnull; }
 
+  /**
+   * Make a copy of the YCbCr data.
+   *
+   * @param aDest           Data object to store the plane data in.
+   * @param aDestSize       Size of the Y plane that was copied.
+   * @param aDestBufferSize Number of bytes allocated for storage.
+   * @param aData           Input image data.
+   * @return                Raw data pointer for the planes or nsnull on failure.
+   */
+  PRUint8 *CopyData(Data& aDest, gfxIntSize& aDestSize,
+                    PRUint32& aDestBufferSize, const Data& aData);
+
+  virtual PRUint8* AllocateBuffer(PRUint32 aSize) { return new (mozilla::fallible_t()) PRUint8[aSize]; };
+
 protected:
   PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {}
 };
 
 /**
  * Currently, the data in a CairoImage surface is treated as being in the
  * device output color space.
  */
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -476,16 +476,59 @@ ContainerLayer::DidRemoveChild(Layer* aL
 void
 ContainerLayer::DidInsertChild(Layer* aLayer)
 {
   if (aLayer->GetType() == TYPE_READBACK) {
     mMayHaveReadbackChild = PR_TRUE;
   }
 }
 
+PRUint8*
+PlanarYCbCrImage::CopyData(Data& aDest, gfxIntSize& aDestSize,
+                           PRUint32& aDestBufferSize, const Data& aData)
+{
+  aDest = aData;
+
+  /* We always have a multiple of 16 width so we can force the stride */
+  aDest.mYStride = aDest.mYSize.width;
+  aDest.mCbCrStride = aDest.mCbCrSize.width;
+
+  // update buffer size
+  aDestBufferSize = aDest.mCbCrStride * aDest.mCbCrSize.height * 2 +
+                    aDest.mYStride * aDest.mYSize.height;
+
+  // get new buffer
+  PRUint8* buffer = AllocateBuffer(aDestBufferSize); 
+  if (!buffer)
+    return nsnull;
+
+  aDest.mYChannel = buffer;
+  aDest.mCbChannel = aDest.mYChannel + aDest.mYStride * aDest.mYSize.height;
+  aDest.mCrChannel = aDest.mCbChannel + aDest.mCbCrStride * aDest.mCbCrSize.height;
+
+  for (int i = 0; i < aDest.mYSize.height; i++) {
+    memcpy(aDest.mYChannel + i * aDest.mYStride,
+           aData.mYChannel + i * aData.mYStride,
+           aDest.mYStride);
+  }
+  for (int i = 0; i < aDest.mCbCrSize.height; i++) {
+    memcpy(aDest.mCbChannel + i * aDest.mCbCrStride,
+           aData.mCbChannel + i * aData.mCbCrStride,
+           aDest.mCbCrStride);
+    memcpy(aDest.mCrChannel + i * aDest.mCbCrStride,
+           aData.mCrChannel + i * aData.mCbCrStride,
+           aDest.mCbCrStride);
+  }
+
+  aDestSize = aData.mPicSize;
+  return buffer;
+}
+                         
+
+
 #ifdef MOZ_LAYERS_HAVE_LOG
 
 static nsACString& PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer);
 
 void
 Layer::Dump(FILE* aFile, const char* aPrefix)
 {
   DumpSelf(aFile, aPrefix);
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -144,88 +144,26 @@ void
 BasicPlanarYCbCrImage::SetData(const Data& aData)
 {
   // Do some sanity checks to prevent integer overflow
   if (aData.mYSize.width > 16384 || aData.mYSize.height > 16384) {
     NS_ERROR("Illegal width or height");
     return;
   }
   
-  gfx::YUVType type = gfx::YV12;
-  int width_shift = 0;
-  int height_shift = 0;
-  if (aData.mYSize.width == aData.mCbCrSize.width &&
-      aData.mYSize.height == aData.mCbCrSize.height) {
-    type = gfx::YV24;
-    width_shift = 0;
-    height_shift = 0;
-  }
-  else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
-           aData.mYSize.height == aData.mCbCrSize.height) {
-    type = gfx::YV16;
-    width_shift = 1;
-    height_shift = 0;
-  }
-  else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
-           aData.mYSize.height / 2 == aData.mCbCrSize.height ) {
-    type = gfx::YV12;
-    width_shift = 1;
-    height_shift = 1;
-  }
-  else {
-    NS_ERROR("YCbCr format not supported");
-  }
-
   if (mDelayedConversion) {
-
-    mData = aData;
-    mData.mCbCrStride = mData.mCbCrSize.width = aData.mPicSize.width >> width_shift;
-    // Round up the values for width and height to make sure we sample enough data
-    // for the last pixel - See bug 590735
-    if (width_shift && (aData.mPicSize.width & 1)) {
-      mData.mCbCrStride++;
-      mData.mCbCrSize.width++;
-    }
-    mData.mCbCrSize.height = aData.mPicSize.height >> height_shift;
-    if (height_shift && (aData.mPicSize.height & 1)) {
-        mData.mCbCrSize.height++;
-    }
-    mData.mYSize = aData.mPicSize;
-    mData.mYStride = mData.mYSize.width;
-    mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
-                  mData.mYStride * mData.mYSize.height;
-    mBuffer = new PRUint8[mBufferSize];
-    
-    mData.mYChannel = mBuffer;
-    mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
-    mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
-    int cbcr_x = aData.mPicX >> width_shift;
-    int cbcr_y = aData.mPicY >> height_shift;
-
-    for (int i = 0; i < mData.mYSize.height; i++) {
-      memcpy(mData.mYChannel + i * mData.mYStride,
-             aData.mYChannel + ((aData.mPicY + i) * aData.mYStride) + aData.mPicX,
-             mData.mYStride);
-    }
-    for (int i = 0; i < mData.mCbCrSize.height; i++) {
-      memcpy(mData.mCbChannel + i * mData.mCbCrStride,
-             aData.mCbChannel + ((cbcr_y + i) * aData.mCbCrStride) + cbcr_x,
-             mData.mCbCrStride);
-    }
-    for (int i = 0; i < mData.mCbCrSize.height; i++) {
-      memcpy(mData.mCrChannel + i * mData.mCbCrStride,
-             aData.mCrChannel + ((cbcr_y + i) * aData.mCbCrStride) + cbcr_x,
-             mData.mCbCrStride);
-    }
-
-    // Fix picture rect to be correct
-    mData.mPicX = mData.mPicY = 0;
-    mSize = aData.mPicSize;
+    mBuffer = CopyData(mData, mSize, mBufferSize, aData);
     return;
   }
+  
+  gfx::YUVType type = 
+    gfx::TypeFromSize(aData.mYSize.width,
+                      aData.mYSize.height,
+                      aData.mCbCrSize.width,
+                      aData.mCbCrSize.height);
 
   gfxASurface::gfxImageFormat format = GetOffscreenFormat();
 
   // 'prescale' is true if the scaling is to be done as part of the
   // YCbCr to RGB conversion rather than on the RGB data when rendered.
   PRBool prescale = mScaleHint.width > 0 && mScaleHint.height > 0 &&
                     mScaleHint != aData.mPicSize;
   if (format == gfxASurface::ImageFormatRGB16_565) {
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -2218,37 +2218,43 @@ BasicShadowableImageLayer::Paint(gfxCont
       if (!BasicManager()->AllocDoubleBuffer(
             mCbCrSize,
             gfxASurface::CONTENT_ALPHA,
             getter_AddRefs(tmpVSurface), getter_AddRefs(mBackBufferV)))
         NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
 
       YUVImage yuv(tmpYSurface->GetShmem(),
                    tmpUSurface->GetShmem(),
-                   tmpVSurface->GetShmem());
+                   tmpVSurface->GetShmem(),
+                   data->GetPictureRect());
 
       BasicManager()->CreatedImageBuffer(BasicManager()->Hold(this),
                                          nsIntSize(mSize.width, mSize.height),
                                          yuv);
 
     }
       
-    memcpy(mBackBufferY->Data(), 
-           data->mYChannel, 
-           data->mYStride * mSize.height);
-    memcpy(mBackBufferU->Data(), 
-           data->mCbChannel, 
-           data->mCbCrStride * mCbCrSize.height);
-    memcpy(mBackBufferV->Data(), 
-           data->mCrChannel, 
-           data->mCbCrStride * mCbCrSize.height);
+    for (int i = 0; i < data->mYSize.height; i++) {
+      memcpy(mBackBufferY->Data() + i * mBackBufferY->Stride(),
+             data->mYChannel + i * data->mYStride,
+             data->mYSize.width);
+    }
+    for (int i = 0; i < data->mCbCrSize.height; i++) {
+      memcpy(mBackBufferU->Data() + i * mBackBufferU->Stride(),
+             data->mCbChannel + i * data->mCbCrStride,
+             data->mCbCrSize.width);
+      memcpy(mBackBufferV->Data() + i * mBackBufferV->Stride(),
+             data->mCrChannel + i * data->mCbCrStride,
+             data->mCbCrSize.width);
+    }
       
     YUVImage yuv(mBackBufferY->GetShmem(),
                  mBackBufferU->GetShmem(),
-                 mBackBufferV->GetShmem());
+                 mBackBufferV->GetShmem(),
+                 data->GetPictureRect());
   
     BasicManager()->PaintedImage(BasicManager()->Hold(this),
                                  yuv);
 
     return;
   }
 
   gfxIntSize oldSize = mSize;
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -82,16 +82,17 @@ union SurfaceDescriptor {
   Shmem;
   SurfaceDescriptorX11;
 };
 
 struct YUVImage {
   Shmem Ydata;
   Shmem Udata;
   Shmem Vdata;
+  nsIntRect picture;
 };
 
 union SharedImage {
   SurfaceDescriptor;
   YUVImage;
 };
 
 struct ThebesBuffer {
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -271,33 +271,39 @@ ImageContainerOGL::GetCurrentAsSurface(g
   if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) {
     PlanarYCbCrImageOGL *yuvImage =
       static_cast<PlanarYCbCrImageOGL*>(mActiveImage.get());
     if (!yuvImage->HasData()) {
       *aSize = gfxIntSize(0, 0);
       return nsnull;
     }
 
-    size = yuvImage->mSize;
+    size = yuvImage->mData.mPicSize;
 
     nsRefPtr<gfxImageSurface> imageSurface =
       new gfxImageSurface(size, gfxASurface::ImageFormatRGB24);
+  
+    gfx::YUVType type = 
+      gfx::TypeFromSize(yuvImage->mData.mYSize.width,
+                        yuvImage->mData.mYSize.height,
+                        yuvImage->mData.mCbCrSize.width,
+                        yuvImage->mData.mCbCrSize.height);
 
     gfx::ConvertYCbCrToRGB32(yuvImage->mData.mYChannel,
                              yuvImage->mData.mCbChannel,
                              yuvImage->mData.mCrChannel,
                              imageSurface->Data(),
-                             0,
-                             0,
+                             yuvImage->mData.mPicX,
+                             yuvImage->mData.mPicY,
                              size.width,
                              size.height,
                              yuvImage->mData.mYStride,
                              yuvImage->mData.mCbCrStride,
                              imageSurface->Stride(),
-                             yuvImage->mType);
+                             type);
 
     *aSize = size;
     return imageSurface.forget().get();
   }
 
   if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) {
     CairoImageOGL *cairoImage =
       static_cast<CairoImageOGL*>(mActiveImage.get());
@@ -427,17 +433,20 @@ ImageLayerOGL::RenderLayer(int,
     program->SetLayerQuadRect(nsIntRect(0, 0,
                                         yuvImage->mSize.width,
                                         yuvImage->mSize.height));
     program->SetLayerTransform(GetEffectiveTransform());
     program->SetLayerOpacity(GetEffectiveOpacity());
     program->SetRenderOffset(aOffset);
     program->SetYCbCrTextureUnits(0, 1, 2);
 
-    mOGLManager->BindAndDrawQuad(program);
+    mOGLManager->BindAndDrawQuadWithTextureRect(program,
+                                                yuvImage->mData.GetPictureRect(),
+                                                nsIntSize(yuvImage->mData.mYSize.width,
+                                                          yuvImage->mData.mYSize.height));
 
     // We shouldn't need to do this, but do it anyway just in case
     // someone else forgets.
     gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
   } else if (image->GetFormat() == Image::CAIRO_SURFACE) {
     CairoImageOGL *cairoImage =
       static_cast<CairoImageOGL*>(image.get());
 
@@ -657,94 +666,21 @@ PlanarYCbCrImageOGL::~PlanarYCbCrImageOG
     mRecycleBin->RecycleTexture(&mTextures[1], RecycleBin::TEXTURE_C, mData.mCbCrSize);
     mRecycleBin->RecycleTexture(&mTextures[2], RecycleBin::TEXTURE_C, mData.mCbCrSize);
   }
 }
 
 void
 PlanarYCbCrImageOGL::SetData(const PlanarYCbCrImage::Data &aData)
 {
-  // For now, we copy the data
-  int width_shift = 0;
-  int height_shift = 0;
-  if (aData.mYSize.width == aData.mCbCrSize.width &&
-      aData.mYSize.height == aData.mCbCrSize.height) {
-     // YV24 format
-     width_shift = 0;
-     height_shift = 0;
-     mType = gfx::YV24;
-  } else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
-             aData.mYSize.height == aData.mCbCrSize.height) {
-    // YV16 format
-    width_shift = 1;
-    height_shift = 0;
-    mType = gfx::YV16;
-  } else if (aData.mYSize.width / 2 == aData.mCbCrSize.width &&
-             aData.mYSize.height / 2 == aData.mCbCrSize.height ) {
-      // YV12 format
-    width_shift = 1;
-    height_shift = 1;
-    mType = gfx::YV12;
-  } else {
-    NS_ERROR("YCbCr format not supported");
-  }
-  
-  mData = aData;
-  mData.mCbCrStride = mData.mCbCrSize.width = aData.mPicSize.width >> width_shift;
-  // Round up the values for width and height to make sure we sample enough data
-  // for the last pixel - See bug 590735
-  if (width_shift && (aData.mPicSize.width & 1)) {
-    mData.mCbCrStride++;
-    mData.mCbCrSize.width++;
-  }
-  mData.mCbCrSize.height = aData.mPicSize.height >> height_shift;
-  if (height_shift && (aData.mPicSize.height & 1)) {
-      mData.mCbCrSize.height++;
-  }
-  mData.mYSize = aData.mPicSize;
-  mData.mYStride = mData.mYSize.width;
-
   // Recycle the previous image main-memory buffer now that we're about to get a new buffer
   if (mBuffer)
     mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
-
-  // update buffer size
-  mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
-                mData.mYStride * mData.mYSize.height;
-
-  // get new buffer
-  mBuffer = mRecycleBin->GetBuffer(mBufferSize);
-  if (!mBuffer)
-    return;
-
-  mData.mYChannel = mBuffer;
-  mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
-  mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
-  int cbcr_x = aData.mPicX >> width_shift;
-  int cbcr_y = aData.mPicY >> height_shift;
-
-  for (int i = 0; i < mData.mYSize.height; i++) {
-    memcpy(mData.mYChannel + i * mData.mYStride,
-           aData.mYChannel + ((aData.mPicY + i) * aData.mYStride) + aData.mPicX,
-           mData.mYStride);
-  }
-  for (int i = 0; i < mData.mCbCrSize.height; i++) {
-    memcpy(mData.mCbChannel + i * mData.mCbCrStride,
-           aData.mCbChannel + ((cbcr_y + i) * aData.mCbCrStride) + cbcr_x,
-           mData.mCbCrStride);
-  }
-  for (int i = 0; i < mData.mCbCrSize.height; i++) {
-    memcpy(mData.mCrChannel + i * mData.mCbCrStride,
-           aData.mCrChannel + ((cbcr_y + i) * aData.mCbCrStride) + cbcr_x,
-           mData.mCbCrStride);
-  }
-
-  // Fix picture rect to be correct
-  mData.mPicX = mData.mPicY = 0;
-  mSize = aData.mPicSize;
+  
+  mBuffer = CopyData(mData, mSize, mBufferSize, aData);
 
   mHasData = PR_TRUE;
 }
 
 void
 PlanarYCbCrImageOGL::AllocateTextures(mozilla::gl::GLContext *gl)
 {
   gl->MakeCurrent();
@@ -760,70 +696,38 @@ PlanarYCbCrImageOGL::AllocateTextures(mo
 }
 
 static void
 UploadYUVToTexture(GLContext* gl, const PlanarYCbCrImage::Data& aData, 
                    GLTexture* aYTexture,
                    GLTexture* aUTexture,
                    GLTexture* aVTexture)
 {
-  GLint alignment;
-
-  if (!((ptrdiff_t)aData.mYStride & 0x7) && !((ptrdiff_t)aData.mYChannel & 0x7)) {
-    alignment = 8;
-  } else if (!((ptrdiff_t)aData.mYStride & 0x3)) {
-    alignment = 4;
-  } else if (!((ptrdiff_t)aData.mYStride & 0x1)) {
-    alignment = 2;
-  } else {
-    alignment = 1;
-  }
-
-  // Set texture alignment for Y plane.
-  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, alignment);
-
-  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aYTexture->GetTextureID());
-  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                     0, 0, aData.mYSize.width, aData.mYSize.height,
-                     LOCAL_GL_LUMINANCE,
-                     LOCAL_GL_UNSIGNED_BYTE,
-                     aData.mYChannel);
+  nsIntRect size(0, 0, aData.mYSize.width, aData.mYSize.height);
+  GLuint texture = aYTexture->GetTextureID();
+  nsRefPtr<gfxASurface> surf = new gfxImageSurface(aData.mYChannel,
+                                                   aData.mYSize,
+                                                   aData.mYStride,
+                                                   gfxASurface::ImageFormatA8);
+  gl->UploadSurfaceToTexture(surf, size, texture, true);
+  
+  size = nsIntRect(0, 0, aData.mCbCrSize.width, aData.mCbCrSize.height);
+  texture = aUTexture->GetTextureID();
+  surf = new gfxImageSurface(aData.mCbChannel,
+                             aData.mCbCrSize,
+                             aData.mCbCrStride,
+                             gfxASurface::ImageFormatA8);
+  gl->UploadSurfaceToTexture(surf, size, texture, true);
 
-  if (!((ptrdiff_t)aData.mCbCrStride & 0x7) && 
-      !((ptrdiff_t)aData.mCbChannel & 0x7) &&
-      !((ptrdiff_t)aData.mCrChannel & 0x7))
-  {
-    alignment = 8;
-  } else if (!((ptrdiff_t)aData.mCbCrStride & 0x3)) {
-    alignment = 4;
-  } else if (!((ptrdiff_t)aData.mCbCrStride & 0x1)) {
-    alignment = 2;
-  } else {
-    alignment = 1;
-  }
-  
-  // Set texture alignment for Cb/Cr plane
-  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, alignment);
-
-  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aUTexture->GetTextureID());
-  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                     0, 0, aData.mCbCrSize.width, aData.mCbCrSize.height,
-                     LOCAL_GL_LUMINANCE,
-                     LOCAL_GL_UNSIGNED_BYTE,
-                     aData.mCbChannel);
-
-  gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aVTexture->GetTextureID());
-  gl->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0,
-                     0, 0, aData.mCbCrSize.width, aData.mCbCrSize.height,
-                     LOCAL_GL_LUMINANCE,
-                     LOCAL_GL_UNSIGNED_BYTE,
-                     aData.mCrChannel);
-
-  // Reset alignment to default
-  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
+  texture = aVTexture->GetTextureID();
+  surf = new gfxImageSurface(aData.mCrChannel,
+                             aData.mCbCrSize,
+                             aData.mCbCrStride,
+                             gfxASurface::ImageFormatA8);
+  gl->UploadSurfaceToTexture(surf, size, texture, true);
 }
 
 void
 PlanarYCbCrImageOGL::UpdateTextures(GLContext *gl)
 {
   if (!mBuffer || !mHasData)
     return;
 
@@ -972,16 +876,19 @@ ShadowImageLayerOGL::Swap(const SharedIm
       const YUVImage& yuv = aNewFront.get_YUVImage();
     
       nsRefPtr<gfxSharedImageSurface> surfY =
         gfxSharedImageSurface::Open(yuv.Ydata());
       nsRefPtr<gfxSharedImageSurface> surfU =
         gfxSharedImageSurface::Open(yuv.Udata());
       nsRefPtr<gfxSharedImageSurface> surfV =
         gfxSharedImageSurface::Open(yuv.Vdata());
+
+      mPictureRect = yuv.picture();
+      mSize = surfY->GetSize();
  
       PlanarYCbCrImage::Data data;
       data.mYChannel = surfY->Data();
       data.mYStride = surfY->Stride();
       data.mYSize = surfY->GetSize();
       data.mCbChannel = surfU->Data();
       data.mCrChannel = surfV->Data();
       data.mCbCrStride = surfU->Stride();
@@ -1051,21 +958,30 @@ ShadowImageLayerOGL::RenderLayer(int aPr
     gl()->fActiveTexture(LOCAL_GL_TEXTURE2);
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[2].GetTextureID());
     ApplyFilter(mFilter);
     
     YCbCrTextureLayerProgram *yuvProgram = mOGLManager->GetYCbCrLayerProgram();
 
     yuvProgram->Activate();
     yuvProgram->SetLayerQuadRect(nsIntRect(0, 0,
-                                           mSize.width,
-                                           mSize.height));
+                                           mPictureRect.width,
+                                           mPictureRect.height));
     yuvProgram->SetYCbCrTextureUnits(0, 1, 2);
 
     program = yuvProgram;
+    program->SetLayerTransform(GetEffectiveTransform());
+    program->SetLayerOpacity(GetEffectiveOpacity());
+    program->SetRenderOffset(aOffset);
+
+    mOGLManager->BindAndDrawQuadWithTextureRect(program,
+                                                mPictureRect,
+                                                nsIntSize(mSize.width, mSize.height));
+
+    return;
   }
 
   program->SetLayerTransform(GetEffectiveTransform());
   program->SetLayerOpacity(GetEffectiveOpacity());
   program->SetRenderOffset(aOffset);
 
   mOGLManager->BindAndDrawQuad(program);
 }
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -202,24 +202,27 @@ public:
 
   PRBool HasData() { return mHasData; }
   PRBool HasTextures()
   {
     return mTextures[0].IsAllocated() && mTextures[1].IsAllocated() &&
            mTextures[2].IsAllocated();
   }
 
+  PRUint8* AllocateBuffer(PRUint32 aSize) {
+    return mRecycleBin->GetBuffer(aSize);
+  }
+
   nsAutoArrayPtr<PRUint8> mBuffer;
   PRUint32 mBufferSize;
   nsRefPtr<RecycleBin> mRecycleBin;
   GLTexture mTextures[3];
   Data mData;
   gfxIntSize mSize;
   PRPackedBool mHasData;
-  gfx::YUVType mType; 
 };
 
 
 class THEBES_API CairoImageOGL : public CairoImage
 {
   typedef mozilla::gl::GLContext GLContext;
 
 public:
@@ -263,13 +266,14 @@ public:
 
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 
 private:
   nsRefPtr<TextureImage> mTexImage;
   GLTexture mYUVTexture[3];
   gfxIntSize mSize;
+  nsIntRect mPictureRect;
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_IMAGELAYEROGL_H */
--- a/gfx/thebes/GLContext.cpp
+++ b/gfx/thebes/GLContext.cpp
@@ -1377,17 +1377,18 @@ GLContext::UploadSurfaceToTexture(gfxASu
     }
 
     nsRefPtr<gfxImageSurface> imageSurface = aSurface->GetAsImageSurface();
     unsigned char* data = NULL;
 
     if (!imageSurface || 
         (imageSurface->Format() != gfxASurface::ImageFormatARGB32 &&
          imageSurface->Format() != gfxASurface::ImageFormatRGB24 &&
-         imageSurface->Format() != gfxASurface::ImageFormatRGB16_565)) {
+         imageSurface->Format() != gfxASurface::ImageFormatRGB16_565 &&
+         imageSurface->Format() != gfxASurface::ImageFormatA8)) {
         // We can't get suitable pixel data for the surface, make a copy
         nsIntRect bounds = aDstRegion.GetBounds();
         imageSurface = 
           new gfxImageSurface(gfxIntSize(bounds.width, bounds.height), 
                               gfxASurface::ImageFormatARGB32);
   
         nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
 
@@ -1425,16 +1426,22 @@ GLContext::UploadSurfaceToTexture(gfxASu
             type = LOCAL_GL_UNSIGNED_BYTE;
             shader = BGRXLayerProgramType;
             break;
         case gfxASurface::ImageFormatRGB16_565:
             format = LOCAL_GL_RGB;
             type = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
             shader = RGBALayerProgramType;
             break;
+        case gfxASurface::ImageFormatA8:
+            format = LOCAL_GL_LUMINANCE;
+            type = LOCAL_GL_UNSIGNED_BYTE;
+            // We don't have a specific luminance shader
+            shader = ShaderProgramType(0);
+            break;
         default:
             NS_ASSERTION(false, "Unhandled image surface format!");
             format = 0;
             type = 0;
             shader = ShaderProgramType(0);
     }
 
 #ifndef USE_GLES2
--- a/gfx/ycbcr/README
+++ b/gfx/ycbcr/README
@@ -18,8 +18,10 @@ convert.patch contains the following cha
   * Change Chromium code to allow a picture region.
   * The YUV conversion will convert within this picture region only.
   * Add YCbCr 4:4:4 support
   * Bug 619178 - Update CPU detection in yuv_convert to new SSE.h interface.
   * Bug 616778 - Split yuv_convert FilterRows vectorized code into separate files so it can
     be properly guarded with cpuid() calls.
 
 win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version
+
+TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes.
new file mode 100644
--- /dev/null
+++ b/gfx/ycbcr/TypeFromSize.patch
@@ -0,0 +1,58 @@
+diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp
+--- a/gfx/ycbcr/yuv_convert.cpp
++++ b/gfx/ycbcr/yuv_convert.cpp
+@@ -26,16 +26,32 @@ namespace mozilla {
+ 
+ namespace gfx {
+  
+ // 16.16 fixed point arithmetic
+ const int kFractionBits = 16;
+ const int kFractionMax = 1 << kFractionBits;
+ const int kFractionMask = ((1 << kFractionBits) - 1);
+ 
++NS_GFX_(YUVType) TypeFromSize(int ywidth, 
++                              int yheight, 
++                              int cbcrwidth, 
++                              int cbcrheight)
++{
++  if (ywidth == cbcrwidth && yheight == cbcrheight) {
++    return YV24;
++  }
++  else if (ywidth / 2 == cbcrwidth && yheight == cbcrheight) {
++    return YV16;
++  }
++  else {
++    return YV12;
++  }
++}
++
+ // Convert a frame of YUV to 32 bit ARGB.
+ NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* y_buf,
+                                   const uint8* u_buf,
+                                   const uint8* v_buf,
+                                   uint8* rgb_buf,
+                                   int pic_x,
+                                   int pic_y,
+                                   int pic_width,
+diff --git a/gfx/ycbcr/yuv_convert.h b/gfx/ycbcr/yuv_convert.h
+--- a/gfx/ycbcr/yuv_convert.h
++++ b/gfx/ycbcr/yuv_convert.h
+@@ -36,16 +36,18 @@ enum Rotate {
+ // Filter affects how scaling looks.
+ enum ScaleFilter {
+   FILTER_NONE = 0,        // No filter (point sampled).
+   FILTER_BILINEAR_H = 1,  // Bilinear horizontal filter.
+   FILTER_BILINEAR_V = 2,  // Bilinear vertical filter.
+   FILTER_BILINEAR = 3     // Bilinear filter.
+ };
+ 
++NS_GFX_(YUVType) TypeFromSize(int ywidth, int yheight, int cbcrwidth, int cbcrheight);
++
+ // Convert a frame of YUV to 32 bit ARGB.
+ // Pass in YV16/YV12 depending on source format
+ NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* yplane,
+                                   const uint8* uplane,
+                                   const uint8* vplane,
+                                   uint8* rgbframe,
+                                   int pic_x,
+                                   int pic_y,
--- a/gfx/ycbcr/update.sh
+++ b/gfx/ycbcr/update.sh
@@ -3,8 +3,9 @@ cp $1/media/base/yuv_convert.h .
 cp $1/media/base/yuv_convert.cc yuv_convert.cpp
 cp $1/media/base/yuv_row.h .
 cp $1/media/base/yuv_row_table.cc yuv_row_table.cpp
 cp $1/media/base/yuv_row_posix.cc yuv_row_posix.cpp
 cp $1/media/base/yuv_row_win.cc yuv_row_win.cpp
 cp $1/media/base/yuv_row_posix.cc yuv_row_c.cpp
 patch -p3 <convert.patch
 patch -p3 <win64.patch
+patch -p3 <TypeFromSize.patch
--- a/gfx/ycbcr/yuv_convert.cpp
+++ b/gfx/ycbcr/yuv_convert.cpp
@@ -26,16 +26,32 @@ namespace mozilla {
 
 namespace gfx {
  
 // 16.16 fixed point arithmetic
 const int kFractionBits = 16;
 const int kFractionMax = 1 << kFractionBits;
 const int kFractionMask = ((1 << kFractionBits) - 1);
 
+NS_GFX_(YUVType) TypeFromSize(int ywidth, 
+                              int yheight, 
+                              int cbcrwidth, 
+                              int cbcrheight)
+{
+  if (ywidth == cbcrwidth && yheight == cbcrheight) {
+    return YV24;
+  }
+  else if (ywidth / 2 == cbcrwidth && yheight == cbcrheight) {
+    return YV16;
+  }
+  else {
+    return YV12;
+  }
+}
+
 // Convert a frame of YUV to 32 bit ARGB.
 NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* y_buf,
                                   const uint8* u_buf,
                                   const uint8* v_buf,
                                   uint8* rgb_buf,
                                   int pic_x,
                                   int pic_y,
                                   int pic_width,
--- a/gfx/ycbcr/yuv_convert.h
+++ b/gfx/ycbcr/yuv_convert.h
@@ -36,16 +36,18 @@ enum Rotate {
 // Filter affects how scaling looks.
 enum ScaleFilter {
   FILTER_NONE = 0,        // No filter (point sampled).
   FILTER_BILINEAR_H = 1,  // Bilinear horizontal filter.
   FILTER_BILINEAR_V = 2,  // Bilinear vertical filter.
   FILTER_BILINEAR = 3     // Bilinear filter.
 };
 
+NS_GFX_(YUVType) TypeFromSize(int ywidth, int yheight, int cbcrwidth, int cbcrheight);
+
 // Convert a frame of YUV to 32 bit ARGB.
 // Pass in YV16/YV12 depending on source format
 NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* yplane,
                                   const uint8* uplane,
                                   const uint8* vplane,
                                   uint8* rgbframe,
                                   int pic_x,
                                   int pic_y,