2013年12月16日 星期一

使用 javah 產生 Native Support / JNI 的標頭檔

在《從 HelloJNI 開始 Android NDK 的使用》這篇文章裡,可以看到 HelloJNI++ 專案的 jni/hellojni.c 裡實作了這段程式碼:

jstring
Java_demo_example_hellojni_HelloActivity_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
} 

而在《使用 C++ 改寫 HelloJNI++》這篇文章裡,也可以在 jni/hellojni.cpp 看到了這段程式碼:

static const char *classPathName = "demo/example/hellojni/HelloActivity";

static JNINativeMethod methods[] = {
  {"stringFromJNI", "()Ljava/lang/String;", (void*)Java_demo_example_hellojni_HelloActivity_stringFromJNI },
}; 

這兩處的程式碼,都是對應到 HelloJNI++ 專案的 Java 程式碼:

package demo.example.hellojni;

public class HelloActivity extends Activity {

    public native String stringFromJNI();

} 

那麼,這裡的 Java_demo_example_hellojni_HelloActivity_stringFromJNI()Ljava/lang/String; 如何定義呢?在網路上找到的《JNI Examples for Android》文件提供了說明;此外,也可以使用 Java JDK 的 javah 這支程式來產生。

首先,我們來看一下 javah 的用途:

Usage: javah [options] <classes>

where [options] include:

        -help                 Print this help message and exit
        -classpath <path>     Path from which to load classes
        -bootclasspath <path> Path from which to load bootstrap classes
        -d <dir>              Output directory
        -o <file>             Output file (only one of -d or -o may be used)
        -jni                  Generate JNI-style header file (default)
        -version              Print version information
        -verbose              Enable verbose output
        -force                Always write output files

<classes> are specified with their fully qualified names (for
instance, java.lang.Object). 

javah 預設的用途,就是用來生成 JNI-style 的標頭檔。

那麼,我們來看一下怎麼使用它吧!最簡單的方法,開啟一個終端程式,然後到 Android 應用程式的專案路徑執行 javah 就可以了,例如以 HelloJNI++ 這個專案來示範。

方法非常簡單,在 HelloJNI++ 專案的路徑執行這個指令:

$ javah -classpath bin/classes -d jni/ demo.example.hellojni.HelloActivity 

其中,要注意的是 classpath 的路徑要指向 bin/classes,而不是 src/ 。執行完成後,就會在 jni/ 路徑下看到生成的標頭檔 demo_example_hellojni_HelloActivity.h 了。標頭檔 demo_example_hellojni_HelloActivity.h 的內容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class demo_example_hellojni_HelloActivity */

#ifndef _Included_demo_example_hellojni_HelloActivity
#define _Included_demo_example_hellojni_HelloActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     demo_example_hellojni_HelloActivity
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_demo_example_hellojni_HelloActivity_stringFromJNI
  (JNIEnv *, jobject);

