Texture.cpp revision 88d842fedf9a6c03dee1f2c91bc7a1f51c8438da
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 bool needsAlloc = updateSize(width, height, internalformat); 99 if (!needsAlloc && !pixels) { 100 return; 101 } 102 mCaches.textureState().activateTexture(0); 103 GL_CHECKPOINT(); 104 if (!mId) { 105 glGenTextures(1, &mId); 106 needsAlloc = true; 107 } 108 GL_CHECKPOINT(); 109 mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); 110 GL_CHECKPOINT(); 111 if (needsAlloc) { 112 glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, 113 format, type, pixels); 114 GL_CHECKPOINT(); 115 } else { 116 glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, 117 format, type, pixels); 118 GL_CHECKPOINT(); 119 } 120} 121 122static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, 123 GLsizei width, GLsizei height, const GLvoid * data) { 124 125 glPixelStorei(GL_UNPACK_ALIGNMENT, bpp); 126 const bool useStride = stride != width 127 && Caches::getInstance().extensions().hasUnpackRowLength(); 128 if ((stride == width) || useStride) { 129 if (useStride) { 130 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); 131 } 132 133 if (resize) { 134 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); 135 } else { 136 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); 137 } 138 139 if (useStride) { 140 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 141 } 142 } else { 143 // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer 144 // if the stride doesn't match the width 145 146 GLvoid * temp = (GLvoid *) malloc(width * height * bpp); 147 if (!temp) return; 148 149 uint8_t * pDst = (uint8_t *)temp; 150 uint8_t * pSrc = (uint8_t *)data; 151 for (GLsizei i = 0; i < height; i++) { 152 memcpy(pDst, pSrc, width * bpp); 153 pDst += width * bpp; 154 pSrc += stride * bpp; 155 } 156 157 if (resize) { 158 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); 159 } else { 160 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); 161 } 162 163 free(temp); 164 } 165} 166 167static void uploadSkBitmapToTexture(const SkBitmap& bitmap, 168 bool resize, GLenum format, GLenum type) { 169 uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(), 170 bitmap.width(), bitmap.height(), bitmap.getPixels()); 171} 172 173static void colorTypeToGlFormatAndType(SkColorType colorType, 174 GLint* outFormat, GLint* outType) { 175 switch (colorType) { 176 case kAlpha_8_SkColorType: 177 *outFormat = GL_ALPHA; 178 *outType = GL_UNSIGNED_BYTE; 179 break; 180 case kRGB_565_SkColorType: 181 *outFormat = GL_RGB; 182 *outType = GL_UNSIGNED_SHORT_5_6_5; 183 break; 184 // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 185 case kARGB_4444_SkColorType: 186 case kIndex_8_SkColorType: 187 case kN32_SkColorType: 188 *outFormat = GL_RGBA; 189 *outType = GL_UNSIGNED_BYTE; 190 break; 191 case kGray_8_SkColorType: 192 *outFormat = GL_LUMINANCE; 193 *outType = GL_UNSIGNED_BYTE; 194 break; 195 default: 196 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); 197 break; 198 } 199} 200 201void Texture::upload(const SkBitmap& bitmap) { 202 SkAutoLockPixels alp(bitmap); 203 204 if (!bitmap.readyToDraw()) { 205 ALOGE("Cannot generate texture from bitmap"); 206 return; 207 } 208 209 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); 210 211 // We could also enable mipmapping if both bitmap dimensions are powers 212 // of 2 but we'd have to deal with size changes. Let's keep this simple 213 const bool canMipMap = mCaches.extensions().hasNPot(); 214 215 // If the texture had mipmap enabled but not anymore, 216 // force a glTexImage2D to discard the mipmap levels 217 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); 218 219 if (!mId) { 220 glGenTextures(1, &mId); 221 needsAlloc = true; 222 } 223 224 GLint format, type; 225 colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); 226 227 if (updateSize(bitmap.width(), bitmap.height(), format)) { 228 needsAlloc = true; 229 } 230 231 blend = !bitmap.isOpaque(); 232 mCaches.textureState().bindTexture(mId); 233 234 if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType 235 || bitmap.colorType() == kIndex_8_SkColorType)) { 236 SkBitmap rgbaBitmap; 237 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight, 238 bitmap.alphaType())); 239 rgbaBitmap.eraseColor(0); 240 241 SkCanvas canvas(rgbaBitmap); 242 canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); 243 244 uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type); 245 } else { 246 uploadSkBitmapToTexture(bitmap, needsAlloc, format, type); 247 } 248 249 if (canMipMap) { 250 mipMap = bitmap.hasHardwareMipMap(); 251 if (mipMap) { 252 glGenerateMipmap(GL_TEXTURE_2D); 253 } 254 } 255 256 if (mFirstFilter) { 257 setFilter(GL_NEAREST); 258 } 259 260 if (mFirstWrap) { 261 setWrap(GL_CLAMP_TO_EDGE); 262 } 263} 264 265void 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