зеркало из https://github.com/mozilla/pjs.git
Merge with mozilla-central
This commit is contained in:
Коммит
f72f4d6343
|
@ -71,5 +71,6 @@ JAVAC_FLAGS = \
|
|||
-bootclasspath $(JAVA_BOOTCLASSPATH) \
|
||||
-encoding UTF8 \
|
||||
-g:source,lines \
|
||||
-Werror \
|
||||
# Still getting warngings against 1.6.0_29/1.7, remove until fixed on m-c
|
||||
#-Werror \
|
||||
$(NULL)
|
||||
|
|
|
@ -1559,7 +1559,7 @@ public class GeckoAppShell
|
|||
Log.i("GeckoShell", "post to " + (mainThread ? "main " : "") + "java thread");
|
||||
getMainHandler().post(new GeckoRunnableCallback());
|
||||
}
|
||||
|
||||
|
||||
public static android.hardware.Camera sCamera = null;
|
||||
|
||||
static native void cameraCallbackBridge(byte[] data);
|
||||
|
|
|
@ -334,4 +334,9 @@ public class GeckoEvent {
|
|||
mBandwidth = bandwidth;
|
||||
mCanBeMetered = canBeMetered;
|
||||
}
|
||||
|
||||
public void doCallback(String json) {
|
||||
// this stub is never called in XUL Fennec, but we need it so that the JNI code
|
||||
// shared between XUL and Native Fennec doesn't die.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,8 +54,11 @@
|
|||
#include "gfxCrashReporterUtils.h"
|
||||
#include "gfxUtils.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Util.h" // for DebugOnly
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
|
@ -388,12 +391,15 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
const char *glRendererString;
|
||||
|
||||
if (mInitialized) {
|
||||
// The order of these strings must match up with the order of the enum
|
||||
// defined in GLContext.h for vendor IDs
|
||||
glVendorString = (const char *)fGetString(LOCAL_GL_VENDOR);
|
||||
const char *vendorMatchStrings[VendorOther] = {
|
||||
"Intel",
|
||||
"NVIDIA",
|
||||
"ATI",
|
||||
"Qualcomm"
|
||||
"Qualcomm",
|
||||
"Imagination"
|
||||
};
|
||||
mVendor = VendorOther;
|
||||
for (int i = 0; i < VendorOther; ++i) {
|
||||
|
@ -403,9 +409,13 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
}
|
||||
}
|
||||
|
||||
// The order of these strings must match up with the order of the enum
|
||||
// defined in GLContext.h for renderer IDs
|
||||
glRendererString = (const char *)fGetString(LOCAL_GL_RENDERER);
|
||||
const char *rendererMatchStrings[RendererOther] = {
|
||||
"Adreno 200"
|
||||
"Adreno 200",
|
||||
"Adreno 205",
|
||||
"PowerVR SGX 540"
|
||||
};
|
||||
mRenderer = RendererOther;
|
||||
for (int i = 0; i < RendererOther; ++i) {
|
||||
|
@ -510,8 +520,14 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
|
|||
fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize);
|
||||
mMaxTextureImageSize = mMaxTextureSize;
|
||||
|
||||
// BGRA ReadPixels support - in particular, what is returned for
|
||||
// GL_IMPLEMENTATION_COLOR_READ_FORMAT et al - is only guaranteed to be
|
||||
// accurate for what's currently bound to the context. It seems that,
|
||||
// after we initialize, we bind something incompatible with the BGRA
|
||||
// pixel format on at least some devices.
|
||||
// For now, just disable this code altogether.
|
||||
mSupport_ES_ReadPixels_BGRA_UByte = false;
|
||||
if (mIsGLES2) {
|
||||
if (false) {
|
||||
if (IsExtensionSupported(gl::GLContext::EXT_bgra)) {
|
||||
mSupport_ES_ReadPixels_BGRA_UByte = true;
|
||||
} else if (IsExtensionSupported(gl::GLContext::EXT_read_format_bgra) ||
|
||||
|
@ -603,19 +619,99 @@ GLContext::InitExtensions()
|
|||
#endif
|
||||
}
|
||||
|
||||
// Take texture data in a given buffer and copy it into a larger buffer,
|
||||
// padding out the edge pixels for filtering if necessary
|
||||
static void
|
||||
CopyAndPadTextureData(const GLvoid* srcBuffer,
|
||||
GLvoid* dstBuffer,
|
||||
GLsizei srcWidth, GLsizei srcHeight,
|
||||
GLsizei dstWidth, GLsizei dstHeight,
|
||||
GLsizei stride, GLint pixelsize)
|
||||
{
|
||||
unsigned char *rowDest = static_cast<unsigned char*>(dstBuffer);
|
||||
const unsigned char *source = static_cast<const unsigned char*>(srcBuffer);
|
||||
|
||||
for (GLsizei h = 0; h < srcHeight; ++h) {
|
||||
memcpy(rowDest, source, srcWidth * pixelsize);
|
||||
rowDest += dstWidth * pixelsize;
|
||||
source += stride;
|
||||
}
|
||||
|
||||
GLsizei padHeight = srcHeight;
|
||||
|
||||
// Pad out an extra row of pixels so that edge filtering doesn't use garbage data
|
||||
if (dstHeight > srcHeight) {
|
||||
memcpy(rowDest, source - stride, srcWidth * pixelsize);
|
||||
padHeight++;
|
||||
}
|
||||
|
||||
// Pad out an extra column of pixels
|
||||
if (dstWidth > srcWidth) {
|
||||
rowDest = static_cast<unsigned char*>(dstBuffer) + srcWidth * pixelsize;
|
||||
for (GLsizei h = 0; h < padHeight; ++h) {
|
||||
memcpy(rowDest, rowDest - pixelsize, pixelsize);
|
||||
rowDest += dstWidth * pixelsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GLContext::IsExtensionSupported(const char *extension)
|
||||
{
|
||||
return ListHasExtension(fGetString(LOCAL_GL_EXTENSIONS), extension);
|
||||
}
|
||||
|
||||
// In both of these cases (for the Adreno at least) it is impossible
|
||||
// to determine good or bad driver versions for POT texture uploads,
|
||||
// so blacklist them all. Newer drivers use a different rendering
|
||||
// string in the form "Adreno (TM) 200" and the drivers we've seen so
|
||||
// far work fine with NPOT textures, so don't blacklist those until we
|
||||
// have evidence of any problems with them.
|
||||
bool
|
||||
GLContext::CanUploadSubTextures()
|
||||
{
|
||||
// There are certain GPUs that we don't want to use glTexSubImage2D on
|
||||
// because that function can be very slow and/or buggy
|
||||
return (Renderer() != RendererAdreno200 &&
|
||||
Renderer() != RendererAdreno205);
|
||||
}
|
||||
|
||||
return !(Renderer() == RendererAdreno200);
|
||||
bool
|
||||
GLContext::CanUploadNonPowerOfTwo()
|
||||
{
|
||||
static bool sPowerOfTwoForced;
|
||||
static bool sPowerOfTwoPrefCached = false;
|
||||
|
||||
if (!sPowerOfTwoPrefCached) {
|
||||
sPowerOfTwoPrefCached = true;
|
||||
mozilla::Preferences::AddBoolVarCache(&sPowerOfTwoForced,
|
||||
"gfx.textures.poweroftwo.force-enabled");
|
||||
}
|
||||
|
||||
// Some GPUs driver crash when uploading non power of two 565 textures.
|
||||
return sPowerOfTwoForced ? false : (Renderer() != RendererAdreno200 &&
|
||||
Renderer() != RendererAdreno205);
|
||||
}
|
||||
|
||||
bool
|
||||
GLContext::WantsSmallTiles()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// We must use small tiles for good performance if we can't use
|
||||
// glTexSubImage2D() for some reason.
|
||||
if (!CanUploadSubTextures())
|
||||
return true;
|
||||
|
||||
// We can't use small tiles on the SGX 540, because of races in texture upload.
|
||||
if (Renderer() == RendererSGX540)
|
||||
return false;
|
||||
|
||||
// Don't use small tiles otherwise. (If we implement incremental texture upload,
|
||||
// then we will want to revisit this.)
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Common code for checking for both GL extensions and GLX extensions.
|
||||
|
@ -685,9 +781,6 @@ void GLContext::ApplyFilterToBoundTexture(gfxPattern::GraphicsFilter aFilter)
|
|||
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
|
||||
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
|
||||
} else {
|
||||
if (aFilter != gfxPattern::FILTER_GOOD) {
|
||||
NS_WARNING("Unsupported filter type!");
|
||||
}
|
||||
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
|
||||
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
|
||||
}
|
||||
|
@ -870,7 +963,7 @@ TiledTextureImage::TiledTextureImage(GLContext* aGL,
|
|||
, mUseNearestFilter(aUseNearestFilter)
|
||||
, mTextureState(Created)
|
||||
{
|
||||
mTileSize = mGL->GetMaxTextureSize();
|
||||
mTileSize = mGL->WantsSmallTiles() ? 256 : mGL->GetMaxTextureSize();
|
||||
if (aSize != nsIntSize(0,0)) {
|
||||
Resize(aSize);
|
||||
}
|
||||
|
@ -1877,16 +1970,23 @@ GLContext::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
|
|||
PushViewportRect(nsIntRect(0, 0, dstSize.width, dstSize.height));
|
||||
|
||||
RectTriangles rects;
|
||||
|
||||
nsIntSize realTexSize = srcSize;
|
||||
if (!CanUploadNonPowerOfTwo()) {
|
||||
realTexSize = nsIntSize(NextPowerOfTwo(srcSize.width),
|
||||
NextPowerOfTwo(srcSize.height));
|
||||
}
|
||||
|
||||
if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
|
||||
rects.addRect(/* dest rectangle */
|
||||
dx0, dy0, dx1, dy1,
|
||||
/* tex coords */
|
||||
srcSubRect.x / float(srcSize.width),
|
||||
srcSubRect.y / float(srcSize.height),
|
||||
srcSubRect.XMost() / float(srcSize.width),
|
||||
srcSubRect.YMost() / float(srcSize.height));
|
||||
srcSubRect.x / float(realTexSize.width),
|
||||
srcSubRect.y / float(realTexSize.height),
|
||||
srcSubRect.XMost() / float(realTexSize.width),
|
||||
srcSubRect.YMost() / float(realTexSize.height));
|
||||
} else {
|
||||
DecomposeIntoNoRepeatTriangles(srcSubRect, srcSize, rects);
|
||||
DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects);
|
||||
|
||||
// now put the coords into the d[xy]0 .. d[xy]1 coordinate space
|
||||
// from the 0..1 that it comes out of decompose
|
||||
|
@ -2131,6 +2231,41 @@ GLContext::TexImage2D(GLenum target, GLint level, GLint internalformat,
|
|||
NS_ASSERTION(format == internalformat,
|
||||
"format and internalformat not the same for glTexImage2D on GLES2");
|
||||
|
||||
if (!CanUploadNonPowerOfTwo()
|
||||
&& (stride != width * pixelsize
|
||||
|| !IsPowerOfTwo(width)
|
||||
|| !IsPowerOfTwo(height))) {
|
||||
|
||||
// Pad out texture width and height to the next power of two
|
||||
// as we don't support/want non power of two texture uploads
|
||||
GLsizei paddedWidth = NextPowerOfTwo(width);
|
||||
GLsizei paddedHeight = NextPowerOfTwo(height);
|
||||
|
||||
GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize];
|
||||
|
||||
// Pad out texture data to be in a POT sized buffer for uploading to
|
||||
// a POT sized texture
|
||||
CopyAndPadTextureData(pixels, paddedPixels, width, height,
|
||||
paddedWidth, paddedHeight, stride, pixelsize);
|
||||
|
||||
fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
|
||||
NS_MIN(GetAddressAlignment((ptrdiff_t)paddedPixels),
|
||||
GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize)));
|
||||
fTexImage2D(target,
|
||||
border,
|
||||
internalformat,
|
||||
paddedWidth,
|
||||
paddedHeight,
|
||||
border,
|
||||
format,
|
||||
type,
|
||||
paddedPixels);
|
||||
fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
|
||||
|
||||
delete[] static_cast<unsigned char*>(paddedPixels);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stride == width * pixelsize) {
|
||||
fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
|
||||
NS_MIN(GetAddressAlignment((ptrdiff_t)pixels),
|
||||
|
@ -2437,8 +2572,10 @@ GLContext::DecomposeIntoNoRepeatTriangles(const nsIntRect& aTexCoordRect,
|
|||
aTexCoordRect.height <= aTexSize.height, "tex coord rect would cause tiling!");
|
||||
|
||||
if (!xwrap && !ywrap) {
|
||||
aRects.addRect(0.0f, 0.0f, 1.0f, 1.0f,
|
||||
tl[0], tl[1], br[0], br[1]);
|
||||
aRects.addRect(0.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
tl[0], tl[1],
|
||||
br[0], br[1]);
|
||||
} else if (!xwrap && ywrap) {
|
||||
GLfloat ymid = (1.0f - tl[1]) / ylen;
|
||||
aRects.addRect(0.0f, 0.0f,
|
||||
|
|
|
@ -697,11 +697,14 @@ public:
|
|||
VendorNVIDIA,
|
||||
VendorATI,
|
||||
VendorQualcomm,
|
||||
VendorImagination,
|
||||
VendorOther
|
||||
};
|
||||
|
||||
enum {
|
||||
RendererAdreno200,
|
||||
RendererAdreno205,
|
||||
RendererSGX540,
|
||||
RendererOther
|
||||
};
|
||||
|
||||
|
@ -714,6 +717,8 @@ public:
|
|||
}
|
||||
|
||||
bool CanUploadSubTextures();
|
||||
bool CanUploadNonPowerOfTwo();
|
||||
bool WantsSmallTiles();
|
||||
|
||||
/**
|
||||
* If this context wraps a double-buffered target, swap the back
|
||||
|
|
|
@ -1133,8 +1133,8 @@ public:
|
|||
// still expensive.
|
||||
#ifndef MOZ_WIDGET_QT
|
||||
if (!mSurface) {
|
||||
// We need to be able to bind the surface when we don't
|
||||
// have access to a surface. We wont be drawing to the screen
|
||||
// We need to be able to bind NO_SURFACE when we don't
|
||||
// have access to a surface. We won't be drawing to the screen
|
||||
// but we will be able to do things like resource releases.
|
||||
succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
|
||||
EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
|
@ -2216,21 +2216,32 @@ CreateSurfaceForWindow(nsIWidget *aWidget, EGLConfig config)
|
|||
{
|
||||
EGLSurface surface;
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
sEGLLibrary.DumpEGLConfig(config);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
printf_stderr("... requesting window surface from bridge\n");
|
||||
surface = mozilla::AndroidBridge::Bridge()->ProvideEGLSurface();
|
||||
printf_stderr("got surface %p\n", surface);
|
||||
return surface;
|
||||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
printf_stderr("... requesting window surface from bridge\n");
|
||||
|
||||
// On Android, we have to ask Java to make the eglCreateWindowSurface
|
||||
// call for us. See GLHelpers.java for a description of why.
|
||||
//
|
||||
// We also only have one true "window", so we just use it directly and ignore
|
||||
// what was passed in.
|
||||
AndroidGeckoSurfaceView& sview = mozilla::AndroidBridge::Bridge()->SurfaceView();
|
||||
if (sview.isNull()) {
|
||||
printf_stderr("got null surface\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf_stderr("... requesting window surface from bridge\n");
|
||||
surface = mozilla::AndroidBridge::Bridge()->
|
||||
CallEglCreateWindowSurface(EGL_DISPLAY(), config,
|
||||
mozilla::AndroidBridge::Bridge()->SurfaceView());
|
||||
CallEglCreateWindowSurface(EGL_DISPLAY(), config, sview);
|
||||
printf_stderr("got surface %p\n", surface);
|
||||
#else
|
||||
surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, GET_NATIVE_WINDOW(aWidget), 0);
|
||||
|
@ -2270,7 +2281,12 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget)
|
|||
return nsnull;
|
||||
}
|
||||
|
||||
EGLSurface surface = CreateSurfaceForWindow(aWidget, config);
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
printf_stderr("... registering OGL compositor with bridge\n");
|
||||
mozilla::AndroidBridge::Bridge()->RegisterCompositor();
|
||||
#endif
|
||||
|
||||
EGLSurface surface = CreateSurfaceForWindow(aWidget, config);
|
||||
|
||||
if (!surface) {
|
||||
return nsnull;
|
||||
|
|
|
@ -122,6 +122,10 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
|||
CPPSRCS += GLContextProviderEGL.cpp
|
||||
endif
|
||||
|
||||
ifdef MOZ_JAVA_COMPOSITOR
|
||||
DEFINES += -DMOZ_JAVA_COMPOSITOR
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
DEFINES := $(filter-out -DUNICODE,$(DEFINES))
|
||||
|
|
|
@ -1345,6 +1345,7 @@ protected:
|
|||
bool mDirty;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ CPPSRCS = \
|
|||
BasicImages.cpp \
|
||||
BasicLayers.cpp \
|
||||
Layers.cpp \
|
||||
RenderTrace.cpp \
|
||||
ReadbackProcessor.cpp \
|
||||
ThebesLayerBuffer.cpp \
|
||||
CanvasLayerOGL.cpp \
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Benoit Girard <bgirard@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "Layers.h"
|
||||
#include "RenderTrace.h"
|
||||
|
||||
// If rendertrace is off let's no compile this code
|
||||
#ifdef MOZ_RENDERTRACE
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
static int colorId = 0;
|
||||
|
||||
static gfx3DMatrix GetRootTransform(Layer *aLayer) {
|
||||
gfx3DMatrix layerTrans = aLayer->GetTransform();
|
||||
if (aLayer->GetParent() != NULL) {
|
||||
return GetRootTransform(aLayer->GetParent()) * layerTrans.ProjectTo2D();
|
||||
}
|
||||
return layerTrans.ProjectTo2D();
|
||||
}
|
||||
|
||||
void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx3DMatrix aRootTransform, bool aReset) {
|
||||
if (!aLayer)
|
||||
return;
|
||||
|
||||
gfx3DMatrix trans = aRootTransform * aLayer->GetTransform();
|
||||
nsIntRect clipRect = aLayer->GetEffectiveVisibleRegion().GetBounds();
|
||||
gfxRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
|
||||
trans.TransformBounds(rect);
|
||||
|
||||
if (strcmp(aLayer->Name(), "ContainerLayer") != 0 &&
|
||||
strcmp(aLayer->Name(), "ShadowContainerLayer") != 0) {
|
||||
printf_stderr("%s RENDERTRACE %u rect #%02X%s %i %i %i %i\n",
|
||||
aLayer->Name(), (int)PR_IntervalNow(),
|
||||
colorId, aColor,
|
||||
(int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
|
||||
}
|
||||
|
||||
colorId++;
|
||||
|
||||
for (Layer* child = aLayer->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
RenderTraceLayers(child, aColor, aRootTransform, false);
|
||||
}
|
||||
|
||||
if (aReset) colorId = 0;
|
||||
}
|
||||
|
||||
void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const nsIntRect aRect) {
|
||||
gfx3DMatrix trans = GetRootTransform(aLayer);
|
||||
gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
trans.TransformBounds(rect);
|
||||
|
||||
printf_stderr("%s RENDERTRACE %u fillrect #%s %i %i %i %i\n",
|
||||
aLayer->Name(), (int)PR_IntervalNow(),
|
||||
aColor,
|
||||
(int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
|
||||
}
|
||||
void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor) {
|
||||
// Clear with an empty rect
|
||||
RenderTraceInvalidateStart(aLayer, aColor, nsIntRect());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Corporation code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Benoit Girard <bgirard@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// This is a general tool that will let you visualize platform operation.
|
||||
// Currently used for the layer system, the general syntax allows this
|
||||
// tools to be adapted to trace other operations.
|
||||
//
|
||||
// For the front end see: https://github.com/staktrace/rendertrace
|
||||
|
||||
// Uncomment this line to enable RENDERTRACE
|
||||
#define MOZ_RENDERTRACE
|
||||
|
||||
#ifndef GFX_RENDERTRACE_H
|
||||
//#define GFX_RENDERTRACE_H
|
||||
|
||||
#include "gfx3DMatrix.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class Layer;
|
||||
|
||||
void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx3DMatrix aRootTransform = gfx3DMatrix(), bool aReset = true);
|
||||
|
||||
void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const nsIntRect aRect);
|
||||
void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor);
|
||||
|
||||
#ifndef MOZ_RENDERTRACE
|
||||
inline void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx3DMatrix aRootTransform, bool aReset)
|
||||
{}
|
||||
|
||||
inline void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const nsIntRect aRect)
|
||||
{}
|
||||
|
||||
inline void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor)
|
||||
{}
|
||||
|
||||
#endif // MOZ_RENDERTRACE
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //GFX_RENDERTRACE_H
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
#include "BasicLayers.h"
|
||||
#include "ImageLayers.h"
|
||||
#include "RenderTrace.h"
|
||||
|
||||
#include "prprf.h"
|
||||
#include "nsTArray.h"
|
||||
|
@ -688,6 +689,9 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
|
|||
mBuffer.Clear();
|
||||
|
||||
nsIntRegion toDraw = IntersectWithClip(GetEffectiveVisibleRegion(), aContext);
|
||||
|
||||
RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
|
||||
|
||||
if (!toDraw.IsEmpty() && !IsHidden()) {
|
||||
if (!aCallback) {
|
||||
BasicManager()->SetTransactionIncomplete();
|
||||
|
@ -723,6 +727,8 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
|
|||
|
||||
aContext->Restore();
|
||||
}
|
||||
|
||||
RenderTraceInvalidateEnd(this, "FFFF00");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -748,11 +754,16 @@ BasicThebesLayer::PaintThebes(gfxContext* aContext,
|
|||
GetEffectiveVisibleRegion());
|
||||
nsIntRegion extendedDrawRegion = state.mRegionToDraw;
|
||||
SetAntialiasingFlags(this, state.mContext);
|
||||
|
||||
RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
|
||||
|
||||
PaintBuffer(state.mContext,
|
||||
state.mRegionToDraw, extendedDrawRegion, state.mRegionToInvalidate,
|
||||
state.mDidSelfCopy,
|
||||
aCallback, aCallbackData);
|
||||
Mutated();
|
||||
|
||||
RenderTraceInvalidateEnd(this, "FFFF00");
|
||||
} else {
|
||||
// It's possible that state.mRegionToInvalidate is nonempty here,
|
||||
// if we are shrinking the valid region to nothing.
|
||||
|
@ -1598,6 +1609,9 @@ BasicLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback,
|
|||
mPhase = PHASE_DRAWING;
|
||||
#endif
|
||||
|
||||
Layer* aLayer = GetRoot();
|
||||
RenderTraceLayers(aLayer, "FF00");
|
||||
|
||||
mTransactionIncomplete = false;
|
||||
|
||||
if (mTarget && mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
|
||||
|
@ -1818,6 +1832,8 @@ Transform3D(gfxASurface* aSource, gfxContext* aDest,
|
|||
return destImage.forget();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
||||
Layer* aLayer,
|
||||
|
|
|
@ -72,6 +72,7 @@ CompositorChild::Destroy()
|
|||
static_cast<ShadowLayersChild*>(ManagedPLayersChild()[0]);
|
||||
layers->Destroy();
|
||||
}
|
||||
printf_stderr("Destroy compositor\n");
|
||||
SendStop();
|
||||
}
|
||||
|
||||
|
|
|
@ -42,12 +42,21 @@
|
|||
#include "ShadowLayersParent.h"
|
||||
#include "LayerManagerOGL.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "RenderTrace.h"
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
#include "AndroidBridge.h"
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
CompositorParent::CompositorParent(nsIWidget* aWidget)
|
||||
: mStopped(false), mWidget(aWidget)
|
||||
: mWidget(aWidget)
|
||||
, mCurrentCompositeTask(NULL)
|
||||
, mPaused(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CompositorParent);
|
||||
}
|
||||
|
@ -70,28 +79,156 @@ CompositorParent::Destroy()
|
|||
bool
|
||||
CompositorParent::RecvStop()
|
||||
{
|
||||
mStopped = true;
|
||||
printf_stderr("Stop composition\n");
|
||||
mPaused = true;
|
||||
Destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::ScheduleRenderOnCompositorThread(::base::Thread &aCompositorThread)
|
||||
{
|
||||
CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::AsyncRender);
|
||||
aCompositorThread.message_loop()->PostTask(FROM_HERE, renderTask);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::PauseComposition()
|
||||
{
|
||||
printf_stderr("Pause composition\n");
|
||||
if (!mPaused) {
|
||||
mPaused = true;
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
static_cast<LayerManagerOGL*>(mLayerManager.get())->gl()->ReleaseSurface();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::ResumeComposition()
|
||||
{
|
||||
mPaused = false;
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
static_cast<LayerManagerOGL*>(mLayerManager.get())->gl()->RenewSurface();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::SchedulePauseOnCompositorThread(::base::Thread &aCompositorThread)
|
||||
{
|
||||
CancelableTask *pauseTask = NewRunnableMethod(this,
|
||||
&CompositorParent::PauseComposition);
|
||||
aCompositorThread.message_loop()->PostTask(FROM_HERE, pauseTask);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::ScheduleResumeOnCompositorThread(::base::Thread &aCompositorThread)
|
||||
{
|
||||
CancelableTask *resumeTask = NewRunnableMethod(this,
|
||||
&CompositorParent::ResumeComposition);
|
||||
aCompositorThread.message_loop()->PostTask(FROM_HERE, resumeTask);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::ScheduleComposition()
|
||||
{
|
||||
CancelableTask *composeTask = NewRunnableMethod(this, &CompositorParent::Composite);
|
||||
MessageLoop::current()->PostTask(FROM_HERE, composeTask);
|
||||
if (mCurrentCompositeTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool initialComposition = mLastCompose.IsNull();
|
||||
TimeDuration delta;
|
||||
if (!initialComposition)
|
||||
delta = mozilla::TimeStamp::Now() - mLastCompose;
|
||||
|
||||
#ifdef COMPOSITOR_PERFORMANCE_WARNING
|
||||
mExpectedComposeTime = mozilla::TimeStamp::Now() + TimeDuration::FromMilliseconds(15);
|
||||
#endif
|
||||
|
||||
printf_stderr("Schedule composition\n");
|
||||
mCurrentCompositeTask = NewRunnableMethod(this, &CompositorParent::Composite);
|
||||
if (!initialComposition && delta.ToMilliseconds() < 15) {
|
||||
#ifdef COMPOSITOR_PERFORMANCE_WARNING
|
||||
mExpectedComposeTime = mozilla::TimeStamp::Now() + TimeDuration::FromMilliseconds(15 - delta.ToMilliseconds());
|
||||
#endif
|
||||
MessageLoop::current()->PostDelayedTask(FROM_HERE, mCurrentCompositeTask, 15 - delta.ToMilliseconds());
|
||||
} else {
|
||||
MessageLoop::current()->PostTask(FROM_HERE, mCurrentCompositeTask);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::SetTransformation(float aScale, nsIntPoint aScrollOffset)
|
||||
{
|
||||
mXScale = aScale;
|
||||
mYScale = aScale;
|
||||
mScrollOffset = aScrollOffset;
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::Composite()
|
||||
{
|
||||
if (mStopped || !mLayerManager) {
|
||||
mCurrentCompositeTask = NULL;
|
||||
|
||||
mLastCompose = mozilla::TimeStamp::Now();
|
||||
|
||||
if (mPaused || !mLayerManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
RequestViewTransform();
|
||||
printf_stderr("Correcting for position fixed %i, %i\n", -mScrollOffset.x, -mScrollOffset.y);
|
||||
TransformShadowTree();
|
||||
#endif
|
||||
|
||||
Layer* aLayer = mLayerManager->GetRoot();
|
||||
mozilla::layers::RenderTraceLayers(aLayer, "0000");
|
||||
|
||||
mLayerManager->EndEmptyTransaction();
|
||||
|
||||
#ifdef COMPOSITOR_PERFORMANCE_WARNING
|
||||
if (mExpectedComposeTime + TimeDuration::FromMilliseconds(15) < mozilla::TimeStamp::Now()) {
|
||||
printf_stderr("Compositor: Compose frame took %i time then expected.\n",
|
||||
(int)(mozilla::TimeStamp::Now() - mExpectedComposeTime).ToMilliseconds());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Do a breadth-first search to find the first layer in the tree that is
|
||||
// scrollable.
|
||||
Layer*
|
||||
CompositorParent::GetPrimaryScrollableLayer()
|
||||
{
|
||||
Layer* root = mLayerManager->GetRoot();
|
||||
|
||||
nsTArray<Layer*> queue;
|
||||
queue.AppendElement(root);
|
||||
for (unsigned i = 0; i < queue.Length(); i++) {
|
||||
ContainerLayer* containerLayer = queue[i]->AsContainerLayer();
|
||||
if (!containerLayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics();
|
||||
if (frameMetrics.IsScrollable()) {
|
||||
return containerLayer;
|
||||
}
|
||||
|
||||
Layer* child = containerLayer->GetFirstChild();
|
||||
while (child) {
|
||||
queue.AppendElement(child);
|
||||
child = child->GetNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Go down shadow layer tree, setting properties to match their non-shadow
|
||||
// counterparts.
|
||||
static void
|
||||
|
@ -109,9 +246,85 @@ SetShadowProperties(Layer* aLayer)
|
|||
}
|
||||
}
|
||||
|
||||
static double GetXScale(const gfx3DMatrix& aTransform)
|
||||
{
|
||||
return aTransform._11;
|
||||
}
|
||||
|
||||
static double GetYScale(const gfx3DMatrix& aTransform)
|
||||
{
|
||||
return aTransform._22;
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::TransformShadowTree()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
Layer* layer = GetPrimaryScrollableLayer();
|
||||
ShadowLayer* shadow = layer->AsShadowLayer();
|
||||
|
||||
gfx3DMatrix shadowTransform = layer->GetTransform();
|
||||
|
||||
ContainerLayer* container = layer->AsContainerLayer();
|
||||
|
||||
const FrameMetrics* metrics = &container->GetFrameMetrics();
|
||||
const gfx3DMatrix& currentTransform = layer->GetTransform();
|
||||
|
||||
if (metrics && metrics->IsScrollable()) {
|
||||
float tempScaleDiffX = GetXScale(mLayerManager->GetRoot()->GetTransform()) * mXScale;
|
||||
float tempScaleDiffY = GetYScale(mLayerManager->GetRoot()->GetTransform()) * mYScale;
|
||||
|
||||
nsIntPoint metricsScrollOffset = metrics->mViewportScrollOffset;
|
||||
|
||||
nsIntPoint scrollCompensation(
|
||||
(mScrollOffset.x / tempScaleDiffX - metricsScrollOffset.x) * mXScale,
|
||||
(mScrollOffset.y / tempScaleDiffY - metricsScrollOffset.y) * mYScale);
|
||||
ViewTransform treeTransform(-scrollCompensation, mXScale,
|
||||
mYScale);
|
||||
shadowTransform = gfx3DMatrix(treeTransform) * currentTransform;
|
||||
|
||||
shadow->SetShadowTransform(shadowTransform);
|
||||
} else {
|
||||
ViewTransform treeTransform(nsIntPoint(0,0), mXScale,
|
||||
mYScale);
|
||||
shadowTransform = gfx3DMatrix(treeTransform) * currentTransform;
|
||||
|
||||
shadow->SetShadowTransform(shadowTransform);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::AsyncRender()
|
||||
{
|
||||
if (mPaused || !mLayerManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
Layer* root = mLayerManager->GetRoot();
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScheduleComposition();
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
void
|
||||
CompositorParent::RequestViewTransform()
|
||||
{
|
||||
mozilla::AndroidBridge::Bridge()->GetViewTransform(mScrollOffset, mXScale, mYScale);
|
||||
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### mScrollOffset=%g %g "
|
||||
"mXScale=%g mYScale=%g", (float)mScrollOffset.x, (float)mScrollOffset.y,
|
||||
(float)mXScale, (float)mYScale);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CompositorParent::ShadowLayersUpdated()
|
||||
{
|
||||
printf_stderr("ShadowLayersUpdated\n");
|
||||
const nsTArray<PLayersParent*>& shadowParents = ManagedPLayersParent();
|
||||
NS_ABORT_IF_FALSE(shadowParents.Length() <= 1,
|
||||
"can only support at most 1 ShadowLayersParent");
|
||||
|
|
|
@ -41,8 +41,17 @@
|
|||
#ifndef mozilla_layers_CompositorParent_h
|
||||
#define mozilla_layers_CompositorParent_h
|
||||
|
||||
// Enable this pref to turn on compositor performance warning.
|
||||
// This will print warnings if the compositor isn't meeting
|
||||
// it's responsiveness objectives:
|
||||
// 1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
|
||||
// 2) Unless a frame was composited within the throttle threshold in
|
||||
// which the deadline will be 15ms + throttle threshold
|
||||
#define COMPOSITOR_PERFORMANCE_WARNING
|
||||
|
||||
#include "mozilla/layers/PCompositorParent.h"
|
||||
#include "mozilla/layers/PLayersParent.h"
|
||||
#include "base/thread.h"
|
||||
#include "ShadowLayersManager.h"
|
||||
|
||||
class nsIWidget;
|
||||
|
@ -52,6 +61,26 @@ namespace layers {
|
|||
|
||||
class LayerManager;
|
||||
|
||||
// Represents (affine) transforms that are calculated from a content view.
|
||||
struct ViewTransform {
|
||||
ViewTransform(nsIntPoint aTranslation = nsIntPoint(0, 0), float aXScale = 1, float aYScale = 1)
|
||||
: mTranslation(aTranslation)
|
||||
, mXScale(aXScale)
|
||||
, mYScale(aYScale)
|
||||
{}
|
||||
|
||||
operator gfx3DMatrix() const
|
||||
{
|
||||
return
|
||||
gfx3DMatrix::ScalingMatrix(mXScale, mYScale, 1) *
|
||||
gfx3DMatrix::Translation(mTranslation.x, mTranslation.y, 0);
|
||||
}
|
||||
|
||||
nsIntPoint mTranslation;
|
||||
float mXScale;
|
||||
float mYScale;
|
||||
};
|
||||
|
||||
class CompositorParent : public PCompositorParent,
|
||||
public ShadowLayersManager
|
||||
{
|
||||
|
@ -67,17 +96,53 @@ public:
|
|||
|
||||
LayerManager* GetLayerManager() { return mLayerManager; }
|
||||
|
||||
void SetTransformation(float aScale, nsIntPoint aScrollOffset);
|
||||
void AsyncRender();
|
||||
|
||||
// Can be called from any thread
|
||||
void ScheduleRenderOnCompositorThread(::base::Thread &aCompositorThread);
|
||||
void SchedulePauseOnCompositorThread(::base::Thread &aCompositorThread);
|
||||
void ScheduleResumeOnCompositorThread(::base::Thread &aCompositorThread);
|
||||
|
||||
protected:
|
||||
virtual PLayersParent* AllocPLayers(const LayersBackend &backendType);
|
||||
virtual bool DeallocPLayers(PLayersParent* aLayers);
|
||||
|
||||
private:
|
||||
void ScheduleComposition();
|
||||
void PauseComposition();
|
||||
void ResumeComposition();
|
||||
|
||||
void Composite();
|
||||
void ScheduleComposition();
|
||||
void TransformShadowTree();
|
||||
|
||||
// Platform specific functions
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
/**
|
||||
* Asks Java for the viewport position and updates the world transform
|
||||
* accordingly.
|
||||
*/
|
||||
void RequestViewTransform();
|
||||
|
||||
/**
|
||||
* Does a breadth-first search to find the first layer in the tree with a
|
||||
* displayport set.
|
||||
*/
|
||||
Layer* GetPrimaryScrollableLayer();
|
||||
#endif
|
||||
|
||||
nsRefPtr<LayerManager> mLayerManager;
|
||||
bool mStopped;
|
||||
nsIWidget* mWidget;
|
||||
CancelableTask *mCurrentCompositeTask;
|
||||
TimeStamp mLastCompose;
|
||||
#ifdef COMPOSITOR_PERFORMANCE_WARNING
|
||||
TimeStamp mExpectedComposeTime;
|
||||
#endif
|
||||
|
||||
bool mPaused;
|
||||
float mXScale;
|
||||
float mYScale;
|
||||
nsIntPoint mScrollOffset;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
|
||||
};
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "ShadowLayersParent.h"
|
||||
#include "ShadowLayerParent.h"
|
||||
#include "ShadowLayers.h"
|
||||
#include "RenderTrace.h"
|
||||
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
|
@ -149,6 +150,10 @@ bool
|
|||
ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
||||
InfallibleTArray<EditReply>* reply)
|
||||
{
|
||||
#ifdef COMPOSITOR_PERFORMANCE_WARNING
|
||||
TimeStamp updateStart = TimeStamp::Now();
|
||||
#endif
|
||||
|
||||
MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
|
||||
|
||||
if (mDestroyed || layer_manager()->IsDestroyed()) {
|
||||
|
@ -318,6 +323,8 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
|||
static_cast<ShadowThebesLayer*>(shadow->AsLayer());
|
||||
const ThebesBuffer& newFront = op.newFrontBuffer();
|
||||
|
||||
RenderTraceInvalidateStart(thebes, "FF00FF", op.updatedRegion().GetBounds());
|
||||
|
||||
OptionalThebesBuffer newBack;
|
||||
nsIntRegion newValidRegion;
|
||||
OptionalThebesBuffer readonlyFront;
|
||||
|
@ -330,6 +337,8 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
|||
shadow, NULL,
|
||||
newBack, newValidRegion,
|
||||
readonlyFront, frontUpdatedRegion));
|
||||
|
||||
RenderTraceInvalidateEnd(thebes, "FF00FF");
|
||||
break;
|
||||
}
|
||||
case Edit::TOpPaintCanvas: {
|
||||
|
@ -340,6 +349,8 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
|||
ShadowCanvasLayer* canvas =
|
||||
static_cast<ShadowCanvasLayer*>(shadow->AsLayer());
|
||||
|
||||
RenderTraceInvalidateStart(canvas, "FF00FF", canvas->GetVisibleRegion().GetBounds());
|
||||
|
||||
canvas->SetAllocator(this);
|
||||
CanvasSurface newBack;
|
||||
canvas->Swap(op.newFrontBuffer(), op.needYFlip(), &newBack);
|
||||
|
@ -347,6 +358,7 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
|||
replyv.push_back(OpBufferSwap(shadow, NULL,
|
||||
newBack));
|
||||
|
||||
RenderTraceInvalidateEnd(canvas, "FF00FF");
|
||||
break;
|
||||
}
|
||||
case Edit::TOpPaintImage: {
|
||||
|
@ -357,12 +369,16 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
|||
ShadowImageLayer* image =
|
||||
static_cast<ShadowImageLayer*>(shadow->AsLayer());
|
||||
|
||||
RenderTraceInvalidateStart(image, "FF00FF", image->GetVisibleRegion().GetBounds());
|
||||
|
||||
image->SetAllocator(this);
|
||||
SharedImage newBack;
|
||||
image->Swap(op.newFrontBuffer(), &newBack);
|
||||
replyv.push_back(OpImageSwap(shadow, NULL,
|
||||
newBack));
|
||||
|
||||
RenderTraceInvalidateEnd(image, "FF00FF");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -385,6 +401,11 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
|||
|
||||
mShadowLayersManager->ShadowLayersUpdated();
|
||||
|
||||
#ifdef COMPOSITOR_PERFORMANCE_WARNING
|
||||
printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n",
|
||||
(int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -283,7 +283,11 @@ CanvasLayerOGL::RenderLayer(int aPreviousDestination,
|
|||
program->SetRenderOffset(aOffset);
|
||||
program->SetTextureUnit(0);
|
||||
|
||||
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip ? true : false);
|
||||
if (gl()->CanUploadNonPowerOfTwo()) {
|
||||
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip ? true : false);
|
||||
} else {
|
||||
mOGLManager->BindAndDrawQuadWithTextureRect(program, drawRect, drawRect.Size());
|
||||
}
|
||||
|
||||
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
|
||||
if (mPixmap && !mDelayedUpdates) {
|
||||
|
@ -415,11 +419,27 @@ ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
|
|||
program->SetTextureUnit(0);
|
||||
|
||||
mTexImage->BeginTileIteration();
|
||||
do {
|
||||
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
|
||||
program->SetLayerQuadRect(mTexImage->GetTileRect());
|
||||
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip); // FIXME flip order of tiles?
|
||||
} while (mTexImage->NextTile());
|
||||
if (gl()->CanUploadNonPowerOfTwo()) {
|
||||
do {
|
||||
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
|
||||
program->SetLayerQuadRect(mTexImage->GetTileRect());
|
||||
mOGLManager->BindAndDrawQuad(program, mNeedsYFlip); // FIXME flip order of tiles?
|
||||
} while (mTexImage->NextTile());
|
||||
} else {
|
||||
do {
|
||||
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
|
||||
program->SetLayerQuadRect(mTexImage->GetTileRect());
|
||||
// We can't use BindAndDrawQuad because that always uploads the whole texture from 0.0f -> 1.0f
|
||||
// in x and y. We use BindAndDrawQuadWithTextureRect to actually draw a subrect of the texture
|
||||
// We need to reset the origin to 0,0 from the tile rect because the tile originates at 0,0 in the
|
||||
// actual texture, even though its origin in the composed (tiled) texture is not 0,0
|
||||
// FIXME: we need to handle mNeedsYFlip, Bug #728625
|
||||
mOGLManager->BindAndDrawQuadWithTextureRect(program,
|
||||
nsIntRect(0, 0, mTexImage->GetTileRect().width,
|
||||
mTexImage->GetTileRect().height),
|
||||
mTexImage->GetTileRect().Size());
|
||||
} while (mTexImage->NextTile());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include "ImageLayerOGL.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "yuv_convert.h"
|
||||
#include "GLContextProvider.h"
|
||||
#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
|
||||
|
@ -47,6 +48,7 @@
|
|||
# include "mozilla/X11Util.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::gl;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -333,8 +335,9 @@ ImageLayerOGL::RenderLayer(int,
|
|||
|
||||
bool tileIsWholeImage = (mTileSourceRect == nsIntRect(0, 0, iwidth, iheight))
|
||||
|| !mUseTileSourceRect;
|
||||
bool imageIsPowerOfTwo = ((iwidth & (iwidth - 1)) == 0 &&
|
||||
(iheight & (iheight - 1)) == 0);
|
||||
bool imageIsPowerOfTwo = IsPowerOfTwo(iwidth) &&
|
||||
IsPowerOfTwo(iheight);
|
||||
|
||||
bool canDoNPOT = (
|
||||
gl()->IsExtensionSupported(GLContext::ARB_texture_non_power_of_two) ||
|
||||
gl()->IsExtensionSupported(GLContext::OES_texture_npot));
|
||||
|
@ -776,11 +779,26 @@ ShadowImageLayerOGL::RenderLayer(int aPreviousFrameBuffer,
|
|||
|
||||
mTexImage->SetFilter(mFilter);
|
||||
mTexImage->BeginTileIteration();
|
||||
do {
|
||||
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
|
||||
colorProgram->SetLayerQuadRect(mTexImage->GetTileRect());
|
||||
mOGLManager->BindAndDrawQuad(colorProgram);
|
||||
} while (mTexImage->NextTile());
|
||||
|
||||
if (gl()->CanUploadNonPowerOfTwo()) {
|
||||
do {
|
||||
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
|
||||
colorProgram->SetLayerQuadRect(mTexImage->GetTileRect());
|
||||
mOGLManager->BindAndDrawQuad(colorProgram);
|
||||
} while (mTexImage->NextTile());
|
||||
} else {
|
||||
do {
|
||||
TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
|
||||
colorProgram->SetLayerQuadRect(mTexImage->GetTileRect());
|
||||
// We can't use BindAndDrawQuad because that always uploads the whole texture from 0.0f -> 1.0f
|
||||
// in x and y. We use BindAndDrawQuadWithTextureRect to actually draw a subrect of the texture
|
||||
mOGLManager->BindAndDrawQuadWithTextureRect(colorProgram,
|
||||
nsIntRect(0, 0, mTexImage->GetTileRect().width,
|
||||
mTexImage->GetTileRect().height),
|
||||
mTexImage->GetTileRect().Size());
|
||||
} while (mTexImage->NextTile());
|
||||
}
|
||||
|
||||
} else {
|
||||
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mYUVTexture[0].GetTextureID());
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "LayerManagerOGLShaders.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
#include "GLContext.h"
|
||||
|
@ -66,9 +67,14 @@
|
|||
|
||||
#include "sampler.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::gl;
|
||||
|
||||
#ifdef CHECK_CURRENT_PROGRAM
|
||||
|
@ -523,6 +529,7 @@ bool LayerManagerOGL::sDrawFPS = false;
|
|||
void
|
||||
LayerManagerOGL::FPSState::DrawFPS(GLContext* context, CopyProgram* copyprog)
|
||||
{
|
||||
printf_stderr("draw fps\n");
|
||||
fcount++;
|
||||
|
||||
int rate = 30;
|
||||
|
@ -537,6 +544,11 @@ LayerManagerOGL::FPSState::DrawFPS(GLContext* context, CopyProgram* copyprog)
|
|||
GLint viewport[4];
|
||||
context->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### Viewport: %d %d %d %d",
|
||||
viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
#endif
|
||||
|
||||
static GLuint texture;
|
||||
if (!initialized) {
|
||||
// Bind the number of textures we need, in this case one.
|
||||
|
@ -683,16 +695,22 @@ LayerManagerOGL::BindAndDrawQuadWithTextureRect(LayerProgram *aProg,
|
|||
|
||||
GLContext::RectTriangles rects;
|
||||
|
||||
nsIntSize realTexSize = aTexSize;
|
||||
if (!mGLContext->CanUploadNonPowerOfTwo()) {
|
||||
realTexSize = nsIntSize(NextPowerOfTwo(aTexSize.width),
|
||||
NextPowerOfTwo(aTexSize.height));
|
||||
}
|
||||
|
||||
if (aWrapMode == LOCAL_GL_REPEAT) {
|
||||
rects.addRect(/* dest rectangle */
|
||||
0.0f, 0.0f, 1.0f, 1.0f,
|
||||
/* tex coords */
|
||||
aTexCoordRect.x / GLfloat(aTexSize.width),
|
||||
aTexCoordRect.y / GLfloat(aTexSize.height),
|
||||
aTexCoordRect.XMost() / GLfloat(aTexSize.width),
|
||||
aTexCoordRect.YMost() / GLfloat(aTexSize.height));
|
||||
aTexCoordRect.x / GLfloat(realTexSize.width),
|
||||
aTexCoordRect.y / GLfloat(realTexSize.height),
|
||||
aTexCoordRect.XMost() / GLfloat(realTexSize.width),
|
||||
aTexCoordRect.YMost() / GLfloat(realTexSize.height));
|
||||
} else {
|
||||
GLContext::DecomposeIntoNoRepeatTriangles(aTexCoordRect, aTexSize, rects);
|
||||
GLContext::DecomposeIntoNoRepeatTriangles(aTexCoordRect, realTexSize, rects);
|
||||
}
|
||||
|
||||
mGLContext->fVertexAttribPointer(vertAttribIndex, 2,
|
||||
|
@ -737,6 +755,8 @@ LayerManagerOGL::Render()
|
|||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
printf_stderr("render %i, %i\n", width, height);
|
||||
|
||||
// If the widget size changed, we have to force a MakeCurrent
|
||||
// to make sure that GL sees the updated widget size.
|
||||
if (mWidgetSize.width != width ||
|
||||
|
@ -773,10 +793,14 @@ LayerManagerOGL::Render()
|
|||
mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Allow widget to render a custom background.
|
||||
mWidget->DrawWindowUnderlay(this, rect);
|
||||
|
||||
// Render our layers.
|
||||
RootLayer()->RenderLayer(mGLContext->IsDoubleBuffered() ? 0 : mBackBufferFBO,
|
||||
nsIntPoint(0, 0));
|
||||
|
||||
// Allow widget to render a custom foreground too.
|
||||
mWidget->DrawWindowOverlay(this, rect);
|
||||
|
||||
if (mTarget) {
|
||||
|
|
|
@ -387,7 +387,7 @@ public:
|
|||
* to a window of the given dimensions.
|
||||
*/
|
||||
void SetupPipeline(int aWidth, int aHeight, WorldTransforPolicy aTransformPolicy);
|
||||
|
||||
|
||||
/**
|
||||
* Setup World transform matrix.
|
||||
* Transform will be ignored if it is not PreservesAxisAlignedRectangles
|
||||
|
|
|
@ -951,6 +951,7 @@ ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
|
|||
OptionalThebesBuffer* aReadOnlyFront,
|
||||
nsIntRegion* aFrontUpdatedRegion)
|
||||
{
|
||||
printf_stderr("Thebes Swap\n");
|
||||
if (!mDestroyed) {
|
||||
if (!mBuffer) {
|
||||
mBuffer = new ShadowBufferOGL(this);
|
||||
|
|
|
@ -170,4 +170,71 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/*
|
||||
* Copyright 2008 The Android Open Source Project
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
// Some helper functions for power-of-two arithmetic
|
||||
// from Skia
|
||||
#if defined(__arm__)
|
||||
#define CountLeadingZeroes(x) __builtin_clz(x)
|
||||
#else
|
||||
|
||||
#define sub_shift(zeros, x, n) \
|
||||
zeros -= n; \
|
||||
x >>= n
|
||||
|
||||
static inline int CountLeadingZeroes(uint32_t aNumber)
|
||||
{
|
||||
if (aNumber == 0) {
|
||||
return 32;
|
||||
}
|
||||
int zeros = 31;
|
||||
if (aNumber & 0xFFFF0000) {
|
||||
sub_shift(zeros, aNumber, 16);
|
||||
}
|
||||
if (aNumber & 0xFF00) {
|
||||
sub_shift(zeros, aNumber, 8);
|
||||
}
|
||||
if (aNumber & 0xF0) {
|
||||
sub_shift(zeros, aNumber, 4);
|
||||
}
|
||||
if (aNumber & 0xC) {
|
||||
sub_shift(zeros, aNumber, 2);
|
||||
}
|
||||
if (aNumber & 0x2) {
|
||||
sub_shift(zeros, aNumber, 1);
|
||||
}
|
||||
return zeros;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns true if |aNumber| is a power of two
|
||||
*/
|
||||
static inline bool
|
||||
IsPowerOfTwo(int aNumber)
|
||||
{
|
||||
return (aNumber & (aNumber - 1)) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first integer greater than |aNumber| which is a power of two
|
||||
* Undefined for |aNumber| < 0
|
||||
*/
|
||||
static inline int
|
||||
NextPowerOfTwo(int aNumber)
|
||||
{
|
||||
return 1 << (32 - CountLeadingZeroes(aNumber - 1));
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
|
@ -871,6 +871,8 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
|||
matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
|
||||
layer->SetTransform(gfx3DMatrix::From2D(matrix));
|
||||
|
||||
// FIXME: Temporary workaround for bug 681192 and bug 724786. Uncomment this code before review!
|
||||
#ifndef MOZ_JAVA_COMPOSITOR
|
||||
// Calculate exact position of the top-left of the active scrolled root.
|
||||
// This might not be 0,0 due to the snapping in ScaleToNearestPixels.
|
||||
gfxPoint activeScrolledRootTopLeft = scaledOffset - matrix.GetTranslation();
|
||||
|
@ -882,6 +884,7 @@ ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
|
|||
nsIntRect invalidate = layer->GetValidRegion().GetBounds();
|
||||
layer->InvalidateRegion(invalidate);
|
||||
}
|
||||
#endif
|
||||
|
||||
return layer.forget();
|
||||
}
|
||||
|
|
|
@ -2065,16 +2065,18 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Since making new layers is expensive, only use nsDisplayScrollLayer
|
||||
// if the area is scrollable.
|
||||
// if the area is scrollable and there's a displayport (or we're the content
|
||||
// process).
|
||||
nsRect scrollRange = GetScrollRange();
|
||||
ScrollbarStyles styles = GetScrollbarStylesFromFrame();
|
||||
mShouldBuildLayer =
|
||||
(XRE_GetProcessType() == GeckoProcessType_Content &&
|
||||
((XRE_GetProcessType() == GeckoProcessType_Content || usingDisplayport) &&
|
||||
(styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN ||
|
||||
styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
|
||||
(scrollRange.width > 0 ||
|
||||
scrollRange.height > 0) &&
|
||||
(!mIsRoot || !mOuter->PresContext()->IsRootContentDocument()));
|
||||
scrollRange.height > 0 || usingDisplayport) &&
|
||||
(usingDisplayport || !mIsRoot ||
|
||||
!mOuter->PresContext()->IsRootContentDocument()));
|
||||
|
||||
if (ShouldBuildLayer()) {
|
||||
// ScrollLayerWrapper must always be created because it initializes the
|
||||
|
|
|
@ -593,6 +593,9 @@ pref("layers.acceleration.disabled", false);
|
|||
pref("layers.acceleration.disabled", true);
|
||||
#endif
|
||||
|
||||
pref("layers.offmainthreadcomposition.enabled", true);
|
||||
pref("layers.acceleration.draw-fps", true);
|
||||
|
||||
pref("notification.feature.enabled", true);
|
||||
|
||||
// prevent tooltips from showing up
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||
|
||||
<!-- App requires OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
||||
|
||||
<application android:label="@MOZ_APP_DISPLAYNAME@"
|
||||
android:icon="@drawable/icon"
|
||||
|
|
|
@ -42,7 +42,7 @@ package org.mozilla.gecko;
|
|||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.gfx.FloatSize;
|
||||
import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
|
||||
import org.mozilla.gecko.gfx.GeckoLayerClient;
|
||||
import org.mozilla.gecko.gfx.IntSize;
|
||||
import org.mozilla.gecko.gfx.Layer;
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
|
@ -143,7 +143,7 @@ abstract public class GeckoApp
|
|||
private Address mLastGeoAddress;
|
||||
private static LayerController mLayerController;
|
||||
private static PlaceholderLayerClient mPlaceholderLayerClient;
|
||||
private static GeckoSoftwareLayerClient mSoftwareLayerClient;
|
||||
private static GeckoLayerClient mLayerClient;
|
||||
private AboutHomeContent mAboutHomeContent;
|
||||
private static AbsoluteLayout mPluginContainer;
|
||||
|
||||
|
@ -546,10 +546,6 @@ abstract public class GeckoApp
|
|||
}
|
||||
}
|
||||
|
||||
public String getLastViewport() {
|
||||
return mLastViewport;
|
||||
}
|
||||
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (mOwnActivityDepth > 0)
|
||||
|
@ -573,21 +569,18 @@ abstract public class GeckoApp
|
|||
}
|
||||
|
||||
public void run() {
|
||||
if (mSoftwareLayerClient == null)
|
||||
if (mLayerClient == null)
|
||||
return;
|
||||
|
||||
synchronized (mSoftwareLayerClient) {
|
||||
synchronized (mLayerClient) {
|
||||
if (!Tabs.getInstance().isSelectedTab(mThumbnailTab))
|
||||
return;
|
||||
|
||||
if (getLayerController().getLayerClient() != mSoftwareLayerClient)
|
||||
return;
|
||||
|
||||
HistoryEntry lastHistoryEntry = mThumbnailTab.getLastHistoryEntry();
|
||||
if (lastHistoryEntry == null)
|
||||
return;
|
||||
|
||||
ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics();
|
||||
ViewportMetrics viewportMetrics = mLayerClient.getGeckoViewportMetrics();
|
||||
// If we don't have viewport metrics, the screenshot won't be right so bail
|
||||
if (viewportMetrics == null)
|
||||
return;
|
||||
|
@ -609,8 +602,7 @@ abstract public class GeckoApp
|
|||
|
||||
void getAndProcessThumbnailForTab(final Tab tab, boolean forceBigSceenshot) {
|
||||
boolean isSelectedTab = Tabs.getInstance().isSelectedTab(tab);
|
||||
final Bitmap bitmap = isSelectedTab ?
|
||||
mSoftwareLayerClient.getBitmap() : null;
|
||||
final Bitmap bitmap = isSelectedTab ? mLayerClient.getBitmap() : null;
|
||||
|
||||
if (bitmap != null) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
@ -625,8 +617,8 @@ abstract public class GeckoApp
|
|||
}
|
||||
|
||||
mLastScreen = null;
|
||||
int sw = forceBigSceenshot ? mSoftwareLayerClient.getWidth() : tab.getMinScreenshotWidth();
|
||||
int sh = forceBigSceenshot ? mSoftwareLayerClient.getHeight(): tab.getMinScreenshotHeight();
|
||||
int sw = forceBigSceenshot ? mLayerClient.getWidth() : tab.getMinScreenshotWidth();
|
||||
int sh = forceBigSceenshot ? mLayerClient.getHeight(): tab.getMinScreenshotHeight();
|
||||
int dw = forceBigSceenshot ? sw : tab.getThumbnailWidth();
|
||||
int dh = forceBigSceenshot ? sh : tab.getThumbnailHeight();
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), sw, sh, dw, dh));
|
||||
|
@ -1526,7 +1518,7 @@ abstract public class GeckoApp
|
|||
return;
|
||||
}
|
||||
|
||||
PointF origin = metrics.getDisplayportOrigin();
|
||||
PointF origin = metrics.getOrigin();
|
||||
x = x + (int)origin.x;
|
||||
y = y + (int)origin.y;
|
||||
|
||||
|
@ -1788,10 +1780,11 @@ abstract public class GeckoApp
|
|||
|
||||
if (mLayerController == null) {
|
||||
/*
|
||||
* Create a layer client so that Gecko will have a buffer to draw into, but don't hook
|
||||
* it up to the layer controller yet.
|
||||
* Create a layer client, but don't hook it up to the layer controller yet.
|
||||
*/
|
||||
mSoftwareLayerClient = new GeckoSoftwareLayerClient(this);
|
||||
Log.e(LOGTAG, "### Creating GeckoLayerClient");
|
||||
mLayerClient = new GeckoLayerClient(this);
|
||||
Log.e(LOGTAG, "### Done creating GeckoLayerClient");
|
||||
|
||||
/*
|
||||
* Hook a placeholder layer client up to the layer controller so that the user can pan
|
||||
|
@ -1803,8 +1796,7 @@ abstract public class GeckoApp
|
|||
* run experience, perhaps?
|
||||
*/
|
||||
mLayerController = new LayerController(this);
|
||||
mPlaceholderLayerClient = PlaceholderLayerClient.createInstance(this);
|
||||
mLayerController.setLayerClient(mPlaceholderLayerClient);
|
||||
mPlaceholderLayerClient = new PlaceholderLayerClient(mLayerController, mLastViewport);
|
||||
|
||||
mGeckoLayout.addView(mLayerController.getView(), 0);
|
||||
}
|
||||
|
@ -2663,7 +2655,7 @@ abstract public class GeckoApp
|
|||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Add", args.toString()));
|
||||
}
|
||||
|
||||
public GeckoSoftwareLayerClient getSoftwareLayerClient() { return mSoftwareLayerClient; }
|
||||
public GeckoLayerClient getLayerClient() { return mLayerClient; }
|
||||
public LayerController getLayerController() { return mLayerController; }
|
||||
|
||||
// accelerometer
|
||||
|
@ -2742,7 +2734,7 @@ abstract public class GeckoApp
|
|||
mPlaceholderLayerClient.destroy();
|
||||
|
||||
LayerController layerController = getLayerController();
|
||||
layerController.setLayerClient(mSoftwareLayerClient);
|
||||
layerController.setLayerClient(mLayerClient);
|
||||
}
|
||||
|
||||
public class GeckoAppHandler extends Handler {
|
||||
|
@ -2804,7 +2796,7 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams
|
|||
}
|
||||
|
||||
public void reset(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) {
|
||||
PointF origin = aViewport.getDisplayportOrigin();
|
||||
PointF origin = aViewport.getOrigin();
|
||||
|
||||
x = mOriginalX = aX + (int)origin.x;
|
||||
y = mOriginalY = aY + (int)origin.y;
|
||||
|
@ -2830,8 +2822,8 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams
|
|||
}
|
||||
|
||||
public void reposition(ViewportMetrics viewport) {
|
||||
PointF targetOrigin = viewport.getDisplayportOrigin();
|
||||
PointF originalOrigin = mOriginalViewport.getDisplayportOrigin();
|
||||
PointF targetOrigin = viewport.getOrigin();
|
||||
PointF originalOrigin = mOriginalViewport.getOrigin();
|
||||
|
||||
Point offset = new Point(Math.round(originalOrigin.x - targetOrigin.x),
|
||||
Math.round(originalOrigin.y - targetOrigin.y));
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
|
||||
import org.mozilla.gecko.gfx.GeckoLayerClient;
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
|
||||
|
@ -132,7 +132,7 @@ public class GeckoAppShell
|
|||
|
||||
// helper methods
|
||||
// public static native void setSurfaceView(GeckoSurfaceView sv);
|
||||
public static native void setSoftwareLayerClient(GeckoSoftwareLayerClient client);
|
||||
public static native void setLayerClient(GeckoLayerClient client);
|
||||
public static native void putenv(String map);
|
||||
public static native void onResume();
|
||||
public static native void onLowMemory();
|
||||
|
@ -177,7 +177,9 @@ public class GeckoAppShell
|
|||
|
||||
public static native ByteBuffer allocateDirectBuffer(long size);
|
||||
public static native void freeDirectBuffer(ByteBuffer buf);
|
||||
public static native void bindWidgetTexture();
|
||||
public static native void scheduleComposite();
|
||||
public static native void schedulePauseComposition();
|
||||
public static native void scheduleResumeComposition();
|
||||
|
||||
// A looper thread, accessed by GeckoAppShell.getHandler
|
||||
private static class LooperThread extends Thread {
|
||||
|
@ -479,9 +481,9 @@ public class GeckoAppShell
|
|||
Log.i(LOGTAG, "post native init");
|
||||
|
||||
// Tell Gecko where the target byte buffer is for rendering
|
||||
GeckoAppShell.setSoftwareLayerClient(GeckoApp.mAppContext.getSoftwareLayerClient());
|
||||
GeckoAppShell.setLayerClient(GeckoApp.mAppContext.getLayerClient());
|
||||
|
||||
Log.i(LOGTAG, "setSoftwareLayerClient called");
|
||||
Log.i(LOGTAG, "setLayerClient called");
|
||||
|
||||
// First argument is the .apk path
|
||||
String combinedArgs = apkPath + " -greomni " + apkPath;
|
||||
|
@ -510,8 +512,7 @@ public class GeckoAppShell
|
|||
private static void geckoLoaded() {
|
||||
final LayerController layerController = GeckoApp.mAppContext.getLayerController();
|
||||
LayerView v = layerController.getView();
|
||||
mInputConnection = GeckoInputConnection.create(v);
|
||||
v.setInputConnectionHandler(mInputConnection);
|
||||
mInputConnection = v.setInputConnectionHandler();
|
||||
|
||||
layerController.setOnTouchListener(new View.OnTouchListener() {
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
|
@ -631,8 +632,6 @@ public class GeckoAppShell
|
|||
public static void enableLocation(final boolean enable) {
|
||||
getMainHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
LayerView v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
LocationManager lm = (LocationManager)
|
||||
GeckoApp.mAppContext.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
|
|
|
@ -62,6 +62,10 @@ import android.util.Log;
|
|||
*/
|
||||
|
||||
public class GeckoEvent {
|
||||
public interface Callback {
|
||||
public void callback(GeckoEvent event, String jsonData);
|
||||
}
|
||||
|
||||
private static final String LOGTAG = "GeckoEvent";
|
||||
|
||||
private static final int INVALID = -1;
|
||||
|
@ -137,6 +141,8 @@ public class GeckoEvent {
|
|||
|
||||
public int mNativeWindow;
|
||||
|
||||
Callback mCallback;
|
||||
|
||||
private GeckoEvent(int evType) {
|
||||
mType = evType;
|
||||
}
|
||||
|
@ -359,12 +365,11 @@ public class GeckoEvent {
|
|||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createSizeChangedEvent(int w, int h, int screenw, int screenh, int tilew, int tileh) {
|
||||
public static GeckoEvent createSizeChangedEvent(int w, int h, int screenw, int screenh) {
|
||||
GeckoEvent event = new GeckoEvent(SIZE_CHANGED);
|
||||
event.mPoints = new Point[3];
|
||||
event.mPoints = new Point[2];
|
||||
event.mPoints[0] = new Point(w, h);
|
||||
event.mPoints[1] = new Point(screenw, screenh);
|
||||
event.mPoints[2] = new Point(tilew, tileh);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
@ -409,4 +414,10 @@ public class GeckoEvent {
|
|||
event.mMetaState = tabId;
|
||||
return event;
|
||||
}
|
||||
|
||||
public void doCallback(String jsonData) {
|
||||
if (mCallback != null) {
|
||||
mCallback.callback(this, jsonData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,16 +108,17 @@ FENNEC_JAVA_FILES = \
|
|||
gfx/CairoImage.java \
|
||||
gfx/CairoUtils.java \
|
||||
gfx/CheckerboardImage.java \
|
||||
gfx/FlexibleGLSurfaceView.java \
|
||||
gfx/FloatSize.java \
|
||||
gfx/GeckoSoftwareLayerClient.java \
|
||||
gfx/GeckoLayerClient.java \
|
||||
gfx/GLController.java \
|
||||
gfx/GLThread.java \
|
||||
gfx/InputConnectionHandler.java \
|
||||
gfx/IntSize.java \
|
||||
gfx/Layer.java \
|
||||
gfx/LayerClient.java \
|
||||
gfx/LayerController.java \
|
||||
gfx/LayerRenderer.java \
|
||||
gfx/LayerView.java \
|
||||
gfx/MultiTileLayer.java \
|
||||
gfx/NinePatchTileLayer.java \
|
||||
gfx/PanningPerfAPI.java \
|
||||
gfx/PlaceholderLayerClient.java \
|
||||
|
@ -130,8 +131,9 @@ FENNEC_JAVA_FILES = \
|
|||
gfx/TextureGenerator.java \
|
||||
gfx/TextureReaper.java \
|
||||
gfx/TileLayer.java \
|
||||
gfx/ViewTransform.java \
|
||||
gfx/ViewportMetrics.java \
|
||||
gfx/WidgetTileLayer.java \
|
||||
gfx/VirtualLayer.java \
|
||||
ui/Axis.java \
|
||||
ui/PanZoomController.java \
|
||||
ui/SimpleScaleGestureDetector.java \
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public class FlexibleGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
|
||||
private static final String LOGTAG = "GeckoFlexibleGLSurfaceView";
|
||||
|
||||
private GLSurfaceView.Renderer mRenderer;
|
||||
private GLThread mGLThread; // Protected by this class's monitor.
|
||||
private GLController mController;
|
||||
private Listener mListener;
|
||||
|
||||
public FlexibleGLSurfaceView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public FlexibleGLSurfaceView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
SurfaceHolder holder = getHolder();
|
||||
holder.addCallback(this);
|
||||
holder.setFormat(PixelFormat.RGB_565);
|
||||
|
||||
mController = new GLController(this);
|
||||
}
|
||||
|
||||
public void setRenderer(GLSurfaceView.Renderer renderer) {
|
||||
mRenderer = renderer;
|
||||
}
|
||||
|
||||
public GLSurfaceView.Renderer getRenderer() {
|
||||
return mRenderer;
|
||||
}
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public synchronized void requestRender() {
|
||||
if (mGLThread != null) {
|
||||
mGLThread.renderFrame();
|
||||
}
|
||||
if (mListener != null) {
|
||||
mListener.renderRequested();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Java GL thread. After this is called, the FlexibleGLSurfaceView may be used just
|
||||
* like a GLSurfaceView. It is illegal to access the controller after this has been called.
|
||||
*/
|
||||
public synchronized void createGLThread() {
|
||||
if (mGLThread != null) {
|
||||
throw new FlexibleGLSurfaceViewException("createGLThread() called with a GL thread " +
|
||||
"already in place!");
|
||||
}
|
||||
|
||||
Log.e(LOGTAG, "### Creating GL thread!");
|
||||
mGLThread = new GLThread(mController);
|
||||
mGLThread.start();
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the Java GL thread. Returns a Thread that completes when the Java GL thread is
|
||||
* fully shut down.
|
||||
*/
|
||||
public synchronized Thread destroyGLThread() {
|
||||
// Wait for the GL thread to be started.
|
||||
Log.e(LOGTAG, "### Waiting for GL thread to be created...");
|
||||
while (mGLThread == null) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(LOGTAG, "### Destroying GL thread!");
|
||||
Thread glThread = mGLThread;
|
||||
mGLThread.shutdown();
|
||||
mGLThread = null;
|
||||
return glThread;
|
||||
}
|
||||
|
||||
public synchronized void recreateSurface() {
|
||||
if (mGLThread == null) {
|
||||
throw new FlexibleGLSurfaceViewException("recreateSurface() called with no GL " +
|
||||
"thread active!");
|
||||
}
|
||||
|
||||
mGLThread.recreateSurface();
|
||||
}
|
||||
|
||||
public synchronized GLController getGLController() {
|
||||
if (mGLThread != null) {
|
||||
throw new FlexibleGLSurfaceViewException("getGLController() called with a GL thread " +
|
||||
"active; shut down the GL thread first!");
|
||||
}
|
||||
|
||||
return mController;
|
||||
}
|
||||
|
||||
public synchronized void surfaceChanged(SurfaceHolder holder, int format, int width,
|
||||
int height) {
|
||||
mController.sizeChanged(width, height);
|
||||
if (mGLThread != null) {
|
||||
mGLThread.surfaceChanged(width, height);
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.surfaceChanged(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void surfaceCreated(SurfaceHolder holder) {
|
||||
mController.surfaceCreated();
|
||||
if (mGLThread != null) {
|
||||
mGLThread.surfaceCreated();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void surfaceDestroyed(SurfaceHolder holder) {
|
||||
mController.surfaceDestroyed();
|
||||
if (mGLThread != null) {
|
||||
mGLThread.surfaceDestroyed();
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.compositionPauseRequested();
|
||||
}
|
||||
}
|
||||
|
||||
// Called from the compositor thread
|
||||
public static GLController registerCxxCompositor() {
|
||||
try {
|
||||
Log.e(LOGTAG, "### registerCxxCompositor point A");
|
||||
System.out.println("register layer comp");
|
||||
Log.e(LOGTAG, "### registerCxxCompositor point B");
|
||||
FlexibleGLSurfaceView flexView = (FlexibleGLSurfaceView)GeckoApp.mAppContext.getLayerController().getView();
|
||||
Log.e(LOGTAG, "### registerCxxCompositor point C: " + flexView);
|
||||
try {
|
||||
flexView.destroyGLThread().join();
|
||||
} catch (InterruptedException e) {}
|
||||
Log.e(LOGTAG, "### registerCxxCompositor point D: " + flexView.getGLController());
|
||||
return flexView.getGLController();
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "### Exception! " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void renderRequested();
|
||||
void compositionPauseRequested();
|
||||
void compositionResumeRequested();
|
||||
void surfaceChanged(int width, int height);
|
||||
}
|
||||
|
||||
public static class FlexibleGLSurfaceViewException extends RuntimeException {
|
||||
public static final long serialVersionUID = 1L;
|
||||
|
||||
FlexibleGLSurfaceViewException(String e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import javax.microedition.khronos.egl.EGL10;
|
||||
import javax.microedition.khronos.egl.EGL11;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.egl.EGLContext;
|
||||
import javax.microedition.khronos.egl.EGLDisplay;
|
||||
import javax.microedition.khronos.egl.EGLSurface;
|
||||
import javax.microedition.khronos.opengles.GL;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
public class GLController {
|
||||
private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
|
||||
private static final String LOGTAG = "GeckoGLController";
|
||||
|
||||
private FlexibleGLSurfaceView mView;
|
||||
private int mGLVersion;
|
||||
private boolean mSurfaceValid;
|
||||
private int mWidth, mHeight;
|
||||
|
||||
private EGL10 mEGL;
|
||||
private EGLDisplay mEGLDisplay;
|
||||
private EGLConfig mEGLConfig;
|
||||
private EGLContext mEGLContext;
|
||||
private EGLSurface mEGLSurface;
|
||||
|
||||
private GL mGL;
|
||||
|
||||
private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4;
|
||||
|
||||
private static final int[] CONFIG_SPEC = {
|
||||
EGL10.EGL_RED_SIZE, 5,
|
||||
EGL10.EGL_GREEN_SIZE, 6,
|
||||
EGL10.EGL_BLUE_SIZE, 5,
|
||||
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
|
||||
EGL10.EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
|
||||
EGL10.EGL_NONE
|
||||
};
|
||||
|
||||
public GLController(FlexibleGLSurfaceView view) {
|
||||
mView = view;
|
||||
mGLVersion = 2;
|
||||
mSurfaceValid = false;
|
||||
}
|
||||
|
||||
public void setGLVersion(int version) {
|
||||
mGLVersion = version;
|
||||
}
|
||||
|
||||
/** You must call this on the same thread you intend to use OpenGL on. */
|
||||
public void initGLContext() {
|
||||
initEGLContext();
|
||||
createEGLSurface();
|
||||
}
|
||||
|
||||
public void disposeGLContext() {
|
||||
if (!mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
|
||||
EGL10.EGL_NO_CONTEXT)) {
|
||||
throw new GLControllerException("EGL context could not be released!");
|
||||
}
|
||||
|
||||
if (mEGLSurface != null) {
|
||||
if (!mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)) {
|
||||
throw new GLControllerException("EGL surface could not be destroyed!");
|
||||
}
|
||||
|
||||
mEGLSurface = null;
|
||||
}
|
||||
|
||||
if (mEGLContext == null) {
|
||||
if (!mEGL.eglDestroyContext(mEGLDisplay, mEGLContext)) {
|
||||
throw new GLControllerException("EGL context could not be destroyed!");
|
||||
}
|
||||
|
||||
mGL = null;
|
||||
mEGLDisplay = null;
|
||||
mEGLConfig = null;
|
||||
mEGLContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
public GL getGL() { return mEGLContext.getGL(); }
|
||||
public EGLDisplay getEGLDisplay() { return mEGLDisplay; }
|
||||
public EGLConfig getEGLConfig() { return mEGLConfig; }
|
||||
public EGLContext getEGLContext() { return mEGLContext; }
|
||||
public EGLSurface getEGLSurface() { return mEGLSurface; }
|
||||
public FlexibleGLSurfaceView getView() { return mView; }
|
||||
|
||||
public boolean hasSurface() {
|
||||
return mEGLSurface != null;
|
||||
}
|
||||
|
||||
public boolean swapBuffers() {
|
||||
return mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface);
|
||||
}
|
||||
|
||||
public boolean checkForLostContext() {
|
||||
if (mEGL.eglGetError() != EGL11.EGL_CONTEXT_LOST) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mEGLDisplay = null;
|
||||
mEGLConfig = null;
|
||||
mEGLContext = null;
|
||||
mEGLSurface = null;
|
||||
mGL = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void waitForValidSurface() {
|
||||
while (!mSurfaceValid) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
public synchronized int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
synchronized void surfaceCreated() {
|
||||
mSurfaceValid = true;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized void surfaceDestroyed() {
|
||||
mSurfaceValid = false;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized void sizeChanged(int newWidth, int newHeight) {
|
||||
mWidth = newWidth;
|
||||
mHeight = newHeight;
|
||||
}
|
||||
|
||||
private void initEGL() {
|
||||
mEGL = (EGL10)EGLContext.getEGL();
|
||||
|
||||
mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
|
||||
if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) {
|
||||
throw new GLControllerException("eglGetDisplay() failed");
|
||||
}
|
||||
|
||||
int[] version = new int[2];
|
||||
if (!mEGL.eglInitialize(mEGLDisplay, version)) {
|
||||
throw new GLControllerException("eglInitialize() failed");
|
||||
}
|
||||
|
||||
mEGLConfig = chooseConfig();
|
||||
}
|
||||
|
||||
private void initEGLContext() {
|
||||
initEGL();
|
||||
|
||||
int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, mGLVersion, EGL10.EGL_NONE };
|
||||
mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig, EGL10.EGL_NO_CONTEXT,
|
||||
attribList);
|
||||
if (mEGLContext == null || mEGLContext == EGL10.EGL_NO_CONTEXT) {
|
||||
throw new GLControllerException("createContext() failed");
|
||||
}
|
||||
}
|
||||
|
||||
private EGLConfig chooseConfig() {
|
||||
int[] numConfigs = new int[1];
|
||||
if (!mEGL.eglChooseConfig(mEGLDisplay, CONFIG_SPEC, null, 0, numConfigs) ||
|
||||
numConfigs[0] <= 0) {
|
||||
throw new GLControllerException("No available EGL configurations");
|
||||
}
|
||||
|
||||
EGLConfig[] configs = new EGLConfig[numConfigs[0]];
|
||||
if (!mEGL.eglChooseConfig(mEGLDisplay, CONFIG_SPEC, configs, numConfigs[0], numConfigs)) {
|
||||
throw new GLControllerException("No EGL configuration for that specification");
|
||||
}
|
||||
|
||||
// Select the first 565 RGB configuration.
|
||||
int[] red = new int[1], green = new int[1], blue = new int[1];
|
||||
for (EGLConfig config : configs) {
|
||||
mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_RED_SIZE, red);
|
||||
mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_GREEN_SIZE, green);
|
||||
mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_BLUE_SIZE, blue);
|
||||
if (red[0] == 5 && green[0] == 6 && blue[0] == 5) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
throw new GLControllerException("No suitable EGL configuration found");
|
||||
}
|
||||
|
||||
private void createEGLSurface() {
|
||||
SurfaceHolder surfaceHolder = mView.getHolder();
|
||||
mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surfaceHolder, null);
|
||||
if (mEGLSurface == null || mEGLSurface == EGL10.EGL_NO_SURFACE) {
|
||||
throw new GLControllerException("EGL window surface could not be created!");
|
||||
}
|
||||
|
||||
if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
|
||||
throw new GLControllerException("EGL surface could not be made into the current " +
|
||||
"surface!");
|
||||
}
|
||||
|
||||
mGL = mEGLContext.getGL();
|
||||
|
||||
if (mView.getRenderer() != null) {
|
||||
mView.getRenderer().onSurfaceCreated((GL10)mGL, mEGLConfig);
|
||||
mView.getRenderer().onSurfaceChanged((GL10)mGL, mView.getWidth(), mView.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
// Provides an EGLSurface without assuming ownership of this surface.
|
||||
private EGLSurface provideEGLSurface() {
|
||||
if (mEGL == null) {
|
||||
initEGL();
|
||||
}
|
||||
|
||||
SurfaceHolder surfaceHolder = mView.getHolder();
|
||||
mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surfaceHolder, null);
|
||||
if (mEGLSurface == null || mEGLSurface == EGL10.EGL_NO_SURFACE) {
|
||||
throw new GLControllerException("EGL window surface could not be created!");
|
||||
}
|
||||
|
||||
return mEGLSurface;
|
||||
}
|
||||
|
||||
public static class GLControllerException extends RuntimeException {
|
||||
public static final long serialVersionUID = 1L;
|
||||
|
||||
GLControllerException(String e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.view.SurfaceHolder;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
// A GL thread managed by Java. It is not necessary to use this class to use the
|
||||
// FlexibleGLSurfaceView, but it can be helpful, especially if the GL rendering is to be done
|
||||
// entirely in Java.
|
||||
class GLThread extends Thread {
|
||||
private LinkedBlockingQueue<Runnable> mQueue;
|
||||
private GLController mController;
|
||||
private boolean mRenderQueued;
|
||||
|
||||
public GLThread(GLController controller) {
|
||||
mQueue = new LinkedBlockingQueue<Runnable>();
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
Runnable runnable;
|
||||
try {
|
||||
runnable = mQueue.take();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
runnable.run();
|
||||
if (runnable instanceof ShutdownMessage) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void recreateSurface() {
|
||||
mQueue.add(new RecreateSurfaceMessage());
|
||||
}
|
||||
|
||||
public void renderFrame() {
|
||||
// Make sure there's only one render event in the queue at a time.
|
||||
synchronized (this) {
|
||||
if (!mRenderQueued) {
|
||||
mQueue.add(new RenderFrameMessage());
|
||||
mRenderQueued = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
mQueue.add(new ShutdownMessage());
|
||||
}
|
||||
|
||||
public void surfaceChanged(int width, int height) {
|
||||
mQueue.add(new SizeChangedMessage(width, height));
|
||||
}
|
||||
|
||||
public void surfaceCreated() {
|
||||
mQueue.add(new SurfaceCreatedMessage());
|
||||
}
|
||||
|
||||
public void surfaceDestroyed() {
|
||||
mQueue.add(new SurfaceDestroyedMessage());
|
||||
}
|
||||
|
||||
private void doRecreateSurface() {
|
||||
mController.disposeGLContext();
|
||||
mController.initGLContext();
|
||||
}
|
||||
|
||||
private GLSurfaceView.Renderer getRenderer() {
|
||||
return mController.getView().getRenderer();
|
||||
}
|
||||
|
||||
private class RecreateSurfaceMessage implements Runnable {
|
||||
public void run() {
|
||||
doRecreateSurface();
|
||||
}
|
||||
}
|
||||
|
||||
private class RenderFrameMessage implements Runnable {
|
||||
public void run() {
|
||||
synchronized (GLThread.this) {
|
||||
mRenderQueued = false;
|
||||
}
|
||||
|
||||
// Bail out if the surface was lost.
|
||||
if (mController.getEGLSurface() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLSurfaceView.Renderer renderer = getRenderer();
|
||||
if (renderer != null) {
|
||||
renderer.onDrawFrame((GL10)mController.getGL());
|
||||
}
|
||||
|
||||
mController.swapBuffers();
|
||||
//if (!mController.swapBuffers() && mController.checkForLostContext()) {
|
||||
// doRecreateSurface();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private class ShutdownMessage implements Runnable {
|
||||
public void run() {
|
||||
mController.disposeGLContext();
|
||||
mController = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class SizeChangedMessage implements Runnable {
|
||||
private int mWidth, mHeight;
|
||||
|
||||
public SizeChangedMessage(int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
GLSurfaceView.Renderer renderer = getRenderer();
|
||||
if (renderer != null) {
|
||||
renderer.onSurfaceChanged((GL10)mController.getGL(), mWidth, mHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SurfaceCreatedMessage implements Runnable {
|
||||
public void run() {
|
||||
if (!mController.hasSurface()) {
|
||||
mController.initGLContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SurfaceDestroyedMessage implements Runnable {
|
||||
public void run() {
|
||||
mController.disposeGLContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,488 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Chris Lord <chrislord.net@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.GeckoEventListener;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.SystemClock;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class GeckoLayerClient implements GeckoEventListener,
|
||||
FlexibleGLSurfaceView.Listener {
|
||||
private static final String LOGTAG = "GeckoLayerClient";
|
||||
|
||||
private LayerController mLayerController;
|
||||
private LayerRenderer mLayerRenderer;
|
||||
private boolean mLayerRendererInitialized;
|
||||
|
||||
private IntSize mScreenSize;
|
||||
private IntSize mWindowSize;
|
||||
private IntSize mBufferSize;
|
||||
|
||||
private VirtualLayer mRootLayer;
|
||||
|
||||
/* The viewport that Gecko is currently displaying. */
|
||||
private ViewportMetrics mGeckoViewport;
|
||||
|
||||
/* The viewport that Gecko will display when drawing is finished */
|
||||
private ViewportMetrics mNewGeckoViewport;
|
||||
|
||||
private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L;
|
||||
private long mLastViewportChangeTime;
|
||||
private boolean mPendingViewportAdjust;
|
||||
private boolean mViewportSizeChanged;
|
||||
private boolean mIgnorePaintsPendingViewportSizeChange;
|
||||
private boolean mFirstPaint = true;
|
||||
|
||||
// mUpdateViewportOnEndDraw is used to indicate that we received a
|
||||
// viewport update notification while drawing. therefore, when the
|
||||
// draw finishes, we need to update the entire viewport rather than
|
||||
// just the page size. this boolean should always be accessed from
|
||||
// inside a transaction, so no synchronization is needed.
|
||||
private boolean mUpdateViewportOnEndDraw;
|
||||
|
||||
private String mLastCheckerboardColor;
|
||||
|
||||
private static Pattern sColorPattern;
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
private DrawListener mDrawListener;
|
||||
|
||||
public GeckoLayerClient(Context context) {
|
||||
mScreenSize = new IntSize(0, 0);
|
||||
mBufferSize = new IntSize(0, 0);
|
||||
}
|
||||
|
||||
/** Attaches the root layer to the layer controller so that Gecko appears. */
|
||||
void setLayerController(LayerController layerController) {
|
||||
mLayerController = layerController;
|
||||
|
||||
layerController.setRoot(mRootLayer);
|
||||
if (mGeckoViewport != null) {
|
||||
layerController.setViewportMetrics(mGeckoViewport);
|
||||
}
|
||||
|
||||
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
|
||||
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
|
||||
|
||||
sendResizeEventIfNecessary(false);
|
||||
|
||||
LayerView view = layerController.getView();
|
||||
view.setListener(this);
|
||||
|
||||
mLayerRenderer = new LayerRenderer(view);
|
||||
}
|
||||
|
||||
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */
|
||||
public Rect beginDrawing(int width, int height, String metadata) {
|
||||
Log.e(LOGTAG, "### beginDrawing " + width + " " + height);
|
||||
|
||||
// If the viewport has changed but we still don't have the latest viewport
|
||||
// from Gecko, ignore the viewport passed to us from Gecko since that is going
|
||||
// to be wrong.
|
||||
if (!mFirstPaint && mIgnorePaintsPendingViewportSizeChange) {
|
||||
return null;
|
||||
}
|
||||
mFirstPaint = false;
|
||||
|
||||
// If we've changed surface types, cancel this draw
|
||||
if (initializeVirtualLayer()) {
|
||||
Log.e(LOGTAG, "### Cancelling draw due to virtual layer initialization");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject viewportObject = new JSONObject(metadata);
|
||||
mNewGeckoViewport = new ViewportMetrics(viewportObject);
|
||||
|
||||
Log.e(LOGTAG, "### beginDrawing new Gecko viewport " + mNewGeckoViewport);
|
||||
|
||||
// Update the background color, if it's present.
|
||||
String backgroundColorString = viewportObject.optString("backgroundColor");
|
||||
if (backgroundColorString != null && !backgroundColorString.equals(mLastCheckerboardColor)) {
|
||||
mLastCheckerboardColor = backgroundColorString;
|
||||
mLayerController.setCheckerboardColor(parseColorFromGecko(backgroundColorString));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Aborting draw, bad viewport description: " + metadata);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Make sure we don't spend time painting areas we aren't interested in.
|
||||
// Only do this if the Gecko viewport isn't going to override our viewport.
|
||||
Rect bufferRect = new Rect(0, 0, width, height);
|
||||
|
||||
if (!mUpdateViewportOnEndDraw) {
|
||||
// First, find out our ideal displayport. This would be what we would
|
||||
// send to Gecko if adjustViewport were called now.
|
||||
ViewportMetrics currentMetrics = mLayerController.getViewportMetrics();
|
||||
PointF currentBestOrigin = RectUtils.getOrigin(currentMetrics.getClampedViewport());
|
||||
|
||||
Rect currentRect = RectUtils.round(new RectF(currentBestOrigin.x, currentBestOrigin.y,
|
||||
currentBestOrigin.x + width, currentBestOrigin.y + height));
|
||||
|
||||
// Second, store Gecko's displayport.
|
||||
PointF currentOrigin = mNewGeckoViewport.getOrigin();
|
||||
bufferRect = RectUtils.round(new RectF(currentOrigin.x, currentOrigin.y,
|
||||
currentOrigin.x + width, currentOrigin.y + height));
|
||||
|
||||
int area = width * height;
|
||||
|
||||
// Take the intersection of the two as the area we're interested in rendering.
|
||||
if (!bufferRect.intersect(currentRect)) {
|
||||
Log.w(LOGTAG, "Prediction would avoid useless paint of " + area + " pixels (100.0%)");
|
||||
// If there's no intersection, we have no need to render anything,
|
||||
// but make sure to update the page size.
|
||||
updateViewport(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
int wasted = area - (bufferRect.width() * bufferRect.height());
|
||||
Log.w(LOGTAG, "Prediction would avoid useless paint of " + wasted + " pixels (" + ((float)wasted * 100.0f / area) + "%)");
|
||||
|
||||
bufferRect.offset(Math.round(-currentOrigin.x), Math.round(-currentOrigin.y));
|
||||
}
|
||||
|
||||
if (mBufferSize.width != width || mBufferSize.height != height) {
|
||||
mBufferSize = new IntSize(width, height);
|
||||
}
|
||||
|
||||
return bufferRect;
|
||||
}
|
||||
|
||||
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */
|
||||
public void endDrawing() {
|
||||
updateViewport(!mUpdateViewportOnEndDraw);
|
||||
mUpdateViewportOnEndDraw = false;
|
||||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing");
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
if (mDrawListener != null) {
|
||||
mDrawListener.drawFinished();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateViewport(boolean onlyUpdatePageSize) {
|
||||
synchronized (mLayerController) {
|
||||
// save and restore the viewport size stored in java; never let the
|
||||
// JS-side viewport dimensions override the java-side ones because
|
||||
// java is the One True Source of this information, and allowing JS
|
||||
// to override can lead to race conditions where this data gets clobbered.
|
||||
FloatSize viewportSize = mLayerController.getViewportSize();
|
||||
mGeckoViewport = mNewGeckoViewport;
|
||||
mGeckoViewport.setSize(viewportSize);
|
||||
|
||||
PointF origin = mGeckoViewport.getOrigin();
|
||||
mRootLayer.setOriginAndResolution(PointUtils.round(origin), mGeckoViewport.getZoomFactor());
|
||||
|
||||
// Set the new origin and resolution instantly.
|
||||
mRootLayer.performUpdates(null);
|
||||
|
||||
Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize +
|
||||
" getTileViewport " + mGeckoViewport);
|
||||
|
||||
if (onlyUpdatePageSize) {
|
||||
// Don't adjust page size when zooming unless zoom levels are
|
||||
// approximately equal.
|
||||
if (FloatUtils.fuzzyEquals(mLayerController.getZoomFactor(),
|
||||
mGeckoViewport.getZoomFactor()))
|
||||
mLayerController.setPageSize(mGeckoViewport.getPageSize());
|
||||
} else {
|
||||
mLayerController.setViewportMetrics(mGeckoViewport);
|
||||
mLayerController.abortPanZoomAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Informs Gecko that the screen size has changed. */
|
||||
private void sendResizeEventIfNecessary(boolean force) {
|
||||
Log.d(LOGTAG, "### sendResizeEventIfNecessary " + force);
|
||||
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
|
||||
IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
|
||||
IntSize newWindowSize = getBufferSize();
|
||||
|
||||
boolean screenSizeChanged = mScreenSize == null || !mScreenSize.equals(newScreenSize);
|
||||
boolean windowSizeChanged = mWindowSize == null || !mWindowSize.equals(newWindowSize);
|
||||
|
||||
if (!force && !screenSizeChanged && !windowSizeChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
mScreenSize = newScreenSize;
|
||||
mWindowSize = newWindowSize;
|
||||
|
||||
if (screenSizeChanged) {
|
||||
Log.i(LOGTAG, "### Screen-size changed to " + mScreenSize);
|
||||
}
|
||||
|
||||
if (windowSizeChanged) {
|
||||
Log.i(LOGTAG, "### Window-size changed to " + mWindowSize);
|
||||
}
|
||||
|
||||
IntSize bufferSize = getBufferSize();
|
||||
GeckoEvent event = GeckoEvent.createSizeChangedEvent(mWindowSize.width, mWindowSize.height, // Window (buffer) size
|
||||
mScreenSize.width, mScreenSize.height); // Screen size
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
// Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color
|
||||
// cannot be parsed, returns white.
|
||||
private static int parseColorFromGecko(String string) {
|
||||
if (sColorPattern == null) {
|
||||
sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)");
|
||||
}
|
||||
|
||||
Matcher matcher = sColorPattern.matcher(string);
|
||||
if (!matcher.matches()) {
|
||||
return Color.WHITE;
|
||||
}
|
||||
|
||||
int r = Integer.parseInt(matcher.group(1));
|
||||
int g = Integer.parseInt(matcher.group(2));
|
||||
int b = Integer.parseInt(matcher.group(3));
|
||||
return Color.rgb(r, g, b);
|
||||
}
|
||||
|
||||
private boolean initializeVirtualLayer() {
|
||||
if (mRootLayer != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.e(LOGTAG, "### Creating virtual layer");
|
||||
VirtualLayer virtualLayer = new VirtualLayer();
|
||||
virtualLayer.setSize(getBufferSize());
|
||||
mLayerController.setRoot(virtualLayer);
|
||||
mRootLayer = virtualLayer;
|
||||
|
||||
sendResizeEventIfNecessary(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private IntSize getBufferSize() {
|
||||
View view = mLayerController.getView();
|
||||
IntSize size = new IntSize(view.getWidth(), view.getHeight());
|
||||
Log.e(LOGTAG, "### getBufferSize " + size);
|
||||
return size;
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void adjustViewportWithThrottling() {
|
||||
if (!mLayerController.getRedrawHint())
|
||||
return;
|
||||
|
||||
if (mPendingViewportAdjust)
|
||||
return;
|
||||
|
||||
long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime;
|
||||
if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) {
|
||||
mLayerController.getView().postDelayed(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
mPendingViewportAdjust = false;
|
||||
adjustViewport();
|
||||
}
|
||||
}, MIN_VIEWPORT_CHANGE_DELAY - timeDelta);
|
||||
mPendingViewportAdjust = true;
|
||||
return;
|
||||
}
|
||||
|
||||
adjustViewport();
|
||||
}
|
||||
|
||||
void viewportSizeChanged() {
|
||||
mViewportSizeChanged = true;
|
||||
mIgnorePaintsPendingViewportSizeChange = true;
|
||||
}
|
||||
|
||||
private void adjustViewport() {
|
||||
ViewportMetrics viewportMetrics =
|
||||
new ViewportMetrics(mLayerController.getViewportMetrics());
|
||||
|
||||
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(viewportMetrics));
|
||||
if (mViewportSizeChanged) {
|
||||
mViewportSizeChanged = false;
|
||||
GeckoAppShell.viewSizeChanged();
|
||||
}
|
||||
|
||||
mLastViewportChangeTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/** Implementation of GeckoEventListener. */
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
if ("Viewport:UpdateAndDraw".equals(event)) {
|
||||
Log.e(LOGTAG, "### Java side Viewport:UpdateAndDraw()!");
|
||||
mUpdateViewportOnEndDraw = true;
|
||||
mIgnorePaintsPendingViewportSizeChange = false;
|
||||
|
||||
// Redraw everything.
|
||||
Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createDrawEvent(rect));
|
||||
} else if ("Viewport:UpdateLater".equals(event)) {
|
||||
Log.e(LOGTAG, "### Java side Viewport:UpdateLater()!");
|
||||
mUpdateViewportOnEndDraw = true;
|
||||
mIgnorePaintsPendingViewportSizeChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
void geometryChanged() {
|
||||
/* Let Gecko know if the screensize has changed */
|
||||
sendResizeEventIfNecessary(false);
|
||||
adjustViewportWithThrottling();
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mBufferSize.width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mBufferSize.height;
|
||||
}
|
||||
|
||||
public ViewportMetrics getGeckoViewportMetrics() {
|
||||
// Return a copy, as we modify this inside the Gecko thread
|
||||
if (mGeckoViewport != null)
|
||||
return new ViewportMetrics(mGeckoViewport);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */
|
||||
public ViewTransform getViewTransform() {
|
||||
Log.e(LOGTAG, "### getViewTransform()");
|
||||
|
||||
// NB: We don't begin a transaction here because this can be called in a synchronous
|
||||
// manner between beginDrawing() and endDrawing(), and that will cause a deadlock.
|
||||
|
||||
synchronized (mLayerController) {
|
||||
ViewportMetrics viewportMetrics = mLayerController.getViewportMetrics();
|
||||
PointF viewportOrigin = viewportMetrics.getOrigin();
|
||||
float scrollX = viewportOrigin.x;
|
||||
float scrollY = viewportOrigin.y;
|
||||
float zoomFactor = viewportMetrics.getZoomFactor();
|
||||
Log.e(LOGTAG, "### Viewport metrics = " + viewportMetrics + " tile reso = " +
|
||||
mRootLayer.getResolution());
|
||||
return new ViewTransform(scrollX, scrollY, zoomFactor);
|
||||
}
|
||||
}
|
||||
|
||||
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */
|
||||
public LayerRenderer.Frame createFrame() {
|
||||
// Create the shaders and textures if necessary.
|
||||
if (!mLayerRendererInitialized) {
|
||||
mLayerRenderer.createProgram();
|
||||
mLayerRendererInitialized = true;
|
||||
}
|
||||
|
||||
// Build the contexts and create the frame.
|
||||
Layer.RenderContext pageContext = mLayerRenderer.createPageContext();
|
||||
Layer.RenderContext screenContext = mLayerRenderer.createScreenContext();
|
||||
return mLayerRenderer.createFrame(pageContext, screenContext);
|
||||
}
|
||||
|
||||
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */
|
||||
public void activateProgram() {
|
||||
mLayerRenderer.activateProgram();
|
||||
}
|
||||
|
||||
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */
|
||||
public void deactivateProgram() {
|
||||
mLayerRenderer.deactivateProgram();
|
||||
}
|
||||
|
||||
/** Implementation of FlexibleGLSurfaceView.Listener */
|
||||
public void renderRequested() {
|
||||
Log.e(LOGTAG, "### Render requested, scheduling composite");
|
||||
GeckoAppShell.scheduleComposite();
|
||||
}
|
||||
|
||||
/** Implementation of FlexibleGLSurfaceView.Listener */
|
||||
public void compositionPauseRequested() {
|
||||
Log.e(LOGTAG, "### Scheduling PauseComposition");
|
||||
GeckoAppShell.schedulePauseComposition();
|
||||
}
|
||||
|
||||
/** Implementation of FlexibleGLSurfaceView.Listener */
|
||||
public void compositionResumeRequested() {
|
||||
Log.e(LOGTAG, "### Scheduling ResumeComposition");
|
||||
GeckoAppShell.scheduleResumeComposition();
|
||||
}
|
||||
|
||||
/** Implementation of FlexibleGLSurfaceView.Listener */
|
||||
public void surfaceChanged(int width, int height) {
|
||||
compositionPauseRequested();
|
||||
mLayerController.setViewportSize(new FloatSize(width, height));
|
||||
compositionResumeRequested();
|
||||
renderRequested();
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */
|
||||
public void setDrawListener(DrawListener listener) {
|
||||
mDrawListener = listener;
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! This is used via reflection by robocop. */
|
||||
public interface DrawListener {
|
||||
public void drawFinished();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,575 +0,0 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Chris Lord <chrislord.net@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.gfx.CairoImage;
|
||||
import org.mozilla.gecko.gfx.IntSize;
|
||||
import org.mozilla.gecko.gfx.LayerClient;
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
import org.mozilla.gecko.gfx.LayerRenderer;
|
||||
import org.mozilla.gecko.gfx.MultiTileLayer;
|
||||
import org.mozilla.gecko.gfx.PointUtils;
|
||||
import org.mozilla.gecko.gfx.WidgetTileLayer;
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.GeckoEventListener;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.SystemClock;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our
|
||||
* compositor.
|
||||
*
|
||||
* TODO: Throttle down Gecko's priority when we pan and zoom.
|
||||
*/
|
||||
public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoSoftwareLayerClient";
|
||||
|
||||
private Context mContext;
|
||||
private int mFormat;
|
||||
private IntSize mScreenSize, mViewportSize;
|
||||
private IntSize mBufferSize;
|
||||
private ByteBuffer mBuffer;
|
||||
private Layer mTileLayer;
|
||||
|
||||
/* The viewport that Gecko is currently displaying. */
|
||||
private ViewportMetrics mGeckoViewport;
|
||||
|
||||
/* The viewport that Gecko will display when drawing is finished */
|
||||
private ViewportMetrics mNewGeckoViewport;
|
||||
|
||||
private CairoImage mCairoImage;
|
||||
|
||||
private static final IntSize TILE_SIZE = new IntSize(256, 256);
|
||||
|
||||
private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L;
|
||||
private long mLastViewportChangeTime;
|
||||
private boolean mPendingViewportAdjust;
|
||||
private boolean mViewportSizeChanged;
|
||||
|
||||
// Whether or not the last paint we got used direct texturing
|
||||
private boolean mHasDirectTexture;
|
||||
|
||||
// mUpdateViewportOnEndDraw is used to indicate that we received a
|
||||
// viewport update notification while drawing. therefore, when the
|
||||
// draw finishes, we need to update the entire viewport rather than
|
||||
// just the page size. this boolean should always be accessed from
|
||||
// inside a transaction, so no synchronization is needed.
|
||||
private boolean mUpdateViewportOnEndDraw;
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
private DrawListener mDrawListener;
|
||||
|
||||
private static Pattern sColorPattern;
|
||||
|
||||
public GeckoSoftwareLayerClient(Context context) {
|
||||
mContext = context;
|
||||
|
||||
mScreenSize = new IntSize(0, 0);
|
||||
mBufferSize = new IntSize(0, 0);
|
||||
mFormat = CairoImage.FORMAT_RGB16_565;
|
||||
|
||||
mCairoImage = new CairoImage() {
|
||||
@Override
|
||||
public ByteBuffer getBuffer() { return mBuffer; }
|
||||
@Override
|
||||
public IntSize getSize() { return mBufferSize; }
|
||||
@Override
|
||||
public int getFormat() { return mFormat; }
|
||||
};
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mBufferSize.width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mBufferSize.height;
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
if (mBuffer != null)
|
||||
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||
mBuffer = null;
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/** Attaches the root layer to the layer controller so that Gecko appears. */
|
||||
@Override
|
||||
public void setLayerController(LayerController layerController) {
|
||||
super.setLayerController(layerController);
|
||||
|
||||
layerController.setRoot(mTileLayer);
|
||||
if (mGeckoViewport != null) {
|
||||
layerController.setViewportMetrics(mGeckoViewport);
|
||||
}
|
||||
|
||||
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
|
||||
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
|
||||
GeckoAppShell.registerGeckoEventListener("Checkerboard:Toggle", this);
|
||||
|
||||
sendResizeEventIfNecessary();
|
||||
}
|
||||
|
||||
private boolean setHasDirectTexture(boolean hasDirectTexture) {
|
||||
if (mTileLayer != null && hasDirectTexture == mHasDirectTexture)
|
||||
return false;
|
||||
|
||||
mHasDirectTexture = hasDirectTexture;
|
||||
|
||||
if (mHasDirectTexture) {
|
||||
Log.i(LOGTAG, "Creating WidgetTileLayer");
|
||||
mTileLayer = new WidgetTileLayer(mCairoImage);
|
||||
} else {
|
||||
Log.i(LOGTAG, "Creating MultiTileLayer");
|
||||
mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE);
|
||||
}
|
||||
|
||||
getLayerController().setRoot(mTileLayer);
|
||||
|
||||
// Force a resize event to be sent because the results of this
|
||||
// are different depending on what tile system we're using
|
||||
sendResizeEventIfNecessary(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight,
|
||||
String metadata, boolean hasDirectTexture) {
|
||||
setHasDirectTexture(hasDirectTexture);
|
||||
|
||||
// Make sure the tile-size matches. If it doesn't, we could crash trying
|
||||
// to access invalid memory.
|
||||
if (mHasDirectTexture) {
|
||||
if (tileWidth != 0 || tileHeight != 0) {
|
||||
Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + tileHeight);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (tileWidth != TILE_SIZE.width || tileHeight != TILE_SIZE.height) {
|
||||
Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + tileHeight);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
LayerController controller = getLayerController();
|
||||
|
||||
try {
|
||||
JSONObject viewportObject = new JSONObject(metadata);
|
||||
mNewGeckoViewport = new ViewportMetrics(viewportObject);
|
||||
|
||||
// Update the background color, if it's present.
|
||||
String backgroundColorString = viewportObject.optString("backgroundColor");
|
||||
if (backgroundColorString != null) {
|
||||
controller.setCheckerboardColor(parseColorFromGecko(backgroundColorString));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Aborting draw, bad viewport description: " + metadata);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure we don't spend time painting areas we aren't interested in.
|
||||
// Only do this if the Gecko viewport isn't going to override our viewport.
|
||||
Rect bufferRect = new Rect(0, 0, width, height);
|
||||
|
||||
if (!mUpdateViewportOnEndDraw) {
|
||||
// First, find out our ideal displayport. We do this by taking the
|
||||
// clamped viewport origin and taking away the optimum viewport offset.
|
||||
// This would be what we would send to Gecko if adjustViewport were
|
||||
// called now.
|
||||
ViewportMetrics currentMetrics = controller.getViewportMetrics();
|
||||
PointF currentBestOrigin = RectUtils.getOrigin(currentMetrics.getClampedViewport());
|
||||
PointF viewportOffset = currentMetrics.getOptimumViewportOffset(new IntSize(width, height));
|
||||
currentBestOrigin.offset(-viewportOffset.x, -viewportOffset.y);
|
||||
|
||||
Rect currentRect = RectUtils.round(new RectF(currentBestOrigin.x, currentBestOrigin.y,
|
||||
currentBestOrigin.x + width, currentBestOrigin.y + height));
|
||||
|
||||
// Second, store Gecko's displayport.
|
||||
PointF currentOrigin = mNewGeckoViewport.getDisplayportOrigin();
|
||||
bufferRect = RectUtils.round(new RectF(currentOrigin.x, currentOrigin.y,
|
||||
currentOrigin.x + width, currentOrigin.y + height));
|
||||
|
||||
|
||||
// Take the intersection of the two as the area we're interested in rendering.
|
||||
if (!bufferRect.intersect(currentRect)) {
|
||||
// If there's no intersection, we have no need to render anything,
|
||||
// but make sure to update the viewport size.
|
||||
beginTransaction(mTileLayer);
|
||||
try {
|
||||
updateViewport(true);
|
||||
} finally {
|
||||
endTransaction(mTileLayer);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
bufferRect.offset(Math.round(-currentOrigin.x), Math.round(-currentOrigin.y));
|
||||
}
|
||||
|
||||
beginTransaction(mTileLayer);
|
||||
|
||||
// Synchronise the buffer size with Gecko.
|
||||
if (mBufferSize.width != width || mBufferSize.height != height) {
|
||||
mBufferSize = new IntSize(width, height);
|
||||
|
||||
// Reallocate the buffer if necessary
|
||||
if (mTileLayer instanceof MultiTileLayer) {
|
||||
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
|
||||
int size = mBufferSize.getArea() * bpp;
|
||||
if (mBuffer == null || mBuffer.capacity() != size) {
|
||||
// Free the old buffer
|
||||
if (mBuffer != null) {
|
||||
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||
mBuffer = null;
|
||||
}
|
||||
|
||||
mBuffer = GeckoAppShell.allocateDirectBuffer(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bufferRect;
|
||||
}
|
||||
|
||||
private void updateViewport(final boolean onlyUpdatePageSize) {
|
||||
// save and restore the viewport size stored in java; never let the
|
||||
// JS-side viewport dimensions override the java-side ones because
|
||||
// java is the One True Source of this information, and allowing JS
|
||||
// to override can lead to race conditions where this data gets clobbered.
|
||||
FloatSize viewportSize = getLayerController().getViewportSize();
|
||||
mGeckoViewport = mNewGeckoViewport;
|
||||
mGeckoViewport.setSize(viewportSize);
|
||||
|
||||
LayerController controller = getLayerController();
|
||||
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
|
||||
mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
|
||||
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
|
||||
|
||||
if (onlyUpdatePageSize) {
|
||||
// Don't adjust page size when zooming unless zoom levels are
|
||||
// approximately equal.
|
||||
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(),
|
||||
mGeckoViewport.getZoomFactor()))
|
||||
controller.setPageSize(mGeckoViewport.getPageSize());
|
||||
} else {
|
||||
controller.setViewportMetrics(mGeckoViewport);
|
||||
controller.abortPanZoomAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Would be cleaner if this took an android.graphics.Rect instead, but that would require
|
||||
* a little more JNI magic.
|
||||
*/
|
||||
public void endDrawing(int x, int y, int width, int height) {
|
||||
synchronized (getLayerController()) {
|
||||
try {
|
||||
updateViewport(!mUpdateViewportOnEndDraw);
|
||||
mUpdateViewportOnEndDraw = false;
|
||||
|
||||
if (mTileLayer instanceof MultiTileLayer) {
|
||||
Rect rect = new Rect(x, y, x + width, y + height);
|
||||
((MultiTileLayer)mTileLayer).invalidate(rect);
|
||||
}
|
||||
} finally {
|
||||
endTransaction(mTileLayer);
|
||||
}
|
||||
}
|
||||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing");
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
if (mDrawListener != null) {
|
||||
mDrawListener.drawFinished(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public ViewportMetrics getGeckoViewportMetrics() {
|
||||
// Return a copy, as we modify this inside the Gecko thread
|
||||
if (mGeckoViewport != null)
|
||||
return new ViewportMetrics(mGeckoViewport);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void copyPixelsFromMultiTileLayer(Bitmap target) {
|
||||
Canvas c = new Canvas(target);
|
||||
ByteBuffer tileBuffer = mBuffer.slice();
|
||||
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
|
||||
|
||||
for (int y = 0; y < mBufferSize.height; y += TILE_SIZE.height) {
|
||||
for (int x = 0; x < mBufferSize.width; x += TILE_SIZE.width) {
|
||||
// Calculate tile size
|
||||
IntSize tileSize = new IntSize(Math.min(mBufferSize.width - x, TILE_SIZE.width),
|
||||
Math.min(mBufferSize.height - y, TILE_SIZE.height));
|
||||
|
||||
// Create a Bitmap from this tile
|
||||
Bitmap tile = Bitmap.createBitmap(tileSize.width, tileSize.height,
|
||||
CairoUtils.cairoFormatTobitmapConfig(mFormat));
|
||||
tile.copyPixelsFromBuffer(tileBuffer.asIntBuffer());
|
||||
|
||||
// Copy the tile to the master Bitmap and recycle it
|
||||
c.drawBitmap(tile, x, y, null);
|
||||
tile.recycle();
|
||||
|
||||
// Progress the buffer to the next tile
|
||||
tileBuffer.position(tileSize.getArea() * bpp);
|
||||
tileBuffer = tileBuffer.slice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
if (mTileLayer == null)
|
||||
return null;
|
||||
|
||||
// Begin a tile transaction, otherwise the buffer can be destroyed while
|
||||
// we're reading from it.
|
||||
beginTransaction(mTileLayer);
|
||||
try {
|
||||
if (mBuffer == null || mBufferSize.width <= 0 || mBufferSize.height <= 0)
|
||||
return null;
|
||||
try {
|
||||
Bitmap b = null;
|
||||
|
||||
if (mTileLayer instanceof MultiTileLayer) {
|
||||
b = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height,
|
||||
CairoUtils.cairoFormatTobitmapConfig(mFormat));
|
||||
copyPixelsFromMultiTileLayer(b);
|
||||
} else {
|
||||
Log.w(LOGTAG, "getBitmap() called on a layer (" + mTileLayer + ") we don't know how to get a bitmap from");
|
||||
}
|
||||
|
||||
return b;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.w(LOGTAG, "Unable to create bitmap", oom);
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
endTransaction(mTileLayer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the back buffer. This function is for Gecko to use. */
|
||||
public ByteBuffer lockBuffer() {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gecko calls this function to signal that it is done with the back buffer. After this call,
|
||||
* it is forbidden for Gecko to touch the buffer.
|
||||
*/
|
||||
public void unlockBuffer() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geometryChanged() {
|
||||
/* Let Gecko know if the screensize has changed */
|
||||
sendResizeEventIfNecessary();
|
||||
render();
|
||||
}
|
||||
|
||||
private void sendResizeEventIfNecessary() {
|
||||
sendResizeEventIfNecessary(false);
|
||||
}
|
||||
|
||||
/* Informs Gecko that the screen size has changed. */
|
||||
private void sendResizeEventIfNecessary(boolean force) {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
|
||||
// Return immediately if the screen size hasn't changed or the viewport
|
||||
// size is zero (which indicates that the rendering surface hasn't been
|
||||
// allocated yet).
|
||||
boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width ||
|
||||
metrics.heightPixels != mScreenSize.height);
|
||||
boolean viewportSizeValid = (getLayerController() != null &&
|
||||
getLayerController().getViewportSize().isPositive());
|
||||
if (!(force || (screenSizeChanged && viewportSizeValid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
|
||||
IntSize bufferSize;
|
||||
IntSize tileSize;
|
||||
|
||||
// Round up depending on layer implementation to remove texture wastage
|
||||
if (!mHasDirectTexture) {
|
||||
// Round to the next multiple of the tile size
|
||||
bufferSize = new IntSize(((mScreenSize.width + LayerController.MIN_BUFFER.width - 1) / TILE_SIZE.width + 1) * TILE_SIZE.width,
|
||||
((mScreenSize.height + LayerController.MIN_BUFFER.height - 1) / TILE_SIZE.height + 1) * TILE_SIZE.height);
|
||||
tileSize = TILE_SIZE;
|
||||
} else {
|
||||
int maxSize = getLayerController().getView().getMaxTextureSize();
|
||||
|
||||
// XXX Integrate gralloc/tiling work to circumvent this
|
||||
if (mScreenSize.width > maxSize || mScreenSize.height > maxSize)
|
||||
throw new RuntimeException("Screen size of " + mScreenSize + " larger than maximum texture size of " + maxSize);
|
||||
|
||||
// Round to next power of two until we have NPOT texture support, respecting maximum texture size
|
||||
bufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)),
|
||||
Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height)));
|
||||
tileSize = new IntSize(0, 0);
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
|
||||
GeckoEvent event = GeckoEvent.createSizeChangedEvent(
|
||||
bufferSize.width, bufferSize.height, metrics.widthPixels,
|
||||
metrics.heightPixels, tileSize.width, tileSize.height);
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void viewportSizeChanged() {
|
||||
mViewportSizeChanged = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
adjustViewportWithThrottling();
|
||||
}
|
||||
|
||||
private void adjustViewportWithThrottling() {
|
||||
if (!getLayerController().getRedrawHint())
|
||||
return;
|
||||
|
||||
if (mPendingViewportAdjust)
|
||||
return;
|
||||
|
||||
long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime;
|
||||
if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) {
|
||||
getLayerController().getView().postDelayed(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
mPendingViewportAdjust = false;
|
||||
adjustViewport();
|
||||
}
|
||||
}, MIN_VIEWPORT_CHANGE_DELAY - timeDelta);
|
||||
mPendingViewportAdjust = true;
|
||||
return;
|
||||
}
|
||||
|
||||
adjustViewport();
|
||||
}
|
||||
|
||||
private void adjustViewport() {
|
||||
ViewportMetrics viewportMetrics =
|
||||
new ViewportMetrics(getLayerController().getViewportMetrics());
|
||||
|
||||
PointF viewportOffset = viewportMetrics.getOptimumViewportOffset(mBufferSize);
|
||||
viewportMetrics.setViewportOffset(viewportOffset);
|
||||
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
|
||||
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(viewportMetrics));
|
||||
if (mViewportSizeChanged) {
|
||||
mViewportSizeChanged = false;
|
||||
GeckoAppShell.viewSizeChanged();
|
||||
}
|
||||
|
||||
mLastViewportChangeTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
if ("Viewport:UpdateAndDraw".equals(event)) {
|
||||
mUpdateViewportOnEndDraw = true;
|
||||
|
||||
// Redraw everything.
|
||||
Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createDrawEvent(rect));
|
||||
} else if ("Viewport:UpdateLater".equals(event)) {
|
||||
mUpdateViewportOnEndDraw = true;
|
||||
} else if ("Checkerboard:Toggle".equals(event)) {
|
||||
try {
|
||||
boolean showChecks = message.getBoolean("value");
|
||||
LayerController controller = getLayerController();
|
||||
controller.setCheckerboardShowChecks(showChecks);
|
||||
Log.i(LOGTAG, "Showing checks: " + showChecks);
|
||||
} catch(JSONException ex) {
|
||||
Log.e(LOGTAG, "Error decoding JSON", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color
|
||||
// cannot be parsed, returns white.
|
||||
private static int parseColorFromGecko(String string) {
|
||||
if (sColorPattern == null) {
|
||||
sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)");
|
||||
}
|
||||
|
||||
Matcher matcher = sColorPattern.matcher(string);
|
||||
if (!matcher.matches()) {
|
||||
return Color.WHITE;
|
||||
}
|
||||
|
||||
int r = Integer.parseInt(matcher.group(1));
|
||||
int g = Integer.parseInt(matcher.group(2));
|
||||
int b = Integer.parseInt(matcher.group(3));
|
||||
return Color.rgb(r, g, b);
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */
|
||||
public void setDrawListener(DrawListener listener) {
|
||||
mDrawListener = listener;
|
||||
}
|
||||
|
||||
/** Used by robocop for testing purposes. Not for production use! This is used via reflection by robocop. */
|
||||
public interface DrawListener {
|
||||
public void drawFinished(int x, int y, int width, int height);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Chris Lord <chrislord.net@gmail.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -43,8 +44,8 @@ import android.graphics.PointF;
|
|||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.util.Log;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
|
||||
public abstract class Layer {
|
||||
|
@ -52,7 +53,6 @@ public abstract class Layer {
|
|||
private boolean mInTransaction;
|
||||
private Point mNewOrigin;
|
||||
private float mNewResolution;
|
||||
private LayerView mView;
|
||||
|
||||
protected Point mOrigin;
|
||||
protected float mResolution;
|
||||
|
@ -67,7 +67,7 @@ public abstract class Layer {
|
|||
* Updates the layer. This returns false if there is still work to be done
|
||||
* after this update.
|
||||
*/
|
||||
public final boolean update(GL10 gl, RenderContext context) {
|
||||
public final boolean update(RenderContext context) {
|
||||
if (mTransactionLock.isHeldByCurrentThread()) {
|
||||
throw new RuntimeException("draw() called while transaction lock held by this " +
|
||||
"thread?!");
|
||||
|
@ -75,7 +75,7 @@ public abstract class Layer {
|
|||
|
||||
if (mTransactionLock.tryLock()) {
|
||||
try {
|
||||
return performUpdates(gl, context);
|
||||
return performUpdates(context);
|
||||
} finally {
|
||||
mTransactionLock.unlock();
|
||||
}
|
||||
|
@ -114,28 +114,20 @@ public abstract class Layer {
|
|||
*
|
||||
* This function may block, so you should never call this on the main UI thread.
|
||||
*/
|
||||
public void beginTransaction(LayerView aView) {
|
||||
public void beginTransaction() {
|
||||
if (mTransactionLock.isHeldByCurrentThread())
|
||||
throw new RuntimeException("Nested transactions are not supported");
|
||||
mTransactionLock.lock();
|
||||
mView = aView;
|
||||
mInTransaction = true;
|
||||
mNewResolution = mResolution;
|
||||
}
|
||||
|
||||
public void beginTransaction() {
|
||||
beginTransaction(null);
|
||||
}
|
||||
|
||||
/** Call this when you're done modifying the layer. */
|
||||
public void endTransaction() {
|
||||
if (!mInTransaction)
|
||||
throw new RuntimeException("endTransaction() called outside a transaction");
|
||||
mInTransaction = false;
|
||||
mTransactionLock.unlock();
|
||||
|
||||
if (mView != null)
|
||||
mView.requestRender();
|
||||
}
|
||||
|
||||
/** Returns true if the layer is currently in a transaction and false otherwise. */
|
||||
|
@ -177,7 +169,7 @@ public abstract class Layer {
|
|||
* superclass implementation. Returns false if there is still work to be done after this
|
||||
* update is complete.
|
||||
*/
|
||||
protected boolean performUpdates(GL10 gl, RenderContext context) {
|
||||
protected boolean performUpdates(RenderContext context) {
|
||||
if (mNewOrigin != null) {
|
||||
mOrigin = mNewOrigin;
|
||||
mNewOrigin = null;
|
||||
|
@ -194,11 +186,18 @@ public abstract class Layer {
|
|||
public final RectF viewport;
|
||||
public final FloatSize pageSize;
|
||||
public final float zoomFactor;
|
||||
public final int positionHandle;
|
||||
public final int textureHandle;
|
||||
public final FloatBuffer coordBuffer;
|
||||
|
||||
public RenderContext(RectF aViewport, FloatSize aPageSize, float aZoomFactor) {
|
||||
public RenderContext(RectF aViewport, FloatSize aPageSize, float aZoomFactor,
|
||||
int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) {
|
||||
viewport = aViewport;
|
||||
pageSize = aPageSize;
|
||||
zoomFactor = aZoomFactor;
|
||||
positionHandle = aPositionHandle;
|
||||
textureHandle = aTextureHandle;
|
||||
coordBuffer = aCoordBuffer;
|
||||
}
|
||||
|
||||
public boolean fuzzyEquals(RenderContext other) {
|
||||
|
|
|
@ -40,8 +40,6 @@ package org.mozilla.gecko.gfx;
|
|||
|
||||
import org.mozilla.gecko.gfx.IntSize;
|
||||
import org.mozilla.gecko.gfx.Layer;
|
||||
import org.mozilla.gecko.gfx.LayerClient;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.ui.PanZoomController;
|
||||
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
|
@ -88,11 +86,11 @@ public class LayerController {
|
|||
*/
|
||||
|
||||
private OnTouchListener mOnTouchListener; /* The touch listener. */
|
||||
private LayerClient mLayerClient; /* The layer client. */
|
||||
private GeckoLayerClient mLayerClient; /* The layer client. */
|
||||
|
||||
/* The new color for the checkerboard. */
|
||||
private int mCheckerboardColor;
|
||||
private boolean mCheckerboardShouldShowChecks;
|
||||
private boolean mCheckerboardShouldShowChecks = true;
|
||||
|
||||
private boolean mForceRedraw;
|
||||
|
||||
|
@ -127,7 +125,7 @@ public class LayerController {
|
|||
|
||||
public void setRoot(Layer layer) { mRootLayer = layer; }
|
||||
|
||||
public void setLayerClient(LayerClient layerClient) {
|
||||
public void setLayerClient(GeckoLayerClient layerClient) {
|
||||
mLayerClient = layerClient;
|
||||
layerClient.setLayerController(this);
|
||||
}
|
||||
|
@ -136,7 +134,6 @@ public class LayerController {
|
|||
mForceRedraw = true;
|
||||
}
|
||||
|
||||
public LayerClient getLayerClient() { return mLayerClient; }
|
||||
public Layer getRoot() { return mRootLayer; }
|
||||
public LayerView getView() { return mView; }
|
||||
public Context getContext() { return mContext; }
|
||||
|
@ -209,17 +206,26 @@ public class LayerController {
|
|||
}
|
||||
}
|
||||
|
||||
PointF newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
|
||||
// For rotations, we want the focus point to be at the top left.
|
||||
boolean rotation = (size.width > oldWidth && size.height < oldHeight) ||
|
||||
(size.width < oldWidth && size.height > oldHeight);
|
||||
PointF newFocus;
|
||||
if (rotation) {
|
||||
newFocus = new PointF(0, 0);
|
||||
} else {
|
||||
newFocus = new PointF(size.width / 2.0f, size.height / 2.0f);
|
||||
}
|
||||
float newZoomFactor = size.width * oldZoomFactor / oldWidth;
|
||||
mViewportMetrics.scaleTo(newZoomFactor, newFocus);
|
||||
|
||||
Log.d(LOGTAG, "setViewportSize: " + mViewportMetrics);
|
||||
setForceRedraw();
|
||||
|
||||
if (mLayerClient != null)
|
||||
if (mLayerClient != null) {
|
||||
notifyLayerClientOfGeometryChange();
|
||||
mLayerClient.viewportSizeChanged();
|
||||
}
|
||||
|
||||
notifyLayerClientOfGeometryChange();
|
||||
mPanZoomController.abortAnimation();
|
||||
mView.requestRender();
|
||||
}
|
||||
|
@ -244,7 +250,7 @@ public class LayerController {
|
|||
mViewportMetrics.setPageSize(size);
|
||||
Log.d(LOGTAG, "setPageSize: " + mViewportMetrics);
|
||||
|
||||
// Page size is owned by the LayerClient, so no need to notify it of
|
||||
// Page size is owned by the layer client, so no need to notify it of
|
||||
// this change.
|
||||
|
||||
mView.post(new Runnable() {
|
||||
|
@ -320,12 +326,15 @@ public class LayerController {
|
|||
* would prefer that the action didn't take place.
|
||||
*/
|
||||
public boolean getRedrawHint() {
|
||||
// FIXME: Allow redraw while a finger is down, but only if we're about to checkerboard.
|
||||
// This requires fixing aboutToCheckerboard() to know about the new buffer size.
|
||||
|
||||
if (mForceRedraw) {
|
||||
mForceRedraw = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return aboutToCheckerboard() && mPanZoomController.getRedrawHint();
|
||||
return mPanZoomController.getRedrawHint();
|
||||
}
|
||||
|
||||
private RectF getTileRect() {
|
||||
|
@ -366,6 +375,9 @@ public class LayerController {
|
|||
// Undo the transforms.
|
||||
PointF origin = mViewportMetrics.getOrigin();
|
||||
PointF newPoint = new PointF(origin.x, origin.y);
|
||||
float zoom = mViewportMetrics.getZoomFactor();
|
||||
viewPoint.x /= zoom;
|
||||
viewPoint.y /= zoom;
|
||||
newPoint.offset(viewPoint.x, viewPoint.y);
|
||||
|
||||
Point rootOrigin = mRootLayer.getOrigin();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Chris Lord <chrislord.net@gmail.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -42,13 +43,13 @@ import org.mozilla.gecko.gfx.BufferedCairoImage;
|
|||
import org.mozilla.gecko.gfx.IntSize;
|
||||
import org.mozilla.gecko.gfx.Layer.RenderContext;
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.gfx.NinePatchTileLayer;
|
||||
import org.mozilla.gecko.gfx.SingleTileLayer;
|
||||
import org.mozilla.gecko.gfx.TextureReaper;
|
||||
import org.mozilla.gecko.gfx.TextureGenerator;
|
||||
import org.mozilla.gecko.gfx.TextLayer;
|
||||
import org.mozilla.gecko.gfx.TileLayer;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Point;
|
||||
|
@ -57,6 +58,7 @@ import android.graphics.Rect;
|
|||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.RegionIterator;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.SystemClock;
|
||||
import android.util.DisplayMetrics;
|
||||
|
@ -64,6 +66,9 @@ import android.util.Log;
|
|||
import android.view.WindowManager;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
@ -92,6 +97,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
private final ScrollbarLayer mHorizScrollLayer;
|
||||
private final ScrollbarLayer mVertScrollLayer;
|
||||
private final FadeRunnable mFadeRunnable;
|
||||
private final FloatBuffer mCoordBuffer;
|
||||
private RenderContext mLastPageContext;
|
||||
private int mMaxTextureSize;
|
||||
|
||||
|
@ -111,6 +117,48 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
/* Used by robocop for testing purposes */
|
||||
private IntBuffer mPixelBuffer;
|
||||
|
||||
// Used by GLES 2.0
|
||||
private int mProgram;
|
||||
private int mPositionHandle;
|
||||
private int mTextureHandle;
|
||||
private int mSampleHandle;
|
||||
private int mTMatrixHandle;
|
||||
|
||||
// column-major matrix applied to each vertex to shift the viewport from
|
||||
// one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by
|
||||
// a factor of 2 to fill up the screen
|
||||
private static final float[] TEXTURE_MATRIX = {
|
||||
2.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 2.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
private static final int COORD_BUFFER_SIZE = 20;
|
||||
|
||||
// The shaders run on the GPU directly, the vertex shader is only applying the
|
||||
// matrix transform detailed above
|
||||
private static final String VERTEX_SHADER =
|
||||
"uniform mat4 uTMatrix;\n" +
|
||||
"attribute vec4 vPosition;\n" +
|
||||
"attribute vec2 aTexCoord;\n" +
|
||||
"varying vec2 vTexCoord;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_Position = uTMatrix * vPosition;\n" +
|
||||
" vTexCoord = aTexCoord;\n" +
|
||||
"}\n";
|
||||
|
||||
// Note we flip the y-coordinate in the fragment shader from a
|
||||
// coordinate system with (0,0) in the top left to one with (0,0) in
|
||||
// the bottom left.
|
||||
private static final String FRAGMENT_SHADER =
|
||||
"precision mediump float;\n" +
|
||||
"varying vec2 vTexCoord;\n" +
|
||||
"uniform sampler2D sTexture;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_FragColor = texture2D(sTexture, vec2(vTexCoord.x, 1.0 - vTexCoord.y));\n" +
|
||||
"}\n";
|
||||
|
||||
public LayerRenderer(LayerView view) {
|
||||
mView = view;
|
||||
|
||||
|
@ -135,20 +183,69 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
mFrameTimings = new int[60];
|
||||
mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
|
||||
mShowFrameRate = false;
|
||||
|
||||
// Initialize the FloatBuffer that will be used to store all vertices and texture
|
||||
// coordinates in draw() commands.
|
||||
ByteBuffer byteBuffer = GeckoAppShell.allocateDirectBuffer(COORD_BUFFER_SIZE * 4);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
mCoordBuffer = byteBuffer.asFloatBuffer();
|
||||
}
|
||||
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
checkMonitoringEnabled();
|
||||
createProgram();
|
||||
activateProgram();
|
||||
}
|
||||
|
||||
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
||||
gl.glDisable(GL10.GL_DITHER);
|
||||
gl.glEnable(GL10.GL_TEXTURE_2D);
|
||||
public void createProgram() {
|
||||
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
|
||||
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
|
||||
|
||||
mProgram = GLES20.glCreateProgram();
|
||||
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
|
||||
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
|
||||
GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
|
||||
|
||||
// Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members.
|
||||
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
|
||||
mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
|
||||
mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
|
||||
mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
|
||||
|
||||
int maxTextureSizeResult[] = new int[1];
|
||||
gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
|
||||
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
|
||||
mMaxTextureSize = maxTextureSizeResult[0];
|
||||
}
|
||||
|
||||
// Activates the shader program.
|
||||
public void activateProgram() {
|
||||
// Add the program to the OpenGL environment
|
||||
GLES20.glUseProgram(mProgram);
|
||||
|
||||
// Set the transformation matrix
|
||||
GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, TEXTURE_MATRIX, 0);
|
||||
|
||||
Log.e(LOGTAG, "### Position handle is " + mPositionHandle + ", texture handle is " +
|
||||
mTextureHandle + ", last error is " + GLES20.glGetError());
|
||||
|
||||
// Enable the arrays from which we get the vertex and texture coordinates
|
||||
GLES20.glEnableVertexAttribArray(mPositionHandle);
|
||||
GLES20.glEnableVertexAttribArray(mTextureHandle);
|
||||
|
||||
GLES20.glUniform1i(mSampleHandle, 0);
|
||||
|
||||
TextureGenerator.get().fill();
|
||||
|
||||
// TODO: Move these calls into a separate deactivate() call that is called after the
|
||||
// underlay and overlay are rendered.
|
||||
}
|
||||
|
||||
// Deactivates the shader program. This must be done to avoid crashes after returning to the
|
||||
// Gecko C++ compositor from Java.
|
||||
public void deactivateProgram() {
|
||||
GLES20.glDisableVertexAttribArray(mTextureHandle);
|
||||
GLES20.glDisableVertexAttribArray(mPositionHandle);
|
||||
GLES20.glUseProgram(0);
|
||||
}
|
||||
|
||||
public int getMaxTextureSize() {
|
||||
|
@ -179,149 +276,14 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
* Called whenever a new frame is about to be drawn.
|
||||
*/
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
long frameStartTime = SystemClock.uptimeMillis();
|
||||
|
||||
TextureReaper.get().reap(gl);
|
||||
TextureGenerator.get().fill();
|
||||
|
||||
LayerController controller = mView.getController();
|
||||
RenderContext screenContext = createScreenContext();
|
||||
|
||||
boolean updated = true;
|
||||
|
||||
synchronized (controller) {
|
||||
Layer rootLayer = controller.getRoot();
|
||||
RenderContext pageContext = createPageContext();
|
||||
|
||||
if (!pageContext.fuzzyEquals(mLastPageContext)) {
|
||||
// the viewport or page changed, so show the scrollbars again
|
||||
// as per UX decision
|
||||
mVertScrollLayer.unfade();
|
||||
mHorizScrollLayer.unfade();
|
||||
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
|
||||
} else if (mFadeRunnable.timeToFade()) {
|
||||
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
|
||||
if (stillFading) {
|
||||
mFadeRunnable.scheduleNextFadeFrame();
|
||||
}
|
||||
}
|
||||
mLastPageContext = pageContext;
|
||||
|
||||
/* Update layers. */
|
||||
if (rootLayer != null) updated &= rootLayer.update(gl, pageContext);
|
||||
updated &= mBackgroundLayer.update(gl, screenContext);
|
||||
updated &= mShadowLayer.update(gl, pageContext);
|
||||
updateCheckerboardLayer(gl, screenContext);
|
||||
updated &= mFrameRateLayer.update(gl, screenContext);
|
||||
updated &= mVertScrollLayer.update(gl, pageContext);
|
||||
updated &= mHorizScrollLayer.update(gl, pageContext);
|
||||
|
||||
for (Layer layer : mExtraLayers)
|
||||
updated &= layer.update(gl, pageContext);
|
||||
|
||||
/* Draw the background. */
|
||||
mBackgroundLayer.draw(screenContext);
|
||||
|
||||
/* Draw the drop shadow, if we need to. */
|
||||
Rect pageRect = getPageRect();
|
||||
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(),
|
||||
pageRect.height());
|
||||
if (!untransformedPageRect.contains(controller.getViewport()))
|
||||
mShadowLayer.draw(pageContext);
|
||||
|
||||
/* Draw the checkerboard. */
|
||||
Rect scissorRect = transformToScissorRect(pageRect);
|
||||
gl.glEnable(GL10.GL_SCISSOR_TEST);
|
||||
gl.glScissor(scissorRect.left, scissorRect.top,
|
||||
scissorRect.width(), scissorRect.height());
|
||||
|
||||
mCheckerboardLayer.draw(screenContext);
|
||||
|
||||
/* Draw the layer the client added to us. */
|
||||
if (rootLayer != null)
|
||||
rootLayer.draw(pageContext);
|
||||
|
||||
gl.glDisable(GL10.GL_SCISSOR_TEST);
|
||||
|
||||
/* Draw any extra layers that were added (likely plugins) */
|
||||
for (Layer layer : mExtraLayers)
|
||||
layer.draw(pageContext);
|
||||
|
||||
/* Draw the vertical scrollbar. */
|
||||
IntSize screenSize = new IntSize(controller.getViewportSize());
|
||||
if (pageRect.height() > screenSize.height)
|
||||
mVertScrollLayer.draw(pageContext);
|
||||
|
||||
/* Draw the horizontal scrollbar. */
|
||||
if (pageRect.width() > screenSize.width)
|
||||
mHorizScrollLayer.draw(pageContext);
|
||||
|
||||
/* Measure how much of the screen is checkerboarding */
|
||||
if ((rootLayer != null) &&
|
||||
(mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) {
|
||||
// Find out how much of the viewport area is valid
|
||||
Rect viewport = RectUtils.round(pageContext.viewport);
|
||||
Region validRegion = rootLayer.getValidRegion(pageContext);
|
||||
validRegion.op(viewport, Region.Op.INTERSECT);
|
||||
|
||||
float checkerboard = 0.0f;
|
||||
if (!(validRegion.isRect() && validRegion.getBounds().equals(viewport))) {
|
||||
int screenArea = viewport.width() * viewport.height();
|
||||
validRegion.op(viewport, Region.Op.REVERSE_DIFFERENCE);
|
||||
|
||||
// XXX The assumption here is that a Region never has overlapping
|
||||
// rects. This is true, as evidenced by reading the SkRegion
|
||||
// source, but is not mentioned in the Android documentation,
|
||||
// and so is liable to change.
|
||||
// If it does change, this code will need to be reevaluated.
|
||||
Rect r = new Rect();
|
||||
int checkerboardArea = 0;
|
||||
for (RegionIterator i = new RegionIterator(validRegion); i.next(r);) {
|
||||
checkerboardArea += r.width() * r.height();
|
||||
}
|
||||
|
||||
checkerboard = checkerboardArea / (float)screenArea;
|
||||
}
|
||||
|
||||
PanningPerfAPI.recordCheckerboard(checkerboard);
|
||||
|
||||
mCompleteFramesRendered += 1.0f - checkerboard;
|
||||
mFramesRendered ++;
|
||||
|
||||
if (frameStartTime - mProfileOutputTime > 1000) {
|
||||
mProfileOutputTime = frameStartTime;
|
||||
printCheckerboardStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the FPS. */
|
||||
if (mShowFrameRate) {
|
||||
updateDroppedFrames(frameStartTime);
|
||||
|
||||
try {
|
||||
gl.glEnable(GL10.GL_BLEND);
|
||||
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
|
||||
mFrameRateLayer.draw(screenContext);
|
||||
} finally {
|
||||
gl.glDisable(GL10.GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
// If a layer update requires further work, schedule another redraw
|
||||
if (!updated)
|
||||
mView.requestRender();
|
||||
|
||||
PanningPerfAPI.recordFrameTime();
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
IntBuffer pixelBuffer = mPixelBuffer;
|
||||
if (updated && pixelBuffer != null) {
|
||||
synchronized (pixelBuffer) {
|
||||
pixelBuffer.position(0);
|
||||
gl.glReadPixels(0, 0, (int)screenContext.viewport.width(), (int)screenContext.viewport.height(), GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuffer);
|
||||
pixelBuffer.notify();
|
||||
}
|
||||
RenderContext pageContext = createPageContext(), screenContext = createScreenContext();
|
||||
Frame frame = createFrame(pageContext, screenContext);
|
||||
synchronized (mView.getController()) {
|
||||
frame.beginDrawing();
|
||||
frame.drawBackground();
|
||||
frame.drawRootLayer();
|
||||
frame.drawForeground();
|
||||
frame.endDrawing();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,15 +308,15 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
return pixelBuffer;
|
||||
}
|
||||
|
||||
private RenderContext createScreenContext() {
|
||||
public RenderContext createScreenContext() {
|
||||
LayerController layerController = mView.getController();
|
||||
IntSize viewportSize = new IntSize(layerController.getViewportSize());
|
||||
RectF viewport = new RectF(0.0f, 0.0f, viewportSize.width, viewportSize.height);
|
||||
FloatSize pageSize = new FloatSize(layerController.getPageSize());
|
||||
return new RenderContext(viewport, pageSize, 1.0f);
|
||||
return createContext(viewport, pageSize, 1.0f);
|
||||
}
|
||||
|
||||
private RenderContext createPageContext() {
|
||||
public RenderContext createPageContext() {
|
||||
LayerController layerController = mView.getController();
|
||||
|
||||
Rect viewport = new Rect();
|
||||
|
@ -362,7 +324,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
|
||||
FloatSize pageSize = new FloatSize(layerController.getPageSize());
|
||||
float zoomFactor = layerController.getZoomFactor();
|
||||
return new RenderContext(new RectF(viewport), pageSize, zoomFactor);
|
||||
return createContext(new RectF(viewport), pageSize, zoomFactor);
|
||||
}
|
||||
|
||||
private RenderContext createContext(RectF viewport, FloatSize pageSize, float zoomFactor) {
|
||||
return new RenderContext(viewport, pageSize, zoomFactor, mPositionHandle, mTextureHandle,
|
||||
mCoordBuffer);
|
||||
}
|
||||
|
||||
private Rect getPageRect() {
|
||||
|
@ -391,14 +358,14 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
}
|
||||
|
||||
public void onSurfaceChanged(GL10 gl, final int width, final int height) {
|
||||
gl.glViewport(0, 0, width, height);
|
||||
GLES20.glViewport(0, 0, width, height);
|
||||
moveFrameRateLayer(width, height);
|
||||
|
||||
// updating the state in the view/controller/client should be
|
||||
// done on the main UI thread, not the GL renderer thread
|
||||
mView.post(new Runnable() {
|
||||
public void run() {
|
||||
mView.setViewportSize(new IntSize(width, height));
|
||||
moveFrameRateLayer(width, height);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -418,7 +385,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length;
|
||||
|
||||
int averageTime = mFrameTimingsSum / mFrameTimings.length;
|
||||
mFrameRateLayer.beginTransaction();
|
||||
mFrameRateLayer.beginTransaction(); // called on compositor thread
|
||||
try {
|
||||
mFrameRateLayer.setText(averageTime + " ms/" + mDroppedFrames);
|
||||
} finally {
|
||||
|
@ -428,7 +395,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
|
||||
/* Given the new dimensions for the surface, moves the frame rate layer appropriately. */
|
||||
private void moveFrameRateLayer(int width, int height) {
|
||||
mFrameRateLayer.beginTransaction();
|
||||
mFrameRateLayer.beginTransaction(); // called on compositor thread
|
||||
try {
|
||||
Point origin = new Point(width - FRAME_RATE_METER_WIDTH - 8,
|
||||
height - FRAME_RATE_METER_HEIGHT + 8);
|
||||
|
@ -451,7 +418,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
}).start();
|
||||
}
|
||||
|
||||
private void updateCheckerboardLayer(GL10 gl, RenderContext renderContext) {
|
||||
private void updateCheckerboardLayer(RenderContext renderContext) {
|
||||
int checkerboardColor = mView.getController().getCheckerboardColor();
|
||||
boolean showChecks = mView.getController().checkerboardShouldShowChecks();
|
||||
if (checkerboardColor == mCheckerboardImage.getColor() &&
|
||||
|
@ -459,7 +426,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
return;
|
||||
}
|
||||
|
||||
mCheckerboardLayer.beginTransaction();
|
||||
mCheckerboardLayer.beginTransaction(); // called on compositor thread
|
||||
try {
|
||||
mCheckerboardImage.update(showChecks, checkerboardColor);
|
||||
mCheckerboardLayer.invalidate();
|
||||
|
@ -467,7 +434,22 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
mCheckerboardLayer.endTransaction();
|
||||
}
|
||||
|
||||
mCheckerboardLayer.update(gl, renderContext);
|
||||
mCheckerboardLayer.update(renderContext); // called on compositor thread
|
||||
}
|
||||
|
||||
/*
|
||||
* create a vertex shader type (GLES20.GL_VERTEX_SHADER)
|
||||
* or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
|
||||
*/
|
||||
private int loadShader(int type, String shaderCode) {
|
||||
int shader = GLES20.glCreateShader(type);
|
||||
GLES20.glShaderSource(shader, shaderCode);
|
||||
GLES20.glCompileShader(shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
public Frame createFrame(RenderContext pageContext, RenderContext screenContext) {
|
||||
return new Frame(pageContext, screenContext);
|
||||
}
|
||||
|
||||
class FadeRunnable implements Runnable {
|
||||
|
@ -505,4 +487,202 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Frame {
|
||||
// The timestamp recording the start of this frame.
|
||||
private long mFrameStartTime;
|
||||
// A rendering context for page-positioned layers, and one for screen-positioned layers.
|
||||
private RenderContext mPageContext, mScreenContext;
|
||||
// Whether a layer was updated.
|
||||
private boolean mUpdated;
|
||||
|
||||
public Frame(RenderContext pageContext, RenderContext screenContext) {
|
||||
mPageContext = pageContext;
|
||||
mScreenContext = screenContext;
|
||||
}
|
||||
|
||||
private void setScissorRect() {
|
||||
Rect scissorRect = transformToScissorRect(getPageRect());
|
||||
GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
|
||||
GLES20.glScissor(scissorRect.left, scissorRect.top,
|
||||
scissorRect.width(), scissorRect.height());
|
||||
}
|
||||
|
||||
/** This function is invoked via JNI; be careful when modifying signature. */
|
||||
public void beginDrawing() {
|
||||
mFrameStartTime = SystemClock.uptimeMillis();
|
||||
|
||||
TextureReaper.get().reap();
|
||||
TextureGenerator.get().fill();
|
||||
|
||||
mUpdated = true;
|
||||
|
||||
LayerController controller = mView.getController();
|
||||
Layer rootLayer = controller.getRoot();
|
||||
|
||||
if (!mPageContext.fuzzyEquals(mLastPageContext)) {
|
||||
// the viewport or page changed, so show the scrollbars again
|
||||
// as per UX decision
|
||||
mVertScrollLayer.unfade();
|
||||
mHorizScrollLayer.unfade();
|
||||
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
|
||||
} else if (mFadeRunnable.timeToFade()) {
|
||||
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
|
||||
if (stillFading) {
|
||||
mFadeRunnable.scheduleNextFadeFrame();
|
||||
}
|
||||
}
|
||||
mLastPageContext = mPageContext;
|
||||
|
||||
/* Update layers. */
|
||||
if (rootLayer != null) mUpdated &= rootLayer.update(mPageContext); // called on compositor thread
|
||||
mUpdated &= mBackgroundLayer.update(mScreenContext); // called on compositor thread
|
||||
mUpdated &= mShadowLayer.update(mPageContext); // called on compositor thread
|
||||
updateCheckerboardLayer(mScreenContext);
|
||||
mUpdated &= mFrameRateLayer.update(mScreenContext); // called on compositor thread
|
||||
mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread
|
||||
mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
|
||||
|
||||
for (Layer layer : mExtraLayers)
|
||||
mUpdated &= layer.update(mPageContext); // called on compositor thread
|
||||
|
||||
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
|
||||
|
||||
// If a layer update requires further work, schedule another redraw
|
||||
if (!mUpdated)
|
||||
mView.requestRender();
|
||||
|
||||
PanningPerfAPI.recordFrameTime();
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
IntBuffer pixelBuffer = mPixelBuffer;
|
||||
if (mUpdated && pixelBuffer != null) {
|
||||
synchronized (pixelBuffer) {
|
||||
pixelBuffer.position(0);
|
||||
GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(),
|
||||
(int)mScreenContext.viewport.height(), GLES20.GL_RGBA,
|
||||
GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
|
||||
pixelBuffer.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This function is invoked via JNI; be careful when modifying signature. */
|
||||
public void drawBackground() {
|
||||
/* Draw the background. */
|
||||
mBackgroundLayer.draw(mScreenContext);
|
||||
|
||||
/* Draw the drop shadow, if we need to. */
|
||||
Rect pageRect = getPageRect();
|
||||
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(),
|
||||
pageRect.height());
|
||||
if (!untransformedPageRect.contains(mView.getController().getViewport()))
|
||||
mShadowLayer.draw(mPageContext);
|
||||
|
||||
/* Draw the checkerboard. */
|
||||
setScissorRect();
|
||||
mCheckerboardLayer.draw(mScreenContext);
|
||||
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
// Draws the layer the client added to us.
|
||||
void drawRootLayer() {
|
||||
Layer rootLayer = mView.getController().getRoot();
|
||||
if (rootLayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setScissorRect();
|
||||
rootLayer.draw(mPageContext);
|
||||
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
/** This function is invoked via JNI; be careful when modifying signature. */
|
||||
public void drawForeground() {
|
||||
Rect pageRect = getPageRect();
|
||||
LayerController controller = mView.getController();
|
||||
|
||||
/* Draw any extra layers that were added (likely plugins) */
|
||||
for (Layer layer : mExtraLayers)
|
||||
layer.draw(mPageContext);
|
||||
|
||||
/* Draw the vertical scrollbar. */
|
||||
IntSize screenSize = new IntSize(controller.getViewportSize());
|
||||
if (pageRect.height() > screenSize.height)
|
||||
mVertScrollLayer.draw(mPageContext);
|
||||
|
||||
/* Draw the horizontal scrollbar. */
|
||||
if (pageRect.width() > screenSize.width)
|
||||
mHorizScrollLayer.draw(mPageContext);
|
||||
|
||||
/* Measure how much of the screen is checkerboarding */
|
||||
Layer rootLayer = controller.getRoot();
|
||||
if ((rootLayer != null) &&
|
||||
(mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) {
|
||||
// Find out how much of the viewport area is valid
|
||||
Rect viewport = RectUtils.round(mPageContext.viewport);
|
||||
Region validRegion = rootLayer.getValidRegion(mPageContext);
|
||||
validRegion.op(viewport, Region.Op.INTERSECT);
|
||||
|
||||
float checkerboard = 0.0f;
|
||||
if (!(validRegion.isRect() && validRegion.getBounds().equals(viewport))) {
|
||||
int screenArea = viewport.width() * viewport.height();
|
||||
validRegion.op(viewport, Region.Op.REVERSE_DIFFERENCE);
|
||||
|
||||
// XXX The assumption here is that a Region never has overlapping
|
||||
// rects. This is true, as evidenced by reading the SkRegion
|
||||
// source, but is not mentioned in the Android documentation,
|
||||
// and so is liable to change.
|
||||
// If it does change, this code will need to be reevaluated.
|
||||
Rect r = new Rect();
|
||||
int checkerboardArea = 0;
|
||||
for (RegionIterator i = new RegionIterator(validRegion); i.next(r);) {
|
||||
checkerboardArea += r.width() * r.height();
|
||||
}
|
||||
|
||||
checkerboard = checkerboardArea / (float)screenArea;
|
||||
}
|
||||
|
||||
PanningPerfAPI.recordCheckerboard(checkerboard);
|
||||
|
||||
mCompleteFramesRendered += 1.0f - checkerboard;
|
||||
mFramesRendered ++;
|
||||
|
||||
if (mFrameStartTime - mProfileOutputTime > 1000) {
|
||||
mProfileOutputTime = mFrameStartTime;
|
||||
printCheckerboardStats();
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the FPS. */
|
||||
if (mShowFrameRate) {
|
||||
updateDroppedFrames(mFrameStartTime);
|
||||
|
||||
GLES20.glEnable(GLES20.GL_BLEND);
|
||||
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
mFrameRateLayer.draw(mScreenContext);
|
||||
}
|
||||
}
|
||||
|
||||
/** This function is invoked via JNI; be careful when modifying signature. */
|
||||
public void endDrawing() {
|
||||
// If a layer update requires further work, schedule another redraw
|
||||
if (!mUpdated)
|
||||
mView.requestRender();
|
||||
|
||||
PanningPerfAPI.recordFrameTime();
|
||||
|
||||
/* Used by robocop for testing purposes */
|
||||
IntBuffer pixelBuffer = mPixelBuffer;
|
||||
if (mUpdated && pixelBuffer != null) {
|
||||
synchronized (pixelBuffer) {
|
||||
pixelBuffer.position(0);
|
||||
GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(),
|
||||
(int)mScreenContext.viewport.height(), GLES20.GL_RGBA,
|
||||
GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
|
||||
pixelBuffer.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -37,17 +38,21 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.GeckoInputConnection;
|
||||
import org.mozilla.gecko.gfx.FloatSize;
|
||||
import org.mozilla.gecko.gfx.InputConnectionHandler;
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.util.Log;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.LinkedList;
|
||||
|
@ -58,7 +63,7 @@ import java.util.LinkedList;
|
|||
* This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
|
||||
* mediator between the LayerRenderer and the LayerController.
|
||||
*/
|
||||
public class LayerView extends GLSurfaceView {
|
||||
public class LayerView extends FlexibleGLSurfaceView {
|
||||
private Context mContext;
|
||||
private LayerController mController;
|
||||
private InputConnectionHandler mInputConnectionHandler;
|
||||
|
@ -79,7 +84,6 @@ public class LayerView extends GLSurfaceView {
|
|||
mController = controller;
|
||||
mRenderer = new LayerRenderer(this);
|
||||
setRenderer(mRenderer);
|
||||
setRenderMode(RENDERMODE_WHEN_DIRTY);
|
||||
mGestureDetector = new GestureDetector(context, controller.getGestureListener());
|
||||
mScaleGestureDetector =
|
||||
new SimpleScaleGestureDetector(controller.getScaleGestureListener());
|
||||
|
@ -88,6 +92,8 @@ public class LayerView extends GLSurfaceView {
|
|||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
|
||||
createGLThread();
|
||||
}
|
||||
|
||||
private void addToEventQueue(MotionEvent event) {
|
||||
|
@ -133,8 +139,10 @@ public class LayerView extends GLSurfaceView {
|
|||
mController.setViewportSize(new FloatSize(size));
|
||||
}
|
||||
|
||||
public void setInputConnectionHandler(InputConnectionHandler handler) {
|
||||
mInputConnectionHandler = handler;
|
||||
public GeckoInputConnection setInputConnectionHandler() {
|
||||
GeckoInputConnection geckoInputConnection = GeckoInputConnection.create(this);
|
||||
mInputConnectionHandler = geckoInputConnection;
|
||||
return geckoInputConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,303 +0,0 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Lord <chrislord.net@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.gfx.CairoImage;
|
||||
import org.mozilla.gecko.gfx.IntSize;
|
||||
import org.mozilla.gecko.gfx.SingleTileLayer;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* Encapsulates the logic needed to draw a layer made of multiple tiles.
|
||||
*
|
||||
* TODO: Support repeating.
|
||||
*/
|
||||
public class MultiTileLayer extends Layer {
|
||||
private static final String LOGTAG = "GeckoMultiTileLayer";
|
||||
|
||||
private final CairoImage mImage;
|
||||
private IntSize mTileSize;
|
||||
private IntSize mBufferSize;
|
||||
private final ArrayList<SubTile> mTiles;
|
||||
|
||||
public MultiTileLayer(CairoImage image, IntSize tileSize) {
|
||||
super();
|
||||
|
||||
mImage = image;
|
||||
mTileSize = tileSize;
|
||||
mBufferSize = new IntSize(0, 0);
|
||||
mTiles = new ArrayList<SubTile>();
|
||||
}
|
||||
|
||||
public void invalidate(Rect dirtyRect) {
|
||||
if (!inTransaction()) {
|
||||
throw new RuntimeException("invalidate() is only valid inside a transaction");
|
||||
}
|
||||
|
||||
for (SubTile layer : mTiles) {
|
||||
IntSize tileSize = layer.getSize();
|
||||
Rect tileRect = new Rect(layer.x, layer.y, layer.x + tileSize.width, layer.y + tileSize.height);
|
||||
|
||||
if (tileRect.intersect(dirtyRect)) {
|
||||
tileRect.offset(-layer.x, -layer.y);
|
||||
layer.invalidate(tileRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
for (SubTile layer : mTiles) {
|
||||
layer.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntSize getSize() {
|
||||
return mImage.getSize();
|
||||
}
|
||||
|
||||
private void validateTiles() {
|
||||
IntSize size = getSize();
|
||||
|
||||
if (size.equals(mBufferSize)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Regenerate tiles
|
||||
mTiles.clear();
|
||||
int offset = 0;
|
||||
final int format = mImage.getFormat();
|
||||
final ByteBuffer buffer = mImage.getBuffer().slice();
|
||||
final int bpp = CairoUtils.bitsPerPixelForCairoFormat(format) / 8;
|
||||
for (int y = 0; y < size.height; y += mTileSize.height) {
|
||||
for (int x = 0; x < size.width; x += mTileSize.width) {
|
||||
// Create a CairoImage implementation that returns a
|
||||
// tile from the parent CairoImage. It's assumed that
|
||||
// the tiles are stored in series.
|
||||
final IntSize layerSize =
|
||||
new IntSize(Math.min(mTileSize.width, size.width - x),
|
||||
Math.min(mTileSize.height, size.height - y));
|
||||
final int tileOffset = offset;
|
||||
|
||||
CairoImage subImage = new CairoImage() {
|
||||
@Override
|
||||
public ByteBuffer getBuffer() {
|
||||
// Create a ByteBuffer that shares the data of the original
|
||||
// buffer, but is positioned and limited so that only the
|
||||
// tile data is accessible.
|
||||
buffer.position(tileOffset);
|
||||
ByteBuffer tileBuffer = buffer.slice();
|
||||
tileBuffer.limit(layerSize.getArea() * bpp);
|
||||
|
||||
return tileBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntSize getSize() {
|
||||
return layerSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFormat() {
|
||||
return format;
|
||||
}
|
||||
};
|
||||
|
||||
mTiles.add(new SubTile(subImage, x, y));
|
||||
offset += layerSize.getArea() * bpp;
|
||||
}
|
||||
}
|
||||
|
||||
// Set tile origins and resolution
|
||||
refreshTileMetrics(getOrigin(), getResolution(), false);
|
||||
|
||||
mBufferSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean performUpdates(GL10 gl, RenderContext context) {
|
||||
super.performUpdates(gl, context);
|
||||
|
||||
validateTiles();
|
||||
|
||||
// Iterate over the tiles and decide which ones we'll be drawing
|
||||
int dirtyTiles = 0;
|
||||
boolean screenUpdateDone = false;
|
||||
SubTile firstDirtyTile = null;
|
||||
for (SubTile layer : mTiles) {
|
||||
// First do a non-texture update to make sure coordinates are
|
||||
// up-to-date.
|
||||
boolean invalid = layer.getSkipTextureUpdate();
|
||||
layer.setSkipTextureUpdate(true);
|
||||
layer.performUpdates(gl, context);
|
||||
|
||||
RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
|
||||
boolean isDirty = layer.isDirty();
|
||||
|
||||
if (isDirty) {
|
||||
if (!RectF.intersects(layerBounds, context.viewport)) {
|
||||
if (firstDirtyTile == null)
|
||||
firstDirtyTile = layer;
|
||||
dirtyTiles ++;
|
||||
invalid = true;
|
||||
} else {
|
||||
// This tile intersects with the screen and is dirty,
|
||||
// update it immediately.
|
||||
layer.setSkipTextureUpdate(false);
|
||||
screenUpdateDone = true;
|
||||
layer.performUpdates(gl, context);
|
||||
invalid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We use the SkipTextureUpdate flag as a marker of a tile's
|
||||
// validity. This is required, as sometimes layers are drawn
|
||||
// without updating first, and we mustn't draw tiles that have
|
||||
// been marked as invalid that we haven't updated.
|
||||
layer.setSkipTextureUpdate(invalid);
|
||||
}
|
||||
|
||||
// Now if no tiles that intersect with the screen were updated, update
|
||||
// a single tile that doesn't (if there are any). This has the effect
|
||||
// of spreading out non-critical texture upload over time, and smoothing
|
||||
// upload-related hitches.
|
||||
if (!screenUpdateDone && firstDirtyTile != null) {
|
||||
firstDirtyTile.setSkipTextureUpdate(false);
|
||||
firstDirtyTile.performUpdates(gl, context);
|
||||
dirtyTiles --;
|
||||
}
|
||||
|
||||
return (dirtyTiles == 0);
|
||||
}
|
||||
|
||||
private void refreshTileMetrics(Point origin, float resolution, boolean inTransaction) {
|
||||
IntSize size = getSize();
|
||||
for (SubTile layer : mTiles) {
|
||||
if (!inTransaction) {
|
||||
layer.beginTransaction(null);
|
||||
}
|
||||
|
||||
if (origin != null) {
|
||||
layer.setOrigin(new Point(origin.x + layer.x, origin.y + layer.y));
|
||||
}
|
||||
if (resolution >= 0.0f) {
|
||||
layer.setResolution(resolution);
|
||||
}
|
||||
|
||||
if (!inTransaction) {
|
||||
layer.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrigin(Point newOrigin) {
|
||||
super.setOrigin(newOrigin);
|
||||
refreshTileMetrics(newOrigin, -1, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResolution(float newResolution) {
|
||||
super.setResolution(newResolution);
|
||||
refreshTileMetrics(null, newResolution, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTransaction(LayerView aView) {
|
||||
super.beginTransaction(aView);
|
||||
|
||||
for (SubTile layer : mTiles) {
|
||||
layer.beginTransaction(aView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction() {
|
||||
for (SubTile layer : mTiles) {
|
||||
layer.endTransaction();
|
||||
}
|
||||
|
||||
super.endTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(RenderContext context) {
|
||||
for (SubTile layer : mTiles) {
|
||||
// We use the SkipTextureUpdate flag as a validity flag. If it's false,
|
||||
// the contents of this tile are invalid and we shouldn't draw it.
|
||||
if (layer.getSkipTextureUpdate())
|
||||
continue;
|
||||
|
||||
// Avoid work, only draw tiles that intersect with the viewport
|
||||
RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize()));
|
||||
if (RectF.intersects(layerBounds, context.viewport))
|
||||
layer.draw(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getValidRegion(RenderContext context) {
|
||||
Region validRegion = new Region();
|
||||
for (SubTile tile : mTiles) {
|
||||
if (tile.getSkipTextureUpdate())
|
||||
continue;
|
||||
validRegion.op(tile.getValidRegion(context), Region.Op.UNION);
|
||||
}
|
||||
|
||||
return validRegion;
|
||||
}
|
||||
|
||||
class SubTile extends SingleTileLayer {
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public SubTile(CairoImage mImage, int mX, int mY) {
|
||||
super(mImage);
|
||||
x = mX;
|
||||
y = mY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -40,11 +41,10 @@ package org.mozilla.gecko.gfx;
|
|||
import org.mozilla.gecko.gfx.FloatSize;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLES11;
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.util.Log;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.nio.FloatBuffer;
|
||||
import android.opengl.GLES20;
|
||||
|
||||
/**
|
||||
* Encapsulates the logic needed to draw a nine-patch bitmap using OpenGL ES.
|
||||
|
@ -54,7 +54,7 @@ import java.nio.FloatBuffer;
|
|||
*/
|
||||
public class NinePatchTileLayer extends TileLayer {
|
||||
private static final int PATCH_SIZE = 16;
|
||||
private static final int TEXTURE_SIZE = 48;
|
||||
private static final int TEXTURE_SIZE = 64;
|
||||
|
||||
public NinePatchTileLayer(CairoImage image) {
|
||||
super(false, image);
|
||||
|
@ -65,14 +65,11 @@ public class NinePatchTileLayer extends TileLayer {
|
|||
if (!initialized())
|
||||
return;
|
||||
|
||||
GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLES11.glEnable(GL10.GL_BLEND);
|
||||
try {
|
||||
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
|
||||
drawPatches(context);
|
||||
} finally {
|
||||
GLES11.glDisable(GL10.GL_BLEND);
|
||||
}
|
||||
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLES20.glEnable(GLES20.GL_BLEND);
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
|
||||
drawPatches(context);
|
||||
}
|
||||
|
||||
private void drawPatches(RenderContext context) {
|
||||
|
@ -91,34 +88,77 @@ public class NinePatchTileLayer extends TileLayer {
|
|||
FloatSize size = context.pageSize;
|
||||
float width = size.width, height = size.height;
|
||||
|
||||
drawPatch(context, 0, 0, /* 0 */
|
||||
drawPatch(context, 0, PATCH_SIZE * 3, /* 0 */
|
||||
0.0f, 0.0f, PATCH_SIZE, PATCH_SIZE);
|
||||
drawPatch(context, PATCH_SIZE, 0, /* 1 */
|
||||
drawPatch(context, PATCH_SIZE, PATCH_SIZE*3, /* 1 */
|
||||
PATCH_SIZE, 0.0f, width, PATCH_SIZE);
|
||||
drawPatch(context, PATCH_SIZE * 2, 0, /* 2 */
|
||||
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE*3, /* 2 */
|
||||
PATCH_SIZE + width, 0.0f, PATCH_SIZE, PATCH_SIZE);
|
||||
drawPatch(context, 0, PATCH_SIZE, /* 3 */
|
||||
drawPatch(context, 0, PATCH_SIZE * 2, /* 3 */
|
||||
0.0f, PATCH_SIZE, PATCH_SIZE, height);
|
||||
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE, /* 4 */
|
||||
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2, /* 4 */
|
||||
PATCH_SIZE + width, PATCH_SIZE, PATCH_SIZE, height);
|
||||
drawPatch(context, 0, PATCH_SIZE * 2, /* 5 */
|
||||
drawPatch(context, 0, PATCH_SIZE, /* 5 */
|
||||
0.0f, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
|
||||
drawPatch(context, PATCH_SIZE, PATCH_SIZE * 2, /* 6 */
|
||||
drawPatch(context, PATCH_SIZE, PATCH_SIZE, /* 6 */
|
||||
PATCH_SIZE, PATCH_SIZE + height, width, PATCH_SIZE);
|
||||
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2, /* 7 */
|
||||
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE, /* 7 */
|
||||
PATCH_SIZE + width, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
|
||||
}
|
||||
|
||||
private void drawPatch(RenderContext context, int textureX, int textureY, float tileX,
|
||||
float tileY, float tileWidth, float tileHeight) {
|
||||
int[] cropRect = { textureX, textureY + PATCH_SIZE, PATCH_SIZE, -PATCH_SIZE };
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect,
|
||||
0);
|
||||
|
||||
private void drawPatch(RenderContext context, int textureX, int textureY,
|
||||
float tileX, float tileY, float tileWidth, float tileHeight) {
|
||||
RectF viewport = context.viewport;
|
||||
float viewportHeight = viewport.height();
|
||||
float drawX = tileX - viewport.left - PATCH_SIZE;
|
||||
float drawY = viewportHeight - (tileY + tileHeight - viewport.top - PATCH_SIZE);
|
||||
GLES11Ext.glDrawTexfOES(drawX, drawY, 0.0f, tileWidth, tileHeight);
|
||||
|
||||
float[] coords = {
|
||||
//x, y, z, texture_x, texture_y
|
||||
drawX/viewport.width(), drawY/viewport.height(), 0,
|
||||
textureX/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,
|
||||
|
||||
drawX/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
|
||||
textureX/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE,
|
||||
|
||||
(drawX+tileWidth)/viewport.width(), drawY/viewport.height(), 0,
|
||||
(textureX+PATCH_SIZE)/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,
|
||||
|
||||
(drawX+tileWidth)/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
|
||||
(textureX+PATCH_SIZE)/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE
|
||||
|
||||
};
|
||||
|
||||
// Get the buffer and handles from the context
|
||||
FloatBuffer coordBuffer = context.coordBuffer;
|
||||
int positionHandle = context.positionHandle;
|
||||
int textureHandle = context.textureHandle;
|
||||
|
||||
// Make sure we are at position zero in the buffer in case other draw methods did not clean
|
||||
// up after themselves
|
||||
coordBuffer.position(0);
|
||||
coordBuffer.put(coords);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
|
||||
GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
|
||||
GLES20.GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Use bilinear filtering for both magnification and minimization of the texture. This
|
||||
// applies only to the shadow layer so we do not incur a high overhead.
|
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,24 +37,13 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.gfx.BufferedCairoImage;
|
||||
import org.mozilla.gecko.gfx.CairoUtils;
|
||||
import org.mozilla.gecko.gfx.FloatSize;
|
||||
import org.mozilla.gecko.gfx.LayerClient;
|
||||
import org.mozilla.gecko.gfx.PointUtils;
|
||||
import org.mozilla.gecko.gfx.SingleTileLayer;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import java.io.File;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
@ -62,22 +51,23 @@ import java.nio.ByteBuffer;
|
|||
* A stand-in for Gecko that renders cached content of the previous page. We use this until Gecko
|
||||
* is up, then we hand off control to it.
|
||||
*/
|
||||
public class PlaceholderLayerClient extends LayerClient {
|
||||
public class PlaceholderLayerClient {
|
||||
private static final String LOGTAG = "PlaceholderLayerClient";
|
||||
|
||||
private Context mContext;
|
||||
private final LayerController mLayerController;
|
||||
|
||||
private ViewportMetrics mViewport;
|
||||
private boolean mViewportUnknown;
|
||||
private int mWidth, mHeight, mFormat;
|
||||
private ByteBuffer mBuffer;
|
||||
|
||||
private PlaceholderLayerClient(Context context) {
|
||||
mContext = context;
|
||||
String viewport = GeckoApp.mAppContext.getLastViewport();
|
||||
public PlaceholderLayerClient(LayerController controller, String lastViewport) {
|
||||
mLayerController = controller;
|
||||
|
||||
mViewportUnknown = true;
|
||||
if (viewport != null) {
|
||||
if (lastViewport != null) {
|
||||
try {
|
||||
JSONObject viewportObject = new JSONObject(viewport);
|
||||
JSONObject viewportObject = new JSONObject(lastViewport);
|
||||
mViewport = new ViewportMetrics(viewportObject);
|
||||
mViewportUnknown = false;
|
||||
} catch (JSONException e) {
|
||||
|
@ -88,10 +78,23 @@ public class PlaceholderLayerClient extends LayerClient {
|
|||
mViewport = new ViewportMetrics();
|
||||
}
|
||||
loadScreenshot();
|
||||
}
|
||||
|
||||
public static PlaceholderLayerClient createInstance(Context context) {
|
||||
return new PlaceholderLayerClient(context);
|
||||
|
||||
if (mViewportUnknown)
|
||||
mViewport.setViewport(mLayerController.getViewport());
|
||||
mLayerController.setViewportMetrics(mViewport);
|
||||
|
||||
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
|
||||
SingleTileLayer tileLayer = new SingleTileLayer(image);
|
||||
|
||||
tileLayer.beginTransaction(); // calling thread irrelevant; nobody else has a ref to tileLayer yet
|
||||
try {
|
||||
tileLayer.setOrigin(PointUtils.round(mViewport.getOrigin()));
|
||||
} finally {
|
||||
tileLayer.endTransaction();
|
||||
}
|
||||
|
||||
mLayerController.setRoot(tileLayer);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
|
@ -104,6 +107,7 @@ public class PlaceholderLayerClient extends LayerClient {
|
|||
boolean loadScreenshot() {
|
||||
if (GeckoApp.mAppContext.mLastScreen == null)
|
||||
return false;
|
||||
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(GeckoApp.mAppContext.mLastScreen));
|
||||
if (bitmap == null)
|
||||
return false;
|
||||
|
@ -121,35 +125,9 @@ public class PlaceholderLayerClient extends LayerClient {
|
|||
|
||||
if (mViewportUnknown) {
|
||||
mViewport.setPageSize(new FloatSize(mWidth, mHeight));
|
||||
if (getLayerController() != null)
|
||||
getLayerController().setPageSize(mViewport.getPageSize());
|
||||
mLayerController.setPageSize(mViewport.getPageSize());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geometryChanged() { /* no-op */ }
|
||||
@Override
|
||||
public void viewportSizeChanged() { /* no-op */ }
|
||||
@Override
|
||||
public void render() { /* no-op */ }
|
||||
|
||||
@Override
|
||||
public void setLayerController(LayerController layerController) {
|
||||
super.setLayerController(layerController);
|
||||
|
||||
if (mViewportUnknown)
|
||||
mViewport.setViewport(layerController.getViewport());
|
||||
layerController.setViewportMetrics(mViewport);
|
||||
|
||||
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
|
||||
SingleTileLayer tileLayer = new SingleTileLayer(image);
|
||||
|
||||
beginTransaction(tileLayer);
|
||||
tileLayer.setOrigin(PointUtils.round(mViewport.getDisplayportOrigin()));
|
||||
endTransaction(tileLayer);
|
||||
|
||||
layerController.setRoot(tileLayer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Kartikaya Gupta <kgupta@mozilla.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -45,11 +46,10 @@ import android.graphics.PointF;
|
|||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLES11;
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.nio.FloatBuffer;
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
||||
|
@ -64,12 +64,6 @@ public class ScrollbarLayer extends TileLayer {
|
|||
private static final int BAR_SIZE = 6;
|
||||
private static final int CAP_RADIUS = (BAR_SIZE / 2);
|
||||
|
||||
private static final int[] CROPRECT_MIDPIXEL = new int[] { CAP_RADIUS, CAP_RADIUS, 1, 1 };
|
||||
private static final int[] CROPRECT_TOPCAP = new int[] { 0, CAP_RADIUS, BAR_SIZE, -CAP_RADIUS };
|
||||
private static final int[] CROPRECT_BOTTOMCAP = new int[] { 0, BAR_SIZE, BAR_SIZE, -CAP_RADIUS };
|
||||
private static final int[] CROPRECT_LEFTCAP = new int[] { 0, BAR_SIZE, CAP_RADIUS, -BAR_SIZE };
|
||||
private static final int[] CROPRECT_RIGHTCAP = new int[] { CAP_RADIUS, BAR_SIZE, CAP_RADIUS, -BAR_SIZE };
|
||||
|
||||
private final boolean mVertical;
|
||||
private final ByteBuffer mBuffer;
|
||||
private final Bitmap mBitmap;
|
||||
|
@ -77,6 +71,56 @@ public class ScrollbarLayer extends TileLayer {
|
|||
private float mOpacity;
|
||||
private boolean mFinalized = false;
|
||||
|
||||
// Dimensions of the texture image
|
||||
private static final float TEX_HEIGHT = 8.0f;
|
||||
private static final float TEX_WIDTH = 8.0f;
|
||||
|
||||
// Texture coordinates for the scrollbar's body
|
||||
// We take a 1x1 pixel from the center of the image and scale it to become the bar
|
||||
private static final float[] BODY_TEX_COORDS = {
|
||||
// x, y
|
||||
CAP_RADIUS/TEX_WIDTH, CAP_RADIUS/TEX_HEIGHT,
|
||||
CAP_RADIUS/TEX_WIDTH, (CAP_RADIUS+1)/TEX_HEIGHT,
|
||||
(CAP_RADIUS+1)/TEX_WIDTH, CAP_RADIUS/TEX_HEIGHT,
|
||||
(CAP_RADIUS+1)/TEX_WIDTH, (CAP_RADIUS+1)/TEX_HEIGHT
|
||||
};
|
||||
|
||||
// Texture coordinates for the top cap of the scrollbar
|
||||
private static final float[] TOP_CAP_TEX_COORDS = {
|
||||
// x, y
|
||||
0 , 1.0f - CAP_RADIUS/TEX_HEIGHT,
|
||||
0 , 1.0f,
|
||||
BAR_SIZE/TEX_WIDTH, 1.0f - CAP_RADIUS/TEX_HEIGHT,
|
||||
BAR_SIZE/TEX_WIDTH, 1.0f
|
||||
};
|
||||
|
||||
// Texture coordinates for the bottom cap of the scrollbar
|
||||
private static final float[] BOT_CAP_TEX_COORDS = {
|
||||
// x, y
|
||||
0 , 1.0f - BAR_SIZE/TEX_HEIGHT,
|
||||
0 , 1.0f - CAP_RADIUS/TEX_HEIGHT,
|
||||
BAR_SIZE/TEX_WIDTH, 1.0f - BAR_SIZE/TEX_HEIGHT,
|
||||
BAR_SIZE/TEX_WIDTH, 1.0f - CAP_RADIUS/TEX_HEIGHT
|
||||
};
|
||||
|
||||
// Texture coordinates for the left cap of the scrollbar
|
||||
private static final float[] LEFT_CAP_TEX_COORDS = {
|
||||
// x, y
|
||||
0 , 1.0f - BAR_SIZE/TEX_HEIGHT,
|
||||
0 , 1.0f,
|
||||
CAP_RADIUS/TEX_WIDTH, 1.0f - BAR_SIZE/TEX_HEIGHT,
|
||||
CAP_RADIUS/TEX_WIDTH, 1.0f
|
||||
};
|
||||
|
||||
// Texture coordinates for the right cap of the scrollbar
|
||||
private static final float[] RIGHT_CAP_TEX_COORDS = {
|
||||
// x, y
|
||||
CAP_RADIUS/TEX_WIDTH, 1.0f - BAR_SIZE/TEX_HEIGHT,
|
||||
CAP_RADIUS/TEX_WIDTH, 1.0f,
|
||||
BAR_SIZE/TEX_WIDTH , 1.0f - BAR_SIZE/TEX_HEIGHT,
|
||||
BAR_SIZE/TEX_WIDTH , 1.0f
|
||||
};
|
||||
|
||||
private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) {
|
||||
super(false, image);
|
||||
mVertical = vertical;
|
||||
|
@ -97,13 +141,13 @@ public class ScrollbarLayer extends TileLayer {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static ScrollbarLayer create(boolean vertical) {
|
||||
// just create an empty image for now, it will get drawn
|
||||
// on demand anyway
|
||||
int imageSize = IntSize.nextPowerOfTwo(BAR_SIZE);
|
||||
ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(imageSize * imageSize * 4);
|
||||
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
||||
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize,
|
||||
CairoImage.FORMAT_ARGB32);
|
||||
return new ScrollbarLayer(image, vertical, buffer);
|
||||
}
|
||||
|
||||
|
@ -116,7 +160,7 @@ public class ScrollbarLayer extends TileLayer {
|
|||
if (FloatUtils.fuzzyEquals(mOpacity, 0.0f)) {
|
||||
return false;
|
||||
}
|
||||
beginTransaction();
|
||||
beginTransaction(); // called on compositor thread
|
||||
try {
|
||||
setOpacity(Math.max(mOpacity - FADE_AMOUNT, 0.0f));
|
||||
invalidate();
|
||||
|
@ -135,7 +179,7 @@ public class ScrollbarLayer extends TileLayer {
|
|||
if (FloatUtils.fuzzyEquals(mOpacity, 1.0f)) {
|
||||
return false;
|
||||
}
|
||||
beginTransaction();
|
||||
beginTransaction(); // called on compositor thread
|
||||
try {
|
||||
setOpacity(1.0f);
|
||||
invalidate();
|
||||
|
@ -165,37 +209,194 @@ public class ScrollbarLayer extends TileLayer {
|
|||
if (!initialized())
|
||||
return;
|
||||
|
||||
try {
|
||||
GLES11.glEnable(GL10.GL_BLEND);
|
||||
GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLES20.glEnable(GLES20.GL_BLEND);
|
||||
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Rect rect = RectUtils.round(mVertical ? getVerticalRect(context) : getHorizontalRect(context));
|
||||
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
|
||||
Rect rect = RectUtils.round(mVertical
|
||||
? getVerticalRect(context)
|
||||
: getHorizontalRect(context));
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
|
||||
|
||||
float viewHeight = context.viewport.height();
|
||||
float viewWidth = context.viewport.width();
|
||||
float viewHeight = context.viewport.height();
|
||||
|
||||
// for the body of the scrollbar, we take a 1x1 pixel from the center of the image
|
||||
// and scale it to become the bar
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_MIDPIXEL, 0);
|
||||
GLES11Ext.glDrawTexfOES(rect.left, viewHeight - rect.bottom, 0.0f, rect.width(), rect.height());
|
||||
float top = viewHeight - rect.top;
|
||||
float bot = viewHeight - rect.bottom;
|
||||
|
||||
if (mVertical) {
|
||||
// top endcap
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_TOPCAP, 0);
|
||||
GLES11Ext.glDrawTexfOES(rect.left, viewHeight - rect.top, 0.0f, BAR_SIZE, CAP_RADIUS);
|
||||
// bottom endcap
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_BOTTOMCAP, 0);
|
||||
GLES11Ext.glDrawTexfOES(rect.left, viewHeight - (rect.bottom + CAP_RADIUS), 0.0f, BAR_SIZE, CAP_RADIUS);
|
||||
} else {
|
||||
// left endcap
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_LEFTCAP, 0);
|
||||
GLES11Ext.glDrawTexfOES(rect.left - CAP_RADIUS, viewHeight - rect.bottom, 0.0f, CAP_RADIUS, BAR_SIZE);
|
||||
// right endcap
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, CROPRECT_RIGHTCAP, 0);
|
||||
GLES11Ext.glDrawTexfOES(rect.right, viewHeight - rect.bottom, 0.0f, CAP_RADIUS, BAR_SIZE);
|
||||
}
|
||||
} finally {
|
||||
GLES11.glDisable(GL10.GL_BLEND);
|
||||
// Coordinates for the scrollbar's body combined with the texture coordinates
|
||||
float[] bodyCoords = {
|
||||
// x, y, z, texture_x, texture_y
|
||||
rect.left/viewWidth, bot/viewHeight, 0,
|
||||
BODY_TEX_COORDS[0], BODY_TEX_COORDS[1],
|
||||
|
||||
rect.left/viewWidth, (bot+rect.height())/viewHeight, 0,
|
||||
BODY_TEX_COORDS[2], BODY_TEX_COORDS[3],
|
||||
|
||||
(rect.left+rect.width())/viewWidth, bot/viewHeight, 0,
|
||||
BODY_TEX_COORDS[4], BODY_TEX_COORDS[5],
|
||||
|
||||
(rect.left+rect.width())/viewWidth, (bot+rect.height())/viewHeight, 0,
|
||||
BODY_TEX_COORDS[6], BODY_TEX_COORDS[7]
|
||||
};
|
||||
|
||||
// Get the buffer and handles from the context
|
||||
FloatBuffer coordBuffer = context.coordBuffer;
|
||||
int positionHandle = context.positionHandle;
|
||||
int textureHandle = context.textureHandle;
|
||||
|
||||
// Make sure we are at position zero in the buffer in case other draw methods did not
|
||||
// clean up after themselves
|
||||
coordBuffer.position(0);
|
||||
coordBuffer.put(bodyCoords);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Reset the position in the buffer for the next set of vertex and texture coordinates.
|
||||
coordBuffer.position(0);
|
||||
|
||||
if (mVertical) {
|
||||
// top endcap
|
||||
float[] topCap = {
|
||||
// x, y, z, texture_x, texture_y
|
||||
rect.left/viewWidth, top/viewHeight, 0,
|
||||
TOP_CAP_TEX_COORDS[0], TOP_CAP_TEX_COORDS[1],
|
||||
|
||||
rect.left/viewWidth, (top+CAP_RADIUS)/viewHeight, 0,
|
||||
TOP_CAP_TEX_COORDS[2], TOP_CAP_TEX_COORDS[3],
|
||||
|
||||
(rect.left+BAR_SIZE)/viewWidth, top/viewHeight, 0,
|
||||
TOP_CAP_TEX_COORDS[4], TOP_CAP_TEX_COORDS[5],
|
||||
|
||||
(rect.left+BAR_SIZE)/viewWidth, (top+CAP_RADIUS)/viewHeight, 0,
|
||||
TOP_CAP_TEX_COORDS[6], TOP_CAP_TEX_COORDS[7]
|
||||
};
|
||||
|
||||
coordBuffer.put(topCap);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the
|
||||
// buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Reset the position in the buffer for the next set of vertex and texture
|
||||
// coordinates.
|
||||
coordBuffer.position(0);
|
||||
|
||||
// bottom endcap
|
||||
float[] botCap = {
|
||||
// x, y, z, texture_x, texture_y
|
||||
rect.left/viewWidth, (bot-CAP_RADIUS)/viewHeight, 0,
|
||||
BOT_CAP_TEX_COORDS[0], BOT_CAP_TEX_COORDS[1],
|
||||
|
||||
rect.left/viewWidth, (bot)/viewHeight, 0,
|
||||
BOT_CAP_TEX_COORDS[2], BOT_CAP_TEX_COORDS[3],
|
||||
|
||||
(rect.left+BAR_SIZE)/viewWidth, (bot-CAP_RADIUS)/viewHeight, 0,
|
||||
BOT_CAP_TEX_COORDS[4], BOT_CAP_TEX_COORDS[5],
|
||||
|
||||
(rect.left+BAR_SIZE)/viewWidth, (bot)/viewHeight, 0,
|
||||
BOT_CAP_TEX_COORDS[6], BOT_CAP_TEX_COORDS[7]
|
||||
};
|
||||
|
||||
coordBuffer.put(botCap);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the
|
||||
// buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Reset the position in the buffer for the next set of vertex and texture
|
||||
// coordinates.
|
||||
coordBuffer.position(0);
|
||||
} else {
|
||||
// left endcap
|
||||
float[] leftCap = {
|
||||
// x, y, z, texture_x, texture_y
|
||||
(rect.left-CAP_RADIUS)/viewWidth, bot/viewHeight, 0,
|
||||
LEFT_CAP_TEX_COORDS[0], LEFT_CAP_TEX_COORDS[1],
|
||||
(rect.left-CAP_RADIUS)/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
|
||||
LEFT_CAP_TEX_COORDS[2], LEFT_CAP_TEX_COORDS[3],
|
||||
(rect.left)/viewWidth, bot/viewHeight, 0, LEFT_CAP_TEX_COORDS[4],
|
||||
LEFT_CAP_TEX_COORDS[5],
|
||||
(rect.left)/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
|
||||
LEFT_CAP_TEX_COORDS[6], LEFT_CAP_TEX_COORDS[7]
|
||||
};
|
||||
|
||||
coordBuffer.put(leftCap);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the
|
||||
// buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Reset the position in the buffer for the next set of vertex and texture
|
||||
// coordinates.
|
||||
coordBuffer.position(0);
|
||||
|
||||
// right endcap
|
||||
float[] rightCap = {
|
||||
// x, y, z, texture_x, texture_y
|
||||
rect.right/viewWidth, (bot)/viewHeight, 0,
|
||||
RIGHT_CAP_TEX_COORDS[0], RIGHT_CAP_TEX_COORDS[1],
|
||||
|
||||
rect.right/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
|
||||
RIGHT_CAP_TEX_COORDS[2], RIGHT_CAP_TEX_COORDS[3],
|
||||
|
||||
(rect.right+CAP_RADIUS)/viewWidth, (bot)/viewHeight, 0,
|
||||
RIGHT_CAP_TEX_COORDS[4], RIGHT_CAP_TEX_COORDS[5],
|
||||
|
||||
(rect.right+CAP_RADIUS)/viewWidth, (bot+BAR_SIZE)/viewHeight, 0,
|
||||
RIGHT_CAP_TEX_COORDS[6], RIGHT_CAP_TEX_COORDS[7]
|
||||
};
|
||||
|
||||
coordBuffer.put(rightCap);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the
|
||||
// buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20,
|
||||
coordBuffer);
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -44,11 +45,8 @@ import org.mozilla.gecko.gfx.LayerController;
|
|||
import org.mozilla.gecko.gfx.TileLayer;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLES11;
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
|
@ -71,7 +69,7 @@ public class SingleTileLayer extends TileLayer {
|
|||
if (!initialized())
|
||||
return;
|
||||
|
||||
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
|
||||
|
||||
RectF bounds;
|
||||
int[] cropRect;
|
||||
|
@ -81,21 +79,49 @@ public class SingleTileLayer extends TileLayer {
|
|||
if (repeats()) {
|
||||
bounds = new RectF(0.0f, 0.0f, viewport.width(), viewport.height());
|
||||
int width = Math.round(viewport.width());
|
||||
int height = Math.round(-viewport.height());
|
||||
cropRect = new int[] { 0, size.height, width, height };
|
||||
int height = Math.round(viewport.height());
|
||||
cropRect = new int[] { 0, 0, width, height };
|
||||
} else {
|
||||
bounds = getBounds(context, new FloatSize(size));
|
||||
cropRect = new int[] { 0, size.height, size.width, -size.height };
|
||||
cropRect = new int[] { 0, 0, size.width, size.height };
|
||||
}
|
||||
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect,
|
||||
0);
|
||||
|
||||
float height = bounds.height();
|
||||
float left = bounds.left - viewport.left;
|
||||
float top = viewport.height() - (bounds.top + height - viewport.top);
|
||||
|
||||
GLES11Ext.glDrawTexfOES(left, top, 0.0f, bounds.width(), height);
|
||||
float[] coords = {
|
||||
//x, y, z, texture_x, texture_y
|
||||
left/viewport.width(), top/viewport.height(), 0,
|
||||
cropRect[0]/(float)size.width, cropRect[1]/(float)size.height,
|
||||
|
||||
left/viewport.width(), (top+height)/viewport.height(), 0,
|
||||
cropRect[0]/(float)size.width, cropRect[3]/(float)size.height,
|
||||
|
||||
(left+bounds.width())/viewport.width(), top/viewport.height(), 0,
|
||||
cropRect[2]/(float)size.width, cropRect[1]/(float)size.height,
|
||||
|
||||
(left+bounds.width())/viewport.width(), (top+height)/viewport.height(), 0,
|
||||
cropRect[2]/(float)size.width, cropRect[3]/(float)size.height
|
||||
};
|
||||
|
||||
FloatBuffer coordBuffer = context.coordBuffer;
|
||||
int positionHandle = context.positionHandle;
|
||||
int textureHandle = context.textureHandle;
|
||||
|
||||
// Make sure we are at position zero in the buffer in case other draw methods did not clean
|
||||
// up after themselves
|
||||
coordBuffer.position(0);
|
||||
coordBuffer.put(coords);
|
||||
|
||||
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
|
||||
coordBuffer.position(0);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
|
||||
|
||||
// Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
|
||||
coordBuffer.position(3);
|
||||
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ import java.nio.ByteOrder;
|
|||
import java.nio.FloatBuffer;
|
||||
import android.hardware.Camera;
|
||||
|
||||
// TODO: Port this to GLES 2.0.
|
||||
public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener {
|
||||
private static final String LOGTAG = "SurfaceTextureLayer";
|
||||
private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3)
|
||||
|
@ -73,7 +74,7 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
|
|||
private boolean mBlend;
|
||||
private boolean mNewBlend;
|
||||
|
||||
private FloatBuffer textureBuffer;
|
||||
private FloatBuffer textureBuffer;
|
||||
private FloatBuffer textureBufferInverted;
|
||||
|
||||
public SurfaceTextureLayer(int textureId) {
|
||||
|
@ -94,19 +95,19 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
|
|||
mSurface = tmp;
|
||||
|
||||
float textureMap[] = {
|
||||
0.0f, 1.0f, // top left
|
||||
0.0f, 0.0f, // bottom left
|
||||
1.0f, 1.0f, // top right
|
||||
1.0f, 0.0f, // bottom right
|
||||
0.0f, 1.0f, // top left
|
||||
0.0f, 0.0f, // bottom left
|
||||
1.0f, 1.0f, // top right
|
||||
1.0f, 0.0f, // bottom right
|
||||
};
|
||||
|
||||
textureBuffer = createBuffer(textureMap);
|
||||
|
||||
float textureMapInverted[] = {
|
||||
0.0f, 0.0f, // bottom left
|
||||
0.0f, 1.0f, // top left
|
||||
1.0f, 0.0f, // bottom right
|
||||
1.0f, 1.0f, // top right
|
||||
0.0f, 0.0f, // bottom left
|
||||
0.0f, 1.0f, // top left
|
||||
1.0f, 0.0f, // bottom right
|
||||
1.0f, 1.0f, // top right
|
||||
};
|
||||
|
||||
textureBufferInverted = createBuffer(textureMapInverted);
|
||||
|
@ -129,18 +130,18 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
|
|||
|
||||
private FloatBuffer createBuffer(float[] input) {
|
||||
// a float has 4 bytes so we allocate for each coordinate 4 bytes
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
|
||||
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
|
||||
floatBuffer.put(input);
|
||||
floatBuffer.position(0);
|
||||
floatBuffer.position(0);
|
||||
|
||||
return floatBuffer;
|
||||
}
|
||||
|
||||
public void update(Point origin, IntSize size, float resolution, boolean inverted, boolean blend) {
|
||||
beginTransaction(null);
|
||||
beginTransaction(); // this is called on the Gecko thread
|
||||
|
||||
setOrigin(origin);
|
||||
setResolution(resolution);
|
||||
|
@ -173,8 +174,8 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean performUpdates(GL10 gl, RenderContext context) {
|
||||
super.performUpdates(gl, context);
|
||||
protected boolean performUpdates(RenderContext context) {
|
||||
super.performUpdates(context);
|
||||
|
||||
if (mNewSize != null) {
|
||||
mSize = mNewSize;
|
||||
|
@ -184,10 +185,10 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
|
|||
mInverted = mNewInverted;
|
||||
mBlend = mNewBlend;
|
||||
|
||||
gl.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
|
||||
gl.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId);
|
||||
GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
|
||||
GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId);
|
||||
mSurfaceTexture.updateTexImage();
|
||||
gl.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
|
||||
GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
|
||||
|
||||
// FIXME: we should return true and rely on onFrameAvailable, but
|
||||
// that isn't working for some reason
|
||||
|
@ -257,10 +258,6 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
|
|||
GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
|
||||
GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
|
||||
GLES11.glLoadIdentity();
|
||||
|
||||
if (mBlend) {
|
||||
GLES11.glDisable(GL10.GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import android.opengl.GLES10;
|
||||
import android.opengl.GLES20;
|
||||
import java.util.Stack;
|
||||
|
||||
public class TextureGenerator {
|
||||
|
@ -64,7 +64,7 @@ public class TextureGenerator {
|
|||
public synchronized void fill() {
|
||||
int[] textures = new int[1];
|
||||
while (mTextureIds.size() < MIN_TEXTURES) {
|
||||
GLES10.glGenTextures(1, textures, 0);
|
||||
GLES20.glGenTextures(1, textures, 0);
|
||||
mTextureIds.push(textures[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -37,7 +38,7 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import android.opengl.GLES20;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** Manages a list of dead tiles, so we don't leak resources. */
|
||||
|
@ -62,13 +63,13 @@ public class TextureReaper {
|
|||
mDeadTextureIDs.add(textureID);
|
||||
}
|
||||
|
||||
public void reap(GL10 gl) {
|
||||
public void reap() {
|
||||
int[] deadTextureIDs = new int[mDeadTextureIDs.size()];
|
||||
for (int i = 0; i < deadTextureIDs.length; i++)
|
||||
deadTextureIDs[i] = mDeadTextureIDs.get(i);
|
||||
mDeadTextureIDs.clear();
|
||||
|
||||
gl.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0);
|
||||
GLES20.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Arkady Blyakher <rkadyb@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -43,8 +44,6 @@ import android.graphics.RectF;
|
|||
import android.graphics.Region;
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Log;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import javax.microedition.khronos.opengles.GL11Ext;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
@ -106,7 +105,7 @@ public abstract class TileLayer extends Layer {
|
|||
return mImage.getSize().isPositive() && (mTextureIDs == null || !mDirtyRect.isEmpty());
|
||||
}
|
||||
|
||||
private void validateTexture(GL10 gl) {
|
||||
private void validateTexture() {
|
||||
/* Calculate the ideal texture size. This must be a power of two if
|
||||
* the texture is repeated or OpenGL ES 2.0 isn't supported, as
|
||||
* OpenGL ES 2.0 is required for NPOT texture support (without
|
||||
|
@ -129,7 +128,7 @@ public abstract class TileLayer extends Layer {
|
|||
|
||||
// Free the texture immediately, so we don't incur a
|
||||
// temporarily increased memory usage.
|
||||
TextureReaper.get().reap(gl);
|
||||
TextureReaper.get().reap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,15 +143,15 @@ public abstract class TileLayer extends Layer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean performUpdates(GL10 gl, RenderContext context) {
|
||||
super.performUpdates(gl, context);
|
||||
protected boolean performUpdates(RenderContext context) {
|
||||
super.performUpdates(context);
|
||||
|
||||
if (mSkipTextureUpdate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reallocate the texture if the size has changed
|
||||
validateTexture(gl);
|
||||
validateTexture();
|
||||
|
||||
// Don't do any work if the image has an invalid size.
|
||||
if (!mImage.getSize().isPositive())
|
||||
|
@ -160,9 +159,9 @@ public abstract class TileLayer extends Layer {
|
|||
|
||||
// If we haven't allocated a texture, assume the whole region is dirty
|
||||
if (mTextureIDs == null) {
|
||||
uploadFullTexture(gl);
|
||||
uploadFullTexture();
|
||||
} else {
|
||||
uploadDirtyRect(gl, mDirtyRect);
|
||||
uploadDirtyRect(mDirtyRect);
|
||||
}
|
||||
|
||||
mDirtyRect.setEmpty();
|
||||
|
@ -170,12 +169,12 @@ public abstract class TileLayer extends Layer {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void uploadFullTexture(GL10 gl) {
|
||||
private void uploadFullTexture() {
|
||||
IntSize bufferSize = mImage.getSize();
|
||||
uploadDirtyRect(gl, new Rect(0, 0, bufferSize.width, bufferSize.height));
|
||||
uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height));
|
||||
}
|
||||
|
||||
private void uploadDirtyRect(GL10 gl, Rect dirtyRect) {
|
||||
private void uploadDirtyRect(Rect dirtyRect) {
|
||||
// If we have nothing to upload, just return for now
|
||||
if (dirtyRect.isEmpty())
|
||||
return;
|
||||
|
@ -189,7 +188,7 @@ public abstract class TileLayer extends Layer {
|
|||
|
||||
if (mTextureIDs == null) {
|
||||
mTextureIDs = new int[1];
|
||||
gl.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
|
||||
GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
|
||||
newlyCreated = true;
|
||||
}
|
||||
|
||||
|
@ -199,20 +198,12 @@ public abstract class TileLayer extends Layer {
|
|||
int cairoFormat = mImage.getFormat();
|
||||
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
|
||||
|
||||
bindAndSetGLParameters(gl);
|
||||
bindAndSetGLParameters();
|
||||
|
||||
if (newlyCreated || dirtyRect.contains(bufferRect)) {
|
||||
if (mSize.equals(bufferSize)) {
|
||||
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height,
|
||||
0, glInfo.format, glInfo.type, imageBuffer);
|
||||
return;
|
||||
} else {
|
||||
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height,
|
||||
0, glInfo.format, glInfo.type, null);
|
||||
gl.glTexSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, bufferSize.width, bufferSize.height,
|
||||
glInfo.format, glInfo.type, imageBuffer);
|
||||
return;
|
||||
}
|
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width,
|
||||
mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure that the dirty region intersects with the buffer rect,
|
||||
|
@ -237,19 +228,21 @@ public abstract class TileLayer extends Layer {
|
|||
}
|
||||
|
||||
viewBuffer.position(position);
|
||||
gl.glTexSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width,
|
||||
Math.min(bufferSize.height - dirtyRect.top, dirtyRect.height()),
|
||||
glInfo.format, glInfo.type, viewBuffer);
|
||||
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width,
|
||||
Math.min(bufferSize.height - dirtyRect.top, dirtyRect.height()),
|
||||
glInfo.format, glInfo.type, viewBuffer);
|
||||
}
|
||||
|
||||
private void bindAndSetGLParameters(GL10 gl) {
|
||||
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]);
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
|
||||
private void bindAndSetGLParameters() {
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_NEAREST);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
|
||||
int repeatMode = mRepeat ? GL10.GL_REPEAT : GL10.GL_CLAMP_TO_EDGE;
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode);
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode);
|
||||
int repeatMode = mRepeat ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE;
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
public class ViewTransform {
|
||||
public float x;
|
||||
public float y;
|
||||
public float scale;
|
||||
|
||||
public ViewTransform(float inX, float inY, float inScale) {
|
||||
x = inX;
|
||||
y = inY;
|
||||
scale = inScale;
|
||||
}
|
||||
}
|
||||
|
|
@ -62,32 +62,21 @@ public class ViewportMetrics {
|
|||
|
||||
private FloatSize mPageSize;
|
||||
private RectF mViewportRect;
|
||||
private PointF mViewportOffset;
|
||||
private float mZoomFactor;
|
||||
|
||||
// A scale from -1,-1 to 1,1 that represents what edge of the displayport
|
||||
// we want the viewport to be biased towards.
|
||||
private PointF mViewportBias;
|
||||
private static final float MAX_BIAS = 0.8f;
|
||||
|
||||
public ViewportMetrics() {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
|
||||
mPageSize = new FloatSize(metrics.widthPixels, metrics.heightPixels);
|
||||
mViewportRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels);
|
||||
mViewportOffset = new PointF(0, 0);
|
||||
mZoomFactor = 1.0f;
|
||||
mViewportBias = new PointF(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public ViewportMetrics(ViewportMetrics viewport) {
|
||||
mPageSize = new FloatSize(viewport.getPageSize());
|
||||
mViewportRect = new RectF(viewport.getViewport());
|
||||
PointF offset = viewport.getViewportOffset();
|
||||
mViewportOffset = new PointF(offset.x, offset.y);
|
||||
mZoomFactor = viewport.getZoomFactor();
|
||||
mViewportBias = viewport.mViewportBias;
|
||||
}
|
||||
|
||||
public ViewportMetrics(JSONObject json) throws JSONException {
|
||||
|
@ -97,55 +86,17 @@ public class ViewportMetrics {
|
|||
float height = (float)json.getDouble("height");
|
||||
float pageWidth = (float)json.getDouble("pageWidth");
|
||||
float pageHeight = (float)json.getDouble("pageHeight");
|
||||
float offsetX = (float)json.getDouble("offsetX");
|
||||
float offsetY = (float)json.getDouble("offsetY");
|
||||
float zoom = (float)json.getDouble("zoom");
|
||||
|
||||
mPageSize = new FloatSize(pageWidth, pageHeight);
|
||||
mViewportRect = new RectF(x, y, x + width, y + height);
|
||||
mViewportOffset = new PointF(offsetX, offsetY);
|
||||
mZoomFactor = zoom;
|
||||
mViewportBias = new PointF(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public PointF getOptimumViewportOffset(IntSize displayportSize) {
|
||||
/* XXX Until bug #524925 is fixed, changing the viewport origin will
|
||||
* cause unnecessary relayouts. This may cause rendering time to
|
||||
* increase and should be considered.
|
||||
*/
|
||||
RectF viewport = getClampedViewport();
|
||||
|
||||
FloatSize bufferSpace = new FloatSize(displayportSize.width - viewport.width(),
|
||||
displayportSize.height - viewport.height());
|
||||
PointF optimumOffset =
|
||||
new PointF(bufferSpace.width * ((mViewportBias.x + 1.0f) / 2.0f),
|
||||
bufferSpace.height * ((mViewportBias.y + 1.0f) / 2.0f));
|
||||
|
||||
// Make sure this offset won't cause wasted pixels in the displayport
|
||||
// (i.e. make sure the resultant displayport intersects with the page
|
||||
// as much as possible)
|
||||
if (viewport.left - optimumOffset.x < 0)
|
||||
optimumOffset.x = viewport.left;
|
||||
else if ((bufferSpace.width - optimumOffset.x) + viewport.right > mPageSize.width)
|
||||
optimumOffset.x = bufferSpace.width - (mPageSize.width - viewport.right);
|
||||
|
||||
if (viewport.top - optimumOffset.y < 0)
|
||||
optimumOffset.y = viewport.top;
|
||||
else if ((bufferSpace.height - optimumOffset.y) + viewport.bottom > mPageSize.height)
|
||||
optimumOffset.y = bufferSpace.height - (mPageSize.height - viewport.bottom);
|
||||
|
||||
return new PointF(Math.round(optimumOffset.x), Math.round(optimumOffset.y));
|
||||
}
|
||||
|
||||
public PointF getOrigin() {
|
||||
return new PointF(mViewportRect.left, mViewportRect.top);
|
||||
}
|
||||
|
||||
public PointF getDisplayportOrigin() {
|
||||
return new PointF(mViewportRect.left - mViewportOffset.x,
|
||||
mViewportRect.top - mViewportOffset.y);
|
||||
}
|
||||
|
||||
public FloatSize getSize() {
|
||||
return new FloatSize(mViewportRect.width(), mViewportRect.height());
|
||||
}
|
||||
|
@ -174,10 +125,6 @@ public class ViewportMetrics {
|
|||
return clampedViewport;
|
||||
}
|
||||
|
||||
public PointF getViewportOffset() {
|
||||
return mViewportOffset;
|
||||
}
|
||||
|
||||
public FloatSize getPageSize() {
|
||||
return mPageSize;
|
||||
}
|
||||
|
@ -195,23 +142,6 @@ public class ViewportMetrics {
|
|||
}
|
||||
|
||||
public void setOrigin(PointF origin) {
|
||||
// When the origin is set, we compare it with the last value set and
|
||||
// change the viewport bias accordingly, so that any viewport based
|
||||
// on these metrics will have a larger buffer in the direction of
|
||||
// movement.
|
||||
|
||||
// XXX Note the comment about bug #524925 in getOptimumViewportOffset.
|
||||
// Ideally, the viewport bias would be a sliding scale, but we
|
||||
// don't want to change it too often at the moment.
|
||||
if (FloatUtils.fuzzyEquals(origin.x, mViewportRect.left))
|
||||
mViewportBias.x = 0;
|
||||
else
|
||||
mViewportBias.x = ((mViewportRect.left - origin.x) > 0) ? MAX_BIAS : -MAX_BIAS;
|
||||
if (FloatUtils.fuzzyEquals(origin.y, mViewportRect.top))
|
||||
mViewportBias.y = 0;
|
||||
else
|
||||
mViewportBias.y = ((mViewportRect.top - origin.y) > 0) ? MAX_BIAS : -MAX_BIAS;
|
||||
|
||||
mViewportRect.set(origin.x, origin.y,
|
||||
origin.x + mViewportRect.width(),
|
||||
origin.y + mViewportRect.height());
|
||||
|
@ -222,10 +152,6 @@ public class ViewportMetrics {
|
|||
mViewportRect.bottom = mViewportRect.top + size.height;
|
||||
}
|
||||
|
||||
public void setViewportOffset(PointF offset) {
|
||||
mViewportOffset = offset;
|
||||
}
|
||||
|
||||
public void setZoomFactor(float zoomFactor) {
|
||||
mZoomFactor = zoomFactor;
|
||||
}
|
||||
|
@ -246,15 +172,6 @@ public class ViewportMetrics {
|
|||
setOrigin(origin);
|
||||
|
||||
mZoomFactor = newZoomFactor;
|
||||
|
||||
// Similar to setOrigin, set the viewport bias based on the focal point
|
||||
// of the zoom so that a viewport based on these metrics will have a
|
||||
// larger buffer based on the direction of movement when scaling.
|
||||
//
|
||||
// This is biased towards scaling outwards, as zooming in doesn't
|
||||
// really require a viewport bias.
|
||||
mViewportBias.set(((focus.x / mViewportRect.width()) * (2.0f * MAX_BIAS)) - MAX_BIAS,
|
||||
((focus.y / mViewportRect.height()) * (2.0f * MAX_BIAS)) - MAX_BIAS);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -267,14 +184,12 @@ public class ViewportMetrics {
|
|||
result.mPageSize = mPageSize.interpolate(to.mPageSize, t);
|
||||
result.mZoomFactor = FloatUtils.interpolate(mZoomFactor, to.mZoomFactor, t);
|
||||
result.mViewportRect = RectUtils.interpolate(mViewportRect, to.mViewportRect, t);
|
||||
result.mViewportOffset = PointUtils.interpolate(mViewportOffset, to.mViewportOffset, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean fuzzyEquals(ViewportMetrics other) {
|
||||
return mPageSize.fuzzyEquals(other.mPageSize)
|
||||
&& RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect)
|
||||
&& FloatUtils.fuzzyEquals(mViewportOffset, other.mViewportOffset)
|
||||
&& FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor);
|
||||
}
|
||||
|
||||
|
@ -291,8 +206,6 @@ public class ViewportMetrics {
|
|||
.append(", \"height\" : ").append(height)
|
||||
.append(", \"pageWidth\" : ").append(mPageSize.width)
|
||||
.append(", \"pageHeight\" : ").append(mPageSize.height)
|
||||
.append(", \"offsetX\" : ").append(mViewportOffset.x)
|
||||
.append(", \"offsetY\" : ").append(mViewportOffset.y)
|
||||
.append(", \"zoom\" : ").append(mZoomFactor)
|
||||
.append(" }");
|
||||
return sb.toString();
|
||||
|
@ -303,9 +216,7 @@ public class ViewportMetrics {
|
|||
StringBuffer buff = new StringBuffer(128);
|
||||
buff.append("v=").append(mViewportRect.toString())
|
||||
.append(" p=").append(mPageSize.toString())
|
||||
.append(" z=").append(mZoomFactor)
|
||||
.append(" o=").append(mViewportOffset.x)
|
||||
.append(',').append(mViewportOffset.y);
|
||||
.append(" z=").append(mZoomFactor);
|
||||
return buff.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Chris Lord <chrislord.net@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -37,40 +38,43 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
/**
|
||||
* A layer client provides tiles and manages other information used by the layer controller.
|
||||
*/
|
||||
public abstract class LayerClient {
|
||||
private LayerController mLayerController;
|
||||
import android.graphics.Point;
|
||||
|
||||
public abstract void geometryChanged();
|
||||
public abstract void viewportSizeChanged();
|
||||
protected abstract void render();
|
||||
public class VirtualLayer extends Layer {
|
||||
private IntSize mSize;
|
||||
|
||||
public LayerController getLayerController() { return mLayerController; }
|
||||
public void setLayerController(LayerController layerController) {
|
||||
mLayerController = layerController;
|
||||
@Override
|
||||
public void draw(RenderContext context) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility function for calling Layer.beginTransaction with the
|
||||
* appropriate LayerView.
|
||||
*/
|
||||
public void beginTransaction(Layer aLayer) {
|
||||
if (mLayerController != null) {
|
||||
LayerView view = mLayerController.getView();
|
||||
if (view != null) {
|
||||
aLayer.beginTransaction(view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
aLayer.beginTransaction();
|
||||
@Override
|
||||
public IntSize getSize() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
// Included for symmetry.
|
||||
public void endTransaction(Layer aLayer) {
|
||||
aLayer.endTransaction();
|
||||
public void setSize(IntSize size) {
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
void setOriginAndResolution(Point newOrigin, float newResolution) {
|
||||
// This is an optimized version of the following code:
|
||||
// beginTransaction();
|
||||
// try {
|
||||
// setOrigin(newOrigin);
|
||||
// setResolution(newResolution);
|
||||
// performUpdates(null);
|
||||
// } finally {
|
||||
// endTransaction();
|
||||
// }
|
||||
|
||||
// it is safe to drop the transaction lock in this instance (i.e. for the
|
||||
// VirtualLayer that is just a shadow of what gecko is painting) because
|
||||
// the origin and resolution of this layer are never used for anything
|
||||
// meaningful.
|
||||
|
||||
mOrigin = newOrigin;
|
||||
mResolution = newResolution;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* James Willcox <jwillcox@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.gfx.LayerController;
|
||||
import org.mozilla.gecko.gfx.SingleTileLayer;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import android.opengl.GLES11;
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.graphics.RectF;
|
||||
import android.util.Log;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* Encapsulates the logic needed to draw the single-tiled Gecko texture
|
||||
*/
|
||||
public class WidgetTileLayer extends Layer {
|
||||
private static final String LOGTAG = "WidgetTileLayer";
|
||||
|
||||
private int[] mTextureIDs;
|
||||
private CairoImage mImage;
|
||||
|
||||
public WidgetTileLayer(CairoImage image) {
|
||||
mImage = image;
|
||||
}
|
||||
|
||||
protected boolean initialized() { return mTextureIDs != null; }
|
||||
|
||||
@Override
|
||||
public IntSize getSize() { return mImage.getSize(); }
|
||||
|
||||
protected void bindAndSetGLParameters() {
|
||||
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]);
|
||||
GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
|
||||
GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (mTextureIDs != null)
|
||||
TextureReaper.get().add(mTextureIDs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean performUpdates(GL10 gl, RenderContext context) {
|
||||
super.performUpdates(gl, context);
|
||||
|
||||
if (mTextureIDs == null) {
|
||||
mTextureIDs = new int[1];
|
||||
GLES11.glGenTextures(1, mTextureIDs, 0);
|
||||
}
|
||||
|
||||
bindAndSetGLParameters();
|
||||
GeckoAppShell.bindWidgetTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(RenderContext context) {
|
||||
// mTextureIDs may be null here during startup if Layer.java's draw method
|
||||
// failed to acquire the transaction lock and call performUpdates.
|
||||
if (!initialized())
|
||||
return;
|
||||
|
||||
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]);
|
||||
|
||||
RectF bounds;
|
||||
int[] cropRect;
|
||||
IntSize size = getSize();
|
||||
RectF viewport = context.viewport;
|
||||
|
||||
bounds = getBounds(context, new FloatSize(size));
|
||||
cropRect = new int[] { 0, size.height, size.width, -size.height };
|
||||
bounds.offset(-viewport.left, -viewport.top);
|
||||
|
||||
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect,
|
||||
0);
|
||||
|
||||
float top = viewport.height() - (bounds.top + bounds.height());
|
||||
|
||||
// There may be errors from a previous GL call, so clear them first because
|
||||
// we want to check for one below
|
||||
while (GLES11.glGetError() != GLES11.GL_NO_ERROR);
|
||||
|
||||
GLES11Ext.glDrawTexfOES(bounds.left, top, 0.0f, bounds.width(), bounds.height());
|
||||
int error = GLES11.glGetError();
|
||||
if (error != GLES11.GL_NO_ERROR) {
|
||||
Log.i(LOGTAG, "Failed to draw texture: " + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,22 +217,10 @@ public class PanZoomController
|
|||
// if that's the case, abort any animation in progress and re-zoom so that the page
|
||||
// snaps to edges. for other cases (where the user's finger(s) are down) don't do
|
||||
// anything special.
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
if (mState == PanZoomState.FLING) {
|
||||
mX.stopFling();
|
||||
mY.stopFling();
|
||||
mState = PanZoomState.NOTHING;
|
||||
// fall through
|
||||
case ANIMATED_ZOOM:
|
||||
// the zoom that's in progress likely makes no sense any more (such as if
|
||||
// the screen orientation changed) so abort it
|
||||
// fall through
|
||||
case NOTHING:
|
||||
// Don't do animations here; they're distracting and can cause flashes on page
|
||||
// transitions.
|
||||
mController.setViewportMetrics(getValidViewportMetrics());
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -830,7 +818,7 @@ public class PanZoomController
|
|||
}
|
||||
|
||||
public boolean getRedrawHint() {
|
||||
return (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING);
|
||||
return (mState != PanZoomState.PINCHING && mState != PanZoomState.ANIMATED_ZOOM);
|
||||
}
|
||||
|
||||
private void sendPointToGecko(String event, MotionEvent motionEvent) {
|
||||
|
|
|
@ -102,6 +102,12 @@ const kElementsReceivingInput = {
|
|||
video: true
|
||||
};
|
||||
|
||||
// How many pixels on each side to buffer.
|
||||
const kBufferAmount = 300;
|
||||
|
||||
// Whether we're using GL layers.
|
||||
const kUsingGLLayers = true;
|
||||
|
||||
function dump(a) {
|
||||
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
|
||||
}
|
||||
|
@ -282,6 +288,18 @@ var BrowserApp = {
|
|||
gScreenHeight = window.arguments[3];
|
||||
}
|
||||
|
||||
// Monitor window size changes
|
||||
window.addEventListener("resize", function() {
|
||||
if (gScreenWidth != window.outerWidth || gScreenHeight != window.outerHeight) {
|
||||
gScreenWidth = window.outerWidth;
|
||||
gScreenHeight = window.outerHeight;
|
||||
BrowserApp.selectedTab.refreshDisplayPort();
|
||||
|
||||
// Java pauses updates until we confirm we have the new display size
|
||||
sendMessageToJava({ gecko: { type: "Viewport:UpdateAndDraw" } });
|
||||
}
|
||||
}, false);
|
||||
|
||||
// XXX maybe we don't do this if the launch was kicked off from external
|
||||
Services.io.offline = false;
|
||||
|
||||
|
@ -426,8 +444,9 @@ var BrowserApp = {
|
|||
return;
|
||||
|
||||
aTab.setActive(true);
|
||||
aTab.refreshDisplayPort();
|
||||
aTab.updateViewport(false);
|
||||
this.deck.selectedPanel = aTab.vbox;
|
||||
this.deck.selectedPanel = aTab.browser;
|
||||
},
|
||||
|
||||
get selectedBrowser() {
|
||||
|
@ -822,56 +841,43 @@ var BrowserApp = {
|
|||
// as well as the browser's content window, and modify the scrollX and scrollY on the content window.
|
||||
focused.scrollIntoView(false);
|
||||
|
||||
// As Gecko isn't aware of the zoom level we're drawing with, the element may not entirely be in view
|
||||
// yet. Check for that, and scroll some extra to compensate, if necessary.
|
||||
let focusedRect = focused.getBoundingClientRect();
|
||||
let visibleContentWidth = gScreenWidth / tab._zoom;
|
||||
let visibleContentHeight = gScreenHeight / tab._zoom;
|
||||
|
||||
let positionChanged = false;
|
||||
let scrollX = win.scrollX;
|
||||
let scrollY = win.scrollY;
|
||||
|
||||
if (focusedRect.right >= visibleContentWidth && focusedRect.left > 0) {
|
||||
// the element is too far off the right side, so we need to scroll to the right more
|
||||
scrollX += Math.min(focusedRect.left, focusedRect.right - visibleContentWidth);
|
||||
positionChanged = true;
|
||||
} else if (focusedRect.left < 0) {
|
||||
// the element is too far off the left side, so we need to scroll to the left more
|
||||
scrollX += focusedRect.left;
|
||||
positionChanged = true;
|
||||
}
|
||||
if (focusedRect.bottom >= visibleContentHeight && focusedRect.top > 0) {
|
||||
// the element is too far down, so we need to scroll down more
|
||||
scrollY += Math.min(focusedRect.top, focusedRect.bottom - visibleContentHeight);
|
||||
positionChanged = true;
|
||||
} else if (focusedRect.top < 0) {
|
||||
// the element is too far up, so we need to scroll up more
|
||||
scrollY += focusedRect.top;
|
||||
positionChanged = true;
|
||||
}
|
||||
|
||||
if (positionChanged)
|
||||
win.scrollTo(scrollX, scrollY);
|
||||
|
||||
// update userScrollPos so that we don't send a duplicate viewport update by triggering
|
||||
// our scroll listener
|
||||
tab.userScrollPos.x = win.scrollX;
|
||||
tab.userScrollPos.y = win.scrollY;
|
||||
|
||||
// note that:
|
||||
// 1. because of the way we do zooming using a CSS transform, gecko does not take into
|
||||
// account the effect of the zoom on the viewport size.
|
||||
// 2. if the input element is near the bottom/right of the page (less than one viewport
|
||||
// height/width away from the bottom/right), the scrollIntoView call will make gecko scroll to the
|
||||
// bottom/right of the page in an attempt to align the input field with the top of the viewport.
|
||||
// however, since gecko doesn't know about the zoom, what it thinks is the "bottom/right of
|
||||
// the page" isn't actually the bottom/right of the page at the current zoom level, and we
|
||||
// need to adjust this further.
|
||||
// 3. we can't actually adjust this by changing the window scroll position, as gecko already thinks
|
||||
// we're at the bottom/right, so instead we do it by changing the viewportExcess on the tab and
|
||||
// moving the browser element.
|
||||
|
||||
let visibleContentWidth = tab._viewport.width / tab._viewport.zoom;
|
||||
let visibleContentHeight = tab._viewport.height / tab._viewport.zoom;
|
||||
// get the rect that the focused element occupies relative to what gecko thinks the viewport is,
|
||||
// and adjust it by viewportExcess to so that it is relative to what the user sees as the viewport.
|
||||
let focusedRect = focused.getBoundingClientRect();
|
||||
focusedRect = {
|
||||
left: focusedRect.left - tab.viewportExcess.x,
|
||||
right: focusedRect.right - tab.viewportExcess.x,
|
||||
top: focusedRect.top - tab.viewportExcess.y,
|
||||
bottom: focusedRect.bottom - tab.viewportExcess.y
|
||||
};
|
||||
let transformChanged = false;
|
||||
if (focusedRect.right >= visibleContentWidth && focusedRect.left > 0) {
|
||||
// the element is too far off the right side, so we need to scroll to the right more
|
||||
tab.viewportExcess.x += Math.min(focusedRect.left, focusedRect.right - visibleContentWidth);
|
||||
transformChanged = true;
|
||||
} else if (focusedRect.left < 0) {
|
||||
// the element is too far off the left side, so we need to scroll to the left more
|
||||
tab.viewportExcess.x += focusedRect.left;
|
||||
transformChanged = true;
|
||||
}
|
||||
if (focusedRect.bottom >= visibleContentHeight && focusedRect.top > 0) {
|
||||
// the element is too far down, so we need to scroll down more
|
||||
tab.viewportExcess.y += Math.min(focusedRect.top, focusedRect.bottom - visibleContentHeight);
|
||||
transformChanged = true;
|
||||
} else if (focusedRect.top < 0) {
|
||||
// the element is too far up, so we need to scroll up more
|
||||
tab.viewportExcess.y += focusedRect.top;
|
||||
transformChanged = true;
|
||||
}
|
||||
if (transformChanged)
|
||||
tab.updateTransform();
|
||||
// finally, let java know where we ended up
|
||||
tab.sendViewportUpdate();
|
||||
}
|
||||
|
@ -1467,13 +1473,10 @@ let gScreenHeight = 1;
|
|||
|
||||
function Tab(aURL, aParams) {
|
||||
this.browser = null;
|
||||
this.vbox = null;
|
||||
this.id = 0;
|
||||
this.showProgress = true;
|
||||
this.create(aURL, aParams);
|
||||
this._viewport = { x: 0, y: 0, width: gScreenWidth, height: gScreenHeight, offsetX: 0, offsetY: 0,
|
||||
pageWidth: gScreenWidth, pageHeight: gScreenHeight, zoom: 1.0 };
|
||||
this.viewportExcess = { x: 0, y: 0 };
|
||||
this._zoom = 1.0;
|
||||
this.documentIdForCurrentViewport = null;
|
||||
this.userScrollPos = { x: 0, y: 0 };
|
||||
this._pluginCount = 0;
|
||||
|
@ -1487,21 +1490,21 @@ Tab.prototype = {
|
|||
|
||||
aParams = aParams || {};
|
||||
|
||||
this.vbox = document.createElement("vbox");
|
||||
this.vbox.align = "start";
|
||||
BrowserApp.deck.appendChild(this.vbox);
|
||||
|
||||
this.browser = document.createElement("browser");
|
||||
this.browser.setAttribute("type", "content-targetable");
|
||||
this.setBrowserSize(980, 480);
|
||||
this.browser.style.MozTransformOrigin = "0 0";
|
||||
this.vbox.appendChild(this.browser);
|
||||
BrowserApp.deck.appendChild(this.browser);
|
||||
|
||||
this.browser.stop();
|
||||
|
||||
// Turn off clipping so we can buffer areas outside of the browser element.
|
||||
let frameLoader = this.browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||
frameLoader.clipSubdocument = false;
|
||||
if (kUsingGLLayers) {
|
||||
frameLoader.renderMode = Ci.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL;
|
||||
frameLoader.clampScrollPosition = false;
|
||||
} else {
|
||||
// Turn off clipping so we can buffer areas outside of the browser element.
|
||||
frameLoader.clipSubdocument = false;
|
||||
}
|
||||
|
||||
this.id = ++gTabIDFactory;
|
||||
|
||||
|
@ -1581,11 +1584,10 @@ Tab.prototype = {
|
|||
// Make sure the previously selected panel remains selected. The selected panel of a deck is
|
||||
// not stable when panels are removed.
|
||||
let selectedPanel = BrowserApp.deck.selectedPanel;
|
||||
BrowserApp.deck.removeChild(this.vbox);
|
||||
BrowserApp.deck.removeChild(this.browser);
|
||||
BrowserApp.deck.selectedPanel = selectedPanel;
|
||||
|
||||
this.browser = null;
|
||||
this.vbox = null;
|
||||
this.documentIdForCurrentViewport = null;
|
||||
},
|
||||
|
||||
|
@ -1604,6 +1606,20 @@ Tab.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
refreshDisplayPort: function refreshDisplayPort() {
|
||||
if (this._zoom <= 0)
|
||||
return;
|
||||
|
||||
dump("### Setting displayport, screen-size = " + gScreenWidth + "x" + gScreenHeight + ", zoom = " + this._zoom);
|
||||
let cwu = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
cwu.setResolution(this._zoom, this._zoom);
|
||||
cwu.setDisplayPortForElement(-kBufferAmount / this._zoom, -kBufferAmount / this._zoom,
|
||||
(gScreenWidth + kBufferAmount * 2) / this._zoom,
|
||||
(gScreenHeight + kBufferAmount * 2) / this._zoom,
|
||||
this.browser.contentDocument.documentElement);
|
||||
},
|
||||
|
||||
set viewport(aViewport) {
|
||||
// Transform coordinates based on zoom
|
||||
aViewport.x /= aViewport.zoom;
|
||||
|
@ -1615,65 +1631,34 @@ Tab.prototype = {
|
|||
this.userScrollPos.x = win.scrollX;
|
||||
this.userScrollPos.y = win.scrollY;
|
||||
|
||||
// If we've been asked to over-scroll, do it via the transformation
|
||||
// and store it separately to the viewport.
|
||||
let excessX = aViewport.x - win.scrollX;
|
||||
let excessY = aViewport.y - win.scrollY;
|
||||
|
||||
this._viewport.width = gScreenWidth = aViewport.width;
|
||||
this._viewport.height = gScreenHeight = aViewport.height;
|
||||
|
||||
let transformChanged = false;
|
||||
|
||||
if ((aViewport.offsetX != this._viewport.offsetX) ||
|
||||
(excessX != this.viewportExcess.x)) {
|
||||
this._viewport.offsetX = aViewport.offsetX;
|
||||
this.viewportExcess.x = excessX;
|
||||
transformChanged = true;
|
||||
// Set zoom level
|
||||
let zoom = aViewport.zoom;
|
||||
if (Math.abs(zoom - this._zoom) >= 1e-6) {
|
||||
this._zoom = zoom;
|
||||
this.refreshDisplayPort();
|
||||
}
|
||||
if ((aViewport.offsetY != this._viewport.offsetY) ||
|
||||
(excessY != this.viewportExcess.y)) {
|
||||
this._viewport.offsetY = aViewport.offsetY;
|
||||
this.viewportExcess.y = excessY;
|
||||
transformChanged = true;
|
||||
}
|
||||
if (Math.abs(aViewport.zoom - this._viewport.zoom) >= 1e-6) {
|
||||
this._viewport.zoom = aViewport.zoom;
|
||||
transformChanged = true;
|
||||
}
|
||||
|
||||
if (transformChanged)
|
||||
this.updateTransform();
|
||||
},
|
||||
|
||||
updateTransform: function() {
|
||||
let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6);
|
||||
let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom);
|
||||
let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom);
|
||||
|
||||
let transform =
|
||||
"translate(" + x + "px, " +
|
||||
y + "px)";
|
||||
if (hasZoom)
|
||||
transform += " scale(" + this._viewport.zoom + ")";
|
||||
|
||||
this.browser.style.MozTransform = transform;
|
||||
},
|
||||
|
||||
get viewport() {
|
||||
let viewport = {
|
||||
width: gScreenWidth,
|
||||
height: gScreenHeight,
|
||||
pageWidth: gScreenWidth,
|
||||
pageHeight: gScreenHeight,
|
||||
zoom: this._zoom
|
||||
};
|
||||
|
||||
// Update the viewport to current dimensions
|
||||
this._viewport.x = (this.browser.contentWindow.scrollX +
|
||||
this.viewportExcess.x) || 0;
|
||||
this._viewport.y = (this.browser.contentWindow.scrollY +
|
||||
this.viewportExcess.y) || 0;
|
||||
viewport.x = this.browser.contentWindow.scrollX || 0;
|
||||
viewport.y = this.browser.contentWindow.scrollY || 0;
|
||||
|
||||
// Transform coordinates based on zoom
|
||||
this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom);
|
||||
this._viewport.y = Math.round(this._viewport.y * this._viewport.zoom);
|
||||
viewport.x = Math.round(viewport.x * viewport.zoom);
|
||||
viewport.y = Math.round(viewport.y * viewport.zoom);
|
||||
|
||||
let doc = this.browser.contentDocument;
|
||||
if (doc != null) {
|
||||
let pageWidth = this._viewport.width, pageHeight = this._viewport.height;
|
||||
let pageWidth = viewport.width, pageHeight = viewport.height;
|
||||
if (doc instanceof SVGDocument) {
|
||||
let rect = doc.rootElement.getBoundingClientRect();
|
||||
// we need to add rect.left and rect.top twice so that the SVG is drawn
|
||||
|
@ -1690,8 +1675,8 @@ Tab.prototype = {
|
|||
}
|
||||
|
||||
/* Transform the page width and height based on the zoom factor. */
|
||||
pageWidth *= this._viewport.zoom;
|
||||
pageHeight *= this._viewport.zoom;
|
||||
pageWidth *= viewport.zoom;
|
||||
pageHeight *= viewport.zoom;
|
||||
|
||||
/*
|
||||
* Avoid sending page sizes of less than screen size before we hit DOMContentLoaded, because
|
||||
|
@ -1699,29 +1684,24 @@ Tab.prototype = {
|
|||
* send updates regardless of page size; we'll zoom to fit the content as needed.
|
||||
*/
|
||||
if (doc.readyState === 'complete' || (pageWidth >= gScreenWidth && pageHeight >= gScreenHeight)) {
|
||||
this._viewport.pageWidth = pageWidth;
|
||||
this._viewport.pageHeight = pageHeight;
|
||||
viewport.pageWidth = pageWidth;
|
||||
viewport.pageHeight = pageHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return this._viewport;
|
||||
return viewport;
|
||||
},
|
||||
|
||||
updateViewport: function(aReset, aZoomLevel) {
|
||||
if (!aZoomLevel)
|
||||
aZoomLevel = this.getDefaultZoomLevel();
|
||||
dump("### JS side updateViewport " + aReset + " zoom " + aZoomLevel + "\n");
|
||||
|
||||
let win = this.browser.contentWindow;
|
||||
let zoom = (aReset ? aZoomLevel : this._viewport.zoom);
|
||||
let xpos = ((aReset && win) ? win.scrollX * zoom : this._viewport.x);
|
||||
let ypos = ((aReset && win) ? win.scrollY * zoom : this._viewport.y);
|
||||
if (aReset) {
|
||||
if (!aZoomLevel)
|
||||
aZoomLevel = this.getDefaultZoomLevel();
|
||||
this._zoom = aZoomLevel;
|
||||
this.refreshDisplayPort();
|
||||
}
|
||||
|
||||
this.viewportExcess = { x: 0, y: 0 };
|
||||
this.viewport = { x: xpos, y: ypos,
|
||||
offsetX: 0, offsetY: 0,
|
||||
width: this._viewport.width, height: this._viewport.height,
|
||||
pageWidth: gScreenWidth, pageHeight: gScreenHeight,
|
||||
zoom: zoom };
|
||||
this.sendViewportUpdate();
|
||||
},
|
||||
|
||||
|
@ -2057,7 +2037,6 @@ Tab.prototype = {
|
|||
}
|
||||
ViewportHandler.setMetadataForDocument(this.browser.contentDocument, aMetadata);
|
||||
this.updateViewportSize();
|
||||
this.updateViewport(true);
|
||||
},
|
||||
|
||||
/** Update viewport when the metadata or the window size changes. */
|
||||
|
@ -2066,8 +2045,8 @@ Tab.prototype = {
|
|||
if (!browser)
|
||||
return;
|
||||
|
||||
let screenW = this._viewport.width;
|
||||
let screenH = this._viewport.height;
|
||||
let screenW = gScreenWidth;
|
||||
let screenH = gScreenHeight;
|
||||
let viewportW, viewportH;
|
||||
|
||||
let metadata = this.metadata;
|
||||
|
@ -2099,10 +2078,10 @@ Tab.prototype = {
|
|||
|
||||
// Make sure the viewport height is not shorter than the window when
|
||||
// the page is zoomed out to show its full width.
|
||||
let minScale = this.getPageZoomLevel(screenW);
|
||||
let minScale = this.getPageZoomLevel();
|
||||
viewportH = Math.max(viewportH, screenH / minScale);
|
||||
|
||||
let oldBrowserWidth = parseInt(this.browser.style.minWidth);
|
||||
let oldBrowserWidth = this.browserWidth;
|
||||
this.setBrowserSize(viewportW, viewportH);
|
||||
|
||||
// Avoid having the scroll position jump around after device rotation.
|
||||
|
@ -2116,8 +2095,7 @@ Tab.prototype = {
|
|||
if (viewportW == oldBrowserWidth)
|
||||
return;
|
||||
|
||||
let viewport = this.viewport;
|
||||
let newZoom = oldBrowserWidth * viewport.zoom / viewportW;
|
||||
let newZoom = oldBrowserWidth * this._zoom / viewportW;
|
||||
this.updateViewport(true, newZoom);
|
||||
},
|
||||
|
||||
|
@ -2126,8 +2104,8 @@ Tab.prototype = {
|
|||
if ("defaultZoom" in md && md.defaultZoom)
|
||||
return md.defaultZoom;
|
||||
|
||||
let browserWidth = parseInt(this.browser.style.minWidth);
|
||||
return gScreenWidth / browserWidth;
|
||||
dump("### getDefaultZoomLevel gScreenWidth=" + gScreenWidth);
|
||||
return gScreenWidth / this.browserWidth;
|
||||
},
|
||||
|
||||
getPageZoomLevel: function getPageZoomLevel() {
|
||||
|
@ -2136,14 +2114,16 @@ Tab.prototype = {
|
|||
if (!this.browser.contentDocument || !this.browser.contentDocument.body)
|
||||
return 1.0;
|
||||
|
||||
return this._viewport.width / this.browser.contentDocument.body.clientWidth;
|
||||
return gScreenWidth / this.browser.contentDocument.body.clientWidth;
|
||||
},
|
||||
|
||||
setBrowserSize: function(aWidth, aHeight) {
|
||||
// Using min width/height so as not to conflict with the fullscreen style rule.
|
||||
// See Bug #709813.
|
||||
this.browser.style.minWidth = aWidth + "px";
|
||||
this.browser.style.minHeight = aHeight + "px";
|
||||
this.browserWidth = aWidth;
|
||||
|
||||
if (!this.browser.contentWindow)
|
||||
return;
|
||||
let cwu = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
cwu.setCSSViewport(aWidth, aHeight);
|
||||
},
|
||||
|
||||
getRequestLoadContext: function(aRequest) {
|
||||
|
@ -2177,6 +2157,15 @@ Tab.prototype = {
|
|||
if (contentDocument == this.browser.contentDocument) {
|
||||
ViewportHandler.updateMetadata(this);
|
||||
this.documentIdForCurrentViewport = ViewportHandler.getIdForDocument(contentDocument);
|
||||
// FIXME: This is a workaround for the fact that we suppress draw events.
|
||||
// This means we need to retrigger a draw event here since we might
|
||||
// have suppressed a draw event before documentIdForCurrentViewport
|
||||
// got updated. The real fix is to get rid of suppressing draw events
|
||||
// based on the value of documentIdForCurrentViewport, which we
|
||||
// can do once the docshell and the browser element are aware
|
||||
// of the existence of <meta viewport>.
|
||||
sendMessageToJava({ gecko: { type: "Viewport:UpdateAndDraw" } });
|
||||
this.refreshDisplayPort();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2261,7 +2250,6 @@ var BrowserEventHandler = {
|
|||
if (element && !FormAssistant.handleClick(element)) {
|
||||
try {
|
||||
let data = JSON.parse(aData);
|
||||
[data.x, data.y] = ElementTouchHelper.toScreenCoords(element.ownerDocument.defaultView, data.x, data.y);
|
||||
|
||||
this._sendMouseEvent("mousemove", element, data.x, data.y);
|
||||
this._sendMouseEvent("mousedown", element, data.x, data.y);
|
||||
|
@ -2309,7 +2297,7 @@ var BrowserEventHandler = {
|
|||
let rect = {};
|
||||
let win = BrowserApp.selectedBrowser.contentWindow;
|
||||
|
||||
let zoom = BrowserApp.selectedTab._viewport.zoom;
|
||||
let zoom = BrowserApp.selectedTab._zoom;
|
||||
let element = ElementTouchHelper.anyElementFromPoint(win, data.x, data.y);
|
||||
if (!element) {
|
||||
this._zoomOut();
|
||||
|
@ -2326,7 +2314,7 @@ var BrowserEventHandler = {
|
|||
this._zoomedToElement = element;
|
||||
rect = ElementTouchHelper.getBoundingContentRect(element);
|
||||
|
||||
let zoom = BrowserApp.selectedTab.viewport.zoom;
|
||||
let zoom = BrowserApp.selectedTab._zoom;
|
||||
rect.x *= zoom;
|
||||
rect.y *= zoom;
|
||||
rect.w *= zoom;
|
||||
|
@ -2397,7 +2385,6 @@ var BrowserEventHandler = {
|
|||
|
||||
let window = aElement.ownerDocument.defaultView;
|
||||
try {
|
||||
[aX, aY] = ElementTouchHelper.toBrowserCoords(window, aX, aY);
|
||||
let cwu = window.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
aButton = aButton || 0;
|
||||
cwu.sendMouseEventToWindow(aName, Math.round(aX), Math.round(aY), aButton, 1, 0, true);
|
||||
|
@ -2504,46 +2491,7 @@ var BrowserEventHandler = {
|
|||
const kReferenceDpi = 240; // standard "pixel" size used in some preferences
|
||||
|
||||
const ElementTouchHelper = {
|
||||
toBrowserCoords: function(aWindow, aX, aY) {
|
||||
if (!aWindow)
|
||||
throw "Must provide a window";
|
||||
|
||||
let browser = BrowserApp.getBrowserForWindow(aWindow.top);
|
||||
if (!browser)
|
||||
throw "Unable to find a browser";
|
||||
|
||||
let tab = BrowserApp.getTabForBrowser(browser);
|
||||
if (!tab)
|
||||
throw "Unable to find a tab";
|
||||
|
||||
let viewport = tab.viewport;
|
||||
return [
|
||||
((aX - tab.viewportExcess.x) * viewport.zoom + viewport.offsetX),
|
||||
((aY - tab.viewportExcess.y) * viewport.zoom + viewport.offsetY)
|
||||
];
|
||||
},
|
||||
|
||||
toScreenCoords: function(aWindow, aX, aY) {
|
||||
if (!aWindow)
|
||||
throw "Must provide a window";
|
||||
|
||||
let browser = BrowserApp.getBrowserForWindow(aWindow.top);
|
||||
if (!browser)
|
||||
throw "Unable to find a browser";
|
||||
|
||||
let tab = BrowserApp.getTabForBrowser(browser);
|
||||
if (!tab)
|
||||
throw "Unable to find a tab";
|
||||
|
||||
let viewport = tab.viewport;
|
||||
return [
|
||||
(aX - viewport.offsetX)/viewport.zoom + tab.viewportExcess.x,
|
||||
(aY - viewport.offsetY)/viewport.zoom + tab.viewportExcess.y
|
||||
];
|
||||
},
|
||||
|
||||
anyElementFromPoint: function(aWindow, aX, aY) {
|
||||
[aX, aY] = this.toScreenCoords(aWindow, aX, aY);
|
||||
let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
let elem = cwu.elementFromPoint(aX, aY, false, true);
|
||||
|
||||
|
@ -2559,7 +2507,6 @@ const ElementTouchHelper = {
|
|||
},
|
||||
|
||||
elementFromPoint: function(aWindow, aX, aY) {
|
||||
[aX, aY] = this.toScreenCoords(aWindow, aX, aY);
|
||||
// browser's elementFromPoint expect browser-relative client coordinates.
|
||||
// subtract browser's scroll values to adjust
|
||||
let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
@ -2831,14 +2778,15 @@ var FormAssistant = {
|
|||
let suggestions = this._getAutocompleteSuggestions(currentElement.value, currentElement);
|
||||
|
||||
let rect = ElementTouchHelper.getBoundingContentRect(currentElement);
|
||||
let viewport = BrowserApp.selectedTab.viewport;
|
||||
let zoom = BrowserApp.selectedTab._zoom;
|
||||
let win = BrowserApp.selectedTab.browser.contentWindow;
|
||||
|
||||
sendMessageToJava({
|
||||
gecko: {
|
||||
type: "FormAssist:AutoComplete",
|
||||
suggestions: suggestions,
|
||||
rect: [rect.x - (viewport.x / viewport.zoom), rect.y - (viewport.y / viewport.zoom), rect.w, rect.h],
|
||||
zoom: viewport.zoom
|
||||
rect: [rect.x - (win.scrollX / zoom), rect.y - (win.scrollY / zoom), rect.w, rect.h],
|
||||
zoom: zoom
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3246,8 +3194,7 @@ var ViewportHandler = {
|
|||
},
|
||||
|
||||
onResize: function onResize() {
|
||||
for (let i = 0; i < BrowserApp.tabs.length; i++)
|
||||
BrowserApp.tabs[i].updateViewportSize();
|
||||
BrowserApp.selectedTab.updateViewportSize();
|
||||
},
|
||||
|
||||
clamp: function(num, min, max) {
|
||||
|
|
|
@ -233,6 +233,10 @@ pref("gfx.canvas.azure.enabled", true);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
pref("gfx.textures.poweroftwo.force-enabled", false);
|
||||
#endif
|
||||
|
||||
pref("accessibility.browsewithcaret", false);
|
||||
pref("accessibility.warn_on_browsewithcaret", true);
|
||||
|
||||
|
@ -3350,7 +3354,7 @@ pref("layers.acceleration.disabled", false);
|
|||
// Whether to force acceleration on, ignoring blacklists.
|
||||
pref("layers.acceleration.force-enabled", false);
|
||||
|
||||
pref("layers.acceleration.draw-fps", false);
|
||||
pref("layers.acceleration.draw-fps", true);
|
||||
|
||||
pref("layers.offmainthreadcomposition.enabled", false);
|
||||
|
||||
|
|
|
@ -286,7 +286,7 @@ SHELL_WRAPPER0(nativeInit)
|
|||
SHELL_WRAPPER1(notifyGeckoOfEvent, jobject)
|
||||
SHELL_WRAPPER0(processNextNativeEvent)
|
||||
SHELL_WRAPPER1(setSurfaceView, jobject)
|
||||
SHELL_WRAPPER1(setSoftwareLayerClient, jobject)
|
||||
SHELL_WRAPPER2(setLayerClient, jobject, jint)
|
||||
SHELL_WRAPPER0(onResume)
|
||||
SHELL_WRAPPER0(onLowMemory)
|
||||
SHELL_WRAPPER3(callObserver, jstring, jstring, jstring)
|
||||
|
@ -298,6 +298,9 @@ SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
|
|||
SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble)
|
||||
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong)
|
||||
SHELL_WRAPPER0(bindWidgetTexture)
|
||||
SHELL_WRAPPER0(scheduleComposite)
|
||||
SHELL_WRAPPER0(schedulePauseComposition)
|
||||
SHELL_WRAPPER0(scheduleResumeComposition)
|
||||
SHELL_WRAPPER3_WITH_RETURN(saveMessageInSentbox, jint, jstring, jstring, jlong)
|
||||
SHELL_WRAPPER6(notifySmsSent, jint, jstring, jstring, jlong, jint, jlong)
|
||||
SHELL_WRAPPER4(notifySmsDelivered, jint, jstring, jstring, jlong)
|
||||
|
@ -697,7 +700,7 @@ loadGeckoLibs(const char *apkName)
|
|||
GETFUNC(notifyGeckoOfEvent);
|
||||
GETFUNC(processNextNativeEvent);
|
||||
GETFUNC(setSurfaceView);
|
||||
GETFUNC(setSoftwareLayerClient);
|
||||
GETFUNC(setLayerClient);
|
||||
GETFUNC(onResume);
|
||||
GETFUNC(onLowMemory);
|
||||
GETFUNC(callObserver);
|
||||
|
@ -709,6 +712,9 @@ loadGeckoLibs(const char *apkName)
|
|||
GETFUNC(notifyBatteryChange);
|
||||
GETFUNC(notifySmsReceived);
|
||||
GETFUNC(bindWidgetTexture);
|
||||
GETFUNC(scheduleComposite);
|
||||
GETFUNC(schedulePauseComposition);
|
||||
GETFUNC(scheduleResumeComposition);
|
||||
GETFUNC(saveMessageInSentbox);
|
||||
GETFUNC(notifySmsSent);
|
||||
GETFUNC(notifySmsDelivered);
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "mozilla/Util.h"
|
||||
#include "mozilla/layers/CompositorChild.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
|
@ -186,6 +190,12 @@ AndroidBridge::Init(JNIEnv *jEnv,
|
|||
jEGLConfigImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLConfigImpl"));
|
||||
jEGLDisplayImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLDisplayImpl"));
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
jFlexSurfaceView = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/gfx/FlexibleGLSurfaceView"));
|
||||
|
||||
AndroidGLController::Init(jEnv);
|
||||
AndroidEGLObject::Init(jEnv);
|
||||
#endif
|
||||
InitAndroidJavaWrappers(jEnv);
|
||||
|
||||
// jEnv should NOT be cached here by anything -- the jEnv here
|
||||
|
@ -978,9 +988,11 @@ AndroidBridge::SetSurfaceView(jobject obj)
|
|||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SetSoftwareLayerClient(jobject obj)
|
||||
AndroidBridge::SetLayerClient(jobject obj)
|
||||
{
|
||||
mSoftwareLayerClient.Init(obj);
|
||||
AndroidGeckoLayerClient *client = new AndroidGeckoLayerClient();
|
||||
client->Init(obj);
|
||||
mLayerClient = client;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1000,12 +1012,11 @@ AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoS
|
|||
{
|
||||
ALOG_BRIDGE("AndroidBridge::CallEglCreateWindowSurface");
|
||||
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
// Called off the main thread by the compositor
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
if (!env)
|
||||
return NULL;
|
||||
|
||||
AutoLocalJNIFrame jniFrame(env);
|
||||
|
||||
/*
|
||||
* This is basically:
|
||||
*
|
||||
|
@ -1045,6 +1056,36 @@ AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoS
|
|||
return (void*) realSurface;
|
||||
}
|
||||
|
||||
static AndroidGLController sController;
|
||||
|
||||
void
|
||||
AndroidBridge::RegisterCompositor()
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::RegisterCompositor");
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
AutoLocalJNIFrame jniFrame(env, 3);
|
||||
|
||||
jmethodID registerCompositor = env->GetStaticMethodID(jFlexSurfaceView, "registerCxxCompositor", "()Lorg/mozilla/gecko/gfx/GLController;");
|
||||
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### registerCxxCompositor()");
|
||||
jobject glController = env->CallStaticObjectMethod(jFlexSurfaceView, registerCompositor);
|
||||
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### Acquire()");
|
||||
sController.Acquire(env, glController);
|
||||
sController.SetGLVersion(2);
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "Registered Compositor");
|
||||
}
|
||||
|
||||
EGLSurface
|
||||
AndroidBridge::ProvideEGLSurface()
|
||||
{
|
||||
sController.WaitForValidSurface();
|
||||
return sController.ProvideEGLSurface();
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidBridge::GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt)
|
||||
{
|
||||
|
@ -1806,6 +1847,64 @@ AndroidBridge::IsTablet()
|
|||
return env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SetCompositorParent(mozilla::layers::CompositorParent* aCompositorParent,
|
||||
::base::Thread* aCompositorThread)
|
||||
{
|
||||
mCompositorParent = aCompositorParent;
|
||||
mCompositorThread = aCompositorThread;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::ScheduleComposite()
|
||||
{
|
||||
if (mCompositorParent) {
|
||||
mCompositorParent->ScheduleRenderOnCompositorThread(*mCompositorThread);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SchedulePauseComposition()
|
||||
{
|
||||
if (mCompositorParent) {
|
||||
mCompositorParent->SchedulePauseOnCompositorThread(*mCompositorThread);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::ScheduleResumeComposition()
|
||||
{
|
||||
if (mCompositorParent) {
|
||||
mCompositorParent->ScheduleResumeOnCompositorThread(*mCompositorThread);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SetViewTransformGetter(AndroidViewTransformGetter& aViewTransformGetter)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### SetViewTransformGetter()");
|
||||
mViewTransformGetter = &aViewTransformGetter;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::GetViewTransform(nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### GetViewTransform()");
|
||||
if (mViewTransformGetter) {
|
||||
(*mViewTransformGetter)(aScrollOffset, aScaleX, aScaleY);
|
||||
}
|
||||
}
|
||||
|
||||
AndroidBridge::AndroidBridge()
|
||||
: mLayerClient(NULL)
|
||||
, mViewTransformGetter(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
AndroidBridge::~AndroidBridge()
|
||||
{
|
||||
}
|
||||
|
||||
/* Implementation file */
|
||||
NS_IMPL_ISUPPORTS1(nsAndroidBridge, nsIAndroidBridge)
|
||||
|
||||
|
|
|
@ -49,11 +49,13 @@
|
|||
#include "nsIObserver.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "AndroidFlexViewWrapper.h"
|
||||
#include "AndroidJavaWrappers.h"
|
||||
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIMIMEInfo.h"
|
||||
#include "nsColor.h"
|
||||
#include "BasicLayers.h"
|
||||
#include "gfxRect.h"
|
||||
|
||||
#include "nsIAndroidBridge.h"
|
||||
|
@ -71,6 +73,10 @@ extern "C" JNIEnv * GetJNIForThread();
|
|||
extern bool mozilla_AndroidBridge_SetMainThread(void *);
|
||||
extern jclass GetGeckoAppShellClass();
|
||||
|
||||
namespace base {
|
||||
class Thread;
|
||||
} // end namespace base
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace hal {
|
||||
|
@ -84,6 +90,10 @@ struct SmsFilterData;
|
|||
} // namespace sms
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
class CompositorParent;
|
||||
} // namespace layers
|
||||
|
||||
// The order and number of the members in this structure must correspond
|
||||
// to the attrsAppearance array in GeckoAppShell.getSystemColors()
|
||||
typedef struct AndroidSystemColors {
|
||||
|
@ -110,6 +120,11 @@ public:
|
|||
NOTIFY_IME_FOCUSCHANGE = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
LAYER_CLIENT_TYPE_NONE = 0,
|
||||
LAYER_CLIENT_TYPE_GL = 2 // AndroidGeckoGLLayerClient
|
||||
};
|
||||
|
||||
static AndroidBridge *ConstructBridge(JNIEnv *jEnv,
|
||||
jclass jGeckoAppShellClass);
|
||||
|
||||
|
@ -174,8 +189,8 @@ public:
|
|||
|
||||
void ScheduleRestart();
|
||||
|
||||
void SetSoftwareLayerClient(jobject jobj);
|
||||
AndroidGeckoSoftwareLayerClient &GetSoftwareLayerClient() { return mSoftwareLayerClient; }
|
||||
void SetLayerClient(jobject jobj);
|
||||
AndroidGeckoLayerClient &GetLayerClient() { return *mLayerClient; }
|
||||
|
||||
void SetSurfaceView(jobject jobj);
|
||||
AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
|
||||
|
@ -314,6 +329,10 @@ public:
|
|||
/* See GLHelpers.java as to why this is needed */
|
||||
void *CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView& surfaceView);
|
||||
|
||||
// Switch Java to composite with the Gecko Compositor thread
|
||||
void RegisterCompositor();
|
||||
EGLSurface ProvideEGLSurface();
|
||||
|
||||
bool GetStaticStringField(const char *classID, const char *field, nsAString &result);
|
||||
|
||||
bool GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt);
|
||||
|
@ -385,6 +404,14 @@ public:
|
|||
void EnableNetworkNotifications();
|
||||
void DisableNetworkNotifications();
|
||||
|
||||
void SetCompositorParent(mozilla::layers::CompositorParent* aCompositorParent,
|
||||
base::Thread* aCompositorThread);
|
||||
void ScheduleComposite();
|
||||
void SchedulePauseComposition();
|
||||
void ScheduleResumeComposition();
|
||||
void SetViewTransformGetter(AndroidViewTransformGetter& aViewTransformGetter);
|
||||
void GetViewTransform(nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY);
|
||||
|
||||
jobject CreateSurface();
|
||||
void DestroySurface(jobject surface);
|
||||
void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend);
|
||||
|
@ -402,12 +429,19 @@ protected:
|
|||
|
||||
// the GeckoSurfaceView
|
||||
AndroidGeckoSurfaceView mSurfaceView;
|
||||
AndroidGeckoSoftwareLayerClient mSoftwareLayerClient;
|
||||
|
||||
AndroidGeckoLayerClient *mLayerClient;
|
||||
|
||||
nsRefPtr<mozilla::layers::CompositorParent> mCompositorParent;
|
||||
base::Thread *mCompositorThread;
|
||||
AndroidViewTransformGetter *mViewTransformGetter;
|
||||
|
||||
// the GeckoAppShell java class
|
||||
jclass mGeckoAppShellClass;
|
||||
|
||||
AndroidBridge() { }
|
||||
AndroidBridge();
|
||||
~AndroidBridge();
|
||||
|
||||
bool Init(JNIEnv *jEnv, jclass jGeckoApp);
|
||||
|
||||
bool mOpenedGraphicsLibraries;
|
||||
|
@ -498,6 +532,10 @@ protected:
|
|||
jclass jEGLContextClass;
|
||||
jclass jEGL10Class;
|
||||
|
||||
jclass jFlexSurfaceView;
|
||||
|
||||
jmethodID jRegisterCompositorMethod;
|
||||
|
||||
// calls we've dlopened from libjnigraphics.so
|
||||
int (* AndroidBitmap_getInfo)(JNIEnv *env, jobject bitmap, void *info);
|
||||
int (* AndroidBitmap_lockPixels)(JNIEnv *env, jobject bitmap, void **buffer);
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "AndroidFlexViewWrapper.h"
|
||||
|
||||
|
||||
static AndroidGLController sController;
|
||||
|
||||
static const char *sEGLDisplayClassName = "com/google/android/gles_jni/EGLDisplayImpl";
|
||||
static const char *sEGLDisplayPointerFieldName = "mEGLDisplay";
|
||||
static jfieldID jEGLDisplayPointerField = 0;
|
||||
|
||||
static const char *sEGLConfigClassName = "com/google/android/gles_jni/EGLConfigImpl";
|
||||
static const char *sEGLConfigPointerFieldName = "mEGLConfig";
|
||||
static jfieldID jEGLConfigPointerField = 0;
|
||||
|
||||
static const char *sEGLContextClassName = "com/google/android/gles_jni/EGLContextImpl";
|
||||
static const char *sEGLContextPointerFieldName = "mEGLContext";
|
||||
static jfieldID jEGLContextPointerField = 0;
|
||||
|
||||
static const char *sEGLSurfaceClassName = "com/google/android/gles_jni/EGLSurfaceImpl";
|
||||
static const char *sEGLSurfacePointerFieldName = "mEGLSurface";
|
||||
static jfieldID jEGLSurfacePointerField = 0;
|
||||
|
||||
void AndroidEGLObject::Init(JNIEnv* aJEnv) {
|
||||
jclass jClass;
|
||||
jClass = reinterpret_cast<jclass>
|
||||
(aJEnv->NewGlobalRef(aJEnv->FindClass(sEGLDisplayClassName)));
|
||||
jEGLDisplayPointerField = aJEnv->GetFieldID(jClass, sEGLDisplayPointerFieldName, "I");
|
||||
jClass = reinterpret_cast<jclass>
|
||||
(aJEnv->NewGlobalRef(aJEnv->FindClass(sEGLConfigClassName)));
|
||||
jEGLConfigPointerField = aJEnv->GetFieldID(jClass, sEGLConfigPointerFieldName, "I");
|
||||
jClass = reinterpret_cast<jclass>
|
||||
(aJEnv->NewGlobalRef(aJEnv->FindClass(sEGLContextClassName)));
|
||||
jEGLContextPointerField = aJEnv->GetFieldID(jClass, sEGLContextPointerFieldName, "I");
|
||||
jClass = reinterpret_cast<jclass>
|
||||
(aJEnv->NewGlobalRef(aJEnv->FindClass(sEGLSurfaceClassName)));
|
||||
jEGLSurfacePointerField = aJEnv->GetFieldID(jClass, sEGLSurfacePointerFieldName, "I");
|
||||
}
|
||||
|
||||
jmethodID AndroidGLController::jSetGLVersionMethod = 0;
|
||||
jmethodID AndroidGLController::jInitGLContextMethod = 0;
|
||||
jmethodID AndroidGLController::jDisposeGLContextMethod = 0;
|
||||
jmethodID AndroidGLController::jGetEGLDisplayMethod = 0;
|
||||
jmethodID AndroidGLController::jGetEGLConfigMethod = 0;
|
||||
jmethodID AndroidGLController::jGetEGLContextMethod = 0;
|
||||
jmethodID AndroidGLController::jGetEGLSurfaceMethod = 0;
|
||||
jmethodID AndroidGLController::jHasSurfaceMethod = 0;
|
||||
jmethodID AndroidGLController::jSwapBuffersMethod = 0;
|
||||
jmethodID AndroidGLController::jCheckForLostContextMethod = 0;
|
||||
jmethodID AndroidGLController::jWaitForValidSurfaceMethod = 0;
|
||||
jmethodID AndroidGLController::jGetWidthMethod = 0;
|
||||
jmethodID AndroidGLController::jGetHeightMethod = 0;
|
||||
jmethodID AndroidGLController::jProvideEGLSurfaceMethod = 0;
|
||||
|
||||
void
|
||||
AndroidGLController::Init(JNIEnv *aJEnv)
|
||||
{
|
||||
const char *className = "org/mozilla/gecko/gfx/GLController";
|
||||
jclass jClass = reinterpret_cast<jclass>(aJEnv->NewGlobalRef(aJEnv->FindClass(className)));
|
||||
|
||||
jSetGLVersionMethod = aJEnv->GetMethodID(jClass, "setGLVersion", "(I)V");
|
||||
jInitGLContextMethod = aJEnv->GetMethodID(jClass, "initGLContext", "()V");
|
||||
jDisposeGLContextMethod = aJEnv->GetMethodID(jClass, "disposeGLContext", "()V");
|
||||
jGetEGLDisplayMethod = aJEnv->GetMethodID(jClass, "getEGLDisplay",
|
||||
"()Ljavax/microedition/khronos/egl/EGLDisplay;");
|
||||
jGetEGLConfigMethod = aJEnv->GetMethodID(jClass, "getEGLConfig",
|
||||
"()Ljavax/microedition/khronos/egl/EGLConfig;");
|
||||
jGetEGLContextMethod = aJEnv->GetMethodID(jClass, "getEGLContext",
|
||||
"()Ljavax/microedition/khronos/egl/EGLContext;");
|
||||
jGetEGLSurfaceMethod = aJEnv->GetMethodID(jClass, "getEGLSurface",
|
||||
"()Ljavax/microedition/khronos/egl/EGLSurface;");
|
||||
jProvideEGLSurfaceMethod = aJEnv->GetMethodID(jClass, "provideEGLSurface",
|
||||
"()Ljavax/microedition/khronos/egl/EGLSurface;");
|
||||
jHasSurfaceMethod = aJEnv->GetMethodID(jClass, "hasSurface", "()Z");
|
||||
jSwapBuffersMethod = aJEnv->GetMethodID(jClass, "swapBuffers", "()Z");
|
||||
jCheckForLostContextMethod = aJEnv->GetMethodID(jClass, "checkForLostContext", "()Z");
|
||||
jWaitForValidSurfaceMethod = aJEnv->GetMethodID(jClass, "waitForValidSurface", "()V");
|
||||
jGetWidthMethod = aJEnv->GetMethodID(jClass, "getWidth", "()I");
|
||||
jGetHeightMethod = aJEnv->GetMethodID(jClass, "getHeight", "()I");
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::Acquire(JNIEnv* aJEnv, jobject aJObj)
|
||||
{
|
||||
mJEnv = aJEnv;
|
||||
mJObj = aJEnv->NewGlobalRef(aJObj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::Acquire(JNIEnv* aJEnv)
|
||||
{
|
||||
mJEnv = aJEnv;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::Release()
|
||||
{
|
||||
if (mJObj) {
|
||||
mJEnv->DeleteGlobalRef(mJObj);
|
||||
mJObj = NULL;
|
||||
}
|
||||
|
||||
mJEnv = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::SetGLVersion(int aVersion)
|
||||
{
|
||||
mJEnv->CallVoidMethod(mJObj, jSetGLVersionMethod, aVersion);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::InitGLContext()
|
||||
{
|
||||
mJEnv->CallVoidMethod(mJObj, jInitGLContextMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::DisposeGLContext()
|
||||
{
|
||||
mJEnv->CallVoidMethod(mJObj, jDisposeGLContextMethod);
|
||||
}
|
||||
|
||||
EGLDisplay
|
||||
AndroidGLController::GetEGLDisplay()
|
||||
{
|
||||
jobject jObj = mJEnv->CallObjectMethod(mJObj, jGetEGLDisplayMethod);
|
||||
return reinterpret_cast<EGLDisplay>(mJEnv->GetIntField(jObj, jEGLDisplayPointerField));
|
||||
}
|
||||
|
||||
EGLConfig
|
||||
AndroidGLController::GetEGLConfig()
|
||||
{
|
||||
jobject jObj = mJEnv->CallObjectMethod(mJObj, jGetEGLConfigMethod);
|
||||
return reinterpret_cast<EGLConfig>(mJEnv->GetIntField(jObj, jEGLConfigPointerField));
|
||||
}
|
||||
|
||||
EGLContext
|
||||
AndroidGLController::GetEGLContext()
|
||||
{
|
||||
jobject jObj = mJEnv->CallObjectMethod(mJObj, jGetEGLContextMethod);
|
||||
return reinterpret_cast<EGLContext>(mJEnv->GetIntField(jObj, jEGLContextPointerField));
|
||||
}
|
||||
|
||||
EGLSurface
|
||||
AndroidGLController::GetEGLSurface()
|
||||
{
|
||||
jobject jObj = mJEnv->CallObjectMethod(mJObj, jGetEGLSurfaceMethod);
|
||||
return reinterpret_cast<EGLSurface>(mJEnv->GetIntField(jObj, jEGLSurfacePointerField));
|
||||
}
|
||||
|
||||
EGLSurface
|
||||
AndroidGLController::ProvideEGLSurface()
|
||||
{
|
||||
jobject jObj = mJEnv->CallObjectMethod(mJObj, jProvideEGLSurfaceMethod);
|
||||
return reinterpret_cast<EGLSurface>(mJEnv->GetIntField(jObj, jEGLSurfacePointerField));
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidGLController::HasSurface()
|
||||
{
|
||||
return mJEnv->CallBooleanMethod(mJObj, jHasSurfaceMethod);
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidGLController::SwapBuffers()
|
||||
{
|
||||
return mJEnv->CallBooleanMethod(mJObj, jSwapBuffersMethod);
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidGLController::CheckForLostContext()
|
||||
{
|
||||
return mJEnv->CallBooleanMethod(mJObj, jCheckForLostContextMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGLController::WaitForValidSurface()
|
||||
{
|
||||
mJEnv->CallVoidMethod(mJObj, jWaitForValidSurfaceMethod);
|
||||
}
|
||||
|
||||
int
|
||||
AndroidGLController::GetWidth()
|
||||
{
|
||||
return mJEnv->CallIntMethod(mJObj, jGetWidthMethod);
|
||||
}
|
||||
|
||||
int
|
||||
AndroidGLController::GetHeight()
|
||||
{
|
||||
return mJEnv->CallIntMethod(mJObj, jGetHeightMethod);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011-2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef AndroidFlexViewWrapper_h__
|
||||
#define AndroidFlexViewWrapper_h__
|
||||
|
||||
#include <jni.h>
|
||||
//#include <GLES/gl.h>
|
||||
//#include <GLES/glext.h>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include <android/log.h>
|
||||
|
||||
typedef void *NativeType;
|
||||
|
||||
class AndroidEGLObject {
|
||||
public:
|
||||
AndroidEGLObject(JNIEnv* aJEnv, jobject aJObj)
|
||||
: mPtr(reinterpret_cast<NativeType>(aJEnv->GetIntField(aJObj, jPointerField))) {}
|
||||
|
||||
static void Init(JNIEnv* aJEnv);
|
||||
|
||||
NativeType const& operator*() const {
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
static jfieldID jPointerField;
|
||||
static const char* sClassName;
|
||||
static const char* sPointerFieldName;
|
||||
|
||||
const NativeType mPtr;
|
||||
};
|
||||
|
||||
typedef void *EGLConfig;
|
||||
typedef void *EGLContext;
|
||||
typedef void *EGLDisplay;
|
||||
typedef void *EGLSurface;
|
||||
|
||||
class AndroidGLController {
|
||||
public:
|
||||
static void Init(JNIEnv* aJEnv);
|
||||
|
||||
void Acquire(JNIEnv *aJEnv, jobject aJObj);
|
||||
void Acquire(JNIEnv *aJEnv);
|
||||
void Release();
|
||||
|
||||
void SetGLVersion(int aVersion);
|
||||
void InitGLContext();
|
||||
void DisposeGLContext();
|
||||
EGLDisplay GetEGLDisplay();
|
||||
EGLConfig GetEGLConfig();
|
||||
EGLContext GetEGLContext();
|
||||
EGLSurface GetEGLSurface();
|
||||
EGLSurface ProvideEGLSurface();
|
||||
bool HasSurface();
|
||||
bool SwapBuffers();
|
||||
bool CheckForLostContext();
|
||||
void WaitForValidSurface();
|
||||
int GetWidth();
|
||||
int GetHeight();
|
||||
|
||||
private:
|
||||
static jmethodID jSetGLVersionMethod;
|
||||
static jmethodID jInitGLContextMethod;
|
||||
static jmethodID jDisposeGLContextMethod;
|
||||
static jmethodID jGetEGLDisplayMethod;
|
||||
static jmethodID jGetEGLConfigMethod;
|
||||
static jmethodID jGetEGLContextMethod;
|
||||
static jmethodID jGetEGLSurfaceMethod;
|
||||
static jmethodID jHasSurfaceMethod;
|
||||
static jmethodID jSwapBuffersMethod;
|
||||
static jmethodID jCheckForLostContextMethod;
|
||||
static jmethodID jWaitForValidSurfaceMethod;
|
||||
static jmethodID jGetWidthMethod;
|
||||
static jmethodID jGetHeightMethod;
|
||||
static jmethodID jProvideEGLSurfaceMethod;
|
||||
|
||||
JNIEnv *mJEnv;
|
||||
jobject mJObj;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -47,11 +47,6 @@
|
|||
#define EGL_NATIVE_BUFFER_ANDROID 0x3140
|
||||
#define EGL_IMAGE_PRESERVED_KHR 0x30D2
|
||||
|
||||
typedef void* EGLContext;
|
||||
typedef void* EGLImageKHR;
|
||||
typedef void* EGLClientBuffer;
|
||||
typedef void* EGLDisplay;
|
||||
|
||||
typedef PRUint32 EGLenum;
|
||||
typedef PRInt32 EGLint;
|
||||
typedef PRUint32 EGLBoolean;
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
#include "gfxASurface.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
typedef void* EGLImageKHR;
|
||||
typedef void* EGLClientBuffer;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ extern "C" {
|
|||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *, jclass, jobject event);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject sv);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject sv);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *, jclass);
|
||||
|
@ -104,8 +104,11 @@ extern "C" {
|
|||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyReadingMessageListFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv* jenv, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_schedulePauseComposition(JNIEnv* jenv, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv* jenv, jclass);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -142,9 +145,9 @@ Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobjec
|
|||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject obj)
|
||||
Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject obj)
|
||||
{
|
||||
AndroidBridge::Bridge()->SetSoftwareLayerClient(jenv->NewGlobalRef(obj));
|
||||
AndroidBridge::Bridge()->SetLayerClient(jenv->NewGlobalRef(obj));
|
||||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
|
@ -889,9 +892,24 @@ Java_org_mozilla_gecko_GeckoAppShell_notifyReadingMessageListFailed(JNIEnv* jenv
|
|||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass)
|
||||
Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv*, jclass)
|
||||
{
|
||||
nsWindow::BindToTexture();
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### scheduleComposite()");
|
||||
AndroidBridge::Bridge()->ScheduleComposite();
|
||||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoAppShell_schedulePauseComposition(JNIEnv*, jclass)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### schedulePauseComposition()");
|
||||
AndroidBridge::Bridge()->SchedulePauseComposition();
|
||||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv*, jclass)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### scheduleResumeComposition()");
|
||||
AndroidBridge::Bridge()->ScheduleResumeComposition();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -76,6 +76,7 @@ jfieldID AndroidGeckoEvent::jLocationField = 0;
|
|||
jfieldID AndroidGeckoEvent::jAddressField = 0;
|
||||
jfieldID AndroidGeckoEvent::jBandwidthField = 0;
|
||||
jfieldID AndroidGeckoEvent::jCanBeMeteredField = 0;
|
||||
jmethodID AndroidGeckoEvent::jDoCallbackMethod = 0;
|
||||
|
||||
jclass AndroidPoint::jPointClass = 0;
|
||||
jfieldID AndroidPoint::jXField = 0;
|
||||
|
@ -109,11 +110,25 @@ jmethodID AndroidAddress::jGetSubLocalityMethod;
|
|||
jmethodID AndroidAddress::jGetSubThoroughfareMethod;
|
||||
jmethodID AndroidAddress::jGetThoroughfareMethod;
|
||||
|
||||
jclass AndroidGeckoSoftwareLayerClient::jGeckoSoftwareLayerClientClass = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jLockBufferMethod = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jUnlockBufferMethod = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jBeginDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jEndDrawingMethod = 0;
|
||||
jclass AndroidGeckoLayerClient::jGeckoLayerClientClass = 0;
|
||||
jmethodID AndroidGeckoLayerClient::jBeginDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoLayerClient::jEndDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoLayerClient::jGetViewTransformMethod = 0;
|
||||
jmethodID AndroidGeckoLayerClient::jCreateFrameMethod = 0;
|
||||
jmethodID AndroidGeckoLayerClient::jActivateProgramMethod = 0;
|
||||
jmethodID AndroidGeckoLayerClient::jDeactivateProgramMethod = 0;
|
||||
|
||||
jclass AndroidLayerRendererFrame::jLayerRendererFrameClass = 0;
|
||||
jmethodID AndroidLayerRendererFrame::jBeginDrawingMethod = 0;
|
||||
jmethodID AndroidLayerRendererFrame::jDrawBackgroundMethod = 0;
|
||||
jmethodID AndroidLayerRendererFrame::jDrawForegroundMethod = 0;
|
||||
jmethodID AndroidLayerRendererFrame::jEndDrawingMethod = 0;
|
||||
|
||||
jclass AndroidViewTransform::jViewTransformClass = 0;
|
||||
jfieldID AndroidViewTransform::jXField = 0;
|
||||
jfieldID AndroidViewTransform::jYField = 0;
|
||||
jfieldID AndroidViewTransform::jScaleField = 0;
|
||||
|
||||
jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jBeginDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jEndDrawingMethod = 0;
|
||||
|
@ -144,7 +159,9 @@ mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
|
|||
AndroidLocation::InitLocationClass(jEnv);
|
||||
AndroidAddress::InitAddressClass(jEnv);
|
||||
AndroidRect::InitRectClass(jEnv);
|
||||
AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(jEnv);
|
||||
AndroidGeckoLayerClient::InitGeckoLayerClientClass(jEnv);
|
||||
AndroidLayerRendererFrame::InitLayerRendererFrameClass(jEnv);
|
||||
AndroidViewTransform::InitViewTransformClass(jEnv);
|
||||
AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(jEnv);
|
||||
}
|
||||
|
||||
|
@ -189,6 +206,8 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
|
|||
jAddressField = getField("mAddress", "Landroid/location/Address;");
|
||||
jBandwidthField = getField("mBandwidth", "D");
|
||||
jCanBeMeteredField = getField("mCanBeMetered", "Z");
|
||||
|
||||
jDoCallbackMethod = getMethod("doCallback", "(Ljava/lang/String;)V");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -321,18 +340,49 @@ AndroidRect::InitRectClass(JNIEnv *jEnv)
|
|||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv)
|
||||
AndroidGeckoLayerClient::InitGeckoLayerClientClass(JNIEnv *jEnv)
|
||||
{
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
initInit();
|
||||
|
||||
jGeckoSoftwareLayerClientClass =
|
||||
getClassGlobalRef("org/mozilla/gecko/gfx/GeckoSoftwareLayerClient");
|
||||
jGeckoLayerClientClass = getClassGlobalRef("org/mozilla/gecko/gfx/GeckoLayerClient");
|
||||
|
||||
jLockBufferMethod = getMethod("lockBuffer", "()Ljava/nio/ByteBuffer;");
|
||||
jUnlockBufferMethod = getMethod("unlockBuffer", "()V");
|
||||
jBeginDrawingMethod = getMethod("beginDrawing", "(IIIILjava/lang/String;Z)Landroid/graphics/Rect;");
|
||||
jEndDrawingMethod = getMethod("endDrawing", "(IIII)V");
|
||||
jBeginDrawingMethod = getMethod("beginDrawing", "(IILjava/lang/String;)Landroid/graphics/Rect;");
|
||||
jEndDrawingMethod = getMethod("endDrawing", "()V");
|
||||
jGetViewTransformMethod = getMethod("getViewTransform",
|
||||
"()Lorg/mozilla/gecko/gfx/ViewTransform;");
|
||||
jCreateFrameMethod = getMethod("createFrame", "()Lorg/mozilla/gecko/gfx/LayerRenderer$Frame;");
|
||||
jActivateProgramMethod = getMethod("activateProgram", "()V");
|
||||
jDeactivateProgramMethod = getMethod("deactivateProgram", "()V");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::InitLayerRendererFrameClass(JNIEnv *jEnv)
|
||||
{
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
initInit();
|
||||
|
||||
jLayerRendererFrameClass = getClassGlobalRef("org/mozilla/gecko/gfx/LayerRenderer$Frame");
|
||||
|
||||
jBeginDrawingMethod = getMethod("beginDrawing", "()V");
|
||||
jDrawBackgroundMethod = getMethod("drawBackground", "()V");
|
||||
jDrawForegroundMethod = getMethod("drawForeground", "()V");
|
||||
jEndDrawingMethod = getMethod("endDrawing", "()V");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
AndroidViewTransform::InitViewTransformClass(JNIEnv *jEnv)
|
||||
{
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
initInit();
|
||||
|
||||
jViewTransformClass = getClassGlobalRef("org/mozilla/gecko/gfx/ViewTransform");
|
||||
|
||||
jXField = getField("x", "F");
|
||||
jYField = getField("y", "F");
|
||||
jScaleField = getField("scale", "F");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -434,6 +484,16 @@ AndroidGeckoEvent::Init(int aType, nsIntRect const& aRect)
|
|||
mRect = aRect;
|
||||
}
|
||||
|
||||
AndroidGeckoEvent::~AndroidGeckoEvent() {
|
||||
if (!wrapped_obj)
|
||||
return;
|
||||
|
||||
JNIEnv *jenv = GetJNIForThread();
|
||||
if (!jenv)
|
||||
return;
|
||||
jenv->DeleteGlobalRef(wrapped_obj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
||||
{
|
||||
|
@ -444,12 +504,14 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
|||
if (!jobj)
|
||||
return;
|
||||
|
||||
jenv->NewGlobalRef(jobj);
|
||||
|
||||
mAction = jenv->GetIntField(jobj, jActionField);
|
||||
mType = jenv->GetIntField(jobj, jTypeField);
|
||||
|
||||
switch (mType) {
|
||||
case SIZE_CHANGED:
|
||||
ReadPointArray(mPoints, jenv, jPoints, 3);
|
||||
ReadPointArray(mPoints, jenv, jPoints, 2);
|
||||
break;
|
||||
|
||||
case KEY_EVENT:
|
||||
|
@ -605,10 +667,50 @@ AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
|
|||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::Init(jobject jobj)
|
||||
AndroidGeckoEvent::DoCallback(const nsAString& data) {
|
||||
JNIEnv* env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return;
|
||||
jstring jData = env->NewString(nsPromiseFlatString(data).get(), data.Length());
|
||||
env->CallVoidMethod(wrapped_obj, jDoCallbackMethod, jData);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoLayerClient::Init(jobject jobj)
|
||||
{
|
||||
NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
|
||||
wrapped_obj = jobj;
|
||||
|
||||
// Register the view transform getter.
|
||||
AndroidBridge::Bridge()->SetViewTransformGetter(mViewTransformGetter);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::Init(jobject jobj)
|
||||
{
|
||||
if (!isNull()) {
|
||||
Dispose();
|
||||
}
|
||||
|
||||
wrapped_obj = GetJNIForThread()->NewGlobalRef(jobj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::Dispose()
|
||||
{
|
||||
if (isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GetJNIForThread()->DeleteGlobalRef(wrapped_obj);
|
||||
wrapped_obj = 0;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidViewTransform::Init(jobject jobj)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
|
||||
wrapped_obj = jobj;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -661,51 +763,10 @@ AndroidGeckoSurfaceView::Draw2D(jobject buffer, int stride)
|
|||
env->CallVoidMethod(wrapped_obj, jDraw2DBufferMethod, buffer, stride);
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSoftwareLayerClient::LockBuffer()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "LockBuffer() called on null software layer client!");
|
||||
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return nsnull;
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame(env, 1);
|
||||
return env->CallObjectMethod(wrapped_obj, jLockBufferMethod);
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
AndroidGeckoSoftwareLayerClient::LockBufferBits()
|
||||
{
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return nsnull;
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame(env, 1);
|
||||
jobject bufferObject = LockBuffer();
|
||||
|
||||
if (bufferObject != nsnull)
|
||||
return reinterpret_cast<unsigned char *>(env->GetDirectBufferAddress(bufferObject));
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::UnlockBuffer()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "UnlockBuffer() called on null software layer client!");
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame(env, 1);
|
||||
env->CallVoidMethod(wrapped_obj, jUnlockBufferMethod);
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, nsIntRect &aDirtyRect, const nsAString &aMetadata, bool aHasDirectTexture)
|
||||
AndroidGeckoLayerClient::BeginDrawing(int aWidth, int aHeight, nsIntRect &aDirtyRect, const nsAString &aMetadata)
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "BeginDrawing() called on null software layer client!");
|
||||
NS_ASSERTION(!isNull(), "BeginDrawing() called on null layer client!");
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return false;
|
||||
|
@ -714,8 +775,8 @@ AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTile
|
|||
jstring jMetadata = env->NewString(nsPromiseFlatString(aMetadata).get(), aMetadata.Length());
|
||||
|
||||
jobject rectObject = env->CallObjectMethod(wrapped_obj, jBeginDrawingMethod,
|
||||
aWidth, aHeight, aTileWidth, aTileHeight,
|
||||
jMetadata, aHasDirectTexture);
|
||||
aWidth, aHeight,
|
||||
jMetadata);
|
||||
|
||||
if (rectObject == nsnull)
|
||||
return false;
|
||||
|
@ -729,15 +790,15 @@ AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTile
|
|||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::EndDrawing(const nsIntRect &aRect)
|
||||
AndroidGeckoLayerClient::EndDrawing()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "EndDrawing() called on null software layer client!");
|
||||
NS_ASSERTION(!isNull(), "EndDrawing() called on null layer client!");
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame(env, 1);
|
||||
return env->CallVoidMethod(wrapped_obj, jEndDrawingMethod, aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
return env->CallVoidMethod(wrapped_obj, jEndDrawingMethod);
|
||||
}
|
||||
|
||||
jobject
|
||||
|
@ -773,11 +834,147 @@ AndroidGeckoSurfaceView::GetSurface()
|
|||
jobject
|
||||
AndroidGeckoSurfaceView::GetSurfaceHolder()
|
||||
{
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (!env)
|
||||
return nsnull;
|
||||
return GetJNIForThread()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
|
||||
}
|
||||
|
||||
return env->CallObjectMethod(wrapped_obj, jGetHolderMethod);
|
||||
void
|
||||
AndroidGeckoLayerClient::GetViewTransform(AndroidViewTransform& aViewTransform)
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at GetViewTransform()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
jobject viewTransformJObj = env->CallObjectMethod(wrapped_obj, jGetViewTransformMethod);
|
||||
NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!");
|
||||
aViewTransform.Init(viewTransformJObj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoLayerClient::CreateFrame(AndroidLayerRendererFrame& aFrame)
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at CreateFrame()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
jobject frameJObj = env->CallObjectMethod(wrapped_obj, jCreateFrameMethod);
|
||||
NS_ABORT_IF_FALSE(frameJObj, "No frame object!");
|
||||
aFrame.Init(frameJObj);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoLayerClient::ActivateProgram()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at ActivateProgram()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(wrapped_obj, jActivateProgramMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoLayerClient::DeactivateProgram()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at DeactivateProgram()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(wrapped_obj, jDeactivateProgramMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::BeginDrawing()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at BeginDrawing()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(wrapped_obj, jBeginDrawingMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::DrawBackground()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at DrawBackground()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(wrapped_obj, jDrawBackgroundMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::DrawForeground()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at DrawForeground()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(wrapped_obj, jDrawForegroundMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLayerRendererFrame::EndDrawing()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
NS_ABORT_IF_FALSE(env, "No JNI environment at EndDrawing()!");
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(wrapped_obj, jEndDrawingMethod);
|
||||
}
|
||||
|
||||
float
|
||||
AndroidViewTransform::GetX()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
if (!env)
|
||||
return 0.0f;
|
||||
return env->GetFloatField(wrapped_obj, jXField);
|
||||
}
|
||||
|
||||
float
|
||||
AndroidViewTransform::GetY()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
if (!env)
|
||||
return 0.0f;
|
||||
return env->GetFloatField(wrapped_obj, jYField);
|
||||
}
|
||||
|
||||
float
|
||||
AndroidViewTransform::GetScale()
|
||||
{
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
if (!env)
|
||||
return 0.0f;
|
||||
return env->GetFloatField(wrapped_obj, jScaleField);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoLayerClientViewTransformGetter::operator()(nsIntPoint& aScrollOffset, float& aScaleX,
|
||||
float& aScaleY)
|
||||
{
|
||||
AndroidViewTransform viewTransform;
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame jniFrame(GetJNIForThread());
|
||||
mLayerClient.GetViewTransform(viewTransform);
|
||||
|
||||
aScrollOffset = nsIntPoint(viewTransform.GetX(), viewTransform.GetY());
|
||||
aScaleX = aScaleY = viewTransform.GetScale();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
class AndroidGeckoLayerClient;
|
||||
|
||||
void InitAndroidJavaWrappers(JNIEnv *jEnv);
|
||||
|
||||
/*
|
||||
|
@ -151,31 +153,95 @@ protected:
|
|||
static jfieldID jTopField;
|
||||
};
|
||||
|
||||
class AndroidGeckoSoftwareLayerClient : public WrappedJavaObject {
|
||||
|
||||
/** A callback that retrieves the view transform. */
|
||||
class AndroidViewTransformGetter
|
||||
{
|
||||
public:
|
||||
static void InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv);
|
||||
|
||||
void Init(jobject jobj);
|
||||
|
||||
AndroidGeckoSoftwareLayerClient() {}
|
||||
AndroidGeckoSoftwareLayerClient(jobject jobj) { Init(jobj); }
|
||||
|
||||
jobject LockBuffer();
|
||||
unsigned char *LockBufferBits();
|
||||
void UnlockBuffer();
|
||||
bool BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, nsIntRect &aDirtyRect, const nsAString &aMetadata, bool aHasDirectTexture);
|
||||
void EndDrawing(const nsIntRect &aRect);
|
||||
|
||||
private:
|
||||
static jclass jGeckoSoftwareLayerClientClass;
|
||||
static jmethodID jLockBufferMethod;
|
||||
static jmethodID jUnlockBufferMethod;
|
||||
|
||||
protected:
|
||||
static jmethodID jBeginDrawingMethod;
|
||||
static jmethodID jEndDrawingMethod;
|
||||
virtual void operator()(nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) = 0;
|
||||
};
|
||||
|
||||
class AndroidGeckoLayerClientViewTransformGetter : public AndroidViewTransformGetter {
|
||||
public:
|
||||
AndroidGeckoLayerClientViewTransformGetter(AndroidGeckoLayerClient& aLayerClient)
|
||||
: mLayerClient(aLayerClient) {}
|
||||
|
||||
virtual void operator()(nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY);
|
||||
|
||||
private:
|
||||
AndroidGeckoLayerClient& mLayerClient;
|
||||
};
|
||||
|
||||
class AndroidViewTransform : public WrappedJavaObject {
|
||||
public:
|
||||
static void InitViewTransformClass(JNIEnv *jEnv);
|
||||
|
||||
void Init(jobject jobj);
|
||||
|
||||
AndroidViewTransform() {}
|
||||
AndroidViewTransform(jobject jobj) { Init(jobj); }
|
||||
|
||||
float GetX();
|
||||
float GetY();
|
||||
float GetScale();
|
||||
|
||||
private:
|
||||
static jclass jViewTransformClass;
|
||||
static jfieldID jXField;
|
||||
static jfieldID jYField;
|
||||
static jfieldID jScaleField;
|
||||
};
|
||||
|
||||
class AndroidLayerRendererFrame : public WrappedJavaObject {
|
||||
public:
|
||||
static void InitLayerRendererFrameClass(JNIEnv *jEnv);
|
||||
|
||||
void Init(jobject jobj);
|
||||
void Dispose();
|
||||
|
||||
void BeginDrawing();
|
||||
void DrawBackground();
|
||||
void DrawForeground();
|
||||
void EndDrawing();
|
||||
|
||||
private:
|
||||
static jclass jLayerRendererFrameClass;
|
||||
static jmethodID jBeginDrawingMethod;
|
||||
static jmethodID jDrawBackgroundMethod;
|
||||
static jmethodID jDrawForegroundMethod;
|
||||
static jmethodID jEndDrawingMethod;
|
||||
};
|
||||
|
||||
class AndroidGeckoLayerClient : public WrappedJavaObject {
|
||||
public:
|
||||
static void InitGeckoLayerClientClass(JNIEnv *jEnv);
|
||||
|
||||
void Init(jobject jobj);
|
||||
|
||||
AndroidGeckoLayerClient()
|
||||
: mViewTransformGetter(*this) {}
|
||||
|
||||
AndroidGeckoLayerClient(jobject jobj)
|
||||
: mViewTransformGetter(*this) { Init(jobj); }
|
||||
|
||||
bool BeginDrawing(int aWidth, int aHeight, nsIntRect &aDirtyRect, const nsAString &aMetadata);
|
||||
void EndDrawing();
|
||||
void GetViewTransform(AndroidViewTransform& aViewTransform);
|
||||
void CreateFrame(AndroidLayerRendererFrame& aFrame);
|
||||
void ActivateProgram();
|
||||
void DeactivateProgram();
|
||||
|
||||
protected:
|
||||
static jclass jGeckoLayerClientClass;
|
||||
static jmethodID jBeginDrawingMethod;
|
||||
static jmethodID jEndDrawingMethod;
|
||||
static jmethodID jGetViewTransformMethod;
|
||||
static jmethodID jCreateFrameMethod;
|
||||
static jmethodID jActivateProgramMethod;
|
||||
static jmethodID jDeactivateProgramMethod;
|
||||
|
||||
AndroidGeckoLayerClientViewTransformGetter mViewTransformGetter;
|
||||
};
|
||||
|
||||
class AndroidGeckoSurfaceView : public WrappedJavaObject
|
||||
{
|
||||
|
@ -425,6 +491,8 @@ public:
|
|||
Init(aResizeEvent);
|
||||
}
|
||||
|
||||
~AndroidGeckoEvent();
|
||||
|
||||
void Init(JNIEnv *jenv, jobject jobj);
|
||||
void Init(int aType);
|
||||
void Init(int x1, int y1, int x2, int y2);
|
||||
|
@ -464,6 +532,7 @@ public:
|
|||
nsGeoPositionAddress* GeoAddress() { return mGeoAddress; }
|
||||
double Bandwidth() { return mBandwidth; }
|
||||
bool CanBeMetered() { return mCanBeMetered; }
|
||||
void DoCallback(const nsAString& data);
|
||||
|
||||
protected:
|
||||
int mAction;
|
||||
|
@ -543,6 +612,8 @@ protected:
|
|||
|
||||
static jfieldID jBandwidthField;
|
||||
static jfieldID jCanBeMeteredField;
|
||||
|
||||
static jmethodID jDoCallbackMethod;
|
||||
|
||||
public:
|
||||
enum {
|
||||
|
|
|
@ -62,6 +62,7 @@ CPPSRCS = \
|
|||
AndroidJavaWrappers.cpp \
|
||||
AndroidBridge.cpp \
|
||||
AndroidDirectTexture.cpp \
|
||||
AndroidFlexViewWrapper.cpp \
|
||||
AndroidGraphicBuffer.cpp \
|
||||
AndroidJNI.cpp \
|
||||
AndroidMediaLayer.cpp \
|
||||
|
@ -91,7 +92,7 @@ XPIDLSRCS = \
|
|||
|
||||
SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a
|
||||
|
||||
EXPORTS = AndroidBridge.h AndroidJavaWrappers.h
|
||||
EXPORTS = AndroidBridge.h AndroidJavaWrappers.h AndroidFlexViewWrapper.h
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
|
|
@ -447,12 +447,13 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
|||
nsCOMPtr<nsIDOMWindow> domWindow;
|
||||
mBrowserApp->GetWindowForTab(curEvent->MetaState(), getter_AddRefs(domWindow));
|
||||
nsTArray<nsIntPoint> points = curEvent->Points();
|
||||
NS_ASSERTION(points.Length() != 2, "Screenshot event does not have enough coordinates");
|
||||
NS_ASSERTION(points.Length() == 2, "Screenshot event does not have enough coordinates");
|
||||
if (domWindow)
|
||||
bridge->TakeScreenshot(domWindow, 0, 0, points[0].x, points[0].y, points[1].x, points[1].y, curEvent->MetaState());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case AndroidGeckoEvent::VIEWPORT:
|
||||
case AndroidGeckoEvent::BROADCAST: {
|
||||
|
||||
|
|
|
@ -88,7 +88,6 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
|
|||
|
||||
// The dimensions of the current android view
|
||||
static gfxIntSize gAndroidBounds = gfxIntSize(0, 0);
|
||||
static gfxIntSize gAndroidTileSize = gfxIntSize(0, 0);
|
||||
static gfxIntSize gAndroidScreenBounds;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
|
@ -98,12 +97,6 @@ bool nsWindow::sAccessibilityEnabled = false;
|
|||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "AndroidDirectTexture.h"
|
||||
|
||||
static AndroidDirectTexture* sDirectTexture = new AndroidDirectTexture(2048, 2048,
|
||||
AndroidGraphicBuffer::UsageSoftwareWrite | AndroidGraphicBuffer::UsageTexture,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -218,6 +211,9 @@ nsWindow::~nsWindow()
|
|||
mRootAccessible = nsnull;
|
||||
#endif
|
||||
ALOG("nsWindow %p destructor", (void*)this);
|
||||
|
||||
AndroidBridge::Bridge()->SetCompositorParent(NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -766,16 +762,31 @@ nsWindow::GetLayerManager(PLayersChild*, LayersBackend, LayerManagerPersistence,
|
|||
return mLayerManager;
|
||||
}
|
||||
|
||||
printf_stderr("nsWindow::GetLayerManager\n");
|
||||
|
||||
nsWindow *topWindow = TopWindow();
|
||||
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### nsWindow::GetLayerManager this=%p "
|
||||
"topWindow=%p", this, topWindow);
|
||||
|
||||
if (!topWindow) {
|
||||
printf_stderr(" -- no topwindow\n");
|
||||
mLayerManager = CreateBasicLayerManager();
|
||||
return mLayerManager;
|
||||
}
|
||||
|
||||
bool useCompositor =
|
||||
Preferences::GetBool("layers.offmainthreadcomposition.enabled", false);
|
||||
|
||||
if (useCompositor) {
|
||||
CreateCompositor();
|
||||
if (mLayerManager) {
|
||||
AndroidBridge::Bridge()->SetCompositorParent(mCompositorParent, mCompositorThread);
|
||||
return mLayerManager;
|
||||
}
|
||||
|
||||
// If we get here, then off main thread compositing failed to initialize.
|
||||
sFailedToCreateGLContext = true;
|
||||
}
|
||||
|
||||
mUseAcceleratedRendering = GetShouldAccelerate();
|
||||
|
||||
if (!mUseAcceleratedRendering ||
|
||||
|
@ -786,102 +797,32 @@ nsWindow::GetLayerManager(PLayersChild*, LayersBackend, LayerManagerPersistence,
|
|||
return mLayerManager;
|
||||
}
|
||||
|
||||
if (!sGLContext) {
|
||||
// the window we give doesn't matter here
|
||||
sGLContext = mozilla::gl::GLContextProvider::CreateForWindow(this);
|
||||
}
|
||||
if (!mLayerManager) {
|
||||
if (!sGLContext) {
|
||||
// the window we give doesn't matter here
|
||||
sGLContext = mozilla::gl::GLContextProvider::CreateForWindow(this);
|
||||
}
|
||||
|
||||
if (sGLContext) {
|
||||
nsRefPtr<mozilla::layers::LayerManagerOGL> layerManager =
|
||||
new mozilla::layers::LayerManagerOGL(this);
|
||||
if (sGLContext) {
|
||||
nsRefPtr<mozilla::layers::LayerManagerOGL> layerManager =
|
||||
new mozilla::layers::LayerManagerOGL(this);
|
||||
|
||||
if (layerManager && layerManager->Initialize(sGLContext))
|
||||
mLayerManager = layerManager;
|
||||
sValidSurface = true;
|
||||
}
|
||||
if (layerManager && layerManager->Initialize(sGLContext))
|
||||
mLayerManager = layerManager;
|
||||
sValidSurface = true;
|
||||
}
|
||||
|
||||
if (!sGLContext || !mLayerManager) {
|
||||
sGLContext = nsnull;
|
||||
sFailedToCreateGLContext = true;
|
||||
if (!sGLContext || !mLayerManager) {
|
||||
sGLContext = nsnull;
|
||||
sFailedToCreateGLContext = true;
|
||||
|
||||
mLayerManager = CreateBasicLayerManager();
|
||||
mLayerManager = CreateBasicLayerManager();
|
||||
}
|
||||
}
|
||||
|
||||
return mLayerManager;
|
||||
}
|
||||
|
||||
gfxASurface*
|
||||
nsWindow::GetThebesSurface()
|
||||
{
|
||||
/* This is really a dummy surface; this is only used when doing reflow, because
|
||||
* we need a RenderingContext to measure text against.
|
||||
*/
|
||||
|
||||
// XXX this really wants to return already_AddRefed, but this only really gets used
|
||||
// on direct assignment to a gfxASurface
|
||||
return new gfxImageSurface(gfxIntSize(5,5), gfxImageSurface::ImageFormatRGB24);
|
||||
}
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
|
||||
void
|
||||
nsWindow::BindToTexture()
|
||||
{
|
||||
sDirectTexture->Bind();
|
||||
}
|
||||
|
||||
bool
|
||||
nsWindow::HasDirectTexture()
|
||||
{
|
||||
static bool sTestedDirectTexture = false;
|
||||
static bool sHasDirectTexture = false;
|
||||
|
||||
// If we already tested, return early
|
||||
if (sTestedDirectTexture)
|
||||
return sHasDirectTexture;
|
||||
|
||||
sTestedDirectTexture = true;
|
||||
|
||||
nsAutoString board;
|
||||
AndroidGraphicBuffer* buffer = NULL;
|
||||
unsigned char* bits = NULL;
|
||||
|
||||
if (AndroidGraphicBuffer::IsBlacklisted()) {
|
||||
ALOG("device is blacklisted for direct texture");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
buffer = new AndroidGraphicBuffer(512, 512,
|
||||
AndroidGraphicBuffer::UsageSoftwareWrite | AndroidGraphicBuffer::UsageTexture,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
|
||||
if (buffer->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, &bits) != 0 || !bits) {
|
||||
ALOG("failed to lock graphic buffer");
|
||||
buffer->Unlock();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (buffer->Unlock() != 0) {
|
||||
ALOG("failed to unlock graphic buffer");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!buffer->Reallocate(1024, 1024, gfxASurface::ImageFormatRGB16_565)) {
|
||||
ALOG("failed to reallocate graphic buffer");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
sHasDirectTexture = true;
|
||||
|
||||
cleanup:
|
||||
if (buffer)
|
||||
delete buffer;
|
||||
|
||||
return sHasDirectTexture;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
|
||||
{
|
||||
|
@ -903,7 +844,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
|
|||
}
|
||||
case AndroidGeckoEvent::SIZE_CHANGED: {
|
||||
nsTArray<nsIntPoint> points = ae->Points();
|
||||
NS_ASSERTION(points.Length() != 3, "Size changed does not have enough coordinates");
|
||||
NS_ASSERTION(points.Length() == 2, "Size changed does not have enough coordinates");
|
||||
|
||||
int nw = points[0].x;
|
||||
int nh = points[0].y;
|
||||
|
@ -923,9 +864,6 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
|
|||
}
|
||||
}
|
||||
|
||||
gAndroidTileSize.width = points[2].x;
|
||||
gAndroidTileSize.height = points[2].y;
|
||||
|
||||
int newScreenWidth = points[1].x;
|
||||
int newScreenHeight = points[1].y;
|
||||
|
||||
|
@ -1106,6 +1044,8 @@ nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
|||
|
||||
switch (GetLayerManager(nsnull)->GetBackendType()) {
|
||||
case LayerManager::LAYERS_BASIC: {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### Basic layers drawing");
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
|
||||
|
||||
{
|
||||
|
@ -1128,6 +1068,8 @@ nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
|||
}
|
||||
|
||||
case LayerManager::LAYERS_OPENGL: {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### OGL layers drawing");
|
||||
|
||||
static_cast<mozilla::layers::LayerManagerOGL*>(GetLayerManager(nsnull))->
|
||||
SetClippingRegion(nsIntRegion(boundsRect));
|
||||
|
||||
|
@ -1187,11 +1129,18 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
|||
return;
|
||||
}
|
||||
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### OnDraw()");
|
||||
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame jniFrame;
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
// We haven't been given a window-size yet, so do nothing
|
||||
if (gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0)
|
||||
if (gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko",
|
||||
"### No window size yet -- skipping draw!");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see whether the presentation shell corresponding to the document on the screen
|
||||
|
@ -1206,6 +1155,7 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
|||
metadataProvider->PaintingSuppressed(&paintingSuppressed);
|
||||
}
|
||||
if (paintingSuppressed) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### Painting suppressed!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1214,67 +1164,47 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
|||
metadataProvider->GetDrawMetadata(metadata);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// BEGIN HACK: gl layers
|
||||
nsPaintEvent event(true, NS_PAINT, this);
|
||||
nsIntRect tileRect(0, 0, gAndroidBounds.width, gAndroidBounds.height);
|
||||
event.region = tileRect;
|
||||
#endif
|
||||
|
||||
static unsigned char *bits2 = new unsigned char[32 * 32 * 2];
|
||||
nsRefPtr<gfxImageSurface> targetSurface =
|
||||
new gfxImageSurface(bits2, gfxIntSize(32, 32), 32 * 2,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
|
||||
#if 0
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
|
||||
AutoLayerManagerSetup setupLayerManager(this, ctx, BasicLayerManager::BUFFER_NONE);
|
||||
|
||||
nsEventStatus status;
|
||||
status = DispatchEvent(&event);
|
||||
|
||||
return;
|
||||
// END HACK: gl layers
|
||||
#endif
|
||||
|
||||
nsIntRect dirtyRect = ae->Rect().Intersect(nsIntRect(0, 0, gAndroidBounds.width, gAndroidBounds.height));
|
||||
|
||||
AndroidGeckoSoftwareLayerClient &client =
|
||||
AndroidBridge::Bridge()->GetSoftwareLayerClient();
|
||||
AndroidGeckoLayerClient &client = AndroidBridge::Bridge()->GetLayerClient();
|
||||
if (!client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height,
|
||||
gAndroidTileSize.width, gAndroidTileSize.height,
|
||||
dirtyRect, metadata, HasDirectTexture())) {
|
||||
dirtyRect, metadata)) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### BeginDrawing returned false!");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char *bits = NULL;
|
||||
if (HasDirectTexture()) {
|
||||
if (sDirectTexture->Width() != gAndroidBounds.width ||
|
||||
sDirectTexture->Height() != gAndroidBounds.height) {
|
||||
sDirectTexture->Reallocate(gAndroidBounds.width, gAndroidBounds.height);
|
||||
}
|
||||
|
||||
sDirectTexture->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, dirtyRect, &bits);
|
||||
if (targetSurface->CairoStatus()) {
|
||||
ALOG("### Failed to create a valid surface from the bitmap");
|
||||
} else {
|
||||
bits = client.LockBufferBits();
|
||||
}
|
||||
if (!bits) {
|
||||
ALOG("### Failed to lock buffer");
|
||||
} else {
|
||||
// If tile size is 0,0, we assume we only have a single tile
|
||||
int tileWidth = (gAndroidTileSize.width > 0) ? gAndroidTileSize.width : gAndroidBounds.width;
|
||||
int tileHeight = (gAndroidTileSize.height > 0) ? gAndroidTileSize.height : gAndroidBounds.height;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
for (int y = 0; y < gAndroidBounds.height; y += tileHeight) {
|
||||
for (int x = 0; x < gAndroidBounds.width; x += tileWidth) {
|
||||
int width = NS_MIN(tileWidth, gAndroidBounds.width - x);
|
||||
int height = NS_MIN(tileHeight, gAndroidBounds.height - y);
|
||||
|
||||
nsRefPtr<gfxImageSurface> targetSurface =
|
||||
new gfxImageSurface(bits + offset,
|
||||
gfxIntSize(width, height),
|
||||
width * 2,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
|
||||
offset += width * height * 2;
|
||||
|
||||
if (targetSurface->CairoStatus()) {
|
||||
ALOG("### Failed to create a valid surface from the bitmap");
|
||||
break;
|
||||
} else {
|
||||
targetSurface->SetDeviceOffset(gfxPoint(-x, -y));
|
||||
DrawTo(targetSurface, dirtyRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### Calling DrawTo()!");
|
||||
DrawTo(targetSurface, dirtyRect);
|
||||
}
|
||||
|
||||
if (HasDirectTexture()) {
|
||||
sDirectTexture->Unlock();
|
||||
} else {
|
||||
client.UnlockBuffer();
|
||||
}
|
||||
|
||||
client.EndDrawing(dirtyRect);
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Gecko", "### Calling EndDrawing()!");
|
||||
client.EndDrawing();
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
@ -2342,3 +2272,34 @@ nsWindow::GetIMEUpdatePreference()
|
|||
return nsIMEUpdatePreference(true, true);
|
||||
}
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
void
|
||||
nsWindow::DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect) {
|
||||
AndroidBridge::AutoLocalJNIFrame jniFrame(GetJNIForThread());
|
||||
|
||||
AndroidGeckoLayerClient& client = AndroidBridge::Bridge()->GetLayerClient();
|
||||
client.CreateFrame(mLayerRendererFrame);
|
||||
|
||||
client.ActivateProgram();
|
||||
mLayerRendererFrame.BeginDrawing();
|
||||
mLayerRendererFrame.DrawBackground();
|
||||
client.DeactivateProgram();
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect) {
|
||||
AndroidBridge::AutoLocalJNIFrame jniFrame(GetJNIForThread());
|
||||
NS_ABORT_IF_FALSE(!mLayerRendererFrame.isNull(),
|
||||
"Frame should have been created in DrawWindowUnderlay()!");
|
||||
|
||||
AndroidGeckoLayerClient& client = AndroidBridge::Bridge()->GetLayerClient();
|
||||
|
||||
client.ActivateProgram();
|
||||
mLayerRendererFrame.DrawForeground();
|
||||
mLayerRendererFrame.EndDrawing();
|
||||
client.DeactivateProgram();
|
||||
|
||||
mLayerRendererFrame.Dispose();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -47,6 +47,11 @@
|
|||
#include "nsAccessible.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
#include "AndroidJavaWrappers.h"
|
||||
#include "Layers.h"
|
||||
#endif
|
||||
|
||||
class gfxASurface;
|
||||
class nsIdleService;
|
||||
|
||||
|
@ -168,7 +173,6 @@ public:
|
|||
LayersBackend aBackendHint = LayerManager::LAYERS_NONE,
|
||||
LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
|
||||
bool* aAllowRetaining = nsnull);
|
||||
gfxASurface* GetThebesSurface();
|
||||
|
||||
NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);
|
||||
|
||||
|
@ -177,8 +181,8 @@ public:
|
|||
#endif
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
static void BindToTexture();
|
||||
static bool HasDirectTexture();
|
||||
virtual void DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect);
|
||||
virtual void DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
@ -246,6 +250,10 @@ private:
|
|||
*/
|
||||
nsAccessible *DispatchAccessibleEvent();
|
||||
#endif // ACCESSIBILITY
|
||||
|
||||
#ifdef MOZ_JAVA_COMPOSITOR
|
||||
mozilla::AndroidLayerRendererFrame mLayerRendererFrame;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* NSWINDOW_H_ */
|
||||
|
|
|
@ -1085,6 +1085,14 @@ class nsIWidget : public nsISupports {
|
|||
LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
|
||||
bool* aAllowRetaining = nsnull) = 0;
|
||||
|
||||
/**
|
||||
* Called before the LayerManager draws the layer tree.
|
||||
*
|
||||
* @param aManager The drawing LayerManager.
|
||||
* @param aWidgetRect The current widget rect that is being drawn.
|
||||
*/
|
||||
virtual void DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect) = 0;
|
||||
|
||||
/**
|
||||
* Called after the LayerManager draws the layer tree
|
||||
*
|
||||
|
|
|
@ -133,6 +133,7 @@ public:
|
|||
bool* aAllowRetaining = nsnull);
|
||||
|
||||
virtual void CreateCompositor();
|
||||
virtual void DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect) {}
|
||||
virtual void DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect) {}
|
||||
virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) {}
|
||||
virtual gfxASurface* GetThebesSurface();
|
||||
|
|
Загрузка…
Ссылка в новой задаче