use of ini.trakem2.display.MipMapImage in project TrakEM2 by trakem2.
the class Loader method preload.
/**
* Returns null when on low memory condition.
*/
@SuppressWarnings("unchecked")
public static final FutureTask<MipMapImage> preload(final Patch p, final double mag, final boolean repaint) {
if (low_memory_conditions || num_preloader_threads < 1)
return null;
final FutureTask<MipMapImage>[] fu = (FutureTask<MipMapImage>[]) new FutureTask[1];
fu[0] = new FutureTask<MipMapImage>(new Callable<MipMapImage>() {
@Override
public MipMapImage call() {
// Utils.log2("preloading " + mag + " :: " + repaint + " :: " + p);
try {
if (p.getProject().getLoader().hs_unloadable.contains(p))
return null;
if (repaint) {
if (Display.willPaint(p)) {
final MipMapImage mipMap = p.getProject().getLoader().fetchImage(p, mag);
// To prevent it:
if (null != mipMap) {
if (!Loader.isSignalImage(mipMap.image) && p.getProject().getLoader().isCached(p, mag)) {
// not the navigator
Display.repaint(p.getLayer(), p, p.getBoundingBox(null), 1, true, false);
}
}
return mipMap;
}
} else {
// just load it into the cache if possible
return p.getProject().getLoader().fetchImage(p, mag);
}
} catch (final Throwable t) {
IJError.print(t);
} finally {
preloads.remove(fu[0]);
}
return null;
}
});
try {
preloads.add(fu[0]);
preloader.submit(fu[0]);
} catch (final Throwable t) {
Utils.log2("Ignoring error with preloading a Patch");
}
return fu[0];
}
use of ini.trakem2.display.MipMapImage in project TrakEM2 by trakem2.
the class FSLoader method fetchMipMap.
/**
* Does the actual fetching of the file. Returns null if the file does not exist.
* Does NOT pre-release memory from the cache;
* call releaseToFit to do that.
*/
public final MipMapImage fetchMipMap(final Patch patch, int level, final long n_bytes) {
final int max_level = getHighestMipMapLevel(patch);
if (level > max_level)
level = max_level;
final double scale = Math.pow(2.0, level);
final String filename = getInternalFileName(patch);
if (null == filename) {
Utils.log2("null internal filename!");
return null;
}
// New style:
final String path = new StringBuilder(dir_mipmaps).append(level).append('/').append(createIdPath(Long.toString(patch.getId()), filename, mExt)).toString();
if (patch.hasAlphaChannel()) {
final Image img = mmio.open(path);
return img == null ? null : new MipMapImage(img, scale, scale);
} else if (patch.paintsWithFalseColor()) {
// AKA Patch has a LUT or is LUT image like a GIF
final Image img = mmio.open(path);
// considers c_alphas
return img == null ? null : new MipMapImage(img, scale, scale);
} else {
final Image img;
switch(patch.getType()) {
case ImagePlus.GRAY16:
case ImagePlus.GRAY8:
case ImagePlus.GRAY32:
// ImageSaver.openGreyJpeg(path);
img = mmio.openGrey(path);
return img == null ? null : new MipMapImage(img, scale, scale);
default:
// For color images: (considers URL as well)
img = mmio.open(path);
// considers c_alphas
return img == null ? null : new MipMapImage(img, scale, scale);
}
}
}
use of ini.trakem2.display.MipMapImage in project TrakEM2 by trakem2.
the class Loader method fetchAWTImage.
public final MipMapImage fetchAWTImage(final Patch p, final int level, final int max_level) {
// Below, the complexity of the synchronized blocks is to provide sufficient granularity. Keep in mind that only one thread at at a time can access a synchronized block for the same object (in this case, the db_lock), and thus calling lock() and unlock() is not enough. One needs to break the statement in as many synch blocks as possible for maximizing the number of threads concurrently accessing different parts of this function.
// find an equal or larger existing pyramid awt
final long id = p.getId();
ImageLoadingLock plock = null;
synchronized (db_lock) {
try {
if (null == mawts) {
// when lazy repainting after closing a project, the awts is null
return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
if (level >= 0 && isMipMapsRegenerationEnabled()) {
// 1 - check if the exact level is cached
final Image mawt = mawts.get(id, level);
if (null != mawt) {
// Utils.log2("returning cached exact mawt for level " + level);
final double scale = Math.pow(2.0, level);
return new MipMapImage(mawt, scale, scale);
}
plock = getOrMakeImageLoadingLock(p.getId(), level);
}
} catch (final Exception e) {
IJError.print(e);
}
}
MipMapImage mipMap = null;
// 2 - check if the exact file is present for the desired level
if (level >= 0 && isMipMapsRegenerationEnabled()) {
synchronized (plock) {
final Image mawt;
synchronized (db_lock) {
mawt = mawts.get(id, level);
}
if (null != mawt) {
final double scale = Math.pow(2.0, level);
// was loaded by a different thread
return new MipMapImage(mawt, scale, scale);
}
}
final long n_bytes = estimateImageFileSize(p, level);
// going to load:
releaseToFit(n_bytes * 8);
synchronized (plock) {
try {
mipMap = fetchMipMapAWT(p, level, n_bytes);
} catch (final Throwable t) {
IJError.print(t);
mipMap = null;
}
synchronized (db_lock) {
try {
if (null != mipMap) {
// Utils.log2("returning exact mawt from file for level " + level);
if (REGENERATING != mipMap.image) {
mawts.put(id, mipMap.image, level);
Display.repaintSnapshot(p);
}
return mipMap;
}
// Check if an appropriate level is cached
mipMap = mawts.getClosestAbove(id, level);
if (mipMap == null) {
// 3 - else, load closest level to it but still giving a larger image
// finds the file for the returned level, otherwise returns zero
final int lev = getClosestMipMapLevel(p, level, max_level);
// Utils.log2("closest mipmap level is " + lev);
if (lev > -1) {
// overestimating n_bytes
mipMap = fetchMipMapAWT(p, lev, n_bytes);
if (null != mipMap) {
mawts.put(id, mipMap.image, lev);
// Utils.log2("from getClosestMipMapLevel: mawt is " + mawt);
Display.repaintSnapshot(p);
// Utils.log2("returning from getClosestMipMapAWT with level " + lev);
return mipMap;
}
} else if (ERROR_PATH_NOT_FOUND == lev) {
mipMap = new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
} else {
return mipMap;
}
} catch (final Throwable t) {
handleCacheError(t);
} finally {
removeImageLoadingLock(plock);
}
}
}
}
synchronized (db_lock) {
try {
// 4 - check if any suitable level is cached (whithout mipmaps, it may be the large image)
mipMap = mawts.getClosestAbove(id, level);
if (null != mipMap) {
// Utils.log2("returning from getClosest with level " + level);
return mipMap;
}
} catch (final Exception e) {
IJError.print(e);
}
}
if (hs_unloadable.contains(p))
return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
synchronized (db_lock) {
try {
plock = getOrMakeImageLoadingLock(p.getId(), level);
} catch (final Exception e) {
// TODO there may be a flaw in the image loading locks: when removing it, if it had been acquired by another thread, then a third thread will create it new. The image loading locks should count the number of threads that have them, and remove themselves when zero.
if (null != plock)
removeImageLoadingLock(plock);
return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
}
synchronized (plock) {
// Check if a previous call made it while waiting:
mipMap = mawts.getClosestAbove(id, level);
if (null != mipMap) {
synchronized (db_lock) {
removeImageLoadingLock(plock);
}
return mipMap;
}
}
Image mawt = null;
try {
// Else, create the mawt:
final Patch.PatchImage pai = p.createTransformedImage();
synchronized (plock) {
if (null != pai && null != pai.target) {
mawt = pai.createImage(p.getMin(), p.getMax());
}
}
} catch (final Exception e) {
Utils.log2("Could not create an image for Patch " + p);
mawt = null;
}
synchronized (db_lock) {
try {
if (null != mawt) {
mawts.put(id, mawt, 0);
Display.repaintSnapshot(p);
// Utils.log2("Created mawt from scratch.");
return new MipMapImage(mawt, 1.0, 1.0);
}
} catch (final Throwable t) {
handleCacheError(t);
} finally {
removeImageLoadingLock(plock);
}
}
return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
use of ini.trakem2.display.MipMapImage in project TrakEM2 by trakem2.
the class FSLoader method fetchMipMapAWT.
/**
* Will NOT free memory.
*/
private final MipMapImage fetchMipMapAWT(final Patch patch, final int level, final long n_bytes, final int retries) {
if (null == dir_mipmaps) {
Utils.log2("null dir_mipmaps");
return null;
}
while (retries < MAX_RETRIES) {
try {
// TODO should wait if the file is currently being generated
final MipMapImage mipMap = fetchMipMap(patch, level, n_bytes);
if (null != mipMap)
return mipMap;
// if we got so far ... try to regenerate the mipmaps
if (!mipmaps_regen) {
return null;
}
// check that REALLY the file doesn't exist.
if (cannot_regenerate.contains(patch)) {
Utils.log("Cannot regenerate mipmaps for patch " + patch);
return null;
}
// Utils.log2("getMipMapAwt: imp is " + imp + " for path " + dir_mipmaps + level + "/" + new File(getAbsolutePath(patch)).getName() + "." + patch.getId() + mExt);
// Regenerate in the case of not asking for an image under 32x32
double scale = 1 / Math.pow(2, level);
if (level >= 0 && patch.getWidth() * scale >= 32 && patch.getHeight() * scale >= 32 && isMipMapsRegenerationEnabled()) {
// regenerate in a separate thread
regenerateMipMaps(patch);
return new MipMapImage(REGENERATING, patch.getWidth() / REGENERATING.getWidth(), patch.getHeight() / REGENERATING.getHeight());
}
} catch (OutOfMemoryError oome) {
Utils.log2("fetchMipMapAWT: recovering from OutOfMemoryError");
recoverOOME();
Thread.yield();
// Retry:
return fetchMipMapAWT(patch, level, n_bytes, retries + 1);
} catch (Throwable t) {
IJError.print(t);
}
}
return null;
}
use of ini.trakem2.display.MipMapImage in project TrakEM2 by trakem2.
the class FSLoader method fetchDataImage.
/**
* Waits until a proper image of the desired size or larger can be returned, which is never the Loader.REGENERATING image.
* If no image can be loaded, returns Loader.NOT_FOUND.
* If the Patch is undergoing mipmap regeneration, it waits until done.
*/
@Override
public MipMapImage fetchDataImage(final Patch p, final double mag) {
Future<Boolean> fu = null;
MipMapImage mipMap = null;
synchronized (gm_lock) {
fu = regenerating_mipmaps.get(p);
}
if (null == fu) {
// Patch is currently not under regeneration
mipMap = fetchImage(p, mag);
// and img will be now Loader.REGENERATING
if (Loader.REGENERATING != mipMap.image) {
return mipMap;
} else {
synchronized (gm_lock) {
fu = regenerating_mipmaps.get(p);
}
}
}
if (null != fu) {
try {
if (!fu.get()) {
Utils.log("Loader.fetchDataImage: could not regenerate mipmaps and get an image for patch " + p);
return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
// Now the image should be good:
mipMap = fetchImage(p, mag);
// Check in any case:
if (Loader.isSignalImage(mipMap.image)) {
// Attempt to create from scratch
return new MipMapImage(p.createTransformedImage().createImage(p.getMin(), p.getMax()), 1, 1);
} else {
return mipMap;
}
} catch (Throwable e) {
IJError.print(e);
}
}
// else:
Utils.log("Loader.fetchDataImage: could not get a data image for patch " + p);
return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
Aggregations