diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn index e24af47472605..984a44e5a5e3f 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn @@ -225,9 +225,9 @@ test("gfx_unittests") { if (is_android) { generate_jni("gfx_jni_headers") { sources = [ - "../android/java/src/org/chromium/ui/gfx/BitmapHelper.java", - "../android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java", - "../android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java", + "android/java/src/org/chromium/ui/gfx/BitmapHelper.java", + "android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java", + "android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java", ] jni_package = "gfx" } diff --git a/ui/gfx/android/java/src/org/chromium/ui/gfx/BitmapHelper.java b/ui/gfx/android/java/src/org/chromium/ui/gfx/BitmapHelper.java new file mode 100644 index 0000000000000..8ffaae4b09371 --- /dev/null +++ b/ui/gfx/android/java/src/org/chromium/ui/gfx/BitmapHelper.java @@ -0,0 +1,120 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.gfx; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * Helper class to decode and sample down bitmap resources. + */ +@JNINamespace("gfx") +public class BitmapHelper { + @CalledByNative + private static Bitmap createBitmap(int width, + int height, + int bitmapFormatValue) { + Bitmap.Config bitmapConfig = getBitmapConfigForFormat(bitmapFormatValue); + return Bitmap.createBitmap(width, height, bitmapConfig); + } + + /** + * Decode and sample down a bitmap resource to the requested width and height. + * + * @param name The resource name of the bitmap to decode. + * @param reqWidth The requested width of the resulting bitmap. + * @param reqHeight The requested height of the resulting bitmap. + * @return A bitmap sampled down from the original with the same aspect ratio and dimensions. + * that are equal to or greater than the requested width and height. + */ + @CalledByNative + private static Bitmap decodeDrawableResource(String name, + int reqWidth, + int reqHeight) { + Resources res = Resources.getSystem(); + int resId = res.getIdentifier(name, null, null); + if (resId == 0) return null; + + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(res, resId, options); + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + options.inJustDecodeBounds = false; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + return BitmapFactory.decodeResource(res, resId, options); + } + + // http://developer.android.com/training/displaying-bitmaps/load-bitmap.html + private static int calculateInSampleSize(BitmapFactory.Options options, + int reqWidth, + int reqHeight) { + // Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + + // Calculate ratios of height and width to requested height and width + final int heightRatio = Math.round((float) height / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + + // Choose the smallest ratio as inSampleSize value, this will guarantee + // a final image with both dimensions larger than or equal to the + // requested height and width. + inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + } + + return inSampleSize; + } + + /** + * Provides a matching integer constant for the Bitmap.Config value passed. + * + * @param bitmapConfig The Bitmap Configuration value. + * @return Matching integer constant for the Bitmap.Config value passed. + */ + @CalledByNative + private static int getBitmapFormatForConfig(Bitmap.Config bitmapConfig) { + switch (bitmapConfig) { + case ALPHA_8: + return BitmapFormat.ALPHA_8; + case ARGB_4444: + return BitmapFormat.ARGB_4444; + case ARGB_8888: + return BitmapFormat.ARGB_8888; + case RGB_565: + return BitmapFormat.RGB_565; + default: + return BitmapFormat.NO_CONFIG; + } + } + + /** + * Provides a matching Bitmap.Config for the enum config value passed. + * + * @param bitmapFormatValue The Bitmap Configuration enum value. + * @return Matching Bitmap.Config for the enum value passed. + */ + private static Bitmap.Config getBitmapConfigForFormat(int bitmapFormatValue) { + switch (bitmapFormatValue) { + case BitmapFormat.ALPHA_8: + return Bitmap.Config.ALPHA_8; + case BitmapFormat.ARGB_4444: + return Bitmap.Config.ARGB_4444; + case BitmapFormat.RGB_565: + return Bitmap.Config.RGB_565; + case BitmapFormat.ARGB_8888: + default: + return Bitmap.Config.ARGB_8888; + } + } + +} diff --git a/ui/gfx/android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java b/ui/gfx/android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java new file mode 100644 index 0000000000000..38569513d08dc --- /dev/null +++ b/ui/gfx/android/java/src/org/chromium/ui/gfx/DeviceDisplayInfo.java @@ -0,0 +1,214 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.gfx; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.Surface; +import android.view.WindowManager; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * This class facilitates access to android information typically only + * available using the Java SDK, including {@link Display} properties. + * + * Currently the information consists of very raw display information (height, width, DPI scale) + * regarding the main display. + */ +@JNINamespace("gfx") +public class DeviceDisplayInfo { + + private final Context mAppContext; + private final WindowManager mWinManager; + private Point mTempPoint = new Point(); + private DisplayMetrics mTempMetrics = new DisplayMetrics(); + + private DeviceDisplayInfo(Context context) { + mAppContext = context.getApplicationContext(); + mWinManager = (WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE); + } + + /** + * @return Display height in physical pixels. + */ + @CalledByNative + public int getDisplayHeight() { + getDisplay().getSize(mTempPoint); + return mTempPoint.y; + } + + /** + * @return Display width in physical pixels. + */ + @CalledByNative + public int getDisplayWidth() { + getDisplay().getSize(mTempPoint); + return mTempPoint.x; + } + + /** + * @return Real physical display height in physical pixels. + */ + @CalledByNative + public int getPhysicalDisplayHeight() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return 0; + } + getDisplay().getRealSize(mTempPoint); + return mTempPoint.y; + } + + /** + * @return Real physical display width in physical pixels. + */ + @CalledByNative + public int getPhysicalDisplayWidth() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return 0; + } + getDisplay().getRealSize(mTempPoint); + return mTempPoint.x; + } + + @SuppressWarnings("deprecation") + private int getPixelFormat() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return getDisplay().getPixelFormat(); + } + // JellyBean MR1 and later always uses RGBA_8888. + return PixelFormat.RGBA_8888; + } + + /** + * @return Bits per pixel. + */ + @CalledByNative + public int getBitsPerPixel() { + int format = getPixelFormat(); + PixelFormat info = new PixelFormat(); + PixelFormat.getPixelFormatInfo(format, info); + return info.bitsPerPixel; + } + + /** + * @return Bits per component. + */ + @SuppressWarnings("deprecation") + @CalledByNative + public int getBitsPerComponent() { + int format = getPixelFormat(); + switch (format) { + case PixelFormat.RGBA_4444: + return 4; + + case PixelFormat.RGBA_5551: + return 5; + + case PixelFormat.RGBA_8888: + case PixelFormat.RGBX_8888: + case PixelFormat.RGB_888: + return 8; + + case PixelFormat.RGB_332: + return 2; + + case PixelFormat.RGB_565: + return 5; + + // Non-RGB formats. + case PixelFormat.A_8: + case PixelFormat.LA_88: + case PixelFormat.L_8: + return 0; + + // Unknown format. Use 8 as a sensible default. + default: + return 8; + } + } + + /** + * @return A scaling factor for the Density Independent Pixel unit. 1.0 is + * 160dpi, 0.75 is 120dpi, 2.0 is 320dpi. + */ + @CalledByNative + public double getDIPScale() { + getDisplay().getMetrics(mTempMetrics); + return mTempMetrics.density; + } + + /** + * @return Smallest screen size in density-independent pixels that the + * application will see, regardless of orientation. + */ + @CalledByNative + private int getSmallestDIPWidth() { + return mAppContext.getResources().getConfiguration().smallestScreenWidthDp; + } + + /** + * @return the screen's rotation angle from its 'natural' orientation. + * Expected values are one of { 0, 90, 180, 270 }. + * See http://developer.android.com/reference/android/view/Display.html#getRotation() + * for more information about Display.getRotation() behavior. + */ + @CalledByNative + public int getRotationDegrees() { + switch (getDisplay().getRotation()) { + case Surface.ROTATION_0: + return 0; + case Surface.ROTATION_90: + return 90; + case Surface.ROTATION_180: + return 180; + case Surface.ROTATION_270: + return 270; + } + + // This should not happen. + assert false; + return 0; + } + + /** + * Inform the native implementation to update its cached representation of + * the DeviceDisplayInfo values. + */ + public void updateNativeSharedDisplayInfo() { + nativeUpdateSharedDeviceDisplayInfo( + getDisplayHeight(), getDisplayWidth(), + getPhysicalDisplayHeight(), getPhysicalDisplayWidth(), + getBitsPerPixel(), getBitsPerComponent(), + getDIPScale(), getSmallestDIPWidth(), getRotationDegrees()); + } + + private Display getDisplay() { + return mWinManager.getDefaultDisplay(); + } + + /** + * Creates DeviceDisplayInfo for a given Context. + * + * @param context A context to use. + * @return DeviceDisplayInfo associated with a given Context. + */ + @CalledByNative + public static DeviceDisplayInfo create(Context context) { + return new DeviceDisplayInfo(context); + } + + private native void nativeUpdateSharedDeviceDisplayInfo( + int displayHeight, int displayWidth, + int physicalDisplayHeight, int physicalDisplayWidth, + int bitsPerPixel, int bitsPerComponent, double dipScale, + int smallestDIPWidth, int rotationDegrees); + +} diff --git a/ui/gfx/android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java b/ui/gfx/android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java new file mode 100644 index 0000000000000..e8fc5b996d8de --- /dev/null +++ b/ui/gfx/android/java/src/org/chromium/ui/gfx/ViewConfigurationHelper.java @@ -0,0 +1,149 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.gfx; + +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.util.TypedValue; +import android.view.ViewConfiguration; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; +import org.chromium.ui.R; + +/** + * This class facilitates access to ViewConfiguration-related properties, also + * providing native-code notifications when such properties have changed. + * + */ +@JNINamespace("gfx") +public class ViewConfigurationHelper { + + // Fallback constants when resource lookup fails, see + // ui/android/java/res/values/dimens.xml. + private static final float MIN_SCALING_SPAN_MM = 27.0f; + private static final float MIN_SCALING_TOUCH_MAJOR_DIP = 48.0f; + + private final Context mAppContext; + private ViewConfiguration mViewConfiguration; + + private ViewConfigurationHelper(Context context) { + mAppContext = context.getApplicationContext(); + mViewConfiguration = ViewConfiguration.get(mAppContext); + } + + private void registerListener() { + mAppContext.registerComponentCallbacks( + new ComponentCallbacks() { + @Override + public void onConfigurationChanged(Configuration configuration) { + updateNativeViewConfigurationIfNecessary(); + } + + @Override + public void onLowMemory() { + } + }); + } + + private void updateNativeViewConfigurationIfNecessary() { + // The ViewConfiguration will differ only if the density has changed. + ViewConfiguration configuration = ViewConfiguration.get(mAppContext); + if (mViewConfiguration == configuration) return; + + mViewConfiguration = configuration; + nativeUpdateSharedViewConfiguration( + getScaledMaximumFlingVelocity(), + getScaledMinimumFlingVelocity(), + getScaledTouchSlop(), + getScaledDoubleTapSlop(), + getScaledMinScalingSpan(), + getScaledMinScalingTouchMajor()); + } + + @CalledByNative + private static int getDoubleTapTimeout() { + return ViewConfiguration.getDoubleTapTimeout(); + } + + @CalledByNative + private static int getLongPressTimeout() { + return ViewConfiguration.getLongPressTimeout(); + } + + @CalledByNative + private static int getTapTimeout() { + return ViewConfiguration.getTapTimeout(); + } + + @CalledByNative + private static float getScrollFriction() { + return ViewConfiguration.getScrollFriction(); + } + + @CalledByNative + private int getScaledMaximumFlingVelocity() { + return mViewConfiguration.getScaledMaximumFlingVelocity(); + } + + @CalledByNative + private int getScaledMinimumFlingVelocity() { + return mViewConfiguration.getScaledMinimumFlingVelocity(); + } + + @CalledByNative + private int getScaledTouchSlop() { + return mViewConfiguration.getScaledTouchSlop(); + } + + @CalledByNative + private int getScaledDoubleTapSlop() { + return mViewConfiguration.getScaledDoubleTapSlop(); + } + + @CalledByNative + private int getScaledMinScalingSpan() { + final Resources res = mAppContext.getResources(); + int id = res.getIdentifier("config_minScalingSpan", "dimen", "android"); + // Fall back to a sensible default if the internal identifier does not exist. + if (id == 0) id = R.dimen.config_min_scaling_span; + try { + return res.getDimensionPixelSize(id); + } catch (Resources.NotFoundException e) { + assert false : "MinScalingSpan resource lookup failed."; + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MIN_SCALING_SPAN_MM, + res.getDisplayMetrics()); + } + } + + @CalledByNative + private int getScaledMinScalingTouchMajor() { + final Resources res = mAppContext.getResources(); + int id = res.getIdentifier("config_minScalingTouchMajor", "dimen", "android"); + // Fall back to a sensible default if the internal identifier does not exist. + if (id == 0) id = R.dimen.config_min_scaling_touch_major; + try { + return res.getDimensionPixelSize(id); + } catch (Resources.NotFoundException e) { + assert false : "MinScalingTouchMajor resource lookup failed."; + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + MIN_SCALING_TOUCH_MAJOR_DIP, res.getDisplayMetrics()); + } + } + + @CalledByNative + private static ViewConfigurationHelper createWithListener(Context context) { + ViewConfigurationHelper viewConfigurationHelper = new ViewConfigurationHelper(context); + viewConfigurationHelper.registerListener(); + return viewConfigurationHelper; + } + + private native void nativeUpdateSharedViewConfiguration( + int scaledMaximumFlingVelocity, int scaledMinimumFlingVelocity, + int scaledTouchSlop, int scaledDoubleTapSlop, + int scaledMinScalingSpan, int scaledMinScalingTouchMajor); +} diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index a5ed5ccbb2a66..43d0765e7a287 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn @@ -289,8 +289,8 @@ if (is_android) { ":surface_jni_headers", ] sources = [ - "../android/java/src/org/chromium/ui/gl/SurfaceTextureListener.java", - "../android/java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java", + "android/java/src/org/chromium/ui/gl/SurfaceTextureListener.java", + "android/java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java", ] jni_package = "ui/gl" } diff --git a/ui/gl/android/java/src/org/chromium/ui/gl/SurfaceTextureListener.java b/ui/gl/android/java/src/org/chromium/ui/gl/SurfaceTextureListener.java new file mode 100644 index 0000000000000..bc57e1faca2e4 --- /dev/null +++ b/ui/gl/android/java/src/org/chromium/ui/gl/SurfaceTextureListener.java @@ -0,0 +1,40 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.gl; + +import android.graphics.SurfaceTexture; + +import org.chromium.base.JNINamespace; + +/** + * Listener to an android SurfaceTexture object for frame availability. + */ +@JNINamespace("gfx") +class SurfaceTextureListener implements SurfaceTexture.OnFrameAvailableListener { + // Used to determine the class instance to dispatch the native call to. + private final long mNativeSurfaceTextureListener; + + SurfaceTextureListener(long nativeSurfaceTextureListener) { + assert nativeSurfaceTextureListener != 0; + mNativeSurfaceTextureListener = nativeSurfaceTextureListener; + } + + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + nativeFrameAvailable(mNativeSurfaceTextureListener); + } + + @Override + protected void finalize() throws Throwable { + try { + nativeDestroy(mNativeSurfaceTextureListener); + } finally { + super.finalize(); + } + } + + private native void nativeFrameAvailable(long nativeSurfaceTextureListener); + private native void nativeDestroy(long nativeSurfaceTextureListener); +} diff --git a/ui/gl/android/java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java b/ui/gl/android/java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java new file mode 100644 index 0000000000000..e785af6a108c3 --- /dev/null +++ b/ui/gl/android/java/src/org/chromium/ui/gl/SurfaceTexturePlatformWrapper.java @@ -0,0 +1,78 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.gl; + +import android.graphics.SurfaceTexture; +import android.os.Build; +import android.util.Log; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * Wrapper class for the underlying platform's SurfaceTexture in order to + * provide a stable JNI API. + */ +@JNINamespace("gfx") +class SurfaceTexturePlatformWrapper { + + private static final String TAG = "SurfaceTexturePlatformWrapper"; + + @CalledByNative + private static SurfaceTexture create(int textureId) { + return new SurfaceTexture(textureId); + } + + @CalledByNative + private static SurfaceTexture createSingleBuffered(int textureId) { + assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + return new SurfaceTexture(textureId, true); + } + + @CalledByNative + private static void destroy(SurfaceTexture surfaceTexture) { + surfaceTexture.setOnFrameAvailableListener(null); + surfaceTexture.release(); + } + + @CalledByNative + private static void setFrameAvailableCallback(SurfaceTexture surfaceTexture, + long nativeSurfaceTextureListener) { + surfaceTexture.setOnFrameAvailableListener( + new SurfaceTextureListener(nativeSurfaceTextureListener)); + } + + @CalledByNative + private static void updateTexImage(SurfaceTexture surfaceTexture) { + try { + surfaceTexture.updateTexImage(); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling updateTexImage", e); + } + } + + @CalledByNative + private static void releaseTexImage(SurfaceTexture surfaceTexture) { + assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + surfaceTexture.releaseTexImage(); + } + + @CalledByNative + private static void getTransformMatrix(SurfaceTexture surfaceTexture, float[] matrix) { + surfaceTexture.getTransformMatrix(matrix); + } + + @CalledByNative + private static void attachToGLContext(SurfaceTexture surfaceTexture, int texName) { + assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + surfaceTexture.attachToGLContext(texName); + } + + @CalledByNative + private static void detachFromGLContext(SurfaceTexture surfaceTexture) { + assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + surfaceTexture.detachFromGLContext(); + } +}