how to check if a base 64 encoded image contains any color but white in razor c#

1.7k views Asked by At

I am working on a form that contains a jSignature element, which is a jquery plugin to store user made signatures to base64 strings(or other formats).

The main issue is that if I try to edit an entry in my List I need to be able to check whether the Signature is empty or not.

So simplified; validate if a base64 string contains black or is just pure white.

Now the different approaches I took and why they failed:

  • saving the source string of an empty signature and comparing it to the new signature

this has failed since whenever I had a different resolution while adding an entry, the source string would be different from the one previously saved, empty one.

  • creating a method in JS to calculate the percentage amount of a color in the base64 string(if not 100% white, there is a signature)

this failed since I need to be able to store the value this method returns in a c# property. But apparently the C# part of razor and the inline JS do not exist at the same time. This would be necessary to check if the signature is empty in an if that either shows the jSignature element to add a signature of a picture of the previously added one

  • creating a similar method to check the colors of an image with C#(in the cshtml)

this would work if I had access to the data layer of the app(which ofc I do have but for this project it would be preferable to not change or add anything there). The problem is that i cannot use the datatype Bitmap in a cshtml file.

I am really just looking for a different approach that could work, but if I should add any code snippets just ask for it and i will gladly do it.

Any help is appreciated!

Edit: updated with code of cshtml:

           @if (!editMode && !formDisabled || signatureEmpty)
            {
                <div class="form-group" style="margin:5%">
                    <div class="form-inline" for="sign">
                        <label class="control-label" style="float:left; margin-top:2%">Unterschrift</label>
                        <button type="button" class="btn btn-primary" style="float:right; margin-bottom:1%; background-color:dodgerblue" onclick="refreshSignature()"><span class="glyphicon glyphicon-refresh"></span></button>                    
                    </div>
                    <div style="width:100%; margin-left:0%;" height:auto" name="sign">
                        <div disabled="@formDisabled" id="signature" class="">

                        </div>
                    </div>
                </div>
            }
            else if(!signatureEmpty) //signature->show image 
            {
                <div class="form-group" style="margin:5%">
                    <div class="form-inline" for="signDisabled">
                        <label class="control-label" style="float:left">Unterschrift</label>
                    </div>
                    <img id="testtest" src="@zeitaufzeichnung.Signature"/> <!--format Signature: data:image/png;base64,i1234lkj123;k4;l1j34l1kj3j...-->
                </div>
            }
            else
            {
                <div class="form-group" style="margin:5%">
                    <label class="control-label" style="float:left">keine Unterschrift vorhanden</label>
                </div>
            }

and the java script methods that should change the C# signatureEmpty attribute(mainly the checkIfSignatureIsEmpty method):

function getColors(ctx) {

        // Get the canvas data
        var pixels = ctx.getImageData(0, 0, ctx.width, ctx.height),
            data = pixels.data,

        // Set up our output object to collect color data
        output = {};

        // For each color we encounter, check the
        // output object. If the color already exists
        // there, simply increase its counted value.
        // If it does not, create a new key.
        for (var i = 0; i < data.length; i += 4) {
            var r = data[i],
                g = data[i + 1],
                b = data[i + 2],
                col = rgbToHex(r, g, b);

            if (output[col])
                output[col]++
            else
                output[col] = 1
        }

        // Count total
        var total = 0;
        for (var key in output) {
            total = total + parseInt(output[key])
        }
        output.total = total;

        // Return the color data as an object
        return output;
    }

    function checkIfSignatureIsEmpty(source)
    {
        var img = new Image();
        img.src = source;
        img.id = "img";
        img.name = "img";

        img.onload=function(){
            var imageData = extract_colors(img);
            if (imageData[0] / imageData.total == 1) {
                return true;
            } else {
                return false;
            }
        }
    }

    function extract_colors(img) {
        var canvas = document.createElement("canvas");
        var c = canvas.getContext('2d');
        c.width = canvas.width = img.width;
        c.height = canvas.height = img.height;
        c.clearRect(0, 0, c.width, c.height);
        c.drawImage(img, 0, 0, img.width, img.height);
        return getColors(c);
    }

    function rgbToHex(r, g, b) {
        return ((r << 16) | (g << 8) | b).toString(16);
    }
1

There are 1 answers

0
Felix Froschauer On

I finally convinced myself to create a method in the data-layer of the application.

Here is the code in case anybody needs it:

public static bool IsSignatureEmpty(string base64Source)
    {
        Bitmap signatureBM = null;

        try
        {
            byte[] byteBuffer = Convert.FromBase64String(base64Source.Replace("data:image/png;base64,", ""));
            MemoryStream memoryStream = new MemoryStream(byteBuffer);

            memoryStream.Position = 0;

            signatureBM = (Bitmap)Bitmap.FromStream(memoryStream);

            memoryStream.Close();
            memoryStream = null;
            byteBuffer = null;
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.Message);
        }

        int width = signatureBM.Width;
        int height = signatureBM.Height;

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                //Name==0 would equal the color white
                if (signatureBM.GetPixel(x, y).Name != "0")
                {
                    return false;
                }
            }
        }
        return true;
    }

if you would really want to use pure JS/Html use the js code from above and refer to the comment by Marco:

Instead of using c# just set a javascript variable. Use a different querystring or route value for editMode and then just hide the div using javascript