Skip to main content

How to Establish a Different Picture of the Android Camera by Image Processing (Gray and Comic)?

How to Establish a Different Picture of the Android Camera by Image Processing (Gray and Comic)?

How to Establish a Different Picture of the Android Camera by Image Processing (Gray and Comic)?

Introduction

Sometimes, we just feel fun to take a picture. Let’s take a different funny picture. We can develop by self. To design gray and comic style picture, it is not hard. You just need to own an Android phone and you can establish a funny camera app.

Normally, you will encounter auto focus when you take a picture. This problem can be solved in this study. Firstly, while we can be waiting the auto focus to finish when we take a picture. That is why need to overwrite auto focus method to help us take a clear picture. Secondly, while the image processing had compute when we need to transform raw data to bitmap format. We used to bitmap factory to help us do this transform. Thirdly, there are two image processing that was chosen by users. The gray scale and comic effect in this study. The gray scale is very simple that is just getting R, G and B of amount and then divide by three. That is your new pixels of gray scale, but we still need to know how to get R, G and B of channel respectively. We are getting a pixel value after use the Color method to help us get R, G and B of values. There is a difficult convolution processing about comic effect. The comic progress is two processing, including Sobel filter and ordered dither processing. We need to combine the two results of the image processing to establish a new image, that is called comic effect.

If you want to develop more image effect, you could use this code of architecture. To keep developing your new idea. Because there are preparing auto focus and take a picture of callback function. You just follow this architecture and then to add new image effect.

In this study, this app will open your camera before you choice image effect and then you have chosen an effect gray scale or comic. You can press 'take a picture' button and then you can watch a processed image. Such as those figures.

To develop this app just only two files, It is very simple, that is why I wrote this article. Because to help beginners in the camera app development. All code presents this article. I need to, you're a little patient to read. And then you will find out that so easy.

Equipment

Operation System: Android 4.1.2
Development Utility: Eclipse ADT

Usage


File Name: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.style.camera"
    android:versionCode="1"
    android:versionName="1.0" >

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.style.camera.MainActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
       <! -- We should set those permissions. -->
	<uses-permission android:name="android.permission.CAMERA"></uses-permission>
	<uses-feature android:name="android.hardware.camera" />
	<uses-feature android:name="android.hardware.camera.autofocus" />
</manifest>


File Name: MainActivity.java
public class MainActivity extends Activity implements SurfaceHolder.Callback  {

