diff options
Diffstat (limited to 'Source')
57 files changed, 626 insertions, 344 deletions
diff --git a/Source/WebCore/bindings/v8/NPV8Object.cpp b/Source/WebCore/bindings/v8/NPV8Object.cpp index 0ecb3c7..64a90b8 100644 --- a/Source/WebCore/bindings/v8/NPV8Object.cpp +++ b/Source/WebCore/bindings/v8/NPV8Object.cpp @@ -58,7 +58,7 @@ namespace WebCore { WrapperTypeInfo* npObjectTypeInfo() { - static WrapperTypeInfo typeInfo = { 0, 0, 0 }; + static WrapperTypeInfo typeInfo = { 0, 0, 0, 0 }; return &typeInfo; } diff --git a/Source/WebCore/bindings/v8/custom/V8HTMLAudioElementConstructor.cpp b/Source/WebCore/bindings/v8/custom/V8HTMLAudioElementConstructor.cpp index 7a0a545..34a8a35 100644 --- a/Source/WebCore/bindings/v8/custom/V8HTMLAudioElementConstructor.cpp +++ b/Source/WebCore/bindings/v8/custom/V8HTMLAudioElementConstructor.cpp @@ -44,7 +44,7 @@ namespace WebCore { -WrapperTypeInfo V8HTMLAudioElementConstructor::info = { V8HTMLAudioElementConstructor::GetTemplate, 0, 0 }; + WrapperTypeInfo V8HTMLAudioElementConstructor::info = { V8HTMLAudioElementConstructor::GetTemplate, 0, 0, 0 }; static v8::Handle<v8::Value> v8HTMLAudioElementConstructorCallback(const v8::Arguments& args) { diff --git a/Source/WebCore/bindings/v8/custom/V8HTMLImageElementConstructor.cpp b/Source/WebCore/bindings/v8/custom/V8HTMLImageElementConstructor.cpp index 24b1709..c339559 100644 --- a/Source/WebCore/bindings/v8/custom/V8HTMLImageElementConstructor.cpp +++ b/Source/WebCore/bindings/v8/custom/V8HTMLImageElementConstructor.cpp @@ -44,7 +44,7 @@ namespace WebCore { -WrapperTypeInfo V8HTMLImageElementConstructor::info = { V8HTMLImageElementConstructor::GetTemplate, 0, 0 }; + WrapperTypeInfo V8HTMLImageElementConstructor::info = { V8HTMLImageElementConstructor::GetTemplate, 0, 0, 0 }; static v8::Handle<v8::Value> v8HTMLImageElementConstructorCallback(const v8::Arguments& args) { diff --git a/Source/WebCore/bindings/v8/custom/V8HTMLOptionElementConstructor.cpp b/Source/WebCore/bindings/v8/custom/V8HTMLOptionElementConstructor.cpp index 14c80b9..413e4f2 100644 --- a/Source/WebCore/bindings/v8/custom/V8HTMLOptionElementConstructor.cpp +++ b/Source/WebCore/bindings/v8/custom/V8HTMLOptionElementConstructor.cpp @@ -44,7 +44,7 @@ namespace WebCore { -WrapperTypeInfo V8HTMLOptionElementConstructor::info = { V8HTMLOptionElementConstructor::GetTemplate, 0, 0 }; + WrapperTypeInfo V8HTMLOptionElementConstructor::info = { V8HTMLOptionElementConstructor::GetTemplate, 0, 0, 0 }; static v8::Handle<v8::Value> v8HTMLOptionElementConstructorCallback(const v8::Arguments& args) { diff --git a/Source/WebCore/html/HTMLCanvasElement.cpp b/Source/WebCore/html/HTMLCanvasElement.cpp index 764620c..9f51f10 100644 --- a/Source/WebCore/html/HTMLCanvasElement.cpp +++ b/Source/WebCore/html/HTMLCanvasElement.cpp @@ -79,7 +79,19 @@ HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document* doc , m_size(DefaultWidth, DefaultHeight) , m_rendererIsCanvas(false) , m_ignoreReset(false) +#ifdef ANDROID + /* In Android we capture the drawing into a displayList, and then + replay that list at various scale factors (sometimes zoomed out, other + times zoomed in for "normal" reading, yet other times at arbitrary + zoom values based on the user's choice). In all of these cases, we do + not re-record the displayList, hence it is usually harmful to perform + any pre-rounding, since we just don't know the actual drawing resolution + at record time. + */ + , m_pageScaleFactor(1) +#else , m_pageScaleFactor(document->frame() ? document->frame()->page()->chrome()->scaleFactor() : 1) +#endif , m_originClean(true) , m_hasCreatedImageBuffer(false) { diff --git a/Source/WebCore/platform/android/PlatformBridge.h b/Source/WebCore/platform/android/PlatformBridge.h index f20a001..a4c1048 100644 --- a/Source/WebCore/platform/android/PlatformBridge.h +++ b/Source/WebCore/platform/android/PlatformBridge.h @@ -148,6 +148,7 @@ public: static int highUsageDeltaMB(); static int memoryUsageMB(); static int actualMemoryUsageMB(); + static bool canSatisfyMemoryAllocation(long bytes); static int screenWidthInDocCoord(const FrameView*); static int screenHeightInDocCoord(const FrameView*); diff --git a/Source/WebCore/platform/android/RenderThemeAndroid.cpp b/Source/WebCore/platform/android/RenderThemeAndroid.cpp index 113fa63..c6e3bc5 100644 --- a/Source/WebCore/platform/android/RenderThemeAndroid.cpp +++ b/Source/WebCore/platform/android/RenderThemeAndroid.cpp @@ -234,7 +234,7 @@ bool RenderThemeAndroid::paintButton(RenderObject* obj, const PaintInfo& info, c if (formControlElement && !formControlElement->isEnabledFormControl()) { android::WebFrame* webFrame = getWebFrame(node); if (webFrame) { - const RenderSkinAndroid* skins = webFrame->renderSkins(); + RenderSkinAndroid* skins = webFrame->renderSkins(); if (skins) skins->renderSkinButton()->draw(getCanvasFromInfo(info), rect, RenderSkinAndroid::kDisabled); diff --git a/Source/WebCore/platform/graphics/android/BaseRenderer.cpp b/Source/WebCore/platform/graphics/android/BaseRenderer.cpp index d73c30f..68af1d6 100644 --- a/Source/WebCore/platform/graphics/android/BaseRenderer.cpp +++ b/Source/WebCore/platform/graphics/android/BaseRenderer.cpp @@ -120,6 +120,11 @@ int BaseRenderer::renderTiledContent(const TileRenderInfo& renderInfo) SkCanvas canvas; setupCanvas(renderInfo, &canvas); + if (!canvas.getDevice()) { + XLOG("Error: No Device"); + return 0; + } + if (visualIndicator) canvas.save(); diff --git a/Source/WebCore/platform/graphics/android/BaseTile.cpp b/Source/WebCore/platform/graphics/android/BaseTile.cpp index 60c1ebb..93d00bd 100644 --- a/Source/WebCore/platform/graphics/android/BaseTile.cpp +++ b/Source/WebCore/platform/graphics/android/BaseTile.cpp @@ -66,14 +66,13 @@ BaseTile::BaseTile(bool isLayerTile) , m_repaintPending(false) , m_usable(true) , m_lastDirtyPicture(0) - , m_lastPaintedPicture(0) + , m_isTexturePainted(false) , m_isLayerTile(isLayerTile) { #ifdef DEBUG_COUNT ClassTracker::instance()->increment("BaseTile"); #endif m_currentDirtyAreaIndex = 0; - m_renderer = new RasterRenderer(); // For EglImage Mode, the internal buffer should be 2. // And for Async Surface Texture mode, this is 3. @@ -128,7 +127,7 @@ void BaseTile::reserveTexture() android::AutoMutex lock(m_atomicSync); if (texture && m_texture != texture) { - m_lastPaintedPicture = 0; + m_isTexturePainted = false; fullInval(); } m_texture = texture; @@ -158,6 +157,8 @@ void BaseTile::fullInval() void BaseTile::markAsDirty(int unsigned pictureCount, const SkRegion& dirtyArea) { + if (dirtyArea.isEmpty()) + return; android::AutoMutex lock(m_atomicSync); m_lastDirtyPicture = pictureCount; for (int i = 0; i < m_maxBufferNumber; i++) @@ -220,7 +221,7 @@ void BaseTile::draw(float transparency, SkRect& rect, float scale) // Early return if set to un-usable in purpose! m_atomicSync.lock(); bool usable = m_usable; - bool isTexturePainted = m_lastPaintedPicture; + bool isTexturePainted = m_isTexturePainted; m_atomicSync.unlock(); if (!usable) { XLOG("early return at BaseTile::draw b/c tile set to unusable !"); @@ -273,6 +274,26 @@ bool BaseTile::isTileReady() return false; } +bool BaseTile::intersectWithRect(int x, int y, int tileWidth, int tileHeight, + float scale, const SkRect& dirtyRect, + SkRect& realTileRect) +{ + // compute the rect to corresponds to pixels + realTileRect.fLeft = x * tileWidth; + realTileRect.fTop = y * tileHeight; + realTileRect.fRight = realTileRect.fLeft + tileWidth; + realTileRect.fBottom = realTileRect.fTop + tileHeight; + + // scale the dirtyRect for intersect computation. + SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale, + dirtyRect.height() * scale); + realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale); + + if (!realTileRect.intersect(realDirtyRect)) + return false; + return true; +} + // This is called from the texture generation thread void BaseTile::paintBitmap() { @@ -326,37 +347,29 @@ void BaseTile::paintBitmap() bool fullRepaint = false; - // TODO: Implement the partial invalidate in Surface Texture Mode if (m_fullRepaint[m_currentDirtyAreaIndex] || textureInfo->m_width != tileWidth - || textureInfo->m_height != tileHeight - || textureInfo->getSharedTextureMode() == SurfaceTextureMode) { + || textureInfo->m_height != tileHeight) { fullRepaint = true; } - if (!fullRepaint) { - while (!cliperator.done()) { - SkRect dirtyRect; - dirtyRect.set(cliperator.rect()); - - // compute the rect to corresponds to pixels - SkRect realTileRect; - realTileRect.fLeft = x * tileWidth; - realTileRect.fTop = y * tileHeight; - realTileRect.fRight = realTileRect.fLeft + tileWidth; - realTileRect.fBottom = realTileRect.fTop + tileHeight; - - // scale the dirtyRect for intersect computation. - SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale, - dirtyRect.height() * scale); - realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale); - - // set realTileRect to the intersection of itself and the dirty rect - if (!realTileRect.intersect(realDirtyRect)) { - cliperator.next(); - continue; - } + bool surfaceTextureMode = textureInfo->getSharedTextureMode() == SurfaceTextureMode; + + while (!fullRepaint && !cliperator.done()) { + SkRect realTileRect; + SkRect dirtyRect; + dirtyRect.set(cliperator.rect()); + bool intersect = intersectWithRect(x, y, tileWidth, tileHeight, + scale, dirtyRect, realTileRect); + + // With SurfaceTexture, just repaint the entire tile if we intersect + // TODO: Implement the partial invalidate in Surface Texture Mode + if (intersect && surfaceTextureMode) { + fullRepaint = true; + break; + } + if (intersect && !surfaceTextureMode) { // initialize finalRealRect to the rounded values of realTileRect SkIRect finalRealRect; realTileRect.roundOut(&finalRealRect); @@ -380,11 +393,12 @@ void BaseTile::paintBitmap() renderInfo.measurePerf = false; pictureCount = m_renderer->renderTiledContent(renderInfo); - - cliperator.next(); } + + cliperator.next(); } + // Do a full repaint if needed if (fullRepaint) { SkIRect rect; rect.set(0, 0, tileWidth, tileHeight); @@ -405,7 +419,7 @@ void BaseTile::paintBitmap() texture->producerReleaseAndSwap(); if (texture == m_texture) { - m_lastPaintedPicture = pictureCount; + m_isTexturePainted = true; // set the fullrepaint flags m_fullRepaint[m_currentDirtyAreaIndex] = false; diff --git a/Source/WebCore/platform/graphics/android/BaseTile.h b/Source/WebCore/platform/graphics/android/BaseTile.h index c8873d4..0770b30 100644 --- a/Source/WebCore/platform/graphics/android/BaseTile.h +++ b/Source/WebCore/platform/graphics/android/BaseTile.h @@ -79,6 +79,10 @@ public: // the only thread-safe function called by the background thread void paintBitmap(); + bool intersectWithRect(int x, int y, int tileWidth, int tileHeight, + float scale, const SkRect& dirtyRect, + SkRect& realTileRect); + void markAsDirty(const unsigned int pictureCount, const SkRegion& dirtyArea); bool isDirty(); @@ -90,7 +94,6 @@ public: int x() const { return m_x; } int y() const { return m_y; } - unsigned int lastPaintedPicture() const { return m_lastPaintedPicture; } BaseTileTexture* texture() { return m_texture; } void setGLWebViewState(GLWebViewState* state) { m_glWebViewState = state; } @@ -131,10 +134,8 @@ private: int m_maxBufferNumber; int m_currentDirtyAreaIndex; - // stores the id of the latest picture painted to the tile. If the id is 0 - // then we know that the picture has not yet been painted an there is nothing - // to display (dirty or otherwise). - unsigned int m_lastPaintedPicture; + // flag used to know if we have a texture that was painted at least once + bool m_isTexturePainted; // This mutex serves two purposes. (1) It ensures that certain operations // happen atomically and (2) it makes sure those operations are synchronized diff --git a/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp b/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp index c964e04..7e6a57d 100644 --- a/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp +++ b/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp @@ -264,8 +264,7 @@ bool BaseTileTexture::readyFor(BaseTile* baseTile) (info->m_x == baseTile->x()) && (info->m_y == baseTile->y()) && (info->m_scale == baseTile->scale()) && - (info->m_painter == baseTile->painter()) && - (info->m_picture == baseTile->lastPaintedPicture())) + (info->m_painter == baseTile->painter())) return true; XLOG("readyFor return false for tile x, y (%d %d) texId %d ," diff --git a/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp b/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp index 569f987..4c5af9e 100644 --- a/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp +++ b/Source/WebCore/platform/graphics/android/DoubleBufferedTexture.cpp @@ -82,6 +82,8 @@ SharedTexture* DoubleBufferedTexture::getReadableTexture() EGLContext DoubleBufferedTexture::producerAcquireContext() { + if (m_sharedTextureMode == SurfaceTextureMode) + return EGL_NO_CONTEXT; if (m_pContext != EGL_NO_CONTEXT) { LOGV("AquireContext has previously generated a context.\n"); diff --git a/Source/WebCore/platform/graphics/android/FontAndroid.cpp b/Source/WebCore/platform/graphics/android/FontAndroid.cpp index b56e37c..332e571 100644 --- a/Source/WebCore/platform/graphics/android/FontAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/FontAndroid.cpp @@ -427,6 +427,7 @@ public: private: void setupFontForScriptRun(); + void setupComplexFont(const char* fontName, const FontPlatformData& platformData); HB_FontRec* allocHarfbuzzFont(); void deleteGlyphArrays(); void createGlyphArrays(int); @@ -465,6 +466,7 @@ private: // each word break we accumulate error. This is the // number of pixels that we are behind so far. unsigned m_letterSpacing; // pixels to be added after each glyph. + FontPlatformData* m_complexPlatformData; }; @@ -479,6 +481,7 @@ TextRunWalker::TextRunWalker(const TextRun& run, unsigned startingX, const Font* , m_padPerWordBreak(0) , m_padError(0) , m_letterSpacing(0) + , m_complexPlatformData(0) { // Do not use |run| inside this constructor. Use |m_run| instead. @@ -507,6 +510,7 @@ TextRunWalker::~TextRunWalker() fastFree(m_item.font); deleteGlyphArrays(); delete[] m_item.log_clusters; + delete m_complexPlatformData; } bool TextRunWalker::isWordBreak(unsigned index, bool isRTL) @@ -620,15 +624,48 @@ void TextRunWalker::setWordAndLetterSpacing(int wordSpacingAdjustment, setLetterSpacingAdjustment(letterSpacingAdjustment); } +void TextRunWalker::setupComplexFont(const char* filename, + const FontPlatformData& platformData) +{ + delete m_complexPlatformData; + + SkTypeface* typeface = SkTypeface::CreateFromFile(filename); + m_complexPlatformData = new FontPlatformData(platformData, typeface); + SkSafeUnref(typeface); + m_item.face = m_complexPlatformData->harfbuzzFace(); + m_item.font->userData = m_complexPlatformData; +} + void TextRunWalker::setupFontForScriptRun() { - const FontData* fontData = m_font->glyphDataForCharacter( - m_item.string[m_item.item.pos], false).fontData; + const FontData* fontData = m_font->glyphDataForCharacter(m_run[0], false).fontData; const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); - m_item.face = platformData.harfbuzzFace(); - void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); - m_item.font->userData = opaquePlatformData; + + if (m_item.item.script == HB_Script_Devanagari) { + setupComplexFont("/system/fonts/Lohit_Hindi.ttf", platformData); + } else if (m_item.item.script == HB_Script_Thai) { + setupComplexFont("/system/fonts/DroidSansThai.ttf", platformData); + } else if (m_item.item.script == HB_Script_Arabic) { + setupComplexFont("/system/fonts/DroidNaskh-Regular.ttf", platformData); + } else if (m_item.item.script == HB_Script_Hebrew) { + switch (platformData.typeface()->style()) { + case SkTypeface::kBold: + case SkTypeface::kBoldItalic: + setupComplexFont("/system/fonts/DroidSansHebrew-Bold.ttf", platformData); + break; + case SkTypeface::kNormal: + case SkTypeface::kItalic: + default: + setupComplexFont("/system/fonts/DroidSansHebrew-Regular.ttf", platformData); + break; + } + } else { + // HB_Script_Common; includes Ethiopic + m_item.face = platformData.harfbuzzFace(); + void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); + m_item.font->userData = opaquePlatformData; + } } HB_FontRec* TextRunWalker::allocHarfbuzzFont() diff --git a/Source/WebCore/platform/graphics/android/FontPlatformData.h b/Source/WebCore/platform/graphics/android/FontPlatformData.h index 3313aca..56ce6e9 100644 --- a/Source/WebCore/platform/graphics/android/FontPlatformData.h +++ b/Source/WebCore/platform/graphics/android/FontPlatformData.h @@ -55,6 +55,7 @@ public: FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic); FontPlatformData(const FontPlatformData& src, float textSize); FontPlatformData(float size, bool syntheticBold, bool syntheticOblique); + FontPlatformData(const FontPlatformData& src, SkTypeface* typeface); ~FontPlatformData(); @@ -87,6 +88,7 @@ public: #endif HB_FaceRec_* harfbuzzFace() const; + SkTypeface* typeface() const { return mTypeface; } private: class RefCountedHarfbuzzFace : public RefCounted<RefCountedHarfbuzzFace> { diff --git a/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp index 337a94d..1c3c5d9 100644 --- a/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -127,6 +127,17 @@ FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) trace(5); } +FontPlatformData::FontPlatformData(const FontPlatformData& src, SkTypeface* tf) + : mTypeface(tf), mTextSize(src.mTextSize), mFakeBold(src.mFakeBold), mFakeItalic(src.mFakeItalic) +{ + if (hashTableDeletedFontValue() != mTypeface) { + SkSafeRef(mTypeface); + } + + inc_count(); + trace(6); +} + FontPlatformData::~FontPlatformData() { dec_count(); diff --git a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp index 3c7375a..9978327 100644 --- a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp +++ b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp @@ -563,20 +563,32 @@ bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect, SkSafeUnref(m_previouslyUsedRoot); m_previouslyUsedRoot = compositedRoot; if (ret) { - FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord(m_frameworkInval); - // Inflate the invalidate rect to avoid precision lost. - frameworkInval.inflate(1); - IntRect inval(frameworkInval.x(), frameworkInval.y(), frameworkInval.width(), frameworkInval.height()); - - inval.unite(m_frameworkLayersInval); - - invalRect->setX(inval.x()); - invalRect->setY(inval.y()); - invalRect->setWidth(inval.width()); - invalRect->setHeight(inval.height()); - - XLOG("invalRect(%d, %d, %d, %d)", inval.x(), - inval.y(), inval.width(), inval.height()); + if (m_frameworkInval.isEmpty()) { + // ret==true && empty inval region means we've inval'd everything, + // but don't have new content. Keep redrawing full view (0,0,0,0) + // until tile generation catches up and we swap pages. + invalRect->setX(0); + invalRect->setY(0); + invalRect->setWidth(0); + invalRect->setHeight(0); + } else { + FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord( + m_frameworkInval); + // Inflate the invalidate rect to avoid precision lost. + frameworkInval.inflate(1); + IntRect inval(frameworkInval.x(), frameworkInval.y(), + frameworkInval.width(), frameworkInval.height()); + + inval.unite(m_frameworkLayersInval); + + invalRect->setX(inval.x()); + invalRect->setY(inval.y()); + invalRect->setWidth(inval.width()); + invalRect->setHeight(inval.height()); + + XLOG("invalRect(%d, %d, %d, %d)", inval.x(), + inval.y(), inval.width(), inval.height()); + } } else { resetFrameworkInval(); } diff --git a/Source/WebCore/platform/graphics/android/GaneshContext.cpp b/Source/WebCore/platform/graphics/android/GaneshContext.cpp index 6118aef..03e6eb7 100644 --- a/Source/WebCore/platform/graphics/android/GaneshContext.cpp +++ b/Source/WebCore/platform/graphics/android/GaneshContext.cpp @@ -85,14 +85,25 @@ void GaneshContext::flush() SkDevice* GaneshContext::getDeviceForBaseTile(const TileRenderInfo& renderInfo) { + // Ganesh should be the only code in the rendering thread that is using GL + // and setting the EGLContext. If this is not the case then we need to + // reset the Ganesh context to prevent rendering issues. + bool contextNeedsReset = false; + if (eglGetCurrentContext() != m_surfaceContext) { + XLOG("Warning: EGLContext has Changed! %p, %p", m_surfaceContext, + eglGetCurrentContext()); + contextNeedsReset = true; + } + SkDevice* device = 0; if (renderInfo.textureInfo->getSharedTextureMode() == SurfaceTextureMode) device = getDeviceForBaseTileSurface(renderInfo); else if (renderInfo.textureInfo->getSharedTextureMode() == EglImageMode) device = getDeviceForBaseTileFBO(renderInfo); - // TODO only need to reset if others are sharing our context - if (device) + // We must reset the Ganesh context only after we are sure we have + // re-established our EGLContext as the current context. + if (device && contextNeedsReset) getGrContext()->resetContext(); return device; @@ -100,11 +111,22 @@ SkDevice* GaneshContext::getDeviceForBaseTile(const TileRenderInfo& renderInfo) SkDevice* GaneshContext::getDeviceForBaseTileSurface(const TileRenderInfo& renderInfo) { - EGLDisplay display = eglGetCurrentDisplay(); - GLUtils::checkEglError("eglGetCurrentDisplay"); + EGLDisplay display; if (!m_surfaceContext) { + if(eglGetCurrentContext() != EGL_NO_CONTEXT) { + XLOG("ERROR: should not have a context yet"); + } + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + GLUtils::checkEglError("eglGetDisplay"); + + EGLint majorVersion; + EGLint minorVersion; + EGLBoolean returnValue = eglInitialize(display, &majorVersion, &minorVersion); + GLUtils::checkEglError("eglInitialize", returnValue); + EGLint numConfigs; static const EGLint configAttribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, @@ -126,26 +148,32 @@ SkDevice* GaneshContext::getDeviceForBaseTileSurface(const TileRenderInfo& rende m_surfaceContext = eglCreateContext(display, m_surfaceConfig, NULL, contextAttribs); GLUtils::checkEglError("eglCreateContext"); + } else { + display = eglGetCurrentDisplay(); + GLUtils::checkEglError("eglGetCurrentDisplay"); } - if (renderInfo.textureInfo->m_eglSurface == EGL_NO_SURFACE) { + TransferQueue* tileQueue = TilesManager::instance()->transferQueue(); + + if (tileQueue->m_eglSurface == EGL_NO_SURFACE) { const float tileWidth = renderInfo.tileSize.width(); const float tileHeight = renderInfo.tileSize.height(); - ANativeWindow* anw = renderInfo.textureInfo->m_ANW.get(); + + ANativeWindow* anw = tileQueue->m_ANW.get(); int result = ANativeWindow_setBuffersGeometry(anw, (int)tileWidth, (int)tileHeight, WINDOW_FORMAT_RGBA_8888); renderInfo.textureInfo->m_width = tileWidth; renderInfo.textureInfo->m_height = tileHeight; - renderInfo.textureInfo->m_eglSurface = eglCreateWindowSurface(display, m_surfaceConfig, anw, NULL); + tileQueue->m_eglSurface = eglCreateWindowSurface(display, m_surfaceConfig, anw, NULL); GLUtils::checkEglError("eglCreateWindowSurface"); XLOG("eglCreateWindowSurface"); } - EGLBoolean returnValue = eglMakeCurrent(display, renderInfo.textureInfo->m_eglSurface, renderInfo.textureInfo->m_eglSurface, m_surfaceContext); + EGLBoolean returnValue = eglMakeCurrent(display, tileQueue->m_eglSurface, tileQueue->m_eglSurface, m_surfaceContext); GLUtils::checkEglError("eglMakeCurrent", returnValue); XLOG("eglMakeCurrent"); diff --git a/Source/WebCore/platform/graphics/android/GaneshRenderer.cpp b/Source/WebCore/platform/graphics/android/GaneshRenderer.cpp index 8f4d0b3..9268ac0 100644 --- a/Source/WebCore/platform/graphics/android/GaneshRenderer.cpp +++ b/Source/WebCore/platform/graphics/android/GaneshRenderer.cpp @@ -84,6 +84,21 @@ void GaneshRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* can GaneshContext* ganesh = GaneshContext::instance(); +#if !DEPRECATED_SURFACE_TEXTURE_MODE + if (renderInfo.textureInfo->getSharedTextureMode() == SurfaceTextureMode) { + TransferQueue* tileQueue = TilesManager::instance()->transferQueue(); + + tileQueue->lockQueue(); + + bool ready = tileQueue->readyForUpdate(); + if (!ready) { + XLOG("!ready"); + tileQueue->unlockQueue(); + return; + } + } +#endif + SkDevice* device = NULL; if (renderInfo.tileSize.width() == TilesManager::tileWidth() && renderInfo.tileSize.height() == TilesManager::tileHeight()) { @@ -102,6 +117,7 @@ void GaneshRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* can // set the GPU device to the canvas canvas->setDevice(device); + canvas->setDeviceFactory(device->getDeviceFactory()); // invert canvas contents if (renderInfo.textureInfo->getSharedTextureMode() == EglImageMode) { @@ -134,7 +150,12 @@ void GaneshRenderer::renderingComplete(const TileRenderInfo& renderInfo, SkCanva // In SurfaceTextureMode we must call swapBuffers to unlock and post the // tile's ANativeWindow (i.e. SurfaceTexture) buffer if (renderInfo.textureInfo->getSharedTextureMode() == SurfaceTextureMode) { - eglSwapBuffers(eglGetCurrentDisplay(), renderInfo.textureInfo->m_eglSurface); +#if !DEPRECATED_SURFACE_TEXTURE_MODE + TransferQueue* tileQueue = TilesManager::instance()->transferQueue(); + eglSwapBuffers(eglGetCurrentDisplay(), tileQueue->m_eglSurface); + tileQueue->addItemInTransferQueue(&renderInfo); + tileQueue->unlockQueue(); +#endif } if (renderInfo.measurePerf) diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp index afd11ff..aa30035 100644 --- a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp @@ -35,6 +35,7 @@ #include "ScaleTransformOperation.h" #include "ScrollableLayerAndroid.h" #include "SkCanvas.h" +#include "SkRegion.h" #include "TransformationMatrix.h" #include "TranslateTransformOperation.h" @@ -125,6 +126,7 @@ GraphicsLayerAndroid::GraphicsLayerAndroid(GraphicsLayerClient* client) : { RenderLayer* renderLayer = renderLayerFromClient(m_client); m_contentLayer = new LayerAndroid(renderLayer); + m_dirtyRegion.setEmpty(); gDebugGraphicsLayerAndroidInstances++; } @@ -618,9 +620,10 @@ bool GraphicsLayerAndroid::repaint() m_contentLayer->getSize().width(), m_contentLayer->getSize().height()); + m_contentLayer->markAsDirty(m_dirtyRegion); + m_dirtyRegion.setEmpty(); m_contentLayer->needsRepaint(); m_needsRepaint = false; - m_invalidatedRects.clear(); return true; } @@ -667,26 +670,11 @@ void GraphicsLayerAndroid::setNeedsDisplayInRect(const FloatRect& rect) return; } - bool addInval = true; - const size_t maxDirtyRects = 8; - for (size_t i = 0; i < m_invalidatedRects.size(); ++i) { - if (m_invalidatedRects[i].contains(rect)) { - addInval = false; - break; - } - } - -#ifdef LAYER_DEBUG - LOG("(%x) layer %d setNeedsDisplayInRect(%d) - (%.2f, %.2f, %.2f, %.2f)", this, - m_contentLayer->uniqueId(), m_needsRepaint, rect.x(), rect.y(), rect.width(), rect.height()); -#endif - - if (addInval) { - if (m_invalidatedRects.size() < maxDirtyRects) - m_invalidatedRects.append(rect); - else - m_invalidatedRects[0].unite(rect); - } + SkRegion region; + region.setRect(rect.x(), rect.y(), + rect.x() + rect.width(), + rect.y() + rect.height()); + m_dirtyRegion.op(region, SkRegion::kUnion_Op); m_needsRepaint = true; askForSync(); diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h index 10db5a1..a693de3 100644 --- a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h @@ -31,6 +31,7 @@ class FloatPoint3D; class Image; class SkBitmapRef; +class SkRegion; namespace WebCore { @@ -148,7 +149,7 @@ private: bool m_newImage; SkBitmapRef* m_imageRef; // only used to remember previously passed images - Vector<FloatRect> m_invalidatedRects; + SkRegion m_dirtyRegion; LayerAndroid* m_contentLayer; ScrollableLayerAndroid* m_foregroundLayer; diff --git a/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp index 5807f87..bbde998 100644 --- a/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp @@ -31,6 +31,7 @@ #include "ColorSpace.h" #include "GraphicsContext.h" #include "NotImplemented.h" +#include "PlatformBridge.h" #include "PlatformGraphicsContext.h" #include "SkBitmapRef.h" #include "SkCanvas.h" @@ -53,8 +54,13 @@ ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, RenderingMode, bool& s : m_data(size) , m_size(size) { - m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); - success = true; + // GraphicsContext creates a 32bpp SkBitmap, so 4 bytes per pixel. + if (!PlatformBridge::canSatisfyMemoryAllocation(size.width() * size.height() * 4)) + success = false; + else { + m_context.set(GraphicsContext::createOffscreenContext(size.width(), size.height())); + success = true; + } } ImageBuffer::~ImageBuffer() @@ -74,10 +80,14 @@ bool ImageBuffer::drawsUsingCopy() const PassRefPtr<Image> ImageBuffer::copyImage() const { ASSERT(context()); + SkCanvas* canvas = context()->platformContext()->mCanvas; SkDevice* device = canvas->getDevice(); const SkBitmap& orig = device->accessBitmap(false); + if (!PlatformBridge::canSatisfyMemoryAllocation(orig.getSize())) + return 0; + SkBitmap copy; orig.copyTo(©, orig.config()); diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp index 97ff692..4b24961 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -78,6 +78,7 @@ LayerAndroid::LayerAndroid(RenderLayer* owner) : Layer(), m_preserves3D = false; m_dirty = false; m_iframeOffset.set(0,0); + m_dirtyRegion.setEmpty(); #ifdef DEBUG_COUNT ClassTracker::instance()->increment("LayerAndroid"); #endif @@ -120,6 +121,7 @@ LayerAndroid::LayerAndroid(const LayerAndroid& layer) : Layer(layer), m_childrenTransform = layer.m_childrenTransform; m_dirty = layer.m_dirty; m_pictureUsed = layer.m_pictureUsed; + m_dirtyRegion = layer.m_dirtyRegion; m_scale = layer.m_scale; m_lastComputeTextureSize = 0; @@ -155,6 +157,7 @@ LayerAndroid::LayerAndroid(SkPicture* picture) : Layer(), m_dirty = false; SkSafeRef(m_recordingPicture); m_iframeOffset.set(0,0); + m_dirtyRegion.setEmpty(); #ifdef DEBUG_COUNT ClassTracker::instance()->increment("LayerAndroid"); #endif @@ -706,26 +709,48 @@ void LayerAndroid::showLayer(int indent) this->getChild(i)->showLayer(indent + 1); } -void LayerAndroid::assignTexture(LayerAndroid* oldTree) +// We go through our tree, and if we have layer in the new +// tree that is similar, we tranfer our texture to it. +// Otherwise, we remove ourselves from the texture so +// that TilesManager::swapLayersTextures() have a chance +// at deallocating the textures (PaintedSurfaces) +void LayerAndroid::assignTextureTo(LayerAndroid* newTree) { int count = this->countChildren(); for (int i = 0; i < count; i++) - this->getChild(i)->assignTexture(oldTree); + this->getChild(i)->assignTextureTo(newTree); - if (oldTree) { - LayerAndroid* oldLayer = oldTree->findById(uniqueId()); - if (oldLayer == this) + if (newTree) { + LayerAndroid* newLayer = newTree->findById(uniqueId()); + if (newLayer == this) return; - - if (oldLayer && oldLayer->texture()) { - oldLayer->texture()->replaceLayer(this); - m_texture = oldLayer->texture(); - SkSafeRef(m_texture); + if (newLayer && m_texture) { + m_texture->replaceLayer(newLayer); + newLayer->m_texture = m_texture; + SkSafeRef(newLayer->m_texture); + } + if (!newLayer && m_texture) { + m_texture->removeLayer(this); + m_texture = 0; } } +} + +void LayerAndroid::createTexture() +{ + int count = this->countChildren(); + for (int i = 0; i < count; i++) + this->getChild(i)->createTexture(); - if (needsTexture() && !m_texture) + if (!needsTexture()) + return; + + if (!m_texture) m_texture = new PaintedSurface(this); + + // pass the invalidated regions to the PaintedSurface + m_texture->markAsDirty(m_dirtyRegion); + m_dirtyRegion.setEmpty(); } static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b) @@ -733,6 +758,24 @@ static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b) return a->zValue() > b->zValue(); } +void LayerAndroid::markAsDirty(const SkRegion& dirtyArea) +{ + m_dirtyRegion.op(dirtyArea, SkRegion::kUnion_Op); +} + +// We call this in WebViewCore, when copying the tree of layers. +// As we construct a new tree that will be passed on the UI, +// we mark the webkit-side tree as having no more dirty region +// (otherwise we would continuously have those dirty region UI-side) +void LayerAndroid::clearDirtyRegion() +{ + int count = this->countChildren(); + for (int i = 0; i < count; i++) + this->getChild(i)->clearDirtyRegion(); + + m_dirtyRegion.setEmpty(); +} + bool LayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix) { TilesManager::instance()->shader()->clip(m_clippingRect); diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.h b/Source/WebCore/platform/graphics/android/LayerAndroid.h index c4ed9fe..a28f1e7 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.h @@ -27,6 +27,7 @@ #include "RefPtr.h" #include "SkBitmap.h" #include "SkColor.h" +#include "SkRegion.h" #include "SkStream.h" #include "TextureOwner.h" #include "TransformationMatrix.h" @@ -237,6 +238,11 @@ public: void needsRepaint() { m_pictureUsed++; } unsigned int pictureUsed() { return m_pictureUsed; } + + void markAsDirty(const SkRegion& dirtyArea); + void clearDirtyRegion(); + const SkRegion& dirtyRegion() { return m_dirtyRegion; } + void contentDraw(SkCanvas*); void extraDraw(SkCanvas*); @@ -252,7 +258,8 @@ public: friend LayerAndroid* android::deserializeLayer(SkStream* stream); PaintedSurface* texture() { return m_texture; } - void assignTexture(LayerAndroid* oldTree); + void assignTextureTo(LayerAndroid* newTree); + void createTexture(); protected: virtual void onDraw(SkCanvas*, SkScalar opacity); @@ -336,6 +343,9 @@ private: bool m_dirty; unsigned int m_pictureUsed; + // dirty regions + SkRegion m_dirtyRegion; + // used to signal the framework we need a repaint bool m_hasRunningAnimations; diff --git a/Source/WebCore/platform/graphics/android/MediaTexture.cpp b/Source/WebCore/platform/graphics/android/MediaTexture.cpp index 3fecfb5..96d136a 100644 --- a/Source/WebCore/platform/graphics/android/MediaTexture.cpp +++ b/Source/WebCore/platform/graphics/android/MediaTexture.cpp @@ -104,10 +104,20 @@ void MediaTexture::drawContent(const TransformationMatrix& matrix) m_surfaceTexture->updateTexImage(); - bool forceBlending = ANativeWindow_getFormat(m_surfaceTextureClient.get()) == WINDOW_FORMAT_RGB_565; + sp<GraphicBuffer> buf = m_surfaceTexture->getCurrentBuffer(); + + PixelFormat f = buf->getPixelFormat(); + // only attempt to use alpha blending if alpha channel exists + bool forceAlphaBlending = !( + PIXEL_FORMAT_RGBX_8888 == f || + PIXEL_FORMAT_RGB_888 == f || + PIXEL_FORMAT_RGB_565 == f || + PIXEL_FORMAT_RGB_332 == f); + TilesManager::instance()->shader()->drawLayerQuad(matrix, m_dimensions, m_textureId, 1.0f, - forceBlending, GL_TEXTURE_EXTERNAL_OES); + forceAlphaBlending, + GL_TEXTURE_EXTERNAL_OES); } void MediaTexture::drawVideo(const TransformationMatrix& matrix, const SkRect& parentBounds) diff --git a/Source/WebCore/platform/graphics/android/PaintedSurface.cpp b/Source/WebCore/platform/graphics/android/PaintedSurface.cpp index fe7044c..306eeb5 100644 --- a/Source/WebCore/platform/graphics/android/PaintedSurface.cpp +++ b/Source/WebCore/platform/graphics/android/PaintedSurface.cpp @@ -60,6 +60,7 @@ void PaintedSurface::removeLayer(LayerAndroid* layer) android::Mutex::Autolock lock(m_layerLock); if (m_layer != layer) return; + SkSafeUnref(m_layer); m_layer = 0; } @@ -72,6 +73,8 @@ void PaintedSurface::replaceLayer(LayerAndroid* layer) if (m_layer && layer->uniqueId() != m_layer->uniqueId()) return; + SkSafeRef(layer); + SkSafeUnref(m_layer); m_layer = layer; } @@ -105,9 +108,6 @@ void PaintedSurface::prepare(GLWebViewState* state) XLOG("layer %d %x prepared at size (%d, %d) @ scale %.2f", m_layer->uniqueId(), m_layer, w, h, scale); - if (!m_tiledTexture) - m_tiledTexture = new TiledTexture(this); - m_tiledTexture->prepare(state, m_pictureUsed != m_layer->pictureUsed()); } @@ -136,10 +136,16 @@ void PaintedSurface::endPaint() m_layerLock.unlock(); } +void PaintedSurface::markAsDirty(const SkRegion& dirtyArea) +{ + m_tiledTexture->markAsDirty(dirtyArea); +} + bool PaintedSurface::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed) { m_layerLock.lock(); LayerAndroid* layer = m_layer; + SkSafeRef(layer); m_layerLock.unlock(); if (!layer) @@ -148,6 +154,7 @@ bool PaintedSurface::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictu layer->contentDraw(canvas); m_pictureUsed = layer->pictureUsed(); *pictureUsed = m_pictureUsed; + SkSafeUnref(layer); return true; } @@ -156,10 +163,13 @@ void PaintedSurface::paintExtra(SkCanvas* canvas) { m_layerLock.lock(); LayerAndroid* layer = m_layer; + SkSafeRef(layer); m_layerLock.unlock(); if (layer) layer->extraDraw(canvas); + + SkSafeUnref(layer); } float PaintedSurface::opacity() { diff --git a/Source/WebCore/platform/graphics/android/PaintedSurface.h b/Source/WebCore/platform/graphics/android/PaintedSurface.h index 84fe64c..ffb609e 100644 --- a/Source/WebCore/platform/graphics/android/PaintedSurface.h +++ b/Source/WebCore/platform/graphics/android/PaintedSurface.h @@ -38,6 +38,7 @@ #include "TransformationMatrix.h" class SkCanvas; +class SkRegion; namespace WebCore { @@ -51,12 +52,15 @@ public: , m_busy(false) { TilesManager::instance()->addPaintedSurface(this); + SkSafeRef(m_layer); #ifdef DEBUG_COUNT ClassTracker::instance()->increment("PaintedSurface"); #endif + m_tiledTexture = new TiledTexture(this); } virtual ~PaintedSurface() { + SkSafeUnref(m_layer); #ifdef DEBUG_COUNT ClassTracker::instance()->decrement("PaintedSurface"); #endif @@ -68,6 +72,7 @@ public: LayerAndroid* layer() { return m_layer; } void prepare(GLWebViewState*); bool draw(); + void markAsDirty(const SkRegion& dirtyArea); bool paint(SkCanvas*); void removeLayer(LayerAndroid* layer); void replaceLayer(LayerAndroid* layer); diff --git a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h index 98fcc49..6d08d44 100644 --- a/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h +++ b/Source/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -107,7 +107,7 @@ public: // re-record to the subpicture, so the master picture will reflect the // change. void updateFocusState(WebCore::RenderSkinAndroid::State state, - const WebCore::RenderSkinButton* buttonSkin) + WebCore::RenderSkinButton* buttonSkin) { if (state == m_state) return; diff --git a/Source/WebCore/platform/graphics/android/ShaderProgram.cpp b/Source/WebCore/platform/graphics/android/ShaderProgram.cpp index c9d2d56..bf5f760 100644 --- a/Source/WebCore/platform/graphics/android/ShaderProgram.cpp +++ b/Source/WebCore/platform/graphics/android/ShaderProgram.cpp @@ -81,37 +81,6 @@ static const char gVideoFragmentShader[] = " gl_FragColor = texture2D(s_yuvTexture, v_texCoord);\n" "}\n"; -// In the long run, the gSurfaceTextureOESFragmentShader is the official way of -// doing Surface Texture for RGBA format. -// Now since the driver is not ready for it yet, we had to support both to be -// ready for the switch. -// TODO: remove SurfaceTexture2D support after switching to OES method. -static const char gSurfaceTexture2DFragmentShader[] = - "#extension GL_OES_EGL_image_external : require\n" - "precision mediump float;\n" - "varying vec2 v_texCoord; \n" - "uniform float alpha; \n" - "uniform sampler2D s_texture; \n" - "void main() {\n" - " gl_FragColor = texture2D(s_texture, v_texCoord); \n" - " gl_FragColor *= alpha; " - "}\n"; - -static const char gSurfaceTexture2DFragmentShaderInverted[] = - "#extension GL_OES_EGL_image_external : require\n" - "precision mediump float;\n" - "varying vec2 v_texCoord; \n" - "uniform float alpha; \n" - "uniform sampler2D s_texture; \n" - "void main() {\n" - " gl_FragColor = texture2D(s_texture, v_texCoord); \n" - " float color = 1.0 - (gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3.0; \n" - " gl_FragColor.r = color; \n" - " gl_FragColor.g = color; \n" - " gl_FragColor.b = color; \n" - " gl_FragColor *= alpha; " - "}\n"; - static const char gSurfaceTextureOESFragmentShader[] = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" @@ -218,10 +187,6 @@ void ShaderProgram::init() { m_program = createProgram(gVertexShader, gFragmentShader); m_videoProgram = createProgram(gVideoVertexShader, gVideoFragmentShader); - m_surfTex2DProgram = - createProgram(gVertexShader, gSurfaceTexture2DFragmentShader); - m_surfTex2DProgramInverted = - createProgram(gVertexShader, gSurfaceTexture2DFragmentShaderInverted); m_surfTexOESProgram = createProgram(gVertexShader, gSurfaceTextureOESFragmentShader); m_surfTexOESProgramInverted = @@ -229,8 +194,6 @@ void ShaderProgram::init() if (m_program == -1 || m_videoProgram == -1 - || m_surfTex2DProgram == -1 - || m_surfTex2DProgramInverted == -1 || m_surfTexOESProgram == -1 || m_surfTexOESProgramInverted == -1) return; @@ -246,18 +209,6 @@ void ShaderProgram::init() m_hVideoTexSampler = glGetUniformLocation(m_videoProgram, "s_yuvTexture"); m_hVideoPosition = glGetAttribLocation(m_program, "vPosition"); - m_hST2DProjectionMatrix = - glGetUniformLocation(m_surfTex2DProgram, "projectionMatrix"); - m_hST2DAlpha = glGetUniformLocation(m_surfTex2DProgram, "alpha"); - m_hST2DTexSampler = glGetUniformLocation(m_surfTex2DProgram, "s_texture"); - m_hST2DPosition = glGetAttribLocation(m_surfTex2DProgram, "vPosition"); - - m_hST2DProjectionMatrixInverted = - glGetUniformLocation(m_surfTex2DProgramInverted, "projectionMatrix"); - m_hST2DAlphaInverted = glGetUniformLocation(m_surfTex2DProgramInverted, "alpha"); - m_hST2DTexSamplerInverted = glGetUniformLocation(m_surfTex2DProgramInverted, "s_texture"); - m_hST2DPositionInverted = glGetAttribLocation(m_surfTex2DProgramInverted, "vPosition"); - m_hSTOESProjectionMatrix = glGetUniformLocation(m_surfTexOESProgram, "projectionMatrix"); m_hSTOESAlpha = glGetUniformLocation(m_surfTexOESProgram, "alpha"); @@ -399,18 +350,6 @@ void ShaderProgram::drawQuad(SkRect& geometry, int textureId, float opacity, m_hSTOESTexSamplerInverted, GL_TEXTURE_EXTERNAL_OES, m_hSTOESPositionInverted, m_hSTOESAlphaInverted, m_hSTOESContrastInverted); - } else if (!textureTarget - && !TilesManager::instance()->invertedScreen()) { - drawQuadInternal(geometry, textureId, opacity, m_surfTex2DProgram, - m_hST2DProjectionMatrix, - m_hST2DTexSampler, GL_TEXTURE_2D, - m_hST2DPosition, m_hST2DAlpha); - } else if (!textureTarget - && TilesManager::instance()->invertedScreen()) { - drawQuadInternal(geometry, textureId, opacity, m_surfTex2DProgramInverted, - m_hST2DProjectionMatrixInverted, - m_hST2DTexSamplerInverted, GL_TEXTURE_2D, - m_hST2DPositionInverted, m_hST2DAlphaInverted); } GLUtils::checkGlError("drawQuad"); } @@ -597,18 +536,6 @@ void ShaderProgram::drawLayerQuad(const TransformationMatrix& drawMatrix, m_hSTOESProjectionMatrixInverted, m_hSTOESTexSamplerInverted, m_hSTOESPositionInverted, m_hSTOESAlphaInverted, m_hSTOESContrastInverted); - } else if (!textureTarget - && !TilesManager::instance()->invertedScreen()) { - drawLayerQuadInternal(projectionMatrix, textureId, opacity, - GL_TEXTURE_2D, m_surfTex2DProgram, - m_hST2DProjectionMatrix, m_hST2DTexSampler, - m_hST2DPosition, m_hST2DAlpha); - } else if (!textureTarget - && TilesManager::instance()->invertedScreen()) { - drawLayerQuadInternal(projectionMatrix, textureId, opacity, - GL_TEXTURE_2D, m_surfTex2DProgramInverted, - m_hST2DProjectionMatrixInverted, m_hST2DTexSamplerInverted, - m_hST2DPositionInverted, m_hST2DAlphaInverted); } setBlendingState(forceBlending || opacity < 1.0); diff --git a/Source/WebCore/platform/graphics/android/ShaderProgram.h b/Source/WebCore/platform/graphics/android/ShaderProgram.h index 5c1f324..d8447bf 100644 --- a/Source/WebCore/platform/graphics/android/ShaderProgram.h +++ b/Source/WebCore/platform/graphics/android/ShaderProgram.h @@ -110,8 +110,6 @@ private: int m_program; int m_videoProgram; - int m_surfTex2DProgram; - int m_surfTex2DProgramInverted; int m_surfTexOESProgram; int m_surfTexOESProgramInverted; @@ -135,16 +133,6 @@ private: int m_hVideoTextureMatrix; int m_hVideoTexSampler; - GLint m_hST2DProjectionMatrix; - GLint m_hST2DAlpha; - GLint m_hST2DTexSampler; - GLint m_hST2DPosition; - - GLint m_hST2DProjectionMatrixInverted; - GLint m_hST2DAlphaInverted; - GLint m_hST2DTexSamplerInverted; - GLint m_hST2DPositionInverted; - GLint m_hSTOESProjectionMatrix; GLint m_hSTOESAlpha; GLint m_hSTOESTexSampler; diff --git a/Source/WebCore/platform/graphics/android/TextureInfo.h b/Source/WebCore/platform/graphics/android/TextureInfo.h index fda85da..1c48937 100644 --- a/Source/WebCore/platform/graphics/android/TextureInfo.h +++ b/Source/WebCore/platform/graphics/android/TextureInfo.h @@ -61,7 +61,6 @@ public: void copyAttributes(const TextureInfo* sourceTexture); SharedTextureMode getSharedTextureMode() { return m_sharedTextureMode; } - GLenum getTextureTarget(); bool operator==(const TextureInfo& otherTexture); GLuint m_textureId; diff --git a/Source/WebCore/platform/graphics/android/TextureOwner.h b/Source/WebCore/platform/graphics/android/TextureOwner.h index 35d3389..d0c60fb 100644 --- a/Source/WebCore/platform/graphics/android/TextureOwner.h +++ b/Source/WebCore/platform/graphics/android/TextureOwner.h @@ -42,6 +42,7 @@ public: virtual TiledPage* page() = 0; virtual GLWebViewState* state() = 0; virtual bool samePageAs(Layer* root) { return false; } + virtual bool isRepaintPending() = 0; }; } diff --git a/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp b/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp index 3f7d6a3..0820688 100644 --- a/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp +++ b/Source/WebCore/platform/graphics/android/TexturesGenerator.cpp @@ -148,7 +148,9 @@ QueuedOperation* TexturesGenerator::popNext() mRequestedOperations.remove(i); return next; } - if (nextPriority < currentPriority) { + // pick items preferrably by priority, or if equal, by order of + // insertion (as we add items at the back of the queue) + if (nextPriority <= currentPriority) { current = next; currentPriority = nextPriority; currentIndex = i; diff --git a/Source/WebCore/platform/graphics/android/TiledTexture.cpp b/Source/WebCore/platform/graphics/android/TiledTexture.cpp index 0efcfc3..1aac44d 100644 --- a/Source/WebCore/platform/graphics/android/TiledTexture.cpp +++ b/Source/WebCore/platform/graphics/android/TiledTexture.cpp @@ -69,7 +69,10 @@ void TiledTexture::prepare(GLWebViewState* state, bool repaint) for (unsigned int i = 0; i < m_tiles.size(); i++) { BaseTile* tile = m_tiles[i]; tile->setUsedLevel(-1); + if (!m_dirtyRegion.isEmpty()) + tile->markAsDirty(1, m_dirtyRegion); } + m_dirtyRegion.setEmpty(); if (area.width() == 0 && area.height() == 0) { m_area.setWidth(0); @@ -115,6 +118,11 @@ void TiledTexture::prepare(GLWebViewState* state, bool repaint) } } +void TiledTexture::markAsDirty(const SkRegion& dirtyArea) +{ + m_dirtyRegion.op(dirtyArea, SkRegion::kUnion_Op); +} + void TiledTexture::prepareTile(bool repaint, int x, int y) { BaseTile* tile = getTile(x, y); @@ -130,11 +138,7 @@ void TiledTexture::prepareTile(bool repaint, int x, int y) tile->setUsedLevel(0); bool schedule = false; - if (repaint) - tile->fullInval(); - if (!tile->isTileReady()) - schedule = true; - if (repaint || tile->isDirty()) + if (tile->isDirty()) schedule = true; LayerAndroid* layer = m_surface->layer(); @@ -183,7 +187,7 @@ bool TiledTexture::draw() rect.fTop = tile->y() * tileHeight; rect.fRight = rect.fLeft + tileWidth; rect.fBottom = rect.fTop + tileHeight; - XLOG(" - [%d], { painter %x vs %x }, tile %x %d,%d at scale %.2f [ready: %d]", i, this, tile->painter(), tile, tile->x(), tile->y(), tile->scale(), tile->isTileReady()); + XLOG(" - [%d], { painter %x vs %x }, tile %x %d,%d at scale %.2f [ready: %d] dirty: %d", i, this, tile->painter(), tile, tile->x(), tile->y(), tile->scale(), tile->isTileReady(), tile->isDirty()); askRedraw |= !tile->isTileReady(); tile->draw(m_surface->opacity(), rect, m_surface->scale()); #ifdef DEBUG diff --git a/Source/WebCore/platform/graphics/android/TiledTexture.h b/Source/WebCore/platform/graphics/android/TiledTexture.h index 532cc2c..6ebfc05 100644 --- a/Source/WebCore/platform/graphics/android/TiledTexture.h +++ b/Source/WebCore/platform/graphics/android/TiledTexture.h @@ -26,12 +26,13 @@ #ifndef TiledTexture_h #define TiledTexture_h +#include "BaseTile.h" #include "BaseTileTexture.h" #include "ClassTracker.h" #include "IntRect.h" #include "LayerAndroid.h" +#include "SkRegion.h" #include "TextureOwner.h" -#include "BaseTile.h" #include "TilePainter.h" class SkCanvas; @@ -48,6 +49,7 @@ public: , m_prevTileY(0) , m_prevScale(1) { + m_dirtyRegion.setEmpty(); #ifdef DEBUG_COUNT ClassTracker::instance()->increment("TiledTexture"); #endif @@ -64,6 +66,7 @@ public: bool draw(); void prepareTile(bool repaint, int x, int y); + void markAsDirty(const SkRegion& dirtyArea); BaseTile* getTile(int x, int y); @@ -82,6 +85,7 @@ private: Vector<BaseTile*> m_tiles; IntRect m_area; + SkRegion m_dirtyRegion; int m_prevTileX; int m_prevTileY; diff --git a/Source/WebCore/platform/graphics/android/TilesManager.cpp b/Source/WebCore/platform/graphics/android/TilesManager.cpp index ba48111..c634be0 100644 --- a/Source/WebCore/platform/graphics/android/TilesManager.cpp +++ b/Source/WebCore/platform/graphics/android/TilesManager.cpp @@ -189,8 +189,22 @@ void TilesManager::resetTextureUsage(TiledPage* page) void TilesManager::swapLayersTextures(LayerAndroid* oldTree, LayerAndroid* newTree) { + if (oldTree) + oldTree->assignTextureTo(newTree); + if (newTree) - newTree->assignTexture(oldTree); + newTree->createTexture(); + + WTF::Vector<PaintedSurface*> collect; + for (unsigned int i = 0; i < m_paintedSurfaces.size(); i++) { + PaintedSurface* surface = m_paintedSurfaces[i]; + if (!surface->layer()) + collect.append(surface); + } + for (unsigned int i = 0; i < collect.size(); i++) { + m_paintedSurfaces.remove(m_paintedSurfaces.find(collect[i])); + SkSafeUnref(collect[i]); + } } void TilesManager::addPaintedSurface(PaintedSurface* surface) @@ -214,6 +228,8 @@ BaseTileTexture* TilesManager::getAvailableTexture(BaseTile* owner) unsigned int max = m_tilesTextures.size(); for (unsigned int i = 0; i < max; i++) { BaseTileTexture* texture = m_tilesTextures[i]; + if (texture->owner() && texture->owner()->isRepaintPending()) + continue; if (!texture->owner() && texture->acquire(owner)) { return texture; } diff --git a/Source/WebCore/platform/graphics/android/TransferQueue.cpp b/Source/WebCore/platform/graphics/android/TransferQueue.cpp index a4fd594..1a377f2 100644 --- a/Source/WebCore/platform/graphics/android/TransferQueue.cpp +++ b/Source/WebCore/platform/graphics/android/TransferQueue.cpp @@ -53,7 +53,8 @@ namespace WebCore { TransferQueue::TransferQueue() - : m_transferQueueIndex(0) + : m_eglSurface(EGL_NO_SURFACE) + , m_transferQueueIndex(0) , m_fboID(0) , m_sharedSurfaceTextureId(0) , m_hasGLContext(true) diff --git a/Source/WebCore/platform/graphics/android/TransferQueue.h b/Source/WebCore/platform/graphics/android/TransferQueue.h index 550623e..291215c 100644 --- a/Source/WebCore/platform/graphics/android/TransferQueue.h +++ b/Source/WebCore/platform/graphics/android/TransferQueue.h @@ -63,12 +63,19 @@ public: bool readyForUpdate(); void interruptTransferQueue(bool); + + void lockQueue() { m_transferQueueItemLocks.lock(); } + void unlockQueue() { m_transferQueueItemLocks.unlock(); } + // This queue can be accessed from UI and TexGen thread, therefore, we need // a lock to protect its access TileTransferData* m_transferQueue; sp<ANativeWindow> m_ANW; + // EGL wrapper around m_ANW for use by the GaneshRenderer + EGLSurface m_eglSurface; + private: bool getHasGLContext(); void setHasGLContext(bool hasContext); diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp index b8e30cb..e757080 100644 --- a/Source/WebCore/rendering/RenderLayerCompositor.cpp +++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp @@ -621,10 +621,10 @@ bool RenderLayerCompositor::overlapsCompositedLayers(OverlapMap& overlapMap, con // bool RenderLayerCompositor::checkForFixedLayers(Vector<RenderLayer*>* list, bool stopAtFixedLayer) { - size_t listSize = list->size(); + int listSize = list->size(); int haveFixedLayer = -1; bool fixedSibling = false; - for (size_t j = 0; j < listSize; ++j) { + for (int j = 0; j < listSize; ++j) { RenderLayer* currentLayer = list->at(j); if (currentLayer->isFixed() && needsToBeComposited(currentLayer)) { haveFixedLayer = j; @@ -644,7 +644,7 @@ bool RenderLayerCompositor::checkForFixedLayers(Vector<RenderLayer*>* list, bool if (stopAtFixedLayer) stop = haveFixedLayer + 1; - for (size_t k = j - 1; k >= stop; --k) { + for (int k = j - 1; k >= stop; --k) { RenderLayer* aLayer = list->at(k); if (aLayer && aLayer->renderer()) { IntRect bounds = aLayer->renderer()->localToAbsoluteQuad( diff --git a/Source/WebCore/storage/StorageAreaImpl.cpp b/Source/WebCore/storage/StorageAreaImpl.cpp index bb2d702..49c291d 100644 --- a/Source/WebCore/storage/StorageAreaImpl.cpp +++ b/Source/WebCore/storage/StorageAreaImpl.cpp @@ -106,7 +106,7 @@ static bool privateBrowsingEnabled(Frame* frame) ASSERT(!frame); return false; #else - return frame->page()->settings()->privateBrowsingEnabled(); + return frame->page() && frame->page()->settings()->privateBrowsingEnabled(); #endif } diff --git a/Source/WebKit/android/RenderSkinAndroid.cpp b/Source/WebKit/android/RenderSkinAndroid.cpp index 9383a9c..4a9ce68 100644 --- a/Source/WebKit/android/RenderSkinAndroid.cpp +++ b/Source/WebKit/android/RenderSkinAndroid.cpp @@ -33,21 +33,30 @@ #include "RenderSkinRadio.h" #include "SkImageDecoder.h" -#include "utils/AssetManager.h" -#include "utils/Asset.h" +#include <utils/AssetManager.h> +#include <utils/Asset.h> namespace WebCore { +String RenderSkinAndroid::s_drawableDirectory = ""; +RenderSkinAndroid::Resolution RenderSkinAndroid::s_drawableResolution = RenderSkinAndroid::MedRes; + RenderSkinAndroid::~RenderSkinAndroid() { delete m_button; } -RenderSkinAndroid::RenderSkinAndroid(android::AssetManager* am, String drawableDirectory) +RenderSkinAndroid::RenderSkinAndroid(String drawableDirectory) { - m_button = new RenderSkinButton(am, drawableDirectory); - RenderSkinCombo::Init(am, drawableDirectory); - RenderSkinMediaButton::Init(am, drawableDirectory); - RenderSkinRadio::Init(am, drawableDirectory); + if (s_drawableDirectory.isEmpty() && !drawableDirectory.isEmpty()) { + s_drawableResolution = MedRes; + if (drawableDirectory.endsWith("-hdpi/")) + s_drawableResolution = HighRes; + else if (drawableDirectory.endsWith("-xhdpi/")) + s_drawableResolution = ExtraHighRes; + + s_drawableDirectory = drawableDirectory; + } + m_button = new RenderSkinButton(drawableDirectory); } bool RenderSkinAndroid::DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap) diff --git a/Source/WebKit/android/RenderSkinAndroid.h b/Source/WebKit/android/RenderSkinAndroid.h index 73773ea..bbc327d 100644 --- a/Source/WebKit/android/RenderSkinAndroid.h +++ b/Source/WebKit/android/RenderSkinAndroid.h @@ -50,11 +50,14 @@ public: kNumStates }; - /** - * Initialize the Android skinning system. The AssetManager may be used to find resources used - * in rendering. - */ - RenderSkinAndroid(android::AssetManager*, String drawableDirectory); + enum Resolution { + MedRes, + HighRes, + ExtraHighRes, + ResolutionCount // Keep at the end + }; + + RenderSkinAndroid(String drawableDirectory); ~RenderSkinAndroid(); /* DecodeBitmap determines which file to use, with the given fileName of the form @@ -63,9 +66,14 @@ public: */ static bool DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap); - const RenderSkinButton* renderSkinButton() const { return m_button; } + static String DrawableDirectory() { return s_drawableDirectory; } + static Resolution DrawableResolution() { return s_drawableResolution; } + + RenderSkinButton* renderSkinButton() const { return m_button; } private: + static String s_drawableDirectory; + static Resolution s_drawableResolution; RenderSkinButton* m_button; }; diff --git a/Source/WebKit/android/RenderSkinButton.cpp b/Source/WebKit/android/RenderSkinButton.cpp index 6a0ae54..11e2fa8 100644 --- a/Source/WebKit/android/RenderSkinButton.cpp +++ b/Source/WebKit/android/RenderSkinButton.cpp @@ -42,6 +42,8 @@ #include <utils/ResourceTypes.h> #include <wtf/text/CString.h> +extern android::AssetManager* globalAssetManager(); + static const char* gFiles[] = { "btn_default_disabled_holo.9.png", "btn_default_normal_holo.9.png", @@ -51,29 +53,42 @@ static const char* gFiles[] = { namespace WebCore { -RenderSkinButton::RenderSkinButton(android::AssetManager* am, String drawableDirectory) +RenderSkinButton::RenderSkinButton(String drawableDirectory) + : m_decoded(false) + , m_decodingAttempted(false) + , m_drawableDirectory(drawableDirectory) { - m_decoded = true; + // Ensure our enums properly line up with our arrays. + android::CompileTimeAssert<(RenderSkinAndroid::kDisabled == 0)> a1; + android::CompileTimeAssert<(RenderSkinAndroid::kNormal == 1)> a2; + android::CompileTimeAssert<(RenderSkinAndroid::kFocused == 2)> a3; + android::CompileTimeAssert<(RenderSkinAndroid::kPressed == 3)> a4; +} + +void RenderSkinButton::decode() +{ + m_decodingAttempted = true; + + android::AssetManager* am = globalAssetManager(); + for (size_t i = 0; i < 4; i++) { - String path = String(drawableDirectory.impl()); + String path = m_drawableDirectory; path.append(String(gFiles[i])); if (!RenderSkinNinePatch::decodeAsset(am, path.utf8().data(), &m_buttons[i])) { m_decoded = false; - LOGE("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw"); + LOGE("RenderSkinButton::decode: button assets failed to decode\n\tWebView buttons will not draw"); return; } } - - // Ensure our enums properly line up with our arrays. - android::CompileTimeAssert<(RenderSkinAndroid::kDisabled == 0)> a1; - android::CompileTimeAssert<(RenderSkinAndroid::kNormal == 1)> a2; - android::CompileTimeAssert<(RenderSkinAndroid::kFocused == 2)> a3; - android::CompileTimeAssert<(RenderSkinAndroid::kPressed == 3)> a4; + m_decoded = true; } void RenderSkinButton::draw(SkCanvas* canvas, const IntRect& r, - RenderSkinAndroid::State newState) const + RenderSkinAndroid::State newState) { + if (!m_decodingAttempted) + decode(); + // If we failed to decode, do nothing. This way the browser still works, // and webkit will still draw the label and layout space for us. if (!m_decoded) { diff --git a/Source/WebKit/android/RenderSkinButton.h b/Source/WebKit/android/RenderSkinButton.h index e9db74c..83c57dd 100644 --- a/Source/WebKit/android/RenderSkinButton.h +++ b/Source/WebKit/android/RenderSkinButton.h @@ -36,19 +36,19 @@ class IntRect; class RenderSkinButton { public: - /** - * Initialize the class before use. Uses the AssetManager to initialize any - * bitmaps the class may use. - */ - RenderSkinButton(android::AssetManager*, String drawableDirectory); + RenderSkinButton(String drawableDirectory); /** * Draw the skin to the canvas, using the rectangle for its bounds and the * State to determine which skin to use, i.e. focused or not focused. */ - void draw(SkCanvas* , const IntRect& , RenderSkinAndroid::State) const; + void draw(SkCanvas* , const IntRect& , RenderSkinAndroid::State); + + void decode(); private: bool m_decoded; + bool m_decodingAttempted; NinePatch m_buttons[4]; + String m_drawableDirectory; }; } // WebCore diff --git a/Source/WebKit/android/RenderSkinCombo.cpp b/Source/WebKit/android/RenderSkinCombo.cpp index b30dc29..970e093 100644 --- a/Source/WebKit/android/RenderSkinCombo.cpp +++ b/Source/WebKit/android/RenderSkinCombo.cpp @@ -33,20 +33,23 @@ #include "RenderStyle.h" #include "SkCanvas.h" #include "SkNinePatch.h" +#include <utils/AssetManager.h> #include <wtf/text/CString.h> +extern android::AssetManager* globalAssetManager(); + namespace WebCore { // Indicates if the entire asset is being drawn, or if the border is being // excluded and just the arrow drawn. enum BorderStyle { FullAsset, - NoBorder + NoBorder, + BorderStyleCount // Keep at the end. }; // There are 2.5 different concepts of a 'border' here, which results -// in rather a lot of magic constants. In each case, there are 2 -// numbers, one for medium res and one for high-res. All sizes are in pixels. +// in rather a lot of magic constants. // Firstly, we have the extra padding that webkit needs to know about, // which defines how much bigger this element is made by the @@ -54,17 +57,35 @@ enum BorderStyle { // asset, to make things look less cramped. The border is the same // width on all sides, except on the right when it's significantly // wider to allow for the arrow. -const int RenderSkinCombo::arrowMargin[2] = {22, 34}; -const int RenderSkinCombo::padMargin[2] = {2, 5}; +const int RenderSkinCombo::arrowMargin[ResolutionCount] = { + 22, // Medium resolution + 34, // High resolution + 46 // Extra high resolution +}; +const int RenderSkinCombo::padMargin[ResolutionCount] = { + 2, // Medium resolution + 5, // High resolution + 6 // Extra high resolution +}; +namespace { // Then we have the borders used for the 9-patch stretch. The // rectangle at the centre of these borders is entirely below and to // the left of the arrow in the asset. Hence the border widths are the // same for the bottom and left, but are different for the top. The // right hand border width happens to be the same as arrowMargin // defined above. -static const int stretchMargin[2] = {3, 5}; // border width for the bottom and left of the 9-patch -static const int stretchTop[2] = {15, 23}; // border width for the top of the 9-patch +const int stretchMargin[RenderSkinAndroid::ResolutionCount] = { // border width for the bottom and left of the 9-patch + 3, // Medium resolution + 5, // High resolution + 6 // Extra high resolution + +}; +const int stretchTop[RenderSkinAndroid::ResolutionCount] = { // border width for the top of the 9-patch + 15, // Medium resolution + 23, // High resolution + 34 // Extra high resolution +}; // Finally, if the border is defined by the CSS, we only draw the // arrow and not the border. We do this by drawing the relevant subset @@ -73,46 +94,71 @@ static const int stretchTop[2] = {15, 23}; // border width for the to // spaced. The border to remove at the top, right and bottom of the // image is the same as stretchMargin above, but we need to know the width // of the arrow. -static const int arrowWidth[2] = {22, 31}; +const int arrowWidth[RenderSkinAndroid::ResolutionCount] = { + 22, // Medium resolution + 31, // High resolution + 42 // Extra high resolution +}; -RenderSkinCombo::Resolution RenderSkinCombo::resolution = MedRes; +// Store the calculated 9 patch margins for each border style. +SkIRect margin[BorderStyleCount]; -const SkIRect RenderSkinCombo::margin[2][2] = {{{ stretchMargin[MedRes], stretchTop[MedRes], - RenderSkinCombo::arrowMargin[MedRes] + stretchMargin[MedRes], stretchMargin[MedRes] }, - {0, stretchTop[MedRes], 0, stretchMargin[MedRes]}}, - {{ stretchMargin[HighRes], stretchTop[HighRes], - RenderSkinCombo::arrowMargin[HighRes] + stretchMargin[HighRes], stretchMargin[HighRes] }, - {0, stretchTop[HighRes], 0, stretchMargin[HighRes]}}}; -static SkBitmap bitmaps[2][2]; // Collection of assets for a combo box -static bool isDecoded; // True if all assets were decoded +SkBitmap bitmaps[2][BorderStyleCount]; // Collection of assets for a combo box - 2 states (enabled/disabled) +bool isDecodingAttempted = false; // True if we've tried to decode the assets +bool isDecoded = false; // True if all assets were decoded -void RenderSkinCombo::Init(android::AssetManager* am, String drawableDirectory) +} // namespace + +void RenderSkinCombo::Decode() { - if (isDecoded) + if (isDecodingAttempted) return; - if (drawableDirectory[drawableDirectory.length() - 5] == 'h') - resolution = HighRes; + isDecodingAttempted = true; + isDecoded = false; + + android::AssetManager* am = globalAssetManager(); + + String drawableDirectory = RenderSkinAndroid::DrawableDirectory(); + Resolution res = RenderSkinAndroid::DrawableResolution(); isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]); isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]); int width = bitmaps[kNormal][FullAsset].width(); int height = bitmaps[kNormal][FullAsset].height(); - SkIRect subset; - subset.set(width - arrowWidth[resolution], 0, width, height); + SkIRect subset; + subset.set(width - arrowWidth[res], 0, width, height); bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset); bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset); -} + // Calculate 9 patch margins. + SkIRect fullAssetMargin; + fullAssetMargin.fLeft = stretchMargin[res]; + fullAssetMargin.fTop = stretchTop[res]; + fullAssetMargin.fRight = arrowMargin[res] + stretchMargin[res]; + fullAssetMargin.fBottom = stretchMargin[res]; + + SkIRect noBorderMargin; + noBorderMargin.fLeft = 0; + noBorderMargin.fTop = stretchTop[res]; + noBorderMargin.fRight = 0; + noBorderMargin.fBottom = stretchMargin[res]; + + margin[FullAsset] = fullAssetMargin; + margin[NoBorder] = noBorderMargin; +} bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height) { + if (!isDecodingAttempted) + Decode(); + if (!isDecoded) return true; State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled; - height = std::max(height, (stretchMargin[resolution]<<1) + 1); + height = std::max(height, (stretchMargin[RenderSkinAndroid::DrawableResolution()]<<1) + 1); SkRect bounds; BorderStyle drawBorder = FullAsset; @@ -139,8 +185,8 @@ bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int wi bounds.fBottom -= SkIntToScalar(style->borderBottomWidth()); drawBorder = NoBorder; } - SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[resolution][drawBorder]); + SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[drawBorder]); return false; } -} //WebCore +} // namspace WebCore diff --git a/Source/WebKit/android/RenderSkinCombo.h b/Source/WebKit/android/RenderSkinCombo.h index 38cd048..4814199 100644 --- a/Source/WebKit/android/RenderSkinCombo.h +++ b/Source/WebKit/android/RenderSkinCombo.h @@ -37,11 +37,8 @@ namespace WebCore { class RenderSkinCombo : public RenderSkinAndroid { public: - /** - * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use. - */ - static void Init(android::AssetManager*, String drawableDirectory); + static void Decode(); /** * Draw the provided Node on the SkCanvas, using the dimensions provided by * x,y,w,h. Return true if we did not draw, and WebKit needs to draw it, @@ -50,18 +47,13 @@ public: static bool Draw(SkCanvas* , Node* , int x, int y, int w, int h); // The image is wider than the RenderObject, so this accounts for that. - static int extraWidth() { return arrowMargin[resolution]; } - static int padding() { return padMargin[resolution]; } + static int extraWidth() { return arrowMargin[RenderSkinAndroid::DrawableResolution()]; } + static int padding() { return padMargin[RenderSkinAndroid::DrawableResolution()]; } + - enum Resolution { - MedRes, - HighRes - }; private: - static Resolution resolution; - const static int arrowMargin[2]; - const static int padMargin[2]; - const static SkIRect margin[2][2]; + const static int arrowMargin[ResolutionCount]; + const static int padMargin[ResolutionCount]; }; } // WebCore diff --git a/Source/WebKit/android/RenderSkinMediaButton.cpp b/Source/WebKit/android/RenderSkinMediaButton.cpp index 090d55e..294dec5 100644 --- a/Source/WebKit/android/RenderSkinMediaButton.cpp +++ b/Source/WebKit/android/RenderSkinMediaButton.cpp @@ -36,10 +36,13 @@ #include "SkCanvas.h" #include "SkNinePatch.h" #include "SkRect.h" +#include <utils/AssetManager.h> #include <utils/Debug.h> #include <utils/Log.h> #include <wtf/text/CString.h> +extern android::AssetManager* globalAssetManager(); + struct PatchData { const char* name; int8_t outset, margin; @@ -64,23 +67,21 @@ static const PatchData gFiles[] = static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])]; static bool gDecoded; -static bool gHighRes; +static bool gDecodingFailed; namespace WebCore { -void RenderSkinMediaButton::Init(android::AssetManager* am, String drawableDirectory) +void RenderSkinMediaButton::Decode() { - static bool gInited; - if (gInited) - return; + String drawableDirectory = RenderSkinAndroid::DrawableDirectory(); - gInited = true; gDecoded = true; - gHighRes = drawableDirectory[drawableDirectory.length() - 5] == 'h'; + gDecodingFailed = false; + android::AssetManager* am = globalAssetManager(); for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) { String path = drawableDirectory + gFiles[i].name; if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) { - gDecoded = false; + gDecodingFailed = true; LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw"); break; } @@ -90,11 +91,14 @@ void RenderSkinMediaButton::Init(android::AssetManager* am, String drawableDirec void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonType, bool translucent, RenderObject* o) { + if (!gDecoded) { + Decode(); + } + // If we failed to decode, do nothing. This way the browser still works, // and webkit will still draw the label and layout space for us. - if (!gDecoded) { + if (gDecodingFailed) return; - } bool drawsNinePatch = false; bool drawsImage = true; diff --git a/Source/WebKit/android/RenderSkinMediaButton.h b/Source/WebKit/android/RenderSkinMediaButton.h index 6aa9c4e..d8b7c8d 100644 --- a/Source/WebKit/android/RenderSkinMediaButton.h +++ b/Source/WebKit/android/RenderSkinMediaButton.h @@ -36,11 +36,7 @@ class RenderObject; class RenderSkinMediaButton { public: - /** - * Initialize the class before use. Uses the AssetManager to initialize any - * bitmaps the class may use. - */ - static void Init(android::AssetManager*, String drawableDirectory); + static void Decode(); /** * Draw the skin to the canvas, using the rectangle for its bounds and the * State to determine which skin to use, i.e. focused or not focused. diff --git a/Source/WebKit/android/RenderSkinNinePatch.cpp b/Source/WebKit/android/RenderSkinNinePatch.cpp index 074a65a..faa9dc4 100644 --- a/Source/WebKit/android/RenderSkinNinePatch.cpp +++ b/Source/WebKit/android/RenderSkinNinePatch.cpp @@ -20,6 +20,7 @@ #include "NinePatchPeeker.h" #include "SkCanvas.h" #include "SkImageDecoder.h" +#include "SkNinePatch.h" #include "SkRect.h" #include "SkStream.h" #include "SkTemplates.h" @@ -85,5 +86,20 @@ bool RenderSkinNinePatch::decodeAsset(AssetManager* am, const char* filename, Ni void RenderSkinNinePatch::DrawNinePatch(SkCanvas* canvas, const SkRect& bounds, const NinePatch& patch) { Res_png_9patch* data = Res_png_9patch::deserialize(patch.m_serializedPatchData); - NinePatch_Draw(canvas, bounds, patch.m_bitmap, *data, 0, 0); + + // if the NinePatch is bigger than the destination on a given axis the default + // decoder will not stretch properly, therefore we fall back to skia's decoder + // which if needed will down-sample and draw the bitmap as best as possible. + if (patch.m_bitmap.width() >= bounds.width() || patch.m_bitmap.height() >= bounds.height()) { + + SkPaint defaultPaint; + // matches default dither in NinePatchDrawable.java. + defaultPaint.setDither(true); + SkNinePatch::DrawMesh(canvas, bounds, patch.m_bitmap, + data->xDivs, data->numXDivs, + data->yDivs, data->numYDivs, + &defaultPaint); + } else { + NinePatch_Draw(canvas, bounds, patch.m_bitmap, *data, 0, 0); + } } diff --git a/Source/WebKit/android/RenderSkinRadio.cpp b/Source/WebKit/android/RenderSkinRadio.cpp index 46198d3..3c29818 100644 --- a/Source/WebKit/android/RenderSkinRadio.cpp +++ b/Source/WebKit/android/RenderSkinRadio.cpp @@ -36,41 +36,54 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkRect.h" +#include <utils/AssetManager.h> #include <wtf/text/CString.h> +extern android::AssetManager* globalAssetManager(); + static const char* checks[] = { "btn_check_off_holo.png", "btn_check_on_holo.png", "btn_radio_off_holo.png", "btn_radio_on_holo.png"}; // Matches the width of the bitmap -static SkScalar SIZE; +static SkScalar s_bitmapWidth; namespace WebCore { static SkBitmap s_bitmap[4]; -static bool s_decoded; +static bool s_decodingAttempted = false; +static bool s_decoded = false; -void RenderSkinRadio::Init(android::AssetManager* am, String drawableDirectory) -{ - if (s_decoded) +void RenderSkinRadio::Decode() { + if (s_decodingAttempted) return; - String path = drawableDirectory + checks[0]; - s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[0]); - path = drawableDirectory + checks[1]; - s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[1]) && s_decoded; - path = drawableDirectory + checks[2]; - s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[2]) && s_decoded; - path = drawableDirectory + checks[3]; - s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[3]) && s_decoded; - SIZE = SkIntToScalar(s_bitmap[0].width()); + + s_decodingAttempted = true; + s_decoded = false; + + android::AssetManager* am = globalAssetManager(); + String drawableDir = RenderSkinAndroid::DrawableDirectory(); + for (int i = 0; i < 4; i++) { + String path = drawableDir + checks[i]; + if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[i])) + return; + } + s_decoded = true; + s_bitmapWidth = SkIntToScalar(s_bitmap[0].width()); } void RenderSkinRadio::Draw(SkCanvas* canvas, Node* element, const IntRect& ir, bool isCheckBox) { - if (!s_decoded || !element) { + if (!element) return; - } + + if (!s_decodingAttempted) + Decode(); + + if (!s_decoded) + return; + SkRect r(ir); // Set up a paint to with filtering to look better. SkPaint paint; @@ -82,7 +95,7 @@ void RenderSkinRadio::Draw(SkCanvas* canvas, Node* element, const IntRect& ir, paint.setAlpha(0x80); } SkScalar width = r.width(); - SkScalar scale = SkScalarDiv(width, SIZE); + SkScalar scale = SkScalarDiv(width, s_bitmapWidth); saveScaleCount = canvas->save(); canvas->translate(r.fLeft, r.fTop); canvas->scale(scale, scale); diff --git a/Source/WebKit/android/RenderSkinRadio.h b/Source/WebKit/android/RenderSkinRadio.h index f77e1be..34101cf 100644 --- a/Source/WebKit/android/RenderSkinRadio.h +++ b/Source/WebKit/android/RenderSkinRadio.h @@ -44,10 +44,10 @@ class IntRect; class RenderSkinRadio { public: - /** - * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use. - */ - static void Init(android::AssetManager*, String drawableDirectory); + static void SetDrawableDirectory(String drawableDirectory); + + // Perform lazy decoding the first time this a radio/checkbox is needed. + static void Decode(); /** * Draw the element to the canvas at the specified size and location. diff --git a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp index ae8bec8..339e91b 100644 --- a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp +++ b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp @@ -441,7 +441,7 @@ public: return; m_glue = new JavaGlue; - m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(I)V"); + m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(Landroid/webkit/WebViewCore;I)V"); m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V"); m_glue->m_play = env->GetMethodID(clazz, "play", "()V"); m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F"); @@ -469,10 +469,17 @@ public: if (!clazz) return; + FrameView* frameView = m_player->mediaPlayerClient()->mediaPlayerOwningDocument()->view(); + if (!frameView) + return; + AutoJObject javaObject = WebViewCore::getWebViewCore(frameView)->getJavaObject(); + if (!javaObject.get()) + return; + jobject obj = 0; // Get the HTML5Audio instance - obj = env->NewObject(clazz, m_glue->m_newInstance, this); + obj = env->NewObject(clazz, m_glue->m_newInstance, javaObject.get(), this); m_glue->m_javaProxy = env->NewGlobalRef(obj); // Clean up. diff --git a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp index 8d8d809..27fe208 100644 --- a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp +++ b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp @@ -30,6 +30,7 @@ #include "Document.h" #include "FileSystemClient.h" #include "FrameView.h" +#include "JNIUtility.h" #include "JavaSharedClient.h" #include "KeyGeneratorClient.h" #include "MemoryUsage.h" @@ -229,6 +230,17 @@ int PlatformBridge::actualMemoryUsageMB() return MemoryUsage::memoryUsageMb(true); } +bool PlatformBridge::canSatisfyMemoryAllocation(long bytes) +{ + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); + jmethodID method = env->GetStaticMethodID(bridgeClass, "canSatisfyMemoryAllocation", "(J)Z"); + jboolean canAllocate = env->CallStaticBooleanMethod(bridgeClass, method, static_cast<jlong>(bytes)); + env->DeleteLocalRef(bridgeClass); + + return canAllocate == JNI_TRUE; +} + } // namespace WebCore diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp index 65b6771..e837244 100644 --- a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp +++ b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp @@ -116,7 +116,7 @@ bool IsOptionElement(Element& element) { bool IsAutofillableElement(const HTMLFormControlElement& element) { HTMLInputElement* html_input_element = HTMLFormControlElementToHTMLInputElement(element); - return html_input_element && IsTextInput(html_input_element) || IsSelectElement(element); + return (html_input_element && IsTextInput(html_input_element)) || IsSelectElement(element); } // This is a helper function for the FindChildText() function (see below). diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp index a7f3897..afc251b 100644 --- a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -1390,11 +1390,10 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss if (directory.isEmpty()) LOGE("Can't find the drawable directory"); else { - // Setup the asset manager. - AssetManager* am = assetManagerForJavaObject(env, jAssetManager); // Initialize our skinning classes - webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(am, directory)); + webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(directory)); } + for (int i = WebCore::PlatformBridge::FileUploadLabel; i <= WebCore::PlatformBridge::FileUploadNoFileChosenLabel; i++) initGlobalLocalizedName( diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.h b/Source/WebKit/android/jni/WebCoreFrameBridge.h index f02c1e9..99ca459 100644 --- a/Source/WebKit/android/jni/WebCoreFrameBridge.h +++ b/Source/WebKit/android/jni/WebCoreFrameBridge.h @@ -158,8 +158,8 @@ class WebFrame : public WebCoreRefObject { bool shouldSaveFormData(); void saveFormData(WebCore::HTMLFormElement*); - const WebCore::RenderSkinAndroid* renderSkins() const { return m_renderSkins; } - void setRenderSkins(const WebCore::RenderSkinAndroid* skins) { m_renderSkins = skins; } + WebCore::RenderSkinAndroid* renderSkins() const { return m_renderSkins; } + void setRenderSkins(WebCore::RenderSkinAndroid* skins) { m_renderSkins = skins; } // Convert a URL from potential punycode I18nDomainName to safe to-be-displayed Unicode. static WTF::String convertIDNToUnicode(const WebCore::KURL& kurl); @@ -171,7 +171,7 @@ class WebFrame : public WebCoreRefObject { WTF::String mUserAgent; bool mBlockNetworkLoads; bool mUserInitiatedAction; - const WebCore::RenderSkinAndroid* m_renderSkins; + WebCore::RenderSkinAndroid* m_renderSkins; }; } // namespace android diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index 1c51b14..964a33d 100644 --- a/Source/WebKit/android/jni/WebViewCore.cpp +++ b/Source/WebKit/android/jni/WebViewCore.cpp @@ -912,6 +912,7 @@ BaseLayerAndroid* WebViewCore::createBaseLayer() LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer()); base->addChild(copyLayer); copyLayer->unref(); + root->contentLayer()->clearDirtyRegion(); } #endif diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp index cf6cd65..c1cb95c 100644 --- a/Source/WebKit/android/nav/WebView.cpp +++ b/Source/WebKit/android/nav/WebView.cpp @@ -71,9 +71,7 @@ #include <JNIUtility.h> #include <JNIHelp.h> #include <jni.h> -#include <android_runtime/android_util_AssetManager.h> #include <ui/KeycodeLabels.h> -#include <utils/AssetManager.h> #include <wtf/text/AtomicString.h> #include <wtf/text/CString.h> @@ -141,7 +139,7 @@ struct JavaGlue { } } m_javaGlue; -WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, AssetManager* am) : +WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir) : m_ring((WebViewCore*) viewImpl) { jclass clazz = env->FindClass("android/webkit/WebView"); @@ -194,10 +192,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, m_ringAnimationEnd = 0; m_baseLayer = 0; m_glDrawFunctor = 0; - if (drawableDir.isEmpty()) - m_buttonSkin = 0; - else - m_buttonSkin = new RenderSkinButton(am, drawableDir); + m_buttonSkin = drawableDir.isEmpty() ? 0 : new RenderSkinButton(drawableDir); #if USE(ACCELERATED_COMPOSITING) m_glWebViewState = 0; m_pageSwapCallbackRegistered = false; @@ -1536,7 +1531,7 @@ private: // local state for WebView GLWebViewState* m_glWebViewState; bool m_pageSwapCallbackRegistered; #endif - const RenderSkinButton* m_buttonSkin; + RenderSkinButton* m_buttonSkin; }; // end of WebView class @@ -1653,12 +1648,10 @@ static void nativeClearCursor(JNIEnv *env, jobject obj) view->clearCursor(); } -static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl, jstring drawableDir, - jobject jAssetManager) +static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl, jstring drawableDir) { - AssetManager* am = assetManagerForJavaObject(env, jAssetManager); WTF::String dir = jstringToWtfString(env, drawableDir); - WebView* webview = new WebView(env, obj, viewImpl, dir, am); + WebView* webview = new WebView(env, obj, viewImpl, dir); // NEED THIS OR SOMETHING LIKE IT! //Release(obj); } @@ -2652,7 +2645,7 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeCacheHitNodePointer }, { "nativeClearCursor", "()V", (void*) nativeClearCursor }, - { "nativeCreate", "(ILjava/lang/String;Landroid/content/res/AssetManager;)V", + { "nativeCreate", "(ILjava/lang/String;)V", (void*) nativeCreate }, { "nativeCursorFramePointer", "()I", (void*) nativeCursorFramePointer }, |