/*
 * Class:     demo_example_hellojni_HelloActivity
 * Method:    unimplementedStringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_demo_example_hellojni_HelloActivity_unimplementedStringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif 

往後,只要在 Java 程式碼裡宣告了 native 的函式,那麼就需要使用 javah 來重新生成這個標題檔,很容易吧!

接著再來看看,既然使用了 Eclipse 這個開發環境,那麼能不能直接將 javah 的動作整合到 Eclipse 裡呢?方法就是使用 Eclipse 的 External Tools。

從 Eclipse 的 Run 下拉選單找到 External Tools,然後點擊 External Tools Configurations。

然後選擇 Program,並新建一個設定。

接著,開始填入以下內容:

  1. Name 的部份任意填入一個容易分辨的名稱。
  2. Location 填入 javah 的路徑,由於我在 Linux 下操作,因此一般為 /usr/bin/javah
  3. Working Directory 填入 ${workspace_loc:/HelloJNI++/bin},其中 HelloJNI++ 對應到 Android 應用程式專案的名稱。也可以按下 Browse Worksapce 鈕來選擇。
  4. Arguments 填入 -classpath ${workspace_loc:/HelloJNI++/bin/classes} -d ${workspace_loc:/HelloJNI++/jni} demo.example.hellojni.HelloActivity,這個是提供給 javah 的參數,其中 HelloJNI++ 同樣對應到 Android 應用程式專案的名稱。

接著,到 Refresh 頁面,勾選 Specific resources,然後按下右側的 Specific Resources 鈕,並將 HelloJNI++ 專案裡的 jni 及 src 兩個目錄勾選起來。

設定完成後,只要從 External Tools 執行這個項目,就會生成 HelloJNI++ 專案的 JNI 標頭檔了。

2013年12月14日 星期六

使用 C++ 改寫 HelloJNI++

在《從 HelloJNI 開始 Android NDK 的使用》這篇文章裡,JNI 裡的 Native Library 是以 C 完成的。接著,我們試著將它改成由 C++ 來完成。

首先,將 jni/hellojni.c 改名稱為 jni/hellojni.cpp,然後同時修改 jni/Android.mk 的內容:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hellojni
LOCAL_SRC_FILES := hellojni.cpp

include $(BUILD_SHARED_LIBRARY) 

接著,修改 jni/hellojni.cpp 的內容:

#include <jni.h>
#include <string.h>
#include <android/log.h>

jstring
Java_demo_example_hellojni_HelloActivity_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
 return env->NewStringUTF("Hello from JNI !");
} 

簡單說,請是將原本的 return (*env)->NewStringUTF(env, "Hello from JNI !"); 改成 return env->NewStringUTF("Hello from JNI !");

接著,執行 Build Project。但,此時可能會出現這樣的錯誤:

這是因為在 AndroidManifest.xml 指定的 minSdkVersion 和使用的 SDK 版本不一樣。

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" /> 

解決的方法很簡單,新建一個 jni/Application.mk,然後填入內容:

APP_PLATFORM := android-8 

這裡使用的版本必須和 minSdkVersion 相同。

最後,將編譯出來的 HelloJNI++.apk 下載到模擬器執行,卻出現了錯誤的狀況。

而,從 Logcat 也可以查看到以下的錯誤訊息:

E/AndroidRuntime(1365): FATAL EXCEPTION: main
E/AndroidRuntime(1365): Process: demo.example.hellojni, PID: 1365
E/AndroidRuntime(1365): java.lang.UnsatisfiedLinkError: Native method not found: demo.example.hellojni.HelloActivity.stringFromJNI:()Ljava/lang/String;
E/AndroidRuntime(1365):     at demo.example.hellojni.HelloActivity.stringFromJNI(Native Method)
E/AndroidRuntime(1365):     at demo.example.hellojni.HelloActivity.onCreate(HelloActivity.java:20)
E/AndroidRuntime(1365):     at android.app.Activity.performCreate(Activity.java:5243)
E/AndroidRuntime(1365):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
E/AndroidRuntime(1365):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
E/AndroidRuntime(1365):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
E/AndroidRuntime(1365):     at android.app.ActivityThread.access$700(ActivityThread.java:135)
E/AndroidRuntime(1365):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
E/AndroidRuntime(1365):     at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(1365):     at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(1365):     at android.app.ActivityThread.main(ActivityThread.java:4998)
E/AndroidRuntime(1365):     at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(1365):     at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(1365):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
E/AndroidRuntime(1365):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
E/AndroidRuntime(1365):     at dalvik.system.NativeStart.main(Native Method) 

在網路上可以找到一份《JNI Examples for Android》的文件,參考它的內容實作 JNI OnLoad() 的部份能處理這個問題。至於實作的內容,在 Android 的原始碼裡可以找到一份 SimpleJNI 的範例,在 https://github.com/android/platform_development/tree/master/samples/SimpleJNI 這裡可以查看程式碼。根據範例裡的 jni/native.cpp,我們可以修改 HelloJNI++ 的 jni/hellojni.cpp 的內容:

#include <jni.h>
#include <string.h>
#include <android/log.h>
#include <stdio.h>

#define LOG_TAG "HelloJNI++: hellojni.cpp"

jstring
Java_demo_example_hellojni_HelloActivity_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return env->NewStringUTF("Hello from JNI !");
}

static const char *classPathName = "demo/example/hellojni/HelloActivity";

static JNINativeMethod methods[] = {
  {"stringFromJNI", "()Ljava/lang/String;", (void*)Java_demo_example_hellojni_HelloActivity_stringFromJNI },
};

/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

/*
 * Register native methods for all classes we know about.
 *
 * returns JNI_TRUE on success.
 */
