Add a way to save to PDF in SampleApp on Android and elsewhere.
authorScroggo <Scroggo@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 21 Jun 2011 14:44:57 +0000 (14:44 +0000)
committerScroggo <Scroggo@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 21 Jun 2011 14:44:57 +0000 (14:44 +0000)
In Android, add the PDF file to downloads, so it can be opened.

Reviewed at http://codereview.appspot.com/4638052/

git-svn-id: http://skia.googlecode.com/svn/trunk@1659 2bbb7eff-a529-9590-31e7-b0007b416f81

android_sample/SampleApp/Android.mk
android_sample/SampleApp/AndroidManifest.xml
android_sample/SampleApp/jni/sample-jni.cpp
android_sample/SampleApp/res/menu/sample.xml
android_sample/SampleApp/res/values/strings.xml
android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
gyp/SampleApp.gyp
include/views/SkOSWindow_Android.h
include/views/SkWindow.h
samplecode/SampleApp.cpp
samplecode/SampleApp.h

index 6054235..4952316 100644 (file)
@@ -40,6 +40,7 @@ LOCAL_C_INCLUDES += \
     external/skia/src/core \
     external/skia/gpu/include \
     frameworks/base/opengl/include/GLES2 \
+    external/skia/include/pdf \
     $(LOCAL_PATH)/jni
 
 LOCAL_SHARED_LIBRARIES := \
@@ -60,6 +61,18 @@ LOCAL_MODULE := libskia-sample
 
 LOCAL_SRC_FILES := \
     ../../src/ports/SkXMLParser_empty.cpp \
+    ../../src/pdf/SkPDFCatalog.cpp \
+    ../../src/pdf/SkPDFDevice.cpp \
+    ../../src/pdf/SkPDFDocument.cpp \
+    ../../src/pdf/SkPDFFont.cpp \
+    ../../src/pdf/SkPDFFormXObject.cpp \
+    ../../src/pdf/SkPDFGraphicState.cpp \
+    ../../src/pdf/SkPDFImage.cpp \
+    ../../src/pdf/SkPDFPage.cpp \
+    ../../src/pdf/SkPDFShader.cpp \
+    ../../src/pdf/SkPDFStream.cpp \
+    ../../src/pdf/SkPDFTypes.cpp \
+    ../../src/pdf/SkPDFUtils.cpp \
     jni/sample-jni.cpp
 
 include external/skia/src/views/views_files.mk
index 5980d29..cf6e5a7 100644 (file)
       package="com.skia.sampleapp"
       android:versionCode="1"
       android:versionName="1.0">
+    <!-- Needed to save a file to the file system. -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <!-- Needed to add to the download manager. -->
+    <uses-permission android:name="android.permission.INTERNET" />
     <uses-sdk android:minSdkVersion="3" />
     <application android:label="@string/app_name"
                  android:debuggable="true">
index 4ba3b9d..a82256c 100644 (file)
@@ -38,11 +38,13 @@ struct ActivityGlue {
     jweak m_obj;
     jmethodID m_setTitle;
     jmethodID m_startTimer;
+    jmethodID m_addToDownloads;
     ActivityGlue() {
         m_env = NULL;
         m_obj = NULL;
         m_setTitle = NULL;
         m_startTimer = NULL;
+        m_addToDownloads = NULL;
     }
 } gActivityGlue;
 
@@ -82,6 +84,24 @@ void SkOSWindow::onHandleInval(const SkIRect& rect)
     gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval);
 }
 
+void SkOSWindow::onPDFSaved(const char title[], const char desc[],
+        const char path[])
+{
+    if (gActivityGlue.m_env) {
+        JNIEnv* env = gActivityGlue.m_env;
+        jstring jtitle = env->NewStringUTF(title);
+        jstring jdesc = env->NewStringUTF(desc);
+        jstring jpath = env->NewStringUTF(path);
+
+        env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_addToDownloads,
+                jtitle, jdesc, jpath);
+
+        env->DeleteLocalRef(jtitle);
+        env->DeleteLocalRef(jdesc);
+        env->DeleteLocalRef(jpath);
+    }
+}
+
 ///////////////////////////////////////////
 /////////////// SkEvent impl //////////////
 ///////////////////////////////////////////
@@ -151,6 +171,8 @@ JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_processSkEvent(
         JNIEnv* env, jobject thiz);
 JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_serviceQueueTimer(
         JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_saveToPdf(
+        JNIEnv* env, jobject thiz);
 };
 
 JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
