Texture.cpp revision 2a38c42e921451abebb4ee5f5ecd738f1b6b04ed
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 26namespace android { 27namespace uirenderer { 28 29// Number of bytes used by a texture in the given format 30static int bytesPerPixel(GLint glFormat) { 31 switch (glFormat) { 32 // The wrapped-texture case, usually means a SurfaceTexture 33 case 0: 34 return 0; 35 case GL_LUMINANCE: 36 case GL_ALPHA: 37 return 1; 38 case GL_SRGB8: 39 case GL_RGB: 40 return 3; 41 case GL_SRGB8_ALPHA8: 42 case GL_RGBA: 43 return 4; 44 case GL_RGBA16F: 45 return 8; 46 default: 47 LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat); 48 } 49} 50 51void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { 52 53 if (force || wrapS != mWrapS || wrapT != mWrapT) { 54 mWrapS = wrapS; 55 mWrapT = wrapT; 56 57 if (bindTexture) { 58 mCaches.textureState().bindTexture(mTarget, mId); 59 } 60 61 glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); 62 glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); 63 } 64} 65 66void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { 67 if (force || min != mMinFilter || mag != mMagFilter) { 68 mMinFilter = min; 69 mMagFilter = mag; 70 71 if (bindTexture) { 72 mCaches.textureState().bindTexture(mTarget, mId); 73 } 74 75 if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; 76 77 glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); 78 glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); 79 } 80} 81 82void Texture::deleteTexture() { 83 mCaches.textureState().deleteTexture(mId); 84 mId = 0; 85} 86 87bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat, 88 GLint format, GLenum target) { 89 if (mWidth == width 90 && mHeight == height 91 && mFormat == format 92 && mInternalFormat == internalFormat 93 && mTarget == target) { 94 return false; 95 } 96 mWidth = width; 97 mHeight = height; 98 mFormat = format; 99 mInternalFormat = internalFormat; 100 mTarget = target; 101 notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); 102 return true; 103} 104 105void Texture::resetCachedParams() { 106 mWrapS = GL_REPEAT; 107 mWrapT = GL_REPEAT; 108 mMinFilter = GL_NEAREST_MIPMAP_LINEAR; 109 mMagFilter = GL_LINEAR; 110} 111 112void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, 113 GLenum format, GLenum type, const void* pixels) { 114 GL_CHECKPOINT(MODERATE); 115 bool needsAlloc = updateSize(width, height, internalFormat, format, GL_TEXTURE_2D); 116 if (!mId) { 117 glGenTextures(1, &mId); 118 needsAlloc = true; 119 resetCachedParams(); 120 } 121 mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); 122 if (needsAlloc) { 123 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, 124 format, type, pixels); 125 } else if (pixels) { 126 glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, 127 format, type, pixels); 128 } 129 GL_CHECKPOINT(MODERATE); 130} 131 132static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type, 133 GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) { 134 135 const bool useStride = stride != width 136 && Caches::getInstance().extensions().hasUnpackRowLength(); 137 if ((stride == width) || useStride) { 138 if (useStride) { 139 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); 140 } 141 142 if (resize) { 143 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); 144 } else { 145 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); 146 } 147 148 if (useStride) { 149 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 150 } 151 } else { 152 // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer 153 // if the stride doesn't match the width 154 155 GLvoid * temp = (GLvoid *) malloc(width * height * bpp); 156 if (!temp) return; 157 158 uint8_t * pDst = (uint8_t *)temp; 159 uint8_t * pSrc = (uint8_t *)data; 160 for (GLsizei i = 0; i < height; i++) { 161 memcpy(pDst, pSrc, width * bpp); 162 pDst += width * bpp; 163 pSrc += stride * bpp; 164 } 165 166 if (resize) { 167 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp); 168 } else { 169 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); 170 } 171 172 free(temp); 173 } 174} 175 176static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, 177 bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) { 178 switch (colorType) { 179 case kAlpha_8_SkColorType: 180 *outFormat = GL_ALPHA; 181 *outInternalFormat = GL_ALPHA; 182 *outType = GL_UNSIGNED_BYTE; 183 break; 184 case kRGB_565_SkColorType: 185 if (needSRGB) { 186 // We would ideally use a GL_RGB/GL_SRGB8 texture but the 187 // intermediate Skia bitmap needs to be ARGB_8888 188 *outFormat = GL_RGBA; 189 *outInternalFormat = caches.rgbaInternalFormat(); 190 *outType = GL_UNSIGNED_BYTE; 191 } else { 192 *outFormat = GL_RGB; 193 *outInternalFormat = GL_RGB; 194 *outType = GL_UNSIGNED_SHORT_5_6_5; 195 } 196 break; 197 // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 198 case kARGB_4444_SkColorType: 199 case kIndex_8_SkColorType: 200 case kN32_SkColorType: 201 *outFormat = GL_RGBA; 202 *outInternalFormat = caches.rgbaInternalFormat(needSRGB); 203 *outType = GL_UNSIGNED_BYTE; 204 break; 205 case kGray_8_SkColorType: 206 // TODO: Handle sRGB 207 *outFormat = GL_LUMINANCE; 208 *outInternalFormat = GL_LUMINANCE; 209 *outType = GL_UNSIGNED_BYTE; 210 break; 211 default: 212 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); 213 break; 214 } 215} 216 217void Texture::upload(Bitmap& bitmap) { 218 if (!bitmap.readyToDraw()) { 219 ALOGE("Cannot generate texture from bitmap"); 220 return; 221 } 222 223 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); 224 225 // We could also enable mipmapping if both bitmap dimensions are powers 226 // of 2 but we'd have to deal with size changes. Let's keep this simple 227 const bool canMipMap = mCaches.extensions().hasNPot(); 228 229 // If the texture had mipmap enabled but not anymore, 230 // force a glTexImage2D to discard the mipmap levels 231 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); 232 bool setDefaultParams = false; 233 234 if (!mId) { 235 glGenTextures(1, &mId); 236 needsAlloc = true; 237 setDefaultParams = true; 238 } 239 240 sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); 241 bool needSRGB = bitmap.info().colorSpace() == sRGB.get(); 242 243 GLint internalFormat, format, type; 244 colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type); 245 246 if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format, GL_TEXTURE_2D)) { 247 needsAlloc = true; 248 } 249 250 blend = !bitmap.isOpaque(); 251 mCaches.textureState().bindTexture(mId); 252 253 // TODO: Handle sRGB gray bitmaps 254 bool hasSRGB = mCaches.extensions().hasSRGB(); 255 if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType 256 || bitmap.colorType() == kIndex_8_SkColorType 257 || (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) { 258 259 SkBitmap rgbaBitmap; 260 rgbaBitmap.allocPixels(SkImageInfo::MakeN32( 261 mWidth, mHeight, bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr)); 262 rgbaBitmap.eraseColor(0); 263 264 SkCanvas canvas(rgbaBitmap); 265 SkBitmap skBitmap; 266 bitmap.getSkBitmap(&skBitmap); 267 canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr); 268 269 uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), 270 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), 271 rgbaBitmap.height(), rgbaBitmap.getPixels()); 272 273 } else { 274 uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), 275 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels()); 276 } 277 278 if (canMipMap) { 279 mipMap = bitmap.hasHardwareMipMap(); 280 if (mipMap) { 281 glGenerateMipmap(GL_TEXTURE_2D); 282 } 283 } 284 285 if (setDefaultParams) { 286 setFilter(GL_NEAREST); 287 setWrap(GL_CLAMP_TO_EDGE); 288 } 289} 290 291void Texture::wrap(GLuint id, uint32_t width, uint32_t height, 292 GLint internalFormat, GLint format, GLenum target) { 293 mId = id; 294 mWidth = width; 295 mHeight = height; 296 mFormat = format; 297 mInternalFormat = internalFormat; 298 mTarget = target; 299 // We're wrapping an existing texture, so don't double count this memory 300 notifySizeChanged(0); 301} 302 303}; // namespace uirenderer 304}; // namespace android 305