Take SkBitmap's stride into account when uploading textures
Bug #10151807

Change-Id: I7ba4804fa3619088fea70eb55f10519fff0bf5f0
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 5e574a6..25d4c5e 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -41,7 +41,7 @@
     inline bool has1BitStencil() const { return mHas1BitStencil; }
     inline bool has4BitStencil() const { return mHas4BitStencil; }
     inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
-
+    inline bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
     inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
     inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
     inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index a63cac6..ed0a79a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -240,20 +240,20 @@
     switch (bitmap->getConfig()) {
     case SkBitmap::kA8_Config:
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-        uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
-                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+                texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
         texture->blend = true;
         break;
     case SkBitmap::kRGB_565_Config:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
-        uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
-                GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+        uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+                texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
         texture->blend = false;
         break;
     case SkBitmap::kARGB_8888_Config:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
-        uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
-                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+                texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
         // Do this after calling getPixels() to make sure Skia's deferred
         // decoding happened
         texture->blend = !bitmap->isOpaque();
@@ -293,17 +293,28 @@
     SkCanvas canvas(rgbaBitmap);
     canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
 
-    uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
+    uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
             GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
 }
 
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
-        GLenum type, const GLvoid * data) {
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+        GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
+    // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+    //       if the stride doesn't match the width
+    const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
+    if (useStride) {
+        glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+    }
+
     if (resize) {
         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
     } else {
         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
     }
+
+    if (useStride) {
+        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+    }
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 80bb22e..57fc19a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -125,8 +125,8 @@
     void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
 
     void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
-    void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
-            GLenum type, const GLvoid * data);
+    void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+            GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
 
     void init();
 
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index cbed3e4..d5f38b5 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -119,7 +119,7 @@
     // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
     // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
     // With OpenGL ES 2.0 we have to upload entire stripes instead.
-    mHasES3 = Extensions::getInstance().getMajorGlVersion() >= 3;
+    mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength();
 }
 
 CacheTexture::~CacheTexture() {
@@ -206,21 +206,21 @@
 bool CacheTexture::upload() {
     const Rect& dirtyRect = mDirtyRect;
 
-    uint32_t x = mHasES3 ? dirtyRect.left : 0;
+    uint32_t x = mHasUnpackRowLength ? dirtyRect.left : 0;
     uint32_t y = dirtyRect.top;
-    uint32_t width = mHasES3 ? dirtyRect.getWidth() : mWidth;
+    uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : mWidth;
     uint32_t height = dirtyRect.getHeight();
 
     // The unpack row length only needs to be specified when a new
     // texture is bound
-    if (mHasES3) {
+    if (mHasUnpackRowLength) {
         glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
     }
 
     mTexture->upload(x, y, width, height);
     setDirty(false);
 
-    return mHasES3;
+    return mHasUnpackRowLength;
 }
 
 void CacheTexture::setDirty(bool dirty) {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 028b611..61b38f8 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -190,7 +190,7 @@
     uint32_t mMaxQuadCount;
     Caches& mCaches;
     CacheBlock* mCacheBlocks;
-    bool mHasES3;
+    bool mHasUnpackRowLength;
     Rect mDirtyRect;
 };