This commit is contained in:
Doug Turner 2012-02-24 11:57:27 -08:00
Родитель 7be1b215e9 0a09dc8dd3
Коммит f72f4d6343
71 изменённых файлов: 4138 добавлений и 2071 удалений

Просмотреть файл

@ -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 \

104
gfx/layers/RenderTrace.cpp Normal file
Просмотреть файл

@ -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

78
gfx/layers/RenderTrace.h Normal file
Просмотреть файл

@ -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();