void TextureCache::uploadToTexture(GLint id, bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); if ((stride == width) || useStride) { if (useStride) { glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); } if (resize) { glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); TT_ADD(id, width, height, format, type, String8("texture"), "[TextureCache.cpp] uploadToTexture +"); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); } if (useStride) { glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } } else { // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer // if the stride doesn't match the width GLvoid * temp = (GLvoid *) malloc(width * height * bpp); if (!temp) return; uint8_t * pDst = (uint8_t *)temp; uint8_t * pSrc = (uint8_t *)data; for (GLsizei i = 0; i < height; i++) { memcpy(pDst, pSrc, width * bpp); pDst += width * bpp; pSrc += stride * bpp; } if (resize) { glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); TT_ADD(id, width, height, format, type, String8("texture"), "[TextureCache.cpp] uploadToTexture +"); } else { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); } free(temp); } }
Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) { ATRACE_CALL(); LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); Caches& caches = Caches::getInstance(); GLuint fbo = caches.fboCache.get(); if (!fbo) { ALOGW("Could not obtain an FBO"); return NULL; } caches.activeTexture(0); Layer* layer = caches.layerCache.get(renderState, width, height); if (!layer) { ALOGW("Could not obtain a layer"); return NULL; } // We first obtain a layer before comparing against the max texture size // because layers are not allocated at the exact desired size. They are // always created slighly larger to improve recycling const uint32_t maxTextureSize = caches.maxTextureSize; if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) { ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)", width, height, maxTextureSize, maxTextureSize); // Creating a new layer always increment its refcount by 1, this allows // us to destroy the layer object if one was created for us ResourceCache::getInstance().decrementRefcount(layer); return NULL; } layer->setFbo(fbo); layer->layer.set(0.0f, 0.0f, width, height); layer->texCoords.set(0.0f, height / float(layer->getHeight()), width / float(layer->getWidth()), 0.0f); layer->setAlpha(255, SkXfermode::kSrcOver_Mode); layer->setColorFilter(NULL); layer->setDirty(true); layer->region.clear(); GLuint previousFbo = renderState.getFramebuffer(); renderState.bindFramebuffer(layer->getFbo()); layer->bindTexture(); // Initialize the texture if needed if (layer->isEmpty()) { layer->setEmpty(false); layer->allocateTexture(); TT_ADD(String8("Layer"), layer->getTexture(), layer->getWidth(), layer->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, String8("layer"), "[LayerRenderer.cpp] allocateTexture +"); // This should only happen if we run out of memory if (glGetError() != GL_NO_ERROR) { ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); renderState.bindFramebuffer(previousFbo); ResourceCache::getInstance().decrementRefcount(layer); return NULL; } } LAYER_RENDERER_LOGD("createRenderLayer %p, %dx%d, fbo %d, texture %d, alpha %d", layer, layer->getWidth(), layer->getHeight(), layer->getFbo(), layer->getTexture(), layer->getAlpha()); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); LAYER_RENDERER_LOGD("Layer %p finished uploading texture %d to fbo %d", layer, layer->getTexture(), layer->getFbo()); renderState.bindFramebuffer(previousFbo); return layer; }
ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len, int numGlyphs, float radius, const float* positions) { ShadowText entry(paint, radius, len, text, positions); ShadowTexture* texture = mCache.get(entry); if (!texture) { SkPaint paintCopy(*paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0, len, numGlyphs, radius, positions); if (!shadow.image) { return NULL; } Caches& caches = Caches::getInstance(); texture = new ShadowTexture(caches); texture->left = shadow.penX; texture->top = shadow.penY; texture->width = shadow.width; texture->height = shadow.height; texture->generation = 0; texture->blend = true; const uint32_t size = shadow.width * shadow.height; texture->bitmapSize = size; // Don't even try to cache a bitmap that's bigger than the cache if (size < mMaxSize) { while (mSize + size > mMaxSize) { mCache.removeOldest(); } } glGenTextures(1, &texture->id); caches.bindTexture(texture->id); // Textures are Alpha8 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); TT_ADD(texture->id, texture->width, texture->height, GL_ALPHA, GL_UNSIGNED_BYTE, String8("textShadow"), "[TextDropShadowCache.cpp] get +"); DUMP_ALPHA_TEXTURE(texture->width, texture->height, shadow.image, "ShadowTexture", SkBitmap::kA8_Config); texture->setFilter(GL_LINEAR); texture->setWrap(GL_CLAMP_TO_EDGE); if (size < mMaxSize) { if (mDebugEnabled) { ALOGD("Shadow texture created, size = %d", texture->bitmapSize); } entry.copyTextLocally(); mSize += size; mCache.put(entry, texture); } else { texture->cleanup = true; } // Cleanup shadow free(shadow.image); } return texture; }