() {
+
+ @Override
+ public CustomState[] newArray(int size) {
+ return new CustomState[size];
+ }
+
+ @Override
+ public CustomState createFromParcel(Parcel incoming) {
+ return new CustomState(incoming);
+ }
+ };
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/Marker.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/Marker.java
new file mode 100644
index 000000000..cd354b290
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/Marker.java
@@ -0,0 +1,196 @@
+package com.yunbao.faceunity.seekbar.internal;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.core.view.ViewCompat;
+
+import com.yunbao.faceunity.R;
+import com.yunbao.faceunity.seekbar.internal.compat.SeekBarCompat;
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * {@link ViewGroup} to be used as the real indicator.
+ *
+ * I've used this to be able to accommodate the TextView
+ * and the {@link MarkerDrawable}
+ * with the required positions and offsets
+ *
+ *
+ * @hide
+ */
+public class Marker extends ViewGroup implements MarkerDrawable.MarkerAnimationListener {
+ private static final int PADDING_DP = 1;
+ private static final int ELEVATION_DP = 8;
+ //The TextView to show the info
+ private TextView mNumber;
+ //The max width of this View
+ private int mWidth;
+ //some distance between the thumb and our bubble marker.
+ //This will be added to our measured height
+ private int mSeparation;
+ MarkerDrawable mMarkerDrawable;
+
+ public Marker(Context context, AttributeSet attrs, int defStyleAttr, String maxValue, int thumbSize, int separation) {
+ super(context, attrs, defStyleAttr);
+ //as we're reading the parent DiscreteSeekBar attributes, it may wrongly set this view's visibility.
+ setVisibility(View.VISIBLE);
+
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscreteSeekBar,
+ R.attr.discreteSeekBarStyle, R.style.Widget_DiscreteSeekBar);
+
+ int padding = (int) (PADDING_DP * displayMetrics.density) * 2;
+ int textAppearanceId = a.getResourceId(R.styleable.DiscreteSeekBar_dsb_indicatorTextAppearance,
+ R.style.Widget_DiscreteIndicatorTextAppearance);
+ mNumber = new TextView(context);
+ //Add some padding to this textView so the bubble has some space to breath
+ mNumber.setPadding(padding, 0, padding, 0);
+ mNumber.setTextAppearance(context, textAppearanceId);
+ mNumber.setGravity(Gravity.CENTER);
+ mNumber.setText(maxValue);
+ mNumber.setMaxLines(1);
+ mNumber.setSingleLine(true);
+ SeekBarCompat.setTextDirection(mNumber, TEXT_DIRECTION_LOCALE);
+ mNumber.setVisibility(View.INVISIBLE);
+
+ //add some padding for the elevation shadow not to be clipped
+ //I'm sure there are better ways of doing this...
+ setPadding(padding, padding, padding, padding);
+
+ resetSizes(maxValue);
+
+ mSeparation = separation;
+ ColorStateList color = a.getColorStateList(R.styleable.DiscreteSeekBar_dsb_indicatorColor);
+ mMarkerDrawable = new MarkerDrawable(color, thumbSize);
+ mMarkerDrawable.setCallback(this);
+ mMarkerDrawable.setMarkerListener(this);
+ mMarkerDrawable.setExternalOffset(padding);
+
+ //Elevation for anroid 5+
+ float elevation = a.getDimension(R.styleable.DiscreteSeekBar_dsb_indicatorElevation, ELEVATION_DP * displayMetrics.density);
+ ViewCompat.setElevation(this, elevation);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ SeekBarCompat.setOutlineProvider(this, mMarkerDrawable);
+ }
+ a.recycle();
+ }
+
+ public void resetSizes(String maxValue) {
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ //Account for negative numbers... is there any proper way of getting the biggest string between our range????
+ mNumber.setText("-" + maxValue);
+ //Do a first forced measure call for the TextView (with the biggest text content),
+ //to calculate the max width and use always the same.
+ //this avoids the TextView from shrinking and growing when the text content changes
+ int wSpec = MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels, MeasureSpec.AT_MOST);
+ int hSpec = MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels, MeasureSpec.AT_MOST);
+ mNumber.measure(wSpec, hSpec);
+ mWidth = Math.max(mNumber.getMeasuredWidth(), mNumber.getMeasuredHeight());
+ removeView(mNumber);
+ addView(mNumber, new FrameLayout.LayoutParams(mWidth, mWidth, Gravity.LEFT | Gravity.TOP));
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mMarkerDrawable.draw(canvas);
+ super.dispatchDraw(canvas);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ int widthSize = mWidth + getPaddingLeft() + getPaddingRight();
+ int heightSize = mWidth + getPaddingTop() + getPaddingBottom();
+ //This diff is the basic calculation of the difference between
+ //a square side size and its diagonal
+ //this helps us account for the visual offset created by MarkerDrawable
+ //when leaving one of the corners un-rounded
+ int diff = (int) ((1.41f * mWidth) - mWidth) / 2;
+ setMeasuredDimension(widthSize, heightSize + diff + mSeparation);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int left = getPaddingLeft();
+ int top = getPaddingTop();
+ int right = getWidth() - getPaddingRight();
+ int bottom = getHeight() - getPaddingBottom();
+ //the TetView is always layout at the top
+ mNumber.layout(left, top, left + mWidth, top + mWidth);
+ //the MarkerDrawable uses the whole view, it will adapt itself...
+ // or it seems so...
+ mMarkerDrawable.setBounds(left, top, right, bottom);
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mMarkerDrawable || super.verifyDrawable(who);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ //HACK: Sometimes, the animateOpen() call is made before the View is attached
+ //so the drawable cannot schedule itself to run the animation
+ //I think we can call it here safely.
+ //I've seen it happen in android 2.3.7
+ animateOpen();
+ }
+
+ public void setValue(CharSequence value) {
+ mNumber.setText(value);
+ }
+
+ public CharSequence getValue() {
+ return mNumber.getText();
+ }
+
+ public void animateOpen() {
+ mMarkerDrawable.stop();
+ mMarkerDrawable.animateToPressed();
+ }
+
+ public void animateClose() {
+ mMarkerDrawable.stop();
+ mNumber.setVisibility(View.INVISIBLE);
+ mMarkerDrawable.animateToNormal();
+ }
+
+ @Override
+ public void onOpeningComplete() {
+ mNumber.setVisibility(View.VISIBLE);
+ if (getParent() instanceof MarkerDrawable.MarkerAnimationListener) {
+ ((MarkerDrawable.MarkerAnimationListener) getParent()).onOpeningComplete();
+ }
+ }
+
+ @Override
+ public void onClosingComplete() {
+ if (getParent() instanceof MarkerDrawable.MarkerAnimationListener) {
+ ((MarkerDrawable.MarkerAnimationListener) getParent()).onClosingComplete();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mMarkerDrawable.stop();
+ }
+
+ public void setColors(int startColor, int endColor) {
+ mMarkerDrawable.setColors(startColor, endColor);
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/PopupIndicator.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/PopupIndicator.java
new file mode 100644
index 000000000..d968abd51
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/PopupIndicator.java
@@ -0,0 +1,256 @@
+package com.yunbao.faceunity.seekbar.internal;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.core.view.GravityCompat;
+
+import com.yunbao.faceunity.seekbar.internal.compat.SeekBarCompat;
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * Class to manage the floating bubble thing, similar (but quite worse tested than {@link android.widget.PopupWindow}
+ *
+ *
+ * This will attach a View to the Window (full-width, measured-height, positioned just under the thumb)
+ *
+ *
+ * @hide
+ * @see #showIndicator(View, Rect)
+ * @see #dismiss()
+ * @see #dismissComplete()
+ * @see Floater
+ */
+public class PopupIndicator {
+
+ private final WindowManager mWindowManager;
+ private boolean mShowing;
+ private Floater mPopupView;
+ //Outside listener for the DiscreteSeekBar to get MarkerDrawable animation events.
+ //The whole chain of events goes this way:
+ //MarkerDrawable->Marker->Floater->mListener->DiscreteSeekBar....
+ //... phew!
+ private MarkerDrawable.MarkerAnimationListener mListener;
+ private int[] mDrawingLocation = new int[2];
+ Point screenSize = new Point();
+
+ public PopupIndicator(Context context, AttributeSet attrs, int defStyleAttr, String maxValue, int thumbSize, int separation) {
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mPopupView = new Floater(context, attrs, defStyleAttr, maxValue, thumbSize, separation);
+ }
+
+ public void updateSizes(String maxValue) {
+ dismissComplete();
+ if (mPopupView != null) {
+ mPopupView.mMarker.resetSizes(maxValue);
+ }
+ }
+
+ public void setListener(MarkerDrawable.MarkerAnimationListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * We want the Floater to be full-width because the contents will be moved from side to side.
+ * We may/should change this in the future to use just the PARENT View width and/or pass it in the constructor
+ */
+ private void measureFloater() {
+ int specWidth = View.MeasureSpec.makeMeasureSpec(screenSize.x, View.MeasureSpec.EXACTLY);
+ int specHeight = View.MeasureSpec.makeMeasureSpec(screenSize.y, View.MeasureSpec.AT_MOST);
+ mPopupView.measure(specWidth, specHeight);
+ }
+
+ public void setValue(CharSequence value) {
+ mPopupView.mMarker.setValue(value);
+ }
+
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ public void showIndicator(View parent, Rect touchBounds) {
+ if (isShowing()) {
+ mPopupView.mMarker.animateOpen();
+ return;
+ }
+
+ IBinder windowToken = parent.getWindowToken();
+ if (windowToken != null) {
+ WindowManager.LayoutParams p = createPopupLayout(windowToken);
+
+ p.gravity = Gravity.TOP | GravityCompat.START;
+ updateLayoutParamsForPosiion(parent, p, touchBounds.bottom);
+ mShowing = true;
+
+ translateViewIntoPosition(touchBounds.centerX());
+ invokePopup(p);
+ }
+ }
+
+ public void move(int x) {
+ if (!isShowing()) {
+ return;
+ }
+ translateViewIntoPosition(x);
+ }
+
+ public void setColors(int startColor, int endColor) {
+ mPopupView.setColors(startColor, endColor);
+ }
+
+ /**
+ * This will start the closing animation of the Marker and call onClosingComplete when finished
+ */
+ public void dismiss() {
+ mPopupView.mMarker.animateClose();
+ }
+
+ /**
+ * FORCE the popup window to be removed.
+ * You typically calls this when the parent view is being removed from the window to avoid a Window Leak
+ */
+ public void dismissComplete() {
+ if (isShowing()) {
+ mShowing = false;
+ try {
+ mWindowManager.removeViewImmediate(mPopupView);
+ } finally {
+ }
+ }
+ }
+
+
+ private void updateLayoutParamsForPosiion(View anchor, WindowManager.LayoutParams p, int yOffset) {
+ DisplayMetrics displayMetrics = anchor.getResources().getDisplayMetrics();
+ screenSize.set(displayMetrics.widthPixels, displayMetrics.heightPixels);
+
+ measureFloater();
+ int measuredHeight = mPopupView.getMeasuredHeight();
+ int paddingBottom = mPopupView.mMarker.getPaddingBottom();
+ anchor.getLocationInWindow(mDrawingLocation);
+ p.x = 0;
+ p.y = mDrawingLocation[1] - measuredHeight + yOffset + paddingBottom;
+ p.width = screenSize.x;
+ p.height = measuredHeight;
+ }
+
+ private void translateViewIntoPosition(final int x) {
+ mPopupView.setFloatOffset(x + mDrawingLocation[0]);
+ }
+
+ private void invokePopup(WindowManager.LayoutParams p) {
+ mWindowManager.addView(mPopupView, p);
+ mPopupView.mMarker.animateOpen();
+ }
+
+ private WindowManager.LayoutParams createPopupLayout(IBinder token) {
+ WindowManager.LayoutParams p = new WindowManager.LayoutParams();
+ p.gravity = Gravity.START | Gravity.TOP;
+ p.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ p.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ p.format = PixelFormat.TRANSLUCENT;
+ p.flags = computeFlags(p.flags);
+ p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ p.token = token;
+ p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+ p.setTitle("DiscreteSeekBar Indicator:" + Integer.toHexString(hashCode()));
+
+ return p;
+ }
+
+ /**
+ * I'm NOT completely sure how all this bitwise things work...
+ *
+ * @param curFlags
+ * @return
+ */
+ private int computeFlags(int curFlags) {
+ curFlags &= ~(
+ WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES |
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
+ WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
+ curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ return curFlags;
+ }
+
+ /**
+ * Small FrameLayout class to hold and move the bubble around when requested
+ * I wanted to use the {@link Marker} directly
+ * but doing so would make some things harder to implement
+ * (like moving the marker around, having the Marker's outline to work, etc)
+ */
+ private class Floater extends FrameLayout implements MarkerDrawable.MarkerAnimationListener {
+ private Marker mMarker;
+ private int mOffset;
+
+ public Floater(Context context, AttributeSet attrs, int defStyleAttr, String maxValue, int thumbSize, int separation) {
+ super(context);
+ mMarker = new Marker(context, attrs, defStyleAttr, maxValue, thumbSize, separation);
+ addView(mMarker, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSie = mMarker.getMeasuredHeight();
+ setMeasuredDimension(widthSize, heightSie);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int centerDiffX = mMarker.getMeasuredWidth() / 2;
+ int offset = (mOffset - centerDiffX);
+ mMarker.layout(offset, 0, offset + mMarker.getMeasuredWidth(), mMarker.getMeasuredHeight());
+ }
+
+ public void setFloatOffset(int x) {
+ mOffset = x;
+ int centerDiffX = mMarker.getMeasuredWidth() / 2;
+ int offset = (x - centerDiffX);
+ mMarker.offsetLeftAndRight(offset - mMarker.getLeft());
+ //Without hardware acceleration (or API levels<11), offsetting a view seems to NOT invalidate the proper area.
+ //We should calc the proper invalidate Rect but this will be for now...
+ if (!SeekBarCompat.isHardwareAccelerated(this)) {
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onClosingComplete() {
+ if (mListener != null) {
+ mListener.onClosingComplete();
+ }
+ dismissComplete();
+ }
+
+ @Override
+ public void onOpeningComplete() {
+ if (mListener != null) {
+ mListener.onOpeningComplete();
+ }
+ }
+
+ public void setColors(int startColor, int endColor) {
+ mMarker.setColors(startColor, endColor);
+ }
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/AnimatorCompat.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/AnimatorCompat.java
new file mode 100644
index 000000000..94f11d8f0
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/AnimatorCompat.java
@@ -0,0 +1,73 @@
+package com.yunbao.faceunity.seekbar.internal.compat;
+
+
+import com.yunbao.faceunity.seekbar.DiscreteSeekBar;
+
+/**
+ * Currently, there's no {@link android.animation.ValueAnimator} compatibility version
+ * and as we didn't want to throw in external dependencies, we made this small class.
+ *
+ *
+ * This will work like {@link android.support.v4.view.ViewPropertyAnimatorCompat}, that is,
+ * not doing anything on API<11 and using the default {@link android.animation.ValueAnimator}
+ * on API>=11
+ *
+ *
+ * This class is used to provide animation to the {@link DiscreteSeekBar}
+ * when navigating with the Keypad
+ *
+ *
+ * @hide
+ */
+public abstract class AnimatorCompat {
+ public interface AnimationFrameUpdateListener {
+ public void onAnimationFrame(float currentValue);
+ }
+
+ AnimatorCompat() {
+
+ }
+
+ public abstract void cancel();
+
+ public abstract boolean isRunning();
+
+ public abstract void setDuration(int progressAnimationDuration);
+
+ public abstract void start();
+
+ public static final AnimatorCompat create(float start, float end, AnimationFrameUpdateListener listener) {
+ return new AnimatorCompatBase(start, end, listener);
+ }
+
+ private static class AnimatorCompatBase extends AnimatorCompat {
+
+ private final AnimationFrameUpdateListener mListener;
+ private final float mEndValue;
+
+ public AnimatorCompatBase(float start, float end, AnimationFrameUpdateListener listener) {
+ mListener = listener;
+ mEndValue = end;
+ }
+
+ @Override
+ public void cancel() {
+
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public void setDuration(int progressAnimationDuration) {
+
+ }
+
+ @Override
+ public void start() {
+ mListener.onAnimationFrame(mEndValue);
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompat.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompat.java
new file mode 100644
index 000000000..55b9e583f
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompat.java
@@ -0,0 +1,130 @@
+package com.yunbao.faceunity.seekbar.internal.compat;
+
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.graphics.drawable.DrawableCompat;
+
+import com.yunbao.faceunity.seekbar.internal.drawable.AlmostRippleDrawable;
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * Wrapper compatibility class to call some API-Specific methods
+ * And offer alternate procedures when possible
+ *
+ * @hide
+ */
+public class SeekBarCompat {
+
+ /**
+ * Sets the custom Outline provider on API>=21.
+ * Does nothing on API<21
+ *
+ * @param view
+ * @param markerDrawable
+ */
+ public static void setOutlineProvider(View view, final MarkerDrawable markerDrawable) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ SeekBarCompatDontCrash.setOutlineProvider(view, markerDrawable);
+ }
+ }
+
+ /**
+ * Our DiscreteSeekBar implementation uses a circular drawable on API < 21
+ * because we don't set it as Background, but draw it ourselves
+ *
+ * @param colorStateList
+ * @return
+ */
+ public static Drawable getRipple(ColorStateList colorStateList) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return SeekBarCompatDontCrash.getRipple(colorStateList);
+ } else {
+ return new AlmostRippleDrawable(colorStateList);
+ }
+ }
+
+ /**
+ * Sets the color of the seekbar ripple
+ *
+ * @param drawable
+ * @param colorStateList The ColorStateList the track ripple will be changed to
+ */
+ public static void setRippleColor(@NonNull Drawable drawable, ColorStateList colorStateList) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ((RippleDrawable) drawable).setColor(colorStateList);
+ } else {
+ ((AlmostRippleDrawable) drawable).setColor(colorStateList);
+ }
+ }
+
+ /**
+ * As our DiscreteSeekBar implementation uses a circular drawable on API < 21
+ * we want to use the same method to set its bounds as the Ripple's hotspot bounds.
+ *
+ * @param drawable
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
+ public static void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ //We don't want the full size rect, Lollipop ripple would be too big
+ int size = (right - left) / 8;
+ DrawableCompat.setHotspotBounds(drawable, left + size, top + size, right - size, bottom - size);
+ } else {
+ drawable.setBounds(left, top, right, bottom);
+ }
+ }
+
+ /**
+ * android.support.v4.view.ViewCompat SHOULD include this once and for all!!
+ * But it doesn't...
+ *
+ * @param view
+ * @param background
+ */
+ @SuppressWarnings("deprecation")
+ public static void setBackground(View view, Drawable background) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ SeekBarCompatDontCrash.setBackground(view, background);
+ } else {
+ view.setBackgroundDrawable(background);
+ }
+ }
+
+ /**
+ * Sets the TextView text direction attribute when possible
+ *
+ * @param textView
+ * @param textDirection
+ * @see TextView#setTextDirection(int)
+ */
+ public static void setTextDirection(TextView textView, int textDirection) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ SeekBarCompatDontCrash.setTextDirection(textView, textDirection);
+ }
+ }
+
+ public static boolean isInScrollingContainer(ViewParent p) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return SeekBarCompatDontCrash.isInScrollingContainer(p);
+ }
+ return false;
+ }
+
+ public static boolean isHardwareAccelerated(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return SeekBarCompatDontCrash.isHardwareAccelerated(view);
+ }
+ return false;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompatDontCrash.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompatDontCrash.java
new file mode 100644
index 000000000..ef13919fb
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/compat/SeekBarCompatDontCrash.java
@@ -0,0 +1,59 @@
+package com.yunbao.faceunity.seekbar.internal.compat;
+
+import android.annotation.TargetApi;
+import android.content.res.ColorStateList;
+import android.graphics.Outline;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import com.yunbao.faceunity.seekbar.internal.drawable.MarkerDrawable;
+
+
+/**
+ * Wrapper compatibility class to call some API-Specific methods
+ * And offer alternate procedures when possible
+ *
+ * @hide
+ */
+@TargetApi(21)
+class SeekBarCompatDontCrash {
+ public static void setOutlineProvider(View marker, final MarkerDrawable markerDrawable) {
+ marker.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setConvexPath(markerDrawable.getPath());
+ }
+ });
+ }
+
+ public static Drawable getRipple(ColorStateList colorStateList) {
+ return new RippleDrawable(colorStateList, null, null);
+ }
+
+ public static void setBackground(View view, Drawable background) {
+ view.setBackground(background);
+ }
+
+ public static void setTextDirection(TextView number, int textDirection) {
+ number.setTextDirection(textDirection);
+ }
+
+ public static boolean isInScrollingContainer(ViewParent p) {
+ while (p != null && p instanceof ViewGroup) {
+ if (((ViewGroup) p).shouldDelayChildPressedState()) {
+ return true;
+ }
+ p = p.getParent();
+ }
+ return false;
+ }
+
+ public static boolean isHardwareAccelerated(View view) {
+ return view.isHardwareAccelerated();
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/AlmostRippleDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/AlmostRippleDrawable.java
new file mode 100644
index 000000000..e69e2df5c
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/AlmostRippleDrawable.java
@@ -0,0 +1,206 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.os.SystemClock;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+
+public class AlmostRippleDrawable extends StateDrawable implements Animatable {
+ private static final long FRAME_DURATION = 1000 / 60;
+ private static final int ANIMATION_DURATION = 250;
+
+ private static final float INACTIVE_SCALE = 0f;
+ private static final float ACTIVE_SCALE = 1f;
+ private float mCurrentScale = INACTIVE_SCALE;
+ private Interpolator mInterpolator;
+ private long mStartTime;
+ private boolean mReverse = false;
+ private boolean mRunning = false;
+ private int mDuration = ANIMATION_DURATION;
+ private float mAnimationInitialValue;
+ //We don't use colors just with our drawable state because of animations
+ private int mPressedColor;
+ private int mFocusedColor;
+ private int mDisabledColor;
+ private int mRippleColor;
+ private int mRippleBgColor;
+
+ public AlmostRippleDrawable(@NonNull ColorStateList tintStateList) {
+ super(tintStateList);
+ mInterpolator = new AccelerateDecelerateInterpolator();
+ setColor(tintStateList);
+ }
+
+ public void setColor(@NonNull ColorStateList tintStateList) {
+ int defaultColor = tintStateList.getDefaultColor();
+ mFocusedColor = tintStateList.getColorForState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, defaultColor);
+ mPressedColor = tintStateList.getColorForState(new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, defaultColor);
+ mDisabledColor = tintStateList.getColorForState(new int[]{-android.R.attr.state_enabled}, defaultColor);
+
+ //The ripple should be partially transparent
+ mFocusedColor = getModulatedAlphaColor(130, mFocusedColor);
+ mPressedColor = getModulatedAlphaColor(130, mPressedColor);
+ mDisabledColor = getModulatedAlphaColor(130, mDisabledColor);
+ }
+
+ private static int getModulatedAlphaColor(int alphaValue, int originalColor) {
+ int alpha = Color.alpha(originalColor);
+ int scale = alphaValue + (alphaValue >> 7);
+ alpha = alpha * scale >> 8;
+ return Color.argb(alpha, Color.red(originalColor), Color.green(originalColor), Color.blue(originalColor));
+ }
+
+ @Override
+ public void doDraw(Canvas canvas, Paint paint) {
+ Rect bounds = getBounds();
+ int size = Math.min(bounds.width(), bounds.height());
+ float scale = mCurrentScale;
+ int rippleColor = mRippleColor;
+ int bgColor = mRippleBgColor;
+ float radius = (size / 2);
+ float radiusAnimated = radius * scale;
+ if (scale > INACTIVE_SCALE) {
+ if (bgColor != 0) {
+ paint.setColor(bgColor);
+ paint.setAlpha(decreasedAlpha(Color.alpha(bgColor)));
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, paint);
+ }
+ if (rippleColor != 0) {
+ paint.setColor(rippleColor);
+ paint.setAlpha(modulateAlpha(Color.alpha(rippleColor)));
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), radiusAnimated, paint);
+ }
+ }
+ }
+
+ private int decreasedAlpha(int alpha) {
+ int scale = 100 + (100 >> 7);
+ return alpha * scale >> 8;
+ }
+
+ @Override
+ public boolean setState(int[] stateSet) {
+ int[] oldState = getState();
+ boolean oldPressed = false;
+ for (int i : oldState) {
+ if (i == android.R.attr.state_pressed) {
+ oldPressed = true;
+ }
+ }
+ super.setState(stateSet);
+ boolean focused = false;
+ boolean pressed = false;
+ boolean disabled = true;
+ for (int i : stateSet) {
+ if (i == android.R.attr.state_focused) {
+ focused = true;
+ } else if (i == android.R.attr.state_pressed) {
+ pressed = true;
+ } else if (i == android.R.attr.state_enabled) {
+ disabled = false;
+ }
+ }
+
+ if (disabled) {
+ unscheduleSelf(mUpdater);
+ mRippleColor = mDisabledColor;
+ mRippleBgColor = 0;
+ mCurrentScale = ACTIVE_SCALE / 2;
+ invalidateSelf();
+ } else {
+ if (pressed) {
+ animateToPressed();
+ mRippleColor = mRippleBgColor = mPressedColor;
+ } else if (oldPressed) {
+ mRippleColor = mRippleBgColor = mPressedColor;
+ animateToNormal();
+ } else if (focused) {
+ mRippleColor = mFocusedColor;
+ mRippleBgColor = 0;
+ mCurrentScale = ACTIVE_SCALE;
+ invalidateSelf();
+ } else {
+ mRippleColor = 0;
+ mRippleBgColor = 0;
+ mCurrentScale = INACTIVE_SCALE;
+ invalidateSelf();
+ }
+ }
+ return true;
+ }
+
+ public void animateToPressed() {
+ unscheduleSelf(mUpdater);
+ if (mCurrentScale < ACTIVE_SCALE) {
+ mReverse = false;
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - ((mAnimationInitialValue - INACTIVE_SCALE) / (ACTIVE_SCALE - INACTIVE_SCALE));
+ mDuration = (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ }
+ }
+
+ public void animateToNormal() {
+ unscheduleSelf(mUpdater);
+ if (mCurrentScale > INACTIVE_SCALE) {
+ mReverse = true;
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - ((mAnimationInitialValue - ACTIVE_SCALE) / (INACTIVE_SCALE - ACTIVE_SCALE));
+ mDuration = (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ }
+ }
+
+ private void updateAnimation(float factor) {
+ float initial = mAnimationInitialValue;
+ float destination = mReverse ? INACTIVE_SCALE : ACTIVE_SCALE;
+ mCurrentScale = initial + (destination - initial) * factor;
+ invalidateSelf();
+ }
+
+ private final Runnable mUpdater = new Runnable() {
+
+ @Override
+ public void run() {
+
+ long currentTime = SystemClock.uptimeMillis();
+ long diff = currentTime - mStartTime;
+ if (diff < mDuration) {
+ float interpolation = mInterpolator.getInterpolation((float) diff / (float) mDuration);
+ scheduleSelf(mUpdater, currentTime + FRAME_DURATION);
+ updateAnimation(interpolation);
+ } else {
+ unscheduleSelf(mUpdater);
+ mRunning = false;
+ updateAnimation(1f);
+ }
+ }
+ };
+
+ @Override
+ public void start() {
+ //No-Op. We control our own animation
+ }
+
+ @Override
+ public void stop() {
+ //No-Op. We control our own animation
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/MarkerDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/MarkerDrawable.java
new file mode 100644
index 000000000..8c5a04745
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/MarkerDrawable.java
@@ -0,0 +1,235 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Animatable;
+import android.os.SystemClock;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * Implementation of {@link StateDrawable} to draw a morphing marker symbol.
+ *
+ * It's basically an implementation of an {@link Animatable} Drawable with the following details:
+ *
+ *
+ * - Animates from a circle shape to a "marker" shape just using a RoundRect
+ * - Animates color change from the normal state color to the pressed state color
+ * - Uses a {@link Path} to also serve as Outline for API>=21
+ *
+ *
+ * @hide
+ */
+public class MarkerDrawable extends StateDrawable implements Animatable {
+ private static final long FRAME_DURATION = 1000 / 60;
+ private static final int ANIMATION_DURATION = 250;
+
+ private float mCurrentScale = 0f;
+ private Interpolator mInterpolator;
+ private long mStartTime;
+ private boolean mReverse = false;
+ private boolean mRunning = false;
+ private int mDuration = ANIMATION_DURATION;
+ //size of the actual thumb drawable to use as circle state size
+ private float mClosedStateSize;
+ //value to store que current scale when starting an animation and interpolate from it
+ private float mAnimationInitialValue;
+ //extra offset directed from the View to account
+ //for its internal padding between circle state and marker state
+ private int mExternalOffset;
+ //colors for interpolation
+ private int mStartColor;//Color when the Marker is OPEN
+ private int mEndColor;//Color when the arker is CLOSED
+
+ Path mPath = new Path();
+ RectF mRect = new RectF();
+ Matrix mMatrix = new Matrix();
+ private MarkerAnimationListener mMarkerListener;
+
+ public MarkerDrawable(@NonNull ColorStateList tintList, int closedSize) {
+ super(tintList);
+ mInterpolator = new AccelerateDecelerateInterpolator();
+ mClosedStateSize = closedSize;
+ mStartColor = tintList.getColorForState(new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, tintList.getDefaultColor());
+ mEndColor = tintList.getDefaultColor();
+
+ }
+
+ public void setExternalOffset(int offset) {
+ mExternalOffset = offset;
+ }
+
+ /**
+ * The two colors that will be used for the seek thumb.
+ *
+ * @param startColor Color used for the seek thumb
+ * @param endColor Color used for popup indicator
+ */
+ public void setColors(int startColor, int endColor) {
+ mStartColor = startColor;
+ mEndColor = endColor;
+ }
+
+ @Override
+ void doDraw(Canvas canvas, Paint paint) {
+ if (!mPath.isEmpty()) {
+ paint.setStyle(Paint.Style.FILL);
+ int color = blendColors(mStartColor, mEndColor, mCurrentScale);
+ paint.setColor(color);
+ canvas.drawPath(mPath, paint);
+ }
+ }
+
+ public Path getPath() {
+ return mPath;
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ computePath(bounds);
+ }
+
+ private void computePath(Rect bounds) {
+ final float currentScale = mCurrentScale;
+ final Path path = mPath;
+ final RectF rect = mRect;
+ final Matrix matrix = mMatrix;
+
+ path.reset();
+ int totalSize = Math.min(bounds.width(), bounds.height());
+
+ float initial = mClosedStateSize;
+ float destination = totalSize;
+ float currentSize = initial + (destination - initial) * currentScale;
+
+ float halfSize = currentSize / 2f;
+ float inverseScale = 1f - currentScale;
+ float cornerSize = halfSize * inverseScale;
+ float[] corners = new float[]{halfSize, halfSize, halfSize, halfSize, halfSize, halfSize, cornerSize, cornerSize};
+ rect.set(bounds.left, bounds.top, bounds.left + currentSize, bounds.top + currentSize);
+ path.addRoundRect(rect, corners, Path.Direction.CCW);
+ matrix.reset();
+ matrix.postRotate(-45, bounds.left + halfSize, bounds.top + halfSize);
+ matrix.postTranslate((bounds.width() - currentSize) / 2, 0);
+ float hDiff = (bounds.bottom - currentSize - mExternalOffset) * inverseScale;
+ matrix.postTranslate(0, hDiff);
+ path.transform(matrix);
+ }
+
+ private void updateAnimation(float factor) {
+ float initial = mAnimationInitialValue;
+ float destination = mReverse ? 0f : 1f;
+ mCurrentScale = initial + (destination - initial) * factor;
+ computePath(getBounds());
+ invalidateSelf();
+ }
+
+ public void animateToPressed() {
+ unscheduleSelf(mUpdater);
+ mReverse = false;
+ if (mCurrentScale < 1) {
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - mCurrentScale;
+ mDuration = (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ } else {
+ notifyFinishedToListener();
+ }
+ }
+
+ public void animateToNormal() {
+ mReverse = true;
+ unscheduleSelf(mUpdater);
+ if (mCurrentScale > 0) {
+ mRunning = true;
+ mAnimationInitialValue = mCurrentScale;
+ float durationFactor = 1f - mCurrentScale;
+ mDuration = ANIMATION_DURATION - (int) (ANIMATION_DURATION * durationFactor);
+ mStartTime = SystemClock.uptimeMillis();
+ scheduleSelf(mUpdater, mStartTime + FRAME_DURATION);
+ } else {
+ notifyFinishedToListener();
+ }
+ }
+
+ private final Runnable mUpdater = new Runnable() {
+
+ @Override
+ public void run() {
+
+ long currentTime = SystemClock.uptimeMillis();
+ long diff = currentTime - mStartTime;
+ if (diff < mDuration) {
+ float interpolation = mInterpolator.getInterpolation((float) diff / (float) mDuration);
+ scheduleSelf(mUpdater, currentTime + FRAME_DURATION);
+ updateAnimation(interpolation);
+ } else {
+ unscheduleSelf(mUpdater);
+ mRunning = false;
+ updateAnimation(1f);
+ notifyFinishedToListener();
+ }
+ }
+ };
+
+ public void setMarkerListener(MarkerAnimationListener listener) {
+ mMarkerListener = listener;
+ }
+
+ private void notifyFinishedToListener() {
+ if (mMarkerListener != null) {
+ if (mReverse) {
+ mMarkerListener.onClosingComplete();
+ } else {
+ mMarkerListener.onOpeningComplete();
+ }
+ }
+ }
+
+ @Override
+ public void start() {
+ //No-Op. We control our own animation
+ }
+
+ @Override
+ public void stop() {
+ unscheduleSelf(mUpdater);
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+
+ private static int blendColors(int color1, int color2, float factor) {
+ final float inverseFactor = 1f - factor;
+ float a = (Color.alpha(color1) * factor) + (Color.alpha(color2) * inverseFactor);
+ float r = (Color.red(color1) * factor) + (Color.red(color2) * inverseFactor);
+ float g = (Color.green(color1) * factor) + (Color.green(color2) * inverseFactor);
+ float b = (Color.blue(color1) * factor) + (Color.blue(color2) * inverseFactor);
+ return Color.argb((int) a, (int) r, (int) g, (int) b);
+ }
+
+
+ /**
+ * A listener interface to porpagate animation events
+ * This is the "poor's man" AnimatorListener for this Drawable
+ */
+ public interface MarkerAnimationListener {
+ public void onClosingComplete();
+
+ public void onOpeningComplete();
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/StateDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/StateDrawable.java
new file mode 100644
index 000000000..9c24bb35c
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/StateDrawable.java
@@ -0,0 +1,105 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * A drawable that changes it's Paint color depending on the Drawable State
+ *
+ * Subclasses should implement {@link #doDraw(Canvas, Paint)}
+ *
+ *
+ * @hide
+ */
+public abstract class StateDrawable extends Drawable {
+ private ColorStateList mTintStateList;
+ private final Paint mPaint;
+ private int mCurrentColor;
+ private int mAlpha = 255;
+
+ public StateDrawable(@NonNull ColorStateList tintStateList) {
+ super();
+ setColorStateList(tintStateList);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ }
+
+ @Override
+ public boolean isStateful() {
+ return (mTintStateList.isStateful()) || super.isStateful();
+ }
+
+ @Override
+ public boolean setState(int[] stateSet) {
+ boolean handled = super.setState(stateSet);
+ handled = updateTint(stateSet) || handled;
+ return handled;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ private boolean updateTint(int[] state) {
+ final int color = mTintStateList.getColorForState(state, mCurrentColor);
+ if (color != mCurrentColor) {
+ mCurrentColor = color;
+ //We've changed states
+ invalidateSelf();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mPaint.setColor(mCurrentColor);
+ int alpha = modulateAlpha(Color.alpha(mCurrentColor));
+ mPaint.setAlpha(alpha);
+ doDraw(canvas, mPaint);
+ }
+
+ public void setColorStateList(@NonNull ColorStateList tintStateList) {
+ mTintStateList = tintStateList;
+ mCurrentColor = tintStateList.getDefaultColor();
+ }
+
+ /**
+ * Subclasses should implement this method to do the actual drawing
+ *
+ * @param canvas The current {@link Canvas} to draw into
+ * @param paint The {@link Paint} preconfigurred with the current
+ * {@link ColorStateList} color
+ */
+ abstract void doDraw(Canvas canvas, Paint paint);
+
+ @Override
+ public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ invalidateSelf();
+ }
+
+ int modulateAlpha(int alpha) {
+ int scale = mAlpha + (mAlpha >> 7);
+ return alpha * scale >> 8;
+ }
+
+ @Override
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mPaint.setColorFilter(cf);
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/ThumbDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/ThumbDrawable.java
new file mode 100644
index 000000000..54256e479
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/ThumbDrawable.java
@@ -0,0 +1,96 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+
+import com.yunbao.faceunity.seekbar.internal.Marker;
+
+
+/**
+ * HACK
+ *
+ * Special {@link StateDrawable} implementation
+ * to draw the Thumb circle.
+ *
+ *
+ * It's special because it will stop drawing once the state is pressed/focused BUT only after a small delay.
+ *
+ *
+ * This special delay is meant to help avoiding frame glitches while the {@link Marker} is added to the Window
+ *
+ *
+ * @hide
+ */
+public class ThumbDrawable extends StateDrawable implements Animatable {
+ //The current size for this drawable. Must be converted to real DPs
+ public static final int DEFAULT_SIZE_DP = 12;
+ private final int mSize;
+ private boolean mOpen;
+ private boolean mRunning;
+
+ public ThumbDrawable(@NonNull ColorStateList tintStateList, int size) {
+ super(tintStateList);
+ mSize = size;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mSize;
+ }
+
+ @Override
+ public void doDraw(Canvas canvas, Paint paint) {
+ if (!mOpen) {
+ Rect bounds = getBounds();
+ float radius = (mSize / 2);
+ canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, paint);
+ }
+ }
+
+ public void animateToPressed() {
+ scheduleSelf(opener, SystemClock.uptimeMillis() + 100);
+ mRunning = true;
+ }
+
+ public void animateToNormal() {
+ mOpen = false;
+ mRunning = false;
+ unscheduleSelf(opener);
+ invalidateSelf();
+ }
+
+ private Runnable opener = new Runnable() {
+ @Override
+ public void run() {
+ mOpen = true;
+ invalidateSelf();
+ mRunning = false;
+ }
+ };
+
+ @Override
+ public void start() {
+ //NOOP
+ }
+
+ @Override
+ public void stop() {
+ animateToNormal();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mRunning;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackOvalDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackOvalDrawable.java
new file mode 100644
index 000000000..f3a5898b1
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackOvalDrawable.java
@@ -0,0 +1,30 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * Simple {@link StateDrawable} implementation
+ * to draw circles/ovals
+ *
+ * @hide
+ */
+public class TrackOvalDrawable extends StateDrawable {
+ private RectF mRectF = new RectF();
+
+ public TrackOvalDrawable(@NonNull ColorStateList tintStateList) {
+ super(tintStateList);
+ }
+
+ @Override
+ void doDraw(Canvas canvas, Paint paint) {
+ mRectF.set(getBounds());
+ canvas.drawOval(mRectF, paint);
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackRectDrawable.java b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackRectDrawable.java
new file mode 100644
index 000000000..165e24f03
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/seekbar/internal/drawable/TrackRectDrawable.java
@@ -0,0 +1,26 @@
+package com.yunbao.faceunity.seekbar.internal.drawable;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import androidx.annotation.NonNull;
+
+
+/**
+ * Simple {@link StateDrawable} implementation
+ * to draw rectangles
+ *
+ * @hide
+ */
+public class TrackRectDrawable extends StateDrawable {
+ public TrackRectDrawable(@NonNull ColorStateList tintStateList) {
+ super(tintStateList);
+ }
+
+ @Override
+ void doDraw(Canvas canvas, Paint paint) {
+ canvas.drawRect(getBounds(), paint);
+ }
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/ui/FaceUnityView.java b/FaceUnity/src/main/java/com/yunbao/faceunity/ui/FaceUnityView.java
new file mode 100644
index 000000000..064872467
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/ui/FaceUnityView.java
@@ -0,0 +1,407 @@
+package com.yunbao.faceunity.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.tabs.TabLayout;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.faceunity.R;
+import com.yunbao.faceunity.adapters.ContainerRecyclerAdapter;
+import com.yunbao.faceunity.adapters.MenuGroupRecyclerAdapter;
+import com.yunbao.faceunity.data.FaceParam;
+import com.yunbao.faceunity.data.FaceUnityDataFactory;
+import com.yunbao.faceunity.entity.BaseBean;
+import com.yunbao.faceunity.data.FineStickerDataFactory;
+import com.yunbao.faceunity.entity.FunctionEnum;
+import com.yunbao.faceunity.entity.MakeupCustomClassBean;
+import com.yunbao.faceunity.entity.MenuGroupBean;
+import com.yunbao.faceunity.entity.net.FineStickerEntity;
+import com.yunbao.faceunity.entity.net.FineStickerTagEntity;
+import com.yunbao.faceunity.repo.AnimojiSource;
+import com.yunbao.faceunity.repo.BodyBeautySource;
+import com.yunbao.faceunity.repo.FaceBeautySource;
+import com.yunbao.faceunity.repo.MakeupSource;
+import com.yunbao.faceunity.repo.PropSource;
+import com.yunbao.faceunity.seekbar.DiscreteSeekBar;
+import com.yunbao.faceunity.utils.net.StickerDownloadHelper;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * DESC:
+ * Created on 2021/4/26
+ */
+public class FaceUnityView extends LinearLayout implements StickerDownloadHelper.Callback {
+
+ private Context mContext;
+ private RecyclerView menuGroup;
+ private RecyclerView containerRecycler;
+ private ContainerRecyclerAdapter containerAdapter;
+ private TabLayout tabLayout;
+ private TextView title;
+ private LinearLayout menu2, menuDiy, reset, menu2Reset;
+ private ImageView menu2Back, back, close;
+ private DiscreteSeekBar seekBar;
+
+ public FaceUnityView(Context context) {
+ super(context);
+ mContext = context;
+ init();
+ }
+
+ public FaceUnityView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ init();
+ }
+
+ public FaceUnityView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContext = context;
+ init();
+ }
+
+
+ private void init() {
+ LayoutInflater.from(mContext).inflate(R.layout.layout_faceunity, this);
+ initView();
+ FineStickerDataFactory.getInstance().addCallback(this);
+ }
+
+ /**
+ * 初始化View
+ */
+ private void initView() {
+ menuGroup = findViewById(R.id.menu_group);
+ containerRecycler = findViewById(R.id.menu2_container_view);
+ tabLayout = findViewById(R.id.menu2_tab);
+ title = findViewById(R.id.menu_title);
+ menu2 = findViewById(R.id.layout_faceunity_menu2);
+ back = findViewById(R.id.menu_back);
+ menu2Back = findViewById(R.id.menu2_back);
+ close = findViewById(R.id.menu_close);
+ reset = findViewById(R.id.menu_reset);
+ menu2Reset = findViewById(R.id.menu2_reset);
+ menuDiy = findViewById(R.id.menu_diy);
+ seekBar = findViewById(R.id.item_seekBar);
+ initMenuGroup();
+ setContainerRecycler(new ArrayList<>());
+ initViewClick();
+ }
+
+ /**
+ * 初始化配置点击事件
+ */
+ private void initViewClick() {
+ menu2Back.setOnClickListener(v -> {
+ Object tag = menu2Back.getTag();
+ if(tag==null) {
+ goBackMainMenu();
+ }else if(tag.equals("makeup")){
+ title.setText(R.string.home_function_name_makeup);
+ title.setVisibility(VISIBLE);
+ menuDiy.setVisibility(VISIBLE);
+ tabLayout.removeAllTabs();
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCombinations());
+ menu2Back.setTag(null);
+ }
+ });
+ menuDiy.setOnClickListener(v -> {
+ menu2Back.setTag("makeup");
+ changeRecyclerItemCount(5);
+ menuDiy.setVisibility(GONE);
+ ArrayList list = MakeupSource.buildCustomClasses();
+ setTab(createTabs(list));
+ });
+ menu2Reset.setOnClickListener(view -> {
+ containerAdapter.reset();
+ });
+ }
+
+ /**
+ * 回到默认界面
+ */
+ private void goBackMainMenu() {
+ setContainerRecycler(new ArrayList<>());
+ title.setText("美顏特效選擇");
+ title.setVisibility(VISIBLE);
+ menu2.setVisibility(GONE);
+ menuGroup.setVisibility(VISIBLE);
+ menuDiy.setVisibility(GONE);
+ }
+
+ /**
+ * 配置主菜单
+ */
+ private void initMenuGroup() {
+ MenuGroupRecyclerAdapter adapter = new MenuGroupRecyclerAdapter(mContext);
+ menuGroup.setLayoutManager(new GridLayoutManager(mContext, 4));
+ List layoutItem = new ArrayList<>();
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_beauty, R.mipmap.ico_home_beauty));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_makeup, R.mipmap.ico_home_makeup));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_beauty_body, R.mipmap.ico_home_beauty_body));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_big_head, R.mipmap.ico_home_big_head));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_animoji, R.mipmap.ico_home_animoji));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_sticker, R.mipmap.ico_home_sticker));
+ layoutItem.add(new MenuGroupBean(R.string.home_function_name_fine_sticker, R.mipmap.ico_home_fine_sticker));
+ adapter.setList(layoutItem);
+ adapter.setOnItemClickListener(position -> {
+ LinkedHashMap map = new LinkedHashMap<>();
+ title.setVisibility(GONE);
+ switch (position) {
+ case 0:
+ map.put(R.string.beauty_radio_skin_beauty, FaceParam.FACE_BEAUTY_SKIN);
+ map.put(R.string.beauty_radio_face_shape, FaceParam.FACE_BEAUTY_SHAPE);
+ map.put(R.string.beauty_radio_filter, FaceParam.FACE_BEAUTY_FILTER);
+ map.put(R.string.beauty_radio_style, FaceParam.FACE_BEAUTY_STYLE);
+ setContainerRecycler(FaceBeautySource.buildSkinParams());
+ break;
+ case 1:
+ title.setText(R.string.home_function_name_makeup);
+ title.setVisibility(VISIBLE);
+ menuDiy.setVisibility(VISIBLE);
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCombinations());
+ break;
+ case 2:
+ title.setText(R.string.home_function_name_beauty_body);
+ title.setVisibility(VISIBLE);
+ changeRecyclerItemCount(2);
+ setContainerRecycler(BodyBeautySource.buildBodyBeauty());
+ break;
+ case 3:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(PropSource.buildPropBeans(FunctionEnum.BIG_HEAD));
+ break;
+ case 4:
+ changeRecyclerItemCount(5);
+ map.put(R.string.animoji_filter, FaceParam.FACE_ANIMOJI);
+ map.put(R.string.cartoon_filter, FaceParam.FACE_ANIM);
+ setContainerRecycler(AnimojiSource.buildAnimojis());
+ break;
+ case 5:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(PropSource.buildPropBeans(FunctionEnum.STICKER));
+ break;
+ case 6:
+ List tagList = FineStickerDataFactory.getInstance().loadTagList();
+ if (!tagList.isEmpty()) {
+ setTab(createTabs(tagList));
+ }
+ break;
+ }
+ setTab(createTabs(map));
+ menuGroup.setVisibility(GONE);
+ menu2.setVisibility(VISIBLE);
+ });
+ menuGroup.setAdapter(adapter);
+ }
+
+ /**
+ * 创建Tab
+ */
+ private List createTabs(Map map) {
+ List list = new ArrayList<>();
+ for (Integer key : map.keySet()) {
+ TabLayout.Tab tab = tabLayout.newTab().setText(key);
+ tab.setTag(map.get(key));
+ list.add(tab);
+ }
+ return list;
+ }
+
+ /**
+ * 创建菜单
+ */
+ private List createTabs(List tags) {
+ List list = new ArrayList<>();
+ for (FineStickerTagEntity tag : tags) {
+ TabLayout.Tab tab;
+ if (WordUtil.isZh()) {
+ tab = tabLayout.newTab().setText(tag.getTag().split("/")[0]);
+ } else {
+ tab = tabLayout.newTab().setText(tag.getTag().split("/")[1]);
+ }
+ tab.setTag(tag);
+ list.add(tab);
+ }
+ return list;
+ }
+
+ /**
+ * 创建菜单
+ */
+ private List createTabs(ArrayList tags) {
+ List list = new ArrayList<>();
+ for (MakeupCustomClassBean tag : tags) {
+ TabLayout.Tab tab;
+ tab = tabLayout.newTab().setText(tag.getDesRes());
+ tab.setTag(tag.getBeanType());
+ list.add(tab);
+ }
+ return list;
+ }
+
+ /**
+ * 设置菜单
+ */
+ private void setTab(List tabs) {
+ tabLayout.removeAllTabs();
+ for (TabLayout.Tab tab : tabs) {
+ tabLayout.addTab(tab);
+ }
+ tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ Object tabTag = tab.getTag();
+ if (tabTag instanceof Integer) {
+ switch ((int) tab.getTag()) {
+ case FaceParam.FACE_BEAUTY_SKIN:
+ changeRecyclerItemCount(2);
+ setContainerRecycler(FaceBeautySource.buildSkinParams());
+ break;
+ case FaceParam.FACE_BEAUTY_SHAPE:
+ changeRecyclerItemCount(2);
+ setContainerRecycler(FaceBeautySource.buildShapeParams());
+ break;
+ case FaceParam.FACE_BEAUTY_FILTER:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(FaceBeautySource.buildFilters());
+ break;
+ case FaceParam.FACE_BEAUTY_STYLE:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(FaceBeautySource.buildStylesParams());
+ break;
+ case FaceParam.FACE_ANIMOJI:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(AnimojiSource.buildAnimojis());
+ break;
+ case FaceParam.FACE_ANIM:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(AnimojiSource.buildFilters());
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_FOUNDATION:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_FOUNDATION));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_LIP_STICK:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_LIP_STICK));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_BLUSHER:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_BLUSHER));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_BROW:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_BROW));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_SHADOW:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_SHADOW));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_LINER:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_LINER));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_LASH:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_LASH));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_HIGH_LIGHT:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_HIGH_LIGHT));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_SHADOW:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_SHADOW));
+ break;
+ case FaceParam.FACE_MAKEUP_TYPE_EYE_PUPIL:
+ changeRecyclerItemCount(5);
+ setContainerRecycler(MakeupSource.buildCustomItemParams().get(MakeupSource.FACE_MAKEUP_TYPE_EYE_PUPIL));
+ break;
+ default:
+ setContainerRecycler(new ArrayList<>());
+ }
+ } else if (tabTag instanceof FineStickerTagEntity) {
+ FineStickerTagEntity tag = (FineStickerTagEntity) tabTag;
+ FineStickerEntity sticker = FineStickerDataFactory.getInstance().loadStickerList(tag);
+ ArrayList list = new ArrayList<>();
+ FineStickerEntity.DocsBean docsBean = new FineStickerEntity.DocsBean();
+ docsBean.set_id("-1");
+ list.add(docsBean);
+ list.addAll(sticker.getDocs());
+ changeRecyclerItemCount(5);
+ setContainerRecycler(list);
+ }
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ });
+ }
+
+ /**
+ * 设置美颜选项配置行数,图标为5个,拖动条为2个
+ * @param count
+ */
+ private void changeRecyclerItemCount(int count) {
+ containerRecycler.setLayoutManager(new GridLayoutManager(mContext, count));
+ }
+
+ /**
+ * 设置美颜Recycler内容
+ * @param list
+ */
+ private void setContainerRecycler(ArrayList extends BaseBean> list) {
+ if (containerAdapter == null) {
+ containerAdapter = new ContainerRecyclerAdapter(mContext);
+ containerAdapter.setSeekBar(seekBar);
+ containerRecycler.setLayoutManager(new GridLayoutManager(mContext, 2));
+ containerRecycler.setAdapter(containerAdapter);
+ }
+ containerAdapter.setList(list);
+ containerAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onGetTags(String[] tags) {
+ List list = FineStickerDataFactory.formatTag(tags);
+ setTab(createTabs(list));
+ }
+
+ @Override
+ public void onGetList(String tag, FineStickerEntity fineSticker) {
+
+ }
+
+ @Override
+ public void onDownload(FineStickerEntity.DocsBean entity) {
+
+ }
+
+ @Override
+ public void onDownloadError(FineStickerEntity.DocsBean entity, String msg) {
+
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Authpack.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Authpack.java
new file mode 100644
index 000000000..85ad85c89
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Authpack.java
@@ -0,0 +1,1299 @@
+package com.yunbao.faceunity.utils;
+
+import java.security.MessageDigest;
+
+public class Authpack {
+ public static int sha1_32(byte[] buf){int ret=0;try{byte[] digest=MessageDigest.getInstance("SHA1").digest(buf);return ((int)(digest[0]&0xff)<<24)+((int)(digest[1]&0xff)<<16)+((int)(digest[2]&0xff)<<8)+((int)(digest[3]&0xff)<<0);}catch(Exception e){}return ret;}
+ public static byte[] A(){
+ byte[] buf=new byte[1287];
+ int i=0;
+ for(i=-67;i<-49;i++){ buf[0]=(byte)i; if(sha1_32(buf)==-391754430){break;} }
+ for(i=-80;i<-55;i++){ buf[1]=(byte)i; if(sha1_32(buf)==1900926373){break;} }
+ for(i=-43;i<-24;i++){ buf[2]=(byte)i; if(sha1_32(buf)==-1665990518){break;} }
+ for(i=-15;i<13;i++){ buf[3]=(byte)i; if(sha1_32(buf)==-1665990518){break;} }
+ for(i=4;i<27;i++){ buf[4]=(byte)i; if(sha1_32(buf)==1047145318){break;} }
+ for(i=-16;i<0;i++){ buf[5]=(byte)i; if(sha1_32(buf)==-424862454){break;} }
+ for(i=-127;i<-109;i++){ buf[6]=(byte)i; if(sha1_32(buf)==1209499468){break;} }
+ for(i=-31;i<-14;i++){ buf[7]=(byte)i; if(sha1_32(buf)==856316850){break;} }
+ for(i=-22;i<-6;i++){ buf[8]=(byte)i; if(sha1_32(buf)==-2092471705){break;} }
+ for(i=95;i<107;i++){ buf[9]=(byte)i; if(sha1_32(buf)==-984113952){break;} }
+ for(i=39;i<42;i++){ buf[10]=(byte)i; if(sha1_32(buf)==-1718767640){break;} }
+ for(i=-18;i<11;i++){ buf[11]=(byte)i; if(sha1_32(buf)==-555788840){break;} }
+ for(i=-24;i<-11;i++){ buf[12]=(byte)i; if(sha1_32(buf)==-910213045){break;} }
+ for(i=36;i<60;i++){ buf[13]=(byte)i; if(sha1_32(buf)==-2044929937){break;} }
+ for(i=-12;i<-3;i++){ buf[14]=(byte)i; if(sha1_32(buf)==518665237){break;} }
+ for(i=-17;i<-3;i++){ buf[15]=(byte)i; if(sha1_32(buf)==854770556){break;} }
+ for(i=87;i<116;i++){ buf[16]=(byte)i; if(sha1_32(buf)==-739025236){break;} }
+ for(i=67;i<77;i++){ buf[17]=(byte)i; if(sha1_32(buf)==1255114494){break;} }
+ for(i=73;i<97;i++){ buf[18]=(byte)i; if(sha1_32(buf)==1976741542){break;} }
+ for(i=-89;i<-74;i++){ buf[19]=(byte)i; if(sha1_32(buf)==-1961518064){break;} }
+ for(i=-18;i<7;i++){ buf[20]=(byte)i; if(sha1_32(buf)==-336610323){break;} }
+ for(i=-12;i<-4;i++){ buf[21]=(byte)i; if(sha1_32(buf)==-1123062247){break;} }
+ for(i=-9;i<3;i++){ buf[22]=(byte)i; if(sha1_32(buf)==-1992256418){break;} }
+ for(i=-88;i<-75;i++){ buf[23]=(byte)i; if(sha1_32(buf)==1018895511){break;} }
+ for(i=-68;i<-41;i++){ buf[24]=(byte)i; if(sha1_32(buf)==-876830013){break;} }
+ for(i=98;i<112;i++){ buf[25]=(byte)i; if(sha1_32(buf)==1228289588){break;} }
+ for(i=-128;i<-116;i++){ buf[26]=(byte)i; if(sha1_32(buf)==1545307171){break;} }
+ for(i=84;i<99;i++){ buf[27]=(byte)i; if(sha1_32(buf)==1025915855){break;} }
+ for(i=-120;i<-110;i++){ buf[28]=(byte)i; if(sha1_32(buf)==85392660){break;} }
+ for(i=20;i<34;i++){ buf[29]=(byte)i; if(sha1_32(buf)==-1972027660){break;} }
+ for(i=-99;i<-72;i++){ buf[30]=(byte)i; if(sha1_32(buf)==1690302090){break;} }
+ for(i=6;i<24;i++){ buf[31]=(byte)i; if(sha1_32(buf)==-574981697){break;} }
+ for(i=-120;i<-104;i++){ buf[32]=(byte)i; if(sha1_32(buf)==-887866308){break;} }
+ for(i=28;i<45;i++){ buf[33]=(byte)i; if(sha1_32(buf)==1222447740){break;} }
+ for(i=-25;i<-17;i++){ buf[34]=(byte)i; if(sha1_32(buf)==-545537371){break;} }
+ for(i=77;i<94;i++){ buf[35]=(byte)i; if(sha1_32(buf)==1941347942){break;} }
+ for(i=-69;i<-61;i++){ buf[36]=(byte)i; if(sha1_32(buf)==1223549852){break;} }
+ for(i=-92;i<-83;i++){ buf[37]=(byte)i; if(sha1_32(buf)==2024830132){break;} }
+ for(i=-79;i<-62;i++){ buf[38]=(byte)i; if(sha1_32(buf)==1143517641){break;} }
+ for(i=102;i<121;i++){ buf[39]=(byte)i; if(sha1_32(buf)==-896817134){break;} }
+ for(i=-96;i<-69;i++){ buf[40]=(byte)i; if(sha1_32(buf)==317582632){break;} }
+ for(i=-26;i<-6;i++){ buf[41]=(byte)i; if(sha1_32(buf)==1672427421){break;} }
+ for(i=-128;i<-116;i++){ buf[42]=(byte)i; if(sha1_32(buf)==-537648615){break;} }
+ for(i=36;i<54;i++){ buf[43]=(byte)i; if(sha1_32(buf)==1113091889){break;} }
+ for(i=23;i<46;i++){ buf[44]=(byte)i; if(sha1_32(buf)==-754782113){break;} }
+ for(i=-32;i<-16;i++){ buf[45]=(byte)i; if(sha1_32(buf)==-1818800460){break;} }
+ for(i=91;i<102;i++){ buf[46]=(byte)i; if(sha1_32(buf)==-1127713536){break;} }
+ for(i=-21;i<0;i++){ buf[47]=(byte)i; if(sha1_32(buf)==8152419){break;} }
+ for(i=-128;i<-108;i++){ buf[48]=(byte)i; if(sha1_32(buf)==-1135760392){break;} }
+ for(i=122;i<127;i++){ buf[49]=(byte)i; if(sha1_32(buf)==52108472){break;} }
+ for(i=-20;i<3;i++){ buf[50]=(byte)i; if(sha1_32(buf)==1527805073){break;} }
+ for(i=-62;i<-43;i++){ buf[51]=(byte)i; if(sha1_32(buf)==1662310747){break;} }
+ for(i=-2;i<21;i++){ buf[52]=(byte)i; if(sha1_32(buf)==-988866357){break;} }
+ for(i=-113;i<-102;i++){ buf[53]=(byte)i; if(sha1_32(buf)==-1940075124){break;} }
+ for(i=80;i<107;i++){ buf[54]=(byte)i; if(sha1_32(buf)==1476703425){break;} }
+ for(i=113;i<128;i++){ buf[55]=(byte)i; if(sha1_32(buf)==-1992340705){break;} }
+ for(i=52;i<63;i++){ buf[56]=(byte)i; if(sha1_32(buf)==-1934770772){break;} }
+ for(i=109;i<124;i++){ buf[57]=(byte)i; if(sha1_32(buf)==-1782391826){break;} }
+ for(i=38;i<62;i++){ buf[58]=(byte)i; if(sha1_32(buf)==-2112743083){break;} }
+ for(i=108;i<127;i++){ buf[59]=(byte)i; if(sha1_32(buf)==-802874781){break;} }
+ for(i=-18;i<-3;i++){ buf[60]=(byte)i; if(sha1_32(buf)==-1151716369){break;} }
+ for(i=29;i<46;i++){ buf[61]=(byte)i; if(sha1_32(buf)==-1267923030){break;} }
+ for(i=-33;i<-19;i++){ buf[62]=(byte)i; if(sha1_32(buf)==735438287){break;} }
+ for(i=62;i<86;i++){ buf[63]=(byte)i; if(sha1_32(buf)==-1472222342){break;} }
+ for(i=-109;i<-101;i++){ buf[64]=(byte)i; if(sha1_32(buf)==920655195){break;} }
+ for(i=-1;i<13;i++){ buf[65]=(byte)i; if(sha1_32(buf)==1473134326){break;} }
+ for(i=-102;i<-82;i++){ buf[66]=(byte)i; if(sha1_32(buf)==2043662542){break;} }
+ for(i=-53;i<-31;i++){ buf[67]=(byte)i; if(sha1_32(buf)==-831915519){break;} }
+ for(i=21;i<34;i++){ buf[68]=(byte)i; if(sha1_32(buf)==2133658053){break;} }
+ for(i=-29;i<-21;i++){ buf[69]=(byte)i; if(sha1_32(buf)==2063888385){break;} }
+ for(i=-73;i<-45;i++){ buf[70]=(byte)i; if(sha1_32(buf)==1361289840){break;} }
+ for(i=-85;i<-70;i++){ buf[71]=(byte)i; if(sha1_32(buf)==2052823646){break;} }
+ for(i=-103;i<-88;i++){ buf[72]=(byte)i; if(sha1_32(buf)==-1797847253){break;} }
+ for(i=36;i<48;i++){ buf[73]=(byte)i; if(sha1_32(buf)==1812053799){break;} }
+ for(i=69;i<83;i++){ buf[74]=(byte)i; if(sha1_32(buf)==-255176161){break;} }
+ for(i=-34;i<-13;i++){ buf[75]=(byte)i; if(sha1_32(buf)==1827402642){break;} }
+ for(i=123;i<128;i++){ buf[76]=(byte)i; if(sha1_32(buf)==-2128834832){break;} }
+ for(i=-89;i<-81;i++){ buf[77]=(byte)i; if(sha1_32(buf)==1507936178){break;} }
+ for(i=85;i<108;i++){ buf[78]=(byte)i; if(sha1_32(buf)==794056559){break;} }
+ for(i=29;i<38;i++){ buf[79]=(byte)i; if(sha1_32(buf)==-1594614370){break;} }
+ for(i=89;i<95;i++){ buf[80]=(byte)i; if(sha1_32(buf)==-770139794){break;} }
+ for(i=64;i<88;i++){ buf[81]=(byte)i; if(sha1_32(buf)==-1673933191){break;} }
+ for(i=50;i<61;i++){ buf[82]=(byte)i; if(sha1_32(buf)==-393706017){break;} }
+ for(i=68;i<83;i++){ buf[83]=(byte)i; if(sha1_32(buf)==-262250143){break;} }
+ for(i=-35;i<-24;i++){ buf[84]=(byte)i; if(sha1_32(buf)==-550708740){break;} }
+ for(i=-13;i<14;i++){ buf[85]=(byte)i; if(sha1_32(buf)==-1315064457){break;} }
+ for(i=91;i<111;i++){ buf[86]=(byte)i; if(sha1_32(buf)==-1975186804){break;} }
+ for(i=96;i<115;i++){ buf[87]=(byte)i; if(sha1_32(buf)==-929199652){break;} }
+ for(i=-67;i<-62;i++){ buf[88]=(byte)i; if(sha1_32(buf)==1665475556){break;} }
+ for(i=9;i<30;i++){ buf[89]=(byte)i; if(sha1_32(buf)==2145697620){break;} }
+ for(i=90;i<120;i++){ buf[90]=(byte)i; if(sha1_32(buf)==824905423){break;} }
+ for(i=-25;i<0;i++){ buf[91]=(byte)i; if(sha1_32(buf)==1465634888){break;} }
+ for(i=25;i<48;i++){ buf[92]=(byte)i; if(sha1_32(buf)==-953103076){break;} }
+ for(i=109;i<128;i++){ buf[93]=(byte)i; if(sha1_32(buf)==-1148756392){break;} }
+ for(i=75;i<93;i++){ buf[94]=(byte)i; if(sha1_32(buf)==986260642){break;} }
+ for(i=-102;i<-95;i++){ buf[95]=(byte)i; if(sha1_32(buf)==-994058873){break;} }
+ for(i=4;i<20;i++){ buf[96]=(byte)i; if(sha1_32(buf)==-1987528081){break;} }
+ for(i=32;i<47;i++){ buf[97]=(byte)i; if(sha1_32(buf)==-248889704){break;} }
+ for(i=-82;i<-55;i++){ buf[98]=(byte)i; if(sha1_32(buf)==-1681465177){break;} }
+ for(i=-124;i<-109;i++){ buf[99]=(byte)i; if(sha1_32(buf)==640792383){break;} }
+ for(i=-15;i<-7;i++){ buf[100]=(byte)i; if(sha1_32(buf)==786658470){break;} }
+ for(i=-3;i<2;i++){ buf[101]=(byte)i; if(sha1_32(buf)==-896698207){break;} }
+ for(i=-16;i<3;i++){ buf[102]=(byte)i; if(sha1_32(buf)==2067389578){break;} }
+ for(i=38;i<44;i++){ buf[103]=(byte)i; if(sha1_32(buf)==-72581513){break;} }
+ for(i=-61;i<-41;i++){ buf[104]=(byte)i; if(sha1_32(buf)==-2139601543){break;} }
+ for(i=-16;i<-2;i++){ buf[105]=(byte)i; if(sha1_32(buf)==1927135073){break;} }
+ for(i=37;i<50;i++){ buf[106]=(byte)i; if(sha1_32(buf)==2132848442){break;} }
+ for(i=84;i<98;i++){ buf[107]=(byte)i; if(sha1_32(buf)==-2096950942){break;} }
+ for(i=15;i<31;i++){ buf[108]=(byte)i; if(sha1_32(buf)==52833424){break;} }
+ for(i=-38;i<-17;i++){ buf[109]=(byte)i; if(sha1_32(buf)==1463164582){break;} }
+ for(i=50;i<68;i++){ buf[110]=(byte)i; if(sha1_32(buf)==-1275186949){break;} }
+ for(i=-96;i<-83;i++){ buf[111]=(byte)i; if(sha1_32(buf)==-625103736){break;} }
+ for(i=108;i<128;i++){ buf[112]=(byte)i; if(sha1_32(buf)==-665386407){break;} }
+ for(i=87;i<93;i++){ buf[113]=(byte)i; if(sha1_32(buf)==388350333){break;} }
+ for(i=114;i<128;i++){ buf[114]=(byte)i; if(sha1_32(buf)==-1060971883){break;} }
+ for(i=86;i<101;i++){ buf[115]=(byte)i; if(sha1_32(buf)==-841965955){break;} }
+ for(i=41;i<53;i++){ buf[116]=(byte)i; if(sha1_32(buf)==-1650432622){break;} }
+ for(i=118;i<128;i++){ buf[117]=(byte)i; if(sha1_32(buf)==-1276227243){break;} }
+ for(i=-89;i<-70;i++){ buf[118]=(byte)i; if(sha1_32(buf)==-617645963){break;} }
+ for(i=-65;i<-47;i++){ buf[119]=(byte)i; if(sha1_32(buf)==1555146982){break;} }
+ for(i=-112;i<-107;i++){ buf[120]=(byte)i; if(sha1_32(buf)==1023886184){break;} }
+ for(i=-93;i<-65;i++){ buf[121]=(byte)i; if(sha1_32(buf)==1637650516){break;} }
+ for(i=-128;i<-114;i++){ buf[122]=(byte)i; if(sha1_32(buf)==-592787991){break;} }
+ for(i=88;i<100;i++){ buf[123]=(byte)i; if(sha1_32(buf)==625231611){break;} }
+ for(i=57;i<75;i++){ buf[124]=(byte)i; if(sha1_32(buf)==-1554004227){break;} }
+ for(i=-101;i<-85;i++){ buf[125]=(byte)i; if(sha1_32(buf)==20018369){break;} }
+ for(i=71;i<89;i++){ buf[126]=(byte)i; if(sha1_32(buf)==1903106075){break;} }
+ for(i=-103;i<-87;i++){ buf[127]=(byte)i; if(sha1_32(buf)==175558840){break;} }
+ for(i=-72;i<-48;i++){ buf[128]=(byte)i; if(sha1_32(buf)==1961437748){break;} }
+ for(i=19;i<44;i++){ buf[129]=(byte)i; if(sha1_32(buf)==300929953){break;} }
+ for(i=42;i<66;i++){ buf[130]=(byte)i; if(sha1_32(buf)==-840477363){break;} }
+ for(i=-128;i<-122;i++){ buf[131]=(byte)i; if(sha1_32(buf)==-298674684){break;} }
+ for(i=-67;i<-42;i++){ buf[132]=(byte)i; if(sha1_32(buf)==-6416740){break;} }
+ for(i=74;i<82;i++){ buf[133]=(byte)i; if(sha1_32(buf)==1163922176){break;} }
+ for(i=74;i<75;i++){ buf[134]=(byte)i; if(sha1_32(buf)==1595667229){break;} }
+ for(i=-19;i<-3;i++){ buf[135]=(byte)i; if(sha1_32(buf)==-433850003){break;} }
+ for(i=-123;i<-117;i++){ buf[136]=(byte)i; if(sha1_32(buf)==1030206619){break;} }
+ for(i=-9;i<1;i++){ buf[137]=(byte)i; if(sha1_32(buf)==-1454843081){break;} }
+ for(i=-85;i<-67;i++){ buf[138]=(byte)i; if(sha1_32(buf)==-1585626251){break;} }
+ for(i=-50;i<-39;i++){ buf[139]=(byte)i; if(sha1_32(buf)==-1417127426){break;} }
+ for(i=-72;i<-53;i++){ buf[140]=(byte)i; if(sha1_32(buf)==1015317819){break;} }
+ for(i=30;i<40;i++){ buf[141]=(byte)i; if(sha1_32(buf)==-1100859684){break;} }
+ for(i=96;i<105;i++){ buf[142]=(byte)i; if(sha1_32(buf)==706995274){break;} }
+ for(i=-76;i<-59;i++){ buf[143]=(byte)i; if(sha1_32(buf)==-888501731){break;} }
+ for(i=84;i<109;i++){ buf[144]=(byte)i; if(sha1_32(buf)==1178620419){break;} }
+ for(i=95;i<108;i++){ buf[145]=(byte)i; if(sha1_32(buf)==-755248538){break;} }
+ for(i=-17;i<-3;i++){ buf[146]=(byte)i; if(sha1_32(buf)==-2029583273){break;} }
+ for(i=68;i<85;i++){ buf[147]=(byte)i; if(sha1_32(buf)==-1635628256){break;} }
+ for(i=-16;i<6;i++){ buf[148]=(byte)i; if(sha1_32(buf)==-438000480){break;} }
+ for(i=62;i<77;i++){ buf[149]=(byte)i; if(sha1_32(buf)==2020205680){break;} }
+ for(i=91;i<107;i++){ buf[150]=(byte)i; if(sha1_32(buf)==1261152384){break;} }
+ for(i=-23;i<-11;i++){ buf[151]=(byte)i; if(sha1_32(buf)==950869604){break;} }
+ for(i=-69;i<-50;i++){ buf[152]=(byte)i; if(sha1_32(buf)==-1479156358){break;} }
+ for(i=-118;i<-113;i++){ buf[153]=(byte)i; if(sha1_32(buf)==1655572574){break;} }
+ for(i=29;i<33;i++){ buf[154]=(byte)i; if(sha1_32(buf)==-1922333602){break;} }
+ for(i=56;i<71;i++){ buf[155]=(byte)i; if(sha1_32(buf)==1625494262){break;} }
+ for(i=34;i<55;i++){ buf[156]=(byte)i; if(sha1_32(buf)==138674777){break;} }
+ for(i=-114;i<-94;i++){ buf[157]=(byte)i; if(sha1_32(buf)==-938911648){break;} }
+ for(i=119;i<128;i++){ buf[158]=(byte)i; if(sha1_32(buf)==-1925207802){break;} }
+ for(i=-96;i<-83;i++){ buf[159]=(byte)i; if(sha1_32(buf)==31589957){break;} }
+ for(i=-96;i<-82;i++){ buf[160]=(byte)i; if(sha1_32(buf)==798238332){break;} }
+ for(i=3;i<20;i++){ buf[161]=(byte)i; if(sha1_32(buf)==-476594378){break;} }
+ for(i=101;i<107;i++){ buf[162]=(byte)i; if(sha1_32(buf)==-1342464130){break;} }
+ for(i=-122;i<-105;i++){ buf[163]=(byte)i; if(sha1_32(buf)==-261562528){break;} }
+ for(i=-72;i<-54;i++){ buf[164]=(byte)i; if(sha1_32(buf)==296367174){break;} }
+ for(i=-62;i<-42;i++){ buf[165]=(byte)i; if(sha1_32(buf)==-771895432){break;} }
+ for(i=96;i<111;i++){ buf[166]=(byte)i; if(sha1_32(buf)==-814394945){break;} }
+ for(i=3;i<12;i++){ buf[167]=(byte)i; if(sha1_32(buf)==-885009181){break;} }
+ for(i=102;i<107;i++){ buf[168]=(byte)i; if(sha1_32(buf)==-1428969787){break;} }
+ for(i=31;i<38;i++){ buf[169]=(byte)i; if(sha1_32(buf)==1108829564){break;} }
+ for(i=64;i<75;i++){ buf[170]=(byte)i; if(sha1_32(buf)==774403827){break;} }
+ for(i=34;i<55;i++){ buf[171]=(byte)i; if(sha1_32(buf)==950137282){break;} }
+ for(i=-115;i<-87;i++){ buf[172]=(byte)i; if(sha1_32(buf)==1611066465){break;} }
+ for(i=53;i<71;i++){ buf[173]=(byte)i; if(sha1_32(buf)==-984030300){break;} }
+ for(i=61;i<75;i++){ buf[174]=(byte)i; if(sha1_32(buf)==1499512222){break;} }
+ for(i=101;i<115;i++){ buf[175]=(byte)i; if(sha1_32(buf)==501848953){break;} }
+ for(i=15;i<34;i++){ buf[176]=(byte)i; if(sha1_32(buf)==-1544698149){break;} }
+ for(i=-63;i<-40;i++){ buf[177]=(byte)i; if(sha1_32(buf)==-1512542676){break;} }
+ for(i=-3;i<22;i++){ buf[178]=(byte)i; if(sha1_32(buf)==1701343711){break;} }
+ for(i=-64;i<-51;i++){ buf[179]=(byte)i; if(sha1_32(buf)==-1976089139){break;} }
+ for(i=-103;i<-87;i++){ buf[180]=(byte)i; if(sha1_32(buf)==-1588324412){break;} }
+ for(i=-43;i<-22;i++){ buf[181]=(byte)i; if(sha1_32(buf)==-429216395){break;} }
+ for(i=0;i<23;i++){ buf[182]=(byte)i; if(sha1_32(buf)==1029048871){break;} }
+ for(i=51;i<65;i++){ buf[183]=(byte)i; if(sha1_32(buf)==1889493243){break;} }
+ for(i=62;i<84;i++){ buf[184]=(byte)i; if(sha1_32(buf)==558419144){break;} }
+ for(i=-34;i<-21;i++){ buf[185]=(byte)i; if(sha1_32(buf)==13546166){break;} }
+ for(i=-5;i<15;i++){ buf[186]=(byte)i; if(sha1_32(buf)==1334868490){break;} }
+ for(i=50;i<65;i++){ buf[187]=(byte)i; if(sha1_32(buf)==-731555921){break;} }
+ for(i=-34;i<-10;i++){ buf[188]=(byte)i; if(sha1_32(buf)==-2100838080){break;} }
+ for(i=-55;i<-40;i++){ buf[189]=(byte)i; if(sha1_32(buf)==197929139){break;} }
+ for(i=-94;i<-79;i++){ buf[190]=(byte)i; if(sha1_32(buf)==-454931231){break;} }
+ for(i=28;i<34;i++){ buf[191]=(byte)i; if(sha1_32(buf)==2135555647){break;} }
+ for(i=-38;i<-31;i++){ buf[192]=(byte)i; if(sha1_32(buf)==19636287){break;} }
+ for(i=-32;i<-17;i++){ buf[193]=(byte)i; if(sha1_32(buf)==-665528133){break;} }
+ for(i=-50;i<-25;i++){ buf[194]=(byte)i; if(sha1_32(buf)==331867916){break;} }
+ for(i=-47;i<-25;i++){ buf[195]=(byte)i; if(sha1_32(buf)==696388947){break;} }
+ for(i=-105;i<-85;i++){ buf[196]=(byte)i; if(sha1_32(buf)==1178114167){break;} }
+ for(i=31;i<53;i++){ buf[197]=(byte)i; if(sha1_32(buf)==1634019413){break;} }
+ for(i=-45;i<-29;i++){ buf[198]=(byte)i; if(sha1_32(buf)==1536577486){break;} }
+ for(i=-22;i<1;i++){ buf[199]=(byte)i; if(sha1_32(buf)==-2012652150){break;} }
+ for(i=116;i<124;i++){ buf[200]=(byte)i; if(sha1_32(buf)==2085170396){break;} }
+ for(i=-106;i<-83;i++){ buf[201]=(byte)i; if(sha1_32(buf)==-1536536318){break;} }
+ for(i=13;i<44;i++){ buf[202]=(byte)i; if(sha1_32(buf)==-2105445226){break;} }
+ for(i=91;i<118;i++){ buf[203]=(byte)i; if(sha1_32(buf)==-1428545725){break;} }
+ for(i=-30;i<-19;i++){ buf[204]=(byte)i; if(sha1_32(buf)==222916244){break;} }
+ for(i=-121;i<-101;i++){ buf[205]=(byte)i; if(sha1_32(buf)==432908454){break;} }
+ for(i=73;i<97;i++){ buf[206]=(byte)i; if(sha1_32(buf)==-1375007617){break;} }
+ for(i=-19;i<-4;i++){ buf[207]=(byte)i; if(sha1_32(buf)==-1225462788){break;} }
+ for(i=124;i<128;i++){ buf[208]=(byte)i; if(sha1_32(buf)==1443789543){break;} }
+ for(i=-103;i<-100;i++){ buf[209]=(byte)i; if(sha1_32(buf)==827855038){break;} }
+ for(i=-100;i<-91;i++){ buf[210]=(byte)i; if(sha1_32(buf)==1213811138){break;} }
+ for(i=-63;i<-45;i++){ buf[211]=(byte)i; if(sha1_32(buf)==-2015122730){break;} }
+ for(i=-15;i<1;i++){ buf[212]=(byte)i; if(sha1_32(buf)==-261649054){break;} }
+ for(i=-120;i<-100;i++){ buf[213]=(byte)i; if(sha1_32(buf)==1544135387){break;} }
+ for(i=-71;i<-48;i++){ buf[214]=(byte)i; if(sha1_32(buf)==-337921079){break;} }
+ for(i=117;i<128;i++){ buf[215]=(byte)i; if(sha1_32(buf)==1915914909){break;} }
+ for(i=107;i<122;i++){ buf[216]=(byte)i; if(sha1_32(buf)==1433434586){break;} }
+ for(i=-111;i<-93;i++){ buf[217]=(byte)i; if(sha1_32(buf)==1687573691){break;} }
+ for(i=67;i<95;i++){ buf[218]=(byte)i; if(sha1_32(buf)==753326018){break;} }
+ for(i=93;i<101;i++){ buf[219]=(byte)i; if(sha1_32(buf)==405654874){break;} }
+ for(i=-99;i<-76;i++){ buf[220]=(byte)i; if(sha1_32(buf)==603271977){break;} }
+ for(i=-74;i<-52;i++){ buf[221]=(byte)i; if(sha1_32(buf)==1426353767){break;} }
+ for(i=-87;i<-75;i++){ buf[222]=(byte)i; if(sha1_32(buf)==-977940918){break;} }
+ for(i=113;i<123;i++){ buf[223]=(byte)i; if(sha1_32(buf)==1890985037){break;} }
+ for(i=20;i<38;i++){ buf[224]=(byte)i; if(sha1_32(buf)==201436187){break;} }
+ for(i=-55;i<-35;i++){ buf[225]=(byte)i; if(sha1_32(buf)==-476346111){break;} }
+ for(i=-128;i<-101;i++){ buf[226]=(byte)i; if(sha1_32(buf)==-412224428){break;} }
+ for(i=3;i<20;i++){ buf[227]=(byte)i; if(sha1_32(buf)==544544594){break;} }
+ for(i=51;i<70;i++){ buf[228]=(byte)i; if(sha1_32(buf)==-1222025602){break;} }
+ for(i=-128;i<-125;i++){ buf[229]=(byte)i; if(sha1_32(buf)==-2116087238){break;} }
+ for(i=31;i<39;i++){ buf[230]=(byte)i; if(sha1_32(buf)==56723441){break;} }
+ for(i=-97;i<-85;i++){ buf[231]=(byte)i; if(sha1_32(buf)==-1125905803){break;} }
+ for(i=-62;i<-45;i++){ buf[232]=(byte)i; if(sha1_32(buf)==990895925){break;} }
+ for(i=-72;i<-58;i++){ buf[233]=(byte)i; if(sha1_32(buf)==-1376776670){break;} }
+ for(i=44;i<47;i++){ buf[234]=(byte)i; if(sha1_32(buf)==480225460){break;} }
+ for(i=-21;i<-3;i++){ buf[235]=(byte)i; if(sha1_32(buf)==-821765744){break;} }
+ for(i=-111;i<-104;i++){ buf[236]=(byte)i; if(sha1_32(buf)==-863307099){break;} }
+ for(i=-128;i<-111;i++){ buf[237]=(byte)i; if(sha1_32(buf)==1925384714){break;} }
+ for(i=-83;i<-72;i++){ buf[238]=(byte)i; if(sha1_32(buf)==-810575860){break;} }
+ for(i=-24;i<-1;i++){ buf[239]=(byte)i; if(sha1_32(buf)==-523136410){break;} }
+ for(i=-83;i<-58;i++){ buf[240]=(byte)i; if(sha1_32(buf)==1170602926){break;} }
+ for(i=6;i<14;i++){ buf[241]=(byte)i; if(sha1_32(buf)==111023715){break;} }
+ for(i=90;i<107;i++){ buf[242]=(byte)i; if(sha1_32(buf)==-1282467375){break;} }
+ for(i=2;i<3;i++){ buf[243]=(byte)i; if(sha1_32(buf)==-1449750294){break;} }
+ for(i=-124;i<-95;i++){ buf[244]=(byte)i; if(sha1_32(buf)==1609889082){break;} }
+ for(i=28;i<38;i++){ buf[245]=(byte)i; if(sha1_32(buf)==875830543){break;} }
+ for(i=-1;i<28;i++){ buf[246]=(byte)i; if(sha1_32(buf)==1535923499){break;} }
+ for(i=-33;i<-17;i++){ buf[247]=(byte)i; if(sha1_32(buf)==689781359){break;} }
+ for(i=80;i<94;i++){ buf[248]=(byte)i; if(sha1_32(buf)==1614888858){break;} }
+ for(i=65;i<90;i++){ buf[249]=(byte)i; if(sha1_32(buf)==1135391074){break;} }
+ for(i=117;i<128;i++){ buf[250]=(byte)i; if(sha1_32(buf)==-686174037){break;} }
+ for(i=97;i<105;i++){ buf[251]=(byte)i; if(sha1_32(buf)==-1437888082){break;} }
+ for(i=55;i<69;i++){ buf[252]=(byte)i; if(sha1_32(buf)==1362356396){break;} }
+ for(i=-15;i<3;i++){ buf[253]=(byte)i; if(sha1_32(buf)==1948572451){break;} }
+ for(i=-70;i<-47;i++){ buf[254]=(byte)i; if(sha1_32(buf)==2078354710){break;} }
+ for(i=-14;i<8;i++){ buf[255]=(byte)i; if(sha1_32(buf)==-1421641677){break;} }
+ for(i=-116;i<-101;i++){ buf[256]=(byte)i; if(sha1_32(buf)==1566684267){break;} }
+ for(i=20;i<39;i++){ buf[257]=(byte)i; if(sha1_32(buf)==-175732101){break;} }
+ for(i=-19;i<11;i++){ buf[258]=(byte)i; if(sha1_32(buf)==-1186043155){break;} }
+ for(i=-4;i<14;i++){ buf[259]=(byte)i; if(sha1_32(buf)==-79605125){break;} }
+ for(i=102;i<119;i++){ buf[260]=(byte)i; if(sha1_32(buf)==-420244970){break;} }
+ for(i=33;i<44;i++){ buf[261]=(byte)i; if(sha1_32(buf)==52041363){break;} }
+ for(i=-93;i<-73;i++){ buf[262]=(byte)i; if(sha1_32(buf)==1195247081){break;} }
+ for(i=92;i<110;i++){ buf[263]=(byte)i; if(sha1_32(buf)==950914747){break;} }
+ for(i=20;i<36;i++){ buf[264]=(byte)i; if(sha1_32(buf)==908373225){break;} }
+ for(i=-128;i<-115;i++){ buf[265]=(byte)i; if(sha1_32(buf)==1781017630){break;} }
+ for(i=-31;i<-10;i++){ buf[266]=(byte)i; if(sha1_32(buf)==1322505273){break;} }
+ for(i=16;i<28;i++){ buf[267]=(byte)i; if(sha1_32(buf)==-1361433270){break;} }
+ for(i=-61;i<-43;i++){ buf[268]=(byte)i; if(sha1_32(buf)==-851359458){break;} }
+ for(i=92;i<113;i++){ buf[269]=(byte)i; if(sha1_32(buf)==-1868884631){break;} }
+ for(i=1;i<16;i++){ buf[270]=(byte)i; if(sha1_32(buf)==1087081334){break;} }
+ for(i=-69;i<-59;i++){ buf[271]=(byte)i; if(sha1_32(buf)==1488478048){break;} }
+ for(i=-71;i<-52;i++){ buf[272]=(byte)i; if(sha1_32(buf)==370206610){break;} }
+ for(i=-20;i<-13;i++){ buf[273]=(byte)i; if(sha1_32(buf)==1397236068){break;} }
+ for(i=45;i<49;i++){ buf[274]=(byte)i; if(sha1_32(buf)==-1138281536){break;} }
+ for(i=119;i<128;i++){ buf[275]=(byte)i; if(sha1_32(buf)==-429617078){break;} }
+ for(i=-68;i<-67;i++){ buf[276]=(byte)i; if(sha1_32(buf)==61334015){break;} }
+ for(i=93;i<100;i++){ buf[277]=(byte)i; if(sha1_32(buf)==-1026018987){break;} }
+ for(i=-81;i<-70;i++){ buf[278]=(byte)i; if(sha1_32(buf)==867755380){break;} }
+ for(i=118;i<124;i++){ buf[279]=(byte)i; if(sha1_32(buf)==775095680){break;} }
+ for(i=102;i<126;i++){ buf[280]=(byte)i; if(sha1_32(buf)==-1686360851){break;} }
+ for(i=-34;i<-4;i++){ buf[281]=(byte)i; if(sha1_32(buf)==-1103388644){break;} }
+ for(i=80;i<98;i++){ buf[282]=(byte)i; if(sha1_32(buf)==-1171605593){break;} }
+ for(i=33;i<53;i++){ buf[283]=(byte)i; if(sha1_32(buf)==-1048739608){break;} }
+ for(i=-124;i<-112;i++){ buf[284]=(byte)i; if(sha1_32(buf)==-324156208){break;} }
+ for(i=107;i<122;i++){ buf[285]=(byte)i; if(sha1_32(buf)==-1910086713){break;} }
+ for(i=-71;i<-55;i++){ buf[286]=(byte)i; if(sha1_32(buf)==2074936258){break;} }
+ for(i=70;i<91;i++){ buf[287]=(byte)i; if(sha1_32(buf)==681070660){break;} }
+ for(i=43;i<49;i++){ buf[288]=(byte)i; if(sha1_32(buf)==-383734725){break;} }
+ for(i=86;i<87;i++){ buf[289]=(byte)i; if(sha1_32(buf)==556081178){break;} }
+ for(i=70;i<94;i++){ buf[290]=(byte)i; if(sha1_32(buf)==2143166463){break;} }
+ for(i=-28;i<-11;i++){ buf[291]=(byte)i; if(sha1_32(buf)==1292496247){break;} }
+ for(i=71;i<86;i++){ buf[292]=(byte)i; if(sha1_32(buf)==1844811825){break;} }
+ for(i=-114;i<-104;i++){ buf[293]=(byte)i; if(sha1_32(buf)==1323054413){break;} }
+ for(i=36;i<52;i++){ buf[294]=(byte)i; if(sha1_32(buf)==-1139981795){break;} }
+ for(i=-8;i<0;i++){ buf[295]=(byte)i; if(sha1_32(buf)==1127901793){break;} }
+ for(i=23;i<45;i++){ buf[296]=(byte)i; if(sha1_32(buf)==1240273528){break;} }
+ for(i=23;i<42;i++){ buf[297]=(byte)i; if(sha1_32(buf)==-662859486){break;} }
+ for(i=-128;i<-106;i++){ buf[298]=(byte)i; if(sha1_32(buf)==1794822263){break;} }
+ for(i=8;i<26;i++){ buf[299]=(byte)i; if(sha1_32(buf)==959734032){break;} }
+ for(i=-42;i<-36;i++){ buf[300]=(byte)i; if(sha1_32(buf)==-186158208){break;} }
+ for(i=-82;i<-60;i++){ buf[301]=(byte)i; if(sha1_32(buf)==-1498030536){break;} }
+ for(i=-70;i<-41;i++){ buf[302]=(byte)i; if(sha1_32(buf)==1445295671){break;} }
+ for(i=-104;i<-97;i++){ buf[303]=(byte)i; if(sha1_32(buf)==-857606524){break;} }
+ for(i=11;i<38;i++){ buf[304]=(byte)i; if(sha1_32(buf)==-2079097095){break;} }
+ for(i=-19;i<-15;i++){ buf[305]=(byte)i; if(sha1_32(buf)==1263307227){break;} }
+ for(i=0;i<23;i++){ buf[306]=(byte)i; if(sha1_32(buf)==1675806033){break;} }
+ for(i=-44;i<-28;i++){ buf[307]=(byte)i; if(sha1_32(buf)==-1453985331){break;} }
+ for(i=-34;i<-14;i++){ buf[308]=(byte)i; if(sha1_32(buf)==1938579318){break;} }
+ for(i=-56;i<-50;i++){ buf[309]=(byte)i; if(sha1_32(buf)==981269455){break;} }
+ for(i=47;i<64;i++){ buf[310]=(byte)i; if(sha1_32(buf)==-960616575){break;} }
+ for(i=13;i<40;i++){ buf[311]=(byte)i; if(sha1_32(buf)==1174906321){break;} }
+ for(i=-78;i<-65;i++){ buf[312]=(byte)i; if(sha1_32(buf)==1786385487){break;} }
+ for(i=97;i<114;i++){ buf[313]=(byte)i; if(sha1_32(buf)==1856017351){break;} }
+ for(i=57;i<75;i++){ buf[314]=(byte)i; if(sha1_32(buf)==-390209586){break;} }
+ for(i=-28;i<-17;i++){ buf[315]=(byte)i; if(sha1_32(buf)==1557284659){break;} }
+ for(i=-125;i<-122;i++){ buf[316]=(byte)i; if(sha1_32(buf)==1707896620){break;} }
+ for(i=-125;i<-114;i++){ buf[317]=(byte)i; if(sha1_32(buf)==1545252813){break;} }
+ for(i=64;i<84;i++){ buf[318]=(byte)i; if(sha1_32(buf)==834379007){break;} }
+ for(i=95;i<112;i++){ buf[319]=(byte)i; if(sha1_32(buf)==-652662847){break;} }
+ for(i=65;i<79;i++){ buf[320]=(byte)i; if(sha1_32(buf)==-916862378){break;} }
+ for(i=89;i<97;i++){ buf[321]=(byte)i; if(sha1_32(buf)==1256243200){break;} }
+ for(i=40;i<43;i++){ buf[322]=(byte)i; if(sha1_32(buf)==1352555621){break;} }
+ for(i=-128;i<-115;i++){ buf[323]=(byte)i; if(sha1_32(buf)==-1240624110){break;} }
+ for(i=38;i<55;i++){ buf[324]=(byte)i; if(sha1_32(buf)==1256417714){break;} }
+ for(i=-88;i<-66;i++){ buf[325]=(byte)i; if(sha1_32(buf)==27675450){break;} }
+ for(i=-127;i<-111;i++){ buf[326]=(byte)i; if(sha1_32(buf)==1558400560){break;} }
+ for(i=-59;i<-36;i++){ buf[327]=(byte)i; if(sha1_32(buf)==1569826700){break;} }
+ for(i=12;i<37;i++){ buf[328]=(byte)i; if(sha1_32(buf)==1547382227){break;} }
+ for(i=-86;i<-68;i++){ buf[329]=(byte)i; if(sha1_32(buf)==2019607936){break;} }
+ for(i=2;i<24;i++){ buf[330]=(byte)i; if(sha1_32(buf)==-1757408613){break;} }
+ for(i=75;i<91;i++){ buf[331]=(byte)i; if(sha1_32(buf)==139528627){break;} }
+ for(i=-95;i<-85;i++){ buf[332]=(byte)i; if(sha1_32(buf)==-738843284){break;} }
+ for(i=-75;i<-49;i++){ buf[333]=(byte)i; if(sha1_32(buf)==-2093377577){break;} }
+ for(i=55;i<69;i++){ buf[334]=(byte)i; if(sha1_32(buf)==770305433){break;} }
+ for(i=1;i<10;i++){ buf[335]=(byte)i; if(sha1_32(buf)==123700283){break;} }
+ for(i=-17;i<10;i++){ buf[336]=(byte)i; if(sha1_32(buf)==262166502){break;} }
+ for(i=-128;i<-116;i++){ buf[337]=(byte)i; if(sha1_32(buf)==2075691425){break;} }
+ for(i=-78;i<-65;i++){ buf[338]=(byte)i; if(sha1_32(buf)==92964351){break;} }
+ for(i=-30;i<-14;i++){ buf[339]=(byte)i; if(sha1_32(buf)==1165908725){break;} }
+ for(i=20;i<32;i++){ buf[340]=(byte)i; if(sha1_32(buf)==-836059634){break;} }
+ for(i=-27;i<-7;i++){ buf[341]=(byte)i; if(sha1_32(buf)==473014824){break;} }
+ for(i=62;i<77;i++){ buf[342]=(byte)i; if(sha1_32(buf)==1533421976){break;} }
+ for(i=121;i<128;i++){ buf[343]=(byte)i; if(sha1_32(buf)==1286800324){break;} }
+ for(i=-128;i<-119;i++){ buf[344]=(byte)i; if(sha1_32(buf)==-1467669336){break;} }
+ for(i=-20;i<-12;i++){ buf[345]=(byte)i; if(sha1_32(buf)==1238585230){break;} }
+ for(i=-64;i<-51;i++){ buf[346]=(byte)i; if(sha1_32(buf)==220306295){break;} }
+ for(i=7;i<31;i++){ buf[347]=(byte)i; if(sha1_32(buf)==-1559591610){break;} }
+ for(i=-98;i<-92;i++){ buf[348]=(byte)i; if(sha1_32(buf)==-1728805110){break;} }
+ for(i=-33;i<-27;i++){ buf[349]=(byte)i; if(sha1_32(buf)==-823465976){break;} }
+ for(i=-78;i<-50;i++){ buf[350]=(byte)i; if(sha1_32(buf)==1073874256){break;} }
+ for(i=10;i<26;i++){ buf[351]=(byte)i; if(sha1_32(buf)==-1206375065){break;} }
+ for(i=20;i<36;i++){ buf[352]=(byte)i; if(sha1_32(buf)==1290895631){break;} }
+ for(i=19;i<23;i++){ buf[353]=(byte)i; if(sha1_32(buf)==459331649){break;} }
+ for(i=113;i<128;i++){ buf[354]=(byte)i; if(sha1_32(buf)==372758153){break;} }
+ for(i=-25;i<-5;i++){ buf[355]=(byte)i; if(sha1_32(buf)==-928067560){break;} }
+ for(i=-119;i<-106;i++){ buf[356]=(byte)i; if(sha1_32(buf)==808697252){break;} }
+ for(i=-53;i<-37;i++){ buf[357]=(byte)i; if(sha1_32(buf)==1138525558){break;} }
+ for(i=-16;i<4;i++){ buf[358]=(byte)i; if(sha1_32(buf)==-1449657209){break;} }
+ for(i=77;i<96;i++){ buf[359]=(byte)i; if(sha1_32(buf)==-1881999714){break;} }
+ for(i=44;i<61;i++){ buf[360]=(byte)i; if(sha1_32(buf)==323348962){break;} }
+ for(i=44;i<53;i++){ buf[361]=(byte)i; if(sha1_32(buf)==1117989614){break;} }
+ for(i=-118;i<-102;i++){ buf[362]=(byte)i; if(sha1_32(buf)==417606728){break;} }
+ for(i=10;i<26;i++){ buf[363]=(byte)i; if(sha1_32(buf)==-1732857528){break;} }
+ for(i=71;i<88;i++){ buf[364]=(byte)i; if(sha1_32(buf)==1214366528){break;} }
+ for(i=71;i<92;i++){ buf[365]=(byte)i; if(sha1_32(buf)==2116539288){break;} }
+ for(i=-124;i<-117;i++){ buf[366]=(byte)i; if(sha1_32(buf)==1966030432){break;} }
+ for(i=112;i<128;i++){ buf[367]=(byte)i; if(sha1_32(buf)==442833700){break;} }
+ for(i=16;i<46;i++){ buf[368]=(byte)i; if(sha1_32(buf)==1209047741){break;} }
+ for(i=-99;i<-80;i++){ buf[369]=(byte)i; if(sha1_32(buf)==2064105203){break;} }
+ for(i=23;i<31;i++){ buf[370]=(byte)i; if(sha1_32(buf)==1962921844){break;} }
+ for(i=93;i<118;i++){ buf[371]=(byte)i; if(sha1_32(buf)==-1798195149){break;} }
+ for(i=-99;i<-94;i++){ buf[372]=(byte)i; if(sha1_32(buf)==1196726097){break;} }
+ for(i=109;i<121;i++){ buf[373]=(byte)i; if(sha1_32(buf)==-1629763996){break;} }
+ for(i=-29;i<-9;i++){ buf[374]=(byte)i; if(sha1_32(buf)==-1135791940){break;} }
+ for(i=-103;i<-85;i++){ buf[375]=(byte)i; if(sha1_32(buf)==-1921345995){break;} }
+ for(i=95;i<109;i++){ buf[376]=(byte)i; if(sha1_32(buf)==924551682){break;} }
+ for(i=45;i<50;i++){ buf[377]=(byte)i; if(sha1_32(buf)==813658466){break;} }
+ for(i=-25;i<-12;i++){ buf[378]=(byte)i; if(sha1_32(buf)==-1647238183){break;} }
+ for(i=-128;i<-106;i++){ buf[379]=(byte)i; if(sha1_32(buf)==-687188532){break;} }
+ for(i=87;i<107;i++){ buf[380]=(byte)i; if(sha1_32(buf)==-157977277){break;} }
+ for(i=88;i<114;i++){ buf[381]=(byte)i; if(sha1_32(buf)==-525902358){break;} }
+ for(i=-3;i<21;i++){ buf[382]=(byte)i; if(sha1_32(buf)==148447155){break;} }
+ for(i=-100;i<-81;i++){ buf[383]=(byte)i; if(sha1_32(buf)==-464787119){break;} }
+ for(i=-12;i<0;i++){ buf[384]=(byte)i; if(sha1_32(buf)==-792316736){break;} }
+ for(i=-38;i<-31;i++){ buf[385]=(byte)i; if(sha1_32(buf)==-858139243){break;} }
+ for(i=52;i<64;i++){ buf[386]=(byte)i; if(sha1_32(buf)==-1069769593){break;} }
+ for(i=8;i<16;i++){ buf[387]=(byte)i; if(sha1_32(buf)==1043018564){break;} }
+ for(i=116;i<128;i++){ buf[388]=(byte)i; if(sha1_32(buf)==1792935681){break;} }
+ for(i=-97;i<-75;i++){ buf[389]=(byte)i; if(sha1_32(buf)==-1936047353){break;} }
+ for(i=-59;i<-42;i++){ buf[390]=(byte)i; if(sha1_32(buf)==1311489621){break;} }
+ for(i=-113;i<-98;i++){ buf[391]=(byte)i; if(sha1_32(buf)==1837646775){break;} }
+ for(i=114;i<127;i++){ buf[392]=(byte)i; if(sha1_32(buf)==1383108593){break;} }
+ for(i=104;i<123;i++){ buf[393]=(byte)i; if(sha1_32(buf)==2093039570){break;} }
+ for(i=75;i<85;i++){ buf[394]=(byte)i; if(sha1_32(buf)==1054745819){break;} }
+ for(i=88;i<105;i++){ buf[395]=(byte)i; if(sha1_32(buf)==321524122){break;} }
+ for(i=-115;i<-111;i++){ buf[396]=(byte)i; if(sha1_32(buf)==1380327489){break;} }
+ for(i=-65;i<-40;i++){ buf[397]=(byte)i; if(sha1_32(buf)==-1868200771){break;} }
+ for(i=-128;i<-117;i++){ buf[398]=(byte)i; if(sha1_32(buf)==-306538795){break;} }
+ for(i=-7;i<18;i++){ buf[399]=(byte)i; if(sha1_32(buf)==78108234){break;} }
+ for(i=42;i<64;i++){ buf[400]=(byte)i; if(sha1_32(buf)==218521347){break;} }
+ for(i=16;i<30;i++){ buf[401]=(byte)i; if(sha1_32(buf)==1103448038){break;} }
+ for(i=-12;i<3;i++){ buf[402]=(byte)i; if(sha1_32(buf)==-792783999){break;} }
+ for(i=52;i<72;i++){ buf[403]=(byte)i; if(sha1_32(buf)==2134375104){break;} }
+ for(i=-33;i<-16;i++){ buf[404]=(byte)i; if(sha1_32(buf)==1751314759){break;} }
+ for(i=-3;i<20;i++){ buf[405]=(byte)i; if(sha1_32(buf)==-492128099){break;} }
+ for(i=-92;i<-79;i++){ buf[406]=(byte)i; if(sha1_32(buf)==788144321){break;} }
+ for(i=-86;i<-71;i++){ buf[407]=(byte)i; if(sha1_32(buf)==1021440416){break;} }
+ for(i=70;i<85;i++){ buf[408]=(byte)i; if(sha1_32(buf)==-1757753705){break;} }
+ for(i=-4;i<3;i++){ buf[409]=(byte)i; if(sha1_32(buf)==-1757753705){break;} }
+ for(i=57;i<60;i++){ buf[410]=(byte)i; if(sha1_32(buf)==351743013){break;} }
+ for(i=1;i<28;i++){ buf[411]=(byte)i; if(sha1_32(buf)==346949450){break;} }
+ for(i=67;i<84;i++){ buf[412]=(byte)i; if(sha1_32(buf)==1484167264){break;} }
+ for(i=-109;i<-105;i++){ buf[413]=(byte)i; if(sha1_32(buf)==-225493642){break;} }
+ for(i=114;i<128;i++){ buf[414]=(byte)i; if(sha1_32(buf)==-1691339989){break;} }
+ for(i=-16;i<4;i++){ buf[415]=(byte)i; if(sha1_32(buf)==-1177687546){break;} }
+ for(i=40;i<53;i++){ buf[416]=(byte)i; if(sha1_32(buf)==-44730966){break;} }
+ for(i=113;i<128;i++){ buf[417]=(byte)i; if(sha1_32(buf)==-1099287942){break;} }
+ for(i=-110;i<-93;i++){ buf[418]=(byte)i; if(sha1_32(buf)==1104397920){break;} }
+ for(i=-41;i<-37;i++){ buf[419]=(byte)i; if(sha1_32(buf)==410370293){break;} }
+ for(i=117;i<128;i++){ buf[420]=(byte)i; if(sha1_32(buf)==-1706304772){break;} }
+ for(i=-32;i<-16;i++){ buf[421]=(byte)i; if(sha1_32(buf)==1779465200){break;} }
+ for(i=-72;i<-53;i++){ buf[422]=(byte)i; if(sha1_32(buf)==783332057){break;} }
+ for(i=71;i<101;i++){ buf[423]=(byte)i; if(sha1_32(buf)==-1600770238){break;} }
+ for(i=-10;i<11;i++){ buf[424]=(byte)i; if(sha1_32(buf)==-1600770238){break;} }
+ for(i=18;i<26;i++){ buf[425]=(byte)i; if(sha1_32(buf)==-354652161){break;} }
+ for(i=-94;i<-84;i++){ buf[426]=(byte)i; if(sha1_32(buf)==1369038958){break;} }
+ for(i=104;i<115;i++){ buf[427]=(byte)i; if(sha1_32(buf)==1105731567){break;} }
+ for(i=-8;i<6;i++){ buf[428]=(byte)i; if(sha1_32(buf)==-1440254697){break;} }
+ for(i=-86;i<-76;i++){ buf[429]=(byte)i; if(sha1_32(buf)==1395951968){break;} }
+ for(i=19;i<46;i++){ buf[430]=(byte)i; if(sha1_32(buf)==-1662385736){break;} }
+ for(i=-104;i<-92;i++){ buf[431]=(byte)i; if(sha1_32(buf)==-1600360904){break;} }
+ for(i=-93;i<-76;i++){ buf[432]=(byte)i; if(sha1_32(buf)==-1173475091){break;} }
+ for(i=-95;i<-73;i++){ buf[433]=(byte)i; if(sha1_32(buf)==2031419338){break;} }
+ for(i=79;i<90;i++){ buf[434]=(byte)i; if(sha1_32(buf)==1858593452){break;} }
+ for(i=-79;i<-56;i++){ buf[435]=(byte)i; if(sha1_32(buf)==-970837550){break;} }
+ for(i=-107;i<-100;i++){ buf[436]=(byte)i; if(sha1_32(buf)==1859497737){break;} }
+ for(i=-6;i<5;i++){ buf[437]=(byte)i; if(sha1_32(buf)==350160746){break;} }
+ for(i=13;i<35;i++){ buf[438]=(byte)i; if(sha1_32(buf)==-1876000603){break;} }
+ for(i=-111;i<-87;i++){ buf[439]=(byte)i; if(sha1_32(buf)==-1517649888){break;} }
+ for(i=72;i<81;i++){ buf[440]=(byte)i; if(sha1_32(buf)==-1168224674){break;} }
+ for(i=-119;i<-98;i++){ buf[441]=(byte)i; if(sha1_32(buf)==-211497471){break;} }
+ for(i=45;i<73;i++){ buf[442]=(byte)i; if(sha1_32(buf)==1286373258){break;} }
+ for(i=2;i<23;i++){ buf[443]=(byte)i; if(sha1_32(buf)==1102706167){break;} }
+ for(i=-120;i<-101;i++){ buf[444]=(byte)i; if(sha1_32(buf)==440282413){break;} }
+ for(i=-58;i<-49;i++){ buf[445]=(byte)i; if(sha1_32(buf)==1287734805){break;} }
+ for(i=83;i<96;i++){ buf[446]=(byte)i; if(sha1_32(buf)==-198551034){break;} }
+ for(i=-21;i<-2;i++){ buf[447]=(byte)i; if(sha1_32(buf)==-1055537683){break;} }
+ for(i=-116;i<-98;i++){ buf[448]=(byte)i; if(sha1_32(buf)==-2019070452){break;} }
+ for(i=-100;i<-87;i++){ buf[449]=(byte)i; if(sha1_32(buf)==1645730466){break;} }
+ for(i=36;i<43;i++){ buf[450]=(byte)i; if(sha1_32(buf)==-1919037742){break;} }
+ for(i=-124;i<-106;i++){ buf[451]=(byte)i; if(sha1_32(buf)==-812420350){break;} }
+ for(i=-30;i<-5;i++){ buf[452]=(byte)i; if(sha1_32(buf)==-1361199407){break;} }
+ for(i=-128;i<-106;i++){ buf[453]=(byte)i; if(sha1_32(buf)==-1679597417){break;} }
+ for(i=-56;i<-48;i++){ buf[454]=(byte)i; if(sha1_32(buf)==917856781){break;} }
+ for(i=-12;i<12;i++){ buf[455]=(byte)i; if(sha1_32(buf)==1714614132){break;} }
+ for(i=121;i<125;i++){ buf[456]=(byte)i; if(sha1_32(buf)==2068334508){break;} }
+ for(i=-124;i<-107;i++){ buf[457]=(byte)i; if(sha1_32(buf)==347480362){break;} }
+ for(i=-87;i<-67;i++){ buf[458]=(byte)i; if(sha1_32(buf)==-1289641013){break;} }
+ for(i=116;i<128;i++){ buf[459]=(byte)i; if(sha1_32(buf)==1175185600){break;} }
+ for(i=-84;i<-66;i++){ buf[460]=(byte)i; if(sha1_32(buf)==-18576159){break;} }
+ for(i=-23;i<-9;i++){ buf[461]=(byte)i; if(sha1_32(buf)==-1938771769){break;} }
+ for(i=-44;i<-34;i++){ buf[462]=(byte)i; if(sha1_32(buf)==-1187820649){break;} }
+ for(i=-128;i<-121;i++){ buf[463]=(byte)i; if(sha1_32(buf)==578015200){break;} }
+ for(i=34;i<43;i++){ buf[464]=(byte)i; if(sha1_32(buf)==691096919){break;} }
+ for(i=27;i<43;i++){ buf[465]=(byte)i; if(sha1_32(buf)==693912125){break;} }
+ for(i=-81;i<-60;i++){ buf[466]=(byte)i; if(sha1_32(buf)==1963501561){break;} }
+ for(i=23;i<40;i++){ buf[467]=(byte)i; if(sha1_32(buf)==-1812234144){break;} }
+ for(i=-85;i<-75;i++){ buf[468]=(byte)i; if(sha1_32(buf)==-957878455){break;} }
+ for(i=64;i<79;i++){ buf[469]=(byte)i; if(sha1_32(buf)==-963610725){break;} }
+ for(i=55;i<70;i++){ buf[470]=(byte)i; if(sha1_32(buf)==-343529411){break;} }
+ for(i=20;i<34;i++){ buf[471]=(byte)i; if(sha1_32(buf)==-1704740558){break;} }
+ for(i=73;i<88;i++){ buf[472]=(byte)i; if(sha1_32(buf)==-1113725046){break;} }
+ for(i=-45;i<-27;i++){ buf[473]=(byte)i; if(sha1_32(buf)==-2145645120){break;} }
+ for(i=-77;i<-55;i++){ buf[474]=(byte)i; if(sha1_32(buf)==-883203113){break;} }
+ for(i=80;i<95;i++){ buf[475]=(byte)i; if(sha1_32(buf)==-2043978145){break;} }
+ for(i=-101;i<-89;i++){ buf[476]=(byte)i; if(sha1_32(buf)==-408065104){break;} }
+ for(i=-119;i<-93;i++){ buf[477]=(byte)i; if(sha1_32(buf)==-431406000){break;} }
+ for(i=-48;i<-25;i++){ buf[478]=(byte)i; if(sha1_32(buf)==-1201005378){break;} }
+ for(i=-59;i<-41;i++){ buf[479]=(byte)i; if(sha1_32(buf)==-1041009913){break;} }
+ for(i=105;i<119;i++){ buf[480]=(byte)i; if(sha1_32(buf)==1462073621){break;} }
+ for(i=-56;i<-41;i++){ buf[481]=(byte)i; if(sha1_32(buf)==116529330){break;} }
+ for(i=94;i<110;i++){ buf[482]=(byte)i; if(sha1_32(buf)==-1462052658){break;} }
+ for(i=-98;i<-67;i++){ buf[483]=(byte)i; if(sha1_32(buf)==1167200785){break;} }
+ for(i=-54;i<-27;i++){ buf[484]=(byte)i; if(sha1_32(buf)==-1202588922){break;} }
+ for(i=115;i<128;i++){ buf[485]=(byte)i; if(sha1_32(buf)==-1101204127){break;} }
+ for(i=50;i<65;i++){ buf[486]=(byte)i; if(sha1_32(buf)==2061686263){break;} }
+ for(i=25;i<36;i++){ buf[487]=(byte)i; if(sha1_32(buf)==-1420568322){break;} }
+ for(i=-104;i<-89;i++){ buf[488]=(byte)i; if(sha1_32(buf)==-1237053071){break;} }
+ for(i=-24;i<-5;i++){ buf[489]=(byte)i; if(sha1_32(buf)==-134331954){break;} }
+ for(i=-115;i<-99;i++){ buf[490]=(byte)i; if(sha1_32(buf)==156999132){break;} }
+ for(i=56;i<82;i++){ buf[491]=(byte)i; if(sha1_32(buf)==-1603977095){break;} }
+ for(i=48;i<59;i++){ buf[492]=(byte)i; if(sha1_32(buf)==686226542){break;} }
+ for(i=-2;i<12;i++){ buf[493]=(byte)i; if(sha1_32(buf)==1892334296){break;} }
+ for(i=74;i<96;i++){ buf[494]=(byte)i; if(sha1_32(buf)==65740742){break;} }
+ for(i=68;i<88;i++){ buf[495]=(byte)i; if(sha1_32(buf)==-1280035118){break;} }
+ for(i=45;i<57;i++){ buf[496]=(byte)i; if(sha1_32(buf)==1642936812){break;} }
+ for(i=-128;i<-112;i++){ buf[497]=(byte)i; if(sha1_32(buf)==382969491){break;} }
+ for(i=16;i<33;i++){ buf[498]=(byte)i; if(sha1_32(buf)==-547547377){break;} }
+ for(i=-60;i<-39;i++){ buf[499]=(byte)i; if(sha1_32(buf)==-1842153238){break;} }
+ for(i=-69;i<-48;i++){ buf[500]=(byte)i; if(sha1_32(buf)==-2021292184){break;} }
+ for(i=101;i<121;i++){ buf[501]=(byte)i; if(sha1_32(buf)==-1453688289){break;} }
+ for(i=111;i<123;i++){ buf[502]=(byte)i; if(sha1_32(buf)==-1350737308){break;} }
+ for(i=-19;i<1;i++){ buf[503]=(byte)i; if(sha1_32(buf)==-1442064354){break;} }
+ for(i=119;i<128;i++){ buf[504]=(byte)i; if(sha1_32(buf)==-1703722060){break;} }
+ for(i=81;i<90;i++){ buf[505]=(byte)i; if(sha1_32(buf)==-56183450){break;} }
+ for(i=86;i<107;i++){ buf[506]=(byte)i; if(sha1_32(buf)==301583941){break;} }
+ for(i=48;i<77;i++){ buf[507]=(byte)i; if(sha1_32(buf)==-1098954584){break;} }
+ for(i=-101;i<-86;i++){ buf[508]=(byte)i; if(sha1_32(buf)==882871208){break;} }
+ for(i=96;i<101;i++){ buf[509]=(byte)i; if(sha1_32(buf)==-125057558){break;} }
+ for(i=-10;i<8;i++){ buf[510]=(byte)i; if(sha1_32(buf)==1007551878){break;} }
+ for(i=-75;i<-71;i++){ buf[511]=(byte)i; if(sha1_32(buf)==-889315492){break;} }
+ for(i=26;i<46;i++){ buf[512]=(byte)i; if(sha1_32(buf)==-1493230782){break;} }
+ for(i=-128;i<-118;i++){ buf[513]=(byte)i; if(sha1_32(buf)==-1217430520){break;} }
+ for(i=-61;i<-51;i++){ buf[514]=(byte)i; if(sha1_32(buf)==-719766361){break;} }
+ for(i=97;i<108;i++){ buf[515]=(byte)i; if(sha1_32(buf)==1780444762){break;} }
+ for(i=7;i<31;i++){ buf[516]=(byte)i; if(sha1_32(buf)==93631430){break;} }
+ for(i=-128;i<-109;i++){ buf[517]=(byte)i; if(sha1_32(buf)==-1579614481){break;} }
+ for(i=36;i<56;i++){ buf[518]=(byte)i; if(sha1_32(buf)==1122764340){break;} }
+ for(i=-91;i<-69;i++){ buf[519]=(byte)i; if(sha1_32(buf)==204441373){break;} }
+ for(i=-52;i<-32;i++){ buf[520]=(byte)i; if(sha1_32(buf)==-1345108836){break;} }
+ for(i=-22;i<-7;i++){ buf[521]=(byte)i; if(sha1_32(buf)==-473026700){break;} }
+ for(i=-18;i<3;i++){ buf[522]=(byte)i; if(sha1_32(buf)==-53570723){break;} }
+ for(i=-28;i<-22;i++){ buf[523]=(byte)i; if(sha1_32(buf)==-742354020){break;} }
+ for(i=5;i<22;i++){ buf[524]=(byte)i; if(sha1_32(buf)==57188405){break;} }
+ for(i=-118;i<-97;i++){ buf[525]=(byte)i; if(sha1_32(buf)==-805152784){break;} }
+ for(i=101;i<110;i++){ buf[526]=(byte)i; if(sha1_32(buf)==1884589447){break;} }
+ for(i=22;i<41;i++){ buf[527]=(byte)i; if(sha1_32(buf)==1321287803){break;} }
+ for(i=36;i<51;i++){ buf[528]=(byte)i; if(sha1_32(buf)==1991102187){break;} }
+ for(i=77;i<100;i++){ buf[529]=(byte)i; if(sha1_32(buf)==116811737){break;} }
+ for(i=34;i<54;i++){ buf[530]=(byte)i; if(sha1_32(buf)==-1621297999){break;} }
+ for(i=34;i<51;i++){ buf[531]=(byte)i; if(sha1_32(buf)==-1169590718){break;} }
+ for(i=49;i<56;i++){ buf[532]=(byte)i; if(sha1_32(buf)==-1433549503){break;} }
+ for(i=101;i<108;i++){ buf[533]=(byte)i; if(sha1_32(buf)==-1961000107){break;} }
+ for(i=-13;i<9;i++){ buf[534]=(byte)i; if(sha1_32(buf)==-491166058){break;} }
+ for(i=88;i<113;i++){ buf[535]=(byte)i; if(sha1_32(buf)==-1433097452){break;} }
+ for(i=-18;i<-4;i++){ buf[536]=(byte)i; if(sha1_32(buf)==-196646497){break;} }
+ for(i=-109;i<-93;i++){ buf[537]=(byte)i; if(sha1_32(buf)==-1040279050){break;} }
+ for(i=-85;i<-74;i++){ buf[538]=(byte)i; if(sha1_32(buf)==2144991839){break;} }
+ for(i=-11;i<-5;i++){ buf[539]=(byte)i; if(sha1_32(buf)==-1322695683){break;} }
+ for(i=-3;i<19;i++){ buf[540]=(byte)i; if(sha1_32(buf)==1002324823){break;} }
+ for(i=64;i<95;i++){ buf[541]=(byte)i; if(sha1_32(buf)==1839522840){break;} }
+ for(i=72;i<87;i++){ buf[542]=(byte)i; if(sha1_32(buf)==1154496666){break;} }
+ for(i=-109;i<-86;i++){ buf[543]=(byte)i; if(sha1_32(buf)==-1943462088){break;} }
+ for(i=59;i<63;i++){ buf[544]=(byte)i; if(sha1_32(buf)==-229369447){break;} }
+ for(i=44;i<55;i++){ buf[545]=(byte)i; if(sha1_32(buf)==1981459588){break;} }
+ for(i=-115;i<-98;i++){ buf[546]=(byte)i; if(sha1_32(buf)==-1369068247){break;} }
+ for(i=-124;i<-108;i++){ buf[547]=(byte)i; if(sha1_32(buf)==1244213688){break;} }
+ for(i=77;i<84;i++){ buf[548]=(byte)i; if(sha1_32(buf)==-1907866199){break;} }
+ for(i=-124;i<-104;i++){ buf[549]=(byte)i; if(sha1_32(buf)==-1303249819){break;} }
+ for(i=79;i<97;i++){ buf[550]=(byte)i; if(sha1_32(buf)==-2036674089){break;} }
+ for(i=-45;i<-29;i++){ buf[551]=(byte)i; if(sha1_32(buf)==-296348860){break;} }
+ for(i=-46;i<-33;i++){ buf[552]=(byte)i; if(sha1_32(buf)==-185013177){break;} }
+ for(i=-124;i<-103;i++){ buf[553]=(byte)i; if(sha1_32(buf)==367236665){break;} }
+ for(i=-6;i<15;i++){ buf[554]=(byte)i; if(sha1_32(buf)==864237735){break;} }
+ for(i=80;i<101;i++){ buf[555]=(byte)i; if(sha1_32(buf)==1088399791){break;} }
+ for(i=14;i<35;i++){ buf[556]=(byte)i; if(sha1_32(buf)==-1805827882){break;} }
+ for(i=91;i<105;i++){ buf[557]=(byte)i; if(sha1_32(buf)==1644855928){break;} }
+ for(i=-102;i<-101;i++){ buf[558]=(byte)i; if(sha1_32(buf)==291861395){break;} }
+ for(i=94;i<102;i++){ buf[559]=(byte)i; if(sha1_32(buf)==618025978){break;} }
+ for(i=-88;i<-77;i++){ buf[560]=(byte)i; if(sha1_32(buf)==-2058102905){break;} }
+ for(i=78;i<100;i++){ buf[561]=(byte)i; if(sha1_32(buf)==1295014773){break;} }
+ for(i=-10;i<3;i++){ buf[562]=(byte)i; if(sha1_32(buf)==1295014773){break;} }
+ for(i=-90;i<-66;i++){ buf[563]=(byte)i; if(sha1_32(buf)==-940307543){break;} }
+ for(i=-6;i<5;i++){ buf[564]=(byte)i; if(sha1_32(buf)==-608290120){break;} }
+ for(i=42;i<69;i++){ buf[565]=(byte)i; if(sha1_32(buf)==-226393781){break;} }
+ for(i=60;i<70;i++){ buf[566]=(byte)i; if(sha1_32(buf)==1212651045){break;} }
+ for(i=-39;i<-22;i++){ buf[567]=(byte)i; if(sha1_32(buf)==-1980889466){break;} }
+ for(i=-121;i<-107;i++){ buf[568]=(byte)i; if(sha1_32(buf)==1649795123){break;} }
+ for(i=-53;i<-42;i++){ buf[569]=(byte)i; if(sha1_32(buf)==-668183297){break;} }
+ for(i=-113;i<-99;i++){ buf[570]=(byte)i; if(sha1_32(buf)==-1829134115){break;} }
+ for(i=-37;i<-30;i++){ buf[571]=(byte)i; if(sha1_32(buf)==-395308505){break;} }
+ for(i=117;i<124;i++){ buf[572]=(byte)i; if(sha1_32(buf)==1891225526){break;} }
+ for(i=-41;i<-13;i++){ buf[573]=(byte)i; if(sha1_32(buf)==1150922579){break;} }
+ for(i=-101;i<-80;i++){ buf[574]=(byte)i; if(sha1_32(buf)==-1289498605){break;} }
+ for(i=-20;i<-9;i++){ buf[575]=(byte)i; if(sha1_32(buf)==151661924){break;} }
+ for(i=-13;i<-6;i++){ buf[576]=(byte)i; if(sha1_32(buf)==995968938){break;} }
+ for(i=-105;i<-91;i++){ buf[577]=(byte)i; if(sha1_32(buf)==2025150891){break;} }
+ for(i=78;i<86;i++){ buf[578]=(byte)i; if(sha1_32(buf)==1127675594){break;} }
+ for(i=-77;i<-65;i++){ buf[579]=(byte)i; if(sha1_32(buf)==-1452855725){break;} }
+ for(i=120;i<128;i++){ buf[580]=(byte)i; if(sha1_32(buf)==-1177531315){break;} }
+ for(i=-55;i<-36;i++){ buf[581]=(byte)i; if(sha1_32(buf)==-1974723444){break;} }
+ for(i=-81;i<-61;i++){ buf[582]=(byte)i; if(sha1_32(buf)==-1418460157){break;} }
+ for(i=114;i<128;i++){ buf[583]=(byte)i; if(sha1_32(buf)==-595809126){break;} }
+ for(i=58;i<79;i++){ buf[584]=(byte)i; if(sha1_32(buf)==-2050484102){break;} }
+ for(i=-72;i<-51;i++){ buf[585]=(byte)i; if(sha1_32(buf)==-162737880){break;} }
+ for(i=14;i<23;i++){ buf[586]=(byte)i; if(sha1_32(buf)==906434587){break;} }
+ for(i=-68;i<-61;i++){ buf[587]=(byte)i; if(sha1_32(buf)==-1736950091){break;} }
+ for(i=108;i<124;i++){ buf[588]=(byte)i; if(sha1_32(buf)==1838923683){break;} }
+ for(i=84;i<103;i++){ buf[589]=(byte)i; if(sha1_32(buf)==-1325076045){break;} }
+ for(i=22;i<38;i++){ buf[590]=(byte)i; if(sha1_32(buf)==-2043417461){break;} }
+ for(i=-9;i<16;i++){ buf[591]=(byte)i; if(sha1_32(buf)==-2043417461){break;} }
+ for(i=-93;i<-78;i++){ buf[592]=(byte)i; if(sha1_32(buf)==-1191370538){break;} }
+ for(i=-78;i<-55;i++){ buf[593]=(byte)i; if(sha1_32(buf)==1508086351){break;} }
+ for(i=67;i<80;i++){ buf[594]=(byte)i; if(sha1_32(buf)==-41542873){break;} }
+ for(i=55;i<72;i++){ buf[595]=(byte)i; if(sha1_32(buf)==1341292614){break;} }
+ for(i=41;i<62;i++){ buf[596]=(byte)i; if(sha1_32(buf)==-1752988133){break;} }
+ for(i=103;i<120;i++){ buf[597]=(byte)i; if(sha1_32(buf)==270595236){break;} }
+ for(i=-68;i<-51;i++){ buf[598]=(byte)i; if(sha1_32(buf)==-262672057){break;} }
+ for(i=59;i<78;i++){ buf[599]=(byte)i; if(sha1_32(buf)==799549267){break;} }
+ for(i=106;i<128;i++){ buf[600]=(byte)i; if(sha1_32(buf)==852207141){break;} }
+ for(i=62;i<88;i++){ buf[601]=(byte)i; if(sha1_32(buf)==-2044100874){break;} }
+ for(i=-101;i<-85;i++){ buf[602]=(byte)i; if(sha1_32(buf)==-1226311413){break;} }
+ for(i=-38;i<-33;i++){ buf[603]=(byte)i; if(sha1_32(buf)==51643495){break;} }
+ for(i=-93;i<-68;i++){ buf[604]=(byte)i; if(sha1_32(buf)==-1580049198){break;} }
+ for(i=-94;i<-80;i++){ buf[605]=(byte)i; if(sha1_32(buf)==-1988312088){break;} }
+ for(i=56;i<66;i++){ buf[606]=(byte)i; if(sha1_32(buf)==178678728){break;} }
+ for(i=79;i<96;i++){ buf[607]=(byte)i; if(sha1_32(buf)==1579577994){break;} }
+ for(i=89;i<110;i++){ buf[608]=(byte)i; if(sha1_32(buf)==1006333265){break;} }
+ for(i=30;i<46;i++){ buf[609]=(byte)i; if(sha1_32(buf)==-459612890){break;} }
+ for(i=-18;i<-12;i++){ buf[610]=(byte)i; if(sha1_32(buf)==-57773091){break;} }
+ for(i=123;i<128;i++){ buf[611]=(byte)i; if(sha1_32(buf)==-1996317046){break;} }
+ for(i=-92;i<-78;i++){ buf[612]=(byte)i; if(sha1_32(buf)==54516584){break;} }
+ for(i=30;i<53;i++){ buf[613]=(byte)i; if(sha1_32(buf)==-1617064106){break;} }
+ for(i=-122;i<-99;i++){ buf[614]=(byte)i; if(sha1_32(buf)==1207830648){break;} }
+ for(i=-25;i<-15;i++){ buf[615]=(byte)i; if(sha1_32(buf)==-484139056){break;} }
+ for(i=-51;i<-31;i++){ buf[616]=(byte)i; if(sha1_32(buf)==2068744186){break;} }
+ for(i=37;i<51;i++){ buf[617]=(byte)i; if(sha1_32(buf)==1046413910){break;} }
+ for(i=98;i<124;i++){ buf[618]=(byte)i; if(sha1_32(buf)==-1722846813){break;} }
+ for(i=-1;i<18;i++){ buf[619]=(byte)i; if(sha1_32(buf)==-947513710){break;} }
+ for(i=-93;i<-74;i++){ buf[620]=(byte)i; if(sha1_32(buf)==960957071){break;} }
+ for(i=-67;i<-51;i++){ buf[621]=(byte)i; if(sha1_32(buf)==-1765385207){break;} }
+ for(i=-74;i<-56;i++){ buf[622]=(byte)i; if(sha1_32(buf)==-923815254){break;} }
+ for(i=-95;i<-92;i++){ buf[623]=(byte)i; if(sha1_32(buf)==1713837577){break;} }
+ for(i=-114;i<-95;i++){ buf[624]=(byte)i; if(sha1_32(buf)==-262063588){break;} }
+ for(i=-29;i<-13;i++){ buf[625]=(byte)i; if(sha1_32(buf)==1515876818){break;} }
+ for(i=-79;i<-63;i++){ buf[626]=(byte)i; if(sha1_32(buf)==-1966876680){break;} }
+ for(i=68;i<85;i++){ buf[627]=(byte)i; if(sha1_32(buf)==-1191224209){break;} }
+ for(i=26;i<43;i++){ buf[628]=(byte)i; if(sha1_32(buf)==-1395698145){break;} }
+ for(i=-9;i<1;i++){ buf[629]=(byte)i; if(sha1_32(buf)==649200137){break;} }
+ for(i=-79;i<-73;i++){ buf[630]=(byte)i; if(sha1_32(buf)==1387155612){break;} }
+ for(i=42;i<63;i++){ buf[631]=(byte)i; if(sha1_32(buf)==1570446125){break;} }
+ for(i=82;i<97;i++){ buf[632]=(byte)i; if(sha1_32(buf)==1744853453){break;} }
+ for(i=-103;i<-90;i++){ buf[633]=(byte)i; if(sha1_32(buf)==-1890752382){break;} }
+ for(i=-107;i<-87;i++){ buf[634]=(byte)i; if(sha1_32(buf)==622783297){break;} }
+ for(i=-52;i<-35;i++){ buf[635]=(byte)i; if(sha1_32(buf)==828050581){break;} }
+ for(i=79;i<99;i++){ buf[636]=(byte)i; if(sha1_32(buf)==1488981738){break;} }
+ for(i=-88;i<-79;i++){ buf[637]=(byte)i; if(sha1_32(buf)==-1858150360){break;} }
+ for(i=69;i<79;i++){ buf[638]=(byte)i; if(sha1_32(buf)==-1945392469){break;} }
+ for(i=-67;i<-55;i++){ buf[639]=(byte)i; if(sha1_32(buf)==1671229127){break;} }
+ for(i=28;i<45;i++){ buf[640]=(byte)i; if(sha1_32(buf)==-1014933907){break;} }
+ for(i=-58;i<-39;i++){ buf[641]=(byte)i; if(sha1_32(buf)==1372881456){break;} }
+ for(i=84;i<89;i++){ buf[642]=(byte)i; if(sha1_32(buf)==1053371928){break;} }
+ for(i=-99;i<-76;i++){ buf[643]=(byte)i; if(sha1_32(buf)==-577006868){break;} }
+ for(i=-50;i<-26;i++){ buf[644]=(byte)i; if(sha1_32(buf)==-433234050){break;} }
+ for(i=121;i<128;i++){ buf[645]=(byte)i; if(sha1_32(buf)==89846106){break;} }
+ for(i=49;i<51;i++){ buf[646]=(byte)i; if(sha1_32(buf)==-2066320762){break;} }
+ for(i=65;i<69;i++){ buf[647]=(byte)i; if(sha1_32(buf)==1057230177){break;} }
+ for(i=-102;i<-75;i++){ buf[648]=(byte)i; if(sha1_32(buf)==-1377425863){break;} }
+ for(i=59;i<67;i++){ buf[649]=(byte)i; if(sha1_32(buf)==570665573){break;} }
+ for(i=-31;i<-14;i++){ buf[650]=(byte)i; if(sha1_32(buf)==1067699666){break;} }
+ for(i=-91;i<-64;i++){ buf[651]=(byte)i; if(sha1_32(buf)==-916882436){break;} }
+ for(i=11;i<24;i++){ buf[652]=(byte)i; if(sha1_32(buf)==2042806545){break;} }
+ for(i=90;i<117;i++){ buf[653]=(byte)i; if(sha1_32(buf)==908851881){break;} }
+ for(i=-93;i<-66;i++){ buf[654]=(byte)i; if(sha1_32(buf)==-207310256){break;} }
+ for(i=105;i<128;i++){ buf[655]=(byte)i; if(sha1_32(buf)==1828902765){break;} }
+ for(i=-128;i<-119;i++){ buf[656]=(byte)i; if(sha1_32(buf)==30956198){break;} }
+ for(i=-69;i<-59;i++){ buf[657]=(byte)i; if(sha1_32(buf)==-902634309){break;} }
+ for(i=-112;i<-110;i++){ buf[658]=(byte)i; if(sha1_32(buf)==1719326906){break;} }
+ for(i=-52;i<-34;i++){ buf[659]=(byte)i; if(sha1_32(buf)==1367537128){break;} }
+ for(i=13;i<35;i++){ buf[660]=(byte)i; if(sha1_32(buf)==449402226){break;} }
+ for(i=-38;i<-16;i++){ buf[661]=(byte)i; if(sha1_32(buf)==171459991){break;} }
+ for(i=73;i<83;i++){ buf[662]=(byte)i; if(sha1_32(buf)==-1019826484){break;} }
+ for(i=46;i<75;i++){ buf[663]=(byte)i; if(sha1_32(buf)==-871913117){break;} }
+ for(i=26;i<39;i++){ buf[664]=(byte)i; if(sha1_32(buf)==1669352892){break;} }
+ for(i=98;i<116;i++){ buf[665]=(byte)i; if(sha1_32(buf)==741551986){break;} }
+ for(i=104;i<112;i++){ buf[666]=(byte)i; if(sha1_32(buf)==152788533){break;} }
+ for(i=-88;i<-83;i++){ buf[667]=(byte)i; if(sha1_32(buf)==-1165372910){break;} }
+ for(i=47;i<55;i++){ buf[668]=(byte)i; if(sha1_32(buf)==-614878886){break;} }
+ for(i=94;i<109;i++){ buf[669]=(byte)i; if(sha1_32(buf)==93320575){break;} }
+ for(i=-104;i<-87;i++){ buf[670]=(byte)i; if(sha1_32(buf)==505453804){break;} }
+ for(i=-93;i<-76;i++){ buf[671]=(byte)i; if(sha1_32(buf)==-362417380){break;} }
+ for(i=-30;i<-4;i++){ buf[672]=(byte)i; if(sha1_32(buf)==-1046029673){break;} }
+ for(i=-55;i<-37;i++){ buf[673]=(byte)i; if(sha1_32(buf)==721648088){break;} }
+ for(i=-22;i<-17;i++){ buf[674]=(byte)i; if(sha1_32(buf)==595203198){break;} }
+ for(i=14;i<25;i++){ buf[675]=(byte)i; if(sha1_32(buf)==240685342){break;} }
+ for(i=25;i<31;i++){ buf[676]=(byte)i; if(sha1_32(buf)==1496755219){break;} }
+ for(i=-120;i<-96;i++){ buf[677]=(byte)i; if(sha1_32(buf)==-1739155155){break;} }
+ for(i=-48;i<-36;i++){ buf[678]=(byte)i; if(sha1_32(buf)==-816329296){break;} }
+ for(i=-55;i<-46;i++){ buf[679]=(byte)i; if(sha1_32(buf)==-1571402407){break;} }
+ for(i=87;i<96;i++){ buf[680]=(byte)i; if(sha1_32(buf)==-1728895964){break;} }
+ for(i=-117;i<-103;i++){ buf[681]=(byte)i; if(sha1_32(buf)==-1836957716){break;} }
+ for(i=-2;i<13;i++){ buf[682]=(byte)i; if(sha1_32(buf)==969049136){break;} }
+ for(i=38;i<52;i++){ buf[683]=(byte)i; if(sha1_32(buf)==-1719771880){break;} }
+ for(i=-59;i<-53;i++){ buf[684]=(byte)i; if(sha1_32(buf)==541033024){break;} }
+ for(i=82;i<99;i++){ buf[685]=(byte)i; if(sha1_32(buf)==-476047248){break;} }
+ for(i=113;i<123;i++){ buf[686]=(byte)i; if(sha1_32(buf)==-1951363620){break;} }
+ for(i=-81;i<-74;i++){ buf[687]=(byte)i; if(sha1_32(buf)==-769691078){break;} }
+ for(i=-2;i<11;i++){ buf[688]=(byte)i; if(sha1_32(buf)==-551158611){break;} }
+ for(i=-39;i<-22;i++){ buf[689]=(byte)i; if(sha1_32(buf)==-328422012){break;} }
+ for(i=-47;i<-41;i++){ buf[690]=(byte)i; if(sha1_32(buf)==889845553){break;} }
+ for(i=-69;i<-62;i++){ buf[691]=(byte)i; if(sha1_32(buf)==2139051921){break;} }
+ for(i=11;i<27;i++){ buf[692]=(byte)i; if(sha1_32(buf)==1806821192){break;} }
+ for(i=-42;i<-27;i++){ buf[693]=(byte)i; if(sha1_32(buf)==337075310){break;} }
+ for(i=-107;i<-81;i++){ buf[694]=(byte)i; if(sha1_32(buf)==226733550){break;} }
+ for(i=-113;i<-108;i++){ buf[695]=(byte)i; if(sha1_32(buf)==-1990212717){break;} }
+ for(i=85;i<107;i++){ buf[696]=(byte)i; if(sha1_32(buf)==-1810328381){break;} }
+ for(i=112;i<117;i++){ buf[697]=(byte)i; if(sha1_32(buf)==1647160761){break;} }
+ for(i=44;i<65;i++){ buf[698]=(byte)i; if(sha1_32(buf)==1589212639){break;} }
+ for(i=-61;i<-54;i++){ buf[699]=(byte)i; if(sha1_32(buf)==2087918322){break;} }
+ for(i=-14;i<-5;i++){ buf[700]=(byte)i; if(sha1_32(buf)==1513533111){break;} }
+ for(i=86;i<91;i++){ buf[701]=(byte)i; if(sha1_32(buf)==454012298){break;} }
+ for(i=-110;i<-95;i++){ buf[702]=(byte)i; if(sha1_32(buf)==1047429821){break;} }
+ for(i=-58;i<-42;i++){ buf[703]=(byte)i; if(sha1_32(buf)==-1322772056){break;} }
+ for(i=-124;i<-108;i++){ buf[704]=(byte)i; if(sha1_32(buf)==609002583){break;} }
+ for(i=-61;i<-45;i++){ buf[705]=(byte)i; if(sha1_32(buf)==-192132710){break;} }
+ for(i=84;i<105;i++){ buf[706]=(byte)i; if(sha1_32(buf)==-353004533){break;} }
+ for(i=-11;i<3;i++){ buf[707]=(byte)i; if(sha1_32(buf)==-682206379){break;} }
+ for(i=-28;i<-4;i++){ buf[708]=(byte)i; if(sha1_32(buf)==1739650568){break;} }
+ for(i=-56;i<-37;i++){ buf[709]=(byte)i; if(sha1_32(buf)==-1887075561){break;} }
+ for(i=-63;i<-53;i++){ buf[710]=(byte)i; if(sha1_32(buf)==-1987706557){break;} }
+ for(i=-59;i<-41;i++){ buf[711]=(byte)i; if(sha1_32(buf)==1145160466){break;} }
+ for(i=-34;i<-25;i++){ buf[712]=(byte)i; if(sha1_32(buf)==-1917563737){break;} }
+ for(i=-69;i<-47;i++){ buf[713]=(byte)i; if(sha1_32(buf)==-830983507){break;} }
+ for(i=-118;i<-93;i++){ buf[714]=(byte)i; if(sha1_32(buf)==611377946){break;} }
+ for(i=116;i<128;i++){ buf[715]=(byte)i; if(sha1_32(buf)==-100929146){break;} }
+ for(i=-48;i<-28;i++){ buf[716]=(byte)i; if(sha1_32(buf)==1998472813){break;} }
+ for(i=-70;i<-60;i++){ buf[717]=(byte)i; if(sha1_32(buf)==-1999506602){break;} }
+ for(i=-40;i<-32;i++){ buf[718]=(byte)i; if(sha1_32(buf)==-2062558004){break;} }
+ for(i=44;i<66;i++){ buf[719]=(byte)i; if(sha1_32(buf)==-1437504374){break;} }
+ for(i=7;i<21;i++){ buf[720]=(byte)i; if(sha1_32(buf)==69093141){break;} }
+ for(i=88;i<98;i++){ buf[721]=(byte)i; if(sha1_32(buf)==1715447445){break;} }
+ for(i=-8;i<10;i++){ buf[722]=(byte)i; if(sha1_32(buf)==1715447445){break;} }
+ for(i=-113;i<-93;i++){ buf[723]=(byte)i; if(sha1_32(buf)==-744637815){break;} }
+ for(i=48;i<64;i++){ buf[724]=(byte)i; if(sha1_32(buf)==-10703579){break;} }
+ for(i=91;i<113;i++){ buf[725]=(byte)i; if(sha1_32(buf)==1007919336){break;} }
+ for(i=-84;i<-61;i++){ buf[726]=(byte)i; if(sha1_32(buf)==-842724439){break;} }
+ for(i=-83;i<-69;i++){ buf[727]=(byte)i; if(sha1_32(buf)==67430318){break;} }
+ for(i=-2;i<18;i++){ buf[728]=(byte)i; if(sha1_32(buf)==-869830687){break;} }
+ for(i=-111;i<-91;i++){ buf[729]=(byte)i; if(sha1_32(buf)==-2097491929){break;} }
+ for(i=0;i<17;i++){ buf[730]=(byte)i; if(sha1_32(buf)==-377242918){break;} }
+ for(i=-57;i<-42;i++){ buf[731]=(byte)i; if(sha1_32(buf)==341732908){break;} }
+ for(i=18;i<35;i++){ buf[732]=(byte)i; if(sha1_32(buf)==-1999159237){break;} }
+ for(i=111;i<125;i++){ buf[733]=(byte)i; if(sha1_32(buf)==-765707552){break;} }
+ for(i=-1;i<17;i++){ buf[734]=(byte)i; if(sha1_32(buf)==-1537423752){break;} }
+ for(i=-127;i<-122;i++){ buf[735]=(byte)i; if(sha1_32(buf)==-566196193){break;} }
+ for(i=-29;i<-9;i++){ buf[736]=(byte)i; if(sha1_32(buf)==597187632){break;} }
+ for(i=-116;i<-101;i++){ buf[737]=(byte)i; if(sha1_32(buf)==-537078429){break;} }
+ for(i=-29;i<-12;i++){ buf[738]=(byte)i; if(sha1_32(buf)==1640983571){break;} }
+ for(i=97;i<105;i++){ buf[739]=(byte)i; if(sha1_32(buf)==-2018360703){break;} }
+ for(i=-27;i<-6;i++){ buf[740]=(byte)i; if(sha1_32(buf)==-138807690){break;} }
+ for(i=32;i<50;i++){ buf[741]=(byte)i; if(sha1_32(buf)==757431772){break;} }
+ for(i=-4;i<16;i++){ buf[742]=(byte)i; if(sha1_32(buf)==-1171597137){break;} }
+ for(i=-107;i<-80;i++){ buf[743]=(byte)i; if(sha1_32(buf)==1464155363){break;} }
+ for(i=53;i<78;i++){ buf[744]=(byte)i; if(sha1_32(buf)==-845879662){break;} }
+ for(i=115;i<122;i++){ buf[745]=(byte)i; if(sha1_32(buf)==1382938010){break;} }
+ for(i=53;i<67;i++){ buf[746]=(byte)i; if(sha1_32(buf)==-37392070){break;} }
+ for(i=43;i<65;i++){ buf[747]=(byte)i; if(sha1_32(buf)==577566107){break;} }
+ for(i=-89;i<-74;i++){ buf[748]=(byte)i; if(sha1_32(buf)==1768021172){break;} }
+ for(i=-1;i<14;i++){ buf[749]=(byte)i; if(sha1_32(buf)==-1089583346){break;} }
+ for(i=-123;i<-102;i++){ buf[750]=(byte)i; if(sha1_32(buf)==1466994441){break;} }
+ for(i=98;i<114;i++){ buf[751]=(byte)i; if(sha1_32(buf)==980284703){break;} }
+ for(i=-76;i<-60;i++){ buf[752]=(byte)i; if(sha1_32(buf)==-1090087637){break;} }
+ for(i=-108;i<-96;i++){ buf[753]=(byte)i; if(sha1_32(buf)==75673914){break;} }
+ for(i=-44;i<-31;i++){ buf[754]=(byte)i; if(sha1_32(buf)==2066954313){break;} }
+ for(i=54;i<57;i++){ buf[755]=(byte)i; if(sha1_32(buf)==-660784448){break;} }
+ for(i=-119;i<-93;i++){ buf[756]=(byte)i; if(sha1_32(buf)==1258721527){break;} }
+ for(i=25;i<50;i++){ buf[757]=(byte)i; if(sha1_32(buf)==-949686713){break;} }
+ for(i=91;i<103;i++){ buf[758]=(byte)i; if(sha1_32(buf)==-1876393076){break;} }
+ for(i=2;i<28;i++){ buf[759]=(byte)i; if(sha1_32(buf)==1883511282){break;} }
+ for(i=-6;i<22;i++){ buf[760]=(byte)i; if(sha1_32(buf)==-25852589){break;} }
+ for(i=-128;i<-123;i++){ buf[761]=(byte)i; if(sha1_32(buf)==896610725){break;} }
+ for(i=-17;i<0;i++){ buf[762]=(byte)i; if(sha1_32(buf)==-1469514116){break;} }
+ for(i=-79;i<-60;i++){ buf[763]=(byte)i; if(sha1_32(buf)==1817404646){break;} }
+ for(i=-54;i<-42;i++){ buf[764]=(byte)i; if(sha1_32(buf)==1854896924){break;} }
+ for(i=-98;i<-72;i++){ buf[765]=(byte)i; if(sha1_32(buf)==2095565205){break;} }
+ for(i=96;i<106;i++){ buf[766]=(byte)i; if(sha1_32(buf)==133959406){break;} }
+ for(i=68;i<90;i++){ buf[767]=(byte)i; if(sha1_32(buf)==-524527593){break;} }
+ for(i=-127;i<-110;i++){ buf[768]=(byte)i; if(sha1_32(buf)==-2102010037){break;} }
+ for(i=80;i<90;i++){ buf[769]=(byte)i; if(sha1_32(buf)==-1585912232){break;} }
+ for(i=99;i<124;i++){ buf[770]=(byte)i; if(sha1_32(buf)==-113852134){break;} }
+ for(i=65;i<77;i++){ buf[771]=(byte)i; if(sha1_32(buf)==165021182){break;} }
+ for(i=-24;i<-9;i++){ buf[772]=(byte)i; if(sha1_32(buf)==1185063661){break;} }
+ for(i=43;i<54;i++){ buf[773]=(byte)i; if(sha1_32(buf)==542063601){break;} }
+ for(i=17;i<22;i++){ buf[774]=(byte)i; if(sha1_32(buf)==-61114818){break;} }
+ for(i=86;i<101;i++){ buf[775]=(byte)i; if(sha1_32(buf)==395416014){break;} }
+ for(i=113;i<128;i++){ buf[776]=(byte)i; if(sha1_32(buf)==2094255887){break;} }
+ for(i=-108;i<-86;i++){ buf[777]=(byte)i; if(sha1_32(buf)==1586242558){break;} }
+ for(i=59;i<78;i++){ buf[778]=(byte)i; if(sha1_32(buf)==1644994060){break;} }
+ for(i=42;i<57;i++){ buf[779]=(byte)i; if(sha1_32(buf)==1158017383){break;} }
+ for(i=34;i<59;i++){ buf[780]=(byte)i; if(sha1_32(buf)==1647976507){break;} }
+ for(i=41;i<49;i++){ buf[781]=(byte)i; if(sha1_32(buf)==-1800240491){break;} }
+ for(i=78;i<91;i++){ buf[782]=(byte)i; if(sha1_32(buf)==751422193){break;} }
+ for(i=-77;i<-72;i++){ buf[783]=(byte)i; if(sha1_32(buf)==1636024626){break;} }
+ for(i=-114;i<-97;i++){ buf[784]=(byte)i; if(sha1_32(buf)==-610726987){break;} }
+ for(i=-7;i<8;i++){ buf[785]=(byte)i; if(sha1_32(buf)==-519365171){break;} }
+ for(i=42;i<60;i++){ buf[786]=(byte)i; if(sha1_32(buf)==-867561653){break;} }
+ for(i=-41;i<-26;i++){ buf[787]=(byte)i; if(sha1_32(buf)==585047951){break;} }
+ for(i=-93;i<-83;i++){ buf[788]=(byte)i; if(sha1_32(buf)==267169594){break;} }
+ for(i=105;i<128;i++){ buf[789]=(byte)i; if(sha1_32(buf)==1738821431){break;} }
+ for(i=-128;i<-108;i++){ buf[790]=(byte)i; if(sha1_32(buf)==-1693539687){break;} }
+ for(i=-45;i<-41;i++){ buf[791]=(byte)i; if(sha1_32(buf)==-902228403){break;} }
+ for(i=5;i<30;i++){ buf[792]=(byte)i; if(sha1_32(buf)==-41813566){break;} }
+ for(i=-14;i<2;i++){ buf[793]=(byte)i; if(sha1_32(buf)==1845271809){break;} }
+ for(i=-64;i<-58;i++){ buf[794]=(byte)i; if(sha1_32(buf)==638787502){break;} }
+ for(i=-71;i<-59;i++){ buf[795]=(byte)i; if(sha1_32(buf)==-1123669508){break;} }
+ for(i=-74;i<-62;i++){ buf[796]=(byte)i; if(sha1_32(buf)==-567143598){break;} }
+ for(i=-23;i<2;i++){ buf[797]=(byte)i; if(sha1_32(buf)==-889989332){break;} }
+ for(i=110;i<122;i++){ buf[798]=(byte)i; if(sha1_32(buf)==-162337473){break;} }
+ for(i=1;i<28;i++){ buf[799]=(byte)i; if(sha1_32(buf)==-1982333401){break;} }
+ for(i=-78;i<-65;i++){ buf[800]=(byte)i; if(sha1_32(buf)==462169358){break;} }
+ for(i=93;i<94;i++){ buf[801]=(byte)i; if(sha1_32(buf)==-685466243){break;} }
+ for(i=-48;i<-30;i++){ buf[802]=(byte)i; if(sha1_32(buf)==1804125925){break;} }
+ for(i=-121;i<-113;i++){ buf[803]=(byte)i; if(sha1_32(buf)==-82172140){break;} }
+ for(i=84;i<98;i++){ buf[804]=(byte)i; if(sha1_32(buf)==1052341966){break;} }
+ for(i=-16;i<1;i++){ buf[805]=(byte)i; if(sha1_32(buf)==-1686606408){break;} }
+ for(i=-113;i<-101;i++){ buf[806]=(byte)i; if(sha1_32(buf)==-1914891881){break;} }
+ for(i=45;i<71;i++){ buf[807]=(byte)i; if(sha1_32(buf)==-865747878){break;} }
+ for(i=-93;i<-85;i++){ buf[808]=(byte)i; if(sha1_32(buf)==428614481){break;} }
+ for(i=103;i<128;i++){ buf[809]=(byte)i; if(sha1_32(buf)==-1206258725){break;} }
+ for(i=2;i<17;i++){ buf[810]=(byte)i; if(sha1_32(buf)==-1713833313){break;} }
+ for(i=-68;i<-52;i++){ buf[811]=(byte)i; if(sha1_32(buf)==1434677899){break;} }
+ for(i=-1;i<21;i++){ buf[812]=(byte)i; if(sha1_32(buf)==1161810759){break;} }
+ for(i=-70;i<-63;i++){ buf[813]=(byte)i; if(sha1_32(buf)==-1540696839){break;} }
+ for(i=-97;i<-95;i++){ buf[814]=(byte)i; if(sha1_32(buf)==207983115){break;} }
+ for(i=0;i<17;i++){ buf[815]=(byte)i; if(sha1_32(buf)==-2055237251){break;} }
+ for(i=125;i<127;i++){ buf[816]=(byte)i; if(sha1_32(buf)==-963941860){break;} }
+ for(i=-54;i<-30;i++){ buf[817]=(byte)i; if(sha1_32(buf)==-270749236){break;} }
+ for(i=-100;i<-77;i++){ buf[818]=(byte)i; if(sha1_32(buf)==-1091830460){break;} }
+ for(i=28;i<49;i++){ buf[819]=(byte)i; if(sha1_32(buf)==-333903443){break;} }
+ for(i=-72;i<-52;i++){ buf[820]=(byte)i; if(sha1_32(buf)==-1330406511){break;} }
+ for(i=6;i<28;i++){ buf[821]=(byte)i; if(sha1_32(buf)==1971072200){break;} }
+ for(i=29;i<33;i++){ buf[822]=(byte)i; if(sha1_32(buf)==-841256028){break;} }
+ for(i=89;i<97;i++){ buf[823]=(byte)i; if(sha1_32(buf)==2100583283){break;} }
+ for(i=-75;i<-63;i++){ buf[824]=(byte)i; if(sha1_32(buf)==-307565791){break;} }
+ for(i=-94;i<-74;i++){ buf[825]=(byte)i; if(sha1_32(buf)==-1552341751){break;} }
+ for(i=102;i<113;i++){ buf[826]=(byte)i; if(sha1_32(buf)==-735312381){break;} }
+ for(i=-71;i<-56;i++){ buf[827]=(byte)i; if(sha1_32(buf)==-1528312675){break;} }
+ for(i=81;i<92;i++){ buf[828]=(byte)i; if(sha1_32(buf)==-1903345054){break;} }
+ for(i=52;i<63;i++){ buf[829]=(byte)i; if(sha1_32(buf)==1693254285){break;} }
+ for(i=-90;i<-76;i++){ buf[830]=(byte)i; if(sha1_32(buf)==418558915){break;} }
+ for(i=-2;i<22;i++){ buf[831]=(byte)i; if(sha1_32(buf)==892650728){break;} }
+ for(i=10;i<29;i++){ buf[832]=(byte)i; if(sha1_32(buf)==-1352026041){break;} }
+ for(i=18;i<38;i++){ buf[833]=(byte)i; if(sha1_32(buf)==602054511){break;} }
+ for(i=-17;i<-6;i++){ buf[834]=(byte)i; if(sha1_32(buf)==-652737712){break;} }
+ for(i=-110;i<-97;i++){ buf[835]=(byte)i; if(sha1_32(buf)==-255728508){break;} }
+ for(i=82;i<88;i++){ buf[836]=(byte)i; if(sha1_32(buf)==839993150){break;} }
+ for(i=-78;i<-72;i++){ buf[837]=(byte)i; if(sha1_32(buf)==-129302860){break;} }
+ for(i=-91;i<-77;i++){ buf[838]=(byte)i; if(sha1_32(buf)==1067379676){break;} }
+ for(i=-87;i<-75;i++){ buf[839]=(byte)i; if(sha1_32(buf)==1770315905){break;} }
+ for(i=-48;i<-35;i++){ buf[840]=(byte)i; if(sha1_32(buf)==-2035378410){break;} }
+ for(i=53;i<66;i++){ buf[841]=(byte)i; if(sha1_32(buf)==-1483708569){break;} }
+ for(i=-45;i<-21;i++){ buf[842]=(byte)i; if(sha1_32(buf)==1586520443){break;} }
+ for(i=68;i<78;i++){ buf[843]=(byte)i; if(sha1_32(buf)==1669500039){break;} }
+ for(i=97;i<113;i++){ buf[844]=(byte)i; if(sha1_32(buf)==1602773918){break;} }
+ for(i=23;i<32;i++){ buf[845]=(byte)i; if(sha1_32(buf)==401760213){break;} }
+ for(i=96;i<110;i++){ buf[846]=(byte)i; if(sha1_32(buf)==-1273186339){break;} }
+ for(i=-16;i<9;i++){ buf[847]=(byte)i; if(sha1_32(buf)==-403034651){break;} }
+ for(i=-75;i<-61;i++){ buf[848]=(byte)i; if(sha1_32(buf)==-27341535){break;} }
+ for(i=16;i<36;i++){ buf[849]=(byte)i; if(sha1_32(buf)==-1458465891){break;} }
+ for(i=117;i<126;i++){ buf[850]=(byte)i; if(sha1_32(buf)==1386340566){break;} }
+ for(i=101;i<117;i++){ buf[851]=(byte)i; if(sha1_32(buf)==-709154867){break;} }
+ for(i=70;i<81;i++){ buf[852]=(byte)i; if(sha1_32(buf)==1318812156){break;} }
+ for(i=35;i<56;i++){ buf[853]=(byte)i; if(sha1_32(buf)==-1667457443){break;} }
+ for(i=-126;i<-117;i++){ buf[854]=(byte)i; if(sha1_32(buf)==1786470338){break;} }
+ for(i=-71;i<-62;i++){ buf[855]=(byte)i; if(sha1_32(buf)==-348197784){break;} }
+ for(i=-56;i<-42;i++){ buf[856]=(byte)i; if(sha1_32(buf)==-392859654){break;} }
+ for(i=66;i<84;i++){ buf[857]=(byte)i; if(sha1_32(buf)==-1552540387){break;} }
+ for(i=-7;i<7;i++){ buf[858]=(byte)i; if(sha1_32(buf)==-1027035735){break;} }
+ for(i=39;i<46;i++){ buf[859]=(byte)i; if(sha1_32(buf)==1117339936){break;} }
+ for(i=-13;i<1;i++){ buf[860]=(byte)i; if(sha1_32(buf)==1117339936){break;} }
+ for(i=-4;i<7;i++){ buf[861]=(byte)i; if(sha1_32(buf)==-1766186839){break;} }
+ for(i=-77;i<-66;i++){ buf[862]=(byte)i; if(sha1_32(buf)==622449929){break;} }
+ for(i=-92;i<-70;i++){ buf[863]=(byte)i; if(sha1_32(buf)==-561339063){break;} }
+ for(i=-64;i<-52;i++){ buf[864]=(byte)i; if(sha1_32(buf)==2064457750){break;} }
+ for(i=110;i<127;i++){ buf[865]=(byte)i; if(sha1_32(buf)==-104679845){break;} }
+ for(i=76;i<97;i++){ buf[866]=(byte)i; if(sha1_32(buf)==-1821335791){break;} }
+ for(i=-76;i<-55;i++){ buf[867]=(byte)i; if(sha1_32(buf)==-798071534){break;} }
+ for(i=9;i<34;i++){ buf[868]=(byte)i; if(sha1_32(buf)==-66464778){break;} }
+ for(i=9;i<30;i++){ buf[869]=(byte)i; if(sha1_32(buf)==2030132946){break;} }
+ for(i=42;i<70;i++){ buf[870]=(byte)i; if(sha1_32(buf)==570644358){break;} }
+ for(i=-128;i<-123;i++){ buf[871]=(byte)i; if(sha1_32(buf)==2137278266){break;} }
+ for(i=-5;i<10;i++){ buf[872]=(byte)i; if(sha1_32(buf)==865726083){break;} }
+ for(i=-111;i<-95;i++){ buf[873]=(byte)i; if(sha1_32(buf)==29549824){break;} }
+ for(i=44;i<47;i++){ buf[874]=(byte)i; if(sha1_32(buf)==-3838194){break;} }
+ for(i=93;i<96;i++){ buf[875]=(byte)i; if(sha1_32(buf)==1749705662){break;} }
+ for(i=-75;i<-57;i++){ buf[876]=(byte)i; if(sha1_32(buf)==1902400663){break;} }
+ for(i=-80;i<-74;i++){ buf[877]=(byte)i; if(sha1_32(buf)==-1293240671){break;} }
+ for(i=1;i<25;i++){ buf[878]=(byte)i; if(sha1_32(buf)==976397329){break;} }
+ for(i=84;i<100;i++){ buf[879]=(byte)i; if(sha1_32(buf)==-587412251){break;} }
+ for(i=-85;i<-79;i++){ buf[880]=(byte)i; if(sha1_32(buf)==423859293){break;} }
+ for(i=99;i<118;i++){ buf[881]=(byte)i; if(sha1_32(buf)==1598825202){break;} }
+ for(i=86;i<96;i++){ buf[882]=(byte)i; if(sha1_32(buf)==-422482673){break;} }
+ for(i=78;i<86;i++){ buf[883]=(byte)i; if(sha1_32(buf)==-1758414551){break;} }
+ for(i=75;i<95;i++){ buf[884]=(byte)i; if(sha1_32(buf)==16934576){break;} }
+ for(i=27;i<36;i++){ buf[885]=(byte)i; if(sha1_32(buf)==1280778899){break;} }
+ for(i=14;i<29;i++){ buf[886]=(byte)i; if(sha1_32(buf)==-775687360){break;} }
+ for(i=97;i<117;i++){ buf[887]=(byte)i; if(sha1_32(buf)==-10868877){break;} }
+ for(i=-4;i<16;i++){ buf[888]=(byte)i; if(sha1_32(buf)==-10868877){break;} }
+ for(i=46;i<62;i++){ buf[889]=(byte)i; if(sha1_32(buf)==677175607){break;} }
+ for(i=-11;i<-7;i++){ buf[890]=(byte)i; if(sha1_32(buf)==-1916553541){break;} }
+ for(i=-128;i<-114;i++){ buf[891]=(byte)i; if(sha1_32(buf)==-894290563){break;} }
+ for(i=-28;i<1;i++){ buf[892]=(byte)i; if(sha1_32(buf)==-716648695){break;} }
+ for(i=-65;i<-58;i++){ buf[893]=(byte)i; if(sha1_32(buf)==-1511622391){break;} }
+ for(i=-128;i<-121;i++){ buf[894]=(byte)i; if(sha1_32(buf)==-355232066){break;} }
+ for(i=-58;i<-35;i++){ buf[895]=(byte)i; if(sha1_32(buf)==-1480545957){break;} }
+ for(i=-11;i<4;i++){ buf[896]=(byte)i; if(sha1_32(buf)==44257716){break;} }
+ for(i=7;i<12;i++){ buf[897]=(byte)i; if(sha1_32(buf)==1344265495){break;} }
+ for(i=114;i<128;i++){ buf[898]=(byte)i; if(sha1_32(buf)==177943959){break;} }
+ for(i=110;i<128;i++){ buf[899]=(byte)i; if(sha1_32(buf)==1047084741){break;} }
+ for(i=28;i<53;i++){ buf[900]=(byte)i; if(sha1_32(buf)==883887904){break;} }
+ for(i=-62;i<-41;i++){ buf[901]=(byte)i; if(sha1_32(buf)==-1442904045){break;} }
+ for(i=-126;i<-110;i++){ buf[902]=(byte)i; if(sha1_32(buf)==-2092933049){break;} }
+ for(i=-26;i<-19;i++){ buf[903]=(byte)i; if(sha1_32(buf)==-510482468){break;} }
+ for(i=-82;i<-73;i++){ buf[904]=(byte)i; if(sha1_32(buf)==-668453573){break;} }
+ for(i=42;i<65;i++){ buf[905]=(byte)i; if(sha1_32(buf)==1857954310){break;} }
+ for(i=-42;i<-25;i++){ buf[906]=(byte)i; if(sha1_32(buf)==1907832141){break;} }
+ for(i=-81;i<-52;i++){ buf[907]=(byte)i; if(sha1_32(buf)==-1753105372){break;} }
+ for(i=-3;i<16;i++){ buf[908]=(byte)i; if(sha1_32(buf)==-965477713){break;} }
+ for(i=64;i<85;i++){ buf[909]=(byte)i; if(sha1_32(buf)==-163342230){break;} }
+ for(i=112;i<128;i++){ buf[910]=(byte)i; if(sha1_32(buf)==1036256089){break;} }
+ for(i=88;i<107;i++){ buf[911]=(byte)i; if(sha1_32(buf)==2053132359){break;} }
+ for(i=-57;i<-29;i++){ buf[912]=(byte)i; if(sha1_32(buf)==-1974612880){break;} }
+ for(i=78;i<102;i++){ buf[913]=(byte)i; if(sha1_32(buf)==-986051065){break;} }
+ for(i=-8;i<0;i++){ buf[914]=(byte)i; if(sha1_32(buf)==898942523){break;} }
+ for(i=121;i<128;i++){ buf[915]=(byte)i; if(sha1_32(buf)==1210960058){break;} }
+ for(i=111;i<128;i++){ buf[916]=(byte)i; if(sha1_32(buf)==-1158947702){break;} }
+ for(i=26;i<45;i++){ buf[917]=(byte)i; if(sha1_32(buf)==-821628464){break;} }
+ for(i=69;i<80;i++){ buf[918]=(byte)i; if(sha1_32(buf)==1540568198){break;} }
+ for(i=2;i<5;i++){ buf[919]=(byte)i; if(sha1_32(buf)==-296279185){break;} }
+ for(i=-122;i<-99;i++){ buf[920]=(byte)i; if(sha1_32(buf)==1337175685){break;} }
+ for(i=5;i<27;i++){ buf[921]=(byte)i; if(sha1_32(buf)==-2144800837){break;} }
+ for(i=63;i<78;i++){ buf[922]=(byte)i; if(sha1_32(buf)==415363442){break;} }
+ for(i=-79;i<-69;i++){ buf[923]=(byte)i; if(sha1_32(buf)==-1781703638){break;} }
+ for(i=-118;i<-116;i++){ buf[924]=(byte)i; if(sha1_32(buf)==-1571473907){break;} }
+ for(i=30;i<50;i++){ buf[925]=(byte)i; if(sha1_32(buf)==1917938139){break;} }
+ for(i=-2;i<14;i++){ buf[926]=(byte)i; if(sha1_32(buf)==-975403474){break;} }
+ for(i=-82;i<-73;i++){ buf[927]=(byte)i; if(sha1_32(buf)==2121550735){break;} }
+ for(i=106;i<123;i++){ buf[928]=(byte)i; if(sha1_32(buf)==-1602277109){break;} }
+ for(i=-101;i<-82;i++){ buf[929]=(byte)i; if(sha1_32(buf)==-1516708668){break;} }
+ for(i=101;i<112;i++){ buf[930]=(byte)i; if(sha1_32(buf)==1622192437){break;} }
+ for(i=41;i<53;i++){ buf[931]=(byte)i; if(sha1_32(buf)==-686978238){break;} }
+ for(i=25;i<45;i++){ buf[932]=(byte)i; if(sha1_32(buf)==166884904){break;} }
+ for(i=-116;i<-92;i++){ buf[933]=(byte)i; if(sha1_32(buf)==-97223331){break;} }
+ for(i=116;i<128;i++){ buf[934]=(byte)i; if(sha1_32(buf)==1263643143){break;} }
+ for(i=-27;i<-7;i++){ buf[935]=(byte)i; if(sha1_32(buf)==1436612733){break;} }
+ for(i=-19;i<9;i++){ buf[936]=(byte)i; if(sha1_32(buf)==1335962688){break;} }
+ for(i=-93;i<-78;i++){ buf[937]=(byte)i; if(sha1_32(buf)==754589298){break;} }
+ for(i=33;i<42;i++){ buf[938]=(byte)i; if(sha1_32(buf)==-1450477894){break;} }
+ for(i=99;i<114;i++){ buf[939]=(byte)i; if(sha1_32(buf)==-1577794591){break;} }
+ for(i=-123;i<-114;i++){ buf[940]=(byte)i; if(sha1_32(buf)==-248050048){break;} }
+ for(i=-64;i<-42;i++){ buf[941]=(byte)i; if(sha1_32(buf)==1968183316){break;} }
+ for(i=36;i<45;i++){ buf[942]=(byte)i; if(sha1_32(buf)==-650365666){break;} }
+ for(i=108;i<128;i++){ buf[943]=(byte)i; if(sha1_32(buf)==-1698723717){break;} }
+ for(i=-31;i<-21;i++){ buf[944]=(byte)i; if(sha1_32(buf)==-1507659087){break;} }
+ for(i=22;i<40;i++){ buf[945]=(byte)i; if(sha1_32(buf)==-1552804680){break;} }
+ for(i=-72;i<-47;i++){ buf[946]=(byte)i; if(sha1_32(buf)==697619244){break;} }
+ for(i=-38;i<-22;i++){ buf[947]=(byte)i; if(sha1_32(buf)==1144754024){break;} }
+ for(i=-55;i<-38;i++){ buf[948]=(byte)i; if(sha1_32(buf)==1611176787){break;} }
+ for(i=-59;i<-34;i++){ buf[949]=(byte)i; if(sha1_32(buf)==-957661096){break;} }
+ for(i=75;i<92;i++){ buf[950]=(byte)i; if(sha1_32(buf)==992333398){break;} }
+ for(i=38;i<45;i++){ buf[951]=(byte)i; if(sha1_32(buf)==-1343154612){break;} }
+ for(i=24;i<29;i++){ buf[952]=(byte)i; if(sha1_32(buf)==-1023071938){break;} }
+ for(i=-91;i<-74;i++){ buf[953]=(byte)i; if(sha1_32(buf)==-1142385161){break;} }
+ for(i=25;i<51;i++){ buf[954]=(byte)i; if(sha1_32(buf)==-1259893995){break;} }
+ for(i=-111;i<-85;i++){ buf[955]=(byte)i; if(sha1_32(buf)==-265732050){break;} }
+ for(i=-6;i<5;i++){ buf[956]=(byte)i; if(sha1_32(buf)==52640541){break;} }
+ for(i=75;i<82;i++){ buf[957]=(byte)i; if(sha1_32(buf)==-1592044720){break;} }
+ for(i=37;i<52;i++){ buf[958]=(byte)i; if(sha1_32(buf)==253203775){break;} }
+ for(i=55;i<67;i++){ buf[959]=(byte)i; if(sha1_32(buf)==-1186737008){break;} }
+ for(i=-106;i<-81;i++){ buf[960]=(byte)i; if(sha1_32(buf)==-1878577613){break;} }
+ for(i=-33;i<-3;i++){ buf[961]=(byte)i; if(sha1_32(buf)==-2056595243){break;} }
+ for(i=106;i<117;i++){ buf[962]=(byte)i; if(sha1_32(buf)==-2120205781){break;} }
+ for(i=-53;i<-36;i++){ buf[963]=(byte)i; if(sha1_32(buf)==125468582){break;} }
+ for(i=-41;i<-21;i++){ buf[964]=(byte)i; if(sha1_32(buf)==-759282965){break;} }
+ for(i=-100;i<-91;i++){ buf[965]=(byte)i; if(sha1_32(buf)==46037119){break;} }
+ for(i=85;i<90;i++){ buf[966]=(byte)i; if(sha1_32(buf)==-1381494939){break;} }
+ for(i=-31;i<-25;i++){ buf[967]=(byte)i; if(sha1_32(buf)==2011245134){break;} }
+ for(i=-68;i<-56;i++){ buf[968]=(byte)i; if(sha1_32(buf)==-231002651){break;} }
+ for(i=-99;i<-88;i++){ buf[969]=(byte)i; if(sha1_32(buf)==-2118350372){break;} }
+ for(i=-100;i<-95;i++){ buf[970]=(byte)i; if(sha1_32(buf)==441671649){break;} }
+ for(i=-66;i<-41;i++){ buf[971]=(byte)i; if(sha1_32(buf)==2083516320){break;} }
+ for(i=20;i<36;i++){ buf[972]=(byte)i; if(sha1_32(buf)==867170251){break;} }
+ for(i=101;i<127;i++){ buf[973]=(byte)i; if(sha1_32(buf)==-1984396790){break;} }
+ for(i=-42;i<-23;i++){ buf[974]=(byte)i; if(sha1_32(buf)==64715245){break;} }
+ for(i=4;i<23;i++){ buf[975]=(byte)i; if(sha1_32(buf)==1684700478){break;} }
+ for(i=63;i<81;i++){ buf[976]=(byte)i; if(sha1_32(buf)==-1149849537){break;} }
+ for(i=-20;i<-7;i++){ buf[977]=(byte)i; if(sha1_32(buf)==-1384937396){break;} }
+ for(i=51;i<73;i++){ buf[978]=(byte)i; if(sha1_32(buf)==-1450734456){break;} }
+ for(i=-54;i<-37;i++){ buf[979]=(byte)i; if(sha1_32(buf)==-2073975361){break;} }
+ for(i=113;i<128;i++){ buf[980]=(byte)i; if(sha1_32(buf)==1020168706){break;} }
+ for(i=76;i<85;i++){ buf[981]=(byte)i; if(sha1_32(buf)==-1137564100){break;} }
+ for(i=0;i<15;i++){ buf[982]=(byte)i; if(sha1_32(buf)==-433673203){break;} }
+ for(i=44;i<57;i++){ buf[983]=(byte)i; if(sha1_32(buf)==1891301518){break;} }
+ for(i=-27;i<-9;i++){ buf[984]=(byte)i; if(sha1_32(buf)==-1026494060){break;} }
+ for(i=-60;i<-31;i++){ buf[985]=(byte)i; if(sha1_32(buf)==-1057319415){break;} }
+ for(i=-126;i<-105;i++){ buf[986]=(byte)i; if(sha1_32(buf)==-1648711413){break;} }
+ for(i=84;i<101;i++){ buf[987]=(byte)i; if(sha1_32(buf)==-2002260226){break;} }
+ for(i=-47;i<-34;i++){ buf[988]=(byte)i; if(sha1_32(buf)==-1163678002){break;} }
+ for(i=-80;i<-69;i++){ buf[989]=(byte)i; if(sha1_32(buf)==-656455502){break;} }
+ for(i=-29;i<-8;i++){ buf[990]=(byte)i; if(sha1_32(buf)==341901046){break;} }
+ for(i=-123;i<-106;i++){ buf[991]=(byte)i; if(sha1_32(buf)==-2036861356){break;} }
+ for(i=-49;i<-39;i++){ buf[992]=(byte)i; if(sha1_32(buf)==-993733222){break;} }
+ for(i=68;i<83;i++){ buf[993]=(byte)i; if(sha1_32(buf)==989275190){break;} }
+ for(i=-105;i<-89;i++){ buf[994]=(byte)i; if(sha1_32(buf)==1662925223){break;} }
+ for(i=4;i<14;i++){ buf[995]=(byte)i; if(sha1_32(buf)==-1800560843){break;} }
+ for(i=-74;i<-62;i++){ buf[996]=(byte)i; if(sha1_32(buf)==323592519){break;} }
+ for(i=41;i<63;i++){ buf[997]=(byte)i; if(sha1_32(buf)==1537635689){break;} }
+ for(i=-91;i<-81;i++){ buf[998]=(byte)i; if(sha1_32(buf)==-714086883){break;} }
+ for(i=80;i<96;i++){ buf[999]=(byte)i; if(sha1_32(buf)==-1350742941){break;} }
+ for(i=-8;i<10;i++){ buf[1000]=(byte)i; if(sha1_32(buf)==2139369971){break;} }
+ for(i=118;i<125;i++){ buf[1001]=(byte)i; if(sha1_32(buf)==-538711097){break;} }
+ for(i=114;i<128;i++){ buf[1002]=(byte)i; if(sha1_32(buf)==-1138038063){break;} }
+ for(i=48;i<64;i++){ buf[1003]=(byte)i; if(sha1_32(buf)==-545071251){break;} }
+ for(i=75;i<92;i++){ buf[1004]=(byte)i; if(sha1_32(buf)==-1184016227){break;} }
+ for(i=-45;i<-31;i++){ buf[1005]=(byte)i; if(sha1_32(buf)==605541705){break;} }
+ for(i=108;i<128;i++){ buf[1006]=(byte)i; if(sha1_32(buf)==-1923048554){break;} }
+ for(i=-113;i<-94;i++){ buf[1007]=(byte)i; if(sha1_32(buf)==880753395){break;} }
+ for(i=-90;i<-79;i++){ buf[1008]=(byte)i; if(sha1_32(buf)==1123046122){break;} }
+ for(i=30;i<48;i++){ buf[1009]=(byte)i; if(sha1_32(buf)==1739166413){break;} }
+ for(i=-26;i<-9;i++){ buf[1010]=(byte)i; if(sha1_32(buf)==-2094165455){break;} }
+ for(i=28;i<54;i++){ buf[1011]=(byte)i; if(sha1_32(buf)==2139995727){break;} }
+ for(i=0;i<14;i++){ buf[1012]=(byte)i; if(sha1_32(buf)==203920209){break;} }
+ for(i=40;i<49;i++){ buf[1013]=(byte)i; if(sha1_32(buf)==-1135645249){break;} }
+ for(i=77;i<94;i++){ buf[1014]=(byte)i; if(sha1_32(buf)==962273992){break;} }
+ for(i=-112;i<-104;i++){ buf[1015]=(byte)i; if(sha1_32(buf)==1112237553){break;} }
+ for(i=-40;i<-31;i++){ buf[1016]=(byte)i; if(sha1_32(buf)==1072697815){break;} }
+ for(i=87;i<102;i++){ buf[1017]=(byte)i; if(sha1_32(buf)==534244137){break;} }
+ for(i=6;i<23;i++){ buf[1018]=(byte)i; if(sha1_32(buf)==-204135386){break;} }
+ for(i=-86;i<-83;i++){ buf[1019]=(byte)i; if(sha1_32(buf)==-1950067505){break;} }
+ for(i=-1;i<25;i++){ buf[1020]=(byte)i; if(sha1_32(buf)==169292245){break;} }
+ for(i=-91;i<-73;i++){ buf[1021]=(byte)i; if(sha1_32(buf)==1668251079){break;} }
+ for(i=118;i<128;i++){ buf[1022]=(byte)i; if(sha1_32(buf)==351773699){break;} }
+ for(i=52;i<71;i++){ buf[1023]=(byte)i; if(sha1_32(buf)==1094142757){break;} }
+ for(i=108;i<115;i++){ buf[1024]=(byte)i; if(sha1_32(buf)==-1499940564){break;} }
+ for(i=-64;i<-60;i++){ buf[1025]=(byte)i; if(sha1_32(buf)==974511154){break;} }
+ for(i=95;i<111;i++){ buf[1026]=(byte)i; if(sha1_32(buf)==-225629822){break;} }
+ for(i=80;i<99;i++){ buf[1027]=(byte)i; if(sha1_32(buf)==775334224){break;} }
+ for(i=70;i<87;i++){ buf[1028]=(byte)i; if(sha1_32(buf)==1989615161){break;} }
+ for(i=-8;i<16;i++){ buf[1029]=(byte)i; if(sha1_32(buf)==2013841521){break;} }
+ for(i=-96;i<-84;i++){ buf[1030]=(byte)i; if(sha1_32(buf)==-38959762){break;} }
+ for(i=32;i<54;i++){ buf[1031]=(byte)i; if(sha1_32(buf)==17007173){break;} }
+ for(i=-128;i<-108;i++){ buf[1032]=(byte)i; if(sha1_32(buf)==-1093049954){break;} }
+ for(i=-33;i<-24;i++){ buf[1033]=(byte)i; if(sha1_32(buf)==-1380990090){break;} }
+ for(i=-76;i<-60;i++){ buf[1034]=(byte)i; if(sha1_32(buf)==-1435978553){break;} }
+ for(i=-128;i<-103;i++){ buf[1035]=(byte)i; if(sha1_32(buf)==275186307){break;} }
+ for(i=96;i<109;i++){ buf[1036]=(byte)i; if(sha1_32(buf)==1781919692){break;} }
+ for(i=-117;i<-114;i++){ buf[1037]=(byte)i; if(sha1_32(buf)==-785589820){break;} }
+ for(i=-93;i<-74;i++){ buf[1038]=(byte)i; if(sha1_32(buf)==-1162000185){break;} }
+ for(i=15;i<36;i++){ buf[1039]=(byte)i; if(sha1_32(buf)==-99645430){break;} }
+ for(i=91;i<119;i++){ buf[1040]=(byte)i; if(sha1_32(buf)==-267557470){break;} }
+ for(i=107;i<123;i++){ buf[1041]=(byte)i; if(sha1_32(buf)==-2072445968){break;} }
+ for(i=-96;i<-74;i++){ buf[1042]=(byte)i; if(sha1_32(buf)==-2082764174){break;} }
+ for(i=-46;i<-42;i++){ buf[1043]=(byte)i; if(sha1_32(buf)==-737479610){break;} }
+ for(i=83;i<100;i++){ buf[1044]=(byte)i; if(sha1_32(buf)==-807107930){break;} }
+ for(i=-128;i<-115;i++){ buf[1045]=(byte)i; if(sha1_32(buf)==-1241257099){break;} }
+ for(i=-128;i<-119;i++){ buf[1046]=(byte)i; if(sha1_32(buf)==1786443960){break;} }
+ for(i=-59;i<-30;i++){ buf[1047]=(byte)i; if(sha1_32(buf)==1665456595){break;} }
+ for(i=121;i<128;i++){ buf[1048]=(byte)i; if(sha1_32(buf)==-1140341445){break;} }
+ for(i=106;i<119;i++){ buf[1049]=(byte)i; if(sha1_32(buf)==-1450332040){break;} }
+ for(i=20;i<43;i++){ buf[1050]=(byte)i; if(sha1_32(buf)==138519052){break;} }
+ for(i=-115;i<-92;i++){ buf[1051]=(byte)i; if(sha1_32(buf)==1443995933){break;} }
+ for(i=-70;i<-61;i++){ buf[1052]=(byte)i; if(sha1_32(buf)==637333117){break;} }
+ for(i=73;i<91;i++){ buf[1053]=(byte)i; if(sha1_32(buf)==-1892526595){break;} }
+ for(i=-70;i<-52;i++){ buf[1054]=(byte)i; if(sha1_32(buf)==645156581){break;} }
+ for(i=-95;i<-89;i++){ buf[1055]=(byte)i; if(sha1_32(buf)==1243080065){break;} }
+ for(i=90;i<109;i++){ buf[1056]=(byte)i; if(sha1_32(buf)==-378078286){break;} }
+ for(i=-123;i<-108;i++){ buf[1057]=(byte)i; if(sha1_32(buf)==-318888651){break;} }
+ for(i=28;i<35;i++){ buf[1058]=(byte)i; if(sha1_32(buf)==1678813882){break;} }
+ for(i=8;i<28;i++){ buf[1059]=(byte)i; if(sha1_32(buf)==691156564){break;} }
+ for(i=-33;i<-21;i++){ buf[1060]=(byte)i; if(sha1_32(buf)==2025172207){break;} }
+ for(i=-88;i<-77;i++){ buf[1061]=(byte)i; if(sha1_32(buf)==559736034){break;} }
+ for(i=-87;i<-59;i++){ buf[1062]=(byte)i; if(sha1_32(buf)==1986380456){break;} }
+ for(i=-79;i<-64;i++){ buf[1063]=(byte)i; if(sha1_32(buf)==-1043296392){break;} }
+ for(i=-35;i<-21;i++){ buf[1064]=(byte)i; if(sha1_32(buf)==85150754){break;} }
+ for(i=41;i<55;i++){ buf[1065]=(byte)i; if(sha1_32(buf)==-1409625681){break;} }
+ for(i=109;i<128;i++){ buf[1066]=(byte)i; if(sha1_32(buf)==2022758805){break;} }
+ for(i=-32;i<-15;i++){ buf[1067]=(byte)i; if(sha1_32(buf)==-1105339080){break;} }
+ for(i=-56;i<-49;i++){ buf[1068]=(byte)i; if(sha1_32(buf)==-370534867){break;} }
+ for(i=-52;i<-27;i++){ buf[1069]=(byte)i; if(sha1_32(buf)==-629446371){break;} }
+ for(i=-83;i<-71;i++){ buf[1070]=(byte)i; if(sha1_32(buf)==726716218){break;} }
+ for(i=15;i<28;i++){ buf[1071]=(byte)i; if(sha1_32(buf)==-2092877279){break;} }
+ for(i=-110;i<-89;i++){ buf[1072]=(byte)i; if(sha1_32(buf)==-1321909687){break;} }
+ for(i=-115;i<-108;i++){ buf[1073]=(byte)i; if(sha1_32(buf)==-1436307508){break;} }
+ for(i=106;i<122;i++){ buf[1074]=(byte)i; if(sha1_32(buf)==1408958465){break;} }
+ for(i=-110;i<-100;i++){ buf[1075]=(byte)i; if(sha1_32(buf)==669571210){break;} }
+ for(i=-112;i<-85;i++){ buf[1076]=(byte)i; if(sha1_32(buf)==-337770707){break;} }
+ for(i=-27;i<-10;i++){ buf[1077]=(byte)i; if(sha1_32(buf)==1786361597){break;} }
+ for(i=112;i<121;i++){ buf[1078]=(byte)i; if(sha1_32(buf)==1106785408){break;} }
+ for(i=-21;i<-10;i++){ buf[1079]=(byte)i; if(sha1_32(buf)==-1358058883){break;} }
+ for(i=85;i<88;i++){ buf[1080]=(byte)i; if(sha1_32(buf)==-1594661626){break;} }
+ for(i=-48;i<-31;i++){ buf[1081]=(byte)i; if(sha1_32(buf)==1947674546){break;} }
+ for(i=-65;i<-40;i++){ buf[1082]=(byte)i; if(sha1_32(buf)==326878353){break;} }
+ for(i=40;i<49;i++){ buf[1083]=(byte)i; if(sha1_32(buf)==-827463723){break;} }
+ for(i=49;i<73;i++){ buf[1084]=(byte)i; if(sha1_32(buf)==-1488052013){break;} }
+ for(i=67;i<89;i++){ buf[1085]=(byte)i; if(sha1_32(buf)==-2145446721){break;} }
+ for(i=51;i<68;i++){ buf[1086]=(byte)i; if(sha1_32(buf)==879361044){break;} }
+ for(i=79;i<89;i++){ buf[1087]=(byte)i; if(sha1_32(buf)==210235629){break;} }
+ for(i=123;i<128;i++){ buf[1088]=(byte)i; if(sha1_32(buf)==1373695713){break;} }
+ for(i=23;i<32;i++){ buf[1089]=(byte)i; if(sha1_32(buf)==839481198){break;} }
+ for(i=52;i<74;i++){ buf[1090]=(byte)i; if(sha1_32(buf)==-233898711){break;} }
+ for(i=5;i<22;i++){ buf[1091]=(byte)i; if(sha1_32(buf)==-1146841254){break;} }
+ for(i=117;i<128;i++){ buf[1092]=(byte)i; if(sha1_32(buf)==1792110236){break;} }
+ for(i=-3;i<6;i++){ buf[1093]=(byte)i; if(sha1_32(buf)==-2085652053){break;} }
+ for(i=-99;i<-87;i++){ buf[1094]=(byte)i; if(sha1_32(buf)==300722691){break;} }
+ for(i=26;i<43;i++){ buf[1095]=(byte)i; if(sha1_32(buf)==-523984871){break;} }
+ for(i=-38;i<-23;i++){ buf[1096]=(byte)i; if(sha1_32(buf)==1572248949){break;} }
+ for(i=17;i<24;i++){ buf[1097]=(byte)i; if(sha1_32(buf)==70148005){break;} }
+ for(i=-93;i<-76;i++){ buf[1098]=(byte)i; if(sha1_32(buf)==1078930454){break;} }
+ for(i=-83;i<-64;i++){ buf[1099]=(byte)i; if(sha1_32(buf)==-1891642768){break;} }
+ for(i=26;i<37;i++){ buf[1100]=(byte)i; if(sha1_32(buf)==-877048145){break;} }
+ for(i=-80;i<-71;i++){ buf[1101]=(byte)i; if(sha1_32(buf)==-2092113685){break;} }
+ for(i=-71;i<-64;i++){ buf[1102]=(byte)i; if(sha1_32(buf)==-64359604){break;} }
+ for(i=-17;i<-1;i++){ buf[1103]=(byte)i; if(sha1_32(buf)==462331862){break;} }
+ for(i=42;i<61;i++){ buf[1104]=(byte)i; if(sha1_32(buf)==-1581108711){break;} }
+ for(i=-16;i<-12;i++){ buf[1105]=(byte)i; if(sha1_32(buf)==-835618069){break;} }
+ for(i=111;i<124;i++){ buf[1106]=(byte)i; if(sha1_32(buf)==103796386){break;} }
+ for(i=103;i<110;i++){ buf[1107]=(byte)i; if(sha1_32(buf)==-1758071957){break;} }
+ for(i=-63;i<-48;i++){ buf[1108]=(byte)i; if(sha1_32(buf)==-1651694256){break;} }
+ for(i=-92;i<-67;i++){ buf[1109]=(byte)i; if(sha1_32(buf)==-527349781){break;} }
+ for(i=68;i<82;i++){ buf[1110]=(byte)i; if(sha1_32(buf)==-2006844027){break;} }
+ for(i=-91;i<-86;i++){ buf[1111]=(byte)i; if(sha1_32(buf)==1011655464){break;} }
+ for(i=-95;i<-72;i++){ buf[1112]=(byte)i; if(sha1_32(buf)==119363532){break;} }
+ for(i=26;i<40;i++){ buf[1113]=(byte)i; if(sha1_32(buf)==1827654098){break;} }
+ for(i=-90;i<-72;i++){ buf[1114]=(byte)i; if(sha1_32(buf)==-1214355055){break;} }
+ for(i=-54;i<-34;i++){ buf[1115]=(byte)i; if(sha1_32(buf)==-1943150988){break;} }
+ for(i=-128;i<-123;i++){ buf[1116]=(byte)i; if(sha1_32(buf)==1666015883){break;} }
+ for(i=14;i<29;i++){ buf[1117]=(byte)i; if(sha1_32(buf)==-178424823){break;} }
+ for(i=-77;i<-57;i++){ buf[1118]=(byte)i; if(sha1_32(buf)==438492201){break;} }
+ for(i=-77;i<-68;i++){ buf[1119]=(byte)i; if(sha1_32(buf)==987747054){break;} }
+ for(i=20;i<31;i++){ buf[1120]=(byte)i; if(sha1_32(buf)==-1233386100){break;} }
+ for(i=-128;i<-113;i++){ buf[1121]=(byte)i; if(sha1_32(buf)==-258497829){break;} }
+ for(i=-28;i<-11;i++){ buf[1122]=(byte)i; if(sha1_32(buf)==-1735166566){break;} }
+ for(i=-117;i<-105;i++){ buf[1123]=(byte)i; if(sha1_32(buf)==-501716166){break;} }
+ for(i=4;i<23;i++){ buf[1124]=(byte)i; if(sha1_32(buf)==1728576604){break;} }
+ for(i=-19;i<-4;i++){ buf[1125]=(byte)i; if(sha1_32(buf)==-1079202770){break;} }
+ for(i=-6;i<6;i++){ buf[1126]=(byte)i; if(sha1_32(buf)==1491635534){break;} }
+ for(i=51;i<71;i++){ buf[1127]=(byte)i; if(sha1_32(buf)==-19365497){break;} }
+ for(i=40;i<61;i++){ buf[1128]=(byte)i; if(sha1_32(buf)==-1154220082){break;} }
+ for(i=72;i<93;i++){ buf[1129]=(byte)i; if(sha1_32(buf)==185062196){break;} }
+ for(i=-71;i<-52;i++){ buf[1130]=(byte)i; if(sha1_32(buf)==1091765059){break;} }
+ for(i=-37;i<-27;i++){ buf[1131]=(byte)i; if(sha1_32(buf)==-2079990844){break;} }
+ for(i=-54;i<-35;i++){ buf[1132]=(byte)i; if(sha1_32(buf)==1738222119){break;} }
+ for(i=-20;i<-11;i++){ buf[1133]=(byte)i; if(sha1_32(buf)==-2121921500){break;} }
+ for(i=111;i<128;i++){ buf[1134]=(byte)i; if(sha1_32(buf)==-1511886409){break;} }
+ for(i=103;i<114;i++){ buf[1135]=(byte)i; if(sha1_32(buf)==-438984646){break;} }
+ for(i=-95;i<-73;i++){ buf[1136]=(byte)i; if(sha1_32(buf)==-1587092422){break;} }
+ for(i=-60;i<-55;i++){ buf[1137]=(byte)i; if(sha1_32(buf)==2085270602){break;} }
+ for(i=58;i<77;i++){ buf[1138]=(byte)i; if(sha1_32(buf)==-542145516){break;} }
+ for(i=95;i<105;i++){ buf[1139]=(byte)i; if(sha1_32(buf)==-1595714757){break;} }
+ for(i=82;i<98;i++){ buf[1140]=(byte)i; if(sha1_32(buf)==-174273588){break;} }
+ for(i=107;i<122;i++){ buf[1141]=(byte)i; if(sha1_32(buf)==1900664030){break;} }
+ for(i=-70;i<-53;i++){ buf[1142]=(byte)i; if(sha1_32(buf)==2066126526){break;} }
+ for(i=20;i<45;i++){ buf[1143]=(byte)i; if(sha1_32(buf)==1659349440){break;} }
+ for(i=-84;i<-54;i++){ buf[1144]=(byte)i; if(sha1_32(buf)==371249220){break;} }
+ for(i=-64;i<-59;i++){ buf[1145]=(byte)i; if(sha1_32(buf)==1092328833){break;} }
+ for(i=43;i<52;i++){ buf[1146]=(byte)i; if(sha1_32(buf)==-153394063){break;} }
+ for(i=-89;i<-71;i++){ buf[1147]=(byte)i; if(sha1_32(buf)==398580307){break;} }
+ for(i=-74;i<-67;i++){ buf[1148]=(byte)i; if(sha1_32(buf)==-1661525123){break;} }
+ for(i=89;i<108;i++){ buf[1149]=(byte)i; if(sha1_32(buf)==1360348283){break;} }
+ for(i=-52;i<-44;i++){ buf[1150]=(byte)i; if(sha1_32(buf)==236358520){break;} }
+ for(i=80;i<101;i++){ buf[1151]=(byte)i; if(sha1_32(buf)==1672281133){break;} }
+ for(i=105;i<108;i++){ buf[1152]=(byte)i; if(sha1_32(buf)==749554350){break;} }
+ for(i=59;i<76;i++){ buf[1153]=(byte)i; if(sha1_32(buf)==-292394596){break;} }
+ for(i=11;i<18;i++){ buf[1154]=(byte)i; if(sha1_32(buf)==-234575886){break;} }
+ for(i=102;i<117;i++){ buf[1155]=(byte)i; if(sha1_32(buf)==-1419752642){break;} }
+ for(i=98;i<104;i++){ buf[1156]=(byte)i; if(sha1_32(buf)==722616406){break;} }
+ for(i=118;i<128;i++){ buf[1157]=(byte)i; if(sha1_32(buf)==207023551){break;} }
+ for(i=21;i<38;i++){ buf[1158]=(byte)i; if(sha1_32(buf)==-34321382){break;} }
+ for(i=-128;i<-110;i++){ buf[1159]=(byte)i; if(sha1_32(buf)==1813147722){break;} }
+ for(i=-42;i<-30;i++){ buf[1160]=(byte)i; if(sha1_32(buf)==-1807506824){break;} }
+ for(i=20;i<26;i++){ buf[1161]=(byte)i; if(sha1_32(buf)==1245686884){break;} }
+ for(i=69;i<89;i++){ buf[1162]=(byte)i; if(sha1_32(buf)==-1191294795){break;} }
+ for(i=-128;i<-123;i++){ buf[1163]=(byte)i; if(sha1_32(buf)==636251977){break;} }
+ for(i=-75;i<-47;i++){ buf[1164]=(byte)i; if(sha1_32(buf)==265150458){break;} }
+ for(i=-111;i<-95;i++){ buf[1165]=(byte)i; if(sha1_32(buf)==1795673353){break;} }
+ for(i=-98;i<-82;i++){ buf[1166]=(byte)i; if(sha1_32(buf)==818727338){break;} }
+ for(i=55;i<73;i++){ buf[1167]=(byte)i; if(sha1_32(buf)==-1686255078){break;} }
+ for(i=114;i<125;i++){ buf[1168]=(byte)i; if(sha1_32(buf)==-663421916){break;} }
+ for(i=-34;i<-24;i++){ buf[1169]=(byte)i; if(sha1_32(buf)==129953861){break;} }
+ for(i=-10;i<12;i++){ buf[1170]=(byte)i; if(sha1_32(buf)==-758812232){break;} }
+ for(i=-121;i<-111;i++){ buf[1171]=(byte)i; if(sha1_32(buf)==1559375840){break;} }
+ for(i=-128;i<-102;i++){ buf[1172]=(byte)i; if(sha1_32(buf)==469597355){break;} }
+ for(i=-82;i<-58;i++){ buf[1173]=(byte)i; if(sha1_32(buf)==-878089631){break;} }
+ for(i=-5;i<9;i++){ buf[1174]=(byte)i; if(sha1_32(buf)==-1831567414){break;} }
+ for(i=-59;i<-40;i++){ buf[1175]=(byte)i; if(sha1_32(buf)==80030536){break;} }
+ for(i=21;i<38;i++){ buf[1176]=(byte)i; if(sha1_32(buf)==-303161426){break;} }
+ for(i=91;i<115;i++){ buf[1177]=(byte)i; if(sha1_32(buf)==-933075582){break;} }
+ for(i=-115;i<-103;i++){ buf[1178]=(byte)i; if(sha1_32(buf)==-1298353680){break;} }
+ for(i=82;i<93;i++){ buf[1179]=(byte)i; if(sha1_32(buf)==-1865930790){break;} }
+ for(i=-123;i<-98;i++){ buf[1180]=(byte)i; if(sha1_32(buf)==-372037200){break;} }
+ for(i=-113;i<-96;i++){ buf[1181]=(byte)i; if(sha1_32(buf)==2109374435){break;} }
+ for(i=-64;i<-48;i++){ buf[1182]=(byte)i; if(sha1_32(buf)==-1813148922){break;} }
+ for(i=-2;i<7;i++){ buf[1183]=(byte)i; if(sha1_32(buf)==-1813148922){break;} }
+ for(i=55;i<72;i++){ buf[1184]=(byte)i; if(sha1_32(buf)==-548158664){break;} }
+ for(i=47;i<60;i++){ buf[1185]=(byte)i; if(sha1_32(buf)==-1842516851){break;} }
+ for(i=-8;i<10;i++){ buf[1186]=(byte)i; if(sha1_32(buf)==-1295167494){break;} }
+ for(i=-12;i<7;i++){ buf[1187]=(byte)i; if(sha1_32(buf)==-710236125){break;} }
+ for(i=58;i<77;i++){ buf[1188]=(byte)i; if(sha1_32(buf)==1268747381){break;} }
+ for(i=100;i<113;i++){ buf[1189]=(byte)i; if(sha1_32(buf)==-747145295){break;} }
+ for(i=43;i<56;i++){ buf[1190]=(byte)i; if(sha1_32(buf)==-1308171970){break;} }
+ for(i=2;i<29;i++){ buf[1191]=(byte)i; if(sha1_32(buf)==-2102687634){break;} }
+ for(i=10;i<35;i++){ buf[1192]=(byte)i; if(sha1_32(buf)==-2110628114){break;} }
+ for(i=114;i<119;i++){ buf[1193]=(byte)i; if(sha1_32(buf)==-1804143624){break;} }
+ for(i=-85;i<-82;i++){ buf[1194]=(byte)i; if(sha1_32(buf)==-1370218155){break;} }
+ for(i=-121;i<-96;i++){ buf[1195]=(byte)i; if(sha1_32(buf)==-839382276){break;} }
+ for(i=29;i<44;i++){ buf[1196]=(byte)i; if(sha1_32(buf)==195442480){break;} }
+ for(i=-84;i<-60;i++){ buf[1197]=(byte)i; if(sha1_32(buf)==-1558366254){break;} }
+ for(i=-29;i<-13;i++){ buf[1198]=(byte)i; if(sha1_32(buf)==-1321274593){break;} }
+ for(i=50;i<72;i++){ buf[1199]=(byte)i; if(sha1_32(buf)==1378503636){break;} }
+ for(i=-114;i<-91;i++){ buf[1200]=(byte)i; if(sha1_32(buf)==1771958436){break;} }
+ for(i=68;i<75;i++){ buf[1201]=(byte)i; if(sha1_32(buf)==-1159135819){break;} }
+ for(i=53;i<66;i++){ buf[1202]=(byte)i; if(sha1_32(buf)==-753115336){break;} }
+ for(i=-73;i<-64;i++){ buf[1203]=(byte)i; if(sha1_32(buf)==20272569){break;} }
+ for(i=-128;i<-118;i++){ buf[1204]=(byte)i; if(sha1_32(buf)==1285477298){break;} }
+ for(i=-119;i<-105;i++){ buf[1205]=(byte)i; if(sha1_32(buf)==-1703165409){break;} }
+ for(i=50;i<62;i++){ buf[1206]=(byte)i; if(sha1_32(buf)==594237475){break;} }
+ for(i=3;i<24;i++){ buf[1207]=(byte)i; if(sha1_32(buf)==-952877050){break;} }
+ for(i=-42;i<-27;i++){ buf[1208]=(byte)i; if(sha1_32(buf)==896862829){break;} }
+ for(i=-101;i<-85;i++){ buf[1209]=(byte)i; if(sha1_32(buf)==1364580057){break;} }
+ for(i=-32;i<-19;i++){ buf[1210]=(byte)i; if(sha1_32(buf)==1819535054){break;} }
+ for(i=27;i<33;i++){ buf[1211]=(byte)i; if(sha1_32(buf)==-1686160198){break;} }
+ for(i=-93;i<-72;i++){ buf[1212]=(byte)i; if(sha1_32(buf)==1023996577){break;} }
+ for(i=-100;i<-91;i++){ buf[1213]=(byte)i; if(sha1_32(buf)==579671853){break;} }
+ for(i=-32;i<-15;i++){ buf[1214]=(byte)i; if(sha1_32(buf)==-188752328){break;} }
+ for(i=-114;i<-94;i++){ buf[1215]=(byte)i; if(sha1_32(buf)==-1788309873){break;} }
+ for(i=89;i<105;i++){ buf[1216]=(byte)i; if(sha1_32(buf)==-1222797574){break;} }
+ for(i=89;i<105;i++){ buf[1217]=(byte)i; if(sha1_32(buf)==-215334977){break;} }
+ for(i=-128;i<-115;i++){ buf[1218]=(byte)i; if(sha1_32(buf)==-1765880598){break;} }
+ for(i=31;i<51;i++){ buf[1219]=(byte)i; if(sha1_32(buf)==1576480807){break;} }
+ for(i=63;i<76;i++){ buf[1220]=(byte)i; if(sha1_32(buf)==1158935180){break;} }
+ for(i=42;i<49;i++){ buf[1221]=(byte)i; if(sha1_32(buf)==-1419476236){break;} }
+ for(i=20;i<35;i++){ buf[1222]=(byte)i; if(sha1_32(buf)==1958000843){break;} }
+ for(i=-106;i<-87;i++){ buf[1223]=(byte)i; if(sha1_32(buf)==786142651){break;} }
+ for(i=62;i<76;i++){ buf[1224]=(byte)i; if(sha1_32(buf)==1546995582){break;} }
+ for(i=-128;i<-126;i++){ buf[1225]=(byte)i; if(sha1_32(buf)==2084041227){break;} }
+ for(i=60;i<79;i++){ buf[1226]=(byte)i; if(sha1_32(buf)==-144463173){break;} }
+ for(i=41;i<56;i++){ buf[1227]=(byte)i; if(sha1_32(buf)==-1235874263){break;} }
+ for(i=-83;i<-74;i++){ buf[1228]=(byte)i; if(sha1_32(buf)==1636272900){break;} }
+ for(i=-75;i<-66;i++){ buf[1229]=(byte)i; if(sha1_32(buf)==1213836500){break;} }
+ for(i=43;i<56;i++){ buf[1230]=(byte)i; if(sha1_32(buf)==1243745150){break;} }
+ for(i=98;i<108;i++){ buf[1231]=(byte)i; if(sha1_32(buf)==1549128667){break;} }
+ for(i=11;i<31;i++){ buf[1232]=(byte)i; if(sha1_32(buf)==636865778){break;} }
+ for(i=-111;i<-93;i++){ buf[1233]=(byte)i; if(sha1_32(buf)==-699507039){break;} }
+ for(i=-105;i<-83;i++){ buf[1234]=(byte)i; if(sha1_32(buf)==-1958780713){break;} }
+ for(i=6;i<16;i++){ buf[1235]=(byte)i; if(sha1_32(buf)==291036541){break;} }
+ for(i=-85;i<-64;i++){ buf[1236]=(byte)i; if(sha1_32(buf)==305816622){break;} }
+ for(i=108;i<127;i++){ buf[1237]=(byte)i; if(sha1_32(buf)==138337407){break;} }
+ for(i=106;i<114;i++){ buf[1238]=(byte)i; if(sha1_32(buf)==990276981){break;} }
+ for(i=-128;i<-112;i++){ buf[1239]=(byte)i; if(sha1_32(buf)==758125124){break;} }
+ for(i=-78;i<-70;i++){ buf[1240]=(byte)i; if(sha1_32(buf)==821392939){break;} }
+ for(i=31;i<38;i++){ buf[1241]=(byte)i; if(sha1_32(buf)==1272689785){break;} }
+ for(i=-68;i<-54;i++){ buf[1242]=(byte)i; if(sha1_32(buf)==-451791906){break;} }
+ for(i=-128;i<-121;i++){ buf[1243]=(byte)i; if(sha1_32(buf)==-1006720983){break;} }
+ for(i=9;i<28;i++){ buf[1244]=(byte)i; if(sha1_32(buf)==-2038368783){break;} }
+ for(i=105;i<116;i++){ buf[1245]=(byte)i; if(sha1_32(buf)==987022077){break;} }
+ for(i=-89;i<-68;i++){ buf[1246]=(byte)i; if(sha1_32(buf)==-138796089){break;} }
+ for(i=54;i<61;i++){ buf[1247]=(byte)i; if(sha1_32(buf)==283079607){break;} }
+ for(i=56;i<79;i++){ buf[1248]=(byte)i; if(sha1_32(buf)==-386702342){break;} }
+ for(i=115;i<128;i++){ buf[1249]=(byte)i; if(sha1_32(buf)==-1278013589){break;} }
+ for(i=24;i<46;i++){ buf[1250]=(byte)i; if(sha1_32(buf)==-1543136169){break;} }
+ for(i=2;i<29;i++){ buf[1251]=(byte)i; if(sha1_32(buf)==776602743){break;} }
+ for(i=20;i<29;i++){ buf[1252]=(byte)i; if(sha1_32(buf)==1740349896){break;} }
+ for(i=66;i<84;i++){ buf[1253]=(byte)i; if(sha1_32(buf)==-281134038){break;} }
+ for(i=-29;i<-6;i++){ buf[1254]=(byte)i; if(sha1_32(buf)==-1704754264){break;} }
+ for(i=67;i<93;i++){ buf[1255]=(byte)i; if(sha1_32(buf)==1978860888){break;} }
+ for(i=-12;i<4;i++){ buf[1256]=(byte)i; if(sha1_32(buf)==-310399196){break;} }
+ for(i=46;i<75;i++){ buf[1257]=(byte)i; if(sha1_32(buf)==-358370940){break;} }
+ for(i=-33;i<-21;i++){ buf[1258]=(byte)i; if(sha1_32(buf)==427972523){break;} }
+ for(i=101;i<119;i++){ buf[1259]=(byte)i; if(sha1_32(buf)==-97871858){break;} }
+ for(i=84;i<97;i++){ buf[1260]=(byte)i; if(sha1_32(buf)==-817348087){break;} }
+ for(i=-112;i<-96;i++){ buf[1261]=(byte)i; if(sha1_32(buf)==1206459387){break;} }
+ for(i=-123;i<-108;i++){ buf[1262]=(byte)i; if(sha1_32(buf)==1759629363){break;} }
+ for(i=-111;i<-97;i++){ buf[1263]=(byte)i; if(sha1_32(buf)==-621376529){break;} }
+ for(i=49;i<63;i++){ buf[1264]=(byte)i; if(sha1_32(buf)==187753316){break;} }
+ for(i=69;i<93;i++){ buf[1265]=(byte)i; if(sha1_32(buf)==-1688586181){break;} }
+ for(i=53;i<69;i++){ buf[1266]=(byte)i; if(sha1_32(buf)==-2002801302){break;} }
+ for(i=11;i<28;i++){ buf[1267]=(byte)i; if(sha1_32(buf)==67885761){break;} }
+ for(i=-120;i<-110;i++){ buf[1268]=(byte)i; if(sha1_32(buf)==310175039){break;} }
+ for(i=-47;i<-26;i++){ buf[1269]=(byte)i; if(sha1_32(buf)==-2065221852){break;} }
+ for(i=59;i<77;i++){ buf[1270]=(byte)i; if(sha1_32(buf)==-1770810308){break;} }
+ for(i=3;i<27;i++){ buf[1271]=(byte)i; if(sha1_32(buf)==414020951){break;} }
+ for(i=78;i<107;i++){ buf[1272]=(byte)i; if(sha1_32(buf)==1642487656){break;} }
+ for(i=49;i<73;i++){ buf[1273]=(byte)i; if(sha1_32(buf)==-1689510636){break;} }
+ for(i=45;i<63;i++){ buf[1274]=(byte)i; if(sha1_32(buf)==1165890790){break;} }
+ for(i=68;i<83;i++){ buf[1275]=(byte)i; if(sha1_32(buf)==1792898471){break;} }
+ for(i=62;i<77;i++){ buf[1276]=(byte)i; if(sha1_32(buf)==1970953191){break;} }
+ for(i=110;i<120;i++){ buf[1277]=(byte)i; if(sha1_32(buf)==1828616521){break;} }
+ for(i=-83;i<-71;i++){ buf[1278]=(byte)i; if(sha1_32(buf)==-501095679){break;} }
+ for(i=-128;i<-121;i++){ buf[1279]=(byte)i; if(sha1_32(buf)==-1042754258){break;} }
+ for(i=107;i<120;i++){ buf[1280]=(byte)i; if(sha1_32(buf)==-2021034367){break;} }
+ for(i=104;i<122;i++){ buf[1281]=(byte)i; if(sha1_32(buf)==-515216398){break;} }
+ for(i=20;i<41;i++){ buf[1282]=(byte)i; if(sha1_32(buf)==1999387980){break;} }
+ for(i=-60;i<-29;i++){ buf[1283]=(byte)i; if(sha1_32(buf)==-797913149){break;} }
+ for(i=-100;i<-85;i++){ buf[1284]=(byte)i; if(sha1_32(buf)==237396739){break;} }
+ for(i=-36;i<-28;i++){ buf[1285]=(byte)i; if(sha1_32(buf)==-1641086748){break;} }
+ for(i=-89;i<-65;i++){ buf[1286]=(byte)i; if(sha1_32(buf)==-1834110753){break;} }
+ return buf;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CPUInfoUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CPUInfoUtil.java
new file mode 100644
index 000000000..a059a1541
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CPUInfoUtil.java
@@ -0,0 +1,172 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.os.Build;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+
+/**
+ * cpu使用率获取工具类
+ * Created by lirui on 2017/8/2.
+ */
+
+public class CPUInfoUtil {
+ private long lastTotalCpu = 0;
+ private long lastProcessCpu = 0;
+
+ private final String PackageName;
+
+ private volatile boolean isRunningCPU = false;
+ private volatile double cpuRate = 0;
+
+ public CPUInfoUtil(Context context) {
+ if (Build.VERSION.SDK_INT >= 26) {
+ final String pn = context.getPackageName();
+ if (pn.length() <= 16) {
+ PackageName = pn;
+ } else {
+ PackageName = pn.substring(0, 15) + "+";
+ }
+// Log.e(TAG, "CSVUtils PackageName " + PackageName);
+ isRunningCPU = true;
+ CPUInfoThread cpuinfothread = new CPUInfoThread();
+ cpuinfothread.start();
+ } else {
+ PackageName = null;
+ }
+ }
+
+ public double getProcessCpuUsed() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ return cpuRate;
+ } else {
+ double pcpu = 0;
+ double tmp = 1.0;
+ long nowTotalCpu = getTotalCpu();
+ long nowProcessCpu = getMyProcessCpu();
+ if (nowTotalCpu != 0 && (nowTotalCpu - lastTotalCpu) != 0) {
+// Log.e(TAG, "cpu used nowProcessCpu " + nowProcessCpu + " lastProcessCpu " + lastProcessCpu + " nowTotalCpu " + nowTotalCpu + " lastTotalCpu " + lastTotalCpu);
+ pcpu = 100 * (tmp * (nowProcessCpu - lastProcessCpu) / (nowTotalCpu - lastTotalCpu));
+ }
+ lastProcessCpu = nowProcessCpu;
+ lastTotalCpu = nowTotalCpu;
+ return pcpu < 0 ? 0 : pcpu;
+ }
+ }
+
+ public void close() {
+ if (Build.VERSION.SDK_INT >= 26) {
+ isRunningCPU = false;
+ }
+ }
+
+ private long getTotalCpu() {
+ String[] cpuInfos = null;
+ try {
+ RandomAccessFile reader = null;
+ reader = new RandomAccessFile("/proc/stat", "r");
+ String load = reader.readLine();
+ reader.close();
+ cpuInfos = load.split(" ");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ long totalCpu = 0;
+ try {
+ totalCpu = Long.parseLong(cpuInfos[2])
+ + Long.parseLong(cpuInfos[3]) + Long.parseLong(cpuInfos[4])
+ + Long.parseLong(cpuInfos[6]) + Long.parseLong(cpuInfos[5])
+ + Long.parseLong(cpuInfos[7]) + Long.parseLong(cpuInfos[8]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ return totalCpu;
+ }
+
+ private long getMyProcessCpu() {
+ String[] cpuInfos = null;
+ try {
+ int pid = android.os.Process.myPid();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ new FileInputStream("/proc/" + pid + "/stat")), 1000);
+ String load = reader.readLine();
+ reader.close();
+ cpuInfos = load.split(" ");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ long appCpuTime = 0;
+ try {
+ appCpuTime = Long.parseLong(cpuInfos[13])
+ + Long.parseLong(cpuInfos[14]) + Long.parseLong(cpuInfos[15])
+ + Long.parseLong(cpuInfos[16]);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ return appCpuTime;
+ }
+
+ class CPUInfoThread extends Thread {
+
+ private double allCPU = 0;
+
+ @Override
+ public void run() {
+ String line = null;
+ InputStream is = null;
+ try {
+
+ Runtime runtime = Runtime.getRuntime();
+ Process proc = runtime.exec("top -d 1");
+ is = proc.getInputStream();
+
+ // 换成BufferedReader
+ BufferedReader buf = new BufferedReader(new InputStreamReader(is));
+ do {
+ line = buf.readLine();
+ if (allCPU == 0 && line.contains("user") && line.contains("nice") && line.contains("sys") && line.contains("idle") && line.contains("iow") && line.contains("irq") && line.contains("sirq") && line.contains("host")) {
+ if (line.indexOf("%cpu ") > 0)
+ allCPU = Double.parseDouble(line.split("%cpu ")[0]);
+ if (allCPU == 0) {
+ String[] s = line.split("%,");
+ for (String st : s) {
+ String[] sts = st.split(" ");
+ if (sts.length > 0)
+ allCPU += Double.parseDouble(sts[sts.length - 1]);
+ }
+ }
+ }
+ // 读取到相应pkgName跳出循环(或者未找到)
+ if (line == null || line.endsWith(PackageName)) {
+// Log.e(TAG, "cpu line : " + line);
+ String str[] = line.split(" ");
+ int t = 0;
+ for (int i = str.length - 1; i > 0; i--) {
+ if (!str[i].isEmpty() && ++t == 4) {
+// Log.e(TAG, "cpu : " + str[i] + " allCPU " + allCPU);
+ cpuRate = 100 * Double.parseDouble(str[i]) / allCPU;
+ }
+ }
+ continue;
+ }
+ } while (isRunningCPU);
+
+ if (is != null) {
+ buf.close();
+ is.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CSVUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CSVUtils.java
new file mode 100644
index 000000000..e30df5b0a
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/CSVUtils.java
@@ -0,0 +1,168 @@
+package com.yunbao.faceunity.utils;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Created by tujh on 2017/11/2.
+ */
+public class CSVUtils {
+ public static final String TAG = CSVUtils.class.getSimpleName();
+ /* 每 100 帧统计一次 */
+ public static final int FRAME_STEP = 100;
+
+ public static final String COMMA = ",";
+
+ private OutputStreamWriter mStreamWriter;
+
+ private ActivityManager mActivityManager;
+
+ private Handler mHandler;
+
+ private CPUInfoUtil mCPUInfoUtil;
+
+ private int mFrameRate;
+ private volatile double mCpuUsed;
+ private volatile double mAverageFps;
+ private volatile double mAverageRenderTime;
+ private volatile double mMemory;
+ private long mSumRenderTimeInNano;
+ private volatile long mTimestamp;
+
+ public CSVUtils(Context context) {
+ mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ mCPUInfoUtil = new CPUInfoUtil(context);
+
+ HandlerThread handlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ }
+
+ public void initHeader(String folderName, StringBuilder headerInfo) {
+ Log.d(TAG, "initHeader() called with: folderName = [" + folderName + "], headerInfo = [" + headerInfo + "]");
+ StringBuilder stringBuilder = new StringBuilder().append("时间").append(COMMA)
+ .append("帧率").append(COMMA)
+ .append("渲染耗时").append(COMMA)
+ .append("CPU").append(COMMA)
+ .append("内存").append(COMMA);
+ if (headerInfo != null) {
+ stringBuilder.append(headerInfo);
+ }
+ stringBuilder.append("\n");
+
+ File file = new File(folderName);
+ File parentFile = file.getParentFile();
+ if (!parentFile.exists()) {
+ parentFile.mkdirs();
+ }
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ mStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), "GBK");
+ } catch (IOException e) {
+ Log.e(TAG, "CSVUtils: ", e);
+ }
+ flush(stringBuilder);
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ public void writeCsv(final StringBuilder extraInfo, long renderTimeInNano) {
+ if (mStreamWriter == null) {
+ return;
+ }
+
+ mSumRenderTimeInNano += renderTimeInNano;
+ if (mFrameRate % FRAME_STEP == FRAME_STEP - 1) {
+ mTimestamp = System.currentTimeMillis();
+ mAverageFps = FPSUtil.fpsAVG(FRAME_STEP);
+ mAverageRenderTime = (double) mSumRenderTimeInNano / FRAME_STEP / 1_000_000;
+ mSumRenderTimeInNano = 0;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCpuUsed = mCPUInfoUtil.getProcessCpuUsed();
+ mMemory = MemoryInfoUtil.getMemory(mActivityManager.getProcessMemoryInfo(new int[]{Process.myPid()}));
+ String strCPU = String.format(Locale.getDefault(), "%.2f", mCpuUsed);
+ String strMemory = String.format(Locale.getDefault(), "%.2f", mMemory);
+
+ StringBuilder stringBuilder = new StringBuilder();
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.getDefault());
+
+ stringBuilder.append(dateFormat.format(new Date(mTimestamp))).append(COMMA)
+ .append(String.format(Locale.getDefault(), "%.2f", mAverageFps)).append(COMMA)
+ .append(String.format(Locale.getDefault(), "%.2f", mAverageRenderTime)).append(COMMA)
+ .append(strCPU).append(COMMA)
+ .append(strMemory).append(COMMA);
+
+ if (extraInfo != null) {
+ stringBuilder.append(extraInfo);
+ }
+ stringBuilder.append("\n");
+ flush(stringBuilder);
+ }
+ });
+ }
+ mFrameRate++;
+ }
+
+ private void flush(StringBuilder stringBuilder) {
+ if (mStreamWriter == null) {
+ return;
+ }
+ try {
+ mStreamWriter.write(stringBuilder.toString());
+ mStreamWriter.flush();
+ } catch (IOException e) {
+ Log.e(TAG, "flush: ", e);
+ }
+ }
+
+ public void close() {
+ Log.d(TAG, "close: ");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mStreamWriter != null) {
+ try {
+ mStreamWriter.close();
+ mStreamWriter = null;
+ } catch (IOException e) {
+ Log.e(TAG, "close: ", e);
+ }
+ }
+ }
+ });
+ mHandler.getLooper().quitSafely();
+ mHandler = null;
+ mCPUInfoUtil.close();
+ }
+
+ public double getCpuUsed() {
+ return mCpuUsed;
+ }
+
+ public double getMemory() {
+ return mMemory;
+ }
+
+ public double getAverageRenderTime() {
+ return mAverageRenderTime;
+ }
+
+ public double getAverageFps() {
+ return mAverageFps;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FPSUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FPSUtil.java
new file mode 100644
index 000000000..4a20acafb
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FPSUtil.java
@@ -0,0 +1,63 @@
+package com.yunbao.faceunity.utils;
+
+/**
+ * FPS工具类
+ * Created by tujh on 2018/5/24.
+ */
+public class FPSUtil {
+ private static final int NANO_IN_ONE_MILLI_SECOND = 1000000;
+ private static final int NANO_IN_ONE_SECOND = 1000 * NANO_IN_ONE_MILLI_SECOND;
+
+ private static long sLastFrameTimeStamp = 0;
+
+ /**
+ * 每帧都计算
+ *
+ * @return
+ */
+ public static double fps() {
+ long tmp = System.nanoTime();
+ double fps = ((double) NANO_IN_ONE_SECOND) / (tmp - sLastFrameTimeStamp);
+ sLastFrameTimeStamp = tmp;
+// Log.e(TAG, "FPS : " + fps);
+ return fps;
+ }
+
+ private static long mStartTime = 0;
+
+ /**
+ * 平均值
+ *
+ * @return
+ */
+ public static double fpsAVG(int time) {
+ long tmp = System.nanoTime();
+ double fps = ((double) NANO_IN_ONE_SECOND) * time / (tmp - mStartTime);
+ mStartTime = tmp;
+// Log.e(TAG, "FPS : " + fps);
+ return fps;
+ }
+
+ private long mLimitMinTime = 33333333;
+ private long mLimitStartTime;
+ private int mLimitFrameRate;
+
+ public void setLimitMinTime(long limitMinTime) {
+ mLimitMinTime = limitMinTime;
+ }
+
+ public void limit() {
+ try {
+ if (mLimitFrameRate == 0 || mLimitFrameRate > 600000) {
+ mLimitStartTime = System.nanoTime();
+ mLimitFrameRate = 0;
+ }
+ long sleepTime = mLimitMinTime * mLimitFrameRate++ - (System.nanoTime() - mLimitStartTime);
+ if (sleepTime > 0) {
+ Thread.sleep(sleepTime / NANO_IN_ONE_MILLI_SECOND, (int) (sleepTime % NANO_IN_ONE_MILLI_SECOND));
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FURenderer.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FURenderer.java
new file mode 100644
index 000000000..313aed9eb
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FURenderer.java
@@ -0,0 +1,326 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.hardware.Camera;
+
+import com.faceunity.core.callback.OperateCallback;
+import com.faceunity.core.entity.FURenderInputData;
+import com.faceunity.core.entity.FURenderOutputData;
+import com.faceunity.core.enumeration.CameraFacingEnum;
+import com.faceunity.core.enumeration.FUAIProcessorEnum;
+import com.faceunity.core.enumeration.FUAITypeEnum;
+import com.faceunity.core.faceunity.FURenderConfig;
+import com.faceunity.core.faceunity.FURenderKit;
+import com.faceunity.core.faceunity.FURenderManager;
+import com.faceunity.core.utils.CameraUtils;
+import com.faceunity.core.utils.FULogger;
+import com.yunbao.faceunity.listener.FURendererListener;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * DESC:
+ * Created on 2021/4/26
+ */
+public class FURenderer extends IFURenderer {
+
+ private static final String TAG = "FURenderer";
+
+ public volatile static FURenderer INSTANCE;
+
+ public static FURenderer getInstance() {
+ if (INSTANCE == null) {
+ synchronized (FURenderer.class) {
+ if (INSTANCE == null) {
+ INSTANCE = new FURenderer();
+ INSTANCE.mFURenderKit = FURenderKit.getInstance();
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * 状态回调监听
+ */
+ private FURendererListener mFURendererListener;
+
+
+ /* 特效FURenderKit*/
+ private FURenderKit mFURenderKit;
+
+ /* AI道具*/
+ private String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor.bundle";
+ private String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
+
+ /* GL 线程 ID */
+ private Long mGlThreadId = 0L;
+ /* 任务队列 */
+ private ArrayList mEventQueue = new ArrayList<>(16);
+ private final Object queueLock = new Object();
+ /* 渲染开关标识 */
+ private volatile boolean mRendererSwitch = false;
+ /* 清除队列标识 */
+ private volatile boolean mClearQueue = false;
+
+ /* 相机角度-朝向映射 */
+ private HashMap cameraOrientationMap = new HashMap<>();
+
+ /*检测类型*/
+ private FUAIProcessorEnum aIProcess = FUAIProcessorEnum.FACE_PROCESSOR;
+ /*检测标识*/
+ private int aIProcessTrackStatus = -1;
+
+ public String getVersion() {
+ return mFURenderKit.getVersion();
+ }
+
+ /**
+ * 初始化鉴权
+ *
+ * @param context
+ */
+ @Override
+ public void setup(Context context) {
+ FURenderManager.setKitDebug(FULogger.LogLevel.TRACE);
+ FURenderManager.setCoreDebug(FULogger.LogLevel.DEBUG);
+ FURenderManager.registerFURender(context, Authpack.A(), new OperateCallback() {
+ @Override
+ public void onSuccess(int i, @NotNull String s) {
+ if (i == FURenderConfig.OPERATE_SUCCESS_AUTH) {
+ mFURenderKit.getFUAIController().loadAIProcessor(BUNDLE_AI_FACE, FUAITypeEnum.FUAITYPE_FACEPROCESSOR);
+ mFURenderKit.getFUAIController().loadAIProcessor(BUNDLE_AI_HUMAN, FUAITypeEnum.FUAITYPE_HUMAN_PROCESSOR);
+ mFURenderKit.setReadBackSync(true);
+ int cameraFrontOrientation = CameraUtils.INSTANCE.getCameraOrientation(Camera.CameraInfo.CAMERA_FACING_FRONT);
+ int cameraBackOrientation = CameraUtils.INSTANCE.getCameraOrientation(Camera.CameraInfo.CAMERA_FACING_BACK);
+ cameraOrientationMap.put(cameraFrontOrientation, CameraFacingEnum.CAMERA_FRONT);
+ cameraOrientationMap.put(cameraBackOrientation, CameraFacingEnum.CAMERA_BACK);
+ }
+ }
+
+ @Override
+ public void onFail(int i, @NotNull String s) {
+ }
+ });
+ }
+
+ /**
+ * 开启合成状态
+ */
+ @Override
+ public void prepareRenderer(FURendererListener mFURendererListener) {
+ this.mFURendererListener = mFURendererListener;
+ mRendererSwitch = true;
+ mClearQueue = false;
+ queueEvent(() -> mGlThreadId = Thread.currentThread().getId());
+ mFURendererListener.onPrepare();
+ }
+
+
+ /**
+ * 双输入接口,输入 buffer 和 texture,必须在具有 GL 环境的线程调用
+ * 由于省去数据拷贝,性能相对最优,优先推荐使用。
+ * 缺点是无法保证 buffer 和纹理对齐,可能出现点位和效果对不上的情况。
+ *
+ * @param img NV21 buffer
+ * @param texId 纹理 ID
+ * @param width 宽
+ * @param height 高
+ * @return
+ */
+ @Override
+ public int onDrawFrameDualInput(byte[] img, int texId, int width, int height) {
+ prepareDrawFrame();
+ if (!mRendererSwitch) {
+ return texId;
+ }
+ FURenderInputData inputData = new FURenderInputData(width, height);
+ if (img != null) {
+ inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));
+ }
+ if (texId != -1) {
+ inputData.setTexture(new FURenderInputData.FUTexture(inputTextureType, texId));
+ }
+ FURenderInputData.FURenderConfig config = inputData.getRenderConfig();
+ config.setExternalInputType(externalInputType);
+ config.setInputOrientation(inputOrientation);
+ config.setDeviceOrientation(deviceOrientation);
+ config.setInputBufferMatrix(inputBufferMatrix);
+ config.setInputTextureMatrix(inputTextureMatrix);
+ config.setOutputMatrix(outputMatrix);
+ config.setCameraFacing(cameraFacing);
+ FURenderOutputData outputData = mFURenderKit.renderWithInput(inputData);
+ if (outputData.getTexture() != null && outputData.getTexture().getTexId() > 0) {
+ return outputData.getTexture().getTexId();
+ }
+ return texId;
+ }
+
+ public FURenderOutputData onDrawFrameInputWithReturn(byte[] img, int width, int height) {
+ prepareDrawFrame();
+ if (!mRendererSwitch) {
+ return null ;
+ }
+ FURenderInputData inputData = new FURenderInputData(width, height);
+ if (img != null) {
+ inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));
+ }
+ FURenderInputData.FURenderConfig config = inputData.getRenderConfig();
+ config.setExternalInputType(externalInputType);
+ config.setInputOrientation(inputOrientation);
+ config.setDeviceOrientation(deviceOrientation);
+ config.setInputBufferMatrix(inputBufferMatrix);
+ config.setInputTextureMatrix(inputTextureMatrix);
+ config.setCameraFacing(cameraFacing);
+ config.setNeedBufferReturn(true);
+ config.setOutputMatrix(outputMatrix);
+ return mFURenderKit.renderWithInput(inputData);
+ }
+
+ /**
+ * 类似 GLSurfaceView 的 queueEvent 机制,把任务抛到 GL 线程执行。
+ *
+ * @param runnable
+ */
+ @Override
+ public void queueEvent(Runnable runnable) {
+ if (runnable == null) {
+ return;
+ }
+ if (mGlThreadId == Thread.currentThread().getId()) {
+ runnable.run();
+ } else {
+ synchronized (queueLock) {
+ mEventQueue.add(runnable);
+ }
+ }
+ }
+
+
+ /**
+ * 释放资源
+ */
+ @Override
+ public void release() {
+ mRendererSwitch = false;
+ mClearQueue = true;
+ mGlThreadId = 0L;
+ synchronized (queueLock) {
+ mEventQueue.clear();
+ mClearQueue = false;
+ mFURenderKit.release();
+ aIProcessTrackStatus = -1;
+ if (mFURendererListener != null) {
+ mFURendererListener.onRelease();
+ mFURendererListener = null;
+ }
+ }
+ }
+
+
+ /**
+ * 渲染前置执行
+ *
+ * @return
+ */
+ private void prepareDrawFrame() {
+ benchmarkFPS();
+
+ // 执行任务队列中的任务
+ synchronized (queueLock) {
+ while (!mEventQueue.isEmpty() && !mClearQueue) {
+ mEventQueue.remove(0).run();
+ }
+ }
+ // AI检测
+ trackStatus();
+ }
+
+ /**
+ * 设置检测类型
+ *
+ * @param type
+ */
+ @Override
+ public void setAIProcessTrackType(FUAIProcessorEnum type) {
+ aIProcess = type;
+ aIProcessTrackStatus = -1;
+ }
+
+ /**
+ * 设置FPS检测
+ *
+ * @param enable
+ */
+ @Override
+ public void setMarkFPSEnable(boolean enable) {
+ mIsRunBenchmark = enable;
+ }
+
+ @Override
+ public void setInputOrientation(int inputOrientation) {
+ if (cameraOrientationMap.containsKey(inputOrientation)) {
+ setCameraFacing(cameraOrientationMap.get(inputOrientation));
+ }
+ super.setInputOrientation(inputOrientation);
+ }
+
+ /**
+ * AI识别数目检测
+ */
+ private void trackStatus() {
+ int trackCount;
+ if (aIProcess == FUAIProcessorEnum.HAND_GESTURE_PROCESSOR) {
+ trackCount = mFURenderKit.getFUAIController().handProcessorGetNumResults();
+ } else if (aIProcess == FUAIProcessorEnum.HUMAN_PROCESSOR) {
+ trackCount = mFURenderKit.getFUAIController().humanProcessorGetNumResults();
+ } else {
+ trackCount = mFURenderKit.getFUAIController().isTracking();
+ }
+ if (trackCount != aIProcessTrackStatus) {
+ aIProcessTrackStatus = trackCount;
+ } else {
+ return;
+ }
+ if (mFURendererListener != null) {
+ mFURendererListener.onTrackStatusChanged(aIProcess, trackCount);
+ }
+ }
+ //endregion AI识别
+
+ //------------------------------FPS 渲染时长回调相关定义------------------------------------
+
+ private static final int NANO_IN_ONE_MILLI_SECOND = 1_000_000;
+ private static final int NANO_IN_ONE_SECOND = 1_000_000_000;
+ private static final int FRAME_COUNT = 20;
+ private boolean mIsRunBenchmark = false;
+ private int mCurrentFrameCount;
+ private long mLastFrameTimestamp;
+ private long mSumCallTime;
+ private long mCallStartTime;
+
+ private void benchmarkFPS() {
+ if (!mIsRunBenchmark) {
+ return;
+ }
+ if (++mCurrentFrameCount == FRAME_COUNT) {
+ long tmp = System.nanoTime();
+ double fps = (double) NANO_IN_ONE_SECOND / ((double) (tmp - mLastFrameTimestamp) / FRAME_COUNT);
+ double renderTime = (double) mSumCallTime / FRAME_COUNT / NANO_IN_ONE_MILLI_SECOND;
+ mLastFrameTimestamp = tmp;
+ mSumCallTime = 0;
+ mCurrentFrameCount = 0;
+
+ if (mFURendererListener != null) {
+ mFURendererListener.onFpsChanged(fps, renderTime);
+ }
+ }
+ }
+
+
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FUUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FUUtils.java
new file mode 100644
index 000000000..2194ea131
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FUUtils.java
@@ -0,0 +1,271 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.faceunity.core.model.facebeauty.FaceBeauty;
+import com.google.gson.Gson;
+import com.yunbao.faceunity.data.FaceBeautyData;
+import com.yunbao.faceunity.entity.FaceBeautyFilterBean;
+
+import java.util.ArrayList;
+
+/**
+ * 将FaceBeauty FaceBeautyData 互相设置
+ */
+public class FUUtils {
+ private static final String SP_NAME = "FaceBeauty";
+ private static final String SP_KEY_NAME = "faceBeautyData";
+
+ /**
+ * 将FaceBeauty 转 FaceBeautyData
+ *
+ * @param faceBeautyData
+ */
+ public static void buildFaceBeautyData(FaceBeautyData faceBeautyData, FaceBeauty faceBeauty, ArrayList filterList, int styleTypeIndex) {
+ if (faceBeauty != null) {
+ /* 美肤 */
+ /* 磨皮类型 */
+ faceBeautyData.blurType = faceBeauty.getBlurType();
+ /* 磨皮程度 */
+ faceBeautyData.blurIntensity = faceBeauty.getBlurIntensity();
+ /* 美白程度 */
+ faceBeautyData.colorIntensity = faceBeauty.getColorIntensity();
+ /* 红润程度 */
+ faceBeautyData.redIntensity = faceBeauty.getRedIntensity();
+ /* 锐化程度 */
+ faceBeautyData.sharpenIntensity = faceBeauty.getSharpenIntensity();
+ /* 亮眼程度 */
+ faceBeautyData.eyeBrightIntensity = faceBeauty.getEyeBrightIntensity();
+ /* 美牙程度 */
+ faceBeautyData.toothIntensity = faceBeauty.getToothIntensity();
+ /* 去黑眼圈强度*/
+ faceBeautyData.removePouchIntensity = faceBeauty.getRemovePouchIntensity();
+ /* 去法令纹强度*/
+ faceBeautyData.removeLawPatternIntensity = faceBeauty.getRemoveLawPatternIntensity();
+
+ /* 美型 */
+ /* 瘦脸程度 */
+ faceBeautyData.cheekThinningIntensity = faceBeauty.getCheekThinningIntensity();
+ /* V脸程度 */
+ faceBeautyData.cheekVIntensity = faceBeauty.getCheekVIntensity();
+ /* 窄脸程度 */
+ faceBeautyData.cheekNarrowIntensity = faceBeauty.getCheekNarrowIntensity();
+ /* 短脸程度 */
+ faceBeautyData.cheekShortIntensity = faceBeauty.getCheekShortIntensity();
+ /* 小脸程度 */
+ faceBeautyData.cheekSmallIntensity = faceBeauty.getCheekSmallIntensity();
+ /* 瘦颧骨 */
+ faceBeautyData.cheekBonesIntensity = faceBeauty.getCheekBonesIntensity();
+ /* 瘦下颌骨 */
+ faceBeautyData.lowerJawIntensity = faceBeauty.getLowerJawIntensity();
+ /* 大眼程度 */
+ faceBeautyData.eyeEnlargingIntensity = faceBeauty.getEyeEnlargingIntensity();
+ /* 圆眼程度 */
+ faceBeautyData.eyeCircleIntensity = faceBeauty.getEyeCircleIntensity();
+ /* 下巴调整程度 */
+ faceBeautyData.chinIntensity = faceBeauty.getChinIntensity();
+ /* 额头调整程度 */
+ faceBeautyData.forHeadIntensity = faceBeauty.getForHeadIntensity();
+ /* 瘦鼻程度 */
+ faceBeautyData.noseIntensity = faceBeauty.getNoseIntensity();
+ /* 嘴巴调整程度 */
+ faceBeautyData.mouthIntensity = faceBeauty.getMouthIntensity();
+ /* 开眼角强度 */
+ faceBeautyData.canthusIntensity = faceBeauty.getCanthusIntensity();
+ /* 眼睛间距 */
+ faceBeautyData.eyeSpaceIntensity = faceBeauty.getEyeSpaceIntensity();
+ /* 眼睛角度 */
+ faceBeautyData.eyeRotateIntensity = faceBeauty.getEyeRotateIntensity();
+ /* 鼻子长度 */
+ faceBeautyData.longNoseIntensity = faceBeauty.getLongNoseIntensity();
+ /* 调节人中 */
+ faceBeautyData.philtrumIntensity = faceBeauty.getPhiltrumIntensity();
+ /* 微笑嘴角强度 */
+ faceBeautyData.smileIntensity = faceBeauty.getSmileIntensity();
+ /* 眉毛上下 */
+ faceBeautyData.browHeightIntensity = faceBeauty.getBrowHeightIntensity();
+ /* 眉毛间距 */
+ faceBeautyData.browSpaceIntensity = faceBeauty.getBrowSpaceIntensity();
+
+ /* 滤镜相关 */
+ if (filterList != null) {
+ for (FaceBeautyFilterBean filterBean:filterList) {
+ faceBeautyData.filterMap.put(filterBean.getKey(),filterBean.getIntensity());
+ }
+ }
+ /* 滤镜名称 */
+ faceBeautyData.filterName = faceBeauty.getFilterName();
+ /* 滤镜强度 */
+ faceBeautyData.filterIntensity = faceBeauty.getFilterIntensity();
+
+ /* 是否开启风格 */
+ faceBeautyData.styleTypeIndex = styleTypeIndex;
+ }
+ }
+
+ /**
+ * 将FaceBeauty 转 FaceBeautyData
+ *
+ * @param faceBeautyData
+ */
+ public static boolean setFaceBeauty(FaceBeautyData faceBeautyData ,FaceBeauty faceBeauty) {
+ if (faceBeautyData == null) {
+ return false;
+ }
+ if (faceBeauty != null) {
+ /* 如果用户开启了风格推荐 */
+ //下面是否则
+ /* 美肤 */
+ /* 磨皮类型 */
+ if (faceBeautyData.blurType != faceBeauty.getBlurType())
+ faceBeauty.setBlurType(faceBeautyData.blurType);
+ /* 磨皮程度 */
+ if (faceBeautyData.blurIntensity != faceBeauty.getBlurIntensity())
+ faceBeauty.setBlurIntensity(faceBeautyData.blurIntensity);
+ /* 美白程度 */
+ if (faceBeautyData.colorIntensity != faceBeauty.getColorIntensity())
+ faceBeauty.setColorIntensity(faceBeautyData.colorIntensity);
+ /* 红润程度 */
+ if (faceBeautyData.redIntensity != faceBeauty.getRedIntensity())
+ faceBeauty.setRedIntensity(faceBeautyData.redIntensity);
+ /* 锐化程度 */
+ if (faceBeautyData.sharpenIntensity != faceBeauty.getSharpenIntensity())
+ faceBeauty.setSharpenIntensity(faceBeautyData.sharpenIntensity);
+ /* 亮眼程度 */
+ if (faceBeautyData.eyeBrightIntensity != faceBeauty.getEyeBrightIntensity())
+ faceBeauty.setEyeBrightIntensity(faceBeautyData.eyeBrightIntensity);
+ /* 美牙程度 */
+ if (faceBeautyData.toothIntensity != faceBeauty.getToothIntensity())
+ faceBeauty.setToothIntensity(faceBeautyData.toothIntensity);
+ /* 去黑眼圈强度*/
+ if (faceBeautyData.removePouchIntensity != faceBeauty.getRemovePouchIntensity())
+ faceBeauty.setRemovePouchIntensity(faceBeautyData.removePouchIntensity);
+ /* 去法令纹强度*/
+ if (faceBeautyData.removeLawPatternIntensity != faceBeauty.getRemoveLawPatternIntensity())
+ faceBeauty.setRemoveLawPatternIntensity(faceBeautyData.removeLawPatternIntensity);
+
+ /* 美型 */
+ /* 瘦脸程度 */
+ if (faceBeautyData.cheekThinningIntensity != faceBeauty.getCheekThinningIntensity())
+ faceBeauty.setCheekThinningIntensity(faceBeautyData.cheekThinningIntensity);
+ /* V脸程度 */
+ if (faceBeautyData.cheekVIntensity != faceBeauty.getCheekVIntensity())
+ faceBeauty.setCheekVIntensity(faceBeautyData.cheekVIntensity);
+ /* 窄脸程度 */
+ /* V脸程度 */
+ if (faceBeautyData.cheekNarrowIntensity != faceBeauty.getCheekNarrowIntensity())
+ faceBeauty.setCheekNarrowIntensity(faceBeautyData.cheekNarrowIntensity);
+ /* 短脸程度 */
+ if (faceBeautyData.cheekShortIntensity != faceBeauty.getCheekShortIntensity())
+ faceBeauty.setCheekShortIntensity(faceBeautyData.cheekShortIntensity);
+ /* 小脸程度 */
+ if (faceBeautyData.cheekSmallIntensity != faceBeauty.getCheekSmallIntensity())
+ faceBeauty.setCheekSmallIntensity(faceBeautyData.cheekSmallIntensity);
+ /* 瘦颧骨 */
+ if (faceBeautyData.cheekBonesIntensity != faceBeauty.getCheekBonesIntensity())
+ faceBeauty.setCheekBonesIntensity(faceBeautyData.cheekBonesIntensity);
+ /* 瘦下颌骨 */
+ if (faceBeautyData.lowerJawIntensity != faceBeauty.getLowerJawIntensity())
+ faceBeauty.setLowerJawIntensity(faceBeautyData.lowerJawIntensity);
+ /* 大眼程度 */
+ if (faceBeautyData.eyeEnlargingIntensity != faceBeauty.getEyeEnlargingIntensity())
+ faceBeauty.setEyeEnlargingIntensity(faceBeautyData.eyeEnlargingIntensity);
+ /* 圆眼程度 */
+ if (faceBeautyData.eyeCircleIntensity != faceBeauty.getEyeCircleIntensity())
+ faceBeauty.setEyeCircleIntensity(faceBeautyData.eyeCircleIntensity);
+ /* 下巴调整程度 */
+ if (faceBeautyData.chinIntensity != faceBeauty.getChinIntensity())
+ faceBeauty.setChinIntensity(faceBeautyData.chinIntensity);
+ /* 额头调整程度 */
+ if (faceBeautyData.forHeadIntensity != faceBeauty.getForHeadIntensity())
+ faceBeauty.setForHeadIntensity(faceBeautyData.forHeadIntensity);
+ /* 瘦鼻程度 */
+ if (faceBeautyData.noseIntensity != faceBeauty.getNoseIntensity())
+ faceBeauty.setNoseIntensity(faceBeautyData.noseIntensity);
+ /* 嘴巴调整程度 */
+ if (faceBeautyData.mouthIntensity != faceBeauty.getMouthIntensity())
+ faceBeauty.setMouthIntensity(faceBeautyData.mouthIntensity);
+ /* 开眼角强度 */
+ if (faceBeautyData.canthusIntensity != faceBeauty.getCanthusIntensity())
+ faceBeauty.setCanthusIntensity(faceBeautyData.canthusIntensity);
+ /* 眼睛间距 */
+ if (faceBeautyData.eyeSpaceIntensity != faceBeauty.getEyeSpaceIntensity())
+ faceBeauty.setEyeSpaceIntensity(faceBeautyData.eyeSpaceIntensity);
+ /* 眼睛角度 */
+ if (faceBeautyData.eyeRotateIntensity != faceBeauty.getEyeRotateIntensity())
+ faceBeauty.setEyeRotateIntensity(faceBeautyData.eyeRotateIntensity);
+ /* 鼻子长度 */
+ if (faceBeautyData.longNoseIntensity != faceBeauty.getLongNoseIntensity())
+ faceBeauty.setLongNoseIntensity(faceBeautyData.longNoseIntensity);
+ /* 调节人中 */
+ if (faceBeautyData.philtrumIntensity != faceBeauty.getPhiltrumIntensity())
+ faceBeauty.setPhiltrumIntensity(faceBeautyData.philtrumIntensity);
+ /* 微笑嘴角强度 */
+ if (faceBeautyData.smileIntensity != faceBeauty.getSmileIntensity())
+ faceBeauty.setSmileIntensity(faceBeautyData.smileIntensity);
+ /* 眉毛上下 */
+ if (faceBeautyData.browHeightIntensity != faceBeauty.getBrowHeightIntensity())
+ faceBeauty.setBrowHeightIntensity(faceBeautyData.browHeightIntensity);
+ /* 眉毛间距 */
+ if (faceBeautyData.browSpaceIntensity != faceBeauty.getBrowSpaceIntensity())
+ faceBeauty.setBrowSpaceIntensity(faceBeautyData.browSpaceIntensity);
+
+ /* 滤镜相关 */
+ /* 滤镜名称 */
+ if (!faceBeautyData.filterName.equals(faceBeauty.getFilterName()))
+ faceBeauty.setFilterName(faceBeautyData.filterName);
+ if (faceBeautyData.filterIntensity != faceBeauty.getFilterIntensity())
+ faceBeauty.setFilterIntensity(faceBeautyData.filterIntensity);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 将FaceBeautyData保存到文件
+ *
+ * @param faceBeautyData
+ */
+ public static void saveFaceBeautyData2File(FaceBeautyData faceBeautyData, FaceBeauty faceBeauty, ArrayList filterList, int styleTypeIndex) {
+ buildFaceBeautyData(faceBeautyData,faceBeauty,filterList,styleTypeIndex);
+ saveFaceBeautyData2File(faceBeautyData);
+ }
+
+ /**
+ * 将FaceBeautyData保存到文件
+ *
+ * @param faceBeautyData
+ */
+ public static void saveFaceBeautyData2File(FaceBeautyData faceBeautyData) {
+ Gson gson = new Gson();
+ String faceBeautyString = gson.toJson(faceBeautyData);
+ saveToSp(SP_KEY_NAME, faceBeautyString);
+ }
+
+ /**
+ * 获取bean对象
+ *
+ * @return
+ */
+ public static FaceBeautyData loadFaceBeautyData() {
+ String faceBeautyData = loadFormSp(SP_KEY_NAME);
+ if (faceBeautyData != null && !faceBeautyData.isEmpty()) {
+ Gson gson = new Gson();
+ FaceBeautyData faceBeautyDataBean = gson.fromJson(faceBeautyData, FaceBeautyData.class);
+ return faceBeautyDataBean;
+ }
+ return null;
+ }
+
+ private static void saveToSp(String key, String value) {
+ SharedPreferences sp = FaceUnityData.mApplication.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ sp.edit().putString(key, value).apply();
+ }
+
+ private static String loadFormSp(String key) {
+ SharedPreferences sp = FaceUnityData.mApplication.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+ return sp.getString(key, "");
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceSPUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceSPUtils.java
new file mode 100644
index 000000000..f4ba6d5b0
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceSPUtils.java
@@ -0,0 +1,41 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.yunbao.common.CommonAppContext;
+
+public class FaceSPUtils {
+ private static FaceSPUtils utils;
+ private SharedPreferences mSharedPreferences;
+ private FaceSPUtils(){
+ mSharedPreferences = CommonAppContext.sInstance.getSharedPreferences("FaceUnityConfig", Context.MODE_PRIVATE);
+ }
+ public static FaceSPUtils getInstance(){
+ if(utils==null){
+ utils=new FaceSPUtils();
+ }
+ return utils;
+ }
+ public void saveString(String key,String value){
+ mSharedPreferences.edit().putString(key,value).apply();
+ }
+ public String getString(String key){
+ return mSharedPreferences.getString(key,null);
+ }
+ public boolean saveBool(String key,boolean value){
+ return mSharedPreferences.getBoolean(key,false);
+ }
+
+ public void del(String key) {
+ mSharedPreferences.edit().remove(key).apply();
+ }
+
+ public void delStart(String key){
+ for (String _key : mSharedPreferences.getAll().keySet()) {
+ if(_key.startsWith(key)){
+ mSharedPreferences.edit().remove(key).apply();
+ }
+ }
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityConfig.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityConfig.java
new file mode 100644
index 000000000..b6a2d51de
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityConfig.java
@@ -0,0 +1,127 @@
+package com.yunbao.faceunity.utils;
+
+import android.app.Application;
+import android.os.Environment;
+
+
+import java.io.File;
+
+/**
+ * 一些配置参数
+ * DESC:
+ * Created on 2021/3/1
+ */
+public class FaceUnityConfig {
+
+ /************************** 算法Model ******************************/
+ // 人脸识别
+ public static String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor.bundle";
+ // 手势
+ public static String BUNDLE_AI_HAND = "model" + File.separator + "ai_hand_processor.bundle";
+
+ //获取人体bundle
+ public static String getAIHumanBundle() {
+ if (FaceUnityConfig.DEVICE_LEVEL > FuDeviceUtils.DEVICE_LEVEL_MID)
+ return BUNDLE_AI_HUMAN_GPU;
+ else
+ return BUNDLE_AI_HUMAN;
+ }
+
+ // 人体
+ public static String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
+ // 人体
+ public static String BUNDLE_AI_HUMAN_GPU = "model" + File.separator + "ai_human_processor_gpu.bundle";
+ // 头发
+ public static String BUNDLE_AI_HAIR_SEG = "model" + File.separator + "ai_hairseg.bundle";
+ // 舌头
+ public static String BUNDLE_AI_TONGUE = "graphics" + File.separator + "tongue.bundle";
+
+ /************************** 业务道具存储 ******************************/
+ // 美颜
+ public static String BUNDLE_FACE_BEAUTIFICATION = "graphics" + File.separator + "face_beautification.bundle";
+
+ // 美妆
+ public static String BUNDLE_FACE_MAKEUP = "graphics" + File.separator + "face_makeup.bundle";
+ // 美妆根目录
+ private static String MAKEUP_RESOURCE_DIR = "makeup" + File.separator;
+ //美妆单项颜色组合文件
+ public static String MAKEUP_RESOURCE_COLOR_SETUP_JSON = MAKEUP_RESOURCE_DIR + "color_setup.json";
+ // 美妆参数配置文件目录
+ public static String MAKEUP_RESOURCE_JSON_DIR = MAKEUP_RESOURCE_DIR + "config_json" + File.separator;
+ //美妆组合妆句柄文件目录
+ public static String MAKEUP_RESOURCE_COMBINATION_BUNDLE_DIR = MAKEUP_RESOURCE_DIR + "combination_bundle" + File.separator;//
+ //美妆妆容单项句柄文件目录
+ public static String MAKEUP_RESOURCE_ITEM_BUNDLE_DIR = MAKEUP_RESOURCE_DIR + "item_bundle" + File.separator;
+
+ // 美体
+ public static String BUNDLE_BODY_BEAUTY = "graphics" + File.separator + "body_slim.bundle";
+
+ //动漫滤镜
+ public static String BUNDLE_ANIMATION_FILTER = "graphics" + File.separator + "fuzzytoonfilter.bundle";
+
+ // 美发正常色
+ public static String BUNDLE_HAIR_NORMAL = "hair_seg" + File.separator + "hair_normal.bundle";
+ // 美发渐变色
+ public static String BUNDLE_HAIR_GRADIENT = "hair_seg" + File.separator + "hair_gradient.bundle";
+ // 轻美妆
+ public static String BUNDLE_LIGHT_MAKEUP = "light_makeup" + File.separator + "light_makeup.bundle";
+
+ // 海报换脸
+ public static String BUNDLE_POSTER_CHANGE_FACE = "change_face" + File.separator + "change_face.bundle";
+
+ // 绿幕抠像
+ public static String BUNDLE_BG_SEG_GREEN = "bg_seg_green" + File.separator + "green_screen.bundle";
+
+ // 3D抗锯齿
+ public static String BUNDLE_ANTI_ALIASING = "graphics" + File.separator + "fxaa.bundle";
+
+ // 人像分割
+ public static String BUNDLE_BG_SEG_CUSTOM = "effect" + File.separator + "segment" + File.separator + "bg_segment.bundle";
+
+ //mask bundle
+ public static String BUNDLE_LANDMARKS = "effect" + File.separator + "landmarks.bundle";
+
+ //设备等级默认为中级
+ public static int DEVICE_LEVEL = FuDeviceUtils.DEVICE_LEVEL_HIGH;
+
+ //人脸置信度 标准
+ public static float FACE_CONFIDENCE_SCORE = 0.95f;
+
+ //测试使用 -> 是否开启人脸点位,目前仅在美颜,美妆 情况下使用
+ public static boolean IS_OPEN_LAND_MARK = false;
+
+ //设备名称
+ public static String DEVICE_NAME = "";
+
+ //是否开启日志重定向到文件
+ public static boolean OPEN_FILE_LOG = false;
+ //TAG
+ public static final String APP_NAME = "KotlinFaceUnityDemo";
+ //文件夹路径
+ public static String OPEN_FILE_PATH = Environment.getExternalStoragePublicDirectory("") + File.separator + "FaceUnity" + File.separator + APP_NAME + File.separator;
+ //文件夹名称
+ public static String OPEN_FILE_NAME = "openFile.txt";
+ //文件大小
+ public static int OPEN_FILE_MAX_SIZE = 100 * 1024 * 1024;
+ //文件数量
+ public static int OPEN_FILES = 100;
+
+ //timeProfile是否开启
+ public static boolean OPEN_TIME_PROFILE_LOG = false;
+ //timeProfile文件夹路径
+ public static String OPEN_TIME_PROFILE_PATH = Environment.getExternalStoragePublicDirectory("") + File.separator + "FaceUnity" + File.separator + APP_NAME + File.separator;
+
+ //是否开启美颜序列化到磁盘
+ public static boolean OPEN_FACE_BEAUTY_TO_FILE = true;
+
+ //获取缓存路径
+ public static String cacheFilePath(Application application){
+ return application.getCacheDir() + File.separator + "attribute";
+ }
+
+ //绿幕背景切换的时候跳过的帧数
+ public static final int BG_GREEN_FILTER_FRAME = 1;
+
+ //测试用是否展示效果还原按钮
+ public static final boolean IS_SHOW_RESET_BUTTON = false;
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityData.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityData.java
new file mode 100644
index 000000000..bb9cc8a03
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FaceUnityData.java
@@ -0,0 +1,7 @@
+package com.yunbao.faceunity.utils;
+
+import android.content.Context;
+
+public class FaceUnityData {
+ public static Context mApplication;
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FileUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FileUtils.java
new file mode 100644
index 000000000..ae38e1cf0
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FileUtils.java
@@ -0,0 +1,577 @@
+package com.yunbao.faceunity.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.YuvImage;
+import android.media.ExifInterface;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * DESC:
+ * Created on 2021/3/12
+ */
+public class FileUtils {
+ public static final String DCIM_FILE_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
+ public static final String photoFilePath;
+ public static final String videoFilePath;
+ public static final String exportVideoDir;
+
+ static {
+ if (Build.FINGERPRINT.contains("Flyme")
+ || Pattern.compile("Flyme", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find()
+ || Build.MANUFACTURER.contains("Meizu")
+ || Build.MANUFACTURER.contains("MeiZu")) {
+ photoFilePath = DCIM_FILE_PATH + File.separator + "Camera" + File.separator;
+ videoFilePath = DCIM_FILE_PATH + File.separator + "Video" + File.separator;
+ } else if (Build.FINGERPRINT.contains("vivo")
+ || Pattern.compile("vivo", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find()
+ || Build.MANUFACTURER.contains("vivo")
+ || Build.MANUFACTURER.contains("Vivo")) {
+ photoFilePath = videoFilePath = Environment.getExternalStoragePublicDirectory("") + File.separator + "相机" + File.separator;
+ } else {
+ photoFilePath = videoFilePath = DCIM_FILE_PATH + File.separator + "Camera" + File.separator;
+ }
+ exportVideoDir = DCIM_FILE_PATH + File.separator + "FaceUnity" + File.separator;
+ createFileDir(photoFilePath);
+ createFileDir(videoFilePath);
+ createFileDir(exportVideoDir);
+ }
+
+ public static final String IMAGE_FORMAT_JPG = ".jpg";
+ public static final String IMAGE_FORMAT_JPEG = ".jpeg";
+ public static final String IMAGE_FORMAT_PNG = ".png";
+ public static final String VIDEO_FORMAT_MP4 = ".mp4";
+
+
+ /**
+ * 创建文件夹
+ *
+ * @param path
+ */
+ public static void createFileDir(String path) {
+ File dir = new File(path);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ }
+
+ /**
+ * 应用外部文件目录
+ *
+ * @return
+ */
+ public static File getExternalFileDir(Context context) {
+ File fileDir = context.getFilesDir();
+ return fileDir;
+ }
+
+ /**
+ * 应用下缓存文件目录
+ *
+ * @return
+ */
+ public static File getCacheFileDir(Context context) {
+ File fileDir = context.getCacheDir();
+ if (fileDir == null) {
+ fileDir = context.getFilesDir();
+ }
+ return fileDir;
+ }
+
+ /**
+ * 获取当前时间日期
+ *
+ * @return
+ */
+ public static String getDateTimeString() {
+ GregorianCalendar now = new GregorianCalendar();
+ return new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(now.getTime());
+ }
+
+
+ /**
+ * 获取视频缓存文件
+ *
+ * @param context Context
+ * @return File
+ */
+ public static File getCacheVideoFile(Context context) {
+ File fileDir = new File(getExternalFileDir(context).getPath() + File.separator + "video");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, getCurrentVideoFileName());
+ if (file.exists()) {
+ file.delete();
+ }
+ return file;
+ }
+
+ /**
+ * 构造视频文件名称
+ *
+ * @return
+ */
+ public static String getCurrentVideoFileName() {
+ return getDateTimeString() + VIDEO_FORMAT_MP4;
+ }
+
+
+ /**
+ * 构造图片文件名称
+ *
+ * @return
+ */
+ public static String getCurrentPhotoFileName() {
+ return getDateTimeString() + IMAGE_FORMAT_JPG;
+ }
+
+
+ /**
+ * Bitmap保存到本地
+ *
+ * @param context Context
+ * @param bitmap Bitmap
+ * @return String?
+ */
+ public static String addBitmapToExternal(Context context, Bitmap bitmap) {
+ if (bitmap == null) return null;
+ File fileDir = new File(getExternalFileDir(context).getPath() + File.separator + "photo");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, getCurrentPhotoFileName());
+ if (file.exists()) {
+ file.delete();
+ }
+ try {
+ FileOutputStream fos = new FileOutputStream(file);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
+ fos.flush();
+ fos.close();
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 将Bitmap文件保存到相册
+ *
+ * @param bitmap Bitmap
+ */
+ public static String addBitmapToAlbum(Context context, Bitmap bitmap) {
+ if (bitmap == null) return null;
+ FileOutputStream fos = null;
+ File dcimFile;
+
+ File fileDir = new File(exportVideoDir);
+ if (fileDir.exists()) {
+ dcimFile = new File(exportVideoDir, getCurrentPhotoFileName());
+ } else {
+ dcimFile = new File(photoFilePath, getCurrentPhotoFileName());
+ }
+ if (dcimFile.exists()) {
+ dcimFile.delete();
+ }
+ try {
+ fos = new FileOutputStream(dcimFile);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
+ fos.flush();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(dcimFile)));
+ return dcimFile.getAbsolutePath();
+ }
+
+
+ /**
+ * 将视频文件保存到相册
+ *
+ * @param videoFile File
+ * @return Uri?
+ */
+
+ public static String addVideoToAlbum(Context context, File videoFile) {
+ if (videoFile == null) return null;
+ File fileDir = new File(exportVideoDir);
+ File dcimFile;
+ if (fileDir.exists()) {
+ dcimFile = new File(exportVideoDir, getCurrentVideoFileName());
+ } else {
+ dcimFile = new File(videoFilePath, getCurrentVideoFileName());
+ }
+ if (dcimFile.exists()) {
+ dcimFile.delete();
+ }
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+ try {
+ bis = new BufferedInputStream(new FileInputStream(videoFile));
+ bos = new BufferedOutputStream(new FileOutputStream(dcimFile));
+ byte[] bytes = new byte[1024 * 10];
+ int length;
+ while ((length = bis.read(bytes)) != -1) {
+ bos.write(bytes, 0, length);
+ }
+ bos.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (bis != null) {
+ try {
+ bis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (bos != null) {
+ try {
+ bos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(dcimFile)));
+ return dcimFile.getAbsolutePath();
+ }
+
+
+ /**
+ * 将Assets文件拷贝到应用作用域存储
+ *
+ * @param context Context
+ * @param assetsPath String
+ * @param fileName String
+ */
+ public static String copyAssetsToExternalFilesDir(Context context, String assetsPath, String fileName) {
+ File fileDir = new File(getExternalFileDir(context).getPath() + File.separator + "assets");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ File file = new File(fileDir, fileName);
+ if (file.exists()) {
+ return file.getAbsolutePath();
+ }
+ try {
+ InputStream inputStream = context.getAssets().open(assetsPath);
+ FileOutputStream fos = new FileOutputStream(file);
+ BufferedInputStream bis = new BufferedInputStream(inputStream);
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+ byte[] byteArray = new byte[1024];
+ int bytes = bis.read(byteArray);
+ while (bytes > 0) {
+ bos.write(byteArray, 0, bytes);
+ bos.flush();
+ bytes = bis.read(byteArray);
+ }
+ bos.close();
+ fos.close();
+ return file.getAbsolutePath();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 获取Uri文件绝对路径
+ *
+ * @param context: Context
+ * @param uri Uri
+ * @return String
+ */
+ public static String getFilePathByUri(Context context, Uri uri) {
+ if (uri == null) return null;
+ return Uri2PathUtil.getRealPathFromUri(context, uri);
+ }
+
+ private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
+ Cursor cursor = null;
+ String[] projection = new String[]{MediaStore.Images.Media.DATA};
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ int index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ return cursor.getString(index);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * load本地图片
+ *
+ * @param path
+ * @param screenWidth
+ * @return
+ */
+ public static Bitmap loadBitmapFromExternal(String path, int screenWidth) {
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(path, opt);
+ int picWidth = opt.outWidth;
+ int picHeight = opt.outHeight;
+ opt.inSampleSize = 1;
+ // 根据屏的大小和图片大小计算出缩放比例
+ if (picWidth > picHeight) {
+ if (picHeight > screenWidth) {
+ opt.inSampleSize = picHeight / screenWidth;
+ }
+ } else {
+ if (picWidth > screenWidth) {
+ opt.inSampleSize = picWidth / screenWidth;
+ }
+ }
+ opt.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeFile(path, opt);
+ int orientation = getPhotoOrientation(path);
+ bitmap = rotateBitmap(bitmap, orientation);
+ return bitmap;
+ }
+
+ /**
+ * load本地图片
+ *
+ * @param path String
+ * @param screenWidth Int
+ * @param screenHeight Int
+ * @return Bitmap
+ */
+ public static Bitmap loadBitmapFromExternal(String path, int screenWidth, int screenHeight) {
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(path, opt);
+ int picWidth = opt.outWidth;
+ int picHeight = opt.outHeight;
+ int inSampleSize = 1;
+ // 根据屏的大小和图片大小计算出缩放比例
+ if (picHeight > screenHeight || picWidth > screenWidth) {
+ int halfHeight = picHeight / 2;
+ int halfWidth = picWidth / 2;
+ while (halfHeight / inSampleSize >= screenHeight && halfWidth / inSampleSize >= screenWidth) {
+ inSampleSize *= 2;
+ }
+ }
+ opt.inSampleSize = inSampleSize;
+ opt.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeFile(path, opt);
+ int orientation = getPhotoOrientation(path);
+ bitmap = rotateBitmap(bitmap, orientation);
+ return bitmap;
+ }
+
+
+ /**
+ * 旋转 Bitmap
+ *
+ * @param bitmap
+ * @param orientation
+ * @return
+ */
+ private static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
+ if (orientation == 90 || orientation == 180 || orientation == 270) {
+ Matrix matrix = new Matrix();
+ matrix.postRotate((float) orientation);
+ bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+ }
+ return bitmap;
+ }
+
+
+ /**
+ * 获取图片的方向
+ *
+ * @param path
+ * @return
+ */
+ public static int getPhotoOrientation(String path) {
+ int orientation = 0;
+ int tagOrientation = 0;
+ try {
+ tagOrientation = new ExifInterface(path).getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ switch (tagOrientation) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ orientation = 90;
+ break;
+
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ orientation = 180;
+ break;
+
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ orientation = 270;
+ break;
+ }
+ return orientation;
+ }
+
+
+ /**
+ * 选中图片
+ *
+ * @param activity Activity
+ */
+ public static void pickImageFile(Activity activity, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("image/*");
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * 选中视频
+ *
+ * @param activity Activity
+ * 回调可参考下方
+ */
+ public static void pickVideoFile(Activity activity, int requestCode) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("video/*");
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * 根据路径获取InputStream
+ *
+ * @param context Context
+ * @param path String
+ * @return InputStream?
+ */
+ public static InputStream readInputStreamByPath(Context context, String path) {
+ if (path == null || path.trim().length() == 0)
+ return null;
+ InputStream inputStream = null;
+ try {
+ inputStream = context.getAssets().open(path);
+ } catch (IOException e1) {
+ try {
+ inputStream = new FileInputStream(path);
+ } catch (IOException e2) {
+ e2.printStackTrace();
+ }
+ }
+ return inputStream;
+ }
+
+ /**
+ * 相机byte转bitmap
+ *
+ * @param buffer
+ * @param width
+ * @param height
+ * @return
+ */
+ public static Bitmap bytes2Bitmap(byte[] buffer, int width, int height) {
+ YuvImage yuvimage = new YuvImage(buffer, ImageFormat.NV21, width, height, null);//20、20分别是图的宽度与高度
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos);//80--JPG图片的质量[0-100],100最高
+ byte[] jdata = baos.toByteArray();
+ return BitmapFactory.decodeByteArray(baos.toByteArray(), 0, jdata.length);
+ }
+
+ /**
+ * 遍历一个文件夹获取改文件夹下所有文件名
+ * @param path
+ * @return
+ */
+ public static ArrayList getFileList(String path) {
+ ArrayList fileList = new ArrayList<>();
+ File dir = new File(path);
+ // 该文件目录下文件全部放入数组
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ // 判断是文件还是文件夹
+ if (files[i].isDirectory()) {
+ // 获取文件绝对路径
+ getFileList(files[i].getAbsolutePath());
+ // 判断文件名是否以.jpg结尾
+ } else {
+ fileList.add(files[i].getName());
+ }
+ }
+ }
+ return fileList;
+ }
+
+ /**
+ * 校验文件是否是图片
+ *
+ * @param path String
+ * @return Boolean
+ */
+ public static Boolean checkIsImage(String path) {
+ String name = new File(path).getName().toLowerCase();
+ return (name.endsWith(IMAGE_FORMAT_PNG) || name.endsWith(IMAGE_FORMAT_JPG)
+ || name.endsWith(IMAGE_FORMAT_JPEG));
+ }
+
+ /**
+ * 校验文件是否是视频
+ *
+ * @param path String
+ * @return Boolean
+ */
+ public static Boolean checkIsVideo(Context context,String path) {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ try {
+ retriever.setDataSource(context, Uri.fromFile(new File(path)));
+ String hasVideo = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+ return "yes".equals(hasVideo);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FuDeviceUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FuDeviceUtils.java
new file mode 100644
index 000000000..79a58785e
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/FuDeviceUtils.java
@@ -0,0 +1,720 @@
+package com.yunbao.faceunity.utils;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.opengl.GLES20;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.faceunity.core.faceunity.OffLineRenderHandler;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 工具类
+ */
+public class FuDeviceUtils {
+
+ public static final String TAG = "FuDeviceUtils";
+ public static final String DEVICE_LEVEL = "device_level";
+
+ public static final int DEVICE_LEVEL_HIGH = 2;
+ public static final int DEVICE_LEVEL_MID = 1;
+ public static final int DEVICE_LEVEL_LOW = 0;
+
+ /**
+ * The default return value of any method in this class when an
+ * error occurs or when processing fails (Currently set to -1). Use this to check if
+ * the information about the device in question was successfully obtained.
+ */
+ public static final int DEVICEINFO_UNKNOWN = -1;
+
+ private static final FileFilter CPU_FILTER = new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ String path = pathname.getName();
+ //regex is slow, so checking char by char.
+ if (path.startsWith("cpu")) {
+ for (int i = 3; i < path.length(); i++) {
+ if (!Character.isDigit(path.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+
+
+ /**
+ * Calculates the total RAM of the device through Android API or /proc/meminfo.
+ *
+ * @param c - Context object for current running activity.
+ * @return Total RAM that the device has, or DEVICEINFO_UNKNOWN = -1 in the event of an error.
+ */
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ public static long getTotalMemory(Context c) {
+ // memInfo.totalMem not supported in pre-Jelly Bean APIs.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
+ ActivityManager am = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
+ am.getMemoryInfo(memInfo);
+ if (memInfo != null) {
+ return memInfo.totalMem;
+ } else {
+ return DEVICEINFO_UNKNOWN;
+ }
+ } else {
+ long totalMem = DEVICEINFO_UNKNOWN;
+ try {
+ FileInputStream stream = new FileInputStream("/proc/meminfo");
+ try {
+ totalMem = parseFileForValue("MemTotal", stream);
+ totalMem *= 1024;
+ } finally {
+ stream.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return totalMem;
+ }
+ }
+
+ /**
+ * Method for reading the clock speed of a CPU core on the device. Will read from either
+ * {@code /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq} or {@code /proc/cpuinfo}.
+ *
+ * @return Clock speed of a core on the device, or -1 in the event of an error.
+ */
+ public static int getCPUMaxFreqKHz() {
+ int maxFreq = DEVICEINFO_UNKNOWN;
+ try {
+ for (int i = 0; i < getNumberOfCPUCores(); i++) {
+ String filename =
+ "/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq";
+ File cpuInfoMaxFreqFile = new File(filename);
+ if (cpuInfoMaxFreqFile.exists() && cpuInfoMaxFreqFile.canRead()) {
+ byte[] buffer = new byte[128];
+ FileInputStream stream = new FileInputStream(cpuInfoMaxFreqFile);
+ try {
+ stream.read(buffer);
+ int endIndex = 0;
+ //Trim the first number out of the byte buffer.
+ while (Character.isDigit(buffer[endIndex]) && endIndex < buffer.length) {
+ endIndex++;
+ }
+ String str = new String(buffer, 0, endIndex);
+ Integer freqBound = Integer.parseInt(str);
+ if (freqBound > maxFreq) {
+ maxFreq = freqBound;
+ }
+ } catch (NumberFormatException e) {
+ //Fall through and use /proc/cpuinfo.
+ } finally {
+ stream.close();
+ }
+ }
+ }
+ if (maxFreq == DEVICEINFO_UNKNOWN) {
+ FileInputStream stream = new FileInputStream("/proc/cpuinfo");
+ try {
+ int freqBound = parseFileForValue("cpu MHz", stream);
+ freqBound *= 1024; //MHz -> kHz
+ if (freqBound > maxFreq) maxFreq = freqBound;
+ } finally {
+ stream.close();
+ }
+ }
+ } catch (IOException e) {
+ maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown.
+ }
+ return maxFreq;
+ }
+
+ /**
+ * Reads the number of CPU cores from the first available information from
+ * {@code /sys/devices/system/cpu/possible}, {@code /sys/devices/system/cpu/present},
+ * then {@code /sys/devices/system/cpu/}.
+ *
+ * @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error.
+ */
+ public static int getNumberOfCPUCores() {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
+ // Gingerbread doesn't support giving a single application access to both cores, but a
+ // handful of devices (Atrix 4G and Droid X2 for example) were released with a dual-core
+ // chipset and Gingerbread; that can let an app in the background run without impacting
+ // the foreground application. But for our purposes, it makes them single core.
+ return 1;
+ }
+ int cores;
+ try {
+ cores = getCoresFromFileInfo("/sys/devices/system/cpu/possible");
+ if (cores == DEVICEINFO_UNKNOWN) {
+ cores = getCoresFromFileInfo("/sys/devices/system/cpu/present");
+ }
+ if (cores == DEVICEINFO_UNKNOWN) {
+ cores = new File("/sys/devices/system/cpu/").listFiles(CPU_FILTER).length;
+ }
+ } catch (SecurityException e) {
+ cores = DEVICEINFO_UNKNOWN;
+ } catch (NullPointerException e) {
+ cores = DEVICEINFO_UNKNOWN;
+ }
+ return cores;
+ }
+
+ /**
+ * Tries to read file contents from the file location to determine the number of cores on device.
+ *
+ * @param fileLocation The location of the file with CPU information
+ * @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error.
+ */
+ private static int getCoresFromFileInfo(String fileLocation) {
+ InputStream is = null;
+ try {
+ is = new FileInputStream(fileLocation);
+ BufferedReader buf = new BufferedReader(new InputStreamReader(is));
+ String fileContents = buf.readLine();
+ buf.close();
+ return getCoresFromFileString(fileContents);
+ } catch (IOException e) {
+ return DEVICEINFO_UNKNOWN;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Do nothing.
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts from a CPU core information format to number of cores.
+ *
+ * @param str The CPU core information string, in the format of "0-N"
+ * @return The number of cores represented by this string
+ */
+ private static int getCoresFromFileString(String str) {
+ if (str == null || !str.matches("0-[\\d]+$")) {
+ return DEVICEINFO_UNKNOWN;
+ }
+ return Integer.valueOf(str.substring(2)) + 1;
+ }
+
+ /**
+ * Helper method for reading values from system files, using a minimised buffer.
+ *
+ * @param textToMatch - Text in the system files to read for.
+ * @param stream - FileInputStream of the system file being read from.
+ * @return A numerical value following textToMatch in specified the system file.
+ * -1 in the event of a failure.
+ */
+ private static int parseFileForValue(String textToMatch, FileInputStream stream) {
+ byte[] buffer = new byte[1024];
+ try {
+ int length = stream.read(buffer);
+ for (int i = 0; i < length; i++) {
+ if (buffer[i] == '\n' || i == 0) {
+ if (buffer[i] == '\n') i++;
+ for (int j = i; j < length; j++) {
+ int textIndex = j - i;
+ //Text doesn't match query at some point.
+ if (buffer[j] != textToMatch.charAt(textIndex)) {
+ break;
+ }
+ //Text matches query here.
+ if (textIndex == textToMatch.length() - 1) {
+ return extractValue(buffer, j);
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ //Ignore any exceptions and fall through to return unknown value.
+ } catch (NumberFormatException e) {
+ }
+ return DEVICEINFO_UNKNOWN;
+ }
+
+ /**
+ * Helper method used by {@link #parseFileForValue(String, FileInputStream) parseFileForValue}. Parses
+ * the next available number after the match in the file being read and returns it as an integer.
+ *
+ * @param index - The index in the buffer array to begin looking.
+ * @return The next number on that line in the buffer, returned as an int. Returns
+ * DEVICEINFO_UNKNOWN = -1 in the event that no more numbers exist on the same line.
+ */
+ private static int extractValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (Character.isDigit(buffer[index])) {
+ int start = index;
+ index++;
+ while (index < buffer.length && Character.isDigit(buffer[index])) {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index - start);
+ return Integer.parseInt(str);
+ }
+ index++;
+ }
+ return DEVICEINFO_UNKNOWN;
+ }
+
+ /**
+ * 获取当前剩余内存(ram)
+ *
+ * @param context
+ * @return
+ */
+ public static long getAvailMemory(Context context) {
+ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
+ am.getMemoryInfo(mi);
+ return mi.availMem;
+ }
+
+ /**
+ * 获取厂商信息
+ *
+ * @return
+ */
+ public static String getBrand() {
+ return Build.BRAND;
+ }
+
+ /**
+ * 获取手机机型
+ *
+ * @return
+ */
+ public static String getModel() {
+ return Build.MODEL;
+ }
+
+ /**
+ * 获取硬件信息(cpu型号)
+ *
+ * @return
+ */
+ public static String getHardWare() {
+ try {
+ FileReader fr = new FileReader("/proc/cpuinfo");
+ BufferedReader br = new BufferedReader(fr);
+ String text;
+ String last = "";
+ while ((text = br.readLine()) != null) {
+ last = text;
+ }
+ //一般机型的cpu型号都会在cpuinfo文件的最后一行
+ if (last.contains("Hardware")) {
+ String[] hardWare = last.split(":\\s+", 2);
+ return hardWare[1];
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return Build.HARDWARE;
+ }
+
+ /**
+ * Level judgement based on current memory and CPU.
+ *
+ * @param context - Context object.
+ * @return
+ */
+ public static int judgeDeviceLevel(Context context) {
+ //加一个sp读取 和 设置
+ int cacheDeviceLevel = getCacheDeviceLevel(context);
+ if (cacheDeviceLevel > -1) {
+ return cacheDeviceLevel;
+ }
+
+ int level;
+ //有一些设备不符合下述的判断规则,则走一个机型判断模式
+ int specialDevice = judgeDeviceLevelInDeviceName();
+ if (specialDevice >= 0) return specialDevice;
+
+ int ramLevel = judgeMemory(context);
+ int cpuLevel = judgeCPU();
+ if (ramLevel == 0 || ramLevel == 1 || cpuLevel == 0) {
+ level = DEVICE_LEVEL_LOW;
+ } else {
+ if (cpuLevel > 1) {
+ level = DEVICE_LEVEL_HIGH;
+ } else {
+ level = DEVICE_LEVEL_MID;
+ }
+ }
+ Log.d(TAG,"DeviceLevel: " + level);
+ saveCacheDeviceLevel(context,level);
+ return level;
+ }
+
+ /**
+ * Level judgement based on current GPU.
+ * 需要GL环境
+ * @return
+ */
+ public static int judgeDeviceLevelGPU(Context context) {
+ int cacheDeviceLevel = getCacheDeviceLevel(context);
+ if (cacheDeviceLevel > -1) {
+ return cacheDeviceLevel;
+ }
+
+ OffLineRenderHandler.getInstance().onResume();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ //加一个sp读取 和 设置
+ final int[] level = {-1};
+ //有一些设备不符合下述的判断规则,则走一个机型判断模式
+ OffLineRenderHandler.getInstance().queueEvent(() -> {
+ try {
+ //高低端名单
+ int specialDevice = judgeDeviceLevelInDeviceName();
+ level[0] = specialDevice;
+ if (specialDevice >= 0) return;
+
+ String glRenderer = GLES20.glGetString(GLES20.GL_RENDERER); //GPU 渲染器
+ String glVendor = GLES20.glGetString(GLES20.GL_VENDOR); //GPU 供应商
+ int GPUVersion;
+ if ("Qualcomm".equals(glVendor)) {
+ //高通
+ if (glRenderer != null && glRenderer.startsWith("Adreno")) {
+ //截取后面的数字
+ String GPUVersionStr = glRenderer.substring(glRenderer.lastIndexOf(" ") + 1);
+ try {
+ GPUVersion = Integer.parseInt(GPUVersionStr);
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ //可能是后面还包含了非数字的东西,那么截取三位
+ String GPUVersionStrNew = GPUVersionStr.substring(0,3);
+ GPUVersion = Integer.parseInt(GPUVersionStrNew);
+ }
+
+ if (GPUVersion >= 512) {
+ level[0] = DEVICE_LEVEL_HIGH;
+ } else {
+ level[0] = DEVICE_LEVEL_MID;
+ }
+ countDownLatch.countDown();
+ }
+ } else if ("ARM".equals(glVendor)) {
+ //ARM
+ if (glRenderer != null && glRenderer.startsWith("Mali")) {
+ //截取-后面的东西
+ String GPUVersionStr = glRenderer.substring(glRenderer.lastIndexOf("-") + 1);
+ String strStart = GPUVersionStr.substring(0, 1);
+ String strEnd = GPUVersionStr.substring(1);
+ try {
+ GPUVersion = Integer.parseInt(strEnd);
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ //可能是后面还包含了非数字的东西,那么截取三位
+ String strEndNew = strEnd.substring(0,2);
+ GPUVersion = Integer.parseInt(strEndNew);
+ }
+
+ if ("G".equals(strStart)) {
+ if (GPUVersion >= 51) {
+ level[0] = DEVICE_LEVEL_HIGH;
+ } else {
+ level[0] = DEVICE_LEVEL_MID;
+ }
+ } else if ("T".equals(strStart)) {
+ if (GPUVersion > 880) {
+ level[0] = DEVICE_LEVEL_HIGH;
+ } else {
+ level[0] = DEVICE_LEVEL_MID;
+ }
+ }
+ countDownLatch.countDown();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ level[0] = -1;
+ countDownLatch.countDown();
+ }
+ });
+
+ try {
+ countDownLatch.await(200,TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ OffLineRenderHandler.getInstance().onPause();
+
+ //存储设备等级
+ saveCacheDeviceLevel(context,level[0]);
+ Log.d(TAG,"DeviceLevel: " + level);
+ return level[0];
+ }
+
+ /**
+ * -1 不是特定的高低端机型
+ * @return
+ */
+ private static int judgeDeviceLevelInDeviceName() {
+ String currentDeviceName = getDeviceName();
+ for (String deviceName:upscaleDevice) {
+ if (deviceName.equals(currentDeviceName)) {
+ return DEVICE_LEVEL_HIGH;
+ }
+ }
+
+ for (String deviceName:middleDevice) {
+ if (deviceName.equals(currentDeviceName)) {
+ return DEVICE_LEVEL_MID;
+ }
+ }
+
+ for (String deviceName:lowDevice) {
+ if (deviceName.equals(currentDeviceName)) {
+ return DEVICE_LEVEL_LOW;
+ }
+ }
+ return -1;
+ }
+
+ public static final String[] upscaleDevice = {"vivo X6S A","MHA-AL00","VKY-AL00","V1838A"};
+ public static final String[] lowDevice = {};
+ public static final String[] middleDevice = {"OPPO R11s","PAR-AL00","MI 8 Lite","ONEPLUS A6000","PRO 6","PRO 7 Plus"};
+
+ /**
+ * 评定内存的等级.
+ *
+ * @return
+ */
+ private static int judgeMemory(Context context) {
+ long ramMB = getTotalMemory(context) / (1024 * 1024);
+ int level = -1;
+ if (ramMB <= 2000) { //2G或以下的最低档
+ level = 0;
+ } else if (ramMB <= 3000) { //2-3G
+ level = 1;
+ } else if (ramMB <= 4000) { //4G档 2018主流中端机
+ level = 2;
+ } else if (ramMB <= 6000) { //6G档 高端机
+ level = 3;
+ } else { //6G以上 旗舰机配置
+ level = 4;
+ }
+ return level;
+ }
+
+ /**
+ * 评定CPU等级.(按频率和厂商型号综合判断)
+ *
+ * @return
+ */
+ private static int judgeCPU() {
+ int level = 0;
+ String cpuName = getHardWare();
+ int freqMHz = getCPUMaxFreqKHz() / 1024;
+
+ //一个不符合下述规律的高级白名单
+ //如果可以获取到CPU型号名称 -> 根据不同的名称走不同判定策略
+ if (!TextUtils.isEmpty(cpuName)) {
+ if (cpuName.contains("qcom") || cpuName.contains("Qualcomm")) { //高通骁龙
+ return judgeQualcommCPU(cpuName, freqMHz);
+ } else if (cpuName.contains("hi") || cpuName.contains("kirin")) { //海思麒麟
+ return judgeSkinCPU(cpuName, freqMHz);
+ } else if (cpuName.contains("MT")) {//联发科
+ return judgeMTCPU(cpuName, freqMHz);
+ }
+ }
+
+ //cpu型号无法获取的普通规则
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 1950) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ return level;
+ }
+
+ /**
+ * 联发科芯片等级判定
+ *
+ * @return
+ */
+ private static int judgeMTCPU(String cpuName, int freqMHz) {
+ //P60之前的全是低端机 MT6771V/C
+ int level = 0;
+ int mtCPUVersion = getMTCPUVersion(cpuName);
+ if (mtCPUVersion == -1) {
+ //读取不出version 按照一个比较严格的方式来筛选出高端机
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2200) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2700) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ } else if (mtCPUVersion < 6771) {
+ //均为中低端机
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else { //2GHz 中端
+ level = 1;
+ }
+ } else {
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 1900) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ }
+
+ return level;
+ }
+
+ /**
+ * 通过联发科CPU型号定义 -> 获取cpu version
+ *
+ * @param cpuName
+ * @return
+ */
+ private static int getMTCPUVersion(String cpuName) {
+ //截取MT后面的四位数字
+ int cpuVersion = -1;
+ if (cpuName.length() > 5) {
+ String cpuVersionStr = cpuName.substring(2, 6);
+ try {
+ cpuVersion = Integer.valueOf(cpuVersionStr);
+ } catch (NumberFormatException exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ return cpuVersion;
+ }
+
+ /**
+ * 高通骁龙芯片等级判定
+ *
+ * @return
+ */
+ private static int judgeQualcommCPU(String cpuName, int freqMHz) {
+ int level = 0;
+ //xxxx inc MSM8937 比较老的芯片
+ //7 8 xxx inc SDM710
+ if (cpuName.contains("MSM")) {
+ //老芯片
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else { //2GHz 低中端
+ level = 1;
+ }
+ } else {
+ //新的芯片
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2000) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ }
+
+ return level;
+ }
+
+ /**
+ * 麒麟芯片等级判定
+ *
+ * @param freqMHz
+ * @return
+ */
+ private static int judgeSkinCPU(String cpuName, int freqMHz) {
+ //型号 -> kirin710之后 & 最高核心频率
+ int level = 0;
+ if (cpuName.startsWith("hi")) {
+ //这个是海思的芯片中低端
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2000) { //2GHz 低中端
+ level = 1;
+ }
+ } else {
+ //这个是海思麒麟的芯片
+ if (freqMHz <= 1600) { //1.5G 低端
+ level = 0;
+ } else if (freqMHz <= 2000) { //2GHz 低中端
+ level = 1;
+ } else if (freqMHz <= 2500) { //2.2 2.3g 中高端
+ level = 2;
+ } else { //高端
+ level = 3;
+ }
+ }
+
+ return level;
+ }
+
+ public static final String Nexus_6P = "Nexus 6P";
+
+ /**
+ * 获取设备名
+ *
+ * @return
+ */
+ public static String getDeviceName() {
+ String deviceName = "";
+ if (Build.MODEL != null) deviceName = Build.MODEL;
+ Log.d(TAG,"deviceName: " + deviceName);
+ return deviceName;
+ }
+
+ /**
+ * 缓存设备等级
+ *
+ * @param level
+ */
+ public static void saveCacheDeviceLevel(Context context,int level) {
+ SharedPreferences sp = context.getSharedPreferences(DEVICE_LEVEL, MODE_PRIVATE);
+ sp.edit().putInt(DEVICE_LEVEL, level).apply();
+ }
+
+ /**
+ * 获取设备等级
+ *
+ * @return
+ */
+ public static int getCacheDeviceLevel(Context context) {
+ SharedPreferences sp = context.getSharedPreferences(DEVICE_LEVEL, MODE_PRIVATE);
+ return sp.getInt(DEVICE_LEVEL, -1);
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/IFURenderer.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/IFURenderer.java
new file mode 100644
index 000000000..e6be37267
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/IFURenderer.java
@@ -0,0 +1,249 @@
+package com.yunbao.faceunity.utils;
+
+
+import android.content.Context;
+
+import com.faceunity.core.enumeration.CameraFacingEnum;
+import com.faceunity.core.enumeration.FUAIProcessorEnum;
+import com.faceunity.core.enumeration.FUExternalInputEnum;
+import com.faceunity.core.enumeration.FUInputBufferEnum;
+import com.faceunity.core.enumeration.FUInputTextureEnum;
+import com.faceunity.core.enumeration.FUTransformMatrixEnum;
+import com.yunbao.faceunity.listener.FURendererListener;
+
+/**
+ * DESC:
+ * Created on 2021/4/26
+ */
+abstract class IFURenderer {
+
+ /**
+ * 渲染属性
+ */
+ protected FUExternalInputEnum externalInputType = FUExternalInputEnum.EXTERNAL_INPUT_TYPE_CAMERA;//数据源类型
+ protected FUInputTextureEnum inputTextureType = FUInputTextureEnum.FU_ADM_FLAG_EXTERNAL_OES_TEXTURE;//纹理类型
+ protected FUInputBufferEnum inputBufferType = FUInputBufferEnum.FU_FORMAT_NV21_BUFFER;//数据类型
+ protected int inputOrientation = 270;//数据源朝向
+ protected int deviceOrientation = 90;//手机设备朝向
+ protected CameraFacingEnum cameraFacing = CameraFacingEnum.CAMERA_FRONT; //数据源为相机时候->前后置相机
+ protected FUTransformMatrixEnum inputTextureMatrix = FUTransformMatrixEnum.CCROT0_FLIPVERTICAL;//纹理旋转类型
+ protected FUTransformMatrixEnum inputBufferMatrix = FUTransformMatrixEnum.CCROT0_FLIPVERTICAL;//图象旋转类型
+ protected FUTransformMatrixEnum outputMatrix = FUTransformMatrixEnum.CCROT0;//图象旋转类型
+
+
+ /**
+ * 初始化鉴权
+ */
+ public abstract void setup(Context context);
+
+
+ /**
+ * 双输入接口,输入 buffer 和 texture,必须在具有 GL 环境的线程调用
+ * 由于省去数据拷贝,性能相对最优,优先推荐使用。
+ * 缺点是无法保证 buffer 和纹理对齐,可能出现点位和效果对不上的情况。
+ *
+ * @param img NV21 buffer
+ * @param texId 纹理 ID
+ * @param width 宽
+ * @param height 高
+ * @return
+ */
+ public abstract int onDrawFrameDualInput(byte[] img, int texId, int width, int height);
+
+ /**
+ * 类似 GLSurfaceView 的 queueEvent 机制,把任务抛到 GL 线程执行。
+ *
+ * @param runnable
+ */
+ public abstract void queueEvent(Runnable runnable);
+
+
+ /**
+ * 设置检测类型
+ *
+ * @param type
+ */
+ public abstract void setAIProcessTrackType(FUAIProcessorEnum type);
+
+ /**
+ * 资源释放
+ */
+ public abstract void release();
+
+ /**
+ * 开启合成状态
+ */
+ public abstract void prepareRenderer(FURendererListener mFURendererListener);
+
+
+ /**
+ * 设置FPS检测
+ *
+ * @param enable
+ */
+ public abstract void setMarkFPSEnable(boolean enable);
+
+
+
+ /**
+ * 获取输入源类型
+ *
+ * @return
+ */
+ public FUExternalInputEnum getExternalInputType() {
+ return externalInputType;
+ }
+
+ /**
+ * 设置输入源类型
+ *
+ * @param externalInputType
+ */
+ public void setExternalInputType(FUExternalInputEnum externalInputType) {
+ this.externalInputType = externalInputType;
+ }
+
+ /**
+ * 获取输入纹理类型
+ *
+ * @return
+ */
+ public FUInputTextureEnum getInputTextureType() {
+ return inputTextureType;
+ }
+
+ /**
+ * 设置输入纹理类型
+ *
+ * @param inputTextureType
+ */
+ public void setInputTextureType(FUInputTextureEnum inputTextureType) {
+ this.inputTextureType = inputTextureType;
+ }
+
+ /**
+ * 获取输入Buffer类型
+ *
+ * @return
+ */
+ public FUInputBufferEnum getInputBufferType() {
+ return inputBufferType;
+ }
+
+ /**
+ * 设置输入Buffer类型
+ *
+ * @param inputBufferType
+ */
+ public void setInputBufferType(FUInputBufferEnum inputBufferType) {
+ this.inputBufferType = inputBufferType;
+ }
+
+ /**
+ * 获取输入数据朝向
+ *
+ * @return
+ */
+ public int getInputOrientation() {
+ return inputOrientation;
+ }
+
+ /**
+ * 设置输入数据朝向
+ *
+ * @param inputOrientation
+ */
+ public void setInputOrientation(int inputOrientation) {
+ this.inputOrientation = inputOrientation;
+ }
+
+ /**
+ * 获取设备类型
+ *
+ * @return
+ */
+ public int getDeviceOrientation() {
+ return deviceOrientation;
+ }
+
+ /**
+ * 设置设备类型
+ *
+ * @param deviceOrientation
+ */
+ public void setDeviceOrientation(int deviceOrientation) {
+ this.deviceOrientation = deviceOrientation;
+ }
+
+ /**
+ * 获取设备朝向
+ *
+ * @return
+ */
+ public CameraFacingEnum getCameraFacing() {
+ return cameraFacing;
+ }
+
+ /**
+ * 设置设备朝向
+ *
+ * @param cameraFacing
+ */
+ public void setCameraFacing(CameraFacingEnum cameraFacing) {
+ this.cameraFacing = cameraFacing;
+ }
+
+ /**
+ * 获取输入纹理旋转类型
+ *
+ * @return
+ */
+ public FUTransformMatrixEnum getInputTextureMatrix() {
+ return inputTextureMatrix;
+ }
+
+ /**
+ * 设置输入纹理旋转类型
+ *
+ * @param inputTextureMatrix
+ */
+ public void setInputTextureMatrix(FUTransformMatrixEnum inputTextureMatrix) {
+ this.inputTextureMatrix = inputTextureMatrix;
+ }
+
+ /**
+ * 获取输入数据旋转类型
+ *
+ * @return
+ */
+ public FUTransformMatrixEnum getInputBufferMatrix() {
+ return inputBufferMatrix;
+ }
+
+ /**
+ * 设置输入数据旋转类型
+ *
+ * @param inputBufferMatrix
+ */
+ public void setInputBufferMatrix(FUTransformMatrixEnum inputBufferMatrix) {
+ this.inputBufferMatrix = inputBufferMatrix;
+ }
+
+ /**
+ * 获取输出数据旋转类型
+ *
+ * @return
+ */
+ public FUTransformMatrixEnum getOutputMatrix() {
+ return outputMatrix;
+ }
+
+ /**
+ * 设置输出数据旋转类型
+ *
+ * @param outputMatrix
+ */
+ public void setOutputMatrix(FUTransformMatrixEnum outputMatrix) {
+ this.outputMatrix = outputMatrix;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/MemoryInfoUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/MemoryInfoUtil.java
new file mode 100644
index 000000000..6b5f95c3f
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/MemoryInfoUtil.java
@@ -0,0 +1,90 @@
+package com.yunbao.faceunity.utils;
+
+import android.os.Build;
+import android.os.Debug;
+
+import androidx.annotation.RequiresApi;
+
+import java.lang.reflect.Field;
+
+/**
+ * 内存使用率获取工具类
+ * Created by tujh on 2018/5/24.
+ */
+public abstract class MemoryInfoUtil {
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static double getMemory(Debug.MemoryInfo[] memoryInfos) {
+ try {
+ if (Build.VERSION.SDK_INT >= 27) {
+ return compute(memoryInfos);
+ }
+
+ Field otherStats_Field = memoryInfos[0].getClass().getDeclaredField("otherStats");
+ otherStats_Field.setAccessible(true);
+ int[] otherStats = (int[]) otherStats_Field.get(memoryInfos[0]);
+
+ Field NUM_CATEGORIES_Field = memoryInfos[0].getClass().getDeclaredField("NUM_CATEGORIES");
+ Field offsetPrivateClean_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateClean");
+ Field offsetPrivateDirty_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateDirty");
+ final int NUM_CATEGORIES = (int) NUM_CATEGORIES_Field.get(memoryInfos[0]);
+ final int offsetPrivateClean = (int) offsetPrivateClean_Field.get(memoryInfos[0]);
+ final int offsetPrivateDirty = (int) offsetPrivateDirty_Field.get(memoryInfos[0]);
+
+ int javaHeap = memoryInfos[0].dalvikPrivateDirty
+ + otherStats[12 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[12 * NUM_CATEGORIES + offsetPrivateDirty];
+ int nativeHeap = memoryInfos[0].nativePrivateDirty;
+ int code = otherStats[6 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[6 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[7 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[7 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[8 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[8 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[9 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[9 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[10 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[10 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[11 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[11 * NUM_CATEGORIES + offsetPrivateDirty];
+ int stack = otherStats[NUM_CATEGORIES + offsetPrivateDirty];
+ int graphics = otherStats[4 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[4 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[14 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[14 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[15 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[15 * NUM_CATEGORIES + offsetPrivateDirty];
+ int other = otherStats[0 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[0 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[2 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[2 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[3 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[3 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[5 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[5 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[13 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[13 * NUM_CATEGORIES + offsetPrivateDirty]
+ + otherStats[16 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[16 * NUM_CATEGORIES + offsetPrivateDirty];
+ int all = javaHeap + nativeHeap + code + stack + graphics + other;
+// Log.d("MemoryAll", "javaHeap=" + javaHeap
+// + "\nnativeHeap=" + nativeHeap + "\ncode=" + code + "\nstack=" + stack
+// + "\ngraphics=" + graphics + "\nother=" + other);
+// Log.e(TAG, "memory " + memoryStr
+// + " java-heap " + String.format("%.2f", ((double) javaHeap) / 1024)
+// + " native-heap " + String.format("%.2f", ((double) nativeHeap) / 1024)
+// + " code " + String.format("%.2f", ((double) code) / 1024)
+// + " stack " + String.format("%.2f", ((double) stack) / 1024)
+// + " graphics " + String.format("%.2f", ((double) graphics) / 1024)
+// + " other " + String.format("%.2f", ((double) other) / 1024)
+// + " pps " + String.format("%.2f", ((double) memoryInfos[0].getTotalPss()) / 1024)
+// );
+ return ((double) all) / 1024;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private static double compute(Debug.MemoryInfo[] memInfo) {
+ String java_mem = memInfo[0].getMemoryStat("summary.java-heap");
+ String native_mem = memInfo[0].getMemoryStat("summary.native-heap");
+ String graphics_mem = memInfo[0].getMemoryStat("summary.graphics");
+ String stack_mem = memInfo[0].getMemoryStat("summary.stack");
+ String code_mem = memInfo[0].getMemoryStat("summary.code");
+ String others_mem = memInfo[0].getMemoryStat("summary.system");
+ int all = Integer.parseInt(java_mem)
+ + Integer.parseInt(native_mem)
+ + Integer.parseInt(graphics_mem)
+ + Integer.parseInt(stack_mem)
+ + Integer.parseInt(code_mem)
+ + Integer.parseInt(others_mem);
+ return ((double) all) / 1024;
+ }
+}
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/SeekBarUtils.kt b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/SeekBarUtils.kt
new file mode 100644
index 000000000..cadecf261
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/SeekBarUtils.kt
@@ -0,0 +1,37 @@
+package com.yunbao.faceunity.utils
+
+import android.view.View
+import com.yunbao.faceunity.seekbar.DiscreteSeekBar
+
+class SeekBarUtils {
+ companion object {
+ /**
+ * 设置滚动条数值
+ * @param value Double 结果值
+ * @param stand Double 标准值
+ * @param range Double 范围区间
+ */
+ fun seekToSeekBar(bar: DiscreteSeekBar, value: Double, stand: Double, range: Double) {
+ if (stand == 0.5) {
+ bar.min = -50
+ bar.max = 50
+ bar.progress = (value * 100 / range - 50).toInt()
+ } else {
+ bar.min = 0
+ bar.max = 100
+ bar.progress = (value * 100 / range).toInt()
+ }
+ bar.visibility = View.VISIBLE
+ }
+
+ /**
+ * 转换成滚动条的值
+ */
+ fun seekToValue(range:Double,value:Int,seekBarMin:Int):Double{
+ val valueF: Double = 1.0 * (value - seekBarMin) / 100
+ return range*valueF;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Uri2PathUtil.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Uri2PathUtil.java
new file mode 100644
index 000000000..01af25574
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/Uri2PathUtil.java
@@ -0,0 +1,161 @@
+package com.yunbao.faceunity.utils;
+
+import android.annotation.TargetApi;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+import androidx.loader.content.CursorLoader;
+
+public class Uri2PathUtil {
+
+ //复杂版处理 (适配多种API)
+ public static String getRealPathFromUri(Context context, Uri uri) {
+ int sdkVersion = Build.VERSION.SDK_INT;
+ if (sdkVersion < 11) return getRealPathFromUri_BelowApi11(context, uri);
+ if (sdkVersion < 19) return getRealPathFromUri_Api11To18(context, uri);
+ else return getRealPathFromUri_AboveApi19(context, uri);
+ }
+
+ /**
+ * 适配api19以上,根据uri获取图片的绝对路径
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) {
+ if (DocumentsContract.isDocumentUri(context, uri)) {
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+ } else if (isDownloadsDocument(uri)) {
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ } else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ } else {
+ contentUri = MediaStore.Files.getContentUri("external");
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[]{split[1]};
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+
+
+ } else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ return getDataColumn(context, uri, null, null);
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * 适配api11-api18,根据uri获取图片的绝对路径
+ */
+ private static String getRealPathFromUri_Api11To18(Context context, Uri uri) {
+ String filePath = null;
+ String[] projection = {MediaStore.Images.Media.DATA};
+ //这个有两个包不知道是哪个。。。。不过这个复杂版一般用不到
+ CursorLoader loader = new CursorLoader(context, uri, projection, null, null, null);
+ Cursor cursor = loader.loadInBackground();
+
+ if (cursor != null) {
+ cursor.moveToFirst();
+ filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+ cursor.close();
+ }
+ return filePath;
+ }
+
+ /**
+ * 适配api11以下(不包括api11),根据uri获取图片的绝对路径
+ */
+ private static String getRealPathFromUri_BelowApi11(Context context, Uri uri) {
+ String filePath = null;
+ String[] projection = {MediaStore.Images.Media.DATA};
+ Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ filePath = cursor.getString(cursor.getColumnIndex(projection[0]));
+ cursor.close();
+ }
+ return filePath;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+ Cursor cursor = null;
+ String column = MediaStore.MediaColumns.DATA;
+ String[] projection = {column};
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+}
\ No newline at end of file
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/ZipUtils.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/ZipUtils.java
new file mode 100644
index 000000000..22cbfa3af
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/ZipUtils.java
@@ -0,0 +1,251 @@
+package com.yunbao.faceunity.utils;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author: lijuan
+ * @description: 解压ZIP文件
+ * @date: 2017-04-11
+ * @time: 09:22
+ */
+public class ZipUtils {
+ public static final String TAG = "ZIP";
+
+ /**
+ * 解压zip到指定的路径
+ *
+ * @param zipFileString ZIP的名称
+ * @param outPathString 要解压缩路径
+ * @throws Exception
+ */
+ public static void unZipFolder(String zipFileString, String outPathString) throws Exception {
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ String szName = "";
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ //获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(outPathString + File.separator + szName);
+ folder.mkdirs();
+ } else {
+ Log.i(TAG, outPathString + File.separator + szName);
+ File file = new File(outPathString + File.separator + szName);
+ if (!file.exists()) {
+ Log.i(TAG, "Create the file:" + outPathString + File.separator + szName);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ // 获取文件的输出流
+ FileOutputStream out = new FileOutputStream(file);
+ int len;
+ byte[] buffer = new byte[1024];
+ // 读取(字节)字节到缓冲区
+ while ((len = inZip.read(buffer)) != -1) {
+ // 从缓冲区(0)位置写入(字节)字节
+ out.write(buffer, 0, len);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+ inZip.close();
+ }
+
+ /**
+ * 解压zip到指定的路径
+ *
+ * @param zipFileString ZIP的名称
+ * @param outPathString 要解压缩路径
+ * @throws Exception
+ */
+ public static ArrayList unZipFolderWithFileName(String zipFileString, String outPathString) throws Exception {
+ //记录所有文件名
+ ArrayList list = new ArrayList<>();
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ String szName = "";
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ //获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(outPathString + File.separator + szName);
+ folder.mkdirs();
+ } else {
+ Log.i(TAG, outPathString + File.separator + szName);
+ list.add(szName);
+ File file = new File(outPathString + File.separator + szName);
+ if (!file.exists()) {
+ Log.i(TAG, "Create the file:" + outPathString + File.separator + szName);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ // 获取文件的输出流
+ FileOutputStream out = new FileOutputStream(file);
+ int len;
+ byte[] buffer = new byte[1024];
+ // 读取(字节)字节到缓冲区
+ while ((len = inZip.read(buffer)) != -1) {
+ // 从缓冲区(0)位置写入(字节)字节
+ out.write(buffer, 0, len);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+ inZip.close();
+ return list;
+ }
+
+ public static void unZipFolder(String zipFileString, String outPathString, String szName) throws Exception {
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ //szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ //获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(outPathString + File.separator + szName);
+ folder.mkdirs();
+ } else {
+ Log.i(TAG, outPathString + File.separator + szName);
+ File file = new File(outPathString + File.separator + szName);
+ if (!file.exists()) {
+ Log.i(TAG, "Create the file:" + outPathString + File.separator + szName);
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ // 获取文件的输出流
+ FileOutputStream out = new FileOutputStream(file);
+ int len;
+ byte[] buffer = new byte[1024];
+ // 读取(字节)字节到缓冲区
+ while ((len = inZip.read(buffer)) != -1) {
+ // 从缓冲区(0)位置写入(字节)字节
+ out.write(buffer, 0, len);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+ inZip.close();
+ }
+
+ /**
+ * 压缩文件和文件夹
+ *
+ * @param srcFileString 要压缩的文件或文件夹
+ * @param zipFileString 解压完成的Zip路径
+ * @throws Exception
+ */
+ public static void zipFolder(String srcFileString, String zipFileString) throws Exception {
+ //创建ZIP
+ ZipOutputStream outZip = new ZipOutputStream(new FileOutputStream(zipFileString));
+ //创建文件
+ File file = new File(srcFileString);
+ //压缩
+ zipFiles(file.getParent() + File.separator, file.getName(), outZip);
+ //完成和关闭
+ outZip.finish();
+ outZip.close();
+ }
+
+ /**
+ * 压缩文件
+ *
+ * @param folderString
+ * @param fileString
+ * @param zipOutputSteam
+ * @throws Exception
+ */
+ private static void zipFiles(String folderString, String fileString, ZipOutputStream zipOutputSteam) throws Exception {
+ if (zipOutputSteam == null)
+ return;
+ File file = new File(folderString + fileString);
+ if (file.isFile()) {
+ ZipEntry zipEntry = new ZipEntry(fileString);
+ FileInputStream inputStream = new FileInputStream(file);
+ zipOutputSteam.putNextEntry(zipEntry);
+ int len;
+ byte[] buffer = new byte[4096];
+ while ((len = inputStream.read(buffer)) != -1) {
+ zipOutputSteam.write(buffer, 0, len);
+ }
+ zipOutputSteam.closeEntry();
+ } else {
+ //文件夹
+ String fileList[] = file.list();
+ //没有子文件和压缩
+ if (fileList.length <= 0) {
+ ZipEntry zipEntry = new ZipEntry(fileString + File.separator);
+ zipOutputSteam.putNextEntry(zipEntry);
+ zipOutputSteam.closeEntry();
+ }
+ //子文件和递归
+ for (int i = 0; i < fileList.length; i++) {
+ zipFiles(folderString, fileString + File.separator + fileList[i], zipOutputSteam);
+ }
+ }
+ }
+
+ /**
+ * 返回zip的文件输入流
+ *
+ * @param zipFileString zip的名称
+ * @param fileString ZIP的文件名
+ * @return InputStream
+ * @throws Exception
+ */
+ public static InputStream upZip(String zipFileString, String fileString) throws Exception {
+ ZipFile zipFile = new ZipFile(zipFileString);
+ ZipEntry zipEntry = zipFile.getEntry(fileString);
+ return zipFile.getInputStream(zipEntry);
+ }
+
+ /**
+ * 返回ZIP中的文件列表(文件和文件夹)
+ *
+ * @param zipFileString ZIP的名称
+ * @param bContainFolder 是否包含文件夹
+ * @param bContainFile 是否包含文件
+ * @return
+ * @throws Exception
+ */
+ public static List getFileList(String zipFileString, boolean bContainFolder, boolean bContainFile) throws Exception {
+ List fileList = new ArrayList();
+ ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFileString));
+ ZipEntry zipEntry;
+ String szName = "";
+ while ((zipEntry = inZip.getNextEntry()) != null) {
+ szName = zipEntry.getName();
+ if (zipEntry.isDirectory()) {
+ // 获取部件的文件夹名
+ szName = szName.substring(0, szName.length() - 1);
+ File folder = new File(szName);
+ if (bContainFolder) {
+ fileList.add(folder);
+ }
+ } else {
+ File file = new File(szName);
+ if (bContainFile) {
+ fileList.add(file);
+ }
+ }
+ }
+ inZip.close();
+ return fileList;
+ }
+}
\ No newline at end of file
diff --git a/FaceUnity/src/main/java/com/yunbao/faceunity/utils/net/GsonConverter.java b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/net/GsonConverter.java
new file mode 100644
index 000000000..8a14aab2f
--- /dev/null
+++ b/FaceUnity/src/main/java/com/yunbao/faceunity/utils/net/GsonConverter.java
@@ -0,0 +1,86 @@
+package com.yunbao.faceunity.utils.net;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * GSON 转换器
+ *
+ * @author Richie on 2018.12.22
+ */
+public final class GsonConverter {
+ private final static Gson GSON = new Gson();
+
+ private GsonConverter() {
+ }
+
+ /**
+ * json to bean
+ *
+ * @param json must be JSONObject
+ * @param classOfT
+ * @param
+ * @return
+ */
+ public static T jsonToBean(String json, Class classOfT) {
+ return GSON.fromJson(json, classOfT);
+ }
+
+ /**
+ * json to bean list, without generic erase problem, recommend
+ *
+ * @param json must be JSONArray
+ * @param classOfT
+ * @param
+ * @return
+ */
+ public static List jsonToList(String json, Class classOfT) {
+ JsonArray array = JsonParser.parseString(json).getAsJsonArray();
+ List list = new ArrayList<>(array.size());
+ for (JsonElement elem : array) {
+ T t = GSON.fromJson(elem, classOfT);
+ list.add(t);
+ }
+ return list;
+ }
+
+ /**
+ * json to map
+ *
+ * @param json must be JSONObject
+ * @return
+ */
+ public static Map jsonToMap(String json) {
+ return GSON.fromJson(json, new TypeToken