diff -r ed1d52c5aa94 -r 763d3961400b project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapPreviewGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapPreviewGenerator.java Sat Aug 18 00:47:51 2012 +0200 @@ -0,0 +1,200 @@ +package org.hedgewars.hedgeroid; + +import java.io.File; +import java.io.FileNotFoundException; + +import org.hedgewars.hedgeroid.Datastructures.MapFile; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; +import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; +import org.hedgewars.hedgeroid.frontlib.Flib; +import org.hedgewars.hedgeroid.frontlib.Frontlib; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapRecipePtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapconnPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.MapimageCallback; +import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback; +import org.hedgewars.hedgeroid.util.FileUtils; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.sun.jna.Pointer; + +/** + * A class that asynchronously generates a map preview from a MapRecipe. + * + * For named maps, this will load the preview image from the filesystem. For others, + * it will call the engine to generate a preview image. + */ +public final class MapPreviewGenerator implements Runnable { + private static final String TAG = MapPreviewGenerator.class.getSimpleName(); + private static final Handler mainHandler = new Handler(Looper.getMainLooper()); + + private final Context appContext; + private final MapRecipe map; + private final Listener listener; + + private boolean resultAvailable; + private Drawable result; + + public static interface Listener { + /** + * This is called on the UI thread once the preview is ready or failed. + * In case of failure, null is passed. + */ + void onMapPreviewResult(Drawable preview); + } + + private MapPreviewGenerator(Context appContext, MapRecipe map, Listener listener) { + this.appContext = appContext; + this.map = map; + this.listener = listener; + } + + public void run() { + if (map.mapgen == Frontlib.MAPGEN_NAMED) { + postToListener(loadPreviewFromFile(appContext, map.name)); + } else { + resultAvailable = false; + result = null; + MapconnPtr conn = Flib.INSTANCE.flib_mapconn_create(MapRecipePtr.createJavaOwned(map)); + if (conn == null) { + postToListener(null); + return; + } + try { + int port = Flib.INSTANCE.flib_mapconn_getport(conn); + Flib.INSTANCE.flib_mapconn_onSuccess(conn, successCb, null); + Flib.INSTANCE.flib_mapconn_onFailure(conn, failureCb, null); + + String configPath; + try { + configPath = FileUtils.getCachePath(appContext).getAbsolutePath(); + } catch(FileNotFoundException e) { + return; + } + + startEngine(configPath, port); + long startTime = System.nanoTime(); + do { + Flib.INSTANCE.flib_mapconn_tick(conn); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore + } + } while(!resultAvailable && System.nanoTime()-startTime < 15000000000l); // 15 seconds timeout + } finally { + Flib.INSTANCE.flib_mapconn_destroy(conn); + postToListener(result); + } + } + } + + public static void startPreviewGeneration(Context appContext, MapRecipe map, Listener listener) { + new Thread(new MapPreviewGenerator(appContext, map, listener)).start(); + } + + private static Drawable loadPreviewFromFile(Context appContext, String mapName) { + if(!mapName.startsWith("+")) { + try { + File previewFile = MapFile.getPreviewFile(appContext, mapName); + return Drawable.createFromPath(previewFile.getAbsolutePath()); + } catch (FileNotFoundException e) { + Log.w("MapPreviewGenerator", "Preview for map "+mapName+" not found."); + } + } + return null; + } + + private static void startEngine(final String configPath, final int port) { + new Thread(new Runnable() { + public void run() { + Log.d(TAG, "Starting engine "+port); + synchronized(PascalExports.engineMutex) { + PascalExports.HWGenLandPreview(port); + } + Log.d(TAG, "Engine finished"); + } + }).start(); + } + + private void postToListener(final Drawable result) { + mainHandler.post(new Runnable() { + public void run() { + listener.onMapPreviewResult(result); + } + }); + } + + /** + * Let's be extra nice here and clip off the left and right sides, so the preview is centered... + * Since the image is present in bytes, we can save some effort by checking entire byte-columns first. + */ + private final MapimageCallback successCb = new MapimageCallback() { + public void callback(Pointer context, Pointer buffer, int hedgehogCount) { + Log.d(TAG, "Running success handler"); + byte[] mapdata = buffer.getByteArray(0, Frontlib.MAPIMAGE_BYTES); + + int leftmostPixel = Frontlib.MAPIMAGE_WIDTH; + int rightmostPixel = -1; + int bytesPerLine = Frontlib.MAPIMAGE_WIDTH/8; + + // Find the leftmost pixel + for(int xbyte=0; xbyte=0; xbyte--) { + for(int y=0; y use default width + if(rightmostPixel==-1) { + leftmostPixel = 0; + rightmostPixel = Frontlib.MAPIMAGE_WIDTH-1; + } + + Bitmap bitmap = Bitmap.createBitmap(rightmostPixel-leftmostPixel+1, Frontlib.MAPIMAGE_HEIGHT, Config.ARGB_8888); + for(int y=0; y>3] & (128>>(pixelnum&7))) != 0; + } + + private final StrCallback failureCb = new StrCallback() { + public void callback(Pointer context, String reason) { + Log.e(TAG, "Error generating map preview: "+reason); + result = null; + resultAvailable = true; + } + }; +} \ No newline at end of file