static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }

  return JNI_TRUE;
}


// ----------------------------------------------------------------------------

/*
 * This is called by the VM when the shared library is first loaded.
 */

typedef union {
    JNIEnv* env;
    void* venv;
} UnionJNIEnvToVoid;

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv* env = NULL;

    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "JNI_OnLoad");

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
        goto bail;
    }
    env = uenv.env;

    if (registerNatives(env) != JNI_TRUE) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: registerNatives failed");
        goto bail;
    }

    result = JNI_VERSION_1_4;

bail:
    return result;
} 

此外,這裡用了 __android_log_print ,請再參考《在 JNI 程式碼使用 Logcat》修改 Android.mk,如此就完成了。

2013年12月13日 星期五

從 HelloJNI 開始 Android NDK 的使用

安裝及設定工具

從某個版本開始,在 Eclipse 的 Android Development Tools (ADT) 已經包含了對 NDK 的支援。因此在安裝 Android Development Tools (ADT) 時可以一併將 NDK plugins 安裝起來。

接著,在 Eclipse 的 Preferences 裡指定 Android SDK 的路徑,並指定 Android NDK 的路徑。在這裡,我分別將 Android SDK 及 Android NDK 放置在 /opt/android/android-sdk-linux/opt/android/android-ndk-r9b 這兩個路徑。

Android 應用程式專案設定

接著,就可以開始在 Android 應用程式專案裡使用了 Android NDK 了。

舉例來說,我建立了一個名字叫「HelloJNI++」的專案,在專案上點滑鼠右鍵,選擇 Android Tools 底下的 Add Native Support 來加入 JNI 的程式碼。

加入 Native Support 時,首先要先輸入 Native Library 的名字。這裡,我使用 hellojni 作為 Native Library 的名字,於是它將會產生一個名為 libhellojni.so 的檔案。

接著,這個步驟非常重要。在專案的 Properties 裡,找到 C/C++ General 裡的 Paths and Symbols,並在 Includes 裡加入 Android NDK 的標頭檔路徑。例如,這個專案指定的 Android API 為 19 (Android 4.4),因此我加入了 /opt/android/android-ndk-r9b/platforms/android-19/arch-arm/usr/include 這個路徑。

這個步驟非常重要,不可以省略!

範例程式碼

以下就可以開始在這個專案填入程式碼了。

Android Development Tools (ADT) 的 NDK plugins 預設會依 Native Library 的名字先建立一個 .cpp 的檔案。在這篇文章裡,由於我將 Native Library 的名字取作 hellojni,因此它會建立 jni/hellojni.cpp 這個檔案。這裡我將它更名為 jni/hellojni.c,並對應修改 jni/Android.mk 的內容:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hellojni
LOCAL_SRC_FILES := hellojni.c

include $(BUILD_SHARED_LIBRARY) 

接著,修改專案的 Java 程式碼,內容如下:

package demo.example.hellojni;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class HelloActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.hello, menu);
        return true;
    }
    
    public native String stringFromJNI();

    static {
        System.loadLibrary("hellojni");
    }        
} 

然後,修改 jni/hello.c,將 stringFromJNI() 實作出來。

#include <jni.h>
#include <string.h>
#include <android/log.h>

jstring
Java_demo_example_hellojni_HelloActivity_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
} 

