Texture.cpp revision 9372ac3621848085e77b867f220c0b5ffce4010d
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 default: 192 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); 193 break; 194 } 195} 196 197void Texture::upload(const SkBitmap& bitmap) { 198 SkAutoLockPixels alp(bitmap); 199 200 if (!bitmap.readyToDraw()) { 201 ALOGE("Cannot generate texture from bitmap"); 202 return; 203 } 204 205 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); 206 207 // We could also enable mipmapping if both bitmap dimensions are powers 208 // of 2 but we'd have to deal with size changes. Let's keep this simple 209 const bool canMipMap = mCaches.extensions().hasNPot(); 210 211 // If the texture had mipmap enabled but not anymore, 212 // force a glTexImage2D to discard the mipmap levels 213 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); 214 215 if (!mId) { 216 glGenTextures(1, &mId); 217 needsAlloc = true; 218 } 219 220 GLint format, type; 221 colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); 222 223 if (updateSize(bitmap.width(), bitmap.height(), format)) { 224 needsAlloc = true; 225 } 226 227 blend = !bitmap.isOpaque(); 228 mCaches.textureState().bindTexture(mId); 229 230 if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType 231 || bitmap.colorType() == kIndex_8_SkColorType)) { 232 SkBitmap rgbaBitmap; 233 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight, 234 bitmap.alphaType())); 235 rgbaBitmap.eraseColor(0); 236 237 SkCanvas canvas(rgbaBitmap); 238 canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); 239 240 uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type); 241 } else { 242 uploadSkBitmapToTexture(bitmap, needsAlloc, format, type); 243 } 244 245 if (canMipMap) { 246 mipMap = bitmap.hasHardwareMipMap(); 247 if (mipMap) { 248 glGenerateMipmap(GL_TEXTURE_2D); 249 } 250 } 251 252 if (mFirstFilter) { 253 setFilter(GL_NEAREST); 254 } 255 256 if (mFirstWrap) { 257 setWrap(GL_CLAMP_TO_EDGE); 258 } 259} 260 261void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) { 262 mId = id; 263 mWidth = width; 264 mHeight = height; 265 mFormat = format; 266 // We're wrapping an existing texture, so don't double count this memory 267 notifySizeChanged(0); 268} 269 270}; // namespace uirenderer 271}; // namespace android 272