    SurfaceView mSurfaceView ;
    Button btn_Capture;        
    Camera mCamera;    
    PictureCallback mPictureCB;
    AutoFocusCallback mAutoFocusCB;
    ImageView ImgView;
    TextView txtView;
    Bitmap bitmapClone;
    RadioGroup rdg_Main;
    RadioButton rdb_Gray;
    RadioButton rdb_Comic;
    int iImageProcessingId; 

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Set the format of window, as per the PixelFormat types. 
        // This overrides the default format that is selected by 
        // the Window based on its window decorations.
        // System chooses a format that supports translucency (many alpha bits). 
        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        // Enable extended window features. 
        // Flag for the "no title" feature, turning off the title at the top of the screen.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // Set the flags of the window, as per the WindowManager.LayoutParams flags.
        // Window flag: Hide all screen decorations (e.g. status bar). 
        // while this window is displayed. This allows the window to use 
        // the entire display space for itself -- the status bar will be 
        // hidden when an app window with this flag set is on the top layer. 
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);           
        // Set the activity content from a layout resource. 
        // The resource will be inflated, adding all top-level views to the activity.
        setContentView(R.layout.activity_main);
        // Change the desired orientation of this activity. 
        // If the activity is currently in the foreground or 
        // otherwise impacting the screen orientation, 
        // the screen will immediately be changed 
        // (possibly causing the activity to be restarted). 
        // Otherwise, this will be used the next time the activity is visible.
        // Constant corresponding to portrait in the android.R.attr.screenOrientation attribute.
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        // -- (Start)
        ImgView = (ImageView)this.findViewById(R.id.ImgView);
        txtView = (TextView)this.findViewById(R.id.txtView);
        btn_Capture = (Button)this.findViewById(R.id.btn_Capture);
        mSurfaceView  = (SurfaceView)this.findViewById(R.id.surView_Camera); 
        rdg_Main = (RadioGroup) findViewById (R.id.rdg_Main);
        rdb_Gray = (RadioButton) findViewById (R.id.rdb_Gray);
        rdb_Comic = (RadioButton) findViewById (R.id.rdb_Comic);
        // -- (End)
        // Set and get SurfaceHolder
        // Abstract interface to someone holding a display surface. 
        // Allows you to control the surface size and format, 
        // edit the pixels in the surface, and monitor changes to the surface. 
        // This interface is typically available through the SurfaceView class. 
        // When using this interface from a thread other than the one running 
        // its SurfaceView, you will want to carefully read the methods 
        // lockCanvas and Callback.surfaceCreated().
        SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder(); 
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        // To record the choice of image processing by the user 
        iImageProcessingId = 0;

        // To listen choice of the user and record it.
        // There are two choices, including Gray and Comic.
        rdg_Main.setOnCheckedChangeListener( new RadioGroup.OnCheckedChangeListener() {


        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            // TODO Auto-generated method stub
            // Gray
            if ( checkedId == rdb_Gray.getId() )
            {
                iImageProcessingId = 0;
            }
            // Comic
            else if ( checkedId == rdb_Comic.getId() )
            {
                iImageProcessingId = 1;
            }
        }
        });

        // To establish Camera.takePicture callback function.
        mPictureCB = new PictureCallback(){
        // Image processing.
        // Overwrite onPictureTake function.
        @Override
        public void onPictureTaken(byte[] data, Camera camera){
               // We use the BitmapFactory to decode become raw data to bitmap format.
               Bitmap mBitmap = BitmapFactory.decodeByteArray(data, 0 , data.length);
               bitmapClone = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), mBitmap.getConfig());
               bitmapClone.copy(mBitmap.getConfig(), true);
               // For debug of switch.
               if ( true )
               {
                   int iY = 0;
                   int iX = 0;
                   int iPixel = 0;
                   int iRed = 0;
                   int iGreen = 0;
                   int iBlue = 0;
                   int iRGBAvg = 0;
                   // Gray of image processing.
                   if ( iImageProcessingId == 0 )
                   {
                          // The height of the image
                          for ( iY = 0; iY < bitmapClone.getHeight(); iY++ )
                          {
                              // The width of the image
                              for ( iX = 0; iX < bitmapClone.getWidth(); iX++ )
                              {
                                  // To get pixel.
                                  iPixel = mBitmap.getPixel(iX, iY);
                                  // To get value of the red channel.
                                  iRed = Color.red(iPixel);
                                  // To get value of the green channel.
                                  iGreen = Color.green(iPixel);
                                  // To get value of the blue channel.
                                  iBlue = Color.blue(iPixel);
                                  // Compute value of gray.
                                  iRGBAvg = ( iRed + iGreen + iBlue ) / 3;
                                  // Set pixel of gray. 
                                  bitmapClone.setPixel(iX, iY, Color.rgb(iRGBAvg, iRGBAvg, iRGBAvg));
                              }
                          }
                   }
                   // Comic
                   else if ( iImageProcessingId == 1 )
                   {
                       // The horizontal of Sobel matrix
                       int iSobel1 [][]= { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 }};
                       // The vertical of Sobel matrix
                       int iSobel2 [][]= { { -1, 0, -1 }, { -2, 0, 2 }, { 1, 0, 1 }};
                       // The ordered dither of the matrix
                       int iOrderDither [][] = { { 28, 255, 57 }, { 142, 113, 227 }, { 170, 198, 85 } };
                       float fYofYUV = 0.0f;
                       int iYofYUV = 0;
                       int iX1 = 0;
                       int iY1 = 0;
                       int iR = 1;
                       int iValue1 = 0;
                       int iValue2 = 0;
                       int iValue = 0;
                       // For a horizontal index of Sobel matrix
                       int iX2 = 0;
                       // For a vertical index of Sobel matrix
                       int iY2 = 0;
                       // Sobel filter of image processing
                       // The height of the image
                       for ( iY = 1; iY < bitmapClone.getHeight() - 1; iY++ )
                       {
                           // The width of the image
                           for ( iX = 1; iX < bitmapClone.getWidth() - 1; iX++ )
                           {
                               iY2= 0;
                               iValue1 = 0;
                               iValue2 = 0;
                               // The height of Sobel matrix
                               for ( iY1 = iY - iR; iY1 <= iY + iR; iY1++ )
                               {
                                   iX2 = 0;
                                   // The width of Sobel matrix
                                   for ( iX1 = iX - iR; iX1 <= iX + iR; iX1++ )
                                   {
                                       // Get value of pixel
                                       iPixel = mBitmap.getPixel(iX1, iY1);
                                       // To get value of the red channel.
                                       iRed = Color.red(iPixel);
                                       // To get value of the green channel.
                                       iGreen = Color.green(iPixel);
                                       // To get value of the blue channel.
                                       iBlue = Color.blue(iPixel);
                                       // Compute value of gray.
                                       fYofYUV = ( 0.299 * iRed ) + ( 0.587f * iGreen ) + ( 0.114f * iBlue );
                                       // To compute Sobel matrix, we transform float to integer.
                                       iYofYUV = (int) fYofYUV;
                                       // Convolution computing horizontal of Sobel matrix.  
                                       iValue1 += iYofYUV * iSobel1[iX2][iY2];
                                       // Convolution computing vertical of Sobel matrix.
                                       iValue2 += iYofYUV * iSobel2[iX2][iY2];
                                       iX2++;
                                   }
                                   iY2++;
                                   iX2 = 0;
                               }
                               // Choice maximum value.
                               iValue = Math.max(iValue1, iValue2);
                               // The twenty-four is a magic number.
                               if ( iValue > 24 )
                               {
                                   // Set the pixel is black.
                                   bitmapClone.setPixel(iX, iY, Color.rgb(0, 0, 0));
                               }
                               else
                               {
                                   // Set the pixel is white.
                                   bitmapClone.setPixel(iX, iY, Color.rgb(255, 255, 255));
                               }
                           }
                       }
                       // The height of the image. But we are stepping to three once.
                       for ( iY = 0; iY < bitmapClone.getHeight() - 3; iY+=3 )
                       {
                           // The width of the image. But we are stepping to three once.
                           for ( iX = 0; iX < bitmapClone.getWidth() - 3; iX+=3 )
                           {
                               iY2 = 0;
                               // The height of the matrix.
                               for ( iY1 = iY; iY1 <= iY + 2 ; iY1++ )
                               {
                                   iX2 = 0;
                                   // The width of the matrix.
                                   for ( iX1 = iX; iX1 <= iX + 2; iX1++ )
                                   {
                                       // Get value of pixel
                                       iPixel = mBitmap.getPixel(iX1, iY1);
                                       // To get value of the red channel.
                                       iRed = Color.red(iPixel);
                                       // To get value of the green channel.
                                       iGreen = Color.green(iPixel);
                                       // To get value of the blue channel.
                                       iBlue = Color.blue(iPixel);
                                       // Compute value of gray.
                                       fYofYUV = ( 0.299 * iRed ) + ( 0.587f * iGreen ) + ( 0.114f * iBlue );
                                       // To compute Sobel matrix, we transform float to integer.
                                       iYofYUV = (int) fYofYUV;
                                       // If the gray depth more than the matrix, it should be white.
                                       if ( iYofYUV >= iOrderDither[iX2][iY2] )
                                       {
                                           bitmapClone.setPixel(iX, iY, Color.rgb(255, 255, 255));
                                       }
                                       // Otherwise, it should be black.
                                       else
                                       {
                                           bitmapClone.setPixel(iX, iY, Color.rgb(0, 0, 0));
                                       }
                                       iX2++;
                                   }
                                   iY2++;
                                   iX2 = 0;
                               }
                           }
                       }
                   }
               }
               // Set processed image to display.
               ImgView.setImageBitmap(bitmapClone);
               // Show the height of the image.
               String strInfo = "";
               strInfo = String.valueOf(mBitmap.getHeight());
               txtView.setText(strInfo);
               // Restart camera to preview.
               camera.startPreview();
               // To disable auto focus of callback function. 
               camera.autoFocus(null);
           }
        };


        // To establish Camera.AutoFocusCallback
        mAutoFocusCB = new AutoFocusCallback(){
            @Override
            public void onAutoFocus(boolean success, Camera camera){
                // When auto focus is done and then we will take a picture.
                if ( success == true )
                {
                    // Into take a picture of callback function.
                    camera.takePicture(null, null, mPictureCB);                    
                }
            }
        };

        // While a user press the take a picture button, when it starts auto focus.
        btn_Capture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    // To make sure the camera is open.
                    if(mCamera != null){
                        // Create a thread.
                        new Thread(new Runnable() {
                            public void run() {
                                // To execute the auto focus.
                                mCamera.autoFocus(mAutoFocusCB);
                            }
                          }).start();
                    }
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        });         

    }


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

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub
        // Get parameters of the camera.
        Camera.Parameters parameters = mCamera.getParameters();
        // Set size of the picture.
        parameters.setPictureSize(640, 480);
        // Set size of preview.
        parameters.setPreviewSize(width, height);
        // Set auto focus.
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        // Set parameters of the camera.
        mCamera.setParameters(parameters);
        // Start preview.
        mCamera.startPreview();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        // If the camera is initially successful and then to open camera.
        if ( mCamera == null )
        {
            mCamera = Camera.open();
        }
        try {
            // Set SurfaceHolder.
            mCamera.setPreviewDisplay(holder);
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        // Stop preview.
        mCamera.stopPreview();        
        // Release Camera.
        mCamera.release();
    }

}