最後,從 Eclipse 的 Project 下拉選單裡執行 Build Project,就完成編譯了。然後,可以將編出來的 HelloJNI++.apk 下載到模擬器或 Android 手機上執行驗證。

2013年12月12日 星期四

[彰化] 旅。咖啡

知道彰市化開了家「旅。咖啡」已經一陣子了,只是一直沒空來坐坐。前些日子,因為一個活動試喝了「旅。咖啡」的花生拿鐵,及聽了店老闆小王子分享搶救「台銀日式舊宿舍群」的故事,於是日前趁著傍晚回家前的空檔繞過來坐坐。

店裡的裝璜用了許多頗具歷史故事的物件,給人一種很舒服、溫暖的感覺。也許因為我也極為喜好這類東西吧!簡直是第一眼就愛上了這裡的裝璜了。

由於日前試喝過「花生拿鐵」,於是第一次來訪我決定試試另一個特別的產品「麵茶咖啡」。但,其實在隔天傍晚,我又跑來完整品嘗了一杯「花生拿鐵」。這兩個特色產品的口感都很棒,我非常推薦第一次來訪的朋友可以先從這兩個特色產品開始品嘗。

店裡的裝璜很有歷史的味道,牆上還有之前搶救「台銀日式舊宿舍群」時的相片。

啊!我超愛這樣的燈耶!

牆上這個也是在台銀日式舊宿舍群拆除時,從怪手中搶救回來的,若你有機會遇到小王子,可以請他好好跟你介紹這東西的來歷唷!

有時我會懷疑,在遠古的時代,我是不是和蠹魚妖有什麼關係呢?因為,我也超愛這樣有歷史味道的書本。這幾本書基本上已經快散了,要小心愛護唷!

靜下心看看這面牆,是不是感覺很棒呢?對了,牆上那個鐘是要上發條的,整點時還會響呢!那個聲音一整個很好聽。說實話,這個鐘讓我回想起以前家裡那個老舊、有單擺的掛鐘了。

晚上時,從外面看起來也很漂亮!對了,店裡面向窗外的那張長桌,桌面可是直接用一塊門板製作的,所以在桌面上還有一個鐵環呢!其實,店裡另外有三張方形桌子也是將門板鋸開製作成的。其中有一桌的桌面上,還有以前貼門神的痕跡唷!

旅。咖啡

地址:彰化市民權路241號

電話:04-7282972

email: tripcafetw@gmail.com

營業時間:週二~週日 10:00-22:00

部落格:http://trip-cafe-tw.blogspot.tw/

小王子的浮世隨筆Ⅱ.0 http://seo-worker.blogspot.tw/

2013年12月10日 星期二

在 JNI 程式碼使用 Logcat

在 Android 應用的程式碼裡,可以使用 Log.v()、Log.d()、Log.i() 等函式將訊息送到 log output,然後開發者可以透過 Logcat 查看。

但,如果在 Android 應用裡使用了 JNI 及 NDK,又該如何將訊息送到 log output,才能在 Logcat 裡查看呢?

首先,查看了 NDK 的 docs/STABLE-APIS.html,找到這段說明:

Android-specific Log Support:
<android/log.h> contains various definitions that can be used to send log messages to the kernel from your native code. Please have a look at its content in (platforms/android-3/arch-arm/usr/include/android/log.h), which contain many informative comments on how to use it.
You should be able to write helpful wrapper macros for your own usage to access this facility.
If you use it, your native module should link to /system/lib/liblog.so with:
    LOCAL_LDLIBS := -llog 

然後在 android/log.h 裡找到:

/*
 * Android log priority values, in ascending priority order.
 */
typedef enum android_LogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
} android_LogPriority;

/*
 * Send a formatted string to the log, used like printf(fmt,...)
 */
int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
#if defined(__GNUC__)
    __attribute__ ((format(printf, 3, 4)))
#endif
    ; 

接著,拿 NDK samples 裡的 hello-jni 來試試。首先,修改 jni/Android.mk,加入這行:

