blob: 1aa22587a73e506d0520fa301b0c785689d97692 [file] [log] [blame]
/*
* 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 android.view.shadow;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.tools.layoutlib.annotations.VisibleForTesting;
import android.graphics.Bitmap;
import android.view.math.Math3DHelper;
/**
* Generate spot shadow bitmap.
*/
class SpotShadowBitmapGenerator {
private final SpotShadowConfig mShadowConfig;
private final TriangleBuffer mTriangle;
private float[] mStrips;
private float[] mLightSources;
private float mTranslateX;
private float mTranslateY;
public SpotShadowBitmapGenerator(SpotShadowConfig config) {
// TODO: Reduce the buffer size based on shadow bounds.
mTriangle = new TriangleBuffer();
mShadowConfig = config;
// For now assume no change to the world size
mTriangle.setSize(config.getWidth(), config.getHeight(), 0);
}
/**
* Populate the shadow bitmap.
*/
public void populateShadow() {
try {
mLightSources = SpotShadowVertexCalculator.calculateLight(
mShadowConfig.getLightRadius(),
mShadowConfig.getLightSourcePoints(),
mShadowConfig.getLightCoord()[0],
mShadowConfig.getLightCoord()[1],
mShadowConfig.getLightCoord()[2]);
mStrips = new float[3 * SpotShadowVertexCalculator.getStripSize(
mShadowConfig.getRays(),
mShadowConfig.getLayers())];
if (SpotShadowVertexCalculator.calculateShadow(
mLightSources,
mShadowConfig.getLightSourcePoints(),
mShadowConfig.getPoly(),
mShadowConfig.getPolyLength(),
mShadowConfig.getRays(),
mShadowConfig.getLayers(),
mShadowConfig.getShadowStrength(),
mStrips) != 1) {
return;
}
// Bit of a hack to re-adjust spot shadow to fit correctly within parent canvas.
// Problem is that outline passed is not a final position, which throws off our
// whereas our shadow rendering algorithm, which requires pre-set range for
// optimization purposes.
float[] shadowBounds = Math3DHelper.flatBound(mStrips, 3);
if ((shadowBounds[2] - shadowBounds[0]) > mShadowConfig.getWidth() ||
(shadowBounds[3] - shadowBounds[1]) > mShadowConfig.getHeight()) {
// Spot shadow to be casted is larger than the parent canvas,
// We'll let ambient shadow do the trick and skip spot shadow here.
return;
}
mTranslateX = 0;
mTranslateY = 0;
if (shadowBounds[0] < 0) {
// translate to right by the offset amount.
mTranslateX = shadowBounds[0] * -1;
} else if (shadowBounds[2] > mShadowConfig.getWidth()) {
// translate to left by the offset amount.
mTranslateX = shadowBounds[2] - mShadowConfig.getWidth();
}
if (shadowBounds[1] < 0) {
mTranslateY = shadowBounds[1] * -1;
} else if (shadowBounds[3] > mShadowConfig.getHeight()) {
mTranslateY = shadowBounds[3] - mShadowConfig.getHeight();
}
Math3DHelper.translate(mStrips, mTranslateX, mTranslateY, 3);
mTriangle.drawTriangles(mStrips, mShadowConfig.getShadowStrength());
} catch (IndexOutOfBoundsException|ArithmeticException mathError) {
Bridge.getLog().warning(LayoutLog.TAG_INFO, "Arithmetic error while drawing " +
"spot shadow",
mathError);
} catch (Exception ex) {
Bridge.getLog().warning(LayoutLog.TAG_INFO, "Error while drawing shadow",
ex);
}
}
public float getTranslateX() {
return mTranslateX;
}
public float getTranslateY() {
return mTranslateY;
}
public void clear() {
mTriangle.clear();
}
/**
* @return true if generated shadow poly is valid. False otherwise.
*/
public boolean validate() {
return mStrips != null && mStrips.length >= 9;
}
/**
* @return the bitmap of shadow after it's populated
*/
public Bitmap getBitmap() {
return mTriangle.getImage();
}
@VisibleForTesting
public float[] getStrips() {
return mStrips;
}
@VisibleForTesting
public void updateLightSource(float x, float y) {
mShadowConfig.setLightCoord(x, y);
}
}