image.png

image.png

You can download source code and binary code, the hyperlink is https://drive.google.com/file/d/0BzHb_OyLyVZlOFlFbVpOd0tRbVk/view?usp=sharing
MD5: 0bcefd51cf373542fae4456a9b1659eb

Exception

  1. For debug, you should enable USB debug with your android phone.

Refernce

None.

Acknowledge

Thank you (Android, Eclipse) very much for this great development utility.

Popular Posts

最佳化處理策略之快速消除扭曲演算法

波蘭文學習之旅:1-1. 波蘭文字母與發音(注音版)

Python 日期與時間的處理

最佳化處理策略之快速消除扭曲演算法

Image

波蘭文學習之旅:1-1. 波蘭文字母與發音(注音版)

Image

Python 日期與時間的處理

Image

Visual Basic 6.0 程式案例學習: 10. 條碼列印程式 (2014版)

Image

Visual Basic .Net 程式案例學習: 06. 題庫測驗系統 (2014版)

Image

修復損毀的 SQLite DB 資料庫

Image

用10種程式語言做影像二值化(Image binarization)

Image

解決 ValueError: If using all scalar values, you must pass an index

Image

Visual Basic 6.0 程式案例學習: 04. 人事考勤管理系統 (2014版)

Image

Visual Basic 6.0 程式案例學習: 07. 收據列印程式 (2014版)

