diff --git a/app/src/androidTest/java/be/abyx/aurora/ExampleInstrumentedTest.java b/app/src/androidTest/java/be/abyx/aurora/ExampleInstrumentedTest.java index 7578ac8..72ba739 100644 --- a/app/src/androidTest/java/be/abyx/aurora/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/be/abyx/aurora/ExampleInstrumentedTest.java @@ -64,7 +64,7 @@ public void testSimpleGradientAurora() throws Exception { Bitmap gradient = factory.createAuroraBasedUponColour(output, 400, 800); - saveImageToExternalStorage(gradient); + saveImageToExternalStorage(gradient, Bitmap.CompressFormat.JPEG); } @Test @@ -78,23 +78,39 @@ public void testBlurryAurora() throws Exception { Bitmap gradient = factory.createAuroraBasedUponDrawable(redImage, new BlurryAurora(appContext), 1200, 1920); - saveImageToExternalStorage(gradient); + saveImageToExternalStorage(gradient, Bitmap.CompressFormat.JPEG); } - private void saveImageToExternalStorage(Bitmap finalBitmap) { + @Test + public void testMagicCrop() throws Exception { + Context appContext = InstrumentationRegistry.getTargetContext(); + + Drawable logo = getInstrumentation().getContext().getResources().getDrawable(be.abyx.aurora.test.R.drawable.delhaize, null); + ImageUtils utils = new ImageUtils(appContext); + + Bitmap cropped = utils.magicCrop(((BitmapDrawable) logo).getBitmap(), Color.WHITE, 0.75f); + saveImageToExternalStorage(cropped, Bitmap.CompressFormat.PNG); + } + + private void saveImageToExternalStorage(Bitmap finalBitmap, Bitmap.CompressFormat format) { File myDir = getInstrumentation().getContext().getExternalFilesDir("gradient"); myDir.mkdirs(); Random generator = new Random(); int n = 10000; n = generator.nextInt(n); - String fname = "Image-" + n + ".jpg"; + String fname; + if (format.equals(Bitmap.CompressFormat.JPEG)) { + fname = "Image-" + n + ".jpg"; + } else { + fname = "Image-" + n + ".png"; + } File file = new File(myDir, fname); System.out.println(file.getAbsolutePath()); if (file.exists()) file.delete(); try { FileOutputStream out = new FileOutputStream(file); - finalBitmap.compress(Bitmap.CompressFormat.JPEG, 98, out); + finalBitmap.compress(format, 100, out); out.flush(); out.close(); } diff --git a/app/src/androidTest/res/drawable/delhaize.png b/app/src/androidTest/res/drawable/delhaize.png new file mode 100644 index 0000000..0f3e4a6 Binary files /dev/null and b/app/src/androidTest/res/drawable/delhaize.png differ diff --git a/app/src/main/java/be/abyx/aurora/ColourParser.java b/app/src/main/java/be/abyx/aurora/ColourParser.java deleted file mode 100644 index 1463466..0000000 --- a/app/src/main/java/be/abyx/aurora/ColourParser.java +++ /dev/null @@ -1,50 +0,0 @@ -package be.abyx.aurora; - -import android.graphics.Color; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * Parse colour data that's stored inside of a JSON-file with a specific format. - * - * @author Pieter Verschaffelt - */ -public class ColourParser { - public List parseFile(InputStream data) throws IOException, JSONException { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - byte[] buffer = new byte[4096]; - int length; - - while ((length = data.read(buffer)) != -1) { - result.write(buffer, 0, length); - } - - String JSON = result.toString("UTF-8"); - - JSONObject parsedJSON = new JSONObject(JSON); - JSONArray parsedColours = parsedJSON.getJSONArray("data"); - - List output = new ArrayList<>(); - - for (int i = 0; i < parsedColours.length(); i++) { - JSONObject obj = parsedColours.getJSONObject(i); - JSONObject rgbObj = obj.getJSONObject("rgb"); - - int red = rgbObj.getInt("r"); - int green = rgbObj.getInt("g"); - int blue = rgbObj.getInt("b"); - - output.add(Color.argb(255, red, green, blue)); - } - - return output; - } -} diff --git a/app/src/main/java/be/abyx/aurora/ImageUtils.java b/app/src/main/java/be/abyx/aurora/ImageUtils.java index 36d1ada..b2e2756 100644 --- a/app/src/main/java/be/abyx/aurora/ImageUtils.java +++ b/app/src/main/java/be/abyx/aurora/ImageUtils.java @@ -26,43 +26,65 @@ public ImageUtils(Context context) { * * @param input A Bitmap that should be automatically cropped. * @param colour The colour of the edges that should be removed. + * @param tolerance How much a colour can deviate from the real given value and should still + * be removed (A value between 0 and 1 where 1 indicates full tolerance). + * @return Bitmap A new Bitmap whose corners are removed. */ - public void magicCrop(Bitmap input, int colour) { - BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inScaled = false; - - Bitmap originalBitmap = BitmapFactory.decodeResource(context.getResources(), R.raw.aurora_fancy_template, opts); - - int width = originalBitmap.getWidth(); - int height = originalBitmap.getHeight(); + public Bitmap magicCrop(Bitmap input, int colour, float tolerance) { + int width = input.getWidth(); + int height = input.getHeight(); int[] pixels = new int[width * height]; - originalBitmap.getPixels(pixels, 0, width, 0, 0, width, height); + input.getPixels(pixels, 0, width, 0, 0, width, height); // We're working with a Stack-based approach of the Flood Fill algorithm to determine // the edges that should get removed. Stack coordinates = new Stack<>(); // Start filling from the four corners of the image - coordinates.push(new ImageCoordinate(0, 0)); coordinates.push(new ImageCoordinate(0, height - 1)); coordinates.push(new ImageCoordinate(width - 1, height - 1)); coordinates.push(new ImageCoordinate(width - 1, 0)); + coordinates.push(new ImageCoordinate(0, 0)); + + int referenceRed = Color.red(colour); + int referenceGreen = Color.green(colour); + int referenceBlue = Color.blue(colour); + int sum = referenceBlue + referenceGreen + referenceRed; + float treshold = 3 * 255 * tolerance; while (!coordinates.isEmpty()) { ImageCoordinate current = coordinates.pop(); - if (pixels[current.getY() * height + current.getX()] == colour) { - pixels[current.getY() * height + current.getX()] = Color.TRANSPARENT; + if (validPosition(current.getX(), current.getY(), width, height) && pixels[current.getY() * width + current.getX()] == colour) { + int pixel = pixels[current.getY() * width + current.getX()]; + int red = Color.red(pixel); + int blue = Color.blue(pixel); + int green = Color.green(pixel); + + float difference = Math.abs((red + blue + green) - sum); - // Reuse current ImageCoordinate-object. - current.setY(current.getY() + 1); - coordinates.push(current); - coordinates.push(new ImageCoordinate(current.getX(), current.getY() - 1)); - coordinates.push(new ImageCoordinate(current.getX() + 1, current.getY())); - coordinates.push(new ImageCoordinate(current.getX() - 1, current.getY())); + if (difference <= treshold) { + pixels[current.getY() * width + current.getX()] = Color.TRANSPARENT; + + // Reuse current ImageCoordinate-object. + coordinates.push(new ImageCoordinate(current.getX() + 1, current.getY())); + coordinates.push(new ImageCoordinate(current.getX() - 1, current.getY())); + coordinates.push(new ImageCoordinate(current.getX(), current.getY() - 1)); + current.setY(current.getY() + 1); + coordinates.push(current); + } } } - input.setPixels(pixels, 0, width, 0, 0, width, height); + Bitmap output = input.copy(input.getConfig(), true); + output.setHasAlpha(true); + output.setPixels(pixels, 0, width, 0, 0, width, height); + + return output; + } + + + private boolean validPosition(int x, int y, int width, int height) { + return x >= 0 && x < width && y >= 0 && y < height; } }