Bug 621778 - Allow TextureImage to upload regions instead of only rectangles. r=joe a=blocking2.0
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 18 Jan 2011 14:32:40 +1300
changeset 60728 31b46f48f71471e83cffae245a98c590eaa844fe
parent 60727 86c446a17a798ff12c3d9cd8af0064dc60c34959
child 60729 6e09e2107ec541005e7f6f8982ff7bb9a6f32305
push id18095
push usermwoodrow@mozilla.com
push dateTue, 18 Jan 2011 01:40:45 +0000
treeherdermozilla-central@6e09e2107ec5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe, blocking2
bugs621778
milestone2.0b10pre
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 621778 - Allow TextureImage to upload regions instead of only rectangles. r=joe a=blocking2.0
gfx/layers/opengl/CanvasLayerOGL.cpp
gfx/thebes/GLContext.cpp
gfx/thebes/GLContext.h
gfx/thebes/GLContextProviderEGL.cpp
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -174,18 +174,18 @@ CanvasLayerOGL::Updated(const nsIntRect&
                                                    updatedAreaImageSurface);
       updatedAreaSurface = updatedAreaImageSurface;
     }
 
     mLayerProgram =
       gl()->UploadSurfaceToTexture(updatedAreaSurface,
                                    mUpdatedRect,
                                    mTexture,
-                                          false,
-                                          mUpdatedRect.TopLeft());
+                                   false,
+                                   mUpdatedRect.TopLeft());
   }
 
   // sanity
   NS_ASSERTION(mBounds.Contains(mUpdatedRect),
                "CanvasLayer: Updated rect bigger than bounds!");
 }
 
 void
--- a/gfx/thebes/GLContext.cpp
+++ b/gfx/thebes/GLContext.cpp
@@ -561,37 +561,36 @@ BasicTextureImage::BeginUpdate(nsIntRegi
     ImageFormat format =
         (GetContentType() == gfxASurface::CONTENT_COLOR) ?
         gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
     if (!mTextureInited)
     {
         // if the texture hasn't been initialized yet, or something important
         // changed, we need to recreate our backing surface and force the
         // client to paint everything
-        mUpdateRect = nsIntRect(nsIntPoint(0, 0), mSize);
+        mUpdateRegion = nsIntRect(nsIntPoint(0, 0), mSize);
     } else {
-        mUpdateRect = aRegion.GetBounds();
+        mUpdateRegion = aRegion;
     }
 
-    // the basic impl can only upload updates to rectangles
-    aRegion = nsIntRegion(mUpdateRect);
+    aRegion = mUpdateRegion;
 
-    nsIntSize rgnSize = mUpdateRect.Size();
-    if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(mUpdateRect)) {
+    nsIntRect rgnSize = mUpdateRegion.GetBounds();
+    if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(rgnSize)) {
         NS_ERROR("update outside of image");
         return NULL;
     }
 
     nsRefPtr<gfxASurface> updateSurface = 
         GetSurfaceForUpdate(gfxIntSize(rgnSize.width, rgnSize.height), format);
 
     if (!updateSurface)
         return NULL;
 
-    updateSurface->SetDeviceOffset(gfxPoint(-mUpdateRect.x, -mUpdateRect.y));
+    updateSurface->SetDeviceOffset(gfxPoint(-rgnSize.x, -rgnSize.y));
 
     mUpdateContext = new gfxContext(updateSurface);
    
     // Clear the returned surface because it might have been re-used.
     if (format == gfxASurface::ImageFormatARGB32) {
         mUpdateContext->SetOperator(gfxContext::OPERATOR_CLEAR);
         mUpdateContext->Paint();
         mUpdateContext->SetOperator(gfxContext::OPERATOR_OVER);
@@ -608,25 +607,22 @@ BasicTextureImage::EndUpdate()
     nsRefPtr<gfxASurface> originalSurface = mUpdateContext->OriginalSurface();
 
     // Undo the device offset that BeginUpdate set; doesn't much matter for us here,
     // but important if we ever do anything directly with the surface.
     originalSurface->SetDeviceOffset(gfxPoint(0, 0));
 
     bool relative = FinishedSurfaceUpdate();
 
-    // The rect to upload from the surface is mUpdateRect sized and located at mUpdateOffset.
-    nsIntRect surfaceRect(mUpdateOffset.x, mUpdateOffset.y, mUpdateRect.width, mUpdateRect.height);
-
     mShaderType =
         mGLContext->UploadSurfaceToTexture(originalSurface,
-                                           surfaceRect,
+                                           mUpdateRegion,
                                            mTexture,
                                            !mTextureInited,
-                                           mUpdateRect.TopLeft(),
+                                           mUpdateOffset,
                                            relative);
     FinishedSurfaceUpload();
 
     mUpdateContext = nsnull;
     mTextureInited = PR_TRUE;
 
     return PR_TRUE;         // mTexture is bound
 }
