alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (GradientShader.java)

This example Android source code file (GradientShader.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

clamp, gradient_size, gradientpaint, gradientshader, illegalargumentexception, mirror, repeat, tilemode

The GradientShader.java Android example source code

/*
 * Copyright (C) 2010 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.graphics;


/**
 * Base class for Gradient shader. This is not a standard android class and is just used
 * as a base class for the re-implemented gradient classes.
 *
 * It also provides a base class to handle common code between the different shaders'
 * implementations of {@link java.awt.Paint}.
 *
 * @see LinearGradient
 * @see RadialGradient
 * @see SweepGradient
 */
public abstract class GradientShader extends Shader {

    protected final int[] mColors;
    protected final float[] mPositions;

    /**
     * Creates the base shader and do some basic test on the parameters.
     *
     * @param colors The colors to be distributed along the gradient line
     * @param positions May be null. The relative positions [0..1] of each
     *            corresponding color in the colors array. If this is null, the
     *            the colors are distributed evenly along the gradient line.
     */
    protected GradientShader(int colors[], float positions[]) {
        if (colors.length < 2) {
            throw new IllegalArgumentException("needs >= 2 number of colors");
        }
        if (positions != null && colors.length != positions.length) {
            throw new IllegalArgumentException("color and position arrays must be of equal length");
        }

        if (positions == null) {
            float spacing = 1.f / (colors.length - 1);
            positions = new float[colors.length];
            positions[0] = 0.f;
            positions[colors.length-1] = 1.f;
            for (int i = 1; i < colors.length - 1 ; i++) {
                positions[i] = spacing * i;
            }
        }

        mColors = colors;
        mPositions = positions;
    }

    /**
     * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
     * on the color and position lists, as well as the {@link TileMode}
     *
     */
    protected abstract static class GradientPaint implements java.awt.Paint {
        private final static int GRADIENT_SIZE = 100;

        private final int[] mColors;
        private final float[] mPositions;
        private final TileMode mTileMode;
        private int[] mGradient;

        protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
            mColors = colors;
            mPositions = positions;
            mTileMode = tileMode;
        }

        public int getTransparency() {
            return java.awt.Paint.TRANSLUCENT;
        }

        /**
         * Pre-computes the colors for the gradient. This must be called once before any call
         * to {@link #getGradientColor(float)}
         */
        protected synchronized void precomputeGradientColors() {
            if (mGradient == null) {
                // actually create an array with an extra size, so that we can really go
                // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
                mGradient = new int[GRADIENT_SIZE+1];

                int prevPos = 0;
                int nextPos = 1;
                for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
                    // compute current position
                    float currentPos = (float)i/GRADIENT_SIZE;
                    while (currentPos > mPositions[nextPos]) {
                        prevPos = nextPos++;
                    }

                    float percent = (currentPos - mPositions[prevPos]) /
                            (mPositions[nextPos] - mPositions[prevPos]);

                    mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
                }
            }
        }

        /**
         * Returns the color based on the position in the gradient.
         * <var>pos can be anything, even < 0 or > > 1, as the gradient
         * will use {@link TileMode} value to convert it into a [0,1] value.
         */
        protected int getGradientColor(float pos) {
            if (pos < 0.f) {
                if (mTileMode != null) {
                    switch (mTileMode) {
                        case CLAMP:
                            pos = 0.f;
                            break;
                        case REPEAT:
                            // remove the integer part to stay in the [0,1] range
                            // careful: this is a negative value, so use ceil instead of floor
                            pos = pos - (float)Math.ceil(pos);
                            break;
                        case MIRROR:
                            // get the integer and the decimal part
                            // careful: this is a negative value, so use ceil instead of floor
                            int intPart = (int)Math.ceil(pos);
                            pos = pos - intPart;
                            // 0  -> -1 : mirrored order
                            // -1 -> -2: normal order
                            // etc..
                            // this means if the intpart is even we invert
                            if ((intPart % 2) == 0) {
                                pos = 1.f - pos;
                            }
                            break;
                    }
                } else {
                    pos = 0.0f;
                }
            } else if (pos > 1f) {
                if (mTileMode != null) {
                    switch (mTileMode) {
                        case CLAMP:
                            pos = 1.f;
                            break;
                        case REPEAT:
                            // remove the integer part to stay in the [0,1] range
                            pos = pos - (float)Math.floor(pos);
                            break;
                        case MIRROR:
                            // get the integer and the decimal part
                            int intPart = (int)Math.floor(pos);
                            pos = pos - intPart;
                            // 0 -> 1 : normal order
                            // 1 -> 2: mirrored
                            // etc..
                            // this means if the intpart is odd we invert
                            if ((intPart % 2) == 1) {
                                pos = 1.f - pos;
                            }
                            break;
                    }
                } else {
                    pos = 1.0f;
                }
            }

            int index = (int)((pos * GRADIENT_SIZE) + .5);

            return mGradient[index];
        }

        /**
         * Returns the color between c1, and c2, based on the percent of the distance
         * between c1 and c2.
         */
        private int computeColor(int c1, int c2, float percent) {
            int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
            int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
            int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
            int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
            return a << 24 | r << 16 | g << 8 | b;
        }

        /**
         * Returns the channel value between 2 values based on the percent of the distance between
         * the 2 values..
         */
        private int computeChannel(int c1, int c2, float percent) {
            return c1 + (int)((percent * (c2-c1)) + .5);
        }


    }
}

Other Android examples (source code examples)

Here is a short list of links related to this Android GradientShader.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.