LOCAL_LDLIBS += -llog 

再修改 jni/hello-jni.c,除了引入 android/log.h 這個標頭檔,並使用 __android_log_print 將訊息送到 log output。

#include <android/log.h>

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    __android_log_print(ANDROID_LOG_INFO, "HelloJNI", "Show: Hello from JNI !");
    return (*env)->NewStringUTF(env, "Hello from JNI !");
} 

如此,當這隻程式執行時,就可以在 Logcat 裡查看由 __android_log_print 送出的訊息了。

2013年12月4日 星期三

使用 PhotoShop 製作暗角效果

使用 PhotoShop 在相片上製作暗角並不是很麻煩的事,利用「光源效果」這個濾鏡就可以達成了。

首先在 PhotoShop 上開啟一張相片,然後從下拉選單的「濾鏡」,找到「演算上色」裡的「光源效果」:

點擊光源效果這個濾鏡後,在 PhotoShop 的左上方會出現幾個預設集,這裡直接選擇「手電筒」這個預設集。

當選擇不同的預設集時,在 PhotoShop 右側的這個欄位也會隨著改變,使用者也可以直接從這裡修改想要的光源效果。

而在圖上會出現像這樣的圈,它可以用來調整光源的大小(縮放)、明暗度和位置。

通常我喜歡將暗角效果弄明顯一點。簡單講,就是讓它暗一點。

最後,再用筆刷將圖片中央範圍的效果刷淡一些,完成的效果就像這樣囉!

2013年12月3日 星期二

自製 PhotoScape 的版型

PhotoScape 提供了「版型」這個選項,讓使用者可以將多張相片排列成一張圖片。這陣子以來,這個功能也是我時常使用的一個,讀者可以參閱《使用 PhotoScape 製作活動記錄或花絮合輯》這篇文章。

然而,使用一段時間之後,總覺得 PhotoScape 預設提供的版型選項有點不足。於是,我便開始在 Google 上尋找如何製作版型的相關文章或討論。終於讓我找到了《[實用] PhotoScpae自製版型》這篇文章。在該篇文章裡以一個較簡單的例子說明了如何製作版型;而這裡我則乾脆找一個複雜點的版型,再說明一下吧!

首先,所有的版型都會被放置在 PhotoScape 安裝目錄內的 page 目錄下。由於我的 Windows 是 64 位元的,所以可以在 C:\Program Files (x86)\PhotoScape\page 找到所有的版型檔案。這時可以看到版型的副檔名為 .pgt,這其實只是一個純文字檔案而已。所以,使用者可以很輕易地用「記事本 (Notepad.exe)」這個軟體,或使用 VIM 打開。

接著,來看一個我比較常用的版型。

  1. 這個版型可放置 16 張相片。
  2. 版型檔案的主檔名 (main_e_02) 和檔案裡描述的 title 相符。
  3. 版型預設大小為 400X400,所以在版型的檔案裡描述了 page_size=400 400

基本上,搞懂這些後,就可以製作一個版型囉!

最後,來看一個自製的版型效果,同時我也將這個版型的內容附上來供大家參考。

version=1.0
title=7X8_32_1
page_type=blank
page_size=700 800
page_entity=photo rect 0 0 200 200
page_entity=photo rect 200 0 100 100
page_entity=photo rect 300 0 100 100
page_entity=photo rect 400 0 100 100
page_entity=photo rect 500 0 200 200

page_entity=photo rect 200 100 100 100
page_entity=photo rect 300 100 200 200

page_entity=photo rect 0 200 100 100
page_entity=photo rect 100 200 200 200
page_entity=photo rect 500 200 100 100
page_entity=photo rect 600 200 100 100

page_entity=photo rect 0 300 100 100
page_entity=photo rect 300 300 100 100
page_entity=photo rect 400 300 200 200
page_entity=photo rect 600 300 100 100