@@ -648,23 +644,27 @@ void
 BasicTextureImage::FinishedSurfaceUpload()
 {
 }
 
 bool 
 BasicTextureImage::DirectUpdate(gfxASurface *aSurf, const nsIntRegion& aRegion)
 {
     nsIntRect bounds = aRegion.GetBounds();
+    nsIntRegion region;
     if (!mTextureInited) {
         bounds = nsIntRect(0, 0, mSize.width, mSize.height);
+        region = nsIntRegion(bounds);
+    } else {
+        region = aRegion;
     }
 
     mShaderType =
         mGLContext->UploadSurfaceToTexture(aSurf,
-                                           bounds,
+                                           region,
                                            mTexture,
                                            !mTextureInited,
                                            bounds.TopLeft(),
                                            PR_FALSE);
     mTextureInited = PR_TRUE;
     return true;
 }
 
@@ -1256,23 +1256,30 @@ GLContext::BlitTextureImage(TextureImage
     }
 
     fEnable(LOCAL_GL_SCISSOR_TEST);
     fEnable(LOCAL_GL_BLEND);
 
     PopViewportRect();
 }
 
+static unsigned int 
+DataOffset(gfxImageSurface *aSurf, const nsIntPoint &aPoint)
+{
+  unsigned int data = aPoint.y * aSurf->Stride();
+  data += aPoint.x * gfxASurface::BytePerPixelFromFormat(aSurf->Format());
+  return data;
+}
 
 ShaderProgramType 
 GLContext::UploadSurfaceToTexture(gfxASurface *aSurface, 
-                                  const nsIntRect& aSrcRect,
+                                  const nsIntRegion& aDstRegion,
                                   GLuint& aTexture,
                                   bool aOverwrite,
-                                  const nsIntPoint& aDstPoint,
+                                  const nsIntPoint& aSrcPoint,
                                   bool aPixelBuffer)
 {
     bool textureInited = aOverwrite ? false : true;
     MakeCurrent();
     fActiveTexture(LOCAL_GL_TEXTURE0);
   
     if (!aTexture) {
         fGenTextures(1, &aTexture);
@@ -1289,44 +1296,51 @@ GLContext::UploadSurfaceToTexture(gfxASu
         fTexParameteri(LOCAL_GL_TEXTURE_2D, 
                        LOCAL_GL_TEXTURE_WRAP_T, 
                        LOCAL_GL_CLAMP_TO_EDGE);
         textureInited = false;
     } else {
         fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture);
     }
 
+    nsIntRegion paintRegion;
+    if (!textureInited) {
+        paintRegion = nsIntRegion(aDstRegion.GetBounds());
+    } else {
+        paintRegion = aDstRegion;
+    }
+
     nsRefPtr<gfxImageSurface> imageSurface = aSurface->GetAsImageSurface();
     unsigned char* data = NULL;
 
     if (!imageSurface || 
         (imageSurface->Format() != gfxASurface::ImageFormatARGB32 &&
          imageSurface->Format() != gfxASurface::ImageFormatRGB24 &&
          imageSurface->Format() != gfxASurface::ImageFormatRGB16_565)) {
         // We can't get suitable pixel data for the surface, make a copy
+        nsIntRect bounds = aDstRegion.GetBounds();
         imageSurface = 
-          new gfxImageSurface(gfxIntSize(aSrcRect.width, aSrcRect.height), 
+          new gfxImageSurface(gfxIntSize(bounds.width, bounds.height), 
                               gfxASurface::ImageFormatARGB32);
   
         nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
 
-        context->Translate(-gfxPoint(aSrcRect.x, aSrcRect.y));
+        context->Translate(-gfxPoint(aSrcPoint.x, aSrcPoint.y));
         context->SetSource(aSurface);
         context->Paint();
         data = imageSurface->Data();
         NS_ASSERTION(!aPixelBuffer,
                      "Must be using an image compatible surface with pixel buffers!");
     } else {
         // If a pixel buffer is bound the data pointer parameter is relative
         // to the start of the data block.
         if (!aPixelBuffer) {
               data = imageSurface->Data();
         }
-        data += aSrcRect.y * imageSurface->Stride();
-        data += aSrcRect.x * 4;
+        data += DataOffset(imageSurface, aSrcPoint);
     }
 
     GLenum format;
     GLenum internalformat;
     GLenum type;
     PRInt32 pixelSize = gfxASurface::BytePerPixelFromFormat(imageSurface->Format());
     ShaderProgramType shader;
 
@@ -1357,71 +1371,87 @@ GLContext::UploadSurfaceToTexture(gfxASu
 
 #ifndef USE_GLES2
     fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 
                  imageSurface->Stride() / pixelSize);
 
     internalformat = LOCAL_GL_RGBA;
 #else
     internalformat = format;
+#endif
 
-    if (imageSurface->Stride() != aSrcRect.width * pixelSize) {
-        // Not using the whole row of texture data and GLES doesn't 
-        // support GL_UNPACK_ROW_LENGTH. We need to upload each row
-        // separately.
-        if (!textureInited) {
+    nsIntRegionRectIterator iter(paintRegion);
+    const nsIntRect *iterRect;
+
+    // Top left point of the region's bounding rectangle.
+    nsIntPoint topLeft = paintRegion.GetBounds().TopLeft();
+
+    while ((iterRect = iter.Next())) {
+        // The inital data pointer is at the top left point of the region's
+        // bounding rectangle. We need to find the offset of this rect
+        // within the region and adjust the data pointer accordingly.
+        unsigned char *rectData = 
+            data + DataOffset(imageSurface, iterRect->TopLeft() - topLeft);
+
+#ifdef USE_GLES2
+        if (imageSurface->Stride() != iterRect->width * pixelSize) {
+            // Not using the whole row of texture data and GLES doesn't 
+            // support GL_UNPACK_ROW_LENGTH. We need to upload each row
+            // separately.
+            if (!textureInited) {
+                fTexImage2D(LOCAL_GL_TEXTURE_2D,
+                            0,
+                            internalformat,
+                            iterRect->width,
+                            iterRect->height,
+                            0,
+                            format,
+                            type,
+                            NULL);
+            }
+
+            for (int h = 0; h < iterRect->height; h++) {
+                fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
+                               0,
+                               iterRect->x,
+                               iterRect->y+h,
+                               iterRect->width,
+                               1,
+                               format,
+                               type,
+                               rectData);
+                rectData += imageSurface->Stride();
+            }
+
+            continue;
+        }
+#endif
+
+        if (textureInited) {
+            fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
+                           0,
+                           iterRect->x,
+                           iterRect->y,
+                           iterRect->width,
+                           iterRect->height,
+                           format,
+                           type,
+                           rectData);
+        } else {
             fTexImage2D(LOCAL_GL_TEXTURE_2D,
                         0,
                         internalformat,
-                        aSrcRect.width,
-                        aSrcRect.height,
+                        iterRect->width,
+                        iterRect->height,
                         0,
                         format,
                         type,
-                        NULL);
-        }
-
-        for (int h = 0; h < aSrcRect.height; h++) {
-            fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
-                           0,
-                           aDstPoint.x,
-                           aDstPoint.y+h,
-                           aSrcRect.width,
-                           1,
-                           format,
-                           type,
-                           data);
-            data += imageSurface->Stride();
+                        rectData);
         }
 
-        return shader;
-  }
-#endif
-
-
-    if (textureInited) {
-        fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
-                       0,
-                       aDstPoint.x,
-                       aDstPoint.y,
-                       aSrcRect.width,
-                       aSrcRect.height,
-                       format,
-                       type,
-                       data);
-    } else {
-        fTexImage2D(LOCAL_GL_TEXTURE_2D,
-                    0,
-                    internalformat,
-                    aSrcRect.width,
-                    aSrcRect.height,
-                    0,
-                    format,
-                    type,
-                    data);
     }
 
 #ifndef USE_GLES2
     fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
 #endif
 
     return shader;
 }
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -314,17 +314,17 @@ public:
     virtual PRBool InUpdate() const { return !!mUpdateContext; }
 
     virtual void Resize(const nsIntSize& aSize);
 protected:
 
     PRBool mTextureInited;
     GLContext* mGLContext;
     nsRefPtr<gfxContext> mUpdateContext;
