How to setColorFilter to a png image

Viewed 427

I am already using setColorFilter() to add color to these lines. The original color for this line is yellow. I use red or green depend on some statement.

This is what I have so far: Someone helped me to do this: How to set filter property in ImageView Android studio

enter image description here

But now I need to add color under the line...like this:

enter image description here

sparkline.setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
sparkline.setImageResource(R.drawable.btc_spike);

<androidx.constraintlayout.utils.widget.ImageFilterView
                android:layout_width="80dp"
                android:layout_height="50dp"
                android:id="@+id/sparkline"
                android:layout_marginRight="2dp"
                app:saturation="80"
                app:brightness="0.85"
                app:crossfade="0.0"
                app:warmth="10"
                android:src="@drawable/btc_spike" />

I also found this page but not sure if it wants me to add another image on top of this or something else...

https://developer.android.com/reference/android/graphics/PorterDuff.Mode#MULTIPLY

1 Answers

To add a color under the line using the png image you can do something like below:

  1. Retrieve the png image as a Bitmap (source Bitmap).
  2. Copy the source Bitmap in a temporary Bitmap (output Bitmap).
  3. Iterate each x,y pixel of the output Bitmap and find the Line pixels (Non-Transparent pixels). The remaining Transparent pixels distinguish them to Transparent pixels above the line area and below the line area.
  4. Finally set the Red/Green color to the line pixels and the Red/Green light color to the Transparent pixels below the line.

Here is a helper function which does the above steps:

public static Bitmap getShadowLineBitmap(@NonNull Bitmap srcBitmap, @ColorInt int lineColor, @ColorInt int shadowUnderLineColor){

        //copy the srcBitmap to the outBitmap
        Bitmap outBitmap = srcBitmap.copy(srcBitmap.getConfig(), true);
        try
        {
            //iterate each x,y pixel
            for (int x = 0; x < outBitmap.getWidth(); x++) {
                boolean foundLinePx = false;
                for (int y = 0; y < outBitmap.getHeight(); y++) {

                    //get the current RGBA x,y pixel
                    int rgba = outBitmap.getPixel(x, y);

                    //if its a Transparent pixel it means that is a pixel above or below the line
                    //so draw the pixels above the line to Transparent and draw the pixels below the line to a red/green light color
                    if (rgba == Color.TRANSPARENT) {
                        if(foundLinePx)
                            outBitmap.setPixel(x, y, shadowUnderLineColor);
                        else
                            outBitmap.setPixel(x, y, rgba);
                    }
                    //if its a Non-Transparent pixel it means that is a line pixel so draw each line pixel to red/green
                    else {
                        outBitmap.setPixel(x, y, lineColor);
                        foundLinePx = true;
                    }
                }
            }
        }catch (Exception e){

        }
        return outBitmap;
    }

And can be used like below:

//get the src Bitmap from Resources (Bitmap can be retrieved also from Glide/Picasso)
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sparkline); 

//Draw the Bitmap with Green Line Color and Green Shadow Light Color
imageView.setImageBitmap(getShadowLineBitmap(srcBitmap, Color.argb(255, 25, 189, 119), Color.argb(255, 231, 250, 243)));

//Draw the Bitmap with Red Line Color and Red Shadow Light Color
imageView.setImageBitmap(getShadowLineBitmap(srcBitmap, Color.argb(255, 228, 112, 117), Color.argb(255, 252, 227, 228)));

Result:

green red

Load Bitmaps using Glide in RecyclerView

I have tested some graph image links from your domain (eg: https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/1.png) with a RecyclerView and it works as expected even if i scrolled the cells up/down.

In on void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) add the below lines of code to load the bitmap using Glide (Glide Version: com.github.bumptech.glide:glide:4.12.0):

