javaandroidimage-processingpngbitmapfactory

Merging png files programmatically


I realise this topic has many posts and I have used them to get this far but I feel I need a little extra to get over the hill as none of them demonstrate the use of arrays for this process.

I am building a child friendly maths app and as such, I am using .png files to display the relevant numbers. I have the numbers 0-9 in png format (required for transparent properties) in the drawable folder and testing worked while using just the numbers 0-9 but I want the scope of the app to go further - numbers > 10 (perhaps > 100) - but feel the app size would be too large if I create a png file for every number and therefore want to combine the number png files programmatically for display and then remove them afterwards (if the only way is to create a new file to display).

Firstly, I have entered the files into a Bitmap array -

    bitmapNumbers[0] = BitmapFactory.decodeResource(getResources(), R.drawable.zero);
    bitmapNumbers[1] = BitmapFactory.decodeResource(getResources(), R.drawable.one);
    bitmapNumbers[2] = BitmapFactory.decodeResource(getResources(), R.drawable.two);
    bitmapNumbers[3] = BitmapFactory.decodeResource(getResources(), R.drawable.three);
    bitmapNumbers[4] = BitmapFactory.decodeResource(getResources(), R.drawable.four);
    bitmapNumbers[5] = BitmapFactory.decodeResource(getResources(), R.drawable.five);
    bitmapNumbers[6] = BitmapFactory.decodeResource(getResources(), R.drawable.six);
    bitmapNumbers[7] = BitmapFactory.decodeResource(getResources(), R.drawable.seven);
    bitmapNumbers[8] = BitmapFactory.decodeResource(getResources(), R.drawable.eight);
    bitmapNumbers[9] = BitmapFactory.decodeResource(getResources(), R.drawable.nine);

and when the Activity loads, it calls a method to generate the sum and display it to the user then require user input which calls another method to check the answer. The following is the generateSum method -

public void generateSum () {

    String stringFirst;
    String stringSecond;

    Bitmap[] buildFirst;
    Bitmap[] buildSecond;

    String newFile = "";

    Random rand = new Random();

    // generate first random number

    firstNumber = rand.nextInt(highestNumber + 1);
    stringFirst = String.valueOf(firstNumber);
    int[] splitFirst = new int[stringFirst.length()];

    for (int i = 0; i < stringFirst.length(); i++) {

        // split number down to individual 'single-digit' elements

        splitFirst[i] = Integer.parseInt(stringFirst.substring(i, i + 1));

    }

    // generate second random number

    secondNumber = rand.nextInt(highestNumber + 1);
    stringSecond = String.valueOf(secondNumber);
    int[] splitSecond = new int[stringSecond.length()];

    for (int i = 0; i < stringSecond.length(); i++) {

        // split number down to individual 'single-number' elements

        splitSecond[i] = Integer.parseInt(stringSecond.substring(i, i + 1));

    }

    // calculate sum answer depending on mode selected in setup

    switch (sPrefs.getString("sumMethod", null)) {
        case ("Addition"):
            answer = firstNumber + secondNumber;
            break;
        case ("Subtraction"):
            if (secondNumber > firstNumber) {
                while (secondNumber > firstNumber) {

                    firstNumber = rand.nextInt(highestNumber + 1);
                    secondNumber = rand.nextInt(highestNumber + 1);

                }
            }
            answer = firstNumber - secondNumber;
            break;
        case ("Multiplication"):
            answer = firstNumber * secondNumber;
            break;
        default:
            return;
    }

    // build Bitmap array based on first number generated and split

    buildFirst = new Bitmap[splitFirst.length];

    for (int i = 0; i < splitFirst.length; i++) {
        for (int j = 0; j <= 9; j++) {
            if (j == splitFirst[i]) {

                // built from Bitmap array containing .png drawable resources

                buildFirst[i] = bitmapNumbers[j];

            }
        }
    }

    // code to generate new merged .png file based on built arrays and .png files

    Bitmap builtFirst = Bitmap.createBitmap(buildFirst[0].getWidth(), buildFirst[0].getHeight(), buildFirst[0].getConfig());
    Canvas canvas = new Canvas(builtFirst);
    newFile = buildFirst[0].toString();

    if (buildFirst.length > 1) {

        for (int i = 1; i < buildFirst.length; i++) {

            canvas.drawBitmap(buildFirst[i], 0, 0, null);
            canvas.drawBitmap(builtFirst, new Matrix(), null);
            newFile = newFile + buildFirst[i].toString();

        }
    }

    try {

        FileOutputStream out = new FileOutputStream("android.resource://com.app.numberup/drawable/" + newFile);
        builtFirst.compress(Bitmap.CompressFormat.PNG, 100, out);

        imageViewFirstNumber.setImageBitmap(builtFirst);

    } catch (Exception e) {

        Log.i("Error caught", e.getMessage());
        e.printStackTrace();

    }
/*
    buildSecond = new int[splitSecond.length];

    for (int i = 0; i <= splitSecond.length; i++) {
        for (int j = 0; j <= 9; j++) {
            if (j == splitSecond[i]) {

                buildSecond[i] = numbers[j];

            }
        }
    }*/

    // original code to display single numbers - worked
    //imageViewFirstNumber.setImageResource(numbers[i]);

    //imageViewSecondNumber.setImageResource(numbers[i]);

}

From all I have looked at, this code should at least build a .png file in the drawable directory (called 72.png if that was the generated number. I am aware this line -

imageViewFirstNumber.setImageBitmap(builtFirst);

won't display that file. I am unsure how to reference the newly generated file, but the bigger problem is that the newFile isn't in the drawable folder to find.

I am also aware that -

if (buildFirst.length > 1) {

        for (int i = 1; i < buildFirst.length; i++) {

            canvas.drawBitmap(buildFirst[i], 0, 0, null);
            canvas.drawBitmap(builtFirst, new Matrix(), null);
            newFile = newFile + buildFirst[i].toString();

        }
    }

This would just heap one number image ontop of the other, I'm thinking if I use buildFirst[i].getwidth * i as the 'left' parameter will put the images to the right of the last but I'd like to be able to show any generated image and then work out the refinements after. At the moment, I only get the default number file displayed after generating the sum.

My error handler does catch a FileNotFoundException on this line of code -

Bitmap builtFirst = Bitmap.createBitmap(buildFirst[0].getWidth(), buildFirst[0].getHeight(), buildFirst[0].getConfig());

I assume this is due to the contents of buildFirst[0] being 'android.graphics.Bitmap@1852540c' with something similar in [1] (2 digit number genereated), can this process not be done with .png files? If that's the case, how can I merge them while keeping the transparent properties of the original .png files? perhaps there is a better way to populate the original Bitmap array?

How do I resolve this image processing step?


Solution

  • 2 days of trial and error finally hit on the solution.

    No need to compress the file or write any new file or use any bitmap as a template and no need to reference the new drawable as a resource (instead just apply the created Bitmap variable) were the issues here.

    Bitmap builtFirst = Bitmap.createBitmap(500 * splitFirst.length, 700, ARGB_8888);
        Canvas canvas = new Canvas(builtFirst);
    
        if (buildFirst.length > 1) {
    
            for (int i = 0; i < buildFirst.length; i++) {
    
                canvas.drawBitmap(buildFirst[i], 300 * i, 0, null);
                canvas.drawBitmap(builtFirst, new Matrix(), null);
    
            }
        }
    
    imageViewFirstNumber.setImageBitmap(builtFirst);