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