Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "Caches.h"
     18 #include "Texture.h"
     19 #include "utils/GLUtils.h"
     20 #include "utils/TraceUtils.h"
     21 
     22 #include <utils/Log.h>
     23 
     24 #include <SkCanvas.h>
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 
     29 static int bytesPerPixel(GLint glFormat) {
     30     switch (glFormat) {
     31     // The wrapped-texture case, usually means a SurfaceTexture
     32     case 0:
     33         return 0;
     34     case GL_ALPHA:
     35         return 1;
     36     case GL_RGB:
     37         return 3;
     38     case GL_RGBA:
     39         return 4;
     40     case GL_RGBA16F:
     41         return 16;
     42     default:
     43         LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
     44     }
     45 }
     46 
     47 void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
     48         GLenum renderTarget) {
     49 
     50     if (force || wrapS != mWrapS || wrapT != mWrapT) {
     51         mWrapS = wrapS;
     52         mWrapT = wrapT;
     53 
     54         if (bindTexture) {
     55             mCaches.textureState().bindTexture(renderTarget, mId);
     56         }
     57 
     58         glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
     59         glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
     60     }
     61 }
     62 
     63 void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force,
     64         GLenum renderTarget) {
     65 
     66     if (force || min != mMinFilter || mag != mMagFilter) {
     67         mMinFilter = min;
     68         mMagFilter = mag;
     69 
     70         if (bindTexture) {
     71             mCaches.textureState().bindTexture(renderTarget, mId);
     72         }
     73 
     74         if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
     75 
     76         glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
     77         glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
     78     }
     79 }
     80 
     81 void Texture::deleteTexture() {
     82     mCaches.textureState().deleteTexture(mId);
     83     mId = 0;
     84 }
     85 
     86 bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) {
     87     if (mWidth == width && mHeight == height && mFormat == format) {
     88         return false;
     89     }
     90     mWidth = width;
     91     mHeight = height;
     92     mFormat = format;
     93     notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat));
     94     return true;
     95 }
     96 
     97 void Texture::resetCachedParams() {
     98     mWrapS = GL_REPEAT;
     99     mWrapT = GL_REPEAT;
    100     mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
    101     mMagFilter = GL_LINEAR;
    102 }
    103 
    104 void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
    105         GLenum format, GLenum type, const void* pixels) {
    106     GL_CHECKPOINT(MODERATE);
    107     bool needsAlloc = updateSize(width, height, internalformat);
    108     if (!mId) {
    109         glGenTextures(1, &mId);
    110         needsAlloc = true;
    111         resetCachedParams();
    112     }
    113     mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
    114     if (needsAlloc) {
    115         glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
    116                 format, type, pixels);
    117     } else if (pixels) {
    118         glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
    119                 format, type, pixels);
    120     }
    121     GL_CHECKPOINT(MODERATE);
    122 }
    123 
    124 static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
    125         GLsizei width, GLsizei height, const GLvoid * data) {
    126 
    127     const bool useStride = stride != width
    128             && Caches::getInstance().extensions().hasUnpackRowLength();
    129     if ((stride == width) || useStride) {
    130         if (useStride) {
    131             glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
    132         }
    133 
    134         if (resize) {
    135             glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
    136         } else {
    137             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
    138         }
    139 
    140         if (useStride) {
    141             glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    142         }
    143     } else {
    144         //  With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
    145         //  if the stride doesn't match the width
    146 
    147         GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
    148         if (!temp) return;
    149 
    150         uint8_t * pDst = (uint8_t *)temp;
    151         uint8_t * pSrc = (uint8_t *)data;
    152         for (GLsizei i = 0; i < height; i++) {
    153             memcpy(pDst, pSrc, width * bpp);
    154             pDst += width * bpp;
    155             pSrc += stride * bpp;
    156         }
    157 
    158         if (resize) {
    159             glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
    160         } else {
    161             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
    162         }
    163 
    164         free(temp);
    165     }
    166 }
    167 
    168 static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
    169         bool resize, GLenum format, GLenum type) {
    170     uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
    171             bitmap.width(), bitmap.height(), bitmap.getPixels());
    172 }
    173 
    174 static void colorTypeToGlFormatAndType(SkColorType colorType,
    175         GLint* outFormat, GLint* outType) {
    176     switch (colorType) {
    177     case kAlpha_8_SkColorType:
    178         *outFormat = GL_ALPHA;
    179         *outType = GL_UNSIGNED_BYTE;
    180         break;
    181     case kRGB_565_SkColorType:
    182         *outFormat = GL_RGB;
    183         *outType = GL_UNSIGNED_SHORT_5_6_5;
    184         break;
    185     // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
    186     case kARGB_4444_SkColorType:
    187     case kIndex_8_SkColorType:
    188     case kN32_SkColorType:
    189         *outFormat = GL_RGBA;
    190         *outType = GL_UNSIGNED_BYTE;
    191         break;
    192     case kGray_8_SkColorType:
    193         *outFormat = GL_LUMINANCE;
    194         *outType = GL_UNSIGNED_BYTE;
    195         break;
    196     default:
    197         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
    198         break;
    199     }
    200 }
    201 
    202 void Texture::upload(const SkBitmap& bitmap) {
    203     SkAutoLockPixels alp(bitmap);
    204 
    205     if (!bitmap.readyToDraw()) {
    206         ALOGE("Cannot generate texture from bitmap");
    207         return;
    208     }
    209 
    210     ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
    211 
    212     // We could also enable mipmapping if both bitmap dimensions are powers
    213     // of 2 but we'd have to deal with size changes. Let's keep this simple
    214     const bool canMipMap = mCaches.extensions().hasNPot();
    215 
    216     // If the texture had mipmap enabled but not anymore,
    217     // force a glTexImage2D to discard the mipmap levels
    218     bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
    219     bool setDefaultParams = false;
    220 
    221     if (!mId) {
    222         glGenTextures(1, &mId);
    223         needsAlloc = true;
    224         setDefaultParams = true;
    225     }
    226 
    227     GLint format, type;
    228     colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type);
    229 
    230     if (updateSize(bitmap.width(), bitmap.height(), format)) {
    231         needsAlloc = true;
    232     }
    233 
    234     blend = !bitmap.isOpaque();
    235     mCaches.textureState().bindTexture(mId);
    236 
    237     if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
    238             || bitmap.colorType() == kIndex_8_SkColorType)) {
    239         SkBitmap rgbaBitmap;
    240         rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight,
    241                 bitmap.alphaType()));
    242         rgbaBitmap.eraseColor(0);
    243 
    244         SkCanvas canvas(rgbaBitmap);
    245         canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
    246 
    247         uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type);
    248     } else {
    249         uploadSkBitmapToTexture(bitmap, needsAlloc, format, type);
    250     }
    251 
    252     if (canMipMap) {
    253         mipMap = bitmap.hasHardwareMipMap();
    254         if (mipMap) {
    255             glGenerateMipmap(GL_TEXTURE_2D);
    256         }
    257     }
    258 
    259     if (setDefaultParams) {
    260         setFilter(GL_NEAREST);
    261         setWrap(GL_CLAMP_TO_EDGE);
    262     }
    263 }
    264 
    265 void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) {
    266     mId = id;
    267     mWidth = width;
    268     mHeight = height;
    269     mFormat = format;
    270     // We're wrapping an existing texture, so don't double count this memory
    271     notifySizeChanged(0);
    272 }
    273 
    274 }; // namespace uirenderer
    275 }; // namespace android
    276