@@ -217,6 +239,8 @@ JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env,
     gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz);
     gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle",
             "(Ljava/lang/CharSequence;)V");
+    gActivityGlue.m_addToDownloads = GetJMethod(env, clazz, "addToDownloads",
+            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     gActivityGlue.m_startTimer = GetJMethod(gActivityGlue.m_env, clazz,
             "startTimer", "(I)V");
     env->DeleteLocalRef(clazz);
@@ -343,3 +367,9 @@ JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_serviceQueueTimer(
 {
     SkEvent::ServiceQueueTimer();
 }
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_saveToPdf(
+        JNIEnv* env, jobject thiz)
+{
+    gWindow->saveToPdf();
+}
index 193f277..96399bd 100644 (file)
@@ -43,7 +43,8 @@
         android:id="@+id/slideshow"
         android:title="@string/slideshow"
         />
-<!--
-        android:icon="@drawable/ic_menu_new_window"
--->
+    <item
+        android:id="@+id/save_to_pdf"
+        android:title="@string/save_to_pdf"
+        />
 </menu>
index 810ff41..487f335 100644 (file)
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name">SampleApp</string>
     <string name="overview">Overview</string>
     <string name="toggle_rendering">Toggle rendering</string>
     <string name="slideshow">Slideshow</string>
     <string name="fps">FPS</string>
+    <string name="file_saved"><xliff:g id="title">%s1</xliff:g> saved!</string>
+    <string name="failed">PDF creation failed</string>
+    <string name="save_to_pdf">Save as PDF</string>
 </resources>
index 370e143..bdd9f2b 100644 (file)
@@ -17,6 +17,7 @@
 package com.skia.sampleapp;
 
 import android.app.Activity;
+import android.app.DownloadManager;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.opengl.GLSurfaceView;
@@ -32,10 +33,13 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.opengles.GL10;
 
+import java.io.File;
+
 public class SampleApp extends Activity
 {
     private TextView mTitle;
@@ -126,6 +130,15 @@ public class SampleApp extends Activity
                     }
                 });
                 return true;
+            case R.id.save_to_pdf:
+                mView.queueEvent(new Runnable() {
+                    @Override
+                    public void run() {
+                        saveToPdf();
+                    }
+                });
+                return true;
+
             default:
                 return false;
         }
@@ -164,6 +177,7 @@ public class SampleApp extends Activity
     }
 
     private static final int SET_TITLE = 1;
+    private static final int TOAST_DOWNLOAD = 2;
 
     private Handler mHandler = new Handler() {
         @Override
@@ -173,6 +187,10 @@ public class SampleApp extends Activity
                     mTitle.setText((String) msg.obj);
                     SampleApp.this.getActionBar().setSubtitle((String) msg.obj);
                     break;
+                case TOAST_DOWNLOAD:
+                    Toast.makeText(SampleApp.this, (String) msg.obj,
+                            Toast.LENGTH_SHORT).show();
+                    break;
                 default:
                     break;
             }
@@ -186,6 +204,30 @@ public class SampleApp extends Activity
 
     // Called by JNI
     @SuppressWarnings("unused")
