From cd7c8b147ad173f7071d3b1d115e913fc08c5cc3 Mon Sep 17 00:00:00 2001 From: Robin Watts Date: Thu, 2 Dec 2010 18:52:55 +0000 Subject: Import Android demo. --- android/AndroidManifest.xml | 23 + android/ReadMe.txt | 94 ++++ android/build.properties | 17 + android/build.sh | 1 + android/build.xml | 84 ++++ android/default.properties | 11 + android/jni/Android.mk | 21 + android/jni/Application.mk | 3 + android/jni/Core.mk | 110 +++++ android/jni/ThirdParty.mk | 150 ++++++ android/jni/mupdf.c | 239 ++++++++++ android/local.properties | 11 + android/res/drawable-hdpi/icon.png | Bin 0 -> 2945 bytes android/res/drawable-ldpi/icon.png | Bin 0 -> 1456 bytes android/res/drawable-mdpi/icon.png | Bin 0 -> 1916 bytes android/res/layout/main.xml | 8 + android/res/values/strings.xml | 4 + android/res/values/theme.xml | 7 + android/src/com/artifex/mupdf/MuPDFActivity.java | 114 +++++ android/src/com/artifex/mupdf/PixmapView.java | 577 +++++++++++++++++++++++ 20 files changed, 1474 insertions(+) create mode 100644 android/AndroidManifest.xml create mode 100644 android/ReadMe.txt create mode 100644 android/build.properties create mode 100644 android/build.sh create mode 100644 android/build.xml create mode 100644 android/default.properties create mode 100644 android/jni/Android.mk create mode 100644 android/jni/Application.mk create mode 100644 android/jni/Core.mk create mode 100644 android/jni/ThirdParty.mk create mode 100644 android/jni/mupdf.c create mode 100644 android/local.properties create mode 100644 android/res/drawable-hdpi/icon.png create mode 100644 android/res/drawable-ldpi/icon.png create mode 100644 android/res/drawable-mdpi/icon.png create mode 100644 android/res/layout/main.xml create mode 100644 android/res/values/strings.xml create mode 100644 android/res/values/theme.xml create mode 100644 android/src/com/artifex/mupdf/MuPDFActivity.java create mode 100644 android/src/com/artifex/mupdf/PixmapView.java diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 00000000..f58ba5ca --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/android/ReadMe.txt b/android/ReadMe.txt new file mode 100644 index 00000000..2678d732 --- /dev/null +++ b/android/ReadMe.txt @@ -0,0 +1,94 @@ +To build/debug android build. + +1) Download the android sdk, and install it. On windows I unpacked it as: + + C:\Program Files\android-sdk-windows + +on Macos as: + + /Library/android-sdk-mac_x86 + +On windows add: C:/Progra~1/android-sdk-windows/tools to your path. +On linux/macos add the equivalent. + +2) Download the android ndk, and install in. On windows I unpacked it as: + + C:\Program Files\android-ndk-r4b + +on Macos as: + + /Library/android-ndk-r4b + +On windows add: C:/Progra~1/android-ndk-r4b to your patyh. On linux/macos +add the equivalent. + +3) On windows, to use the ndk, you *must* be running under cygwin. This means +you need to install Cygwin 1.7 or greater now. + +4) Bring up a shell, and run 'android'. This will bring up a graphical +gui for the sdk. From here you can install the different SDK components +for the different flavours of android. Download them all - bandwidth and disk +space are cheap, right? + +5) Now go to the Virtual Devices entry on the right hand side. You need to +create yourself an emulator image to use. Click 'New...' on the right hand +side and a window will appear. Fill in the entries as follows: + + Name: FroyoEm + Target: Android 2.2 - API Level 8 + SD card: Size: 1024MiB + Skin: Resolution: 480x756 (756 just fits my macbook screen, but 800 may + be 'more standard') + +Click 'Create AVD' and wait for a minute or so while it is prepared. Now +you can exit the GUI. + +6) Now we are ready to build mupdf for Android. Check out a copy of MuPDF +(but you've done that already, cos you're reading this, right?). Get the +thirdparty package from mupdf.com and unpack that into mupdf/thirdparty. +Also get the pregen package from the same place and unpack that into +mupdf/pregen. + +7) Load local.properties into your favourite editor, and edit the sdk +path there as appropriate. This should be the only bit of localisation +you need to do. + +8) Change into the android directory, and execute (in a Cygwin window on +Windows!): + + ndk-build + +This should build the native code portion. Then execute: + + ant debug + +This should build the java wrapper. + +9) Now start the emulator by executing: + + emulator -avd FroyoEm + +This will take a while to full start up (be patient). + +10) We now need to give the demo file something to chew on, so let's copy +a file into the SD card image of the emulator (this should only need to be +done once). With the emulator running type: + + adb push ../../MyTests/pdf_reference17.pdf /mnt/sdcard/Download/test.pdf + +(where obviously ../../MyTests/pdf_reference17.pdf is altered for your +machine). + +11) With the emulator running (see step 9), execute + + ant install + +and that will copy MuPDF into the emulator where you can run it from the +launchpad screen. + +12) To see debug messages from the emulator (including stdout/stderr from +our app), execute: + + adb logcat + +Good luck! diff --git a/android/build.properties b/android/build.properties new file mode 100644 index 00000000..edc7f230 --- /dev/null +++ b/android/build.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked in Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/android/build.sh b/android/build.sh new file mode 100644 index 00000000..36ad883f --- /dev/null +++ b/android/build.sh @@ -0,0 +1 @@ +ndk-build && ant.bat install diff --git a/android/build.xml b/android/build.xml new file mode 100644 index 00000000..3ba2015b --- /dev/null +++ b/android/build.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/default.properties b/android/default.properties new file mode 100644 index 00000000..0b9250e0 --- /dev/null +++ b/android/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/android/jni/Android.mk b/android/jni/Android.mk new file mode 100644 index 00000000..66c69c9c --- /dev/null +++ b/android/jni/Android.mk @@ -0,0 +1,21 @@ +TOP_LOCAL_PATH := $(call my-dir) + +MUPDF_ROOT := ../.. + +include $(TOP_LOCAL_PATH)/Core.mk +include $(TOP_LOCAL_PATH)/ThirdParty.mk + +LOCAL_PATH = $(TOP_LOCAL_PATH) +include $(CLEAR_VARS) + +LOCAL_CFLAGS := \ + -I$(LOCAL_PATH)/$(MY_ROOT)/draw \ + -I$(LOCAL_PATH)/$(MY_ROOT)/fitz \ + -I$(LOCAL_PATH)/$(MY_ROOT)/mupdf +LOCAL_MODULE := mupdf +LOCAL_SRC_FILES := mupdf.c +LOCAL_STATIC_LIBRARIES := mupdfcore mupdfthirdparty + +LOCAL_LDLIBS := -lm -llog -ljnigraphics + +include $(BUILD_SHARED_LIBRARY) diff --git a/android/jni/Application.mk b/android/jni/Application.mk new file mode 100644 index 00000000..16de5796 --- /dev/null +++ b/android/jni/Application.mk @@ -0,0 +1,3 @@ +# The ARMv7 is significanly faster due to the use of the hardware FPU +APP_ABI := armeabi armeabi-v7a +APP_OPTIM := debug diff --git a/android/jni/Core.mk b/android/jni/Core.mk new file mode 100644 index 00000000..02ad14c2 --- /dev/null +++ b/android/jni/Core.mk @@ -0,0 +1,110 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +MY_ROOT := ../.. + +LOCAL_CFLAGS := \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jbig2dec \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/openjpeg/libopenjpeg \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jpeg \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/zlib \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/freetype/include \ + -I$(LOCAL_PATH)/$(MY_ROOT)/draw \ + -I$(LOCAL_PATH)/$(MY_ROOT)/fitz \ + -I$(LOCAL_PATH)/$(MY_ROOT)/mupdf + +LOCAL_MODULE := mupdfcore +LOCAL_SRC_FILES := \ + $(MY_ROOT)/fitz/base_error.c \ + $(MY_ROOT)/fitz/base_geometry.c \ + $(MY_ROOT)/fitz/base_getopt.c \ + $(MY_ROOT)/fitz/base_hash.c \ + $(MY_ROOT)/fitz/base_memory.c \ + $(MY_ROOT)/fitz/base_string.c \ + $(MY_ROOT)/fitz/base_time.c \ + $(MY_ROOT)/fitz/crypt_aes.c \ + $(MY_ROOT)/fitz/crypt_arc4.c \ + $(MY_ROOT)/fitz/crypt_md5.c \ + $(MY_ROOT)/fitz/dev_bbox.c \ + $(MY_ROOT)/fitz/dev_draw.c \ + $(MY_ROOT)/fitz/dev_list.c \ + $(MY_ROOT)/fitz/dev_null.c \ + $(MY_ROOT)/fitz/dev_text.c \ + $(MY_ROOT)/fitz/dev_trace.c \ + $(MY_ROOT)/fitz/filt_basic.c \ + $(MY_ROOT)/fitz/filt_dctd.c \ + $(MY_ROOT)/fitz/filt_faxd.c \ + $(MY_ROOT)/fitz/filt_flate.c \ + $(MY_ROOT)/fitz/filt_jbig2d.c \ + $(MY_ROOT)/fitz/filt_jpxd.c \ + $(MY_ROOT)/fitz/filt_lzwd.c \ + $(MY_ROOT)/fitz/filt_predict.c \ + $(MY_ROOT)/fitz/obj_array.c \ + $(MY_ROOT)/fitz/obj_dict.c \ + $(MY_ROOT)/fitz/obj_print.c \ + $(MY_ROOT)/fitz/obj_simple.c \ + $(MY_ROOT)/fitz/res_colorspace.c \ + $(MY_ROOT)/fitz/res_font.c \ + $(MY_ROOT)/fitz/res_path.c \ + $(MY_ROOT)/fitz/res_pixmap.c \ + $(MY_ROOT)/fitz/res_shade.c \ + $(MY_ROOT)/fitz/res_text.c \ + $(MY_ROOT)/fitz/stm_buffer.c \ + $(MY_ROOT)/fitz/stm_open.c \ + $(MY_ROOT)/fitz/stm_read.c \ + $(MY_ROOT)/draw/archport.c \ + $(MY_ROOT)/draw/blendmodes.c \ + $(MY_ROOT)/draw/glyphcache.c \ + $(MY_ROOT)/draw/imagedraw.c \ + $(MY_ROOT)/draw/imagescale.c \ + $(MY_ROOT)/draw/imagesmooth.c \ + $(MY_ROOT)/draw/imageunpack.c \ + $(MY_ROOT)/draw/meshdraw.c \ + $(MY_ROOT)/draw/pathfill.c \ + $(MY_ROOT)/draw/pathscan.c \ + $(MY_ROOT)/draw/pathstroke.c \ + $(MY_ROOT)/draw/porterduff.c \ + $(MY_ROOT)/mupdf/pdf_annot.c \ + $(MY_ROOT)/mupdf/pdf_build.c \ + $(MY_ROOT)/mupdf/pdf_cmap.c \ + $(MY_ROOT)/mupdf/pdf_cmap_load.c \ + $(MY_ROOT)/mupdf/pdf_cmap_parse.c \ + $(MY_ROOT)/mupdf/pdf_cmap_table.c \ + $(MY_ROOT)/mupdf/pdf_colorspace.c \ + $(MY_ROOT)/mupdf/pdf_crypt.c \ + $(MY_ROOT)/mupdf/pdf_debug.c \ + $(MY_ROOT)/mupdf/pdf_font.c \ + $(MY_ROOT)/mupdf/pdf_fontagl.c \ + $(MY_ROOT)/mupdf/pdf_fontenc.c \ + $(MY_ROOT)/mupdf/pdf_fontfile.c \ + $(MY_ROOT)/mupdf/pdf_fontmtx.c \ + $(MY_ROOT)/mupdf/pdf_function.c \ + $(MY_ROOT)/mupdf/pdf_image.c \ + $(MY_ROOT)/mupdf/pdf_interpret.c \ + $(MY_ROOT)/mupdf/pdf_lex.c \ + $(MY_ROOT)/mupdf/pdf_nametree.c \ + $(MY_ROOT)/mupdf/pdf_outline.c \ + $(MY_ROOT)/mupdf/pdf_page.c \ + $(MY_ROOT)/mupdf/pdf_pagetree.c \ + $(MY_ROOT)/mupdf/pdf_parse.c \ + $(MY_ROOT)/mupdf/pdf_pattern.c \ + $(MY_ROOT)/mupdf/pdf_repair.c \ + $(MY_ROOT)/mupdf/pdf_shade.c \ + $(MY_ROOT)/mupdf/pdf_store.c \ + $(MY_ROOT)/mupdf/pdf_stream.c \ + $(MY_ROOT)/mupdf/pdf_type3.c \ + $(MY_ROOT)/mupdf/pdf_unicode.c \ + $(MY_ROOT)/mupdf/pdf_xobject.c \ + $(MY_ROOT)/mupdf/pdf_xref.c \ + $(MY_ROOT)/pregen/cmap_unicode.c \ + $(MY_ROOT)/pregen/cmap_cns.c \ + $(MY_ROOT)/pregen/cmap_gb.c \ + $(MY_ROOT)/pregen/cmap_japan.c \ + $(MY_ROOT)/pregen/cmap_korea.c \ + $(MY_ROOT)/pregen/font_base14.c \ + $(MY_ROOT)/pregen/font_cjk.c + +LOCAL_LDLIBS := -lm -llog -ljnigraphics + +include $(BUILD_STATIC_LIBRARY) diff --git a/android/jni/ThirdParty.mk b/android/jni/ThirdParty.mk new file mode 100644 index 00000000..6fa7ecf8 --- /dev/null +++ b/android/jni/ThirdParty.mk @@ -0,0 +1,150 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +MY_ROOT := ../.. + +LOCAL_CFLAGS := \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jbig2dec \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/openjpeg/libopenjpeg \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/jpeg \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/zlib \ + -I$(LOCAL_PATH)/$(MY_ROOT)/thirdparty/freetype/include \ + -DFT2_BUILD_LIBRARY -DDARWIN_NO_CARBON -DHAVE_STDINT_H + +LOCAL_MODULE := mupdfthirdparty +LOCAL_SRC_FILES := \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_arith.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_arith_int.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_arith_iaid.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_huffman.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_segment.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_page.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_symbol_dict.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_text.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_generic.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_refinement.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_mmr.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_image.c \ + $(MY_ROOT)/thirdparty/jbig2dec/jbig2_metadata.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/bio.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/cio.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/dwt.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/event.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/image.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/j2k.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/j2k_lib.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/jp2.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/jpt.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/mct.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/mqc.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/openjpeg.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/pi.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/raw.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/t1.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/t2.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/tcd.c \ + $(MY_ROOT)/thirdparty/openjpeg/libopenjpeg/tgt.c \ + $(MY_ROOT)/thirdparty/jpeg/jaricom.c \ + $(MY_ROOT)/thirdparty/jpeg/jcapimin.c \ + $(MY_ROOT)/thirdparty/jpeg/jcapistd.c \ + $(MY_ROOT)/thirdparty/jpeg/jcarith.c \ + $(MY_ROOT)/thirdparty/jpeg/jccoefct.c \ + $(MY_ROOT)/thirdparty/jpeg/jccolor.c \ + $(MY_ROOT)/thirdparty/jpeg/jcdctmgr.c \ + $(MY_ROOT)/thirdparty/jpeg/jchuff.c \ + $(MY_ROOT)/thirdparty/jpeg/jcinit.c \ + $(MY_ROOT)/thirdparty/jpeg/jcmainct.c \ + $(MY_ROOT)/thirdparty/jpeg/jcmarker.c \ + $(MY_ROOT)/thirdparty/jpeg/jcmaster.c \ + $(MY_ROOT)/thirdparty/jpeg/jcomapi.c \ + $(MY_ROOT)/thirdparty/jpeg/jcparam.c \ + $(MY_ROOT)/thirdparty/jpeg/jcprepct.c \ + $(MY_ROOT)/thirdparty/jpeg/jcsample.c \ + $(MY_ROOT)/thirdparty/jpeg/jctrans.c \ + $(MY_ROOT)/thirdparty/jpeg/jdapimin.c \ + $(MY_ROOT)/thirdparty/jpeg/jdapistd.c \ + $(MY_ROOT)/thirdparty/jpeg/jdarith.c \ + $(MY_ROOT)/thirdparty/jpeg/jdatadst.c \ + $(MY_ROOT)/thirdparty/jpeg/jdatasrc.c \ + $(MY_ROOT)/thirdparty/jpeg/jdcoefct.c \ + $(MY_ROOT)/thirdparty/jpeg/jdcolor.c \ + $(MY_ROOT)/thirdparty/jpeg/jddctmgr.c \ + $(MY_ROOT)/thirdparty/jpeg/jdhuff.c \ + $(MY_ROOT)/thirdparty/jpeg/jdinput.c \ + $(MY_ROOT)/thirdparty/jpeg/jdmainct.c \ + $(MY_ROOT)/thirdparty/jpeg/jdmarker.c \ + $(MY_ROOT)/thirdparty/jpeg/jdmaster.c \ + $(MY_ROOT)/thirdparty/jpeg/jdmerge.c \ + $(MY_ROOT)/thirdparty/jpeg/jdpostct.c \ + $(MY_ROOT)/thirdparty/jpeg/jdsample.c \ + $(MY_ROOT)/thirdparty/jpeg/jdtrans.c \ + $(MY_ROOT)/thirdparty/jpeg/jerror.c \ + $(MY_ROOT)/thirdparty/jpeg/jfdctflt.c \ + $(MY_ROOT)/thirdparty/jpeg/jfdctfst.c \ + $(MY_ROOT)/thirdparty/jpeg/jfdctint.c \ + $(MY_ROOT)/thirdparty/jpeg/jidctflt.c \ + $(MY_ROOT)/thirdparty/jpeg/jidctfst.c \ + $(MY_ROOT)/thirdparty/jpeg/jidctint.c \ + $(MY_ROOT)/thirdparty/jpeg/jquant1.c \ + $(MY_ROOT)/thirdparty/jpeg/jquant2.c \ + $(MY_ROOT)/thirdparty/jpeg/jutils.c \ + $(MY_ROOT)/thirdparty/jpeg/jmemmgr.c \ + $(MY_ROOT)/thirdparty/jpeg/jmemansi.c \ + $(MY_ROOT)/thirdparty/zlib/adler32.c \ + $(MY_ROOT)/thirdparty/zlib/compress.c \ + $(MY_ROOT)/thirdparty/zlib/crc32.c \ + $(MY_ROOT)/thirdparty/zlib/deflate.c \ + $(MY_ROOT)/thirdparty/zlib/gzclose.c \ + $(MY_ROOT)/thirdparty/zlib/gzlib.c \ + $(MY_ROOT)/thirdparty/zlib/gzread.c \ + $(MY_ROOT)/thirdparty/zlib/gzwrite.c \ + $(MY_ROOT)/thirdparty/zlib/infback.c \ + $(MY_ROOT)/thirdparty/zlib/inffast.c \ + $(MY_ROOT)/thirdparty/zlib/inflate.c \ + $(MY_ROOT)/thirdparty/zlib/inftrees.c \ + $(MY_ROOT)/thirdparty/zlib/trees.c \ + $(MY_ROOT)/thirdparty/zlib/uncompr.c \ + $(MY_ROOT)/thirdparty/zlib/zutil.c \ + $(MY_ROOT)/thirdparty/freetype/src/autofit/autofit.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftbase.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftbbox.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftbdf.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftbitmap.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftdebug.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftgasp.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftglyph.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftgxval.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftinit.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftlcdfil.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftmm.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftotval.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftpfr.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftstroke.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftsynth.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftsystem.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/fttype1.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftwinfnt.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftxf86.c \ + $(MY_ROOT)/thirdparty/freetype/src/base/ftpatent.c \ + $(MY_ROOT)/thirdparty/freetype/src/bdf/bdf.c \ + $(MY_ROOT)/thirdparty/freetype/src/cache/ftcache.c \ + $(MY_ROOT)/thirdparty/freetype/src/cff/cff.c \ + $(MY_ROOT)/thirdparty/freetype/src/cid/type1cid.c \ + $(MY_ROOT)/thirdparty/freetype/src/gzip/ftgzip.c \ + $(MY_ROOT)/thirdparty/freetype/src/lzw/ftlzw.c \ + $(MY_ROOT)/thirdparty/freetype/src/pcf/pcf.c \ + $(MY_ROOT)/thirdparty/freetype/src/pfr/pfr.c \ + $(MY_ROOT)/thirdparty/freetype/src/psaux/psaux.c \ + $(MY_ROOT)/thirdparty/freetype/src/pshinter/pshinter.c \ + $(MY_ROOT)/thirdparty/freetype/src/psnames/psnames.c \ + $(MY_ROOT)/thirdparty/freetype/src/raster/raster.c \ + $(MY_ROOT)/thirdparty/freetype/src/smooth/smooth.c \ + $(MY_ROOT)/thirdparty/freetype/src/sfnt/sfnt.c \ + $(MY_ROOT)/thirdparty/freetype/src/truetype/truetype.c \ + $(MY_ROOT)/thirdparty/freetype/src/type1/type1.c \ + $(MY_ROOT)/thirdparty/freetype/src/type42/type42.c \ + $(MY_ROOT)/thirdparty/freetype/src/winfonts/winfnt.c + +include $(BUILD_STATIC_LIBRARY) diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c new file mode 100644 index 00000000..ea11be2d --- /dev/null +++ b/android/jni/mupdf.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "fitz.h" +#include "mupdf.h" + +#define LOG_TAG "libmupdf" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) + +/* Set to 1 to enable debug log traces. */ +#define DEBUG 0 + +/* Globals */ +fz_colorspace *colorspace; +fz_glyphcache *glyphcache; +pdf_xref *xref; +int pagenum = 1; +int resolution = 160; +pdf_page *currentPage; +float pageWidth = 100; +float pageHeight = 100; + +JNIEXPORT int JNICALL Java_com_artifex_mupdf_PixmapView_mupdfOpenFile(JNIEnv * env, jobject thiz, jstring jfilename) +{ + const char *filename; + char *password = ""; + int accelerate = 1; + fz_error error; + + filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (filename == NULL) + { + LOGE("Failed to get filename"); + return 0; + } + + if (accelerate) + fz_accelerate(); + glyphcache = fz_newglyphcache(); + colorspace = fz_devicergb; + + LOGE("Opening document..."); + error = pdf_openxref(&xref, filename, password); + if (error) + { + LOGE("Cannot open document: '%s'\n", filename); + return 0; + } + + LOGE("Loading page tree..."); + error = pdf_loadpagetree(xref); + if (error) + { + LOGE("Cannot load page tree: '%s'\n", filename); + return 0; + } + LOGE("Done! %d pages", pdf_getpagecount(xref)); + + return pdf_getpagecount(xref); +} + +JNIEXPORT void JNICALL Java_com_artifex_mupdf_PixmapView_mupdfGotoPage( + JNIEnv *env, + jobject thiz, + int page) +{ + float zoom; + fz_matrix ctm; + fz_obj *pageobj; + fz_bbox bbox; + fz_error error; + + /* In the event of an error, ensure we give a non-empty page */ + pageWidth = 100; + pageHeight = 100; + + /* Free any current page */ + if (currentPage != NULL) + { + pdf_freepage(currentPage); + currentPage = NULL; + } + + LOGE("Goto page %d...", page); + pagenum = page; + pageobj = pdf_getpageobject(xref, pagenum); + if (pageobj == NULL) + return; + error = pdf_loadpage(¤tPage, xref, pageobj); + if (error) + return; + zoom = resolution / 72; + ctm = fz_translate(0, -currentPage->mediabox.y1); + ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); + ctm = fz_concat(ctm, fz_rotate(currentPage->rotate)); + bbox = fz_roundrect(fz_transformrect(ctm, currentPage->mediabox)); + pageWidth = bbox.x1-bbox.x0; + pageHeight = bbox.y1-bbox.y0; + LOGE("Success [w=%g, h=%g]", pageWidth, pageHeight); +} + +JNIEXPORT float JNICALL Java_com_artifex_mupdf_PixmapView_mupdfPageWidth( + JNIEnv *env, + jobject thiz) +{ + LOGE("PageWidth=%g", pageWidth); + return pageWidth; +} + +JNIEXPORT float JNICALL Java_com_artifex_mupdf_PixmapView_mupdfPageHeight( + JNIEnv *env, + jobject thiz) +{ + LOGE("PageHeight=%g", pageHeight); + return pageHeight; +} + + +JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_PixmapView_mupdfDrawPage( + JNIEnv *env, + jobject thiz, + jobject bitmap, + int pageW, + int pageH, + int patchX, + int patchY, + int patchW, + int patchH) +{ + AndroidBitmapInfo info; + void* pixels; + int ret, i, c; + + LOGI("In native method\n"); + if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { + LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); + return 0; + } + + LOGI("Checking format\n"); + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { + LOGE("Bitmap format is not RGBA_8888 !"); + return 0; + } + + LOGI("locking pixels\n"); + if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { + LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); + return 0; + } + + LOGI("render page\n"); + /* Call mupdf to render page */ + { + fz_error error; + fz_displaylist *list; + fz_device *dev; + + /* Render to list */ + LOGI("make list\n"); + list = fz_newdisplaylist(); + LOGI("make device\n"); + dev = fz_newlistdevice(list); + LOGI("render to device\n"); + error = pdf_runpage(xref, currentPage, dev, fz_identity); + if (error) + { + LOGE("cannot draw page %d", pagenum); + return 0; + } + LOGI("free device\n"); + fz_freedevice(dev); + + /* Render to screen */ + LOGE("Rendering page=%dx%d patch=[%d,%d,%d,%d]", + pageW, pageH, patchX, patchY, patchW, patchH); + { + float zoom; + fz_matrix ctm; + fz_bbox bbox; + fz_pixmap *pix; + float xscale, yscale; + + zoom = resolution / 72; + ctm = fz_translate(0, -currentPage->mediabox.y1); + ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); + ctm = fz_concat(ctm, fz_rotate(currentPage->rotate)); + bbox = fz_roundrect(fz_transformrect(ctm,currentPage->mediabox)); + + LOGE("mediabox=%g %g %g %g zoom=%g rotate=%d", + currentPage->mediabox.x0, + currentPage->mediabox.y0, + currentPage->mediabox.x1, + currentPage->mediabox.y1, + zoom, + currentPage->rotate); + LOGE("ctm = [%g %g %g %g %g %g] to [%d %d %d %d]", ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f, bbox.x0, bbox.y0, bbox.x1, bbox.y1); + /* Now, adjust ctm so that it would give the correct page width + * heights. */ + xscale = (float)pageW/(float)(bbox.x1-bbox.x0); + yscale = (float)pageH/(float)(bbox.y1-bbox.y0); + ctm = fz_concat(ctm, fz_scale(xscale, yscale)); + bbox = fz_roundrect(fz_transformrect(ctm,currentPage->mediabox)); + LOGE("ctm = [%g %g %g %g %g %g] to [%d %d %d %d]", ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f, bbox.x0, bbox.y0, bbox.x1, bbox.y1); + + pix = fz_newpixmapwithdata(colorspace, + patchX, + patchY, + patchW, + patchH, + pixels); + LOGE("Clearing"); + fz_clearpixmapwithcolor(pix, 0xff); + LOGE("Cleared"); + dev = fz_newdrawdevice(glyphcache, pix); + fz_executedisplaylist(list, dev, ctm); + fz_freedevice(dev); + fz_droppixmap(pix); + } + LOGE("Rendered"); + fz_freedisplaylist(list); + } + + AndroidBitmap_unlockPixels(env, bitmap); + + return 1; +} + +void android_log(char *err, int n) +{ + LOGE(err, n); +} diff --git a/android/local.properties b/android/local.properties new file mode 100644 index 00000000..93292dc5 --- /dev/null +++ b/android/local.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked in Version Control Systems, +# as it contains information specific to your local configuration. + +# location of the SDK. This is only used by Ant +# For customization when using a Version Control System, please read the +# header note. +#sdk.dir=/Library/android-sdk-mac_x86 +sdk.dir=C:\\Program Files\\android-sdk-windows diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png new file mode 100644 index 00000000..1d47b235 Binary files /dev/null and b/android/res/drawable-hdpi/icon.png differ diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png new file mode 100644 index 00000000..f9fe4d1c Binary files /dev/null and b/android/res/drawable-ldpi/icon.png differ diff --git a/android/res/drawable-mdpi/icon.png b/android/res/drawable-mdpi/icon.png new file mode 100644 index 00000000..4bb3e7cd Binary files /dev/null and b/android/res/drawable-mdpi/icon.png differ diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml new file mode 100644 index 00000000..0da95fc5 --- /dev/null +++ b/android/res/layout/main.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml new file mode 100644 index 00000000..ec3f9ae6 --- /dev/null +++ b/android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + MuPDF + diff --git a/android/res/values/theme.xml b/android/res/values/theme.xml new file mode 100644 index 00000000..68c55644 --- /dev/null +++ b/android/res/values/theme.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/src/com/artifex/mupdf/MuPDFActivity.java b/android/src/com/artifex/mupdf/MuPDFActivity.java new file mode 100644 index 00000000..f1dd1cc8 --- /dev/null +++ b/android/src/com/artifex/mupdf/MuPDFActivity.java @@ -0,0 +1,114 @@ +package com.artifex.mupdf; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Environment; +import android.view.*; +import android.view.View.OnClickListener; +import android.widget.*; +import android.widget.LinearLayout.*; +import java.io.File; + +import com.artifex.mupdf.PixmapView; + +public class MuPDFActivity extends Activity +{ + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) + { + String state = Environment.getExternalStorageState(); + + if (Environment.MEDIA_MOUNTED.equals(state)) + { + System.out.println("Media mounted read/write"); + } + else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) + { + System.out.println("Media mounted read only"); + } + else + { + System.out.println("No media at all! Bale!\n"); + return; + } + File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + File file = new File(path, "test.pdf"); + System.out.println("Trying to open "+file.toString()); + PixmapView pixmapView = new PixmapView(this, file.toString()); + super.onCreate(savedInstanceState); + + /* Now create the UI */ + RelativeLayout layout; + LinearLayout bar; + MyButtonHandler bh = new MyButtonHandler(pixmapView); + + bar = new LinearLayout(this); + bar.setOrientation(LinearLayout.HORIZONTAL); + bh.buttonStart = new Button(this); + bh.buttonStart.setText("<<"); + bh.buttonStart.setOnClickListener(bh); + bar.addView(bh.buttonStart); + bh.buttonPrev = new Button(this); + bh.buttonPrev.setText("<"); + bh.buttonPrev.setOnClickListener(bh); + bar.addView(bh.buttonPrev); + bh.buttonNext = new Button(this); + bh.buttonNext.setText(">"); + bh.buttonNext.setOnClickListener(bh); + bar.addView(bh.buttonNext); + bh.buttonEnd = new Button(this); + bh.buttonEnd.setText(">>"); + bh.buttonEnd.setOnClickListener(bh); + bar.addView(bh.buttonEnd); + + layout = new RelativeLayout(this); + layout.setLayoutParams(new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.FILL_PARENT, + RelativeLayout.LayoutParams.FILL_PARENT)); + layout.setGravity(Gravity.FILL); + + RelativeLayout.LayoutParams barParams = + new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.FILL_PARENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + barParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + bar.setId(100); + layout.addView(bar, barParams); + + RelativeLayout.LayoutParams pixmapParams = + new RelativeLayout.LayoutParams( + RelativeLayout.LayoutParams.FILL_PARENT, + RelativeLayout.LayoutParams.FILL_PARENT); + pixmapParams.addRule(RelativeLayout.ABOVE,100); + layout.addView(pixmapView, pixmapParams); + + setContentView(layout); + } + + private class MyButtonHandler implements OnClickListener + { + Button buttonStart; + Button buttonPrev; + Button buttonNext; + Button buttonEnd; + PixmapView pixmapView; + + public MyButtonHandler(PixmapView pixmapView) + { + this.pixmapView = pixmapView; + } + + public void onClick(View v) + { + if (v == buttonStart) + pixmapView.changePage(Integer.MIN_VALUE); + else if (v == buttonPrev) + pixmapView.changePage(-1); + else if (v == buttonNext) + pixmapView.changePage(+1); + else if (v == buttonEnd) + pixmapView.changePage(Integer.MAX_VALUE); + } + } +} diff --git a/android/src/com/artifex/mupdf/PixmapView.java b/android/src/com/artifex/mupdf/PixmapView.java new file mode 100644 index 00000000..80fcb5f1 --- /dev/null +++ b/android/src/com/artifex/mupdf/PixmapView.java @@ -0,0 +1,577 @@ +package com.artifex.mupdf; + +import android.app.*; +import android.os.*; +import android.content.*; +import android.content.res.*; +import android.graphics.*; +import android.util.*; +import android.view.*; +import android.widget.*; +import java.net.*; +import java.io.*; + +public class PixmapView extends SurfaceView implements SurfaceHolder.Callback +{ + private SurfaceHolder holder; + private MuPDFThread thread = null; + private boolean threadStarted = false; + + /* Constructor */ + public PixmapView(Context context, String filename) + { + super(context); + System.out.println("PixmapView construct"); + holder = getHolder(); + holder.addCallback(this); + thread = new MuPDFThread(holder, filename); + setFocusable(true); // need to get the key events + } + + /* load our native library */ + static { + System.loadLibrary("mupdf"); + } + + /* Handlers for keys - so we can actually do stuff */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (thread.onKeyDown(keyCode, event)) + return true; + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) + { + if (thread.onKeyUp(keyCode, event)) + return true; + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (thread.onTouchEvent(event)) + return true; + return super.onTouchEvent(event); + } + + public void changePage(int delta) + { + thread.changePage(delta); + } + + /* Handlers for SurfaceHolder callbacks; these are called when the + * surface is created/destroyed/changed. We need to ensure that we only + * draw into the surface between the created and destroyed calls. + * Therefore, we start/stop the thread that actually runs MuPDF on + * creation/destruction. */ + public void surfaceCreated(SurfaceHolder holder) + { + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) + { + thread.newScreenSize(width, height); + if (!threadStarted) + { + threadStarted = true; + thread.setRunning(true); + thread.start(); + } + } + + public void surfaceDestroyed(SurfaceHolder holder) + { + boolean retry = true; + thread.setRunning(false); + while (retry) + { + try + { + thread.join(); + retry = false; + } + catch (InterruptedException e) + { + } + } + threadStarted = false; + } + + class MuPDFThread extends Thread + { + private SurfaceHolder holder; + private boolean running = false; + private int keycode = -1; + private String filename; + private int screenWidth; + private int screenHeight; + private int screenGeneration; + private Bitmap bitmap; + private int numPages; + + /* The following variables deal with the size of the current page; + * specifically, its position on the screen, its raw size, its + * current scale, and its current scaled size (in terms of whole + * pixels). + */ + private int pageOriginX; + private int pageOriginY; + private float pageRawWidth; + private float pageRawHeight; + private float pageScale; + private int pageWidth; + private int pageHeight; + + /* The following variables deal with the multitouch handling */ + private final int NONE = 0; + private final int DRAG = 1; + private final int ZOOM = 2; + private int touchMode = NONE; + private float touchInitialSpacing; + private float touchDragStartX; + private float touchDragStartY; + private float touchInitialOriginX; + private float touchInitialOriginY; + private float touchInitialScale; + private PointF touchZoomMidpoint; + + /* The following control the inner loop; other events etc cause + * action to be set. The inner loop runs around a tight loop + * performing the action requested of it. + */ + private boolean wakeMe = false; + private int action; + private final int SLEEP = 0; + private final int REDRAW = 1; + private final int DIE = 2; + private final int GOTOPAGE = 3; + private int actionPageNum; + + /* Members for blitting, declared here to avoid causing gcs */ + private Rect srcRect; + private RectF dstRect; + + public MuPDFThread(SurfaceHolder holder, String filename) + { + this.holder = holder; + this.filename = filename; + touchZoomMidpoint = new PointF(0,0); + srcRect = new Rect(0,0,0,0); + dstRect = new RectF(0,0,0,0); + } + + public void setRunning(boolean running) + { + this.running = running; + } + + public void newScreenSize(int width, int height) + { + this.screenWidth = width; + this.screenHeight = height; + this.screenGeneration++; + } + + public boolean onKeyDown(int keyCode, KeyEvent msg) + { + keycode = keyCode; + return false; + } + + public boolean onKeyUp(int keyCode, KeyEvent msg) + { + return false; + } + + public synchronized void changePage(int delta) + { + action = GOTOPAGE; + if (delta == Integer.MIN_VALUE) + actionPageNum = 1; + else if (delta == Integer.MAX_VALUE) + actionPageNum = numPages; + else + { + actionPageNum += delta; + if (actionPageNum < 1) + actionPageNum = 1; + if (actionPageNum > numPages) + actionPageNum = numPages; + } + if (wakeMe) + { + wakeMe = false; + this.notify(); + } + } + + private float spacing(MotionEvent event) + { + float x = event.getX(0) - event.getX(1); + float y = event.getY(0) - event.getY(1); + return FloatMath.sqrt(x*x+y*y); + } + + private void midpoint(PointF point, MotionEvent event) + { + float x = event.getX(0) + event.getX(1); + float y = event.getY(0) + event.getY(1); + point.set(x/2, y/2); + } + + private synchronized void forceRedraw() + { + if (wakeMe) + { + wakeMe = false; + this.notify(); + } + action = REDRAW; + } + + public synchronized void setPageOriginTo(int x, int y) + { + /* Adjust the coordinates so that the page always covers the + * centre of the screen. */ + if (x + pageWidth < screenWidth/2) + { + x = screenWidth/2 - pageWidth; + } + else if (x > screenWidth/2) + { + x = screenWidth/2; + } + if (y + pageHeight < screenHeight/2) + { + y = screenHeight/2 - pageHeight; + } + else if (y > screenHeight/2) + { + y = screenHeight/2; + } + if ((x != pageOriginX) || (y != pageOriginY)) + { + pageOriginX = x; + pageOriginY = y; + } + forceRedraw(); + } + + public void setPageScaleTo(float scale, PointF midpoint) + { + float x, y; + /* Convert midpoint (in screen coords) to page coords */ + x = (midpoint.x - pageOriginX)/pageScale; + y = (midpoint.y - pageOriginY)/pageScale; + /* Find new scaled page sizes */ + synchronized(this) + { + pageWidth = (int)(pageRawWidth*scale+0.5); + if (pageWidth < screenWidth/2) + { + scale = screenWidth/2/pageRawWidth; + pageWidth = (int)(pageRawWidth*scale+0.5); + } + pageHeight = (int)(pageRawHeight*scale+0.5); + if (pageHeight < screenHeight/2) + { + scale = screenHeight/2/pageRawHeight; + pageWidth = (int)(pageRawWidth *scale+0.5); + pageHeight = (int)(pageRawHeight*scale+0.5); + } + pageScale = scale; + /* Now given this new scale, calculate page origins so that + * x and y are at midpoint */ + float xscale = (float)pageWidth /(float)pageRawWidth; + float yscale = (float)pageHeight/(float)pageRawHeight; + setPageOriginTo((int)(midpoint.x - x*xscale + 0.5), + (int)(midpoint.y - y*yscale + 0.5)); + } + } + + public void scalePageToScreen() + { + float scaleX, scaleY; + scaleX = (float)screenWidth/pageRawWidth; + scaleY = (float)screenHeight/pageRawHeight; + synchronized(this) + { + if (scaleX < scaleY) + pageScale = scaleX; + else + pageScale = scaleY; + pageWidth = (int)(pageRawWidth * pageScale + 0.5); + pageHeight = (int)(pageRawHeight * pageScale + 0.5); + pageOriginX = (screenWidth - pageWidth)/2; + pageOriginY = (screenHeight - pageHeight)/2; + forceRedraw(); + } + System.out.println("scalePageToScreen: Raw="+ + pageRawWidth+"x"+pageRawHeight+" scaled="+ + pageWidth+","+pageHeight+" pageScale="+ + pageScale); + } + + public boolean onTouchEvent(MotionEvent event) + { + int action = event.getAction(); + boolean done = false; + switch (action & MotionEvent.ACTION_MASK) + { + case MotionEvent.ACTION_DOWN: + touchMode = DRAG; + touchDragStartX = event.getX(); + touchDragStartY = event.getY(); + touchInitialOriginX = pageOriginX; + touchInitialOriginY = pageOriginY; + System.out.println("Starting dragging from: "+touchDragStartX+","+touchDragStartY+" ("+pageOriginX+","+pageOriginY+")"); + done = true; + break; + case MotionEvent.ACTION_POINTER_DOWN: + touchInitialSpacing = spacing(event); + if (touchInitialSpacing > 10f) + { + System.out.println("Started zooming: spacing="+touchInitialSpacing); + touchInitialScale = pageScale; + touchMode = ZOOM; + done = true; + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + if (touchMode != NONE) + { + System.out.println("Released!"); + touchMode = NONE; + done = true; + } + break; + case MotionEvent.ACTION_MOVE: + if (touchMode == DRAG) + { + float x = touchInitialOriginX+event.getX()-touchDragStartX; + float y = touchInitialOriginY+event.getY()-touchDragStartY; + System.out.println("Dragged to "+x+","+y); + setPageOriginTo((int)(x+0.5),(int)(y+0.5)); + done = true; + } + else if (touchMode == ZOOM) + { + float newSpacing = spacing(event); + if (newSpacing > 10f) + { + float newScale = touchInitialScale*newSpacing/touchInitialSpacing; + System.out.println("Zoomed to "+newSpacing); + midpoint(touchZoomMidpoint,event); + setPageScaleTo(newScale,touchZoomMidpoint); + done = true; + } + } + } + return done; + } + + public void run() + { + boolean redraw = false; + int patchW = 0; + int patchH = 0; + int patchX = 0; + int patchY = 0; + int localPageW = 0; + int localPageH = 0; + int localScreenGeneration = screenGeneration; + int localAction; + int localActionPageNum = 1; + numPages = mupdfOpenFile(filename); + if (numPages <= 0) + { + /* Error whilst loading file */ + } + System.out.println("File loaded: "+numPages+" pages"); + /* Set up our default action */ + action = GOTOPAGE; + actionPageNum = 1; + while (action != DIE) + { + synchronized(this) + { + while (action == SLEEP) + { + wakeMe = true; + try + { + System.out.println("Render thread sleeping"); + this.wait(); + System.out.println("Render thread woken"); + } + catch (java.lang.InterruptedException e) + { + System.out.println("Render thread exception:"+e); + } + } + /* Now we do as little as we can get away with while + * synchronised. In general this means copying any action + * or global variables into local ones so that when we + * unsynchronoise, other people can alter them again. + */ + switch (action) + { + case GOTOPAGE: + localActionPageNum = actionPageNum; + break; + case REDRAW: + /* Figure out what area of the page we want to + * redraw (in local variables, in docspace). + * We'll always draw a screensized lump, unless + * that's too big. */ + System.out.println("page="+pageWidth+","+pageHeight+" ("+pageRawWidth+","+pageRawHeight+"@"+pageScale+") @ "+pageOriginX+","+pageOriginY); + localPageW = pageWidth; + localPageH = pageHeight; + patchW = pageWidth; + patchH = pageHeight; + patchX = -pageOriginX; + patchY = -pageOriginY; + if (patchX < 0) + patchX = 0; + if (patchW > screenWidth) + patchW = screenWidth; + srcRect.left = 0; + if (patchX+patchW > pageWidth) + { + srcRect.left += patchX+patchW-pageWidth; + patchX = pageWidth-patchW; + } + if (patchY < 0) + patchY = 0; + if (patchH > screenHeight) + patchH = screenHeight; + srcRect.top = 0; + if (patchY+patchH > pageHeight) + { + srcRect.top += patchY+patchH-pageHeight; + patchY = pageHeight-patchH; + } + dstRect.left = pageOriginX; + if (dstRect.left < 0) + dstRect.left = 0; + dstRect.top = pageOriginY; + if (dstRect.top < 0) + dstRect.top = 0; + dstRect.right = dstRect.left + patchW; + srcRect.right = srcRect.left + patchW; + if (srcRect.right > screenWidth) + { + dstRect.right -= srcRect.right-screenWidth; + srcRect.right = screenWidth; + } + if (dstRect.right > screenWidth) + { + srcRect.right -= dstRect.right-screenWidth; + dstRect.right = screenWidth; + } + dstRect.bottom = dstRect.top + patchH; + srcRect.bottom = srcRect.top + patchH; + if (srcRect.bottom > screenHeight) + { + dstRect.bottom -=srcRect.bottom-screenHeight; + srcRect.bottom = screenHeight; + } + if (dstRect.bottom > screenHeight) + { + srcRect.bottom -=dstRect.bottom-screenHeight; + dstRect.bottom = screenHeight; + } + System.out.println("patch=["+patchX+","+patchY+","+patchW+","+patchH+"]"); + break; + } + localAction = action; + action = SLEEP; + } + /* In the redraw case: + * pW, pH, pX, pY, localPageW, localPageH are now all set + * in local variables, and we are safe from the global vars + * being altered in calls from other threads. This is all + * the information we need to actually do our render. + */ + switch (localAction) + { + case GOTOPAGE: + mupdfGotoPage(localActionPageNum); + pageRawWidth = mupdfPageWidth(); + pageRawHeight = mupdfPageHeight(); + scalePageToScreen(); + action = REDRAW; + break; + case REDRAW: + if ((bitmap == null) || + (bitmap.getWidth() != patchW) || + (bitmap.getHeight() != patchH)) + { + /* make bitmap of required size */ + bitmap = Bitmap.createBitmap(patchW, patchH, + Bitmap.Config.ARGB_8888); + } + System.out.println("Calling redraw native method"); + mupdfDrawPage(bitmap, localPageW, localPageH, + patchX, patchY, patchW, patchH); + System.out.println("Called native method"); + { + Canvas c = null; + try + { + c = holder.lockCanvas(null); + synchronized(holder) + { + if (localScreenGeneration == screenGeneration) + { + doDraw(c); + } + else + { + /* Someone has changed the screen + * under us! Better redraw again... + */ + action = REDRAW; + } + } + } + finally + { + if (c != null) + holder.unlockCanvasAndPost(c); + } + } + } + } + } + + protected void doDraw(Canvas canvas) + { + if ((canvas == null) || (bitmap == null)) + return; + /* Clear the screen */ + canvas.drawRGB(128,128,128); + /* Draw our bitmap on top */ + System.out.println("Blitting bitmap from "+srcRect.left+","+srcRect.top+","+srcRect.right+","+srcRect.bottom+" to "+dstRect.left+","+dstRect.top+","+dstRect.right+","+dstRect.bottom); + canvas.drawBitmap(bitmap, srcRect, dstRect, (Paint)null); + } + } + + /* These should be native functions */ + private static native int mupdfOpenFile(String filename); + private static native void mupdfGotoPage(int localActionPageNum); + private static native float mupdfPageWidth(); + private static native float mupdfPageHeight(); + private static native void mupdfDrawPage(Bitmap bitmap, + int pageW, + int pageH, + int patchX, + int patchY, + int patchW, + int patchH); +} -- cgit v1.2.3