Glide.with(itemView.getContext().getApplicationContext())
        .asBitmap()
        .placeholder(R.drawable.placeholer)
        .error(R.drawable.error)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .override(164, 48) //we know the web image size (164x48)
        .load(url)
        .into(new CustomTarget<Bitmap>(164, 48)
        {
            @Override
            public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
               
                        //set resource to ImageBitmap
                        imageView.setImageBitmap(resource);

                        //set Green Color filter to line and Green Shadow line colour in AsyncTask
                        if(isGreen) {
                            imageView.setColorFilter(Color.argb(255, 25, 189, 119), PorterDuff.Mode.MULTIPLY);
                            new DrawBitmapShadowAsyncTask(imageView,
                                    Color.argb(255, 25, 189, 119),
                                    Color.argb(255, 231, 250, 243))
                                    .execute(resource);
                        }
                        //set Red Color filter to line and Red Shadow line colour in AsyncTask
                        else{
                            imageView.setColorFilter(Color.argb(255, 228, 112, 117), PorterDuff.Mode.MULTIPLY);
                            new DrawBitmapShadowAsyncTask(imageView,
                                    Color.argb(255, 228, 112, 117),
                                    Color.argb(255, 252, 227, 228))
                                    .execute(resource);
                        }
            }

            @Override
            public void onLoadStarted(@Nullable Drawable placeholder) {
                if(placeholder!=null){
                    imageView.setImageDrawable(placeholder);
                }
                else {
                    imageView.setImageBitmap(BitmapFactory.decodeResource(itemView.getResources(), R.drawable.placeholer));
                }
            }

            @Override
            public void onLoadFailed(@Nullable Drawable errorDrawable) {
                if(errorDrawable!=null){
                    imageView.setImageDrawable(errorDrawable);
                }
                else {
                    imageView.setImageBitmap(BitmapFactory.decodeResource(itemView.getResources(), R.drawable.error));
                }
            }

            @Override
            public void onLoadCleared(@Nullable Drawable placeholder) {

            }
        });

From above:

  1. The void onLoadStarted(@Nullable Drawable placeholder) sets the ImageView placeholder drawable when a load is started.
  2. The void onLoadFailed(@Nullable Drawable errorDrawable) sets the ImageView error drawable when a load fails.
  3. The void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) called when the resource load has finished and the Bitmap is ready to be used. In this callback we use the previous helper function in AsyncTask to draw with light color under the graph line.

Below is the DrawBitmapShadowAsyncTask AsyncTask private static class inside RecyclerView Adapter used from Glide onResourceReady to draw the shadow color under the line in the Background Thread:

    /**
     * Draws Shadow Color under the Graph line asynchronously
     */
    private static class DrawBitmapShadowAsyncTask extends AsyncTask<Bitmap, Void, Bitmap> {

        private WeakReference<ImageView> imageView = null;
        private @ColorInt int lineColor;
        private @ColorInt int shadowUnderLineColor;

        private DrawBitmapShadowAsyncTask(ImageView imageView, @ColorInt int lineColor, @ColorInt int shadowUnderLineColor){
            this.imageView = new WeakReference<>(imageView);
            this.lineColor = lineColor;
            this.shadowUnderLineColor = shadowUnderLineColor;
        }

        /**
         * Colorize the srcBitmap below the graph line in a Background Thread
         * @param bitmaps bitmaps[0] (srcBitmap)
         * @return the output bitmap
         */
        @Override
        protected Bitmap doInBackground(Bitmap... bitmaps) {
            return getShadowLineBitmap(bitmaps[0], lineColor, shadowUnderLineColor);
        }

        /**
         * Set the output Bitmap to imageView in Main Thread
         * @param bitmap the output bitmap
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if(imageView!=null && imageView.get()!=null){
                imageView.get().clearColorFilter();
                imageView.get().setImageBitmap(bitmap);
            }
        }

        /**
         * Helper method to draw shadow color under the line
         * @param srcBitmap The source Bitmap
         * @param lineColor The color line
         * @param shadowUnderLineColor The shadow color under the line
         * @return Bitmap The output Bitmap
         */
        private Bitmap getShadowLineBitmap(@NonNull Bitmap srcBitmap, @ColorInt int lineColor, @ColorInt int shadowUnderLineColor){

            //copy the srcBitmap to the outBitmap
            Bitmap outBitmap = srcBitmap.copy(srcBitmap.getConfig(), true);
            try
            {
                //iterate each x,y pixel
                for (int x = 0; x < outBitmap.getWidth(); x++) {
                    boolean foundLinePx = false;
                    for (int y = 0; y < outBitmap.getHeight(); y++) {

                        //get the current RGBA x,y pixel
                        int rgba = outBitmap.getPixel(x, y);

                        //if its a Transparent pixel it means that is a pixel above or below the line
                        //so draw the pixels above the line to Transparent and draw the pixels below the line to a red/green light color
                        if (rgba == Color.TRANSPARENT) {
                            if(foundLinePx)
                                outBitmap.setPixel(x, y, shadowUnderLineColor);
                            else
                                outBitmap.setPixel(x, y, rgba);
                        }
                        //if its a Non-Transparent pixel it means that is a line pixel so draw each line pixel to red/green
                        else {
                            outBitmap.setPixel(x, y, lineColor);
                            foundLinePx = true;
                        }
                    }
                }
            }catch (Exception e){

            }
            return outBitmap;
        }
    }

RecyclerView Results:

recyclerview_red recyclerview_green

Related