Merge "fix NPE in updateClientUids"
diff --git a/api/current.txt b/api/current.txt
index 1cf14aa..71b6b14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28963,7 +28963,9 @@
ctor public QuickContactBadge(android.content.Context, android.util.AttributeSet);
ctor public QuickContactBadge(android.content.Context, android.util.AttributeSet, int);
method public void assignContactFromEmail(java.lang.String, boolean);
+ method public void assignContactFromEmail(java.lang.String, boolean, android.os.Bundle);
method public void assignContactFromPhone(java.lang.String, boolean);
+ method public void assignContactFromPhone(java.lang.String, boolean, android.os.Bundle);
method public void assignContactUri(android.net.Uri);
method public void onClick(android.view.View);
method public void setExcludeMimes(java.lang.String[]);
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 7739ff7..e0cf3b2 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -433,20 +433,16 @@
@Override
public boolean clipPath(Path path) {
- // TODO: Implement
- path.computeBounds(mPathBounds, true);
- return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
- mPathBounds.right, mPathBounds.bottom, Region.Op.INTERSECT.nativeInt);
+ return nClipPath(mRenderer, path.mNativePath, Region.Op.INTERSECT.nativeInt);
}
@Override
public boolean clipPath(Path path, Region.Op op) {
- // TODO: Implement
- path.computeBounds(mPathBounds, true);
- return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
- mPathBounds.right, mPathBounds.bottom, op.nativeInt);
+ return nClipPath(mRenderer, path.mNativePath, op.nativeInt);
}
+ private static native boolean nClipPath(int renderer, int path, int op);
+
@Override
public boolean clipRect(float left, float top, float right, float bottom) {
return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
@@ -465,8 +461,8 @@
return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
}
- private static native boolean nClipRect(int renderer, int left, int top, int right, int bottom,
- int op);
+ private static native boolean nClipRect(int renderer, int left, int top,
+ int right, int bottom, int op);
@Override
public boolean clipRect(Rect rect) {
@@ -492,20 +488,16 @@
@Override
public boolean clipRegion(Region region) {
- // TODO: Implement
- region.getBounds(mClipBounds);
- return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
- mClipBounds.right, mClipBounds.bottom, Region.Op.INTERSECT.nativeInt);
+ return nClipRegion(mRenderer, region.mNativeRegion, Region.Op.INTERSECT.nativeInt);
}
@Override
public boolean clipRegion(Region region, Region.Op op) {
- // TODO: Implement
- region.getBounds(mClipBounds);
- return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
- mClipBounds.right, mClipBounds.bottom, op.nativeInt);
+ return nClipRegion(mRenderer, region.mNativeRegion, op.nativeInt);
}
+ private static native boolean nClipRegion(int renderer, int region, int op);
+
@Override
public boolean getClipBounds(Rect bounds) {
return nGetClipBounds(mRenderer, bounds);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 7ecdcbe..0d45bbc 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.view;
import android.content.ComponentCallbacks2;
@@ -174,6 +173,17 @@
public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
/**
+ * Turn on to allow region clipping (see
+ * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and
+ * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}.
+ *
+ * When this option is turned on a stencil buffer is always required.
+ * If this option is off a stencil buffer is only created when the overdraw
+ * debugging mode is turned on.
+ */
+ private static final boolean REGION_CLIPPING_ENABLED = false;
+
+ /**
* A process can set this flag to false to prevent the use of hardware
* rendering.
*
@@ -876,10 +886,12 @@
changed = true;
mShowOverdraw = value;
- if (surface != null && isEnabled()) {
- if (validate()) {
- sEglConfig = loadEglConfig();
- invalidate(surface);
+ if (!REGION_CLIPPING_ENABLED) {
+ if (surface != null && isEnabled()) {
+ if (validate()) {
+ sEglConfig = loadEglConfig();
+ invalidate(surface);
+ }
}
}
}
@@ -1752,6 +1764,11 @@
@Override
int[] getConfig(boolean dirtyRegions) {
+ //noinspection PointlessBooleanExpression
+ final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ?
+ GLES20Canvas.getStencilSize() : 0;
+ final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+
return new int[] {
EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
@@ -1760,14 +1777,12 @@
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_CONFIG_CAVEAT, EGL_NONE,
- // TODO: Find a better way to choose the stencil size
- EGL_STENCIL_SIZE, mShowOverdraw ? GLES20Canvas.getStencilSize() : 0,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
- (dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
+ EGL_STENCIL_SIZE, stencilSize,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
EGL_NONE
};
}
-
+
@Override
void initCaches() {
GLES20Canvas.initCaches();
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 786afe2..622ac8f5 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -27,6 +27,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
@@ -50,6 +51,7 @@
private Drawable mOverlay;
private QueryHandler mQueryHandler;
private Drawable mDefaultAvatar;
+ private Bundle mExtras = null;
protected String[] mExcludeMimes = null;
@@ -58,6 +60,8 @@
static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2;
static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3;
+ static final private String EXTRA_URI_CONTENT = "uri_content";
+
static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
RawContacts.CONTACT_ID,
Contacts.LOOKUP_KEY,
@@ -175,7 +179,26 @@
* until this view is clicked.
*/
public void assignContactFromEmail(String emailAddress, boolean lazyLookup) {
+ assignContactFromEmail(emailAddress, lazyLookup, null);
+ }
+
+ /**
+ * Assign a contact based on an email address. This should only be used when
+ * the contact's URI is not available, as an extra query will have to be
+ * performed to lookup the URI based on the email.
+
+ @param emailAddress The email address of the contact.
+ @param lazyLookup If this is true, the lookup query will not be performed
+ until this view is clicked.
+ @param extras A bundle of extras to populate the contact edit page with if the contact
+ is not found and the user chooses to add the email address to an existing contact or
+ create a new contact. Uses the same string constants as those found in
+ {@link #ContactsContract.Intents.Insert}
+ */
+
+ public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) {
mContactEmail = emailAddress;
+ mExtras = extras;
if (!lazyLookup) {
mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null,
Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
@@ -186,6 +209,7 @@
}
}
+
/**
* Assign a contact based on a phone number. This should only be used when
* the contact's URI is not available, as an extra query will have to be
@@ -196,7 +220,25 @@
* until this view is clicked.
*/
public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) {
+ assignContactFromPhone(phoneNumber, lazyLookup, new Bundle());
+ }
+
+ /**
+ * Assign a contact based on a phone number. This should only be used when
+ * the contact's URI is not available, as an extra query will have to be
+ * performed to lookup the URI based on the phone number.
+ *
+ * @param phoneNumber The phone number of the contact.
+ * @param lazyLookup If this is true, the lookup query will not be performed
+ * until this view is clicked.
+ * @param extras A bundle of extras to populate the contact edit page with if the contact
+ * is not found and the user chooses to add the phone number to an existing contact or
+ * create a new contact. Uses the same string constants as those found in
+ * {@link #ContactsContract.Intents.Insert}
+ */
+ public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) {
mContactPhone = phoneNumber;
+ mExtras = extras;
if (!lazyLookup) {
mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null,
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
@@ -213,15 +255,21 @@
@Override
public void onClick(View v) {
+ // If contact has been assigned, mExtras should no longer be null, but do a null check
+ // anyway just in case assignContactFromPhone or Email was called with a null bundle or
+ // wasn't assigned previously.
+ final Bundle extras = (mExtras == null) ? new Bundle() : mExtras;
if (mContactUri != null) {
QuickContact.showQuickContact(getContext(), QuickContactBadge.this, mContactUri,
QuickContact.MODE_LARGE, mExcludeMimes);
} else if (mContactEmail != null) {
- mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, mContactEmail,
+ extras.putString(EXTRA_URI_CONTENT, mContactEmail);
+ mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras,
Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)),
EMAIL_LOOKUP_PROJECTION, null, null, null);
} else if (mContactPhone != null) {
- mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, mContactPhone,
+ extras.putString(EXTRA_URI_CONTENT, mContactPhone);
+ mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras,
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone),
PHONE_LOOKUP_PROJECTION, null, null, null);
} else {
@@ -262,12 +310,12 @@
Uri lookupUri = null;
Uri createUri = null;
boolean trigger = false;
-
+ Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
try {
switch(token) {
case TOKEN_PHONE_LOOKUP_AND_TRIGGER:
trigger = true;
- createUri = Uri.fromParts("tel", (String)cookie, null);
+ createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null);
//$FALL-THROUGH$
case TOKEN_PHONE_LOOKUP: {
@@ -281,7 +329,8 @@
}
case TOKEN_EMAIL_LOOKUP_AND_TRIGGER:
trigger = true;
- createUri = Uri.fromParts("mailto", (String)cookie, null);
+ createUri = Uri.fromParts("mailto",
+ extras.getString(EXTRA_URI_CONTENT), null);
//$FALL-THROUGH$
case TOKEN_EMAIL_LOOKUP: {
@@ -309,6 +358,10 @@
} else if (createUri != null) {
// Prompt user to add this person to contacts
final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
+ if (extras != null) {
+ extras.remove(EXTRA_URI_CONTENT);
+ intent.putExtras(extras);
+ }
getContext().startActivity(intent);
}
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d3ba11a..d705024 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -91,7 +91,7 @@
android/graphics/DrawFilter.cpp \
android/graphics/CreateJavaOutputStreamAdaptor.cpp \
android/graphics/Graphics.cpp \
- android/graphics/HarfbuzzSkia.cpp \
+ android/graphics/HarfBuzzNGFaceSkia.cpp \
android/graphics/Interpolator.cpp \
android/graphics/LayerRasterizer.cpp \
android/graphics/MaskFilter.cpp \
@@ -170,8 +170,7 @@
external/icu4c/i18n \
external/icu4c/common \
external/jpeg \
- external/harfbuzz/contrib \
- external/harfbuzz/src \
+ external/harfbuzz_ng/src \
external/zlib \
frameworks/opt/emoji \
libcore/include
@@ -206,7 +205,7 @@
libwpa_client \
libjpeg \
libusbhost \
- libharfbuzz \
+ libharfbuzz_ng \
libz
ifeq ($(USE_OPENGL_RENDERER),true)
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
new file mode 100644
index 0000000..1752e5b
--- /dev/null
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "TextLayoutCache"
+
+#include "HarfBuzzNGFaceSkia.h"
+
+#include <cutils/log.h>
+#include <SkFontHost.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkUtils.h>
+
+#include <hb.h>
+
+namespace android {
+
+// Our implementation of the callbacks which Harfbuzz requires by using Skia
+// calls. See the Harfbuzz source for references about what these callbacks do.
+
+struct HarfBuzzFontData {
+ HarfBuzzFontData(SkPaint* paint) : m_paint(paint) { }
+ SkPaint* m_paint;
+};
+
+static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
+{
+ ALOG_ASSERT(codepoint <= 0xFFFF);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ SkScalar skWidth;
+ SkRect skBounds;
+ uint16_t glyph = codepoint;
+
+ paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
+ ALOGD("returned glyph for %i: width = %f", codepoint, skWidth);
+ if (width)
+ *width = SkScalarToHBFixed(skWidth);
+ if (extents) {
+ // Invert y-axis because Skia is y-grows-down but we set up harfbuzz to be y-grows-up.
+ extents->x_bearing = SkScalarToHBFixed(skBounds.fLeft);
+ extents->y_bearing = SkScalarToHBFixed(-skBounds.fTop);
+ extents->width = SkScalarToHBFixed(skBounds.width());
+ extents->height = SkScalarToHBFixed(-skBounds.height());
+ }
+}
+
+static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
+{
+ HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+
+ if (unicode > 0x10ffff) {
+ unicode = 0xfffd;
+ }
+ SkPaint* paint = hbFontData->m_paint;
+ // It would be better to use kUTF32_TextEncoding directly
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ uint16_t glyph16;
+ uint16_t unichar[2];
+ size_t size = SkUTF16_FromUnichar(unicode, unichar);
+ paint->textToGlyphs(unichar, size * sizeof(*unichar), &glyph16);
+ *glyph = glyph16;
+ return !!*glyph;
+}
+
+static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
+{
+ HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+ hb_position_t advance = 0;
+
+ SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, &advance, 0);
+ return advance;
+}
+
+static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
+{
+ // Just return true, following the way that Harfbuzz-FreeType
+ // implementation does.
+ return true;
+}
+
+static hb_bool_t harfbuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData)
+{
+ HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
+
+ SkiaGetGlyphWidthAndExtents(hbFontData->m_paint, glyph, 0, extents);
+ return true;
+}
+
+static hb_font_funcs_t* harfbuzzSkiaGetFontFuncs()
+{
+ static hb_font_funcs_t* harfbuzzSkiaFontFuncs = 0;
+
+ // We don't set callback functions which we can't support.
+ // Harfbuzz will use the fallback implementation if they aren't set.
+ if (!harfbuzzSkiaFontFuncs) {
+ harfbuzzSkiaFontFuncs = hb_font_funcs_create();
+ hb_font_funcs_set_glyph_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyph, 0, 0);
+ hb_font_funcs_set_glyph_h_advance_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
+ hb_font_funcs_set_glyph_h_origin_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
+ hb_font_funcs_set_glyph_extents_func(harfbuzzSkiaFontFuncs, harfbuzzGetGlyphExtents, 0, 0);
+ hb_font_funcs_make_immutable(harfbuzzSkiaFontFuncs);
+ }
+ return harfbuzzSkiaFontFuncs;
+}
+
+hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData)
+{
+ SkTypeface* typeface = reinterpret_cast<SkTypeface*>(userData);
+ SkFontID uniqueID = typeface->uniqueID();
+
+ const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag);
+ if (!tableSize)
+ return 0;
+
+ char* buffer = reinterpret_cast<char*>(malloc(tableSize));
+ if (!buffer)
+ return 0;
+ size_t actualSize = SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
+ if (tableSize != actualSize) {
+ free(buffer);
+ return 0;
+ }
+
+ return hb_blob_create(const_cast<char*>(buffer), tableSize,
+ HB_MEMORY_MODE_WRITABLE, buffer, free);
+}
+
+static void destroyHarfBuzzFontData(void* data) {
+ delete (HarfBuzzFontData*)data;
+}
+
+hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) {
+ hb_font_t* font = hb_font_create(face);
+
+ // Note: this needs to be reworked when we do subpixels
+ int x_ppem = floor(sizeX + 0.5);
+ int y_ppem = floor(sizeY + 0.5);
+ hb_font_set_ppem(font, x_ppem, y_ppem);
+ hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY));
+
+ HarfBuzzFontData* data = new HarfBuzzFontData(paint);
+ hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData);
+
+ return font;
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
similarity index 71%
rename from core/jni/android/graphics/HarfbuzzSkia.h
rename to core/jni/android/graphics/HarfBuzzNGFaceSkia.h
index 2772f4d..23ec81a 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.h
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.h
@@ -24,31 +24,35 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef HarfbuzzSkia_h
-#define HarfbuzzSkia_h
+#ifndef HarfBuzzNGFaceSkia_h
+#define HarfBuzzNGFaceSkia_h
-#include "SkScalar.h"
-#include "SkTypeface.h"
-#include "SkPaint.h"
+#include <SkScalar.h>
+#include <SkPaint.h>
-extern "C" {
-#include "harfbuzz-shaper.h"
-}
+#include <hb.h>
namespace android {
-static inline float HBFixedToFloat(HB_Fixed v) {
- // Harfbuzz uses 26.6 fixed point values for pixel offsets
- return v * (1.0f / 64);
+static inline float
+HBFixedToFloat (hb_position_t v)
+{
+ return scalblnf (v, -8);
}
-static inline HB_Fixed SkScalarToHBFixed(SkScalar value) {
- // HB_Fixed is a 26.6 fixed point format.
- return SkScalarToFloat(value) * 64.0f;
+static inline hb_position_t
+HBFloatToFixed (float v)
+{
+ return scalblnf (v, +8);
}
-HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
-extern const HB_FontClass harfbuzzSkiaClass;
+static inline hb_position_t SkScalarToHBFixed(SkScalar value) {
+ return HBFloatToFixed(SkScalarToFloat(value));
+}
+
+hb_blob_t* harfbuzzSkiaReferenceTable(hb_face_t* face, hb_tag_t tag, void* userData);
+
+hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY);
} // namespace android
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
deleted file mode 100644
index 7e08379..0000000
--- a/core/jni/android/graphics/HarfbuzzSkia.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 2011, The Android Open Source Project
- * Copyright 2011, Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define LOG_TAG "HarfbuzzSkia"
-
-#include "HarfbuzzSkia.h"
-
-#include "SkFontHost.h"
-
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkPoint.h"
-#include "SkRect.h"
-#include "SkTypeface.h"
-
-#include <utils/Log.h>
-
-extern "C" {
-#include "harfbuzz-shaper.h"
-}
-
-// This file implements the callbacks which Harfbuzz requires by using Skia
-// calls. See the Harfbuzz source for references about what these callbacks do.
-
-namespace android {
-
-static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
- HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
- uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
- int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
-
- // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
- // |glyphs| array needs to be converted.
- for (int i = numGlyphs - 1; i >= 0; --i) {
- glyphs[i] = skiaGlyphs[i];
- }
-
- *glyphsSize = numGlyphs;
- return 1;
-}
-
-static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
- HB_Fixed* advances, int flags)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- uint16_t* glyphs16 = new uint16_t[numGlyphs];
- if (!glyphs16)
- return;
- for (unsigned i = 0; i < numGlyphs; ++i)
- glyphs16[i] = glyphs[i];
- SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
- paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
-
- // The |advances| values which Skia outputs are SkScalars, which are floats
- // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
- // These two formats are both 32-bits long.
- for (unsigned i = 0; i < numGlyphs; ++i) {
- advances[i] = SkScalarToHBFixed(scalarAdvances[i]);
-#if DEBUG_ADVANCES
- ALOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]);
-#endif
- }
- delete glyphs16;
-}
-
-static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
- uint16_t* glyphs16 = new uint16_t[length];
- int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
-
- bool result = true;
- for (int i = 0; i < numGlyphs; ++i) {
- if (!glyphs16[i]) {
- result = false;
- break;
- }
- }
- delete glyphs16;
- return result;
-}
-
-static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
- HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
-{
- if (flags & HB_ShaperFlag_UseDesignMetrics)
- // This is requesting pre-hinted positions. We can't support this.
- return HB_Err_Invalid_Argument;
-
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- uint16_t glyph16 = glyph;
- SkPath path;
- paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
- uint32_t numPoints = path.getPoints(0, 0);
- if (point >= numPoints)
- return HB_Err_Invalid_SubTable;
- SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
- if (!points)
- return HB_Err_Invalid_SubTable;
- // Skia does let us get a single point from the path.
- path.getPoints(points, point + 1);
- *xPos = SkScalarToHBFixed(points[point].fX);
- *yPos = SkScalarToHBFixed(points[point].fY);
- *resultingNumPoints = numPoints;
- delete points;
-
- return HB_Err_Ok;
-}
-
-static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- uint16_t glyph16 = glyph;
- SkScalar width;
- SkRect bounds;
- paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
-
- metrics->x = SkScalarToHBFixed(bounds.fLeft);
- metrics->y = SkScalarToHBFixed(bounds.fTop);
- metrics->width = SkScalarToHBFixed(bounds.width());
- metrics->height = SkScalarToHBFixed(bounds.height());
-
- metrics->xOffset = SkScalarToHBFixed(width);
- // We can't actually get the |y| correct because Skia doesn't export
- // the vertical advance. However, nor we do ever render vertical text at
- // the moment so it's unimportant.
- metrics->yOffset = 0;
-}
-
-static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
-{
- SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
-
- SkPaint::FontMetrics skiaMetrics;
- paint->getFontMetrics(&skiaMetrics);
-
- switch (metric) {
- case HB_FontAscent:
- return SkScalarToHBFixed(-skiaMetrics.fAscent);
- // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
- default:
- return 0;
- }
- return 0;
-}
-
-const HB_FontClass harfbuzzSkiaClass = {
- stringToGlyphs,
- glyphsToAdvances,
- canRender,
- getOutlinePoint,
- getGlyphMetrics,
- getFontMetric,
-};
-
-HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
-{
- SkTypeface* typeface = static_cast<SkTypeface*>(font);
-
- if (!typeface) {
- ALOGD("Typeface cannot be null");
- return HB_Err_Invalid_Argument;
- }
- const size_t tableSize = SkFontHost::GetTableSize(typeface->uniqueID(), tag);
- if (!tableSize)
- return HB_Err_Invalid_Argument;
- // If Harfbuzz specified a NULL buffer then it's asking for the size of the table.
- if (!buffer) {
- *len = tableSize;
- return HB_Err_Ok;
- }
-
- if (*len < tableSize)
- return HB_Err_Invalid_Argument;
- SkFontHost::GetTableData(typeface->uniqueID(), tag, 0, tableSize, buffer);
- return HB_Err_Ok;
-}
-
-} // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index dce48a3..d7df402 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -22,13 +22,10 @@
#include "TextLayout.h"
#include "SkFontHost.h"
#include "SkTypeface_android.h"
+#include "HarfBuzzNGFaceSkia.h"
#include <unicode/unistr.h>
-#include <unicode/normlzr.h>
#include <unicode/uchar.h>
-
-extern "C" {
- #include "harfbuzz-unicode.h"
-}
+#include <hb-icu.h>
namespace android {
@@ -341,20 +338,10 @@
return mElapsedTime;
}
-TextLayoutShaper::TextLayoutShaper() : mShaperItemGlyphArraySize(0) {
+TextLayoutShaper::TextLayoutShaper() {
init();
- mFontRec.klass = &harfbuzzSkiaClass;
- mFontRec.userData = 0;
-
- // Note that the scaling values (x_ and y_ppem, x_ and y_scale) will be set
- // below, when the paint transform and em unit of the actual shaping font
- // are known.
-
- memset(&mShaperItem, 0, sizeof(mShaperItem));
-
- mShaperItem.font = &mFontRec;
- mShaperItem.font->userData = &mShapingPaint;
+ mBuffer = hb_buffer_create();
}
void TextLayoutShaper::init() {
@@ -366,8 +353,9 @@
}
TextLayoutShaper::~TextLayoutShaper() {
+ hb_buffer_destroy(mBuffer);
+
unrefTypefaces();
- deleteShaperItemGlyphArrays();
}
void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
@@ -394,7 +382,7 @@
bool forceLTR = false;
bool forceRTL = false;
- switch (dirFlags) {
+ switch (dirFlags & kBidi_Mask) {
case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
@@ -478,7 +466,7 @@
ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
i, startRun, lengthRun, isRTL);
#endif
- computeRunValues(paint, chars + startRun, lengthRun, isRTL,
+ computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
outAdvances, outTotalAdvance, outGlyphs, outPos);
}
@@ -502,7 +490,7 @@
ALOGD("Using a SINGLE BiDi Run "
"-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
#endif
- computeRunValues(paint, chars + start, count, isRTL,
+ computeRunValues(paint, chars, start, count, contextCount, isRTL,
outAdvances, outTotalAdvance, outGlyphs, outPos);
}
@@ -512,18 +500,197 @@
#endif
}
-static void logGlyphs(HB_ShaperItem shaperItem) {
- ALOGD(" -- glyphs count=%d", shaperItem.num_glyphs);
- for (size_t i = 0; i < shaperItem.num_glyphs; i++) {
- ALOGD(" -- glyph[%d] = %d, offset.x = %0.2f, offset.y = %0.2f", i,
- shaperItem.glyphs[i],
- HBFixedToFloat(shaperItem.offsets[i].x),
- HBFixedToFloat(shaperItem.offsets[i].y));
+#define HB_IsHighSurrogate(ucs) \
+ (((ucs) & 0xfc00) == 0xd800)
+
+#define HB_IsLowSurrogate(ucs) \
+ (((ucs) & 0xfc00) == 0xdc00)
+
+#ifndef HB_SurrogateToUcs4
+#define HB_SurrogateToUcs4_(high, low) \
+ (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
+#endif
+
+#define HB_InvalidCodePoint ~0u
+
+hb_codepoint_t
+utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
+ const uint16_t v = chars[(*iter)++];
+ if (HB_IsHighSurrogate(v)) {
+ // surrogate pair
+ if (size_t(*iter) >= len) {
+ // the surrogate is incomplete.
+ return HB_InvalidCodePoint;
+ }
+ const uint16_t v2 = chars[(*iter)++];
+ if (!HB_IsLowSurrogate(v2)) {
+ // invalidate surrogate pair.
+ (*iter)--;
+ return HB_InvalidCodePoint;
+ }
+
+ return HB_SurrogateToUcs4(v, v2);
+ }
+
+ if (HB_IsLowSurrogate(v)) {
+ // this isn't a valid code point
+ return HB_InvalidCodePoint;
+ }
+
+ return v;
+}
+
+hb_codepoint_t
+utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
+ const uint16_t v = chars[(*iter)--];
+ if (HB_IsLowSurrogate(v)) {
+ // surrogate pair
+ if (*iter < 0) {
+ // the surrogate is incomplete.
+ return HB_InvalidCodePoint;
+ }
+ const uint16_t v2 = chars[(*iter)--];
+ if (!HB_IsHighSurrogate(v2)) {
+ // invalidate surrogate pair.
+ (*iter)++;
+ return HB_InvalidCodePoint;
+ }
+
+ return HB_SurrogateToUcs4(v2, v);
+ }
+
+ if (HB_IsHighSurrogate(v)) {
+ // this isn't a valid code point
+ return HB_InvalidCodePoint;
+ }
+
+ return v;
+}
+
+struct ScriptRun {
+ hb_script_t script;
+ size_t pos;
+ size_t length;
+};
+
+hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
+ static hb_unicode_funcs_t* u;
+ if (!u) {
+ u = hb_icu_get_unicode_funcs();
+ }
+ return hb_unicode_script(u, codepoint);
+}
+
+bool
+hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
+ if (size_t(*iter) == len)
+ return false;
+
+ run->pos = *iter;
+ const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
+ const hb_script_t init_script = code_point_to_script(init_cp);
+ hb_script_t current_script = init_script;
+ run->script = init_script;
+
+ for (;;) {
+ if (size_t(*iter) == len)
+ break;
+ const ssize_t prev_iter = *iter;
+ const uint32_t cp = utf16_to_code_point(chars, len, iter);
+ const hb_script_t script = code_point_to_script(cp);
+
+ if (script != current_script) {
+ /* BEGIN android-changed
+ The condition was not correct by doing "a == b == constant"
+ END android-changed */
+ if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
+ // If we started off as inherited, we take whatever we can find.
+ run->script = script;
+ current_script = script;
+ continue;
+ } else if (script == HB_SCRIPT_INHERITED) {
+ continue;
+ } else {
+ *iter = prev_iter;
+ break;
+ }
+ }
+ }
+
+ if (run->script == HB_SCRIPT_INHERITED)
+ run->script = HB_SCRIPT_COMMON;
+
+ run->length = *iter - run->pos;
+ return true;
+}
+
+bool
+hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
+ if (*iter == -1)
+ return false;
+
+ const size_t ending_index = *iter;
+ const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
+ const hb_script_t init_script = code_point_to_script(init_cp);
+ hb_script_t current_script = init_script;
+ run->script = init_script;
+
+ for (;;) {
+ if (*iter < 0)
+ break;
+ const ssize_t prev_iter = *iter;
+ const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
+ const hb_script_t script = code_point_to_script(cp);
+
+ if (script != current_script) {
+ if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
+ // If we started off as inherited, we take whatever we can find.
+ run->script = script;
+ current_script = script;
+ continue;
+ } else if (script == HB_SCRIPT_INHERITED) {
+ /* BEGIN android-changed
+ We apply the same fix for Chrome to Android.
+ Chrome team will talk with upsteam about it.
+ Just assume that whatever follows this combining character is within
+ the same script. This is incorrect if you had language1 + combining
+ char + language 2, but that is rare and this code is suspicious
+ anyway.
+ END android-changed */
+ continue;
+ } else {
+ *iter = prev_iter;
+ break;
+ }
+ }
+ }
+
+ if (run->script == HB_SCRIPT_INHERITED)
+ run->script = HB_SCRIPT_COMMON;
+
+ run->pos = *iter + 1;
+ run->length = ending_index - *iter;
+ return true;
+}
+
+
+static void logGlyphs(hb_buffer_t* buffer) {
+ unsigned int numGlyphs;
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
+ hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
+ ALOGD(" -- glyphs count=%d", numGlyphs);
+ for (size_t i = 0; i < numGlyphs; i++) {
+ ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
+ info[i].codepoint,
+ info[i].cluster,
+ HBFixedToFloat(positions[i].x_advance),
+ HBFixedToFloat(positions[i].x_offset),
+ HBFixedToFloat(positions[i].y_offset));
}
}
-void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* chars,
- size_t count, bool isRTL,
+void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
+ size_t start, size_t count, size_t contextCount, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
if (!count) {
@@ -535,95 +702,9 @@
for (size_t i = 0; i < count; i++) {
outAdvances->add(0);
}
- UErrorCode error = U_ZERO_ERROR;
- bool useNormalizedString = false;
- for (ssize_t i = count - 1; i >= 0; --i) {
- UChar ch1 = chars[i];
- if (::ublock_getCode(ch1) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
- // So we have found a diacritic, let's get now the main code point which is paired
- // with it. As we can have several diacritics in a row, we need to iterate back again
-#if DEBUG_GLYPHS
- ALOGD("The BiDi run '%s' is containing a Diacritic at position %d",
- String8(chars, count).string(), int(i));
-#endif
- ssize_t j = i - 1;
- for (; j >= 0; --j) {
- UChar ch2 = chars[j];
- if (::ublock_getCode(ch2) != UBLOCK_COMBINING_DIACRITICAL_MARKS) {
- break;
- }
- }
-
- // We could not found the main code point, so we will just use the initial chars
- if (j < 0) {
- break;
- }
-
-#if DEBUG_GLYPHS
- ALOGD("Found main code point at index %d", int(j));
-#endif
- // We found the main code point, so we can normalize the "chunk" and fill
- // the remaining with ZWSP so that the Paint.getTextWidth() APIs will still be able
- // to get one advance per char
- mBuffer.remove();
- Normalizer::normalize(UnicodeString(chars + j, i - j + 1),
- UNORM_NFC, 0 /* no options */, mBuffer, error);
- if (U_SUCCESS(error)) {
- if (!useNormalizedString) {
- useNormalizedString = true;
- mNormalizedString.setTo(false /* not terminated*/, chars, count);
- }
- // Set the normalized chars
- for (ssize_t k = j; k < j + mBuffer.length(); ++k) {
- mNormalizedString.setCharAt(k, mBuffer.charAt(k - j));
- }
- // Fill the remain part with ZWSP (ZWNJ and ZWJ would lead to weird results
- // because some fonts are missing those glyphs)
- for (ssize_t k = j + mBuffer.length(); k <= i; ++k) {
- mNormalizedString.setCharAt(k, UNICODE_ZWSP);
- }
- }
- i = j - 1;
- }
- }
-
- // Reverse "BiDi mirrored chars" in RTL mode only
- // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
- // This is a workaround because Harfbuzz is not able to do mirroring in all cases and
- // script-run splitting with Harfbuzz is splitting on parenthesis
- if (isRTL) {
- for (ssize_t i = 0; i < ssize_t(count); i++) {
- UChar32 ch = chars[i];
- if (!u_isMirrored(ch)) continue;
- if (!useNormalizedString) {
- useNormalizedString = true;
- mNormalizedString.setTo(false /* not terminated*/, chars, count);
- }
- UChar result = (UChar) u_charMirror(ch);
- mNormalizedString.setCharAt(i, result);
-#if DEBUG_GLYPHS
- ALOGD("Rewriting codepoint '%d' to '%d' at position %d",
- ch, mNormalizedString[i], int(i));
-#endif
- }
- }
-
-#if DEBUG_GLYPHS
- if (useNormalizedString) {
- ALOGD("Will use normalized string '%s', length = %d",
- String8(mNormalizedString.getTerminatedBuffer(),
- mNormalizedString.length()).string(),
- mNormalizedString.length());
- } else {
- ALOGD("Normalization is not needed or cannot be done, using initial string");
- }
-#endif
-
- assert(mNormalizedString.length() == count);
// Set the string properties
- mShaperItem.string = useNormalizedString ? mNormalizedString.getTerminatedBuffer() : chars;
- mShaperItem.stringLength = count;
+ const UChar* chars = contextChars + start;
// Define shaping paint properties
mShapingPaint.setTextSize(paint->getTextSize());
@@ -637,130 +718,66 @@
// Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
// into the shaperItem
- ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0;
- unsigned numCodePoints = 0;
+ ssize_t indexFontRun = isRTL ? count - 1 : 0;
jfloat totalAdvance = *outTotalAdvance;
+ ScriptRun run; // relative to chars
while ((isRTL) ?
- hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string,
- mShaperItem.stringLength, &indexFontRun):
- hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, mShaperItem.string,
- mShaperItem.stringLength, &indexFontRun)) {
-
- ssize_t startScriptRun = mShaperItem.item.pos;
- size_t countScriptRun = mShaperItem.item.length;
- ssize_t endScriptRun = startScriptRun + countScriptRun;
+ hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
+ hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
#if DEBUG_GLYPHS
ALOGD("-------- Start of Script Run --------");
ALOGD("Shaping Script Run with");
ALOGD(" -- isRTL = %d", isRTL);
- ALOGD(" -- HB script = %d", mShaperItem.item.script);
- ALOGD(" -- startFontRun = %d", int(startScriptRun));
- ALOGD(" -- endFontRun = %d", int(endScriptRun));
- ALOGD(" -- countFontRun = %d", countScriptRun);
- ALOGD(" -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string());
+ ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script));
+ ALOGD(" -- run.pos = %d", int(run.pos));
+ ALOGD(" -- run.length = %d", int(run.length));
+ ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string());
ALOGD(" -- string = '%s'", String8(chars, count).string());
#endif
+ hb_buffer_reset(mBuffer);
+ // Note: if we want to set unicode functions, etc., this is the place.
+
+ hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
+ hb_buffer_set_script(mBuffer, run.script);
+ // Should set language here (for bug 7004056)
+ hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
+
// Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
// and shape the Font run
- size_t glyphBaseCount = shapeFontRun(paint, isRTL);
+ size_t glyphBaseCount = shapeFontRun(paint);
+ unsigned int numGlyphs;
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
+ hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
#if DEBUG_GLYPHS
ALOGD("Got from Harfbuzz");
ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
- ALOGD(" -- num_glypth = %d", mShaperItem.num_glyphs);
- ALOGD(" -- kerning_applied = %d", mShaperItem.kerning_applied);
+ ALOGD(" -- num_glyph = %d", numGlyphs);
ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
+ ALOGD(" -- initial totalAdvance = %f", totalAdvance);
- logGlyphs(mShaperItem);
+ logGlyphs(mBuffer);
#endif
- if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
-#if DEBUG_GLYPHS
- ALOGD("Advances array is empty or num_glypth = 0");
-#endif
- continue;
+ for (size_t i = 0; i < numGlyphs; i++) {
+ size_t cluster = info[i].cluster - start;
+ float xAdvance = HBFixedToFloat(positions[i].x_advance);
+ outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
+ outGlyphs->add(info[i].codepoint + glyphBaseCount);
+ float xo = HBFixedToFloat(positions[i].x_offset);
+ float yo = -HBFixedToFloat(positions[i].y_offset);
+ outPos->add(totalAdvance + xo + yo * skewX);
+ outPos->add(yo);
+ totalAdvance += xAdvance;
}
-
-#if DEBUG_GLYPHS
- ALOGD("Returned logclusters");
- for (size_t i = 0; i < mShaperItem.num_glyphs; i++) {
- ALOGD(" -- lc[%d] = %d, hb-adv[%d] = %0.2f", i, mShaperItem.log_clusters[i],
- i, HBFixedToFloat(mShaperItem.advances[i]));
- }
-#endif
- jfloat totalFontRunAdvance = 0;
- size_t clusterStart = 0;
- for (size_t i = 0; i < countScriptRun; i++) {
- size_t cluster = mShaperItem.log_clusters[i];
- size_t clusterNext = i == countScriptRun - 1 ? mShaperItem.num_glyphs :
- mShaperItem.log_clusters[i + 1];
- if (cluster != clusterNext) {
- jfloat advance = 0;
- // The advance for the cluster is the sum of the advances of all glyphs within
- // the cluster.
- for (size_t j = cluster; j < clusterNext; j++) {
- advance += HBFixedToFloat(mShaperItem.advances[j]);
- }
- totalFontRunAdvance += advance;
- outAdvances->replaceAt(advance, startScriptRun + clusterStart);
- clusterStart = i + 1;
- }
- }
-
-#if DEBUG_ADVANCES
- ALOGD("Returned advances");
- for (size_t i = 0; i < countScriptRun; i++) {
- ALOGD(" -- hb-adv[%d] = %0.2f, log_clusters = %d, total = %0.2f", i,
- (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance);
- }
-#endif
-
- // Get Glyphs and reverse them in place if RTL
- if (outGlyphs) {
- size_t countGlyphs = mShaperItem.num_glyphs;
-#if DEBUG_GLYPHS
- ALOGD("Returned script run glyphs -- count = %d", countGlyphs);
-#endif
- for (size_t i = 0; i < countGlyphs; i++) {
- jchar glyph = glyphBaseCount +
- (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
-#if DEBUG_GLYPHS
- ALOGD(" -- glyph[%d] = %d", i, glyph);
-#endif
- outGlyphs->add(glyph);
- }
- }
-
- // Get glyph positions (and reverse them in place if RTL)
- if (outPos) {
- size_t countGlyphs = mShaperItem.num_glyphs;
- jfloat x = totalAdvance;
- for (size_t i = 0; i < countGlyphs; i++) {
- size_t index = (!isRTL) ? i : countGlyphs - 1 - i;
- float xo = HBFixedToFloat(mShaperItem.offsets[index].x);
- float yo = HBFixedToFloat(mShaperItem.offsets[index].y);
- // Apply skewX component of transform to position offsets. Note
- // that scale has already been applied through x_ and y_scale
- // set in the mFontRec.
- outPos->add(x + xo + yo * skewX);
- outPos->add(yo);
-#if DEBUG_GLYPHS
- ALOGD(" -- hb adv[%d] = %f, log_cluster[%d] = %d",
- index, HBFixedToFloat(mShaperItem.advances[index]),
- index, mShaperItem.log_clusters[index]);
-#endif
- x += HBFixedToFloat(mShaperItem.advances[index]);
- }
- }
-
- totalAdvance += totalFontRunAdvance;
}
*outTotalAdvance = totalAdvance;
#if DEBUG_GLYPHS
+ ALOGD(" -- final totalAdvance = %f", totalAdvance);
ALOGD("-------- End of Script Run --------");
#endif
}
@@ -774,37 +791,33 @@
* for the default font live in a global cache.
*/
SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
- HB_Script script) {
+ hb_script_t script) {
SkTypeface::Style currentStyle = SkTypeface::kNormal;
if (typeface) {
currentStyle = typeface->style();
}
- typeface = SkCreateTypefaceForScript(script, currentStyle);
+ typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
#if DEBUG_GLYPHS
ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle);
#endif
return typeface;
}
-bool TextLayoutShaper::isComplexScript(HB_Script script) {
+bool TextLayoutShaper::isComplexScript(hb_script_t script) {
switch (script) {
- case HB_Script_Common:
- case HB_Script_Greek:
- case HB_Script_Cyrillic:
- case HB_Script_Hangul:
- case HB_Script_Inherited:
+ case HB_SCRIPT_COMMON:
+ case HB_SCRIPT_GREEK:
+ case HB_SCRIPT_CYRILLIC:
+ case HB_SCRIPT_HANGUL:
+ case HB_SCRIPT_INHERITED:
return false;
default:
return true;
}
}
-size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint, bool isRTL) {
- // Reset kerning
- mShaperItem.kerning_applied = false;
-
+size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
// Update Harfbuzz Shaper
- mShaperItem.item.bidiLevel = isRTL;
SkTypeface* typeface = paint->getTypeface();
@@ -813,19 +826,21 @@
// when we are shaping any script that needs to use a fallback Font.
// If we are a "common" script we dont need to shift
size_t baseGlyphCount = 0;
- SkUnichar firstUnichar = 0;
- if (isComplexScript(mShaperItem.item.script)) {
- const uint16_t* text16 = (const uint16_t*) (mShaperItem.string + mShaperItem.item.pos);
- const uint16_t* text16End = text16 + mShaperItem.item.length;
- firstUnichar = SkUTF16_NextUnichar(&text16);
- while (firstUnichar == ' ' && text16 < text16End) {
- firstUnichar = SkUTF16_NextUnichar(&text16);
+ hb_codepoint_t firstUnichar = 0;
+ if (isComplexScript(hb_buffer_get_script(mBuffer))) {
+ unsigned int numGlyphs;
+ hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
+ for (size_t i = 0; i < numGlyphs; i++) {
+ firstUnichar = info[i].codepoint;
+ if (firstUnichar != ' ') {
+ break;
+ }
}
baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
}
if (baseGlyphCount != 0) {
- typeface = typefaceForScript(paint, typeface, mShaperItem.item.script);
+ typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
if (!typeface) {
typeface = mDefaultTypeface;
SkSafeRef(typeface);
@@ -844,94 +859,44 @@
}
mShapingPaint.setTypeface(typeface);
- mShaperItem.face = getCachedHBFace(typeface);
+ hb_face_t* face = referenceCachedHBFace(typeface);
- int textSize = paint->getTextSize();
- float scaleX = paint->getTextScaleX();
- mFontRec.x_ppem = floor(scaleX * textSize + 0.5);
- mFontRec.y_ppem = textSize;
- uint32_t unitsPerEm = SkFontHost::GetUnitsPerEm(typeface->uniqueID());
- // x_ and y_scale are the conversion factors from font design space
- // (unitsPerEm) to 1/64th of device pixels in 16.16 format.
- const int kDevicePixelFraction = 64;
- const int kMultiplyFor16Dot16 = 1 << 16;
- float emScale = kDevicePixelFraction * kMultiplyFor16Dot16 / (float)unitsPerEm;
- mFontRec.x_scale = emScale * scaleX * textSize;
- mFontRec.y_scale = emScale * textSize;
+ float sizeY = paint->getTextSize();
+ float sizeX = sizeY * paint->getTextScaleX();
+ hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
+ hb_face_destroy(face);
#if DEBUG_GLYPHS
- ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
- typeface, typeface->uniqueID(), mShaperItem.face);
+ ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
+ typeface, typeface->uniqueID(), face);
#endif
SkSafeUnref(typeface);
- // Shape
- assert(mShaperItem.item.length > 0); // Harfbuzz will overwrite other memory if length is 0.
- size_t size = mShaperItem.item.length * 3 / 2;
- while (!doShaping(size)) {
- // We overflowed our glyph arrays. Resize and retry.
- // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
- size = mShaperItem.num_glyphs * 2;
- }
+ hb_shape(font, mBuffer, NULL, 0);
+ hb_font_destroy(font);
+
return baseGlyphCount;
}
-bool TextLayoutShaper::doShaping(size_t size) {
- if (size > mShaperItemGlyphArraySize) {
- deleteShaperItemGlyphArrays();
- createShaperItemGlyphArrays(size);
- }
- mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
- memset(mShaperItem.offsets, 0, mShaperItem.num_glyphs * sizeof(HB_FixedPoint));
- return HB_ShapeItem(&mShaperItem);
-}
-
-void TextLayoutShaper::createShaperItemGlyphArrays(size_t size) {
-#if DEBUG_GLYPHS
- ALOGD("Creating Glyph Arrays with size = %d", size);
-#endif
- mShaperItemGlyphArraySize = size;
-
- // These arrays are all indexed by glyph.
- mShaperItem.glyphs = new HB_Glyph[size];
- mShaperItem.attributes = new HB_GlyphAttributes[size];
- mShaperItem.advances = new HB_Fixed[size];
- mShaperItem.offsets = new HB_FixedPoint[size];
-
- // Although the log_clusters array is indexed by character, Harfbuzz expects that
- // it is big enough to hold one element per glyph. So we allocate log_clusters along
- // with the other glyph arrays above.
- mShaperItem.log_clusters = new unsigned short[size];
-}
-
-void TextLayoutShaper::deleteShaperItemGlyphArrays() {
- delete[] mShaperItem.glyphs;
- delete[] mShaperItem.attributes;
- delete[] mShaperItem.advances;
- delete[] mShaperItem.offsets;
- delete[] mShaperItem.log_clusters;
-}
-
-HB_Face TextLayoutShaper::getCachedHBFace(SkTypeface* typeface) {
+hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
SkFontID fontId = typeface->uniqueID();
ssize_t index = mCachedHBFaces.indexOfKey(fontId);
if (index >= 0) {
- return mCachedHBFaces.valueAt(index);
+ return hb_face_reference(mCachedHBFaces.valueAt(index));
}
- HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
- if (face) {
+ // TODO: destroy function
+ hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
#if DEBUG_GLYPHS
- ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
+ ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
#endif
- mCachedHBFaces.add(fontId, face);
- }
- return face;
+ mCachedHBFaces.add(fontId, face);
+ return hb_face_reference(face);
}
void TextLayoutShaper::purgeCaches() {
size_t cacheSize = mCachedHBFaces.size();
for (size_t i = 0; i < cacheSize; i++) {
- HB_FreeFace(mCachedHBFaces.valueAt(i));
+ hb_face_destroy(mCachedHBFaces.valueAt(i));
}
mCachedHBFaces.clear();
unrefTypefaces();
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 22de523..06bb941 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -35,11 +35,9 @@
#include <SkLanguage.h>
#include <unicode/ubidi.h>
-#include <unicode/ushape.h>
#include <unicode/unistr.h>
-#include "HarfbuzzSkia.h"
-#include "harfbuzz-shaper.h"
+#include <hb.h>
#include <android_runtime/AndroidRuntime.h>
@@ -189,14 +187,9 @@
private:
/**
- * Harfbuzz shaper item
+ * Harfbuzz buffer for shaping
*/
- HB_ShaperItem mShaperItem;
-
- /**
- * Harfbuzz font
- */
- HB_FontRec mFontRec;
+ hb_buffer_t* mBuffer;
/**
* Skia Paint used for shaping
@@ -211,30 +204,15 @@
/**
* Cache of Harfbuzz faces
*/
- KeyedVector<SkFontID, HB_Face> mCachedHBFaces;
-
- /**
- * Cache of glyph array size
- */
- size_t mShaperItemGlyphArraySize;
-
- /**
- * Buffer for containing the ICU normalized form of a run
- */
- UnicodeString mNormalizedString;
-
- /**
- * Buffer for normalizing a piece of a run with ICU
- */
- UnicodeString mBuffer;
+ KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces;
void init();
void unrefTypefaces();
SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
- HB_Script script);
+ hb_script_t script);
- size_t shapeFontRun(const SkPaint* paint, bool isRTL);
+ size_t shapeFontRun(const SkPaint* paint);
void computeValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
@@ -242,17 +220,14 @@
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
void computeRunValues(const SkPaint* paint, const UChar* chars,
- size_t count, bool isRTL,
+ size_t start, size_t count, size_t contextCount, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos);
- SkTypeface* getCachedTypeface(SkTypeface** typeface, HB_Script script, SkTypeface::Style style);
- HB_Face getCachedHBFace(SkTypeface* typeface);
+ SkTypeface* setCachedTypeface(SkTypeface** typeface, hb_script_t script, SkTypeface::Style style);
+ hb_face_t* referenceCachedHBFace(SkTypeface* typeface);
- bool doShaping(size_t size);
- void createShaperItemGlyphArrays(size_t size);
- void deleteShaperItemGlyphArrays();
- bool isComplexScript(HB_Script script);
+ bool isComplexScript(hb_script_t script);
}; // TextLayoutShaper
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 7d886da..de14826 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -275,6 +275,16 @@
return renderer->clipRect(float(left), float(top), float(right), float(bottom), op);
}
+static bool android_view_GLES20Canvas_clipPath(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer, SkPath* path, SkRegion::Op op) {
+ return renderer->clipPath(path, op);
+}
+
+static bool android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer, SkRegion* region, SkRegion::Op op) {
+ return renderer->clipRegion(region, op);
+}
+
static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, jobject rect) {
const android::uirenderer::Rect& bounds(renderer->getClipBounds());
@@ -961,6 +971,8 @@
{ "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
{ "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
{ "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
+ { "nClipPath", "(III)Z", (void*) android_view_GLES20Canvas_clipPath },
+ { "nClipRegion", "(III)Z", (void*) android_view_GLES20Canvas_clipRegion },
{ "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
{ "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index a6057de..649e39d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -28,6 +28,7 @@
import android.net.wifi.WifiConfiguration.IpAssignment;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.wifi.WifiEnterpriseConfig;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
@@ -67,7 +68,6 @@
* networkprefixlength.
*/
public class AccessPointParserHelper {
- private static final String KEYSTORE_SPACE = "keystore://";
private static final String TAG = "AccessPointParserHelper";
static final int NONE = 0;
static final int WEP = 1;
@@ -212,14 +212,11 @@
config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
// Initialize other fields.
- config.phase2.setValue("");
- config.ca_cert.setValue("");
- config.client_cert.setValue("");
- config.engine.setValue("");
- config.engine_id.setValue("");
- config.key_id.setValue("");
- config.identity.setValue("");
- config.anonymous_identity.setValue("");
+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE);
+ config.enterpriseConfig.setCaCertificate("");
+ config.enterpriseConfig.setClientCertificate("");
+ config.enterpriseConfig.setIdentity("");
+ config.enterpriseConfig.setAnonymousIdentity("");
break;
default:
throw new SAXException();
@@ -246,7 +243,7 @@
config.preSharedKey = '"' + passwordStr + '"';
}
} else if (securityType == EAP) {
- config.password.setValue(passwordStr);
+ config.enterpriseConfig.setPassword(passwordStr);
} else {
throw new SAXException();
}
@@ -257,33 +254,46 @@
if (!validateEapValue(eapValue)) {
throw new SAXException();
}
- config.eap.setValue(eapValue);
+ if (eapValue.equals("TLS")) {
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ } else if (eapValue.equals("TTLS")) {
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ } else if (eapValue.equals("PEAP")) {
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.PEAP);
+ }
eap = false;
}
if (phase2) {
String phase2Value = new String(ch, start, length);
- config.phase2.setValue("auth=" + phase2Value);
+ if (phase2Value.equals("PAP")) {
+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
+ } else if (phase2Value.equals("MSCHAP")) {
+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAP);
+ } else if (phase2Value.equals("MSCHAPV2")) {
+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAPV2);
+ } else if (phase2Value.equals("GTC")) {
+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+ }
phase2 = false;
}
if (identity) {
String identityValue = new String(ch, start, length);
- config.identity.setValue(identityValue);
+ config.enterpriseConfig.setIdentity(identityValue);
identity = false;
}
if (anonymousidentity) {
String anonyId = new String(ch, start, length);
- config.anonymous_identity.setValue(anonyId);
+ config.enterpriseConfig.setAnonymousIdentity(anonyId);
anonymousidentity = false;
}
if (cacert) {
String cacertValue = new String(ch, start, length);
- // need to install the credentail to "keystore://"
- config.ca_cert.setValue(KEYSTORE_SPACE);
+ config.enterpriseConfig.setCaCertificate(cacertValue);
cacert = false;
}
if (usercert) {
String usercertValue = new String(ch, start, length);
- config.client_cert.setValue(KEYSTORE_SPACE);
+ config.enterpriseConfig.setClientCertificate(usercertValue);
usercert = false;
}
if (ip) {
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 6795ac3..dfc4e25c 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -35,6 +35,9 @@
// Turn on to enable layers debugging when rendered as regions
#define DEBUG_LAYERS_AS_REGIONS 0
+// Turn on to enable debugging when the clip is not a rect
+#define DEBUG_CLIP_REGIONS 0
+
// Turn on to display debug info about vertex/fragment shaders
#define DEBUG_PROGRAMS 0
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 06574cd..f0c9ce4 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -44,6 +44,8 @@
"SetMatrix",
"ConcatMatrix",
"ClipRect",
+ "ClipPath",
+ "ClipRegion",
"DrawDisplayList",
"DrawLayer",
"DrawBitmap",
@@ -166,6 +168,10 @@
delete mPaints.itemAt(i);
}
+ for (size_t i = 0; i < mRegions.size(); i++) {
+ delete mRegions.itemAt(i);
+ }
+
for (size_t i = 0; i < mPaths.size(); i++) {
SkPath* path = mPaths.itemAt(i);
caches.pathCache.remove(path);
@@ -182,6 +188,7 @@
mShaders.clear();
mSourcePaths.clear();
mPaints.clear();
+ mRegions.clear();
mPaths.clear();
mMatrices.clear();
mLayers.clear();
@@ -259,20 +266,10 @@
caches.resourceCache.unlock();
- const Vector<SkPaint*>& paints = recorder.getPaints();
- for (size_t i = 0; i < paints.size(); i++) {
- mPaints.add(paints.itemAt(i));
- }
-
- const Vector<SkPath*>& paths = recorder.getPaths();
- for (size_t i = 0; i < paths.size(); i++) {
- mPaths.add(paths.itemAt(i));
- }
-
- const Vector<SkMatrix*>& matrices = recorder.getMatrices();
- for (size_t i = 0; i < matrices.size(); i++) {
- mMatrices.add(matrices.itemAt(i));
- }
+ mPaints.appendVector(recorder.getPaints());
+ mRegions.appendVector(recorder.getRegions());
+ mPaths.appendVector(recorder.getPaths());
+ mMatrices.appendVector(recorder.getMatrices());
}
void DisplayList::init() {
@@ -429,6 +426,18 @@
f1, f2, f3, f4, regionOp);
}
break;
+ case ClipPath: {
+ SkPath* path = getPath();
+ int regionOp = getInt();
+ ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+ }
+ break;
+ case ClipRegion: {
+ SkRegion* region = getRegion();
+ int regionOp = getInt();
+ ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+ }
+ break;
case DrawDisplayList: {
DisplayList* displayList = getDisplayList();
int32_t flags = getInt();
@@ -1031,6 +1040,20 @@
renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp);
}
break;
+ case ClipPath: {
+ SkPath* path = getPath();
+ int32_t regionOp = getInt();
+ DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+ renderer.clipPath(path, (SkRegion::Op) regionOp);
+ }
+ break;
+ case ClipRegion: {
+ SkRegion* region = getRegion();
+ int32_t regionOp = getInt();
+ DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
+ renderer.clipRegion(region, (SkRegion::Op) regionOp);
+ }
+ break;
case DrawDisplayList: {
DisplayList* displayList = getDisplayList();
int32_t flags = getInt();
@@ -1415,6 +1438,9 @@
mPaints.clear();
mPaintMap.clear();
+ mRegions.clear();
+ mRegionMap.clear();
+
mPaths.clear();
mPathMap.clear();
@@ -1571,6 +1597,20 @@
return OpenGLRenderer::clipRect(left, top, right, bottom, op);
}
+bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+ addOp(DisplayList::ClipPath);
+ addPath(path);
+ addInt(op);
+ return OpenGLRenderer::clipPath(path, op);
+}
+
+bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+ addOp(DisplayList::ClipRegion);
+ addRegion(region);
+ addInt(op);
+ return OpenGLRenderer::clipRegion(region, op);
+}
+
status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
Rect& dirty, int32_t flags, uint32_t level) {
// dirty is an out parameter and should not be recorded,
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index fb01753..f55f1f2 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -85,6 +85,8 @@
SetMatrix,
ConcatMatrix,
ClipRect,
+ ClipPath,
+ ClipRegion,
// Drawing operations
DrawDisplayList,
DrawLayer,
@@ -457,6 +459,10 @@
return (SkPath*) getInt();
}
+ SkRegion* getRegion() {
+ return (SkRegion*) getInt();
+ }
+
SkPaint* getPaint(OpenGLRenderer& renderer) {
return renderer.filterPaint((SkPaint*) getInt());
}
@@ -496,6 +502,7 @@
Vector<SkPaint*> mPaints;
Vector<SkPath*> mPaths;
SortedVector<SkPath*> mSourcePaths;
+ Vector<SkRegion*> mRegions;
Vector<SkMatrix*> mMatrices;
Vector<SkiaShader*> mShaders;
Vector<Layer*> mLayers;
@@ -577,6 +584,8 @@
virtual void concatMatrix(SkMatrix* matrix);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+ virtual bool clipPath(SkPath* path, SkRegion::Op op);
+ virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
uint32_t level = 0);
@@ -657,6 +666,10 @@
return mSourcePaths;
}
+ const Vector<SkRegion*>& getRegions() const {
+ return mRegions;
+ }
+
const Vector<Layer*>& getLayers() const {
return mLayers;
}
@@ -802,6 +815,26 @@
return paintCopy;
}
+ inline SkRegion* addRegion(SkRegion* region) {
+ if (!region) {
+ addInt((int) NULL);
+ return region;
+ }
+
+ SkRegion* regionCopy = mRegionMap.valueFor(region);
+ // TODO: Add generation ID to SkRegion
+ if (regionCopy == NULL) {
+ regionCopy = new SkRegion(*region);
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mRegionMap.replaceValueFor(region, regionCopy);
+ mRegions.add(regionCopy);
+ }
+
+ addInt((int) regionCopy);
+
+ return regionCopy;
+ }
+
inline void addDisplayList(DisplayList* displayList) {
// TODO: To be safe, the display list should be ref-counted in the
// resources cache, but we rely on the caller (UI toolkit) to
@@ -876,6 +909,9 @@
SortedVector<SkPath*> mSourcePaths;
+ Vector<SkRegion*> mRegions;
+ DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap;
+
Vector<SkiaShader*> mShaders;
DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8cda729..bb1edbb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1288,10 +1288,38 @@
bool clipped = mSnapshot->clip(left, top, right, bottom, op);
if (clipped) {
dirtyClip();
+#if DEBUG_CLIP_REGIONS
+ if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) {
+ int count = 0;
+ Vector<float> rects;
+ SkRegion::Iterator it(*mSnapshot->clipRegion);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ rects.push(r.fLeft);
+ rects.push(r.fTop);
+ rects.push(r.fRight);
+ rects.push(r.fBottom);
+ count++;
+ it.next();
+ }
+
+ drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true);
+ }
+#endif
}
return !mSnapshot->clipRect->isEmpty();
}
+bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+ const SkRect& bounds = path->getBounds();
+ return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+}
+
+bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
+ const SkIRect& bounds = region->getBounds();
+ return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+}
+
Rect* OpenGLRenderer::getClipRect() {
return mSnapshot->clipRect;
}
@@ -3046,6 +3074,19 @@
return DrawGlInfo::kStatusDone;
}
+ int color = paint->getColor();
+ // If a shader is set, preserve only the alpha
+ if (mShader) {
+ color |= 0x00ffffff;
+ }
+ SkXfermode::Mode mode = getXfermode(paint->getXfermode());
+
+ return drawColorRects(rects, count, color, mode);
+}
+
+status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
+ SkXfermode::Mode mode, bool ignoreTransform) {
+
float left = FLT_MAX;
float top = FLT_MAX;
float right = FLT_MIN;
@@ -3081,13 +3122,6 @@
if (count == 0) return DrawGlInfo::kStatusDone;
- int color = paint->getColor();
- // If a shader is set, preserve only the alpha
- if (mShader) {
- color |= 0x00ffffff;
- }
- SkXfermode::Mode mode = getXfermode(paint->getXfermode());
-
setupDraw();
setupDrawNoTexture();
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
@@ -3096,7 +3130,7 @@
setupDrawBlending(mode);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
- setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f);
+ setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true);
setupDrawColorUniforms();
setupDrawShaderUniforms();
setupDrawColorFilterUniforms();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5520edb..f07325f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -164,6 +164,8 @@
ANDROID_API bool quickReject(float left, float top, float right, float bottom);
bool quickRejectNoScissor(float left, float top, float right, float bottom);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+ virtual bool clipPath(SkPath* path, SkRegion::Op op);
+ virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
virtual Rect* getClipRect();
virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags,
@@ -498,7 +500,8 @@
/**
* Draws a colored rectangle with the specified color. The specified coordinates
- * are transformed by the current snapshot's transform matrix.
+ * are transformed by the current snapshot's transform matrix unless specified
+ * otherwise.
*
* @param left The left coordinate of the rectangle
* @param top The top coordinate of the rectangle
@@ -512,6 +515,20 @@
int color, SkXfermode::Mode mode, bool ignoreTransform = false);
/**
+ * Draws a series of colored rectangles with the specified color. The specified
+ * coordinates are transformed by the current snapshot's transform matrix unless
+ * specified otherwise.
+ *
+ * @param rects A list of rectangles, 4 floats (left, top, right, bottom)
+ * per rectangle
+ * @param color The rectangles' ARGB color, defined as a packed 32 bits word
+ * @param mode The Skia xfermode to use
+ * @param ignoreTransform True if the current transform should be ignored
+ */
+ status_t drawColorRects(const float* rects, int count, int color,
+ SkXfermode::Mode mode, bool ignoreTransform = false);
+
+ /**
* Draws the shape represented by the specified path texture.
* This method invokes drawPathTexture() but takes into account
* the extra left/top offset and the texture offset to correctly
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index fbc8455..d947299 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -130,6 +130,7 @@
switch (op) {
case SkRegion::kIntersect_Op: {
if (CC_UNLIKELY(clipRegion)) {
+ ensureClipRegion();
clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
} else {
clipped = clipRect->intersect(r);
@@ -142,6 +143,7 @@
}
case SkRegion::kUnion_Op: {
if (CC_UNLIKELY(clipRegion)) {
+ ensureClipRegion();
clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
} else {
clipped = clipRect->unionWith(r);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
index b2a508b..d5daa5f 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -18,13 +18,7 @@
import android.app.Activity;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Region;
import android.os.Bundle;
import android.view.View;
@@ -50,8 +44,10 @@
canvas.save();
canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f,
Region.Op.DIFFERENCE);
- canvas.drawARGB(255, 255, 0, 0);
+ canvas.drawARGB(128, 255, 0, 0);
canvas.restore();
+
+ invalidate();
}
}
}