-    nsIntRect mUpdateRect;
+    nsIntRegion mUpdateRegion;
 
     // The offset into the update surface at which the update rect is located.
     nsIntPoint mUpdateOffset;
 };
 
 struct THEBES_API ContextFormat
 {
     static const ContextFormat BasicRGBA32Format;
@@ -754,30 +754,31 @@ public:
      * and that its size is equal to or greater than aSrcRect + aDstPoint.
      * You can alternatively set the overwrite flag to true and have a new
      * texture memory block allocated.
      *
      * The aDstPoint parameter is ignored if no texture was provided
      * or aOverwrite is true.
      *
      * \param aSurface Surface to upload. 
-     * \param aSrcRect Region of aSurface to upload.
+     * \param aDstRegion Region of texture to upload to.
      * \param aTexture Texture to use, or 0 to have one created for you.
      * \param aOverwrite Over an existing texture with a new one.
-     * \param aDstPoint Offset into existing texture to upload contents.
+     * \param aSrcPoint Offset into aSrc where the region's bound's 
+     *  TopLeft() sits.
      * \param aPixelBuffer Pass true to upload texture data with an
      *  offset from the base data (generally for pixel buffer objects), 
      *  otherwise textures are upload with an absolute pointer to the data.
      * \return Shader program needed to render this texture.
      */
     ShaderProgramType UploadSurfaceToTexture(gfxASurface *aSurface, 
-                                             const nsIntRect& aSrcRect,
+                                             const nsIntRegion& aDstRegion,
                                              GLuint& aTexture,
                                              bool aOverwrite = false,
-                                             const nsIntPoint& aDstPoint = nsIntPoint(0, 0),
+                                             const nsIntPoint& aSrcPoint = nsIntPoint(0, 0),
                                              bool aPixelBuffer = PR_FALSE);
 
 #ifndef MOZ_ENABLE_LIBXUL
     virtual ShaderProgramType UploadSurfaceToTextureExternal(gfxASurface *aSurface, 
                                                              const nsIntRect& aSrcRect,
                                                              GLuint& aTexture,
                                                              bool aOverwrite = false,
                                                              const nsIntPoint& aDstPoint = nsIntPoint(0, 0),
--- a/gfx/thebes/GLContextProviderEGL.cpp
+++ b/gfx/thebes/GLContextProviderEGL.cpp
@@ -1203,44 +1203,43 @@ public:
 
         mUpdateContext = nsnull;
         return PR_TRUE;         // mTexture is bound
     }
 
     virtual bool DirectUpdate(gfxASurface *aSurf, const nsIntRegion& aRegion)
     {
         nsIntRect bounds = aRegion.GetBounds();
-        nsIntPoint dest = bounds.TopLeft();
-
-        // Bounds is the destination rect, it will be at 0,0 on the source
-        bounds.x = 0;
-        bounds.y = 0;
   
+        nsIntRegion region;
         if (!mCreated) {
             bounds = nsIntRect(0, 0, mSize.width, mSize.height);
+            region = nsIntRegion(bounds);
+        } else {
+            region = aRegion;
         }
 
         if (mBackingSurface && sEGLLibrary.HasKHRLockSurface()) {
             nsRefPtr<gfxASurface> surface = GetLockSurface();
             if (surface) {
                 mUpdateContext = new gfxContext(surface);
                 gfxUtils::ClipToRegion(mUpdateContext, aRegion);
                 mUpdateContext->SetSource(aSurf);
                 mUpdateContext->SetOperator(gfxContext::OPERATOR_SOURCE);
                 mUpdateContext->Paint();
                 mUpdateContext = nsnull;
                 UnlockSurface();
             }
         } else {
             mShaderType =
               mGLContext->UploadSurfaceToTexture(aSurf,
-                                                 bounds,
+                                                 region,
                                                  mTexture,
                                                  !mCreated,
-                                                 dest,
+                                                 bounds.TopLeft(),
                                                  PR_FALSE);
         }
 
         mCreated = PR_TRUE;
         return true;
     }
 
     virtual PRBool InUpdate() const { return !!mUpdateContext; }