Image

佑佑的 Line 貼圖創作

貼圖作者網址:

https://line.me/S/shop/sticker/author/3883362

靜態貼圖

 
 
 
 
  牡羊座:狗狗角色

作者:佑佑
依照牡羊座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  金牛座:兔兔角色

作者:佑佑
依照金牛座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  雙子座:貓貓角色

作者:佑佑
依照雙子座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  巨蟹座:倉鼠角色

作者:佑佑
依照巨蟹座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
 
 
 
 
  獅子座:幼獅角色

作者:佑佑
依照獅子座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  處女座:松鼠角色

作者:佑佑
依照處女座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  天秤座:鴿子角色

作者:佑佑
依照天秤座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  天蠍座:鳳凰角色

作者:佑佑
依照天蠍座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
 
 
 
 
  射手座:人馬角色

作者:佑佑
依照射手座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  魔羯座:山羊角色

作者:佑佑
依照魔羯座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  水瓶座:海豚角色

作者:佑佑
依照水瓶座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
  雙魚座:小丑魚角色

作者:佑佑
依照雙魚座的個性優點、個性缺點、生活習慣、理財習慣、社交習慣、愛情觀、節假日、禮貌問候,所編撰的貼圖。星座只能當參考用,不會完全準確。
購買貼圖
 
 
 
 
  上班族都變成狒狒,心裡想要說的話。

