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 id20613
push usermlamouri@mozilla.com
push dateMon, 27 Jun 2011 09:03:51 +0000
treeherdermozilla-central@aee9017b0b4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersderf
bugs656185
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,