/*
* Copyright 2015 Daniel Dittmar
*
* 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 dan.dit.whatsthat.util.mosaic.reconstruction;
import android.graphics.Bitmap;
import dan.dit.whatsthat.util.image.ImageUtil;
/**
* This class models an abstract Reconstructor which is used to reconstruct
* a provided image. How the image is fragmented can be defined by the
* Reconstructor. A typical implementation is a fragmentation into
* rects (or squares) as provided by {@link RectReconstructor}. This
* class allows to keep the fragmentation and merging progress close together
* and easily allow different fragmentations.
* @author Daniel
*
*/
public abstract class Reconstructor {
protected static Bitmap obtainBaseBitmap(int width, int height, Bitmap.Config config) {
Bitmap base = ImageUtil.CACHE.getReusableBitmap(width, height, config);
if (base != null) {
return base;
}
return Bitmap.createBitmap(width, height, config);
}
/**
* Gives the Reconstructor the next image for the next missing Fragment.
* The given image must match the requirements of the Fragment provided by
* nextFragment(). In most cases this will mean a direct match for height
* and value and the best approximation for the average RGB color.
* @param nextFragmentImage The image which will be used for the next fragment.
* @return <code>true</code> if the given image was accepted by the
* Reconstructor. If <code>false</code> you need to find another image,
* use nextFragment() to check the requirements of the image.
*/
public abstract boolean giveNext(Bitmap nextFragmentImage);
/**
* Returns the next MosaicFragment which specifies some parameters of the
* next image to provide like the image's height and width and the
* average RGB it should have.
* @return The next MosaicFragment. <code>null</code> if hasAll() is <code>true</code>.
*/
public abstract MosaicFragment nextFragment();
/**
* Returns <code>true</code> if all images needed were provided. Can
* If this is <code>true</code>, nextFragment will return <code>null</code>.
* Can only change when giveNext() returned <code>true</code>.
* @return If all images were provided and the reconstructor has
* enough information to build the new image.
*/
public abstract boolean hasAll();
/**
* Returns the reconstructed image if the reconstructor hasAll() required
* images. The reconstructor can decide to build the image here or simply return it
* here. After this method returned a valid Bitmap, future calls to this method
* can return
* <code>null</code>, as the reference to the created image can be deleted. The policy
* for this behavior is to be defined by the implementing Reconstructor.
* @return The newly build image or <code>null</code> if the reconstructor has
* not all required images or if this method was already successfully invoked and the
* Reconstructor does not save the image.
*/
public abstract Bitmap getReconstructed();
/**
* Returns the closest value next to wantedCount which divides the given imageDimension.
* @param imageDimension The dimension that must be divided. Must be positive. (Image height or width).
* @param wantedCount The wanted amount of rows or columns.
* @return A divisor of imageDimension (so in range 1 to imageDimension (inclusive)) which is closest
* to wantedCount.
* @throws IllegalArgumentException If a parameter is negative or zero.
*/
static int getClosestCount(int imageDimension, int wantedCount) {
if (imageDimension <= 0 || wantedCount <= 0) {
throw new IllegalArgumentException("Image and wanted rect dimension must be greater than zero");
} else {
if (wantedCount > imageDimension) {
return imageDimension;
} else {
return findClosestDivisor(imageDimension, wantedCount);
}
}
}
/**
* Returns the closest divisor to the given number. Not really fast, especially if
* toNumber is prime. Expects valid parameters.
* @param toNumber The number that has to be divided.
* @param wantedDivisor The wanted divisior for the given number. Must be greater than zero and
* smaller than or equal toNumber.
* @return A proper divisor in range 1 to toNumber (both inclusive) of toNumber which
* is closest to wantedDivisior;
*/
private static int findClosestDivisor(int toNumber, int wantedDivisor) {
int currDivisor = wantedDivisor;
int delta = 0;
while (toNumber % currDivisor != 0) {
// makes the divisor greater/smaller by one each iteration, circulating near wantedDivisor
// will terminate if divisor gets equal to one or to toNumber
delta++;
currDivisor = (delta % 2 == 0) ? (currDivisor - delta) : (currDivisor + delta);
}
return currDivisor;
}
public abstract int estimatedProgressPercent();
}