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