作者:佑佑
最近在台灣走紅的狒狒,用擬人化的方式,變成上班族,畫出心裡的想說的話。
購買貼圖
  接案派遣的日常

作者:佑佑
接案派遣到其他公司的日常對話,有調皮、正經、日常對話。
購買貼圖
  貓女事務員的搞笑時刻

作者:佑佑
把行政人員在職場上遇到千奇百怪的事情畫出來,用可愛的貓女來擔任行政人員的代言人。
購買貼圖
  貓貓說每天都會用到的話

作者:佑佑
日常生活常見的短句,用小貓擬人化的方式呈現,比較親切可愛。
購買貼圖
 
     
  大人的煩惱特輯:小兔角色

作者:佑佑
錢包餓了,生活苦了。變美變健康?先讓我睡飽吧。心好累,我需要充電。
購買貼圖
     

動態貼圖

 
 
 
 
  牡羊座日常交際:狗狗角色

作者:佑佑
依照牡羊座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  金牛座日常交際:兔兔角色

作者:佑佑
依照金牛座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  雙子座日常交際:貓貓角色

作者:佑佑
依照雙子座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  巨蟹座日常交際:倉鼠角色

作者:佑佑
依照巨蟹座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
 
 
 
 
  獅子座日常交際:幼獅角色

作者:佑佑
依照獅子座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  處女座日常交際:松鼠角色

作者:佑佑
依照處女座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  天秤座日常交際:鴿子角色

作者:佑佑
依照天秤座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  天蠍座日常交際:鳳凰角色

作者:佑佑
依照天蠍座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
 
 
 
 
  射手座日常交際:人馬角色

作者:佑佑
依照射手座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  魔羯座日常交際:山羊角色

作者:佑佑
依照魔羯座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  水瓶座日常交際:海豚角色

作者:佑佑
依照水瓶座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
  雙魚座日常交際:小丑魚角色

作者:佑佑
依照雙魚座的個性,描繪日常生活的行為,供交際表達心情使用。星座只能當參考用,不會完全準確。
購買貼圖
 
     
  厭世報:鬍渣男

作者:佑佑
厭世總有個理由跟原因,實在是令人不爽,為什麼最倒楣的總是我?真希望明天就是世界末日,讓大家都一樣慘。
購買貼圖
     

表情貼

 
 
 
 
  貓貓說每天都用得到的表情貼

作者:佑佑
日常生活常見的表情用語,用小貓擬人化的方式呈現,比較親切可愛。
購買貼圖
  天秤座的表情貼:鴿子角色

作者:佑佑
日常生活常見的表情用語,天秤座的鴿子擬人化方式呈現,比較親切可愛。
購買貼圖
  天蠍座的表情貼:鳳凰角色

作者:佑佑
日常生活常見的表情用語,天蠍座的鳳凰擬人化方式呈現,比較親切可愛。
購買貼圖
  射手座的表情貼:人馬角色

作者:佑佑
日常生活常見的表情用語,射手座的人馬擬人化方式呈現,比較親切可愛。
購買貼圖
 
 
 
 
  魔羯座的表情貼:山羊角色

作者:佑佑
日常生活常見的表情用語,魔羯座的山羊擬人化方式呈現,比較親切可愛。
購買貼圖
  水瓶座的表情貼:海豚角色

作者:佑佑
日常生活常見的表情用語,水瓶座的海豚擬人化方式呈現,比較親切可愛。
購買貼圖
  雙魚座的表情貼:小丑魚角色

作者:佑佑
日常生活常見的表情用語,雙魚座的小丑魚擬人化方式呈現,比較親切可愛。
購買貼圖