+    private void addToDownloads(final String title, final String desc,
+            final String path) {
+        File file = new File(path);
+        final long length = file.exists() ? file.length() : 0;
+        if (length == 0) {
+            String failed = getString(R.string.failed);
+            mHandler.obtainMessage(TOAST_DOWNLOAD, failed).sendToTarget();
+            return;
+        }
+        String toast = getString(R.string.file_saved).replace("%s", title);
+        mHandler.obtainMessage(TOAST_DOWNLOAD, toast).sendToTarget();
+        final DownloadManager manager = (DownloadManager) getSystemService(
+                Context.DOWNLOAD_SERVICE);
+        new Thread("Add pdf to downloads") {
+            @Override
+            public void run() {
+                manager.addCompletedDownload(title, desc, true,
+                        "application/pdf", path, length, true);
+            }
+        }.start();
+    }
+
+    // Called by JNI
+    @SuppressWarnings("unused")
     private void startTimer(int ms) {
         // After the delay, queue an event to the Renderer's thread
         // to handle the event on the timer queue
@@ -217,6 +259,7 @@ public class SampleApp extends Activity
     native void toggleFps();
     native void processSkEvent();
     native void serviceQueueTimer();
+    native void saveToPdf();
 
     static {
         System.loadLibrary("skia-sample");
index b0df3a7..50baa62 100644 (file)
         'experimental.gyp:experimental',
         'gpu.gyp:gr',
         'gpu.gyp:skgr',
+        'pdf.gyp:pdf',
       ],
       'conditions' : [
        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
index e547609..c84ff2e 100644 (file)
@@ -29,6 +29,9 @@ public:
     void detachGL() {}
     void presentGL() {}
 
+    virtual void onPDFSaved(const char title[], const char desc[],
+        const char path[]);
+
 protected:
     // overrides from SkWindow
     virtual void onHandleInval(const SkIRect&);
index fd4ce0a..1477ec6 100644 (file)
@@ -70,6 +70,8 @@ public:
     void    preConcat(const SkMatrix&);
     void    postConcat(const SkMatrix&);
 
+    virtual void onPDFSaved(const char title[], const char desc[],
+        const char path[]) {}
 protected:
     virtual bool onEvent(const SkEvent&);
     virtual bool onDispatchClick(int x, int y, Click::State);
index 2fb73b7..2d5d0ca 100644 (file)
 
 #include "GrGLInterface.h"
 
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkStream.h"
+
 #define TEST_GPIPEx
 
 #ifdef  TEST_GPIPE
@@ -383,6 +387,9 @@ SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) {
     fZoomLevel = 0;
     fZoomScale = SK_Scalar1;
 
+    fSaveToPdf = false;
+    fPdfCanvas = NULL;
+
 //    this->setConfig(SkBitmap::kRGB_565_Config);
     this->setConfig(SkBitmap::kARGB_8888_Config);
     this->setVisibleP(true);
@@ -412,6 +419,7 @@ SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) {
 SampleWindow::~SampleWindow() {
     delete fPicture;
     delete fGpuCanvas;
+    delete fPdfCanvas;
     if (NULL != fGrContext) {
         fGrContext->unref();
     }
@@ -549,7 +557,7 @@ void SampleWindow::draw(SkCanvas* canvas) {
     } else {
         this->INHERITED::draw(canvas);
     }
-    if (fShowZoomer && fCanvasType != kGPU_CanvasType) {
+    if (fShowZoomer && fCanvasType != kGPU_CanvasType && !fSaveToPdf) {
         // In the GPU case, INHERITED::draw calls beforeChildren, which
         // creates an SkGpuCanvas.  All further draw calls are directed
         // at that canvas, which is deleted in afterChildren (which is
@@ -668,6 +676,12 @@ static void reverseRedAndBlue(const SkBitmap& bm) {
     }
 }
 
+void SampleWindow::saveToPdf()
+{
+    fSaveToPdf = true;
+    this->inval(NULL);
+}
+
 SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
     if (kGPU_CanvasType != fCanvasType) {
 #ifdef SK_SUPPORT_GL
@@ -675,47 +689,57 @@ SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
 #endif
     }
 
-    switch (fCanvasType) {
-        case kRaster_CanvasType:
-            canvas = this->INHERITED::beforeChildren(canvas);
-            break;
-        case kPicture_CanvasType:
-            fPicture = new SkPicture;
-            canvas = fPicture->beginRecording(9999, 9999);
-            break;
-        case kGPU_CanvasType: {
-            if (make3DReady()) {
-                SkDevice* device = canvas->getDevice();
-                const SkBitmap& bitmap = device->accessBitmap(true);
-
-                GrRenderTarget* renderTarget;
-
-                GrPlatformSurfaceDesc desc;
-                desc.reset();
-                desc.fSurfaceType = kRenderTarget_GrPlatformSurfaceType;
-                desc.fWidth = bitmap.width();
-                desc.fHeight = bitmap.height();
-                desc.fConfig = kRGBA_8888_GrPixelConfig;
-                desc.fStencilBits = 8;
-                GrGLint buffer;
-                GR_GL_GetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer);
-                desc.fPlatformRenderTarget = buffer;
-
-                renderTarget = static_cast<GrRenderTarget*>(
-                        fGrContext->createPlatformSurface(desc));
-                fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget);
-                renderTarget->unref();
-
-                device = new SkGpuDevice(fGrContext, renderTarget);
-                fGpuCanvas->setDevice(device)->unref();
-
-                fGpuCanvas->concat(canvas->getTotalMatrix());
-                canvas = fGpuCanvas;
-
-            } else {
+    if (fSaveToPdf) {
+        const SkBitmap& bmp = canvas->getDevice()->accessBitmap(false);
+        SkISize size = SkISize::Make(bmp.width(), bmp.height());
+        SkPDFDevice* pdfDevice = new SkPDFDevice(size, size,
+                canvas->getTotalMatrix());
+        fPdfCanvas = new SkCanvas(pdfDevice);
+        pdfDevice->unref();
+        canvas = fPdfCanvas;
+    } else {
+        switch (fCanvasType) {
+            case kRaster_CanvasType:
                 canvas = this->INHERITED::beforeChildren(canvas);
+                break;
+            case kPicture_CanvasType:
+                fPicture = new SkPicture;
+                canvas = fPicture->beginRecording(9999, 9999);
+                break;
+            case kGPU_CanvasType: {
+                if (make3DReady()) {
+                    SkDevice* device = canvas->getDevice();
+                    const SkBitmap& bitmap = device->accessBitmap(true);
+
+                    GrRenderTarget* renderTarget;
+
+                    GrPlatformSurfaceDesc desc;
+                    desc.reset();
+                    desc.fSurfaceType = kRenderTarget_GrPlatformSurfaceType;
+                    desc.fWidth = bitmap.width();
+                    desc.fHeight = bitmap.height();
+                    desc.fConfig = kRGBA_8888_GrPixelConfig;
+                    desc.fStencilBits = 8;
+                    GrGLint buffer;
+                    GR_GL_GetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer);
+                    desc.fPlatformRenderTarget = buffer;
+
+                    renderTarget = static_cast<GrRenderTarget*>(
+                            fGrContext->createPlatformSurface(desc));
+                    fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget);
+                    renderTarget->unref();
+
+                    device = new SkGpuDevice(fGrContext, renderTarget);
+                    fGpuCanvas->setDevice(device)->unref();
+
+                    fGpuCanvas->concat(canvas->getTotalMatrix());
+                    canvas = fGpuCanvas;
+
+                } else {
+                    canvas = this->INHERITED::beforeChildren(canvas);
+                }
+                break;
             }
-            break;
         }
     }
 
@@ -738,6 +762,34 @@ static void paint_rgn(const SkBitmap& bm, const SkIRect& r,
 }
 
 void SampleWindow::afterChildren(SkCanvas* orig) {
+    if (fSaveToPdf) {
+        fSaveToPdf = false;
+        if (fShowZoomer) {
+            showZoomer(fPdfCanvas);
+        }
+        SkString name;
+        name.printf("%s.pdf", this->getTitle());
+        SkPDFDocument doc;
+        SkPDFDevice* device = static_cast<SkPDFDevice*>(fPdfCanvas->getDevice());
+        doc.appendPage(device);
+#ifdef ANDROID
+        name.prepend("/sdcard/");
+#endif
+        SkFILEWStream stream(name.c_str());
+        if (stream.isValid()) {
+            doc.emitPDF(&stream);
+            const char* desc = "File saved from Skia SampleApp";
+            this->onPDFSaved(this->getTitle(), desc, name.c_str());
+        }
+        delete fPdfCanvas;
+        fPdfCanvas = NULL;
+
+        // We took over the draw calls in order to create the PDF, so we need
+        // to redraw.
+        this->inval(NULL);
+        return;
+    }
+
     if (fRequestGrabImage) {
         fRequestGrabImage = false;
 
@@ -1005,6 +1057,9 @@ bool SampleWindow::onHandleChar(SkUnichar uni) {
         case 'd':
             SkGraphics::SetFontCacheUsed(0);
             return true;
+        case 'e':
+            this->saveToPdf();
+            break;
         case 'f':
             this->toggleFPS();
             break;
index 25e3964..a58c688 100644 (file)
@@ -60,6 +60,7 @@ public:
     bool previousSample();
     bool handleTouch(int ownerId, float x, float y,
             SkView::Click::State state);
+    void saveToPdf();
 
 protected:
     virtual void onDraw(SkCanvas* canvas);
@@ -98,6 +99,9 @@ private:
     };
     CanvasType fCanvasType;
 
+    bool fSaveToPdf;
+    SkCanvas* fPdfCanvas;
+
     bool fUseClip;
     bool fNClip;
     bool fRepeatDrawing;