Gradle dependencies
compile group: 'androidx.constraintlayout', name: 'constraintlayout', version: '2.2.0-beta01'
- groupId: androidx.constraintlayout
- artifactId: constraintlayout
- version: 2.2.0-beta01
Artifact androidx.constraintlayout:constraintlayout:2.2.0-beta01 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.constraintlayout:constraintlayout com.android.support.constraint:constraint-layout
Overview
An ImageView that can display, combine and filter images. Added in 2.0
Subclass of ImageView to handle various common filtering operations
ImageFilterView attributes
altSrc |
Provide and alternative image to the src image to allow cross fading |
saturation |
Sets the saturation of the image. 0 = grayscale, 1 = original, 2 = hyper saturated |
brightness |
Sets the brightness of the image. 0 = black, 1 = original, 2 = twice as bright
|
warmth |
This adjust the apparent color temperature of the image. 1=neutral, 2=warm, .5=cold |
contrast |
This sets the contrast. 1 = unchanged, 0 = gray, 2 = high contrast |
crossfade |
Set the current mix between the two images. 0=src 1= altSrc image |
round |
(id) call the TransitionListener with this trigger id |
roundPercent |
Set the corner radius of curvature as a fraction of the smaller side.
For squares 1 will result in a circle |
overlay |
Defines whether the alt image will be faded in on top of the original image or if it will be
crossfaded with it. Default is true. Set to false for semitransparent objects |
Summary
Methods |
---|
public void | draw(Canvas canvas)
|
public float | getBrightness()
Returns the currently applied brightness |
public float | getContrast()
Returns the currently applied contrast |
public float | getCrossfade()
Returns the currently applied crossfade. |
public float | getImagePanX()
Gets the pan from the center
pan of 1 the image is "all the way to the right"
if the images width is greater than the screen width,
pan = 1 results in the left edge lining up
if the images width is less than the screen width,
pan = 1 results in the right edges lining up
if image width == screen width it does nothing |
public float | getImagePanY()
gets the pan from the center
pan of 1 the image is "all the way to the bottom"
if the images width is greater than the screen height,
pan = 1 results in the bottom edge lining up
if the images width is less than the screen height,
pan = 1 results in the top edges lining up
if image height == screen height it does nothing |
public float | getImageRotate()
gets the rotation |
public float | getImageZoom()
gets the zoom where 1 scales the image just enough to fill the view |
public float | getRound()
Get the corner radius of curvature NaN = RoundPercent in effect. |
public float | getRoundPercent()
Get the fractional corner radius of curvature. |
public float | getSaturation()
Returns the currently applied saturation |
public float | getWarmth()
Returns the currently applied warmth |
public void | layout(int l, int t, int r, int b)
|
public void | setAltImageDrawable(Drawable altDrawable)
Set the alternative Image Drawable used in cross fading. |
public void | setAltImageResource(int resId)
Set the alternative Image resource used in cross fading |
public void | setBrightness(float brightness)
sets the brightness of the image;
0 = black, 1 = original, 2 = twice as bright |
public void | setContrast(float contrast)
This sets the contrast. |
public void | setCrossfade(float crossfade)
Set the current mix between the two images that can be set on this view. |
public void | setImageDrawable(Drawable drawable)
|
public void | setImagePanX(float pan)
sets the pan from the center
pan of 1 the image is "all the way to the right"
if the images width is greater than the screen width,
pan = 1 results in the left edge lining up
if the images width is less than the screen width,
pan = 1 results in the right edges lining up
if image width == screen width it does nothing |
public void | setImagePanY(float pan)
sets the pan from the center
pan of 1 the image is "all the way to the bottom"
if the images width is greater than the screen height,
pan = 1 results in the bottom edge lining up
if the images width is less than the screen height,
pan = 1 results in the top edges lining up
if image height == screen height it does nothing |
public void | setImageResource(int resId)
Sets a drawable as the content of this ImageView. |
public void | setImageRotate(float rotation)
sets the rotation angle of the image in degrees |
public void | setImageZoom(float zoom)
sets the zoom where 1 scales the image just enough to fill the view |
public void | setRound(float round)
Set the corner radius of curvature |
public void | setRoundPercent(float round)
Set the corner radius of curvature as a fraction of the smaller side. |
public void | setSaturation(float saturation)
sets the saturation of the image;
0 = grayscale, 1 = original, 2 = hyper saturated |
public void | setWarmth(float warmth)
This makes the apparent color temperature of the image warmer or colder. |
from AppCompatImageView | drawableStateChanged, getSupportBackgroundTintList, getSupportBackgroundTintMode, getSupportImageTintList, getSupportImageTintMode, hasOverlappingRendering, setBackgroundDrawable, setBackgroundResource, setImageBitmap, setImageLevel, setImageURI, setSupportBackgroundTintList, setSupportBackgroundTintMode, setSupportImageTintList, setSupportImageTintMode |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructors
public
ImageFilterView(Context context)
public
ImageFilterView(Context context, AttributeSet attrs)
public
ImageFilterView(Context context, AttributeSet attrs, int defStyleAttr)
Methods
public float
getImagePanX()
Gets the pan from the center
pan of 1 the image is "all the way to the right"
if the images width is greater than the screen width,
pan = 1 results in the left edge lining up
if the images width is less than the screen width,
pan = 1 results in the right edges lining up
if image width == screen width it does nothing
Returns:
the pan in X. Where 0 is centered = Float. NaN if not set
public float
getImagePanY()
gets the pan from the center
pan of 1 the image is "all the way to the bottom"
if the images width is greater than the screen height,
pan = 1 results in the bottom edge lining up
if the images width is less than the screen height,
pan = 1 results in the top edges lining up
if image height == screen height it does nothing
Returns:
pan in y. Where 0 is centered NaN if not set
public float
getImageZoom()
gets the zoom where 1 scales the image just enough to fill the view
Returns:
the zoom factor
public float
getImageRotate()
gets the rotation
Returns:
the rotation in degrees
public void
setImagePanX(float pan)
sets the pan from the center
pan of 1 the image is "all the way to the right"
if the images width is greater than the screen width,
pan = 1 results in the left edge lining up
if the images width is less than the screen width,
pan = 1 results in the right edges lining up
if image width == screen width it does nothing
Parameters:
pan: sets the pan in X. Where 0 is centered
public void
setImagePanY(float pan)
sets the pan from the center
pan of 1 the image is "all the way to the bottom"
if the images width is greater than the screen height,
pan = 1 results in the bottom edge lining up
if the images width is less than the screen height,
pan = 1 results in the top edges lining up
if image height == screen height it does nothing
Parameters:
pan: sets the pan in X. Where 0 is centered
public void
setImageZoom(float zoom)
sets the zoom where 1 scales the image just enough to fill the view
Parameters:
zoom: the zoom factor
public void
setImageRotate(float rotation)
sets the rotation angle of the image in degrees
Parameters:
rotation: the rotation in degrees
public void
setImageDrawable(Drawable drawable)
public void
setImageResource(int resId)
Sets a drawable as the content of this ImageView.
Allows the use of vector drawables when running on older versions of the platform.
Parameters:
resId: the resource identifier of the drawable
See also: {@link androidx.appcompat.R.attr#srcCompat}
public void
setAltImageResource(int resId)
Set the alternative Image resource used in cross fading
Parameters:
resId: id of drawable
public void
setAltImageDrawable(Drawable altDrawable)
Set the alternative Image Drawable used in cross fading.
Parameters:
altDrawable: of drawable
public void
setSaturation(float saturation)
sets the saturation of the image;
0 = grayscale, 1 = original, 2 = hyper saturated
Parameters:
saturation:
public float
getSaturation()
Returns the currently applied saturation
Returns:
0 = grayscale, 1 = original, 2 = hyper saturated
public void
setContrast(float contrast)
This sets the contrast. 1 = unchanged, 0 = gray, 2 = high contrast
Parameters:
contrast:
public float
getContrast()
Returns the currently applied contrast
Returns:
1 = unchanged, 0 = gray, 2 = high contrast
public void
setWarmth(float warmth)
This makes the apparent color temperature of the image warmer or colder.
Parameters:
warmth: 1 is neutral, 2 is warm, .5 is cold
Returns the currently applied warmth
Returns:
warmth 1 is neutral, 2 is warm, .5 is cold
public void
setCrossfade(float crossfade)
Set the current mix between the two images that can be set on this view.
Parameters:
crossfade: a number from 0 to 1
public float
getCrossfade()
Returns the currently applied crossfade.
Returns:
a number from 0 to 1
public void
setBrightness(float brightness)
sets the brightness of the image;
0 = black, 1 = original, 2 = twice as bright
Parameters:
brightness:
public float
getBrightness()
Returns the currently applied brightness
Returns:
brightness 0 = black, 1 = original, 2 = twice as bright
public void
setRoundPercent(float round)
Set the corner radius of curvature as a fraction of the smaller side.
For squares 1 will result in a circle
Parameters:
round: the radius of curvature as a fraction of the smaller width
public void
setRound(float round)
Set the corner radius of curvature
Parameters:
round: the radius of curvature NaN = default meaning roundPercent in effect
public float
getRoundPercent()
Get the fractional corner radius of curvature.
Returns:
Fractional radius of curvature with respect to smallest size
Get the corner radius of curvature NaN = RoundPercent in effect.
Returns:
Radius of curvature
public void
draw(Canvas canvas)
public void
layout(int l, int t, int r, int b)
Source
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.constraintlayout.utils.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.R;
/**
* An ImageView that can display, combine and filter images. <b>Added in 2.0</b>
* <p>
* Subclass of ImageView to handle various common filtering operations
* </p>
*
* <h2>ImageFilterView attributes</h2>
* <table summary="KeyTrigger attributes">
* <tr>
* <td>altSrc</td>
* <td>Provide and alternative image to the src image to allow cross fading</td>
* </tr>
* <tr>
* <td>saturation</td>
* <td>Sets the saturation of the image.<br> 0 = grayscale, 1 = original, 2 = hyper saturated</td>
* </tr
* <tr>
* <td>brightness</td>
* <td>Sets the brightness of the image.<br> 0 = black, 1 = original, 2 = twice as bright
* </td>
* </tr>
* <tr>
* <td>warmth</td>
* <td>This adjust the apparent color temperature of the image.<br> 1=neutral, 2=warm, .5=cold</td>
* </tr>
* <tr>
* <td>contrast</td>
* <td>This sets the contrast. 1 = unchanged, 0 = gray, 2 = high contrast</td>
* </tr>
* <tr>
* <td>crossfade</td>
* <td>Set the current mix between the two images. <br> 0=src 1= altSrc image</td>
* </tr>
* <tr>
* <td>round</td>
* <td>(id) call the TransitionListener with this trigger id</td>
* </tr>
* <tr>
* <td>roundPercent</td>
* <td>Set the corner radius of curvature as a fraction of the smaller side.
* For squares 1 will result in a circle</td>
* </tr>
* <tr>
* <td>overlay</td>
* <td>Defines whether the alt image will be faded in on top of the original image or if it will be
* crossfaded with it. Default is true. Set to false for semitransparent objects</td>
* </tr>
* </table>
*/
public class ImageFilterView extends androidx.appcompat.widget.AppCompatImageView {
static class ImageMatrix {
float[] mMatrix = new float[4 * 5];
ColorMatrix mColorMatrix = new ColorMatrix();
ColorMatrix mTmpColorMatrix = new ColorMatrix();
float mBrightness = 1;
float mSaturation = 1;
float mContrast = 1;
float mWarmth = 1;
private void saturation(float saturationStrength) {
float Rf = 0.2999f;
float Gf = 0.587f;
float Bf = 0.114f;
float s = saturationStrength;
float ms = 1.0f - s;
float Rt = Rf * ms;
float Gt = Gf * ms;
float Bt = Bf * ms;
mMatrix[0] = (Rt + s);
mMatrix[1] = Gt;
mMatrix[2] = Bt;
mMatrix[3] = 0;
mMatrix[4] = 0;
mMatrix[5] = Rt;
mMatrix[6] = (Gt + s);
mMatrix[7] = Bt;
mMatrix[8] = 0;
mMatrix[9] = 0;
mMatrix[10] = Rt;
mMatrix[11] = Gt;
mMatrix[12] = (Bt + s);
mMatrix[13] = 0;
mMatrix[14] = 0;
mMatrix[15] = 0;
mMatrix[16] = 0;
mMatrix[17] = 0;
mMatrix[18] = 1;
mMatrix[19] = 0;
}
private void warmth(float warmth) {
float baseTemperature = 5000;
if (warmth <= 0) warmth = .01f;
float tmpColor_r;
float tmpColor_g;
float tmpColor_b;
float kelvin = baseTemperature / warmth;
{ // simulate a black body radiation
float centiKelvin = kelvin / 100;
float colorR, colorG, colorB;
if (centiKelvin > 66) {
float tmp = centiKelvin - 60.f;
// Original statements (all decimal values)
// colorR = (329.698727446f * (float) Math.pow(tmp, -0.1332047592f))
// colorG = (288.1221695283f * (float) Math.pow(tmp, 0.0755148492f))
colorR = (329.69873f * (float) Math.pow(tmp, -0.13320476f));
colorG = (288.12216f * (float) Math.pow(tmp, 0.07551485f));
} else {
// Original statements (all decimal values)
// colorG = (99.4708025861f * (float) Math.log(centiKelvin) - 161.1195681661f);
colorG = (99.4708f * (float) Math.log(centiKelvin) - 161.11957f);
colorR = 255;
}
if (centiKelvin < 66) {
if (centiKelvin > 19) {
// Original statements (all decimal values)
// 138.5177312231f * (float) Math.log(centiKelvin - 10) - 305.0447927307f);
colorB = (138.51773f
* (float) Math.log(centiKelvin - 10) - 305.0448f);
} else {
colorB = 0;
}
} else {
colorB = 255;
}
tmpColor_r = Math.min(255, Math.max(colorR, 0));
tmpColor_g = Math.min(255, Math.max(colorG, 0));
tmpColor_b = Math.min(255, Math.max(colorB, 0));
}
float color_r = tmpColor_r;
float color_g = tmpColor_g;
float color_b = tmpColor_b;
kelvin = baseTemperature;
{ // simulate a black body radiation
float centiKelvin = kelvin / 100;
float colorR, colorG, colorB;
if (centiKelvin > 66) {
float tmp = centiKelvin - 60.f;
// Original statements (all decimal values)
// colorR = (329.698727446f * (float) Math.pow(tmp, -0.1332047592f));
// colorG = (288.1221695283f * (float) Math.pow(tmp, 0.0755148492f));
colorR = (329.69873f * (float) Math.pow(tmp, -0.13320476f));
colorG = (288.12216f * (float) Math.pow(tmp, 0.07551485f));
} else {
// Original statements (all decimal values)
//float of (99.4708025861f * (float) Math.log(centiKelvin) - 161.1195681661f);
colorG = (99.4708f * (float) Math.log(centiKelvin) - 161.11957f);
colorR = 255;
}
if (centiKelvin < 66) {
if (centiKelvin > 19) {
// Original statements (all decimal values)
//float of (138.5177312231 * Math.log(centiKelvin - 10) - 305.0447927307);
colorB = (138.51773f * (float) Math.log(centiKelvin - 10) - 305.0448f);
} else {
colorB = 0;
}
} else {
colorB = 255;
}
tmpColor_r = Math.min(255, Math.max(colorR, 0));
tmpColor_g = Math.min(255, Math.max(colorG, 0));
tmpColor_b = Math.min(255, Math.max(colorB, 0));
}
color_r /= tmpColor_r;
color_g /= tmpColor_g;
color_b /= tmpColor_b;
mMatrix[0] = color_r;
mMatrix[1] = 0;
mMatrix[2] = 0;
mMatrix[3] = 0;
mMatrix[4] = 0;
mMatrix[5] = 0;
mMatrix[6] = color_g;
mMatrix[7] = 0;
mMatrix[8] = 0;
mMatrix[9] = 0;
mMatrix[10] = 0;
mMatrix[11] = 0;
mMatrix[12] = color_b;
mMatrix[13] = 0;
mMatrix[14] = 0;
mMatrix[15] = 0;
mMatrix[16] = 0;
mMatrix[17] = 0;
mMatrix[18] = 1;
mMatrix[19] = 0;
}
private void brightness(float brightness) {
mMatrix[0] = brightness;
mMatrix[1] = 0;
mMatrix[2] = 0;
mMatrix[3] = 0;
mMatrix[4] = 0;
mMatrix[5] = 0;
mMatrix[6] = brightness;
mMatrix[7] = 0;
mMatrix[8] = 0;
mMatrix[9] = 0;
mMatrix[10] = 0;
mMatrix[11] = 0;
mMatrix[12] = brightness;
mMatrix[13] = 0;
mMatrix[14] = 0;
mMatrix[15] = 0;
mMatrix[16] = 0;
mMatrix[17] = 0;
mMatrix[18] = 1;
mMatrix[19] = 0;
}
void updateMatrix(ImageView view) {
mColorMatrix.reset();
boolean filter = false;
if (mSaturation != 1.0f) {
saturation(mSaturation);
mColorMatrix.set(mMatrix);
filter = true;
}
if (mContrast != 1.0f) {
mTmpColorMatrix.setScale(mContrast, mContrast, mContrast, 1);
mColorMatrix.postConcat(mTmpColorMatrix);
filter = true;
}
if (mWarmth != 1.0f) {
warmth(mWarmth);
mTmpColorMatrix.set(mMatrix);
mColorMatrix.postConcat(mTmpColorMatrix);
filter = true;
}
if (mBrightness != 1.0f) {
brightness(mBrightness);
mTmpColorMatrix.set(mMatrix);
mColorMatrix.postConcat(mTmpColorMatrix);
filter = true;
}
if (filter) {
view.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
} else {
view.clearColorFilter();
}
}
}
private ImageMatrix mImageMatrix = new ImageMatrix();
private boolean mOverlay = true;
private Drawable mAltDrawable = null;
private Drawable mDrawable = null;
private float mCrossfade = 0;
private float mRoundPercent = 0; // rounds the corners as a percent
private float mRound = Float.NaN; // rounds the corners in dp if NaN RoundPercent is in effect
private Path mPath;
ViewOutlineProvider mViewOutlineProvider;
RectF mRect;
Drawable[] mLayers = new Drawable[2];
LayerDrawable mLayer;
// ======================== support for pan/zoom/rotate =================
// defined as 0 = center of screen
// if with < scree with, 1 is the right edge lines up with screen
// if width > screen width, 1 is thee left edge lines up
// -1 works similarly
// zoom 1 = the image fits such that the view is filed
float mPanX = Float.NaN;
float mPanY = Float.NaN;
float mZoom = Float.NaN;
float mRotate = Float.NaN;
/**
* Gets the pan from the center
* pan of 1 the image is "all the way to the right"
* if the images width is greater than the screen width,
* pan = 1 results in the left edge lining up
* if the images width is less than the screen width,
* pan = 1 results in the right edges lining up
* if image width == screen width it does nothing
*
* @return the pan in X. Where 0 is centered = Float. NaN if not set
*/
public float getImagePanX() {
return mPanX;
}
/**
* gets the pan from the center
* pan of 1 the image is "all the way to the bottom"
* if the images width is greater than the screen height,
* pan = 1 results in the bottom edge lining up
* if the images width is less than the screen height,
* pan = 1 results in the top edges lining up
* if image height == screen height it does nothing
*
* @return pan in y. Where 0 is centered NaN if not set
*/
public float getImagePanY() {
return mPanY;
}
/**
* gets the zoom where 1 scales the image just enough to fill the view
*
* @return the zoom factor
*/
public float getImageZoom() {
return mZoom;
}
/**
* gets the rotation
*
* @return the rotation in degrees
*/
public float getImageRotate() {
return mRotate;
}
/**
* sets the pan from the center
* pan of 1 the image is "all the way to the right"
* if the images width is greater than the screen width,
* pan = 1 results in the left edge lining up
* if the images width is less than the screen width,
* pan = 1 results in the right edges lining up
* if image width == screen width it does nothing
*
* @param pan sets the pan in X. Where 0 is centered
*/
public void setImagePanX(float pan) {
mPanX = pan;
updateViewMatrix();
}
/**
* sets the pan from the center
* pan of 1 the image is "all the way to the bottom"
* if the images width is greater than the screen height,
* pan = 1 results in the bottom edge lining up
* if the images width is less than the screen height,
* pan = 1 results in the top edges lining up
* if image height == screen height it does nothing
*
* @param pan sets the pan in X. Where 0 is centered
*/
public void setImagePanY(float pan) {
mPanY = pan;
updateViewMatrix();
}
/**
* sets the zoom where 1 scales the image just enough to fill the view
*
* @param zoom the zoom factor
*/
public void setImageZoom(float zoom) {
mZoom = zoom;
updateViewMatrix();
}
/**
* sets the rotation angle of the image in degrees
*
* @param rotation the rotation in degrees
*/
public void setImageRotate(float rotation) {
mRotate = rotation;
updateViewMatrix();
}
@Override
public void setImageDrawable(Drawable drawable) {
if (mAltDrawable != null && drawable != null) {
mDrawable = drawable.mutate();
mLayers[0] = mDrawable;
mLayers[1] = mAltDrawable;
mLayer = new LayerDrawable(mLayers);
super.setImageDrawable(mLayer);
setCrossfade(mCrossfade);
} else {
super.setImageDrawable(drawable);
}
}
@Override
public void setImageResource(int resId) {
if (mAltDrawable != null) {
mDrawable = AppCompatResources.getDrawable(getContext(), resId).mutate();
mLayers[0] = mDrawable;
mLayers[1] = mAltDrawable;
mLayer = new LayerDrawable(mLayers);
super.setImageDrawable(mLayer);
setCrossfade(mCrossfade);
} else {
super.setImageResource(resId);
}
}
/**
* Set the alternative Image resource used in cross fading
* @param resId id of drawable
*/
public void setAltImageResource(int resId) {
mAltDrawable = AppCompatResources.getDrawable(getContext(), resId);
setAltImageDrawable(mAltDrawable);
}
/**
* Set the alternative Image Drawable used in cross fading.
* @param altDrawable of drawable
*/
public void setAltImageDrawable(Drawable altDrawable) {
mAltDrawable = altDrawable.mutate();
mLayers[0] = mDrawable;
mLayers[1] = mAltDrawable;
mLayer = new LayerDrawable(mLayers);
super.setImageDrawable(mLayer);
setCrossfade(mCrossfade);
}
private void updateViewMatrix() {
if (Float.isNaN(mPanX)
&& Float.isNaN(mPanY)
&& Float.isNaN(mZoom)
&& Float.isNaN(mRotate)
) {
setScaleType(ScaleType.FIT_CENTER);
return;
}
setMatrix();
}
private void setMatrix() {
if (Float.isNaN(mPanX)
&& Float.isNaN(mPanY)
&& Float.isNaN(mZoom)
&& Float.isNaN(mRotate)
) {
return;
}
float panX = Float.isNaN(mPanX) ? 0 : mPanX;
float panY = Float.isNaN(mPanY) ? 0 : mPanY;
float zoom = Float.isNaN(mZoom) ? 1 : mZoom;
float rota = Float.isNaN(mRotate) ? 0 : mRotate;
Matrix imageMatrix = new Matrix();
imageMatrix.reset();
float iw = getDrawable().getIntrinsicWidth();
float ih = getDrawable().getIntrinsicHeight();
float sw = getWidth();
float sh = getHeight();
float scale = zoom * ((iw * sh < ih * sw) ? sw / iw : sh / ih);
imageMatrix.postScale(scale, scale);
float tx = 0.5f * (panX * (sw - scale * iw) + sw - (scale * iw));
float ty = 0.5f * (panY * (sh - scale * ih) + sh - (scale * ih));
imageMatrix.postTranslate(tx, ty);
imageMatrix.postRotate(rota, sw / 2, sh / 2);
setImageMatrix(imageMatrix);
setScaleType(ScaleType.MATRIX);
}
public ImageFilterView(Context context) {
super(context);
init(context, null);
}
public ImageFilterView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public ImageFilterView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context
.obtainStyledAttributes(attrs, R.styleable.ImageFilterView);
final int count = a.getIndexCount();
mAltDrawable = a.getDrawable(R.styleable.ImageFilterView_altSrc);
for (int i = 0; i < count; i++) {
int attr = a.getIndex(i);
if (attr == R.styleable.ImageFilterView_crossfade) {
mCrossfade = a.getFloat(attr, 0);
} else if (attr == R.styleable.ImageFilterView_warmth) {
setWarmth(a.getFloat(attr, 0));
} else if (attr == R.styleable.ImageFilterView_saturation) {
setSaturation(a.getFloat(attr, 0));
} else if (attr == R.styleable.ImageFilterView_contrast) {
setContrast(a.getFloat(attr, 0));
} else if (attr == R.styleable.ImageFilterView_brightness) {
setBrightness(a.getFloat(attr, 0));
} else if (attr == R.styleable.ImageFilterView_round) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setRound(a.getDimension(attr, 0));
}
} else if (attr == R.styleable.ImageFilterView_roundPercent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setRoundPercent(a.getFloat(attr, 0));
}
} else if (attr == R.styleable.ImageFilterView_overlay) {
setOverlay(a.getBoolean(attr, mOverlay));
} else if (attr == R.styleable.ImageFilterView_imagePanX) {
setImagePanX(a.getFloat(attr, mPanX));
} else if (attr == R.styleable.ImageFilterView_imagePanY) {
setImagePanY(a.getFloat(attr, mPanY));
} else if (attr == R.styleable.ImageFilterView_imageRotate) {
setImageRotate(a.getFloat(attr, mRotate));
} else if (attr == R.styleable.ImageFilterView_imageZoom) {
setImageZoom(a.getFloat(attr, mZoom));
}
}
a.recycle();
mDrawable = getDrawable();
if (mAltDrawable != null && mDrawable != null) {
mLayers[0] = mDrawable = getDrawable().mutate();
mLayers[1] = mAltDrawable.mutate();
mLayer = new LayerDrawable(mLayers);
mLayer.getDrawable(1).setAlpha((int) (255 * mCrossfade));
if (!mOverlay) {
mLayer.getDrawable(0).setAlpha((int) (255 * (1 - mCrossfade)));
}
super.setImageDrawable(mLayer);
} else {
mDrawable = getDrawable();
if (mDrawable != null) {
mLayers[0] = mDrawable = mDrawable.mutate();
}
}
}
}
/**
* Defines whether the alt image will be faded in on top
* of the original image or if it will be crossfaded with it.
* Default is true;
*
* @param overlay
*/
private void setOverlay(boolean overlay) {
mOverlay = overlay;
}
/**
* sets the saturation of the image;
* 0 = grayscale, 1 = original, 2 = hyper saturated
*
* @param saturation
*/
public void setSaturation(float saturation) {
mImageMatrix.mSaturation = saturation;
mImageMatrix.updateMatrix(this);
}
/**
* Returns the currently applied saturation
*
* @return 0 = grayscale, 1 = original, 2 = hyper saturated
*/
public float getSaturation() {
return mImageMatrix.mSaturation;
}
/**
* This sets the contrast. 1 = unchanged, 0 = gray, 2 = high contrast
*
* @param contrast
*/
public void setContrast(float contrast) {
mImageMatrix.mContrast = contrast;
mImageMatrix.updateMatrix(this);
}
/**
* Returns the currently applied contrast
*
* @return 1 = unchanged, 0 = gray, 2 = high contrast
*/
public float getContrast() {
return mImageMatrix.mContrast;
}
/**
* This makes the apparent color temperature of the image warmer or colder.
*
* @param warmth 1 is neutral, 2 is warm, .5 is cold
*/
public void setWarmth(float warmth) {
mImageMatrix.mWarmth = warmth;
mImageMatrix.updateMatrix(this);
}
/**
* Returns the currently applied warmth
*
* @return warmth 1 is neutral, 2 is warm, .5 is cold
*/
public float getWarmth() {
return mImageMatrix.mWarmth;
}
/**
* Set the current mix between the two images that can be set on this view.
*
* @param crossfade a number from 0 to 1
*/
public void setCrossfade(float crossfade) {
mCrossfade = crossfade;
if (mLayers != null) {
if (!mOverlay) {
mLayer.getDrawable(0).setAlpha((int) (255 * (1 - mCrossfade)));
}
mLayer.getDrawable(1).setAlpha((int) (255 * mCrossfade));
super.setImageDrawable(mLayer);
}
}
/**
* Returns the currently applied crossfade.
*
* @return a number from 0 to 1
*/
public float getCrossfade() {
return mCrossfade;
}
/**
* sets the brightness of the image;
* 0 = black, 1 = original, 2 = twice as bright
*
* @param brightness
*/
public void setBrightness(float brightness) {
mImageMatrix.mBrightness = brightness;
mImageMatrix.updateMatrix(this);
}
/**
* Returns the currently applied brightness
*
* @return brightness 0 = black, 1 = original, 2 = twice as bright
*/
public float getBrightness() {
return mImageMatrix.mBrightness;
}
/**
* Set the corner radius of curvature as a fraction of the smaller side.
* For squares 1 will result in a circle
*
* @param round the radius of curvature as a fraction of the smaller width
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void setRoundPercent(float round) {
boolean change = (mRoundPercent != round);
mRoundPercent = round;
if (mRoundPercent != 0.0f) {
if (mPath == null) {
mPath = new Path();
}
if (mRect == null) {
mRect = new RectF();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mViewOutlineProvider == null) {
mViewOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
int w = getWidth();
int h = getHeight();
float r = Math.min(w, h) * mRoundPercent / 2;
outline.setRoundRect(0, 0, w, h, r);
}
};
setOutlineProvider(mViewOutlineProvider);
}
setClipToOutline(true);
}
int w = getWidth();
int h = getHeight();
float r = Math.min(w, h) * mRoundPercent / 2;
mRect.set(0, 0, w, h);
mPath.reset();
mPath.addRoundRect(mRect, r, r, Path.Direction.CW);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setClipToOutline(false);
}
}
if (change) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
}
}
}
/**
* Set the corner radius of curvature
*
* @param round the radius of curvature NaN = default meaning roundPercent in effect
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void setRound(float round) {
if (Float.isNaN(round)) {
mRound = round;
float tmp = mRoundPercent;
mRoundPercent = -1;
setRoundPercent(tmp); // force eval of roundPercent
return;
}
boolean change = (mRound != round);
mRound = round;
if (mRound != 0.0f) {
if (mPath == null) {
mPath = new Path();
}
if (mRect == null) {
mRect = new RectF();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mViewOutlineProvider == null) {
mViewOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
int w = getWidth();
int h = getHeight();
outline.setRoundRect(0, 0, w, h, mRound);
}
};
setOutlineProvider(mViewOutlineProvider);
}
setClipToOutline(true);
}
int w = getWidth();
int h = getHeight();
mRect.set(0, 0, w, h);
mPath.reset();
mPath.addRoundRect(mRect, mRound, mRound, Path.Direction.CW);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setClipToOutline(false);
}
}
if (change) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
}
}
}
/**
* Get the fractional corner radius of curvature.
*
* @return Fractional radius of curvature with respect to smallest size
*/
public float getRoundPercent() {
return mRoundPercent;
}
/**
* Get the corner radius of curvature NaN = RoundPercent in effect.
*
* @return Radius of curvature
*/
public float getRound() {
return mRound;
}
@Override
public void draw(@NonNull Canvas canvas) {
boolean clip = false;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (mRoundPercent != 0.0f && mPath != null) {
clip = true;
canvas.save();
canvas.clipPath(mPath);
}
}
super.draw(canvas);
if (clip) {
canvas.restore();
}
}
@Override
public void layout(int l, int t, int r, int b) {
super.layout(l, t, r, b);
setMatrix();
}
}