page_entity=photo rect 0 400 100 100
page_entity=photo rect 100 400 100 100
page_entity=photo rect 200 400 100 100
page_entity=photo rect 300 400 100 100
page_entity=photo rect 600 400 100 100

page_entity=photo rect 0 500 100 100
page_entity=photo rect 100 500 200 200
page_entity=photo rect 300 500 100 100
page_entity=photo rect 400 500 100 100
page_entity=photo rect 500 500 200 200

page_entity=photo rect 0 600 100 100
page_entity=photo rect 300 600 200 200

page_entity=photo rect 0 700 100 100
page_entity=photo rect 100 700 100 100
page_entity=photo rect 200 700 100 100
page_entity=photo rect 500 700 100 100
page_entity=photo rect 600 700 100 100 

2013年12月2日 星期一

如何從 Linux Mint 15 升級至 Linux Mint 16?

我猜想,最近 Linux 玩家關注的消息或許是 Linux Mint 16 終於正式發佈了:

這幾天大概 Linux Mint 的伺服器會很忙,因為有不少玩家想下載試試或升級的。好吧!我就是其中一個想從 Linux Mint 15 升級到 Linux Mint 16 的人。在 Linux Mint 網站的文章裡提到,想升級的使用者可以參閱這篇文章的內容:

基本上,該篇文章介紹了 "Fresh" 升級的方式。這個方式簡單說就是下載 Linux Mint 的 LiveCD,然後重新安裝並覆蓋原來的系統。但,其實我比較喜歡採用 "Package" 升級的方式,也就是直接使用 APT 指令來做。之前在 Debian GNU/Linux 已經嘗試過幾個版本的跨版本升級了,並沒有遇到什麼問題;在 Linux Mint 目前則是頭一遭,希望也是能順利完成呀!

首先,我們必須修改 sources.list 的內容,找到 /etc/apt/sources.list.d/official-package-repositories.list,然後將 olivia 修改成 petra;並將 raring 修改成 saucy。其中,Petra 是 Linux Mint 16 的代碼;而 Saucy 是 Ubuntu 13.10 的代碼。

$ sudo sed -i 's/raring/saucy/' /etc/apt/sources.list.d/official-package-repositories.list

$ sudo sed -i 's/olivia/petra/' /etc/apt/sources.list.d/official-package-repositories.list

接著執行 APT 指令升級系統:

$ sudo apt-get update; sudo apt-get dist-upgrade

其實,直接編輯 /etc/apt/sources.list 加入 Linux Mint 16 需要的套件庫就可以了。不過由於 Linux Mint 的 /etc/apt/sources.list 實值上是空的,所有的套件來源都放在 /etc/apt/sources.list.d 這個目錄裡,因此才會改成修改 /etc/apt/sources.list.d/official-package-repositories.list 這個檔案。

2013年11月28日 星期四

使用 PhotoCap 製作不規則的拼貼圖

PhotoCap 是由國人 Johnson Wang 所開發的一個相片編修軟體,使用者可以在 http://www.photocap.com.tw/forum/ 這個網站找到更多有關於 PhotoCap 的討論,也可以在該網站下載到目前最新的版本 (PhotoCap 6.0)

在這篇文章裡,我將示範如何使用 PhotoCap 來製作相片拼貼。首先,從下拉式選單「功能」找到「照片拼貼」:

接著會出現載入相片的對話框,按下左上方的「載入照片」:

然後選擇要加入拼貼的相片:

載入相片後,若選擇「開啟隨機角度」,那麼就可以製作不規則排列的拼貼了。在這個對話框只是先做隨機的拼貼,按下 OK 後,會進入 PhotoCap 的編修視窗。

在編修視窗裡,右側會將所有加入的相片各自成為一個圖層,此外也可以在調整每一張相片的大小、角度和位置。

這篇文章一開始我使用了白色的背景,若一開始選擇「照片拼貼」前先打開一張相片,那麼就可以用那張相片作為拼貼的背景圖了。

最後,來看一下拼貼完成的照片吧!