diff options
author | Derek Sollenberger <djsollen@google.com> | 2011-06-08 10:09:47 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-06-08 10:09:47 -0700 |
commit | d689f8cb9d3ad0d50a4b6e9c6c7b7f02939c4c72 (patch) | |
tree | f9a3b1867d52a0f2c44afd8540e8a0e45209315e | |
parent | bc70b8e05fb595bd4aafd796517122c116a4675a (diff) | |
parent | 0b15698a8c76bb8abc1b555c1d91892669b4118f (diff) | |
download | skia-d689f8cb9d3ad0d50a4b6e9c6c7b7f02939c4c72.tar.gz |
Merge "Skia Merge (revision 1510)"
315 files changed, 40788 insertions, 3871 deletions
diff --git a/Android.mk b/Android.mk index 6c27066cec..41d17afaaa 100644 --- a/Android.mk +++ b/Android.mk @@ -30,6 +30,7 @@ LOCAL_SRC_FILES:= \ src/core/SkCordic.cpp \ src/core/SkDebug.cpp \ src/core/SkFloatBits.cpp \ + src/core/SkFontHost.cpp \ src/core/SkPoint.cpp \ src/core/SkRect.cpp \ src/core/SkRegion.cpp \ @@ -181,6 +182,7 @@ LOCAL_SRC_FILES:= \ src/utils/SkBoundaryPatch.cpp \ src/utils/SkCamera.cpp \ src/utils/SkDumpCanvas.cpp \ + src/utils/SkEGLContext_none.cpp \ src/utils/SkInterpolator.cpp \ src/utils/SkLayer.cpp \ src/utils/SkOSFile.cpp \ @@ -290,7 +292,6 @@ LOCAL_SRC_FILES:= \ gpu/src/GrInOrderDrawBuffer.cpp \ gpu/src/GrMatrix.cpp \ gpu/src/GrMemory.cpp \ - gpu/src/GrPath.cpp \ gpu/src/GrPathUtils.cpp \ gpu/src/GrRectanizer_fifo.cpp \ gpu/src/GrResource.cpp \ diff --git a/android_sample/SampleApp/Android.mk b/android_sample/SampleApp/Android.mk new file mode 100644 index 0000000000..6d62c35426 --- /dev/null +++ b/android_sample/SampleApp/Android.mk @@ -0,0 +1,71 @@ +###################################### +# Build the app. +###################################### + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := SampleApp + +LOCAL_JNI_SHARED_LIBRARIES := libskia-sample + +include $(BUILD_PACKAGE) + +###################################### +# Build the shared library. +###################################### + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES += \ + external/skia/include/core \ + external/skia/include/config \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/include/utils \ + external/skia/include/utils/android \ + external/skia/include/views \ + external/skia/samplecode \ + external/skia/include/xml \ + external/skia/include/gpu \ + external/skia/src/core \ + external/skia/gpu/include \ + frameworks/base/core/jni/android/graphics \ + frameworks/base/native/include/android \ + $(LOCAL_PATH)/jni + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libskia \ + libandroid_runtime \ + libGLESv2 + +LOCAL_STATIC_LIBRARIES := \ + libskiagpu + +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE := libskia-sample + +LOCAL_SRC_FILES := \ + ../../src/ports/SkXMLParser_empty.cpp \ + jni/sample-jni.cpp + +include external/skia/src/views/views_files.mk +LOCAL_SRC_FILES += $(addprefix ../../src/views/, $(SOURCE)) + +include external/skia/src/xml/xml_files.mk +LOCAL_SRC_FILES += $(addprefix ../../src/xml/, $(SOURCE)) + +include external/skia/samplecode/samplecode_files.mk +LOCAL_SRC_FILES += $(addprefix ../../samplecode/, $(SOURCE)) + +include $(BUILD_SHARED_LIBRARY) diff --git a/android_sample/SampleApp/AndroidManifest.xml b/android_sample/SampleApp/AndroidManifest.xml new file mode 100644 index 0000000000..e3232468f6 --- /dev/null +++ b/android_sample/SampleApp/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 Skia + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.skia.sampleapp" + android:versionCode="1" + android:versionName="1.0"> + <uses-sdk android:minSdkVersion="3" /> + <application android:label="@string/app_name" + android:debuggable="true"> + <activity android:name=".SampleApp" + android:theme="@android:style/Theme.Holo.NoActionBar" + 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> +</manifest> diff --git a/android_sample/SampleApp/README.txt b/android_sample/SampleApp/README.txt new file mode 100644 index 0000000000..a7d8e54d2e --- /dev/null +++ b/android_sample/SampleApp/README.txt @@ -0,0 +1,25 @@ +Building the sample app for Android using an Android tree: + +Copy this folder into an Android tree in packages/apps. In addition to jni, +res, and src, there needs to be a fourth folder named "skia_extra". This +will include the skia files which are not part of an Android checkout. It +should have three folders: include, samplecode, and src. + +skia/trunk/include/views -> skia_extra/include/views +skia/trunk/include/xml -> skia_extra/include/xml + +skia/trunk/samplecode -> skia_extra/samplecode + +skia/trunk/src/views -> skia_extra/src/views +skia/trunk/src/ports/SkXMLParser_empty.cpp -> skia_extra/src/ports/ +skia/trunk/src/xml -> skia_extra/src/xml + +skia/trunk/include/utils/android/AndroidKeyToSkKey.h -> jni/ + +From packages/apps/SampleApp, type "mm" to build, and install the +resulting apk. + +(It may be necessary to remove samples that do not build from +skia_extra/samplecode/samplecode_files.mk) + +TODO: Instructions for building from SDK/NDK diff --git a/android_sample/SampleApp/jni/sample-jni.cpp b/android_sample/SampleApp/jni/sample-jni.cpp new file mode 100644 index 0000000000..529047c318 --- /dev/null +++ b/android_sample/SampleApp/jni/sample-jni.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <jni.h> + +#include "SkCanvas.h" +#include "GraphicsJNI.h" +#include "SkEvent.h" +#include "SkWindow.h" +#include "SkApplication.h" +#include "utils/android/AndroidKeyToSkKey.h" + +/////////////////////////////////////////// +///////////////// Globals ///////////////// +/////////////////////////////////////////// + +struct ActivityGlue { + JNIEnv* m_env; + jweak m_obj; + jmethodID m_setTitle; + ActivityGlue() { + m_env = NULL; + m_obj = NULL; + m_setTitle = NULL; + } +} gActivityGlue; + +struct WindowGlue { + jweak m_obj; + jmethodID m_inval; + WindowGlue() { + m_obj = NULL; + m_inval = NULL; + } +} gWindowGlue; + +SkOSWindow* gWindow; + +/////////////////////////////////////////// +///////////// SkOSWindow impl ///////////// +/////////////////////////////////////////// + +void SkOSWindow::onSetTitle(const char title[]) +{ + if (gActivityGlue.m_env) { + JNIEnv* env = gActivityGlue.m_env; + jstring string = env->NewStringUTF(title); + env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_setTitle, + string); + env->DeleteLocalRef(string); + } +} + +void SkOSWindow::onHandleInval(const SkIRect& rect) +{ + if (!gActivityGlue.m_env || !gWindowGlue.m_inval || !gWindowGlue.m_obj) { + return; + } + gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); +} + +/////////////////////////////////////////// +/////////////// SkEvent impl ////////////// +/////////////////////////////////////////// + +void SkEvent::SignalQueueTimer(SkMSec) {} + +void SkEvent::SignalNonEmptyQueue() {} + +/////////////////////////////////////////// +////////////////// JNI //////////////////// +/////////////////////////////////////////// + +static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], + const char signature[]) +{ + jmethodID m = env->GetMethodID(clazz, name, signature); + if (!m) SkDebugf("Could not find Java method %s\n", name); + return m; +} + +extern "C" { +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas( + JNIEnv* env, jobject thiz, jobject jcanvas); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize( + JNIEnv* env, jobject thiz, jint w, jint h); +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown( + JNIEnv* env, jobject thiz, jint keyCode, jint uni); +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp( + JNIEnv* env, jobject thiz, jint keyCode); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick( + JNIEnv* env, jobject thiz, jint x, jint y, jint state); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow( + JNIEnv* env, jobject thiz, jobject jsampleView); +}; + +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown( + JNIEnv* env, jobject thiz, jint keyCode, jint uni) +{ + bool handled = gWindow->handleKey(AndroidKeycodeToSkKey(keyCode)); + handled |= gWindow->handleChar((SkUnichar) uni); + return handled; +} + +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(JNIEnv* env, + jobject thiz, jint keyCode) +{ + return gWindow->handleKeyUp(AndroidKeycodeToSkKey(keyCode)); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(JNIEnv* env, + jobject thiz, jint x, jint y, jint jstate) +{ + SkView::Click::State state; + switch(jstate) { + case 0: // MotionEvent.ACTION_DOWN + state = SkView::Click::kDown_State; + break; + case 1: // MotionEvent.ACTION_UP + case 3: // MotionEvent.ACTION_CANCEL + state = SkView::Click::kUp_State; + break; + case 2: // MotionEvent.ACTION_MOVE + state = SkView::Click::kMoved_State; + break; + default: + SkDebugf("motion event ignored\n"); + return; + } + gWindow->handleClick(x, y, state); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(JNIEnv* env, + jobject thiz, jint w, jint h) +{ + gWindow->resize(w, h); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow( + JNIEnv* env, jobject thiz, jobject jsampleView) +{ + gWindow = create_sk_window(NULL); + // Only using a method on View. + jclass clazz = gActivityGlue.m_env->FindClass("android/view/View"); + gWindowGlue.m_obj = gActivityGlue.m_env->NewWeakGlobalRef(jsampleView); + gWindowGlue.m_inval = GetJMethod(gActivityGlue.m_env, clazz, "invalidate", + "(IIII)V"); + gActivityGlue.m_env->DeleteLocalRef(clazz); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env, + jobject thiz) +{ + gActivityGlue.m_env = env; + // Only using a method on Activity. + jclass clazz = env->FindClass("android/app/Activity"); + gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz); + gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle", + "(Ljava/lang/CharSequence;)V"); + env->DeleteLocalRef(clazz); + + application_init(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(JNIEnv* env, + jobject thiz) +{ + application_term(); + if (gWindowGlue.m_obj) { + env->DeleteWeakGlobalRef(gWindowGlue.m_obj); + gWindowGlue.m_obj = NULL; + } + if (gActivityGlue.m_obj) { + env->DeleteWeakGlobalRef(gActivityGlue.m_obj); + gActivityGlue.m_obj = NULL; + } + delete gWindow; + gWindow = NULL; +} + + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas( + JNIEnv* env, jobject thiz, jobject jcanvas) +{ + if (!gWindow) return; + gWindow->update(NULL); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + canvas->drawBitmap(gWindow->getBitmap(), 0, 0); +} diff --git a/android_sample/SampleApp/res/layout/layout.xml b/android_sample/SampleApp/res/layout/layout.xml new file mode 100644 index 0000000000..d71116bae1 --- /dev/null +++ b/android_sample/SampleApp/res/layout/layout.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 Skia + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/holder" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView android:id="@+id/title_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> +</LinearLayout> + diff --git a/android_sample/SampleApp/res/values/strings.xml b/android_sample/SampleApp/res/values/strings.xml new file mode 100644 index 0000000000..72d3bc8596 --- /dev/null +++ b/android_sample/SampleApp/res/values/strings.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 Skia + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="app_name">SampleApp</string> +</resources> diff --git a/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java new file mode 100644 index 0000000000..b02c4d7fe4 --- /dev/null +++ b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.skia.sampleapp; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class SampleApp extends Activity +{ + private TextView mTitle; + + public class SampleView extends View { + public SampleView(Context context) { + super(context); + createOSWindow(this); + } + + @Override + protected void onDraw(Canvas canvas) { + drawToCanvas(canvas); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + updateSize(w, h); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + final int x = (int) event.getX(); + final int y = (int) event.getY(); + final int action = event.getAction(); + handleClick(x, y, action); + return true; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + init(); + setContentView(R.layout.layout); + mTitle = (TextView) findViewById(R.id.title_view); + LinearLayout holder = (LinearLayout) findViewById(R.id.holder); + View view = new SampleView(this); + holder.addView(view, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + } + + @Override + public void onDestroy() + { + term(); + super.onDestroy(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + int uni = event.getUnicodeChar(event.getMetaState()); + return handleKeyDown(event.getKeyCode(), uni); + case KeyEvent.ACTION_UP: + return handleKeyUp(event.getKeyCode()); + default: + return false; + } + } + + @Override + public void setTitle(CharSequence title) { + mTitle.setText(title); + } + + private native void drawToCanvas(Canvas canvas); + private native void init(); + private native void term(); + // Currently depends on init having already been called. + private native void createOSWindow(SampleView view); + private native void updateSize(int w, int h); + private native void handleClick(int x, int y, int state); + private native boolean handleKeyDown(int key, int uni); + private native boolean handleKeyUp(int key); + + static { + System.loadLibrary("skia-sample"); + } +} diff --git a/bench/Android.mk b/bench/Android.mk index b20aef9729..71523af4e9 100644 --- a/bench/Android.mk +++ b/bench/Android.mk @@ -6,6 +6,9 @@ LOCAL_SRC_FILES := \ BitmapBench.cpp \ DecodeBench.cpp \ FPSBench.cpp \ + GradientBench.cpp \ + MatrixBench.cpp \ + PathBench.cpp \ RectBench.cpp \ RepeatTileBench.cpp \ TextBench.cpp \ @@ -17,7 +20,8 @@ LOCAL_SRC_FILES += \ ../src/utils/SkNWayCanvas.cpp \ ../src/utils/SkParse.cpp -LOCAL_SHARED_LIBRARIES := libcutils libskia +LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 +LOCAL_STATIC_LIBRARIES := libskiagpu LOCAL_C_INCLUDES := \ external/skia/include/config \ external/skia/include/core \ diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp new file mode 100644 index 0000000000..d963bc7126 --- /dev/null +++ b/bench/MatrixBench.cpp @@ -0,0 +1,229 @@ +#include "SkBenchmark.h" +#include "SkMatrix.h" +#include "SkRandom.h" +#include "SkString.h" + +class MatrixBench : public SkBenchmark { + SkString fName; + enum { N = 100000 }; +public: + MatrixBench(void* param, const char name[]) : INHERITED(param) { + fName.printf("matrix_%s", name); + } + + virtual void performTest() = 0; + +protected: + virtual int mulLoopCount() const { return 1; } + + virtual const char* onGetName() { + return fName.c_str(); + } + + virtual void onDraw(SkCanvas* canvas) { + int n = N * this->mulLoopCount(); + for (int i = 0; i < n; i++) { + this->performTest(); + } + } + +private: + typedef SkBenchmark INHERITED; +}; + +// we want to stop the compiler from eliminating code that it thinks is a no-op +// so we have a non-static global we increment, hoping that will convince the +// compiler to execute everything +int gMatrixBench_NonStaticGlobal; + +#define always_do(pred) \ + do { \ + if (pred) { \ + ++gMatrixBench_NonStaticGlobal; \ + } \ + } while (0) + +class EqualsMatrixBench : public MatrixBench { +public: + EqualsMatrixBench(void* param) : INHERITED(param, "equals") {} +protected: + virtual void performTest() { + SkMatrix m0, m1, m2; + + m0.reset(); + m1.reset(); + m2.reset(); + always_do(m0 == m1); + always_do(m1 == m2); + always_do(m2 == m0); + always_do(m0.getType()); + always_do(m1.getType()); + always_do(m2.getType()); + } +private: + typedef MatrixBench INHERITED; +}; + +class ScaleMatrixBench : public MatrixBench { +public: + ScaleMatrixBench(void* param) : INHERITED(param, "scale") { + + fM0.reset(); + fM1.setScale(fSX, fSY); + fM2.setTranslate(fSX, fSY); + fSX = fSY = SkFloatToScalar(1.5f); + } +protected: + virtual void performTest() { + SkMatrix m; + m = fM0; m.preScale(fSX, fSY); + m = fM1; m.preScale(fSX, fSY); + m = fM2; m.preScale(fSX, fSY); + } +private: + SkMatrix fM0, fM1, fM2; + SkScalar fSX, fSY; + typedef MatrixBench INHERITED; +}; + +// having unknown values in our arrays can throw off the timing a lot, perhaps +// handling NaN values is a lot slower. Anyway, this guy is just meant to put +// reasonable values in our arrays. +template <typename T> void init9(T array[9]) { + SkRandom rand; + for (int i = 0; i < 9; i++) { + array[i] = rand.nextSScalar1(); + } +} + +// Test the performance of setConcat() non-perspective case: +// using floating point precision only. +class FloatConcatMatrixBench : public MatrixBench { +public: + FloatConcatMatrixBench(void* p) : INHERITED(p, "concat_floatfloat") { + init9(mya); + init9(myb); + init9(myr); + } +protected: + virtual int mulLoopCount() const { return 4; } + + static inline void muladdmul(float a, float b, float c, float d, + float* result) { + *result = a * b + c * d; + } + virtual void performTest() { + const float* a = mya; + const float* b = myb; + float* r = myr; + muladdmul(a[0], b[0], a[1], b[3], &r[0]); + muladdmul(a[0], b[1], a[1], b[4], &r[1]); + muladdmul(a[0], b[2], a[1], b[5], &r[2]); + r[2] += a[2]; + muladdmul(a[3], b[0], a[4], b[3], &r[3]); + muladdmul(a[3], b[1], a[4], b[4], &r[4]); + muladdmul(a[3], b[2], a[4], b[5], &r[5]); + r[5] += a[5]; + r[6] = r[7] = 0.0f; + r[8] = 1.0f; + } +private: + float mya [9]; + float myb [9]; + float myr [9]; + typedef MatrixBench INHERITED; +}; + +static inline float SkDoubleToFloat(double x) { + return static_cast<float>(x); +} + +// Test the performance of setConcat() non-perspective case: +// using floating point precision but casting up to float for +// intermediate results during computations. +class FloatDoubleConcatMatrixBench : public MatrixBench { +public: + FloatDoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_floatdouble") { + init9(mya); + init9(myb); + init9(myr); + } +protected: + virtual int mulLoopCount() const { return 4; } + + static inline void muladdmul(float a, float b, float c, float d, + float* result) { + *result = SkDoubleToFloat((double)a * b + (double)c * d); + } + virtual void performTest() { + const float* a = mya; + const float* b = myb; + float* r = myr; + muladdmul(a[0], b[0], a[1], b[3], &r[0]); + muladdmul(a[0], b[1], a[1], b[4], &r[1]); + muladdmul(a[0], b[2], a[1], b[5], &r[2]); + r[2] += a[2]; + muladdmul(a[3], b[0], a[4], b[3], &r[3]); + muladdmul(a[3], b[1], a[4], b[4], &r[4]); + muladdmul(a[3], b[2], a[4], b[5], &r[5]); + r[5] += a[5]; + r[6] = r[7] = 0.0f; + r[8] = 1.0f; + } +private: + float mya [9]; + float myb [9]; + float myr [9]; + typedef MatrixBench INHERITED; +}; + +// Test the performance of setConcat() non-perspective case: +// using double precision only. +class DoubleConcatMatrixBench : public MatrixBench { +public: + DoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_double") { + init9(mya); + init9(myb); + init9(myr); + } +protected: + virtual int mulLoopCount() const { return 4; } + + static inline void muladdmul(double a, double b, double c, double d, + double* result) { + *result = a * b + c * d; + } + virtual void performTest() { + const double* a = mya; + const double* b = myb; + double* r = myr; + muladdmul(a[0], b[0], a[1], b[3], &r[0]); + muladdmul(a[0], b[1], a[1], b[4], &r[1]); + muladdmul(a[0], b[2], a[1], b[5], &r[2]); + r[2] += a[2]; + muladdmul(a[3], b[0], a[4], b[3], &r[3]); + muladdmul(a[3], b[1], a[4], b[4], &r[4]); + muladdmul(a[3], b[2], a[4], b[5], &r[5]); + r[5] += a[5]; + r[6] = r[7] = 0.0; + r[8] = 1.0; + } +private: + double mya [9]; + double myb [9]; + double myr [9]; + typedef MatrixBench INHERITED; +}; + + +static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); } +static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); } +static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); } +static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); } +static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); } + +static BenchRegistry gReg0(M0); +static BenchRegistry gReg1(M1); +static BenchRegistry gReg2(M2); +static BenchRegistry gReg3(M3); +static BenchRegistry gReg4(M4); diff --git a/bench/bench_compare.py b/bench/bench_compare.py new file mode 100644 index 0000000000..f6909b10b0 --- /dev/null +++ b/bench/bench_compare.py @@ -0,0 +1,140 @@ +''' +Created on May 16, 2011 + +@author: bungeman +''' +import sys +import getopt +import re + +def parse(lines): + """Takes iterable lines of bench output, returns {bench:{config:time}}.""" + + benches = {} + current_bench = None + + for line in lines: + #see if this line starts a new bench + new_bench = re.search('running bench \[\d+ \d+\] (.{28})', line) + if new_bench: + current_bench = new_bench.group(1) + + #add configs on this line to the current bench + if current_bench: + for new_config in re.finditer(' (.{4}): msecs = (\d+\.\d+)', line): + current_config = new_config.group(1) + current_time = float(new_config.group(2)) + if current_bench in benches: + benches[current_bench][current_config] = current_time + else: + benches[current_bench] = {current_config : current_time} + + return benches + +def usage(): + """Prints simple usage information.""" + + print '-o <file> the old bench output file.' + print '-n <file> the new bench output file.' + print '-h causes headers to be output.' + print '-f <fieldSpec> which fields to output and in what order.' + print ' Not specifying is the same as -f "bcondp".' + print ' b: bench' + print ' c: config' + print ' o: old time' + print ' n: new time' + print ' d: diff' + print ' p: percent diff' + + +def main(): + """Parses command line and writes output.""" + + try: + opts, args = getopt.getopt(sys.argv[1:], "f:o:n:h") + except getopt.GetoptError, err: + print str(err) + usage() + sys.exit(2) + + column_formats = { + 'b' : '{bench: >28} ', + 'c' : '{config: <4} ', + 'o' : '{old_time: >10.2f} ', + 'n' : '{new_time: >10.2f} ', + 'd' : '{diff: >+10.2f} ', + 'p' : '{diffp: >+7.1%} ', + } + header_formats = { + 'b' : '{bench: >28} ', + 'c' : '{config: <4} ', + 'o' : '{old_time: >10} ', + 'n' : '{new_time: >10} ', + 'd' : '{diff: >10} ', + 'p' : '{diffp: >7} ', + } + + old = None + new = None + column_format = "" + header_format = "" + columns = 'bcondp' + header = False + + for option, value in opts: + if option == "-o": + old = value + elif option == "-n": + new = value + elif option == "-h": + header = True + elif option == "-f": + columns = value + else: + usage() + assert False, "unhandled option" + + if old is None or new is None: + usage() + sys.exit(2) + + for column_char in columns: + if column_formats[column_char]: + column_format += column_formats[column_char] + header_format += header_formats[column_char] + else: + usage() + sys.exit(2) + + if header: + print header_format.format( + bench='bench' + , config='conf' + , old_time='old' + , new_time='new' + , diff='diff' + , diffp='diffP' + ) + + old_benches = parse(open(old, 'r')) + new_benches = parse(open(new, 'r')) + + for old_bench, old_configs in old_benches.items(): + if old_bench in new_benches: + new_configs = new_benches[old_bench] + for old_config, old_time in old_configs.items(): + if old_config in new_configs: + new_time = new_configs[old_config] + old_time = old_configs[old_config] + print column_format.format( + bench=old_bench.strip() + , config=old_config.strip() + , old_time=old_time + , new_time=new_time + , diff=(old_time - new_time) + , diffp=((old_time-new_time)/old_time) + ) + + +if __name__ == "__main__": + main() diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp index 4f6d81ce96..066573aee9 100644 --- a/bench/benchmain.cpp +++ b/bench/benchmain.cpp @@ -6,6 +6,9 @@ #include "SkPicture.h" #include "SkString.h" #include "SkTime.h" +#include "GrContext.h" +#include "SkGpuDevice.h" +#include "SkEGLContext.h" #include "SkBenchmark.h" @@ -30,6 +33,7 @@ static void erase(SkBitmap& bm) { } } +#if 0 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) { if (bm1.width() != bm2.width() || bm1.height() != bm2.height() || @@ -43,9 +47,9 @@ static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) { return false; } } - return true; } +#endif class Iter { public: @@ -62,7 +66,7 @@ public: } return NULL; } - + private: const BenchRegistry* fBench; void* fParam; @@ -102,7 +106,7 @@ static void saveFile(const char name[], const char config[], const char dir[], *p++ = c | (SK_A32_MASK << SK_A32_SHIFT); } } - + SkString str; make_filename(name, &str); str.appendf("_%s.png", config); @@ -118,7 +122,7 @@ static void performClip(SkCanvas* canvas, int w, int h) { r.set(SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(w*2/3), SkIntToScalar(h*2/3)); canvas->clipRect(r, SkRegion::kIntersect_Op); - + r.set(SkIntToScalar(w/3), SkIntToScalar(h/3), SkIntToScalar(w-10), SkIntToScalar(h-10)); canvas->clipRect(r, SkRegion::kXOR_Op); @@ -143,21 +147,6 @@ static void performScale(SkCanvas* canvas, int w, int h) { canvas->translate(-x, -y); } -static void compare_pict_to_bitmap(SkPicture* pict, const SkBitmap& bm) { - SkBitmap bm2; - - bm2.setConfig(bm.config(), bm.width(), bm.height()); - bm2.allocPixels(); - erase(bm2); - - SkCanvas canvas(bm2); - canvas.drawPicture(*pict); - - if (!equal(bm, bm2)) { - SkDebugf("----- compare_pict_to_bitmap failed\n"); - } -} - static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) { if (argv < stop) { *var = atoi(*argv) != 0; @@ -166,16 +155,43 @@ static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) { return false; } +enum Backend { + kRaster_Backend, + kGPU_Backend, + kPDF_Backend, +}; + +static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size, + Backend backend, GrContext* context) { + SkDevice* device = NULL; + SkBitmap bitmap; + bitmap.setConfig(config, size.fX, size.fY); + + switch (backend) { + case kRaster_Backend: + bitmap.allocPixels(); + erase(bitmap); + device = new SkDevice(NULL, bitmap, true); + break; + case kGPU_Backend: + device = new SkGpuDevice(context, bitmap, SkGpuDevice::Current3DApiRenderTarget()); +// device->clear(0xFFFFFFFF); + break; + case kPDF_Backend: + default: + SkASSERT(!"unsupported"); + } + return device; +} + static const struct { SkBitmap::Config fConfig; const char* fName; + Backend fBackend; } gConfigs[] = { - { SkBitmap::kARGB_8888_Config, "8888" }, - { SkBitmap::kRGB_565_Config, "565", }, -#if 0 - { SkBitmap::kARGB_4444_Config, "4444", }, - { SkBitmap::kA8_Config, "A8", } -#endif + { SkBitmap::kARGB_8888_Config, "8888", kRaster_Backend }, + { SkBitmap::kRGB_565_Config, "565", kRaster_Backend }, + { SkBitmap::kARGB_8888_Config, "GPU", kGPU_Backend }, }; static int findConfig(const char config[]) { @@ -189,7 +205,7 @@ static int findConfig(const char config[]) { int main (int argc, char * const argv[]) { SkAutoGraphics ag; - + SkTDict<const char*> defineDict(1024); int repeatDraw = 1; int forceAlpha = 0xFF; @@ -199,16 +215,16 @@ int main (int argc, char * const argv[]) { bool doScale = false; bool doRotate = false; bool doClip = false; - bool doPict = false; const char* matchStr = NULL; bool hasStrokeWidth = false; float strokeWidth; - + SkString outDir; SkBitmap::Config outConfig = SkBitmap::kNo_Config; const char* configName = ""; + Backend backend = kRaster_Backend; // for warning int configCount = SK_ARRAY_COUNT(gConfigs); - + char* const* stop = argv + argc; for (++argv; argv < stop; ++argv) { if (strcmp(*argv, "-o") == 0) { @@ -219,8 +235,6 @@ int main (int argc, char * const argv[]) { outDir.append("/"); } } - } else if (strcmp(*argv, "-pict") == 0) { - doPict = true; } else if (strcmp(*argv, "-repeat") == 0) { argv++; if (argv < stop) { @@ -290,6 +304,7 @@ int main (int argc, char * const argv[]) { if (index >= 0) { outConfig = gConfigs[index].fConfig; configName = gConfigs[index].fName; + backend = gConfigs[index].fBackend; configCount = 1; } else { SkString str; @@ -316,7 +331,7 @@ int main (int argc, char * const argv[]) { return -1; } } - + // report our current settings { SkString str; @@ -324,7 +339,13 @@ int main (int argc, char * const argv[]) { forceAlpha, forceAA, forceFilter); log_progress(str); } - + + GrContext* context = NULL; + SkEGLContext eglContext; + if (eglContext.init(1024, 1024)) { + context = GrContext::CreateGLShaderContext(); + } + Iter iter(&defineDict); SkBenchmark* bench; while ((bench = iter.next()) != NULL) { @@ -340,32 +361,34 @@ int main (int argc, char * const argv[]) { if (hasStrokeWidth) { bench->setStrokeWidth(strokeWidth); } - + // only run benchmarks if their name contains matchStr if (matchStr && strstr(bench->getName(), matchStr) == NULL) { continue; } - + { SkString str; str.printf("running bench [%d %d] %28s", dim.fX, dim.fY, bench->getName()); log_progress(str); } - + for (int configIndex = 0; configIndex < configCount; configIndex++) { if (configCount > 1) { outConfig = gConfigs[configIndex].fConfig; configName = gConfigs[configIndex].fName; + backend = gConfigs[configIndex].fBackend; } - SkBitmap bm; - bm.setConfig(outConfig, dim.fX, dim.fY); - bm.allocPixels(); - erase(bm); - - SkCanvas canvas(bm); - + if (kGPU_Backend == backend && NULL == context) { + continue; + } + + SkDevice* device = make_device(outConfig, dim, backend, context); + SkCanvas canvas(device); + device->unref(); + if (doClip) { performClip(&canvas, dim.fX, dim.fY); } @@ -375,33 +398,27 @@ int main (int argc, char * const argv[]) { if (doRotate) { performRotate(&canvas, dim.fX, dim.fY); } - + + //warm up caches if needed if (repeatDraw > 1) { SkAutoCanvasRestore acr(&canvas, true); bench->draw(&canvas); + if (kGPU_Backend == backend && context) { + context->flush(); + glFinish(); + } } - + SkMSec now = SkTime::GetMSecs(); for (int i = 0; i < repeatDraw; i++) { - SkCanvas* c = &canvas; - - SkNWayCanvas nway; - SkPicture* pict = NULL; - if (doPict) { - pict = new SkPicture; - nway.addCanvas(pict->beginRecording(bm.width(), bm.height())); - nway.addCanvas(&canvas); - c = &nway; - } - - SkAutoCanvasRestore acr(c, true); - bench->draw(c); - - if (pict) { - compare_pict_to_bitmap(pict, bm); - pict->unref(); - } + SkAutoCanvasRestore acr(&canvas, true); + bench->draw(&canvas); + } + if (kGPU_Backend == backend && context) { + context->flush(); + glFinish(); } + if (repeatDraw > 1) { double duration = SkTime::GetMSecs() - now; SkString str; @@ -409,7 +426,8 @@ int main (int argc, char * const argv[]) { log_progress(str); } if (outDir.size() > 0) { - saveFile(bench->getName(), configName, outDir.c_str(), bm); + saveFile(bench->getName(), configName, outDir.c_str(), + device->accessBitmap(false)); } } log_progress("\n"); diff --git a/gm/Android.mk b/gm/Android.mk index b3aeadf1f8..acfb4a5b54 100644 --- a/gm/Android.mk +++ b/gm/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES := \ complexclip.cpp \ filltypes.cpp \ gradients.cpp \ + nocolorbleed.cpp \ pathfill.cpp \ points.cpp \ poly2poly.cpp \ diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp index 0487fe6958..39039130cd 100644 --- a/gm/bitmapfilters.cpp +++ b/gm/bitmapfilters.cpp @@ -3,15 +3,20 @@ namespace skiagm { static void make_bm(SkBitmap* bm) { - const SkColor colors[] = { + const SkColor colors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE }; - SkColorTable* ctable = new SkColorTable(colors, 4); + SkPMColor colorsPM[4]; + for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) { + colorsPM[i] = SkPreMultiplyColor(colors[i]); + } + SkColorTable* ctable = new SkColorTable(colorsPM, 4); + bm->setConfig(SkBitmap::kIndex8_Config, 2, 2); bm->allocPixels(ctable); ctable->unref(); - + *bm->getAddr8(0, 0) = 0; *bm->getAddr8(1, 0) = 1; *bm->getAddr8(0, 1) = 2; @@ -57,7 +62,7 @@ static SkScalar draw_row(SkCanvas* canvas, const SkBitmap& bm) { canvas->translate(SkIntToScalar(48), 0); canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale)); - + x += draw_set(canvas, bm, 0, &paint); paint.reset(); paint.setAlpha(0x80); @@ -90,7 +95,7 @@ protected: SkScalar x = SkIntToScalar(10); SkScalar y = SkIntToScalar(10); - + canvas->translate(x, y); y = draw_row(canvas, fBM8); canvas->translate(0, y); @@ -100,7 +105,7 @@ protected: canvas->translate(0, y); draw_row(canvas, fBM32); } - + private: typedef GM INHERITED; }; diff --git a/gm/blurs.cpp b/gm/blurs.cpp index 26fdc7995e..c934178e4a 100644 --- a/gm/blurs.cpp +++ b/gm/blurs.cpp @@ -5,14 +5,14 @@ namespace skiagm { class BlursGM : public GM { public: - BlursGM() {} + BlursGM() {} protected: virtual SkString onShortName() { return SkString("blurs"); } - virtual SkISize onISize() { + virtual SkISize onISize() { return make_isize(700, 500); } @@ -37,8 +37,8 @@ protected: SkPaint paint; paint.setAntiAlias(true); - paint.setTextSize(25); - canvas->translate(-40, 0); + paint.setTextSize(SkIntToScalar(25)); + canvas->translate(SkIntToScalar(-40), SkIntToScalar(0)); SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag; for (int j = 0; j < 2; j++) { @@ -46,27 +46,32 @@ protected: paint.setColor(SK_ColorBLUE); for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) { if (gRecs[i].fStyle != NONE) { - SkMaskFilter* mf = SkBlurMaskFilter::Create(20, - gRecs[i].fStyle, - flags); + SkMaskFilter* mf = SkBlurMaskFilter::Create( + SkIntToScalar(20), gRecs[i].fStyle, flags + ); paint.setMaskFilter(mf)->unref(); } else { paint.setMaskFilter(NULL); } - canvas->drawCircle(200 + gRecs[i].fCx*100, - 200 + gRecs[i].fCy*100, 50, paint); + canvas->drawCircle(SkIntToScalar(200 + gRecs[i].fCx*100) + , SkIntToScalar(200 + gRecs[i].fCy*100) + , SkIntToScalar(50) + , paint); } // draw text { - SkMaskFilter* mf = SkBlurMaskFilter::Create(4, - SkBlurMaskFilter::kNormal_BlurStyle, - flags); + SkMaskFilter* mf = SkBlurMaskFilter::Create( + SkIntToScalar(4) + , SkBlurMaskFilter::kNormal_BlurStyle + , flags + ); paint.setMaskFilter(mf)->unref(); SkScalar x = SkIntToScalar(70); SkScalar y = SkIntToScalar(400); paint.setColor(SK_ColorBLACK); canvas->drawText("Hamburgefons Style", 18, x, y, paint); - canvas->drawText("Hamburgefons Style", 18, x, y + SkIntToScalar(50), paint); + canvas->drawText("Hamburgefons Style", 18 + , x, y + SkIntToScalar(50), paint); paint.setMaskFilter(NULL); paint.setColor(SK_ColorWHITE); x -= SkIntToScalar(2); @@ -75,7 +80,7 @@ protected: } canvas->restore(); flags = SkBlurMaskFilter::kHighQuality_BlurFlag; - canvas->translate(350, 0); + canvas->translate(SkIntToScalar(350), SkIntToScalar(0)); } } diff --git a/gm/gm_files.mk b/gm/gm_files.mk index e86782035b..fec20b6199 100644 --- a/gm/gm_files.mk +++ b/gm/gm_files.mk @@ -3,6 +3,7 @@ SOURCE := \ blurs.cpp \ filltypes.cpp \ gradients.cpp \ + nocolorbleed.cpp \ pathfill.cpp \ points.cpp \ poly2poly.cpp \ diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp index 6267fd4b85..ea205e34bd 100644 --- a/gm/gmmain.cpp +++ b/gm/gmmain.cpp @@ -441,7 +441,7 @@ int main(int argc, char * const argv[]) { const char* readPath = NULL; // if non-null, were we read from to compare const char* diffPath = NULL; // if non-null, where we write our diffs (from compare) - bool doReplay = false; + bool doReplay = true; bool doSerialize = false; const char* const commandName = argv[0]; char* const* stop = argv + argc; @@ -461,8 +461,8 @@ int main(int argc, char * const argv[]) { if (argv < stop && **argv) { diffPath = *argv; } - } else if (strcmp(*argv, "--replay") == 0) { - doReplay = true; + } else if (strcmp(*argv, "--noreplay") == 0) { + doReplay = false; } else if (strcmp(*argv, "--serialize") == 0) { doSerialize = true; } else { diff --git a/gm/nocolorbleed.cpp b/gm/nocolorbleed.cpp new file mode 100755 index 0000000000..3dec7ccc94 --- /dev/null +++ b/gm/nocolorbleed.cpp @@ -0,0 +1,75 @@ +#include "gm.h" + +namespace skiagm { + +class NoColorBleedGM : public GM { +public: + NoColorBleedGM() {} + +protected: + virtual SkString onShortName() { + return SkString("nocolorbleed"); + } + + virtual SkISize onISize() { + return make_isize(200, 200); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); + } + + virtual void onDraw(SkCanvas* canvas) { + drawBG(canvas); + + SkBitmap sprite; + sprite.setConfig(SkBitmap::kARGB_8888_Config, 4, 4, 4*sizeof(SkColor)); + const SkColor spriteData[16] = { + SK_ColorBLACK, SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW, + SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorRED, + SK_ColorGREEN, SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLUE, + SK_ColorYELLOW, SK_ColorMAGENTA, SK_ColorCYAN, SK_ColorBLACK + }; + sprite.allocPixels(); + sprite.lockPixels(); + SkPMColor* addr = sprite.getAddr32(0, 0); + for (size_t i = 0; i < SK_ARRAY_COUNT(spriteData); ++i) { + addr[i] = SkPreMultiplyColor(spriteData[i]); + } + sprite.unlockPixels(); + + // We draw a magnified subrect of the sprite + // sample interpolation may cause color bleeding around edges + // the subrect is a pure white area + SkIRect srcRect; + SkRect dstRect; + SkPaint paint; + paint.setFilterBitmap(true); + //First row : full texture with and without filtering + srcRect.setXYWH(0, 0, 4, 4); + dstRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0) + , SkIntToScalar(100), SkIntToScalar(100)); + canvas->drawBitmapRect(sprite, &srcRect, dstRect, &paint); + dstRect.setXYWH(SkIntToScalar(100), SkIntToScalar(0) + , SkIntToScalar(100), SkIntToScalar(100)); + canvas->drawBitmapRect(sprite, &srcRect, dstRect); + //Second row : sub rect of texture with and without filtering + srcRect.setXYWH(1, 1, 2, 2); + dstRect.setXYWH(SkIntToScalar(25), SkIntToScalar(125) + , SkIntToScalar(50), SkIntToScalar(50)); + canvas->drawBitmapRect(sprite, &srcRect, dstRect, &paint); + dstRect.setXYWH(SkIntToScalar(125), SkIntToScalar(125) + , SkIntToScalar(50), SkIntToScalar(50)); + canvas->drawBitmapRect(sprite, &srcRect, dstRect); + } + +private: + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new NoColorBleedGM; } +static GMRegistry reg(MyFactory); + +} diff --git a/gm/pathfill.cpp b/gm/pathfill.cpp index ec56942a96..713847f514 100644 --- a/gm/pathfill.cpp +++ b/gm/pathfill.cpp @@ -6,14 +6,15 @@ typedef SkScalar (*MakePathProc)(SkPath*); static SkScalar make_frame(SkPath* path) { - SkRect r = { 10, 10, 630, 470 }; - path->addRoundRect(r, 15, 15); + SkRect r = { SkIntToScalar(10), SkIntToScalar(10), + SkIntToScalar(630), SkIntToScalar(470) }; + path->addRoundRect(r, SkIntToScalar(15), SkIntToScalar(15)); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(5); + paint.setStrokeWidth(SkIntToScalar(5)); paint.getFillPath(*path, path); - return 15; + return SkIntToScalar(15); } static SkScalar make_triangle(SkPath* path) { @@ -24,21 +25,23 @@ static SkScalar make_triangle(SkPath* path) { path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3])); path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5])); path->close(); - path->offset(10, 0); + path->offset(SkIntToScalar(10), SkIntToScalar(0)); return SkIntToScalar(30); } static SkScalar make_rect(SkPath* path) { - SkRect r = { 10, 10, 30, 30 }; + SkRect r = { SkIntToScalar(10), SkIntToScalar(10), + SkIntToScalar(30), SkIntToScalar(30) }; path->addRect(r); - path->offset(10, 0); + path->offset(SkIntToScalar(10), SkIntToScalar(0)); return SkIntToScalar(30); } static SkScalar make_oval(SkPath* path) { - SkRect r = { 10, 10, 30, 30 }; + SkRect r = { SkIntToScalar(10), SkIntToScalar(10), + SkIntToScalar(30), SkIntToScalar(30) }; path->addOval(r); - path->offset(10, 0); + path->offset(SkIntToScalar(10), SkIntToScalar(0)); return SkIntToScalar(30); } @@ -56,8 +59,8 @@ static SkScalar make_sawtooth(SkPath* path) { x += dx; path->lineTo(x, y + dy); } - path->lineTo(x, y + 2 * dy); - path->lineTo(x0, y + 2 * dy); + path->lineTo(x, y + (2 * dy)); + path->lineTo(x0, y + (2 * dy)); path->close(); return SkIntToScalar(30); } @@ -100,7 +103,7 @@ class PathFillGM : public GM { SkPath fPath[N]; SkScalar fDY[N]; public: - PathFillGM() { + PathFillGM() { for (size_t i = 0; i < N; i++) { fDY[i] = gProcs[i](&fPath[i]); } @@ -111,7 +114,7 @@ protected: return SkString("pathfill"); } - virtual SkISize onISize() { + virtual SkISize onISize() { return make_isize(640, 480); } @@ -127,7 +130,7 @@ protected: for (size_t i = 0; i < N; i++) { canvas->drawPath(fPath[i], paint); - canvas->translate(0, fDY[i]); + canvas->translate(SkIntToScalar(0), fDY[i]); } } diff --git a/gm/shadertext.cpp b/gm/shadertext.cpp index 1cf562cc86..ea8782307d 100644 --- a/gm/shadertext.cpp +++ b/gm/shadertext.cpp @@ -11,7 +11,7 @@ static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) { bm->eraseColor(0); SkCanvas canvas(*bm); - SkScalar s = w < h ? w : h; + SkScalar s = SkIntToScalar(SkMin32(w, h)); SkPoint pts[] = { { 0, 0 }, { s, s } }; SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 }; @@ -121,7 +121,7 @@ protected: const char text[] = "Shaded Text"; const int textLen = SK_ARRAY_COUNT(text) - 1; - static int pointSize = SkIntToScalar(48); + const int pointSize = 48; int w = pointSize * textLen; int h = pointSize; @@ -194,5 +194,3 @@ static GM* MyFactory(void*) { return new ShaderTextGM; } static GMRegistry reg(MyFactory); } - - diff --git a/gm/shadows.cpp b/gm/shadows.cpp index 5afde495fe..bba997fb38 100644 --- a/gm/shadows.cpp +++ b/gm/shadows.cpp @@ -1,32 +1,29 @@ #include "gm.h" -#include "SkPicture.h" -#include "SkRectShape.h" #include "SkBlurDrawLooper.h" namespace skiagm { /////////////////////////////////////////////////////////////////////////////// -class ShadowsGM : public GM { +static void setup(SkPaint* paint, SkColor c, SkScalar strokeWidth) { + paint->setColor(c); + if (strokeWidth < 0) { + paint->setStyle(SkPaint::kFill_Style); + } else { + paint->setStyle(SkPaint::kStroke_Style); + paint->setStrokeWidth(strokeWidth); + } +} +class ShadowsGM : public GM { public: SkPath fCirclePath; - SkPaint fPaint; - SkRectShape fRectShape; + SkRect fRect; + ShadowsGM() { fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) ); - fPaint.setStrokeWidth(SkIntToScalar(4)); - fPaint.setAntiAlias(true); - fPaint.setColor(0xFF00FF00); - fPaint.setStyle(SkPaint::kStroke_Style); - SkRect rect; - rect.set(SkIntToScalar(10), SkIntToScalar(10), - SkIntToScalar(30), SkIntToScalar(30)); - fRectShape.setRect(rect); - fRectShape.paint().setColor(SK_ColorRED); - } - - virtual ~ShadowsGM() { + fRect.set(SkIntToScalar(10), SkIntToScalar(10), + SkIntToScalar(30), SkIntToScalar(30)); } protected: @@ -47,43 +44,61 @@ protected: SkBlurDrawLooper* shadowLoopers[5]; shadowLoopers[0] = - new SkBlurDrawLooper (10, 5, 10, 0xFF0000FF, + new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5), + SkIntToScalar(10), 0xFF0000FF, SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag ); SkAutoUnref aurL0(shadowLoopers[0]); shadowLoopers[1] = - new SkBlurDrawLooper (10, 5, 10, 0xFF0000FF, + new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5), + SkIntToScalar(10), 0xFF0000FF, SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag ); SkAutoUnref aurL1(shadowLoopers[1]); shadowLoopers[2] = - new SkBlurDrawLooper (5, 5, 10, 0xFF000000, + new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(5), + SkIntToScalar(10), 0xFF000000, SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag ); SkAutoUnref aurL2(shadowLoopers[2]); shadowLoopers[3] = - new SkBlurDrawLooper (5, -5 ,-10, 0x7FFF0000, + new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(-5), + SkIntToScalar(-10), 0x7FFF0000, SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag ); SkAutoUnref aurL3(shadowLoopers[3]); shadowLoopers[4] = - new SkBlurDrawLooper (0, 5, 5, 0xFF000000, + new SkBlurDrawLooper (SkIntToScalar(0), SkIntToScalar(5), + SkIntToScalar(5), 0xFF000000, SkBlurDrawLooper::kIgnoreTransform_BlurFlag | SkBlurDrawLooper::kOverrideColor_BlurFlag | SkBlurDrawLooper::kHighQuality_BlurFlag ); SkAutoUnref aurL4(shadowLoopers[4]); - for (int looper = 0; looper < 5; looper ++) - { - fRectShape.paint().setLooper(shadowLoopers[looper]); - canvas->resetMatrix(); - canvas->translate(SkIntToScalar(looper*40), SkIntToScalar(0)); - canvas->drawShape(&fRectShape); - fPaint.setLooper(shadowLoopers[looper]); + static const struct { + SkColor fColor; + SkScalar fStrokeWidth; + } gRec[] = { + { SK_ColorRED, -SK_Scalar1 }, + { SK_ColorGREEN, SkIntToScalar(4) }, + }; + + SkPaint paint; + paint.setAntiAlias(true); + for (size_t i = 0; i < SK_ARRAY_COUNT(shadowLoopers); ++i) { + SkAutoCanvasRestore acr(canvas, true); + + paint.setLooper(shadowLoopers[i]); + + canvas->translate(SkIntToScalar(i*40), SkIntToScalar(0)); + setup(&paint, gRec[0].fColor, gRec[0].fStrokeWidth); + canvas->drawRect(fRect, paint); + canvas->translate(SkIntToScalar(0), SkIntToScalar(40)); - canvas->drawPath(fCirclePath, fPaint); + setup(&paint, gRec[1].fColor, gRec[1].fStrokeWidth); + canvas->drawPath(fCirclePath, paint); } } diff --git a/gm/shapes.cpp b/gm/shapes.cpp index 324ce7e871..5daea0aa4d 100644 --- a/gm/shapes.cpp +++ b/gm/shapes.cpp @@ -57,6 +57,8 @@ public: for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) { SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i)); } + SkScalar c = SkIntToScalar(50); + fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c); } virtual ~ShapesGM() { @@ -81,10 +83,6 @@ protected: virtual void onDraw(SkCanvas* canvas) { this->drawBG(canvas); - SkMatrix saveM = *fMatrixRefs[3]; - SkScalar c = SkIntToScalar(50); - fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c); - SkMatrix matrix; SkGroupShape* gs = new SkGroupShape; @@ -111,8 +109,6 @@ protected: canvas->drawPicture(*pict); pict->unref(); #endif - - *fMatrixRefs[3] = saveM; } private: diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp index b716407212..891b95a8c3 100644 --- a/gm/strokerects.cpp +++ b/gm/strokerects.cpp @@ -29,14 +29,14 @@ static const SkScalar SH = SkIntToScalar(H); class StrokeRectGM : public GM { public: - StrokeRectGM() {} + StrokeRectGM() {} protected: virtual SkString onShortName() { return SkString("strokerects"); } - virtual SkISize onISize() { + virtual SkISize onISize() { return make_isize(W*2, H*2); } @@ -63,7 +63,10 @@ protected: SkAutoCanvasRestore acr(canvas, true); canvas->translate(SW * x, SH * y); - canvas->clipRect(SkRect::MakeLTRB(2, 2, SW - 2, SH - 2)); + canvas->clipRect(SkRect::MakeLTRB( + SkIntToScalar(2), SkIntToScalar(2) + , SW - SkIntToScalar(2), SH - SkIntToScalar(2) + )); SkRandom rand; for (int i = 0; i < N; i++) { diff --git a/gpu/include/GrClipIterator.h b/gpu/include/GrClipIterator.h index 1fcbdd178a..f7f74a76b1 100644 --- a/gpu/include/GrClipIterator.h +++ b/gpu/include/GrClipIterator.h @@ -47,7 +47,7 @@ public: * Return the current path. It is an error to call this when isDone() is * true or when getType() is kRect_Type. */ - virtual GrPathIter* getPathIter() = 0; + virtual const GrPath* getPath() = 0; /** * Return the fill rule for the path. It is an error to call this when diff --git a/gpu/include/GrConfig.h b/gpu/include/GrConfig.h index f1f24375ca..9ee37c73cc 100644 --- a/gpu/include/GrConfig.h +++ b/gpu/include/GrConfig.h @@ -189,7 +189,7 @@ typedef unsigned __int64 uint64_t; // debug -vs- release // -extern void GrPrintf(const char format[], ...); +extern GR_API void GrPrintf(const char format[], ...); /** * GR_STRING makes a string of X where X is expanded before conversion to a string diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h index 951c0e66e7..58c53ba27e 100644 --- a/gpu/include/GrContext.h +++ b/gpu/include/GrContext.h @@ -25,7 +25,6 @@ class GrFontCache; class GrGpu; struct GrGpuStats; -class GrPathIter; class GrVertexBufferAllocPool; class GrIndexBufferAllocPool; class GrInOrderDrawBuffer; @@ -191,35 +190,16 @@ public: * on failure. */ GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc); - /** - * DEPRECATED, WILL BE REMOVED SOON. USE createPlatformSurface. - * - * Wraps an externally-created rendertarget in a GrRenderTarget. - * @param platformRenderTarget 3D API-specific render target identifier - * e.g. in GL platforamRenderTarget is an FBO - * id. - * @param stencilBits the number of stencil bits that the render - * target has. - * @param isMultisampled specify whether the render target is - * multisampled. - * @param width width of the render target. - * @param height height of the render target. - */ - GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, int height); - - /** - * DEPRECATED, WILL BE REMOVED SOON. USE createPlatformSurface. - * * Reads the current target object (e.g. FBO or IDirect3DSurface9*) and * viewport state from the underlying 3D API and wraps it in a * GrRenderTarget. The GrRenderTarget will not attempt to delete/destroy the * underlying object in its destructor and it is up to caller to guarantee * that it remains valid while the GrRenderTarget is used. * + * Will not detect that the render target is also a texture. If you need + * to also use the render target as a GrTexture use createPlatformSurface. + * * @return the newly created GrRenderTarget */ GrRenderTarget* createRenderTargetFrom3DApiState(); @@ -323,22 +303,14 @@ public: * Draws a path. * * @param paint describes how to color pixels. - * @param pathIter the path to draw + * @param path the path to draw * @param fill the path filling rule to use. * @param translate optional additional translation applied to the * path. */ - void drawPath(const GrPaint& paint, - GrPathIter* pathIter, - GrPathFill fill, - const GrPoint* translate = NULL); - /** - * Helper version of drawPath that takes a GrPath - */ - void drawPath(const GrPaint& paint, - const GrPath& path, - GrPathFill fill, + void drawPath(const GrPaint& paint, const GrPath& path, GrPathFill fill, const GrPoint* translate = NULL); + /** * Draws vertices with a paint. * @@ -585,16 +557,9 @@ private: void drawClipIntoStencil(); - GrPathRenderer* getPathRenderer(const GrDrawTarget* target, - GrPathIter* path, - GrPathFill fill); + GrPathRenderer* getPathRenderer(const GrDrawTarget*, const GrPath&, GrPathFill); struct OffscreenRecord; - // we currently only expose stage 0 through the paint so use stage 1. We - // use stage 1 for the offscreen. - enum { - kOffscreenStage = 1, - }; bool doOffscreenAA(GrDrawTarget* target, const GrPaint& paint, @@ -613,6 +578,15 @@ private: const GrIRect& boundRect, OffscreenRecord* record); + // computes vertex layout bits based on the paint. If paint expresses + // a texture for a stage, the stage coords will be bound to postitions + // unless hasTexCoords[s]==true in which case stage s's input coords + // are bound to tex coord index s. hasTexCoords == NULL is a shortcut + // for an array where all the values are false. + static int PaintStageVertexLayoutBits( + const GrPaint& paint, + const bool hasTexCoords[GrPaint::kTotalStages]); + }; /** diff --git a/gpu/include/GrContext_impl.h b/gpu/include/GrContext_impl.h index fae4e923f9..c79a191e2f 100644 --- a/gpu/include/GrContext_impl.h +++ b/gpu/include/GrContext_impl.h @@ -40,19 +40,15 @@ inline void GrContext::drawCustomVertices(const GrPaint& paint, const COL_SRC* colorSrc, const IDX_SRC* idxSrc) { - GrVertexLayout layout = 0; - GrDrawTarget::AutoReleaseGeometry geo; GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); - if (NULL != paint.getTexture()) { - if (NULL != texCoordSrc) { - layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0); - } else { - layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); - } - } + bool hasTexCoords[GrPaint::kTotalStages] = { + NULL != texCoordSrc, // texCoordSrc provides explicit stage 0 coords + 0 // remaining stages use positions + }; + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords); if (NULL != colorSrc) { layout |= GrDrawTarget::kColor_VertexLayoutBit; diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h index 93b381dd9f..cd70d3e7c5 100644 --- a/gpu/include/GrDrawTarget.h +++ b/gpu/include/GrDrawTarget.h @@ -32,7 +32,6 @@ class GrTexture; class GrClipIterator; class GrVertexBuffer; class GrIndexBuffer; -class GrEffect; class GrDrawTarget : public GrRefCnt { public: @@ -51,10 +50,21 @@ public: * or not. */ enum { - kNumStages = 2, + kNumStages = 3, kMaxTexCoords = kNumStages }; + + /** + * The absolute maximum number of edges that may be specified for + * a single draw call when performing edge antialiasing. This is used for + * the size of several static buffers, so implementations of getMaxEdges() + * (below) should clamp to this value. + */ + enum { + kMaxEdges = 32 + }; + /** * Bitfield used to indicate which stages are in use. */ @@ -79,9 +89,6 @@ public: kNoColorWrites_StateBit = 0x08, //<! If set it disables writing colors. // Useful while performing stencil // ops. - kEdgeAA_StateBit = 0x10, //<! Perform edge anti-aliasing. - // Requires the edges to be passed in - // setEdgeAAData(). // subclass may use additional bits internally kDummyStateBit, @@ -129,6 +136,20 @@ public: fCurrDrawState.fStencilSettings.setDisabled(); } + class Edge { + public: + Edge() {} + Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {} + GrPoint intersect(const Edge& other) { + return GrPoint::Make( + (fY * other.fZ - other.fY * fZ) / + (fX * other.fY - other.fX * fY), + (fX * other.fZ - other.fX * fZ) / + (other.fX * fY - fX * other.fY)); + } + float fX, fY, fZ; + }; + protected: struct DrState { @@ -137,19 +158,26 @@ protected: // all DrState members should default to something // valid by the memset memset(this, 0, sizeof(DrState)); - // This is an exception to our memset, since it will - // result in no change. + + // memset exceptions fColorFilterXfermode = SkXfermode::kDstIn_Mode; + fFirstCoverageStage = kNumStages; + + // pedantic assertion that our ptrs will + // be NULL (0 ptr is mem addr 0) GrAssert((intptr_t)(void*)NULL == 0LL); + + // default stencil setting should be disabled GrAssert(fStencilSettings.isDisabled()); + fFirstCoverageStage = kNumStages; } uint32_t fFlagBits; GrBlendCoeff fSrcBlend; GrBlendCoeff fDstBlend; GrColor fBlendConstant; GrTexture* fTextures[kNumStages]; - GrEffect* fEffects[kNumStages]; GrSamplerState fSamplerStates[kNumStages]; + int fFirstCoverageStage; GrRenderTarget* fRenderTarget; GrColor fColor; DrawFace fDrawFace; @@ -158,7 +186,8 @@ protected: GrStencilSettings fStencilSettings; GrMatrix fViewMatrix; - float fEdgeAAEdges[18]; + Edge fEdgeAAEdges[kMaxEdges]; + int fEdgeAANumEdges; bool operator ==(const DrState& s) const { return 0 == memcmp(this, &s, sizeof(DrState)); } @@ -245,6 +274,18 @@ public: } /** + * Shortcut for preConcatSamplerMatrix on all stages in mask with same + * matrix + */ + void preConcatSamplerMatrices(int stageMask, const GrMatrix& matrix) { + for (int i = 0; i < kNumStages; ++i) { + if ((1 << i) & stageMask) { + this->preConcatSamplerMatrix(i, matrix); + } + } + } + + /** * Gets the matrix of a stage's sampler * * @param stage the stage to of sampler to get @@ -343,6 +384,26 @@ public: void setDrawFace(DrawFace face) { fCurrDrawState.fDrawFace = face; } /** + * A common pattern is to compute a color with the initial stages and then + * modulate that color by a coverage value in later stage(s) (AA, mask- + * filters, glyph mask, etc). Color-filters, xfermodes, etc should be + * computed based on the pre-coverage-modulated color. The division of + * stages between color-computing and coverage-computing is specified by + * this method. Initially this is kNumStages (all stages are color- + * computing). + */ + void setFirstCoverageStage(int firstCoverageStage) { + fCurrDrawState.fFirstCoverageStage = firstCoverageStage; + } + + /** + * Gets the index of the first coverage-computing stage. + */ + int getFirstCoverageStage() const { + return fCurrDrawState.fFirstCoverageStage; + } + + /** * Gets whether the target is drawing clockwise, counterclockwise, * or both faces. * @return the current draw face(s). @@ -392,7 +453,7 @@ public: * @param srcCoef coeffecient applied to the src color. * @param dstCoef coeffecient applied to the dst color. */ - void setBlendFunc(GrBlendCoeff srcCoef, GrBlendCoeff dstCoef); + void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff); /** * Sets the blending function constant referenced by the following blending @@ -498,7 +559,7 @@ public: * @param edges 3 * 6 float values, representing the edge * equations in Ax + By + C form */ - void setEdgeAAData(const float edges[18]); + void setEdgeAAData(const Edge* edges, int numEdges); private: static const int TEX_COORD_BIT_CNT = kNumStages*kMaxTexCoords; @@ -766,6 +827,15 @@ public: */ virtual void clear(const GrIRect* rect, GrColor color) = 0; + /** + * Returns the maximum number of edges that may be specified in a single + * draw call when performing edge antialiasing. This is usually limited + * by the number of fragment uniforms which may be uploaded. Must be a + * minimum of six, since a triangle's vertices each belong to two boundary + * edges which may be distinct. + */ + virtual int getMaxEdges() const { return 6; } + /////////////////////////////////////////////////////////////////////////// class AutoStateRestore : ::GrNoncopyable { diff --git a/gpu/include/GrFontScaler.h b/gpu/include/GrFontScaler.h index 77730d7eb9..ab73ea4f5c 100644 --- a/gpu/include/GrFontScaler.h +++ b/gpu/include/GrFontScaler.h @@ -21,7 +21,7 @@ #include "GrGlyph.h" #include "GrKey.h" -class GrPath; +class SkPath; /** * This is a virtual base class which Gr's interface to the host platform's @@ -37,7 +37,7 @@ public: virtual bool getPackedGlyphBounds(GrGlyph::PackedID, GrIRect* bounds) = 0; virtual bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height, int rowBytes, void* image) = 0; - virtual bool getGlyphPath(uint16_t glyphID, GrPath*) = 0; + virtual bool getGlyphPath(uint16_t glyphID, SkPath*) = 0; }; #endif diff --git a/gpu/include/GrGLDefines.h b/gpu/include/GrGLDefines.h index 29e56f3f44..6c2483bacf 100644 --- a/gpu/include/GrGLDefines.h +++ b/gpu/include/GrGLDefines.h @@ -69,6 +69,12 @@ /* GL_DST_ALPHA */ /* GL_ONE_MINUS_DST_ALPHA */ +/* ExtendedBlendFactors */ +#define GR_GL_SRC1_COLOR 0x88F9 +#define GR_GL_ONE_MINUS_SRC1_COLOR 0x88FA +/* GL_SRC1_ALPHA */ +#define GR_GL_ONE_MINUS_SRC1_ALPHA 0x88FB + /* BlendEquationSeparate */ #define GR_GL_FUNC_ADD 0x8006 #define GR_GL_BLEND_EQUATION 0x8009 @@ -318,6 +324,8 @@ #define GR_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GR_GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GR_GL_CURRENT_PROGRAM 0x8B8D +#define GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GR_GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A /* StencilFunction */ #define GR_GL_NEVER 0x0200 diff --git a/gpu/include/GrGLInterface.h b/gpu/include/GrGLInterface.h index 591ab8c2e5..150e8e469a 100644 --- a/gpu/include/GrGLInterface.h +++ b/gpu/include/GrGLInterface.h @@ -63,6 +63,7 @@ typedef unsigned int GrGLenum; typedef unsigned char GrGLboolean; typedef unsigned int GrGLbitfield; typedef signed char GrGLbyte; +typedef char GrGLchar; typedef short GrGLshort; typedef int GrGLint; typedef int GrGLsizei; @@ -199,6 +200,9 @@ extern "C" { // Buffer mapping (extension in ES). typedef GrGLvoid* (GR_GL_FUNCTION_TYPE *GrGLMapBufferProc)(GrGLenum target, GrGLenum access); typedef GrGLboolean (GR_GL_FUNCTION_TYPE *GrGLUnmapBufferProc)(GrGLenum target); + + // Dual source blending + typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name); } // extern "C" /* @@ -333,6 +337,9 @@ struct GrGLInterface { GrGLMapBufferProc fMapBuffer; GrGLUnmapBufferProc fUnmapBuffer; + // Dual Source Blending + GrGLBindFragDataLocationIndexedProc fBindFragDataLocationIndexed; + // Code that initializes this struct using a static initializer should // make this the last entry in the static initializer. It can help to guard // against failing to initialize newly-added members of this struct. diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h index e44956f709..574a430be9 100644 --- a/gpu/include/GrGpu.h +++ b/gpu/include/GrGpu.h @@ -60,6 +60,21 @@ class GrGpu : public GrDrawTarget { public: /** + * Additional blend coeffecients for dual source blending, not exposed + * through GrPaint/GrContext. + */ + enum ExtendedBlendCoeffs { + // source 2 refers to second output color when + // using dual source blending. + kS2C_BlendCoeff = kPublicBlendCoeffCount, + kIS2C_BlendCoeff, + kS2A_BlendCoeff, + kIS2A_BlendCoeff, + + kTotalBlendCoeffCount + }; + + /** * Create an instance of GrGpu that matches the specified Engine backend. * If the requested engine is not supported (at compile-time or run-time) * this returns NULL. @@ -109,20 +124,6 @@ public: */ GrTexture* createTexture(const GrTextureDesc& desc, const void* srcData, size_t rowBytes); - /** - * Wraps an externally-created rendertarget in a GrRenderTarget. - * @param platformRenderTarget handle to the the render target in the - * underlying 3D API. Interpretation depends on - * GrGpu subclass in use. - * @param stencilBits number of stencil bits the target has - * @param isMultisampled specify whether the RT is multisampled - * @param width width of the render target - * @param height height of the render target - */ - GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, int height); GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc); @@ -204,6 +205,15 @@ public: bool supports4x4DownsampleFilter() const { return f4X4DownsampleFilterSupport; } /** + * Does this instance support dual-source blending? Required for proper + * blending with partial coverage with certain blend modes (dst coeff is + * not 1, ISA, or ISC) + */ + bool supportsDualSourceBlending() const { + return fDualSourceBlendingSupport; + } + + /** * Gets the minimum width of a render target. If a texture/rt is created * with a width less than this size the GrGpu object will clamp it to this * value. @@ -385,6 +395,7 @@ protected: bool fAALineSupport; bool fFSAASupport; bool f4X4DownsampleFilterSupport; // supports GrSamplerState::k4x4Downsample_Filter + bool fDualSourceBlendingSupport; // set by subclass to true if index and vertex buffers can be locked, false // otherwise. @@ -427,11 +438,6 @@ protected: const void* srcData, size_t rowBytes) = 0; virtual GrResource* onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc) = 0; - virtual GrRenderTarget* onCreatePlatformRenderTarget( - intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, int height) = 0; virtual GrRenderTarget* onCreateRenderTargetFrom3DApiState() = 0; virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size, bool dynamic) = 0; @@ -513,8 +519,7 @@ private: void prepareIndexPool(); // determines the path renderer used to draw a clip path element. - GrPathRenderer* getClipPathRenderer(GrPathIter* path, - GrPathFill fill); + GrPathRenderer* getClipPathRenderer(const SkPath& path, GrPathFill fill); void handleDirtyContext() { if (fContextIsDirty) { diff --git a/gpu/include/GrPaint.h b/gpu/include/GrPaint.h index 3035ca1b67..a7923f5656 100644 --- a/gpu/include/GrPaint.h +++ b/gpu/include/GrPaint.h @@ -25,12 +25,17 @@ /** * The paint describes how pixels are colored when the context draws to - * them. + * them. TODO: Make this a "real" class with getters and setters, default + * values, and documentation. */ class GrPaint { public: + enum { + kMaxTextures = 1, + kMaxMasks = 1, + }; - // All the paint fields are public except texture (it's ref-counted) + // All the paint fields are public except textures/samplers GrBlendCoeff fSrcBlendCoeff; GrBlendCoeff fDstBlendCoeff; bool fAntiAlias; @@ -38,22 +43,75 @@ public: GrColor fColor; - GrSamplerState fSampler; - GrColor fColorFilterColor; SkXfermode::Mode fColorFilterXfermode; - void setTexture(GrTexture* texture) { + void setTexture(int i, GrTexture* texture) { + GrAssert((unsigned)i < kMaxTextures); GrSafeRef(texture); - GrSafeUnref(fTexture); - fTexture = texture; + GrSafeUnref(fTextures[i]); + fTextures[i] = texture; + } + + GrTexture* getTexture(int i) const { + GrAssert((unsigned)i < kMaxTextures); + return fTextures[i]; + } + + GrSamplerState* getTextureSampler(int i) { + GrAssert((unsigned)i < kMaxTextures); + return fTextureSamplers + i; + } + + const GrSamplerState* getTextureSampler(int i) const { + GrAssert((unsigned)i < kMaxTextures); + return fTextureSamplers + i; + } + + // The mask can be alpha-only or per channel. It is applied + // after the colorfilter + void setMask(int i, GrTexture* mask) { + GrAssert((unsigned)i < kMaxMasks); + GrSafeRef(mask); + GrSafeUnref(fMaskTextures[i]); + fMaskTextures[i] = mask; + } + + GrTexture* getMask(int i) const { + GrAssert((unsigned)i < kMaxMasks); + return fMaskTextures[i]; + } + + // mask's sampler matrix is always applied to the positions + // (i.e. no explicit texture coordinates) + GrSamplerState* getMaskSampler(int i) { + GrAssert((unsigned)i < kMaxMasks); + return fMaskSamplers + i; + } + + const GrSamplerState* getMaskSampler(int i) const { + GrAssert((unsigned)i < kMaxMasks); + return fMaskSamplers + i; } - GrTexture* getTexture() const { return fTexture; } + // pre-concats sampler matrices for non-NULL textures and masks + void preConcatActiveSamplerMatrices(const GrMatrix& matrix) { + for (int i = 0; i < kMaxTextures; ++i) { + fTextureSamplers[i].preConcatMatrix(matrix); + } + for (int i = 0; i < kMaxMasks; ++i) { + fMaskSamplers[i].preConcatMatrix(matrix); + } + } // uninitialized GrPaint() { - fTexture = NULL; + for (int i = 0; i < kMaxTextures; ++i) { + fTextures[i] = NULL; + } + for (int i = 0; i < kMaxMasks; ++i) { + fMaskTextures[i] = NULL; + } } GrPaint(const GrPaint& paint) { @@ -67,22 +125,35 @@ public: fColorFilterColor = paint.fColorFilterColor; fColorFilterXfermode = paint.fColorFilterXfermode; - fSampler = paint.fSampler; - fTexture = paint.fTexture; - GrSafeRef(fTexture); + for (int i = 0; i < kMaxTextures; ++i) { + fTextureSamplers[i] = paint.fTextureSamplers[i]; + fTextures[i] = paint.fTextures[i]; + GrSafeRef(fTextures[i]); + } + for (int i = 0; i < kMaxMasks; ++i) { + fMaskSamplers[i] = paint.fMaskSamplers[i]; + fMaskTextures[i] = paint.fMaskTextures[i]; + GrSafeRef(fMaskTextures[i]); + } } ~GrPaint() { - GrSafeUnref(fTexture); + for (int i = 0; i < kMaxTextures; ++i) { + GrSafeUnref(fTextures[i]); + } + for (int i = 0; i < kMaxMasks; ++i) { + GrSafeUnref(fMaskTextures[i]); + } } - // sets paint to src-over, solid white, no texture + // sets paint to src-over, solid white, no texture, no mask void reset() { - resetBlend(); - resetOptions(); - resetColor(); - resetTexture(); - resetColorFilter(); + this->resetBlend(); + this->resetOptions(); + this->resetColor(); + this->resetTextures(); + this->resetColorFilter(); + this->resetMasks(); } void resetColorFilter() { @@ -90,8 +161,60 @@ public: fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff); } + bool hasTexture() const { + return 0 != this->getActiveTextureStageMask(); + } + + bool hasMask() const { + return 0 != this->getActiveMaskStageMask(); + } + + bool hasTextureOrMask() const { + return this->hasTexture() || this->hasMask(); + } + + // helpers for GrContext, GrTextContext + int getActiveTextureStageMask() const { + int mask = 0; + for (int i = 0; i < kMaxTextures; ++i) { + if (NULL != fTextures[i]) { + mask |= 1 << (i + kFirstTextureStage); + } + } + return mask; + } + + int getActiveMaskStageMask() const { + int mask = 0; + for (int i = 0; i < kMaxMasks; ++i) { + if (NULL != fMaskTextures[i]) { + mask |= 1 << (i + kFirstMaskStage); + } + } + return mask; + } + + int getActiveStageMask() const { + return this->getActiveTextureStageMask() | + this->getActiveMaskStageMask(); + } + + // internal use + // GrPaint's textures and masks map to the first N stages + // of GrDrawTarget in that order (textures followed by masks) + enum { + kFirstTextureStage = 0, + kFirstMaskStage = kMaxTextures, + kTotalStages = kMaxTextures + kMaxMasks, + }; + private: - GrTexture* fTexture; + + GrSamplerState fTextureSamplers[kMaxTextures]; + GrSamplerState fMaskSamplers[kMaxMasks]; + + GrTexture* fTextures[kMaxTextures]; + GrTexture* fMaskTextures[kMaxMasks]; void resetBlend() { fSrcBlendCoeff = kOne_BlendCoeff; @@ -107,11 +230,19 @@ private: fColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff); } - void resetTexture() { - setTexture(NULL); - fSampler.setClampNoFilter(); + void resetTextures() { + for (int i = 0; i < kMaxTextures; ++i) { + this->setTexture(i, NULL); + fTextureSamplers[i].setClampNoFilter(); + } } + void resetMasks() { + for (int i = 0; i < kMaxMasks; ++i) { + this->setMask(i, NULL); + fMaskSamplers[i].setClampNoFilter(); + } + } }; #endif diff --git a/gpu/include/GrPath.h b/gpu/include/GrPath.h index f958329d3f..c23cfc47c8 100644 --- a/gpu/include/GrPath.h +++ b/gpu/include/GrPath.h @@ -18,91 +18,10 @@ #ifndef GrPath_DEFINED #define GrPath_DEFINED -#include "GrPathSink.h" -#include "GrPathIter.h" -#include "GrTDArray.h" -#include "GrPoint.h" -#include "GrRect.h" +#include "GrTypes.h" +#include "SkPath.h" -class GrPath : public GrPathSink { -public: - GrPath(); - GrPath(const GrPath&); - explicit GrPath(GrPathIter&); - virtual ~GrPath(); - - GrConvexHint getConvexHint() const { return fConvexHint; } - void setConvexHint(GrConvexHint hint) { fConvexHint = hint; } - - const GrRect& getConservativeBounds() const { return fConservativeBounds; } - - void resetFromIter(GrPathIter*); - - bool operator ==(const GrPath& path) const; - bool operator !=(const GrPath& path) const { return !(*this == path); } - // overrides from GrPathSink - - virtual void moveTo(GrScalar x, GrScalar y); - virtual void lineTo(GrScalar x, GrScalar y); - virtual void quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1); - virtual void cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1, - GrScalar x2, GrScalar y2); - virtual void close(); - - /** - * Offset the path by (tx, ty), adding tx to the horizontal position - * and adds ty to the vertical position of every point. - */ - void offset(GrScalar tx, GrScalar ty); - - class Iter : public GrPathIter { - public: - /** - * Creates an uninitialized iterator - */ - Iter(); - - Iter(const GrPath& path); - - // overrides from GrPathIter - virtual GrPathCmd next(GrPoint points[]); - virtual GrConvexHint convexHint() const; - virtual GrPathCmd next(); - virtual void rewind(); - virtual bool getConservativeBounds(GrRect* rect) const; - - /** - * Sets iterator to begining of path - */ - void reset(const GrPath& path); - private: - const GrPath* fPath; - GrPoint fLastPt; - int fCmdIndex; - int fPtIndex; - }; - - static void ConvexUnitTest(); - -private: - - GrTDArray<GrPathCmd> fCmds; - GrTDArray<GrPoint> fPts; - GrConvexHint fConvexHint; - GrRect fConservativeBounds; - - // this ensures we have a moveTo at the start of each contour - inline void ensureMoveTo(); - - bool wasLastVerb(GrPathCmd cmd) const { - int count = fCmds.count(); - return count > 0 && cmd == fCmds[count - 1]; - } - - friend class Iter; - - typedef GrPathSink INHERITED; -}; +typedef SkPath GrPath; #endif diff --git a/gpu/include/GrPathIter.h b/gpu/include/GrPathIter.h deleted file mode 100644 index e67ff69856..0000000000 --- a/gpu/include/GrPathIter.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright 2010 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - -#ifndef GrPathIter_DEFINED -#define GrPathIter_DEFINED - -#include "GrRect.h" - -/** - 2D Path iterator. Porting layer creates a subclass of this. It allows Ganesh to - parse the top-level API's 2D paths. Supports lines, quadratics, and cubic - pieces and moves (multi-part paths). - */ -class GrPathIter { -public: - - virtual ~GrPathIter() {}; - - /** - * Iterates through the path. Should not be called after - * kEnd_Command has been returned once. This version retrieves the - * points for the command. - * @param points The points relevant to returned commend. See Command - * enum for number of points valid for each command. - * @return The next command of the path. - */ - virtual GrPathCmd next(GrPoint points[4]) = 0; - - /** - * If the host API has knowledge of the convexity of the path - * it can be communicated by this hint. Gr can analyze the path - * as it is iterated. So it is not necessary to do additional work to - * compute convexity status if it isn't already determined. - * - * @return a hint about the convexity of the path. - */ - virtual GrConvexHint convexHint() const = 0; - - /** - * Iterates through the path. Should not be called after - * kEnd_Command has been returned once. This version does not retrieve the - * points for the command. - * @return The next command of the path. - */ - virtual GrPathCmd next() = 0; - - /** - * Returns conservative bounds on the path points. If returns false then - * no bounds are available. - */ - virtual bool getConservativeBounds(GrRect* rect) const = 0; - - /** - Restarts iteration from the beginning. - */ - virtual void rewind() = 0; - -}; - -#endif diff --git a/gpu/include/GrPathRenderer.h b/gpu/include/GrPathRenderer.h index 21cab6bb1a..1ebad4f530 100644 --- a/gpu/include/GrPathRenderer.h +++ b/gpu/include/GrPathRenderer.h @@ -19,7 +19,7 @@ #include "GrDrawTarget.h" -class GrPathIter; +class SkPath; struct GrPoint; /** @@ -37,8 +37,7 @@ public: * * @return true if the path can be drawn by this object, false otherwise. */ - virtual bool canDrawPath(const GrDrawTarget* target, - GrPathIter* path, + virtual bool canDrawPath(const GrDrawTarget* target, const SkPath& path, GrPathFill fill) const = 0; /** @@ -57,7 +56,7 @@ public: */ virtual void drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const SkPath& path, GrPathFill fill, const GrPoint* translate) = 0; @@ -80,7 +79,7 @@ public: * clips. */ virtual bool requiresStencilPass(const GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill) const { return false; } /** @@ -102,7 +101,7 @@ public: * the path. NULL means (0,0). */ virtual void drawPathToStencil(GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill, const GrPoint* translate) { GrCrash("Unexpected call to drawPathToStencil."); @@ -113,7 +112,7 @@ public: * having FSAA enabled for a render target) */ virtual bool supportsAA(GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill) { return false; } /** @@ -138,26 +137,26 @@ public: bool stencilWrapOpsSupport); virtual bool canDrawPath(const GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill) const { return true; } virtual void drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const SkPath& path, GrPathFill fill, const GrPoint* translate); virtual bool requiresStencilPass(const GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill) const; virtual void drawPathToStencil(GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill, const GrPoint* translate); private: void onDrawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const SkPath& path, GrPathFill fill, const GrPoint* translate, bool stencilOnly); diff --git a/gpu/include/GrSamplerState.h b/gpu/include/GrSamplerState.h index dd47c53726..373ea94cb3 100644 --- a/gpu/include/GrSamplerState.h +++ b/gpu/include/GrSamplerState.h @@ -94,6 +94,7 @@ public: fSampleMode = kNormal_SampleMode; fFilter = filter; fMatrix.setIdentity(); + fTextureDomain.setEmpty(); } GrSamplerState(WrapMode wx, WrapMode wy, Filter filter) { @@ -102,6 +103,7 @@ public: fSampleMode = kNormal_SampleMode; fFilter = filter; fMatrix.setIdentity(); + fTextureDomain.setEmpty(); } GrSamplerState(WrapMode wx, WrapMode wy, @@ -111,6 +113,7 @@ public: fSampleMode = kNormal_SampleMode; fFilter = filter; fMatrix = matrix; + fTextureDomain.setEmpty(); } GrSamplerState(WrapMode wx, WrapMode wy, SampleMode sample, @@ -120,12 +123,15 @@ public: fSampleMode = sample; fMatrix = matrix; fFilter = filter; + fTextureDomain.setEmpty(); } WrapMode getWrapX() const { return fWrapX; } WrapMode getWrapY() const { return fWrapY; } SampleMode getSampleMode() const { return fSampleMode; } const GrMatrix& getMatrix() const { return fMatrix; } + const GrRect& getTextureDomain() const { return fTextureDomain; } + bool hasTextureDomain() const {return SkIntToScalar(0) != fTextureDomain.right();} Filter getFilter() const { return fFilter; } bool isGradient() const { @@ -146,6 +152,13 @@ public: void setMatrix(const GrMatrix& matrix) { fMatrix = matrix; } /** + * Sets the sampler's texture coordinate domain to a + * custom rectangle, rather than the default (0,1). + * This option is currently only supported with kClamp_WrapMode + */ + void setTextureDomain(const GrRect& textureDomain) { fTextureDomain = textureDomain; } + + /** * Multiplies the current sampler matrix a matrix * * After this call M' = M*m where M is the old matrix, m is the parameter @@ -169,6 +182,7 @@ public: fSampleMode = kNormal_SampleMode; fFilter = kNearest_Filter; fMatrix.setIdentity(); + fTextureDomain.setEmpty(); } GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; } @@ -198,6 +212,7 @@ private: SampleMode fSampleMode; Filter fFilter; GrMatrix fMatrix; + GrRect fTextureDomain; // these are undefined unless fSampleMode == kRadial2_SampleMode GrScalar fRadial2CenterX1; diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h index a26b67c3bc..35cd61a22c 100644 --- a/gpu/include/GrScalar.h +++ b/gpu/include/GrScalar.h @@ -40,6 +40,7 @@ #define GrScalarHalf(a) SkScalarHalf(a) #define GrScalarAve(a,b) SkScalarAve(a,b) #define GrMul(a,b) SkScalarMul(a,b) +#define GrScalarDiv(a,b) SkScalarDiv(a, b) #define GrScalarToFloat(a) SkScalarToFloat(a) #define GrFloatToScalar(a) SkScalarToFloat(a) #define GrIntToScalar(a) SkIntToScalar(a) diff --git a/gpu/include/GrStencil.h b/gpu/include/GrStencil.h index b014ee5586..44a390d3fc 100644 --- a/gpu/include/GrStencil.h +++ b/gpu/include/GrStencil.h @@ -159,7 +159,7 @@ struct GrStencilSettings { return kKeep_StencilOp == fFrontPassOp && kKeep_StencilOp == fBackPassOp && kKeep_StencilOp == fFrontFailOp && - kKeep_StencilOp == fFrontFailOp && + kKeep_StencilOp == fBackFailOp && kAlways_StencilFunc == fFrontFunc && kAlways_StencilFunc == fBackFunc; } diff --git a/gpu/include/GrTesselatedPathRenderer.h b/gpu/include/GrTesselatedPathRenderer.h index accd114e4c..e37e66bdf8 100644 --- a/gpu/include/GrTesselatedPathRenderer.h +++ b/gpu/include/GrTesselatedPathRenderer.h @@ -25,22 +25,22 @@ public: virtual void drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const GrPath& path, GrPathFill fill, const GrPoint* translate); virtual bool canDrawPath(const GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill) const; virtual bool requiresStencilPass(const GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill) const { return false; } virtual void drawPathToStencil(GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill, const GrPoint* translate); virtual bool supportsAA(GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill); }; diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h index 08b10f0033..9348375987 100644 --- a/gpu/include/GrTypes.h +++ b/gpu/include/GrTypes.h @@ -18,11 +18,9 @@ #ifndef GrTypes_DEFINED #define GrTypes_DEFINED +#include "SkTypes.h" #include "GrConfig.h" -#include <memory.h> -#include <string.h> - //////////////////////////////////////////////////////////////////////////////// /** @@ -53,7 +51,7 @@ * Macro to round n up to the next multiple of 4, or return it unchanged if * n is already a multiple of 4 */ -#define GrALIGN4(n) (((n) + 3) >> 2 << 2) +#define GrALIGN4(n) SkAlign4(n) #define GrIsALIGN4(n) (((n) & 3) == 0) template <typename T> const T& GrMin(const T& a, const T& b) { @@ -111,7 +109,7 @@ static inline uint32_t GrSizeAlignDown(size_t x, uint32_t alignment) { /** * Count elements in an array */ -#define GR_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) +#define GR_ARRAY_COUNT(array) SK_ARRAY_COUNT(array) //!< allocate a block of memory, will never return NULL extern void* GrMalloc(size_t bytes); @@ -232,7 +230,7 @@ enum GrBlendCoeff { kConstA_BlendCoeff, //<! constant color alpha kIConstA_BlendCoeff, //<! one minus constant color alpha - kBlendCoeffCount + kPublicBlendCoeffCount }; /** diff --git a/gpu/src/GrBinHashKey.h b/gpu/src/GrBinHashKey.h index 683528b61e..565b4605a6 100644 --- a/gpu/src/GrBinHashKey.h +++ b/gpu/src/GrBinHashKey.h @@ -28,19 +28,20 @@ class GrBinHashKeyBuilder { public: GrBinHashKeyBuilder() {} virtual ~GrBinHashKeyBuilder() {} - virtual void keyData(const uint8_t *dataToAdd, size_t len) = 0; + virtual void keyData(const uint32_t *dataToAdd, size_t len) = 0; }; /** * Hash function class than can take a data stream of indeterminate length. - * It also has the ability to recieve data in several chunks (steamed). The - * hash function used is Adler-32. + * It also has the ability to recieve data in several chunks (steamed). The + * hash function used is the One-at-a-Time Hash + * (http://burtleburtle.net/bob/hash/doobs.html). * - * Keys are built in two passes the first pass builds the key until the + * Keys are built in two passes the first pass builds the key until the * allocated storage for the key runs out, raw data accumulation stops, but * the calculation of the 32-bit hash value and total key length continue. * The second pass is only necessary if storage ran-out during the first pass. - * If that is the case, the heap storage portion of the key will be + * If that is the case, the heap storage portion of the key will be * re-allocated so that the entire key can be stored in the second pass. * * Code for building a key: @@ -57,9 +58,8 @@ public: template<typename Entry, size_t StackSize> class GrBinHashKey : public GrBinHashKeyBuilder { public: - GrBinHashKey() - : fA(1) - , fB(0) + GrBinHashKey() + : fA(0) , fLength(0) , fHeapData(NULL) , fPhysicalSize(StackSize) @@ -77,15 +77,15 @@ private: // errors with template classes, which are hard to trace back to the use // of assignment. GrBinHashKey(const GrBinHashKey<Entry, StackSize>&) {} - GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry, + GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry, StackSize>&) { return this; } -public: +public: void copyAndTakeOwnership(GrBinHashKey<Entry, StackSize>& key) { - memcpy(this, &key, sizeof(*this)); GrAssert(key.fIsValid); + copyFields(key); if (fUseHeap) { key.fHeapData = NULL; // ownership transfer } @@ -98,7 +98,7 @@ public: void deepCopyFrom(const GrBinHashKey<Entry, StackSize>& key) { GrAssert(key.fIsValid); - memcpy(this, &key, sizeof(key)); + copyFields(key); if (fUseHeap) { fHeapData = reinterpret_cast<uint8_t*>( GrMalloc(sizeof(uint8_t) * fPhysicalSize)); @@ -121,7 +121,7 @@ public: if (1 == fPass) { bool passNeeded = false; if (fLength > fPhysicalSize) { - // If the first pass ran out of space the we need to + // If the first pass ran out of space the we need to // re-allocate and perform a second pass GrFree(fHeapData); fHeapData = reinterpret_cast<uint8_t*>( @@ -132,14 +132,15 @@ public: fLength = 0; } fPass++; - return passNeeded; + return passNeeded; } return false; } - void keyData(const uint8_t *dataToAdd, size_t len) { + void keyData(const uint32_t *dataToAdd, size_t len) { GrAssert(fIsValid); GrAssert(fPass); + GrAssert(GrIsALIGN4(len)); if (fUseHeap) { GrAssert(fHeapData); GrAssert(fLength + len <= fPhysicalSize); @@ -155,13 +156,18 @@ public: fLength += len; if (1 == fPass) { - // update the 32-bit hash - while (len) { - fA = (fA + *dataToAdd) % kBigPrime; - fB = (fB + fA) % kBigPrime; - dataToAdd++; - len--; + uint32_t a = fA; + while (len >= 4) { + a += *dataToAdd++; + a += (a << 10); + a ^= (a >> 6); + len -= 4; } + a += (a << 3); + a ^= (a >> 11); + a += (a << 15); + + fA = a; } } @@ -175,34 +181,44 @@ public: return memcmp(fStackData, key.fStackData, fLength); } } - + return (fLength - key.fLength); } - static bool + static bool EQ(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) { GrAssert(key.fIsValid); return 0 == entry.compare(key); } - - static bool + + static bool LT(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) { GrAssert(key.fIsValid); - return entry.compare(key) < 0; + return entry.compare(key) < 0; } uint32_t getHash() const { GrAssert(fIsValid); - return (fB << 16) | fA; + return fA; } private: - // For computing the Adler-32 hash - enum Constants { - kBigPrime = 65521 // largest prime smaller than 2^16 - }; + void copyFields(const GrBinHashKey<Entry, StackSize>& src) { + if (fUseHeap) { + GrFree(fHeapData); + } + // We do a field-by-field copy because this is a non-POD + // class, and therefore memcpy would be bad + fA = src.fA; + fLength = src.fLength; + memcpy(fStackData, src.fStackData, StackSize); + fHeapData = src.fHeapData; + fPhysicalSize = src.fPhysicalSize; + fUseHeap = src.fUseHeap; + fPass = src.fPass; + } + uint32_t fA; - uint32_t fB; // For accumulating the variable length binary key size_t fLength; // length of data accumulated so far diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp index 2d1680cff8..c2613bbe25 100644 --- a/gpu/src/GrClip.cpp +++ b/gpu/src/GrClip.cpp @@ -132,7 +132,7 @@ void GrClip::setFromIterator(GrClipIterator* iter, GrScalar tx, GrScalar ty, } break; case kPath_ClipType: - e.fPath.resetFromIter(iter->getPathIter()); + e.fPath = *iter->getPath(); if (tx || ty) { e.fPath.offset(tx, ty); } diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp index 8cb932b2c4..dae1cd14ab 100644 --- a/gpu/src/GrContext.cpp +++ b/gpu/src/GrContext.cpp @@ -19,14 +19,16 @@ #include "GrTextureCache.h" #include "GrTextStrike.h" #include "GrMemory.h" -#include "GrPathIter.h" #include "GrClipIterator.h" #include "GrIndexBuffer.h" #include "GrInOrderDrawBuffer.h" #include "GrBufferAllocPool.h" #include "GrPathRenderer.h" -#define ENABLE_OFFSCREEN_AA 0 +// larger than this, and we don't AA. set to 0 for no AA +#ifndef GR_MAX_OFFSCREEN_AA_DIM + #define GR_MAX_OFFSCREEN_AA_DIM 0 +#endif #define DEFER_TEXT_RENDERING 1 @@ -107,6 +109,26 @@ void GrContext::freeGpuResources() { //////////////////////////////////////////////////////////////////////////////// +int GrContext::PaintStageVertexLayoutBits( + const GrPaint& paint, + const bool hasTexCoords[GrPaint::kTotalStages]) { + int stageMask = paint.getActiveStageMask(); + int layout = 0; + for (int i = 0; i < GrPaint::kTotalStages; ++i) { + if ((1 << i) & stageMask) { + if (NULL != hasTexCoords && hasTexCoords[i]) { + layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(i, i); + } else { + layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i); + } + } + } + return layout; +} + + +//////////////////////////////////////////////////////////////////////////////// + enum { kNPOTBit = 0x1, kFilterBit = 0x2, @@ -360,22 +382,7 @@ GrResource* GrContext::createPlatformSurface(const GrPlatformSurfaceDesc& desc) return fGpu->createPlatformSurface(desc); } -GrRenderTarget* GrContext::createPlatformRenderTarget(intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, int height) { -#if GR_DEBUG - GrPrintf("Using deprecated createPlatformRenderTarget API."); -#endif - return fGpu->createPlatformRenderTarget(platformRenderTarget, - stencilBits, isMultisampled, - width, height); -} - GrRenderTarget* GrContext::createRenderTargetFrom3DApiState() { -#if GR_DEBUG - GrPrintf("Using deprecated createRenderTargetFrom3DApiState API."); -#endif return fGpu->createRenderTargetFrom3DApiState(); } @@ -447,7 +454,7 @@ void GrContext::drawPaint(const GrPaint& paint) { bool GrContext::doOffscreenAA(GrDrawTarget* target, const GrPaint& paint, bool isLines) const { -#if !ENABLE_OFFSCREEN_AA +#if GR_MAX_OFFSCREEN_AA_DIM==0 return false; #else if (!paint.fAntiAlias) { @@ -477,7 +484,7 @@ bool GrContext::setupOffscreenAAPass1(GrDrawTarget* target, bool requireStencil, const GrIRect& boundRect, OffscreenRecord* record) { - GrAssert(ENABLE_OFFSCREEN_AA); + GrAssert(GR_MAX_OFFSCREEN_AA_DIM > 0); GrAssert(NULL == record->fEntry0); GrAssert(NULL == record->fEntry1); @@ -577,6 +584,10 @@ void GrContext::offscreenAAPass2(GrDrawTarget* target, GrTexture* src = record->fEntry0->texture(); int scale; + enum { + kOffscreenStage = GrPaint::kTotalStages, + }; + if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) { GrAssert(NULL != record->fEntry1); scale = 2; @@ -606,11 +617,14 @@ void GrContext::offscreenAAPass2(GrDrawTarget* target, } // setup for draw back to main RT + int stageMask = paint.getActiveStageMask(); + target->restoreDrawState(record->fSavedState); - if (NULL != paint.getTexture()) { + + if (stageMask) { GrMatrix invVM; if (target->getViewInverse(&invVM)) { - target->preConcatSamplerMatrix(0, invVM); + target->preConcatSamplerMatrices(stageMask, invVM); } } target->setViewMatrix(GrMatrix::I()); @@ -624,7 +638,7 @@ void GrContext::offscreenAAPass2(GrDrawTarget* target, target->setSamplerState(kOffscreenStage, sampler); GrRect dstRect; - int stages = (1 << kOffscreenStage) | (NULL == paint.getTexture() ? 0 : 1); + int stages = (1 << kOffscreenStage) | stageMask; dstRect.set(boundRect); target->drawSimpleRect(dstRect, NULL, stages); @@ -662,11 +676,14 @@ static void setStrokeRectStrip(GrPoint verts[10], GrRect rect, } static GrColor getColorForMesh(const GrPaint& paint) { - if (NULL == paint.getTexture()) { - return paint.fColor; - } else { + // FIXME: This was copied from SkGpuDevice, seems like + // we should have already smeared a in caller if that + // is what is desired. + if (paint.hasTexture()) { unsigned a = GrColorUnpackA(paint.fColor); return GrColorPackRGBA(a, a, a, a); + } else { + return paint.fColor; } } @@ -742,10 +759,8 @@ void GrContext::fillAARect(GrDrawTarget* target, const GrPaint& paint, const GrRect& devRect) { - GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit; - if (NULL != paint.getTexture()) { - layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); - } + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) | + GrDrawTarget::kColor_VertexLayoutBit; size_t vsize = GrDrawTarget::VertexSize(layout); @@ -783,11 +798,8 @@ void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint, const GrScalar rx = GrMul(dx, GR_ScalarHalf); const GrScalar ry = GrMul(dy, GR_ScalarHalf); - GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit; - - if (NULL != paint.getTexture()) { - layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); - } + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) | + GrDrawTarget::kColor_VertexLayoutBit; GrScalar spare; { @@ -903,9 +915,9 @@ void GrContext::drawRect(const GrPaint& paint, GrScalar width, const GrMatrix* matrix) { - bool textured = NULL != paint.getTexture(); GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); + int stageMask = paint.getActiveStageMask(); GrRect devRect = rect; GrMatrix combinedMatrix; @@ -914,10 +926,10 @@ void GrContext::drawRect(const GrPaint& paint, if (doAA) { GrDrawTarget::AutoViewMatrixRestore avm(target); - if (textured) { + if (stageMask) { GrMatrix inv; if (combinedMatrix.invert(&inv)) { - target->preConcatSamplerMatrix(0, inv); + target->preConcatSamplerMatrices(stageMask, inv); } } target->setViewMatrix(GrMatrix::I()); @@ -941,9 +953,8 @@ void GrContext::drawRect(const GrPaint& paint, // TODO: consider making static vertex buffers for these cases. // Hairline could be done by just adding closing vertex to // unitSquareVertexBuffer() - GrVertexLayout layout = textured ? - GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) : - 0; + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL); + static const int worstCaseVertCount = 10; GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0); @@ -974,17 +985,14 @@ void GrContext::drawRect(const GrPaint& paint, if (NULL != matrix) { avmr.set(target); target->preConcatViewMatrix(*matrix); - if (textured) { - target->preConcatSamplerMatrix(0, *matrix); - } + target->preConcatSamplerMatrices(stageMask, *matrix); } target->drawNonIndexed(primType, 0, vertCount); } else { #if GR_STATIC_RECT_VB - GrVertexLayout layout = (textured) ? - GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) : - 0; + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL); + target->setVertexSourceToBuffer(layout, fGpu->getUnitSquareVertexBuffer()); GrDrawTarget::AutoViewMatrixRestore avmr(target); @@ -998,13 +1006,11 @@ void GrContext::drawRect(const GrPaint& paint, } target->preConcatViewMatrix(m); - - if (textured) { - target->preConcatSamplerMatrix(0, m); - } + target->preConcatSamplerMatrices(stageMask, m); + target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4); #else - target->drawSimpleRect(rect, matrix, textured ? 1 : 0); + target->drawSimpleRect(rect, matrix, stageMask); #endif } } @@ -1015,7 +1021,8 @@ void GrContext::drawRectToRect(const GrPaint& paint, const GrMatrix* dstMatrix, const GrMatrix* srcMatrix) { - if (NULL == paint.getTexture()) { + // srcRect refers to paint's first texture + if (NULL == paint.getTexture(0)) { drawRect(paint, dstRect, -1, dstMatrix); return; } @@ -1024,8 +1031,8 @@ void GrContext::drawRectToRect(const GrPaint& paint, #if GR_STATIC_RECT_VB GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); - - GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); + + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL); GrDrawTarget::AutoViewMatrixRestore avmr(target); GrMatrix m; @@ -1038,13 +1045,20 @@ void GrContext::drawRectToRect(const GrPaint& paint, } target->preConcatViewMatrix(m); + // srcRect refers to first stage + int otherStageMask = paint.getActiveStageMask() & + (~(1 << GrPaint::kFirstTextureStage)); + if (otherStageMask) { + target->preConcatSamplerMatrices(otherStageMask, m); + } + m.setAll(srcRect.width(), 0, srcRect.fLeft, 0, srcRect.height(), srcRect.fTop, 0, 0, GrMatrix::I()[8]); if (NULL != srcMatrix) { m.postConcat(*srcMatrix); } - target->preConcatSamplerMatrix(0, m); + target->preConcatSamplerMatrix(GrPaint::kFirstTextureStage, m); target->setVertexSourceToBuffer(layout, fGpu->getUnitSquareVertexBuffer()); target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4); @@ -1074,26 +1088,22 @@ void GrContext::drawVertices(const GrPaint& paint, const GrColor colors[], const uint16_t indices[], int indexCount) { - GrVertexLayout layout = 0; - int vertexSize = sizeof(GrPoint); GrDrawTarget::AutoReleaseGeometry geo; GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); - if (NULL != paint.getTexture()) { - if (NULL == texCoords) { - layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); - } else { - layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0); - vertexSize += sizeof(GrPoint); - } - } + bool hasTexCoords[GrPaint::kTotalStages] = { + NULL != texCoords, // texCoordSrc provides explicit stage 0 coords + 0 // remaining stages use positions + }; + + GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords); if (NULL != colors) { layout |= GrDrawTarget::kColor_VertexLayoutBit; - vertexSize += sizeof(GrColor); } + int vertexSize = GrDrawTarget::VertexSize(layout); bool doAA = false; OffscreenRecord record; @@ -1106,9 +1116,9 @@ void GrContext::drawVertices(const GrPaint& paint, } int texOffsets[GrDrawTarget::kMaxTexCoords]; int colorOffset; - int vsize = GrDrawTarget::VertexSizeAndOffsetsByIdx(layout, - texOffsets, - &colorOffset); + GrDrawTarget::VertexSizeAndOffsetsByIdx(layout, + texOffsets, + &colorOffset); void* curVertex = geo.vertices(); for (int i = 0; i < vertexCount; ++i) { @@ -1120,7 +1130,7 @@ void GrContext::drawVertices(const GrPaint& paint, if (colorOffset > 0) { *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i]; } - curVertex = (void*)((intptr_t)curVertex + vsize); + curVertex = (void*)((intptr_t)curVertex + vertexSize); } } else { // we don't do offscreen AA when we have per-vertex tex coords or colors @@ -1155,10 +1165,8 @@ void GrContext::drawVertices(const GrPaint& paint, /////////////////////////////////////////////////////////////////////////////// -void GrContext::drawPath(const GrPaint& paint, - GrPathIter* path, - GrPathFill fill, - const GrPoint* translate) { +void GrContext::drawPath(const GrPaint& paint, const GrPath& path, + GrPathFill fill, const GrPoint* translate) { GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); GrPathRenderer* pr = this->getPathRenderer(target, path, fill); @@ -1180,9 +1188,10 @@ void GrContext::drawPath(const GrPaint& paint, return; } } - GrRect pathBounds; - if (path->getConservativeBounds(&pathBounds)) { - GrIRect pathIBounds; + + GrRect pathBounds = path.getBounds(); + GrIRect pathIBounds; + if (!pathBounds.isEmpty()) { target->getViewMatrix().mapRect(&pathBounds, pathBounds); pathBounds.roundOut(&pathIBounds); if (!bound.intersect(pathIBounds)) { @@ -1190,29 +1199,27 @@ void GrContext::drawPath(const GrPaint& paint, } } + // for now, abort antialiasing if our bounds are too big, so we don't + // hit the FBO size limit + if (pathIBounds.width() > GR_MAX_OFFSCREEN_AA_DIM || + pathIBounds.height() > GR_MAX_OFFSCREEN_AA_DIM) { + goto NO_AA; + } + if (this->setupOffscreenAAPass1(target, needsStencil, bound, &record)) { pr->drawPath(target, 0, path, fill, translate); this->offscreenAAPass2(target, paint, bound, &record); return; } - } - GrDrawTarget::StageBitfield enabledStages = 0; - if (NULL != paint.getTexture()) { - enabledStages |= 1; } - pr->drawPath(target, enabledStages, path, fill, translate); -} +// we can fall out of the AA section for some reasons, and land here +NO_AA: + GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask(); -void GrContext::drawPath(const GrPaint& paint, - const GrPath& path, - GrPathFill fill, - const GrPoint* translate) { - GrPath::Iter iter(path); - this->drawPath(paint, &iter, fill, translate); + pr->drawPath(target, enabledStages, path, fill, translate); } - //////////////////////////////////////////////////////////////////////////////// void GrContext::flush(int flagsBitfield) { @@ -1320,8 +1327,21 @@ void GrContext::writePixels(int left, int top, int width, int height, //////////////////////////////////////////////////////////////////////////////// void GrContext::SetPaint(const GrPaint& paint, GrDrawTarget* target) { - target->setTexture(0, paint.getTexture()); - target->setSamplerState(0, paint.fSampler); + + for (int i = 0; i < GrPaint::kMaxTextures; ++i) { + int s = i + GrPaint::kFirstTextureStage; + target->setTexture(s, paint.getTexture(i)); + target->setSamplerState(s, *paint.getTextureSampler(i)); + } + + target->setFirstCoverageStage(GrPaint::kFirstMaskStage); + + for (int i = 0; i < GrPaint::kMaxMasks; ++i) { + int s = i + GrPaint::kFirstMaskStage; + target->setTexture(s, paint.getMask(i)); + target->setSamplerState(s, *paint.getMaskSampler(i)); + } + target->setColor(paint.fColor); if (paint.fDither) { @@ -1483,7 +1503,7 @@ const GrIndexBuffer* GrContext::getQuadIndexBuffer() const { } GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill) { if (NULL != fCustomPathRenderer && fCustomPathRenderer->canDrawPath(target, path, fill)) { diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp index 518b4eed6d..2848999fe4 100644 --- a/gpu/src/GrDrawTarget.cpp +++ b/gpu/src/GrDrawTarget.cpp @@ -19,6 +19,8 @@ #include "GrGpuVertex.h" #include "GrTexture.h" +namespace { + // recursive helper for creating mask with all the tex coord bits set for // one stage template <int N> @@ -26,16 +28,16 @@ int stage_mask_recur(int stage) { return GrDrawTarget::StageTexCoordVertexLayoutBit(stage, N) | stage_mask_recur<N+1>(stage); } -template<> // linux build doesn't like static on specializations +template<> int stage_mask_recur<GrDrawTarget::kNumStages>(int) { return 0; } // mask of all tex coord indices for one stage -static int stage_tex_coord_mask(int stage) { +int stage_tex_coord_mask(int stage) { return stage_mask_recur<0>(stage); } // mask of all bits relevant to one stage -static int stage_mask(int stage) { +int stage_mask(int stage) { return stage_tex_coord_mask(stage) | GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(stage); } @@ -47,11 +49,11 @@ int tex_coord_mask_recur(int texCoordIdx) { return GrDrawTarget::StageTexCoordVertexLayoutBit(N, texCoordIdx) | tex_coord_mask_recur<N+1>(texCoordIdx); } -template<> // linux build doesn't like static on specializations +template<> int tex_coord_mask_recur<GrDrawTarget::kMaxTexCoords>(int) { return 0; } // mask of all bits relevant to one texture coordinate index -static int tex_coord_idx_mask(int texCoordIdx) { +int tex_coord_idx_mask(int texCoordIdx) { return tex_coord_mask_recur<0>(texCoordIdx); } @@ -66,6 +68,8 @@ bool check_layout(GrVertexLayout layout) { return true; } +} //unnamed namespace + size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) { GrAssert(check_layout(vertexLayout)); @@ -370,10 +374,34 @@ void GrDrawTarget::disableState(uint32_t bits) { fCurrDrawState.fFlagBits &= ~(bits); } -void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoef, - GrBlendCoeff dstCoef) { - fCurrDrawState.fSrcBlend = srcCoef; - fCurrDrawState.fDstBlend = dstCoef; +void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoeff, + GrBlendCoeff dstCoeff) { + fCurrDrawState.fSrcBlend = srcCoeff; + fCurrDrawState.fDstBlend = dstCoeff; +#if GR_DEBUG + switch (dstCoeff) { + case kDC_BlendCoeff: + case kIDC_BlendCoeff: + case kDA_BlendCoeff: + case kIDA_BlendCoeff: + GrPrintf("Unexpected dst blend coeff. Won't work correctly with" + "coverage stages.\n"); + break; + default: + break; + } + switch (srcCoeff) { + case kSC_BlendCoeff: + case kISC_BlendCoeff: + case kSA_BlendCoeff: + case kISA_BlendCoeff: + GrPrintf("Unexpected src blend coeff. Won't work correctly with" + "coverage stages.\n"); + break; + default: + break; + } +#endif } void GrDrawTarget::setColor(GrColor c) { @@ -478,10 +506,16 @@ void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) { /////////////////////////////////////////////////////////////////////////////// bool GrDrawTarget::canDisableBlend() const { - // If we're using edge antialiasing, we can't force blend off. - if (fCurrDrawState.fFlagBits & kEdgeAA_StateBit) { + // If we compute a coverage value (using edge AA or a coverage stage) then + // we can't force blending off. + if (fCurrDrawState.fEdgeAANumEdges > 0) { return false; } + for (int s = fCurrDrawState.fFirstCoverageStage; s < kNumStages; ++s) { + if (this->isStageEnabled(s)) { + return false; + } + } if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) && (kZero_BlendCoeff == fCurrDrawState.fDstBlend)) { @@ -506,8 +540,8 @@ bool GrDrawTarget::canDisableBlend() const { return false; } - // ...and there isn't a texture with an alpha channel... - for (int s = 0; s < kNumStages; ++s) { + // ...and there isn't a texture stage with an alpha channel... + for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) { if (this->isStageEnabled(s)) { GrAssert(NULL != fCurrDrawState.fTextures[s]); @@ -531,8 +565,10 @@ bool GrDrawTarget::canDisableBlend() const { } /////////////////////////////////////////////////////////////////////////////// -void GrDrawTarget::setEdgeAAData(const float edges[18]) { - memcpy(fCurrDrawState.fEdgeAAEdges, edges, sizeof(fCurrDrawState.fEdgeAAEdges)); +void GrDrawTarget::setEdgeAAData(const Edge* edges, int numEdges) { + GrAssert(numEdges <= kMaxEdges); + memcpy(fCurrDrawState.fEdgeAAEdges, edges, numEdges * sizeof(Edge)); + fCurrDrawState.fEdgeAANumEdges = numEdges; } diff --git a/gpu/src/GrGLEffect.h b/gpu/src/GrGLEffect.h deleted file mode 100644 index ef00df8f4b..0000000000 --- a/gpu/src/GrGLEffect.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2011 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#ifndef GrGLEffect_DEFINED -#define GrGLEffect_DEFINED - -#include "GrGLInterface.h" -#include "GrStringBuilder.h" - -class GrEffect; - -struct ShaderCodeSegments { - GrStringBuilder fVSUnis; - GrStringBuilder fVSAttrs; - GrStringBuilder fVaryings; - GrStringBuilder fFSUnis; - GrStringBuilder fVSCode; - GrStringBuilder fFSCode; -}; - -/** - * This class is currently a stub. This will be a base class for "effects", - * which extend the data model of GrPaint and extend the capability of - * GrGLProgram in a modular fashion. - */ -class GrGLEffect { -protected: - GrGLEffect(GrEffect* effect) {} -public: - virtual ~GrGLEffect() {} - static GrGLEffect* Create(GrEffect* effect) { return NULL; } - void genShaderCode(ShaderCodeSegments* segments) {} - bool doGLSetup(GrPrimitiveType type, GrGLint program) { return true; } - bool doGLPost() { return true; } - void buildKey(GrBinHashKeyBuilder& key) const {} - GrGLEffect* nextEffect() { return NULL; } -}; - -#endif diff --git a/gpu/src/GrGLInterface.cpp b/gpu/src/GrGLInterface.cpp index 0825a3f925..5ecf8eb591 100644 --- a/gpu/src/GrGLInterface.cpp +++ b/gpu/src/GrGLInterface.cpp @@ -333,6 +333,15 @@ bool GrGLInterface::validate(GrEngine engine) const { } } + // Dual source blending + if (kDesktop_GrGLBinding == fBindingsExported && + (has_gl_extension_from_string("GL_ARB_blend_func_extended", ext) || + (3 < major) || (3 == major && 3 <= minor))) { + if (NULL == fBindFragDataLocationIndexed) { + return false; + } + } + return true; } diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp index 5d2d8b342d..ecb4753fea 100644 --- a/gpu/src/GrGLProgram.cpp +++ b/gpu/src/GrGLProgram.cpp @@ -18,7 +18,6 @@ #include "GrBinHashKey.h" #include "GrGLConfig.h" -#include "GrGLEffect.h" #include "GrMemory.h" #include "SkXfermode.h" @@ -86,6 +85,23 @@ static inline const char* vector_all_coords(int count) { return ALL[count]; } +static inline const char* all_ones_vec(int count) { + static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)", + "vec3(1,1,1)", "vec4(1,1,1,1)"}; + GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC)); + return ONESVEC[count]; +} + +static inline const char* all_zeros_vec(int count) { + static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)", + "vec3(0,0,0)", "vec4(0,0,0,0)"}; + GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC)); + return ZEROSVEC[count]; +} + +static inline const char* declared_color_output_name() { return "fsColorOut"; } +static inline const char* dual_source_output_name() { return "dualSourceOut"; } + static void tex_matrix_name(int stage, GrStringBuilder* s) { #if GR_GL_ATTRIBUTE_MATRICES *s = "aTexM"; @@ -120,113 +136,194 @@ static void radial2_varying_name(int stage, GrStringBuilder* s) { s->appendS32(stage); } +static void tex_domain_name(int stage, GrStringBuilder* s) { + *s = "uTexDom"; + s->appendS32(stage); +} + GrGLProgram::GrGLProgram() { - for(int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) { - fStageEffects[stage] = NULL; - } } GrGLProgram::~GrGLProgram() { } +void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff, + GrBlendCoeff* dstCoeff) const { + switch (fProgramDesc.fDualSrcOutput) { + case ProgramDesc::kNone_DualSrcOutput: + break; + // the prog will write a coverage value to the secondary + // output and the dst is blended by one minus that value. + case ProgramDesc::kCoverage_DualSrcOutput: + case ProgramDesc::kCoverageISA_DualSrcOutput: + case ProgramDesc::kCoverageISC_DualSrcOutput: + *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff; + break; + default: + GrCrash("Unexpected dual source blend output"); + break; + } +} + void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const { // Add stage configuration to the key - key.keyData(reinterpret_cast<const uint8_t*>(&fProgramDesc), sizeof(ProgramDesc)); - - for(int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) { - // First pass: count effects and write the count to the key. - // This may seem like we are adding redundant data to the - // key, but in ensures the one key cannot be a prefix of - // another key, or identical to the key of a different program. - GrGLEffect* currentEffect = fStageEffects[stage]; - uint8_t effectCount = 0; - while (currentEffect) { - GrAssert(effectCount < 255); // overflow detection - ++effectCount; - currentEffect = currentEffect->nextEffect(); - } - key.keyData(reinterpret_cast<const uint8_t*>(&effectCount), sizeof(uint8_t)); + key.keyData(reinterpret_cast<const uint32_t*>(&fProgramDesc), sizeof(ProgramDesc)); +} - // Second pass: continue building key using the effects - currentEffect = fStageEffects[stage]; - while (currentEffect) { - fStageEffects[stage]->buildKey(key); - } +// assigns modulation of two vars to an output var +// vars can be vec4s or floats (or one of each) +// result is always vec4 +// if either var is "" then assign to the other var +// if both are "" then assign all ones +static inline void modulate_helper(const char* outputVar, + const char* var0, + const char* var1, + GrStringBuilder* code) { + GrAssert(NULL != outputVar); + GrAssert(NULL != var0); + GrAssert(NULL != var1); + GrAssert(NULL != code); + + bool has0 = '\0' != *var0; + bool has1 = '\0' != *var1; + + if (!has0 && !has1) { + code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4)); + } else if (!has0) { + code->appendf("\t%s = vec4(%s);\n", outputVar, var1); + } else if (!has1) { + code->appendf("\t%s = vec4(%s);\n", outputVar, var0); + } else { + code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1); } } -bool GrGLProgram::doGLSetup(GrPrimitiveType type, - GrGLProgram::CachedData* programData) const { - for (int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) { - GrGLEffect* effect = fStageEffects[stage]; - if (effect) { - if (!effect->doGLSetup(type, programData->fProgramID)) { - return false; - } - } +// assigns addition of two vars to an output var +// vars can be vec4s or floats (or one of each) +// result is always vec4 +// if either var is "" then assign to the other var +// if both are "" then assign all zeros +static inline void add_helper(const char* outputVar, + const char* var0, + const char* var1, + GrStringBuilder* code) { + GrAssert(NULL != outputVar); + GrAssert(NULL != var0); + GrAssert(NULL != var1); + GrAssert(NULL != code); + + bool has0 = '\0' != *var0; + bool has1 = '\0' != *var1; + + if (!has0 && !has1) { + code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4)); + } else if (!has0) { + code->appendf("\t%s = vec4(%s);\n", outputVar, var1); + } else if (!has1) { + code->appendf("\t%s = vec4(%s);\n", outputVar, var0); + } else { + code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1); } - - return true; } -void GrGLProgram::doGLPost() const { - for (int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) { - GrGLEffect* effect = fStageEffects[stage]; - if (effect) { - effect->doGLPost(); - } +// given two blend coeffecients determine whether the src +// and/or dst computation can be omitted. +static inline void needBlendInputs(SkXfermode::Coeff srcCoeff, + SkXfermode::Coeff dstCoeff, + bool* needSrcValue, + bool* needDstValue) { + if (SkXfermode::kZero_Coeff == srcCoeff) { + switch (dstCoeff) { + // these all read the src + case SkXfermode::kSC_Coeff: + case SkXfermode::kISC_Coeff: + case SkXfermode::kSA_Coeff: + case SkXfermode::kISA_Coeff: + *needSrcValue = true; + break; + default: + *needSrcValue = false; + break; + } + } else { + *needSrcValue = true; + } + if (SkXfermode::kZero_Coeff == dstCoeff) { + switch (srcCoeff) { + // these all read the dst + case SkXfermode::kDC_Coeff: + case SkXfermode::kIDC_Coeff: + case SkXfermode::kDA_Coeff: + case SkXfermode::kIDA_Coeff: + *needDstValue = true; + break; + default: + *needDstValue = false; + break; + } + } else { + *needDstValue = true; } } /** - * Create a text coefficient to be used in fragment shader code. + * Create a blend_coeff * value string to be used in shader code. Sets empty + * string if result is trivially zero. */ -static void coefficientString(GrStringBuilder* str, SkXfermode::Coeff coeff, - const char* src, const char* dst) { +static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff, + const char* src, const char* dst, + const char* value) { switch (coeff) { case SkXfermode::kZero_Coeff: /** 0 */ - *str = "0.0"; + *str = ""; break; case SkXfermode::kOne_Coeff: /** 1 */ - *str = "1.0"; + *str = value; + break; + case SkXfermode::kSC_Coeff: + str->printf("(%s * %s)", src, value); + break; + case SkXfermode::kISC_Coeff: + str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value); + break; + case SkXfermode::kDC_Coeff: + str->printf("(%s * %s)", dst, value); + break; + case SkXfermode::kIDC_Coeff: + str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value); break; case SkXfermode::kSA_Coeff: /** src alpha */ - str->appendf("%s.a", src); + str->printf("(%s.a * %s)", src, value); break; case SkXfermode::kISA_Coeff: /** inverse src alpha (i.e. 1 - sa) */ - str->appendf("(1.0 - %s.a)", src); + str->printf("((1.0 - %s.a) * %s)", src, value); break; case SkXfermode::kDA_Coeff: /** dst alpha */ - str->appendf("%s.a", dst); + str->printf("(%s.a * %s)", dst, value); break; case SkXfermode::kIDA_Coeff: /** inverse dst alpha (i.e. 1 - da) */ - str->appendf("(1.0 - %s.a)", dst); - break; - case SkXfermode::kSC_Coeff: - str->append(src); + str->printf("((1.0 - %s.a) * %s)", dst, value); break; default: + GrCrash("Unexpected xfer coeff."); break; } } - /** * Adds a line to the fragment shader code which modifies the color by * the specified color filter. */ -static void addColorFilter(GrStringBuilder* FSCode, const char * outputVar, - SkXfermode::Mode colorFilterXfermode, const char* dstColor) { - SkXfermode::Coeff srcCoeff, dstCoeff; - SkDEBUGCODE(bool success =) - SkXfermode::ModeAsCoeff(colorFilterXfermode, &srcCoeff, &dstCoeff); - // We currently do not handle modes that cannot be represented as - // coefficients. - GrAssert(success); - GrStringBuilder srcCoeffStr, dstCoeffStr; - coefficientString(&srcCoeffStr, srcCoeff, COL_FILTER_UNI_NAME, dstColor); - coefficientString(&dstCoeffStr, dstCoeff, COL_FILTER_UNI_NAME, dstColor); - FSCode->appendf("\t%s = %s*%s + %s*%s;\n", outputVar, srcCoeffStr.c_str(), - COL_FILTER_UNI_NAME, dstCoeffStr.c_str(), dstColor); +static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar, + SkXfermode::Coeff uniformCoeff, + SkXfermode::Coeff colorCoeff, + const char* inColor) { + GrStringBuilder colorStr, constStr; + blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME, + inColor, inColor); + blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME, + inColor, COL_FILTER_UNI_NAME); + + add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode); } bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const { @@ -236,6 +333,39 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const { programData->fUniLocations.reset(); + SkXfermode::Coeff colorCoeff, uniformCoeff; + // The rest of transfer mode color filters have not been implemented + if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) { + GR_DEBUGCODE(bool success =) + SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode> + (fProgramDesc.fColorFilterXfermode), + &uniformCoeff, &colorCoeff); + GR_DEBUGASSERT(success); + } else { + colorCoeff = SkXfermode::kOne_Coeff; + uniformCoeff = SkXfermode::kZero_Coeff; + } + + bool needColorFilterUniform; + bool needComputedColor; + needBlendInputs(uniformCoeff, colorCoeff, + &needColorFilterUniform, &needComputedColor); + + // the dual source output has no canonical var name, have to + // declare an output, which is incompatible with gl_FragColor/gl_FragData. + const char* fsColorOutput; + bool dualSourceOutputWritten = false; + bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput != + fProgramDesc.fDualSrcOutput; + if (usingDeclaredOutputs) { + GrAssert(0 == segments.fHeader.size()); + segments.fHeader.printf("#version 150\n"); + fsColorOutput = declared_color_output_name(); + segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput); + } else { + fsColorOutput = "gl_FragColor"; + } + #if GR_GL_ATTRIBUTE_MATRICES segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n"; programData->fUniLocations.fViewMatrixUni = kSetAsAttribute; @@ -253,25 +383,23 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const { // incoming color to current stage being processed. GrStringBuilder inColor; - switch (fProgramDesc.fColorType) { - case ProgramDesc::kAttribute_ColorType: - segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n"); - segments.fVaryings.append("varying vec4 vColor;\n"); - segments.fVSCode.append( "\tvColor = " COL_ATTR_NAME ";\n"); - inColor = "vColor"; - break; - case ProgramDesc::kUniform_ColorType: - segments.fFSUnis.append( "uniform vec4 " COL_UNI_NAME ";\n"); - programData->fUniLocations.fColorUni = kUseUniform; - inColor = COL_UNI_NAME; - break; - case ProgramDesc::kNone_ColorType: - inColor = ""; - break; - } - - if (fProgramDesc.fUsesEdgeAA) { - segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[6];\n"); + if (needComputedColor) { + switch (fProgramDesc.fColorType) { + case ProgramDesc::kAttribute_ColorType: + segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n"); + segments.fVaryings.append("varying vec4 vColor;\n"); + segments.fVSCode.append( "\tvColor = " COL_ATTR_NAME ";\n"); + inColor = "vColor"; + break; + case ProgramDesc::kUniform_ColorType: + segments.fFSUnis.append( "uniform vec4 " COL_UNI_NAME ";\n"); + programData->fUniLocations.fColorUni = kUseUniform; + inColor = COL_UNI_NAME; + break; + default: + GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType); + break; + } } if (fProgramDesc.fEmitsPointSize){ @@ -289,109 +417,204 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const { } } - bool useColorFilter = - // The rest of transfer mode color filters have not been implemented - fProgramDesc.fColorFilterXfermode <= SkXfermode::kMultiply_Mode - // This mode has no effect. - && fProgramDesc.fColorFilterXfermode != SkXfermode::kDst_Mode; - bool onlyUseColorFilter = useColorFilter - && (fProgramDesc.fColorFilterXfermode == SkXfermode::kClear_Mode - || fProgramDesc.fColorFilterXfermode == SkXfermode::kSrc_Mode); - if (useColorFilter) { - // Set up a uniform for the color - segments.fFSUnis.append( "uniform vec4 " COL_FILTER_UNI_NAME ";\n"); - programData->fUniLocations.fColorFilterUni = kUseUniform; - } - - // for each enabled stage figure out what the input coordinates are - // and count the number of stages in use. - const char* stageInCoords[GrDrawTarget::kNumStages]; - int numActiveStages = 0; + /////////////////////////////////////////////////////////////////////////// + // compute the final color - if (!onlyUseColorFilter) { - for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { - if (fProgramDesc.fStages[s].fEnabled) { - if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) { - stageInCoords[s] = POS_ATTR_NAME; + // if we have color stages string them together, feeding the output color + // of each to the next and generating code for each stage. + if (needComputedColor) { + GrStringBuilder outColor; + for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) { + if (fProgramDesc.fStages[s].isEnabled()) { + // create var to hold stage result + outColor = "color"; + outColor.appendS32(s); + segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str()); + + const char* inCoords; + // figure out what our input coords are + if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & + layout) { + inCoords = POS_ATTR_NAME; } else { int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout); // we better have input tex coordinates if stage is enabled. GrAssert(tcIdx >= 0); GrAssert(texCoordAttrs[tcIdx].size()); - stageInCoords[s] = texCoordAttrs[tcIdx].c_str(); + inCoords = texCoordAttrs[tcIdx].c_str(); } - ++numActiveStages; + + genStageCode(s, + fProgramDesc.fStages[s], + inColor.size() ? inColor.c_str() : NULL, + outColor.c_str(), + inCoords, + &segments, + &programData->fUniLocations.fStages[s]); + inColor = outColor; } } } - // if we have active stages string them together, feeding the output color - // of each to the next and generating code for each stage. - if (numActiveStages) { - int currActiveStage = 0; - GrStringBuilder outColor; - for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { - if (fProgramDesc.fStages[s].fEnabled) { - if (currActiveStage < (numActiveStages - 1) || useColorFilter) { - outColor = "color"; - outColor.appendS32(currActiveStage); - segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str()); + // if have all ones for the "dst" input to the color filter then we can make + // additional optimizations. + if (needColorFilterUniform && !inColor.size() && + (SkXfermode::kIDC_Coeff == uniformCoeff || + SkXfermode::kIDA_Coeff == uniformCoeff)) { + uniformCoeff = SkXfermode::kZero_Coeff; + bool bogus; + needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff, + &needColorFilterUniform, &bogus); + } + if (needColorFilterUniform) { + segments.fFSUnis.append( "uniform vec4 " COL_FILTER_UNI_NAME ";\n"); + programData->fUniLocations.fColorFilterUni = kUseUniform; + } + + bool wroteFragColorZero = false; + if (SkXfermode::kZero_Coeff == uniformCoeff && + SkXfermode::kZero_Coeff == colorCoeff) { + segments.fFSCode.appendf("\t%s = %s;\n", + fsColorOutput, + all_zeros_vec(4)); + wroteFragColorZero = true; + } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) { + segments.fFSCode.appendf("\tvec4 filteredColor;\n"); + const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4); + addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff, + colorCoeff, color); + inColor = "filteredColor"; + } + + /////////////////////////////////////////////////////////////////////////// + // compute the partial coverage (coverage stages and edge aa) + + GrStringBuilder inCoverage; + + // we don't need to compute coverage at all if we know the final shader + // output will be zero and we don't have a dual src blend output. + if (!wroteFragColorZero || + ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) { + if (fProgramDesc.fEdgeAANumEdges > 0) { + segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "["); + segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges); + segments.fFSUnis.append("];\n"); + programData->fUniLocations.fEdgesUni = kUseUniform; + int count = fProgramDesc.fEdgeAANumEdges; + segments.fFSCode.append( + "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n"); + for (int i = 0; i < count; i++) { + segments.fFSCode.append("\tfloat a"); + segments.fFSCode.appendS32(i); + segments.fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "["); + segments.fFSCode.appendS32(i); + segments.fFSCode.append("], pos), 0.0, 1.0);\n"); + } + segments.fFSCode.append("\tfloat edgeAlpha = "); + for (int i = 0; i < count - 1; i++) { + segments.fFSCode.append("min(a"); + segments.fFSCode.appendS32(i); + segments.fFSCode.append(" * a"); + segments.fFSCode.appendS32(i + 1); + segments.fFSCode.append(", "); + } + segments.fFSCode.append("a"); + segments.fFSCode.appendS32(count - 1); + segments.fFSCode.append(" * a0"); + for (int i = 0; i < count - 1; i++) { + segments.fFSCode.append(")"); + } + segments.fFSCode.append(";\n"); + inCoverage = "edgeAlpha"; + } + + GrStringBuilder outCoverage; + const int& startStage = fProgramDesc.fFirstCoverageStage; + for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) { + if (fProgramDesc.fStages[s].isEnabled()) { + // create var to hold stage output + outCoverage = "coverage"; + outCoverage.appendS32(s); + segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str()); + + const char* inCoords; + // figure out what our input coords are + if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) { + inCoords = POS_ATTR_NAME; } else { - outColor = "gl_FragColor"; + int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout); + // we better have input tex coordinates if stage is enabled. + GrAssert(tcIdx >= 0); + GrAssert(texCoordAttrs[tcIdx].size()); + inCoords = texCoordAttrs[tcIdx].c_str(); } genStageCode(s, fProgramDesc.fStages[s], - inColor.size() ? inColor.c_str() : NULL, - outColor.c_str(), - stageInCoords[s], + inCoverage.size() ? inCoverage.c_str() : NULL, + outCoverage.c_str(), + inCoords, &segments, &programData->fUniLocations.fStages[s]); - ++currActiveStage; - inColor = outColor; + inCoverage = outCoverage; } } - if (useColorFilter) { - addColorFilter(&segments.fFSCode, "gl_FragColor", - fProgramDesc.fColorFilterXfermode, outColor.c_str()); - } - - } else { - if (fProgramDesc.fUsesEdgeAA) { - // FIXME: put the a's in a loop - segments.fFSCode.append( - "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n" - "\tfloat a0 = clamp(dot(uEdges[0], pos), 0.0, 1.0);\n" - "\tfloat a1 = clamp(dot(uEdges[1], pos), 0.0, 1.0);\n" - "\tfloat a2 = clamp(dot(uEdges[2], pos), 0.0, 1.0);\n" - "\tfloat a3 = clamp(dot(uEdges[3], pos), 0.0, 1.0);\n" - "\tfloat a4 = clamp(dot(uEdges[4], pos), 0.0, 1.0);\n" - "\tfloat a5 = clamp(dot(uEdges[5], pos), 0.0, 1.0);\n" - "\tfloat edgeAlpha = min(min(a0 * a1, a2 * a3), a4 * a5);\n"); - if (inColor.size()) { - inColor.append(" * edgeAlpha"); + if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) { + segments.fFSOutputs.appendf("out vec4 %s;\n", + dual_source_output_name()); + bool outputIsZero = false; + GrStringBuilder coeff; + if (ProgramDesc::kCoverage_DualSrcOutput != + fProgramDesc.fDualSrcOutput && !wroteFragColorZero) { + if (!inColor.size()) { + outputIsZero = true; + } else { + if (fProgramDesc.fDualSrcOutput == + ProgramDesc::kCoverageISA_DualSrcOutput) { + coeff.printf("(1 - %s.a)", inColor.c_str()); + } else { + coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str()); + } + } + } + if (outputIsZero) { + segments.fFSCode.appendf("\t%s = %s;\n", + dual_source_output_name(), + all_zeros_vec(4)); } else { - inColor = "vec4(edgeAlpha)"; + modulate_helper(dual_source_output_name(), + coeff.c_str(), + inCoverage.c_str(), + &segments.fFSCode); } - } - // we may not have any incoming color - const char * incomingColor = (inColor.size() ? inColor.c_str() - : "vec4(1,1,1,1)"); - if (useColorFilter) { - addColorFilter(&segments.fFSCode, "gl_FragColor", - fProgramDesc.fColorFilterXfermode, incomingColor); - } else { - segments.fFSCode.appendf("\tgl_FragColor = %s;\n", incomingColor); + dualSourceOutputWritten = true; } } + + /////////////////////////////////////////////////////////////////////////// + // combine color and coverage as frag color + + if (!wroteFragColorZero) { + modulate_helper(fsColorOutput, + inColor.c_str(), + inCoverage.c_str(), + &segments.fFSCode); + } + segments.fVSCode.append("}\n"); segments.fFSCode.append("}\n"); + /////////////////////////////////////////////////////////////////////////// + // compile and setup attribs and unis + if (!CompileFSAndVS(segments, programData)) { return false; } - if (!this->bindAttribsAndLinkProgram(texCoordAttrs, programData)) { + if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs, + usingDeclaredOutputs, + dualSourceOutputWritten, + programData)) { return false; } @@ -403,10 +626,16 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const { bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, CachedData* programData) { - const char* strings[4]; - int lengths[4]; + static const int MAX_STRINGS = 6; + const char* strings[MAX_STRINGS]; + int lengths[MAX_STRINGS]; int stringCnt = 0; + if (segments.fHeader.size()) { + strings[stringCnt] = segments.fHeader.c_str(); + lengths[stringCnt] = segments.fHeader.size(); + ++stringCnt; + } if (segments.fVSUnis.size()) { strings[stringCnt] = segments.fVSUnis.c_str(); lengths[stringCnt] = segments.fVSUnis.size(); @@ -429,12 +658,14 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, ++stringCnt; #if PRINT_SHADERS + GrPrintf(segments.fHeader.c_str()); GrPrintf(segments.fVSUnis.c_str()); GrPrintf(segments.fVSAttrs.c_str()); GrPrintf(segments.fVaryings.c_str()); GrPrintf(segments.fVSCode.c_str()); GrPrintf("\n"); #endif + GrAssert(stringCnt <= MAX_STRINGS); programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER, stringCnt, strings, @@ -446,6 +677,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, stringCnt = 0; + if (segments.fHeader.size()) { + strings[stringCnt] = segments.fHeader.c_str(); + lengths[stringCnt] = segments.fHeader.size(); + ++stringCnt; + } if (strlen(GrShaderPrecision()) > 1) { strings[stringCnt] = GrShaderPrecision(); lengths[stringCnt] = strlen(GrShaderPrecision()); @@ -461,6 +697,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, lengths[stringCnt] = segments.fVaryings.size(); ++stringCnt; } + if (segments.fFSOutputs.size()) { + strings[stringCnt] = segments.fFSOutputs.c_str(); + lengths[stringCnt] = segments.fFSOutputs.size(); + ++stringCnt; + } GrAssert(segments.fFSCode.size()); strings[stringCnt] = segments.fFSCode.c_str(); @@ -468,12 +709,15 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, ++stringCnt; #if PRINT_SHADERS + GrPrintf(segments.fHeader.c_str()); GrPrintf(GrShaderPrecision()); GrPrintf(segments.fFSUnis.c_str()); GrPrintf(segments.fVaryings.c_str()); + GrPrintf(segments.fFSOutputs.c_str()); GrPrintf(segments.fFSCode.c_str()); GrPrintf("\n"); #endif + GrAssert(stringCnt <= MAX_STRINGS); programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER, stringCnt, strings, @@ -482,6 +726,7 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments, if (!programData->fFShaderID) { return false; } + return true; } @@ -521,8 +766,11 @@ GrGLuint GrGLProgram::CompileShader(GrGLenum type, return shader; } -bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[], - CachedData* programData) const { +bool GrGLProgram::bindOutputsAttribsAndLinkProgram( + GrStringBuilder texCoordAttrNames[], + bool bindColorOut, + bool bindDualSrcOut, + CachedData* programData) const { programData->fProgramID = GR_GL(CreateProgram()); if (!programData->fProgramID) { return false; @@ -532,6 +780,15 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[], GR_GL(AttachShader(progID, programData->fVShaderID)); GR_GL(AttachShader(progID, programData->fFShaderID)); + if (bindColorOut) { + GR_GL(BindFragDataLocationIndexed(programData->fProgramID, + 0, 0, declared_color_output_name())); + } + if (bindDualSrcOut) { + GR_GL(BindFragDataLocationIndexed(programData->fProgramID, + 0, 1, dual_source_output_name())); + } + // Bind the attrib locations to same values for all shaders GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME)); for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) { @@ -542,7 +799,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[], } } - if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) { GR_GL(BindAttribLocation(progID, ViewMatrixAttributeIdx(), @@ -560,7 +816,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[], } } - GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME)); GR_GL(LinkProgram(progID)); @@ -595,18 +850,18 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni); } if (kUseUniform == programData->fUniLocations.fColorUni) { - programData->fUniLocations.fColorUni = + programData->fUniLocations.fColorUni = GR_GL(GetUniformLocation(progID, COL_UNI_NAME)); GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni); } if (kUseUniform == programData->fUniLocations.fColorFilterUni) { - programData->fUniLocations.fColorFilterUni = + programData->fUniLocations.fColorFilterUni = GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME)); GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni); } - if (fProgramDesc.fUsesEdgeAA) { - programData->fUniLocations.fEdgesUni = + if (kUseUniform == programData->fUniLocations.fEdgesUni) { + programData->fUniLocations.fEdgesUni = GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME)); GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni); } else { @@ -615,7 +870,7 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const for (int s = 0; s < GrDrawTarget::kNumStages; ++s) { StageUniLocations& locations = programData->fUniLocations.fStages[s]; - if (fProgramDesc.fStages[s].fEnabled) { + if (fProgramDesc.fStages[s].isEnabled()) { if (kUseUniform == locations.fTextureMatrixUni) { GrStringBuilder texMName; tex_matrix_name(s, &texMName); @@ -637,7 +892,7 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const if (kUseUniform == locations.fNormalizedTexelSizeUni) { GrStringBuilder texelSizeName; normalized_texel_size_name(s, &texelSizeName); - locations.fNormalizedTexelSizeUni = + locations.fNormalizedTexelSizeUni = GR_GL(GetUniformLocation(progID, texelSizeName.c_str())); GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni); } @@ -650,6 +905,15 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const radial2ParamName.c_str())); GrAssert(kUnusedUniform != locations.fRadial2Uni); } + + if (kUseUniform == locations.fTexDomUni) { + GrStringBuilder texDomName; + tex_domain_name(s, &texDomName); + locations.fTexDomUni = GR_GL(GetUniformLocation( + progID, + texDomName.c_str())); + GrAssert(kUnusedUniform != locations.fTexDomUni); + } } } GR_GL(UseProgram(progID)); @@ -726,7 +990,7 @@ void GrGLProgram::genStageCode(int stageNum, segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str()); } - segments->fVaryings.appendf("varying %s %s;\n", + segments->fVaryings.appendf("varying %s %s;\n", float_vector_type(varyingDims), varyingName.c_str()); if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) { @@ -748,9 +1012,9 @@ void GrGLProgram::genStageCode(int stageNum, if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) { - segments->fVSUnis.appendf("uniform %s float %s[6];\n", + segments->fVSUnis.appendf("uniform %s float %s[6];\n", GrPrecision(), radial2ParamsName.c_str()); - segments->fFSUnis.appendf("uniform float %s[6];\n", + segments->fFSUnis.appendf("uniform float %s[6];\n", radial2ParamsName.c_str()); locations->fRadial2Uni = kUseUniform; @@ -869,6 +1133,24 @@ void GrGLProgram::genStageCode(int stageNum, modulate.printf(" * %s", fsInColor); } + if (desc.fOptFlags & + ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) { + GrStringBuilder texDomainName; + tex_domain_name(stageNum, &texDomainName); + segments->fFSUnis.appendf("uniform %s %s;\n", + float_vector_type(4), + texDomainName.c_str()); + GrStringBuilder coordVar("clampCoord"); + segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n", + float_vector_type(coordDims), + coordVar.c_str(), + sampleCoords.c_str(), + texDomainName.c_str(), + texDomainName.c_str()); + sampleCoords = coordVar; + locations->fTexDomUni = kUseUniform; + } + if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) { locations->fNormalizedTexelSizeUni = kUseUniform; if (complexCoord) { @@ -889,11 +1171,6 @@ void GrGLProgram::genStageCode(int stageNum, segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear); segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str()); } else { - segments->fFSCode.appendf("\t%s = %s(%s, %s)%s %s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str()); - } - - if(fStageEffects[stageNum]) { - fStageEffects[stageNum]->genShaderCode(segments); + segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str()); } } - diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h index 1611ca21c4..473bcb65a0 100644 --- a/gpu/src/GrGLProgram.h +++ b/gpu/src/GrGLProgram.h @@ -19,13 +19,22 @@ #include "GrGLInterface.h" #include "GrStringBuilder.h" -#include "GrDrawTarget.h" +#include "GrGpu.h" #include "SkXfermode.h" class GrBinHashKeyBuilder; -class GrGLEffect; -struct ShaderCodeSegments; + +struct ShaderCodeSegments { + GrStringBuilder fHeader; // VS+FS, GLSL version, etc + GrStringBuilder fVSUnis; + GrStringBuilder fVSAttrs; + GrStringBuilder fVaryings; + GrStringBuilder fFSUnis; + GrStringBuilder fFSOutputs; + GrStringBuilder fVSCode; + GrStringBuilder fFSCode; +}; /** * This class manages a GPU program and records per-program information. @@ -58,39 +67,29 @@ public: */ bool genProgram(CachedData* programData) const; - /** - * Routine that is called before rendering. Sets-up all the state and - * other initializations required for the Gpu Program to run. - */ - bool doGLSetup(GrPrimitiveType type, CachedData* programData) const; - - /** - * Routine that is called after rendering. Performs state restoration. - * May perform secondary render passes. - */ - void doGLPost() const; + /** + * The shader may modify the blend coeffecients. Params are in/out + */ + void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const; /** - * Configures the GrGLProgram based on the state of a GrDrawTarget - * object. This is the fast and light initialization. Retrieves all the - * state that is required for performing the heavy init (i.e. genProgram), - * or for retrieving heavy init results from cache. + * Attribute indices */ - void buildFromTarget(const GrDrawTarget* target); - static int PositionAttributeIdx() { return 0; } static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; } static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; } - static int ViewMatrixAttributeIdx() { - return 2 + GrDrawTarget::kMaxTexCoords; + static int ViewMatrixAttributeIdx() { + return 2 + GrDrawTarget::kMaxTexCoords; } - static int TextureMatrixAttributeIdx(int stage) { - return 5 + GrDrawTarget::kMaxTexCoords + 3 * stage; + static int TextureMatrixAttributeIdx(int stage) { + return 5 + GrDrawTarget::kMaxTexCoords + 3 * stage; } private: - //Parameters that affect code generation + // Parameters that affect code generation + // These structs should be kept compact; they are the input to an + // expensive hash key generator. struct ProgramDesc { ProgramDesc() { // since we use this as part of a key we can't have any unitialized @@ -98,46 +97,76 @@ private: memset(this, 0, sizeof(ProgramDesc)); } - // stripped of bits that don't affect prog generation - GrVertexLayout fVertexLayout; - - enum { - kNone_ColorType = 0, - kAttribute_ColorType = 1, - kUniform_ColorType = 2, - } fColorType; - - bool fEmitsPointSize; - bool fUsesEdgeAA; - - SkXfermode::Mode fColorFilterXfermode; - struct StageDesc { enum OptFlagBits { - kNoPerspective_OptFlagBit = 0x1, - kIdentityMatrix_OptFlagBit = 0x2 + kNoPerspective_OptFlagBit = 1 << 0, + kIdentityMatrix_OptFlagBit = 1 << 1, + kCustomTextureDomain_OptFlagBit = 1 << 2, + kIsEnabled_OptFlagBit = 1 << 7 }; - - unsigned fOptFlags; - bool fEnabled; - enum Modulation { kColor_Modulation, kAlpha_Modulation - } fModulation; - + }; enum FetchMode { kSingle_FetchMode, k2x2_FetchMode - } fFetchMode; - + }; enum CoordMapping { kIdentity_CoordMapping, kRadialGradient_CoordMapping, kSweepGradient_CoordMapping, kRadial2Gradient_CoordMapping - } fCoordMapping; - } fStages[GrDrawTarget::kNumStages]; + }; + + uint8_t fOptFlags; + uint8_t fModulation; // casts to enum Modulation + uint8_t fFetchMode; // casts to enum FetchMode + uint8_t fCoordMapping; // casts to enum CoordMapping + + inline bool isEnabled() const { + return fOptFlags & kIsEnabled_OptFlagBit; + } + inline void setEnabled(bool newValue) { + if (newValue) { + fOptFlags |= kIsEnabled_OptFlagBit; + } else { + fOptFlags &= ~kIsEnabled_OptFlagBit; + } + } + }; + + enum ColorType { + kNone_ColorType = 0, + kAttribute_ColorType = 1, + kUniform_ColorType = 2, + }; + // Dual-src blending makes use of a secondary output color that can be + // used as a per-pixel blend coeffecient. This controls whether a + // secondary source is output and what value it holds. + enum DualSrcOutput { + kNone_DualSrcOutput, + kCoverage_DualSrcOutput, + kCoverageISA_DualSrcOutput, + kCoverageISC_DualSrcOutput, + kDualSrcOutputCnt + }; + + // stripped of bits that don't affect prog generation + GrVertexLayout fVertexLayout; + + StageDesc fStages[GrDrawTarget::kNumStages]; + + uint8_t fColorType; // casts to enum ColorType + uint8_t fDualSrcOutput; // casts to enum DualSrcOutput + int8_t fFirstCoverageStage; + SkBool8 fEmitsPointSize; + + int8_t fEdgeAANumEdges; + uint8_t fColorFilterXfermode; // casts to enum SkXfermode::Mode + + uint8_t fPadTo32bLengthMultiple [2]; + } fProgramDesc; const ProgramDesc& getDesc() { return fProgramDesc; } @@ -153,11 +182,13 @@ public: GrGLint fNormalizedTexelSizeUni; GrGLint fSamplerUni; GrGLint fRadial2Uni; + GrGLint fTexDomUni; void reset() { fTextureMatrixUni = kUnusedUniform; fNormalizedTexelSizeUni = kUnusedUniform; fSamplerUni = kUnusedUniform; fRadial2Uni = kUnusedUniform; + fTexDomUni = kUnusedUniform; } }; @@ -181,35 +212,13 @@ public: class CachedData : public ::GrNoncopyable { public: CachedData() { - GR_DEBUGCODE(fEffectUniCount = 0;) - fEffectUniLocationsExtended = NULL; } ~CachedData() { - GrFree(fEffectUniLocationsExtended); } void copyAndTakeOwnership(CachedData& other) { memcpy(this, &other, sizeof(*this)); - other.fEffectUniLocationsExtended = NULL; // ownership transfer - GR_DEBUGCODE(other.fEffectUniCount = 0;) - } - - void setEffectUniformCount(size_t effectUniforms) { - GR_DEBUGCODE(fEffectUniCount = effectUniforms;) - GrFree(fEffectUniLocationsExtended); - if (effectUniforms > kUniLocationPreAllocSize) { - fEffectUniLocationsExtended = (GrGLint*)GrMalloc(sizeof(GrGLint)*(effectUniforms-kUniLocationPreAllocSize)); - } else { - fEffectUniLocationsExtended = NULL; - } - } - - GrGLint& effectUniLocation(size_t index) { - GrAssert(index < fEffectUniCount); - return (index < kUniLocationPreAllocSize) ? - fEffectUniLocations[index] : - fEffectUniLocationsExtended[index - kUniLocationPreAllocSize]; } public: @@ -234,19 +243,15 @@ public: GrScalar fRadial2CenterX1[GrDrawTarget::kNumStages]; GrScalar fRadial2Radius0[GrDrawTarget::kNumStages]; bool fRadial2PosRoot[GrDrawTarget::kNumStages]; + GrRect fTextureDomain[GrDrawTarget::kNumStages]; private: enum Constants { kUniLocationPreAllocSize = 8 }; - GrGLint fEffectUniLocations[kUniLocationPreAllocSize]; - GrGLint* fEffectUniLocationsExtended; - GR_DEBUGCODE(size_t fEffectUniCount;) }; // CachedData - GrGLEffect* fStageEffects[GrDrawTarget::kNumStages]; - private: enum { kUseUniform = 2000 @@ -273,8 +278,11 @@ private: // Creates a GL program ID, binds shader attributes to GL vertex attrs, and // links the program - bool bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords], - CachedData* programData) const; + bool bindOutputsAttribsAndLinkProgram( + GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords], + bool bindColorOut, + bool bindDualSrcOut, + CachedData* programData) const; // Gets locations for all uniforms set to kUseUniform and initializes cache // to invalid values. diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp index 9950afd27b..4fe7ccc30d 100644 --- a/gpu/src/GrGpu.cpp +++ b/gpu/src/GrGpu.cpp @@ -142,17 +142,6 @@ GrTexture* GrGpu::createTexture(const GrTextureDesc& desc, return this->onCreateTexture(desc, srcData, rowBytes); } -GrRenderTarget* GrGpu::createPlatformRenderTarget(intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, int height) { - this->handleDirtyContext(); - return this->onCreatePlatformRenderTarget(platformRenderTarget, - stencilBits, - isMultisampled, - width, height); -} - GrRenderTarget* GrGpu::createRenderTargetFrom3DApiState() { this->handleDirtyContext(); return this->onCreateRenderTargetFrom3DApiState(); @@ -467,17 +456,16 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { // resolve in/out status. GrPathRenderer* pr = NULL; - GrPath::Iter pathIter; + const GrPath* clipPath = NULL; if (kRect_ClipType == clip.getElementType(c)) { canRenderDirectToStencil = true; fill = kEvenOdd_PathFill; } else { fill = clip.getPathFill(c); - const GrPath& path = clip.getPath(c); - pathIter.reset(path); - pr = this->getClipPathRenderer(&pathIter, NonInvertedFill(fill)); + clipPath = &clip.getPath(c); + pr = this->getClipPathRenderer(*clipPath, NonInvertedFill(fill)); canRenderDirectToStencil = - !pr->requiresStencilPass(this, &pathIter, + !pr->requiresStencilPass(this, *clipPath, NonInvertedFill(fill)); } @@ -513,12 +501,10 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { } else { if (canRenderDirectToStencil) { this->setStencil(gDrawToStencil); - pr->drawPath(this, 0, - &pathIter, - NonInvertedFill(fill), + pr->drawPath(this, 0, *clipPath, NonInvertedFill(fill), NULL); } else { - pr->drawPathToStencil(this, &pathIter, + pr->drawPathToStencil(this, *clipPath, NonInvertedFill(fill), NULL); } @@ -537,7 +523,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { } else { SET_RANDOM_COLOR GrAssert(!IsFillInverted(fill)); - pr->drawPath(this, 0, &pathIter, fill, NULL); + pr->drawPath(this, 0, *clipPath, fill, NULL); } } else { SET_RANDOM_COLOR @@ -561,7 +547,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { return true; } -GrPathRenderer* GrGpu::getClipPathRenderer(GrPathIter* path, +GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path, GrPathFill fill) { if (NULL != fClientPathRenderer && fClientPathRenderer->canDrawPath(this, path, fill)) { diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp index e8c7afb0ed..ff2d406e3d 100644 --- a/gpu/src/GrGpuGL.cpp +++ b/gpu/src/GrGpuGL.cpp @@ -16,6 +16,7 @@ #include "GrGpuGL.h" #include "GrMemory.h" +#include "GrTypes.h" static const GrGLuint GR_MAX_GLUINT = ~0; static const GrGLint GR_INVAL_GLINT = ~0; @@ -41,9 +42,15 @@ static const GrGLenum gXfermodeCoeff2Blend[] = { GR_GL_ONE_MINUS_CONSTANT_COLOR, GR_GL_CONSTANT_ALPHA, GR_GL_ONE_MINUS_CONSTANT_ALPHA, + + // extended blend coeffs + GR_GL_SRC1_COLOR, + GR_GL_ONE_MINUS_SRC1_COLOR, + GR_GL_SRC1_ALPHA, + GR_GL_ONE_MINUS_SRC1_ALPHA, }; -bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) { +bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) { static const bool gCoeffReferencesBlendConst[] = { false, false, @@ -59,28 +66,40 @@ bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) { true, true, true, + + // extended blend coeffs + false, + false, + false, + false, }; return gCoeffReferencesBlendConst[coeff]; - GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst)); + GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst)); + + GR_STATIC_ASSERT(0 == kZero_BlendCoeff); + GR_STATIC_ASSERT(1 == kOne_BlendCoeff); + GR_STATIC_ASSERT(2 == kSC_BlendCoeff); + GR_STATIC_ASSERT(3 == kISC_BlendCoeff); + GR_STATIC_ASSERT(4 == kDC_BlendCoeff); + GR_STATIC_ASSERT(5 == kIDC_BlendCoeff); + GR_STATIC_ASSERT(6 == kSA_BlendCoeff); + GR_STATIC_ASSERT(7 == kISA_BlendCoeff); + GR_STATIC_ASSERT(8 == kDA_BlendCoeff); + GR_STATIC_ASSERT(9 == kIDA_BlendCoeff); + GR_STATIC_ASSERT(10 == kConstC_BlendCoeff); + GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff); + GR_STATIC_ASSERT(12 == kConstA_BlendCoeff); + GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff); + + GR_STATIC_ASSERT(14 == kS2C_BlendCoeff); + GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff); + GR_STATIC_ASSERT(16 == kS2A_BlendCoeff); + GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff); + + // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope + GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend)); } -GR_STATIC_ASSERT(0 == kZero_BlendCoeff); -GR_STATIC_ASSERT(1 == kOne_BlendCoeff); -GR_STATIC_ASSERT(2 == kSC_BlendCoeff); -GR_STATIC_ASSERT(3 == kISC_BlendCoeff); -GR_STATIC_ASSERT(4 == kDC_BlendCoeff); -GR_STATIC_ASSERT(5 == kIDC_BlendCoeff); -GR_STATIC_ASSERT(6 == kSA_BlendCoeff); -GR_STATIC_ASSERT(7 == kISA_BlendCoeff); -GR_STATIC_ASSERT(8 == kDA_BlendCoeff); -GR_STATIC_ASSERT(9 == kIDA_BlendCoeff); -GR_STATIC_ASSERT(10 == kConstC_BlendCoeff); -GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff); -GR_STATIC_ASSERT(12 == kConstA_BlendCoeff); -GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff); - -GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend)); - /////////////////////////////////////////////////////////////////////////////// void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture, @@ -201,6 +220,16 @@ GrGpuGL::GrGpuGL() { GR_GL_GetIntegerv(GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits); GrAssert(maxTextureUnits > kNumStages); } + if (GR_GL_SUPPORT_ES2) { + GR_GL_GetIntegerv(GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS, + &fMaxFragmentUniformVectors); + } else if (GR_GL_SUPPORT_DESKTOP) { + GrGLint max; + GR_GL_GetIntegerv(GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max); + fMaxFragmentUniformVectors = max / 4; + } else { + fMaxFragmentUniformVectors = 16; + } //////////////////////////////////////////////////////////////////////////// // Check for supported features. @@ -604,32 +633,6 @@ GrResource* GrGpuGL::onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc) } } -GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget( - intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, - int height) { - GrGLRenderTarget::GLRenderTargetIDs rtIDs; - rtIDs.fStencilRenderbufferID = 0; - rtIDs.fMSColorRenderbufferID = 0; - rtIDs.fTexFBOID = 0; - rtIDs.fOwnIDs = false; - GrGLIRect viewport; - - // viewport is in GL coords (top >= bottom) - viewport.fLeft = 0; - viewport.fBottom = 0; - viewport.fWidth = width; - viewport.fHeight = height; - - rtIDs.fRTFBOID = (GrGLuint)platformRenderTarget; - rtIDs.fTexFBOID = (GrGLuint)platformRenderTarget; - - return new GrGLRenderTarget(this, rtIDs, NULL, stencilBits, - isMultisampled, viewport, NULL); -} - GrRenderTarget* GrGpuGL::onCreateRenderTargetFrom3DApiState() { GrGLRenderTarget::GLRenderTargetIDs rtIDs; @@ -1667,7 +1670,9 @@ void GrGpuGL::flushAAState(GrPrimitiveType type) { } } -void GrGpuGL::flushBlend(GrPrimitiveType type) { +void GrGpuGL::flushBlend(GrPrimitiveType type, + GrBlendCoeff srcCoeff, + GrBlendCoeff dstCoeff) { if (GrIsPrimTypeLines(type) && useSmoothLines()) { if (fHWBlendDisabled) { GR_GL(Enable(GR_GL_BLEND)); @@ -1691,15 +1696,15 @@ void GrGpuGL::flushBlend(GrPrimitiveType type) { fHWBlendDisabled = blendOff; } if (!blendOff) { - if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend || - fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) { - GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend], - gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend])); - fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend; - fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend; + if (fHWDrawState.fSrcBlend != srcCoeff || + fHWDrawState.fDstBlend != dstCoeff) { + GR_GL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff], + gXfermodeCoeff2Blend[dstCoeff])); + fHWDrawState.fSrcBlend = srcCoeff; + fHWDrawState.fDstBlend = dstCoeff; } - if ((BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) || - BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) && + if ((BlendCoeffReferencesConstant(srcCoeff) || + BlendCoeffReferencesConstant(dstCoeff)) && fHWDrawState.fBlendConstant != fCurrDrawState.fBlendConstant) { float c[] = { @@ -1802,7 +1807,6 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) { } this->flushRenderTarget(rect); this->flushAAState(type); - this->flushBlend(type); if ((fCurrDrawState.fFlagBits & kDither_StateBit) != (fHWDrawState.fFlagBits & kDither_StateBit)) { @@ -2064,3 +2068,9 @@ void GrGpuGL::setBuffers(bool indexed, } } } + +int GrGpuGL::getMaxEdges() const { + // FIXME: This is a pessimistic estimate based on how many other things + // want to add uniforms. This should be centralized somewhere. + return GR_CT_MIN(fMaxFragmentUniformVectors - 8, kMaxEdges); +} diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h index da955cfc56..696b72f290 100644 --- a/gpu/src/GrGpuGL.h +++ b/gpu/src/GrGpuGL.h @@ -81,11 +81,6 @@ protected: virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size, bool dynamic); virtual GrResource* onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc); - virtual GrRenderTarget* onCreatePlatformRenderTarget( - intptr_t platformRenderTarget, - int stencilBits, - bool isMultisampled, - int width, int height); virtual GrRenderTarget* onCreateRenderTargetFrom3DApiState(); virtual void onClear(const GrIRect* rect, GrColor color); @@ -107,6 +102,7 @@ protected: virtual void flushScissor(const GrIRect* rect); void clearStencil(uint32_t value, uint32_t mask); virtual void clearStencilClip(const GrIRect& rect); + virtual int getMaxEdges() const; // binds texture unit in GL void setTextureUnit(int unitIdx); @@ -120,13 +116,17 @@ protected: // flushes state that is common to fixed and programmable GL // dither // line smoothing - // blend func // texture binding // sampler state (filtering, tiling) // FBO binding // line width bool flushGLStateCommon(GrPrimitiveType type); + // subclass should call this to flush the blend state + void flushBlend(GrPrimitiveType type, + GrBlendCoeff srcCoeff, + GrBlendCoeff dstCoeff); + // adjusts texture matrix to account for orientation, size, and npotness static void AdjustTextureMatrix(const GrGLTexture* texture, GrSamplerState::SampleMode mode, @@ -138,7 +138,7 @@ protected: static bool TextureMatrixIsIdentity(const GrGLTexture* texture, const GrSamplerState& sampler); - static bool BlendCoefReferencesConstant(GrBlendCoeff coeff); + static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff); private: @@ -160,7 +160,6 @@ private: void flushRenderTarget(const GrIRect* bound); void flushStencil(); void flushAAState(GrPrimitiveType type); - void flushBlend(GrPrimitiveType type); void resolveRenderTarget(GrGLRenderTarget* texture); @@ -189,6 +188,9 @@ private: // Do we have stencil wrap ops. bool fHasStencilWrap; + // The maximum number of fragment uniform vectors (GLES has min. 16). + int fMaxFragmentUniformVectors; + // ES requires an extension to support RGBA8 in RenderBufferStorage bool fRGBA8Renderbuffer; diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp index e197a82533..65229dc0a1 100644 --- a/gpu/src/GrGpuGLFixed.cpp +++ b/gpu/src/GrGpuGLFixed.cpp @@ -57,6 +57,7 @@ static const GrGLenum gMatrixMode2Enum[] = { GrGpuGLFixed::GrGpuGLFixed() { f4X4DownsampleFilterSupport = false; + fDualSourceBlendingSupport = false; } GrGpuGLFixed::~GrGpuGLFixed() { @@ -136,8 +137,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) { } if (GR_GL_SUPPORT_ES1) { - if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) || - BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) { + if (BlendCoeffReferencesConstant(fCurrDrawState.fSrcBlend) || + BlendCoeffReferencesConstant(fCurrDrawState.fDstBlend)) { unimpl("ES1 doesn't support blend constant"); return false; } @@ -147,6 +148,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) { return false; } + this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend); + if (fDirtyFlags.fRenderTargetChanged) { flushProjectionMatrix(); } diff --git a/gpu/src/GrGpuGLFixed.h b/gpu/src/GrGpuGLFixed.h index 077b6e2a38..487c09f087 100644 --- a/gpu/src/GrGpuGLFixed.h +++ b/gpu/src/GrGpuGLFixed.h @@ -41,7 +41,7 @@ private: const GrMatrix& getHWSamplerMatrix(int stage) const { return fHWDrawState.fSamplerStates[stage].getMatrix(); } - const void recordHWSamplerMatrix(int stage, const GrMatrix& matrix) { + void recordHWSamplerMatrix(int stage, const GrMatrix& matrix) { fHWDrawState.fSamplerStates[stage].setMatrix(matrix); } diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp index 8965b06520..50be67f0be 100644 --- a/gpu/src/GrGpuGLShaders.cpp +++ b/gpu/src/GrGpuGLShaders.cpp @@ -15,7 +15,6 @@ */ #include "GrBinHashKey.h" -#include "GrGLEffect.h" #include "GrGLProgram.h" #include "GrGpuGLShaders.h" #include "GrGpuVertex.h" @@ -169,6 +168,14 @@ void GrGpuGLShaders::ProgramUnitTest() { GrRandom random; for (int t = 0; t < NUM_TESTS; ++t) { +#if 0 + GrPrintf("\nTest Program %d\n-------------\n", t); + static const int stop = -1; + if (t == stop) { + int breakpointhere = 9; + } +#endif + pdesc.fVertexLayout = 0; pdesc.fEmitsPointSize = random.nextF() > .5f; float colorType = random.nextF(); @@ -179,6 +186,24 @@ void GrGpuGLShaders::ProgramUnitTest() { } else { pdesc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType; } + + int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt)); + pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx; + + idx = (int)(random.nextF() * (kNumStages+1)); + pdesc.fFirstCoverageStage = idx; + + pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1)); + + if (fDualSourceBlendingSupport) { + pdesc.fDualSrcOutput = + (GrGLProgram::ProgramDesc::DualSrcOutput) + (int)(random.nextF() * GrGLProgram::ProgramDesc::kDualSrcOutputCnt); + } else { + pdesc.fDualSrcOutput = + GrGLProgram::ProgramDesc::kNone_DualSrcOutput; + } + for (int s = 0; s < kNumStages; ++s) { // enable the stage? if (random.nextF() > .5f) { @@ -194,19 +219,15 @@ void GrGpuGLShaders::ProgramUnitTest() { if (random.nextF() > .5f) { pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit; } - } - - for (int s = 0; s < kNumStages; ++s) { - int x; - pdesc.fStages[s].fEnabled = VertexUsesStage(s, pdesc.fVertexLayout); - x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS)); - pdesc.fStages[s].fOptFlags = STAGE_OPTS[x]; - x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES)); - pdesc.fStages[s].fModulation = STAGE_MODULATES[x]; - x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS)); - pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[x]; - x = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES)); - pdesc.fStages[s].fFetchMode = FETCH_MODES[x]; + idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS)); + pdesc.fStages[s].fOptFlags = STAGE_OPTS[idx]; + idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES)); + pdesc.fStages[s].fModulation = STAGE_MODULATES[idx]; + idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS)); + pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[idx]; + idx = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES)); + pdesc.fStages[s].fFetchMode = FETCH_MODES[idx]; + pdesc.fStages[s].setEnabled(VertexUsesStage(s, pdesc.fVertexLayout)); } GrGLProgram::CachedData cachedData; program.genProgram(&cachedData); @@ -219,11 +240,20 @@ void GrGpuGLShaders::ProgramUnitTest() { } } - GrGpuGLShaders::GrGpuGLShaders() { resetContext(); + int major, minor; + gl_version(&major, &minor); + f4X4DownsampleFilterSupport = true; + if (GR_GL_SUPPORT_DESKTOP) { + fDualSourceBlendingSupport = + major > 3 ||(3 == major && 3 <= minor) || + has_gl_extension("GL_ARB_blend_func_extended"); + } else { + fDualSourceBlendingSupport = false; + } fProgramData = NULL; fProgramCache = new ProgramCache(); @@ -283,16 +313,16 @@ void GrGpuGLShaders::flushViewMatrix() { // ES doesn't allow you to pass true to the transpose param, // so do our own transpose - GrScalar mt[] = { - m[GrMatrix::kMScaleX], - m[GrMatrix::kMSkewY], - m[GrMatrix::kMPersp0], - m[GrMatrix::kMSkewX], - m[GrMatrix::kMScaleY], - m[GrMatrix::kMPersp1], - m[GrMatrix::kMTransX], - m[GrMatrix::kMTransY], - m[GrMatrix::kMPersp2] + GrGLfloat mt[] = { + GrScalarToFloat(m[GrMatrix::kMScaleX]), + GrScalarToFloat(m[GrMatrix::kMSkewY]), + GrScalarToFloat(m[GrMatrix::kMPersp0]), + GrScalarToFloat(m[GrMatrix::kMSkewX]), + GrScalarToFloat(m[GrMatrix::kMScaleY]), + GrScalarToFloat(m[GrMatrix::kMPersp1]), + GrScalarToFloat(m[GrMatrix::kMTransX]), + GrScalarToFloat(m[GrMatrix::kMTransY]), + GrScalarToFloat(m[GrMatrix::kMPersp2]) }; if (GrGLProgram::kSetAsAttribute == @@ -309,8 +339,48 @@ void GrGpuGLShaders::flushViewMatrix() { } } +void GrGpuGLShaders::flushTextureDomain(int s) { + const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni; + if (GrGLProgram::kUnusedUniform != uni) { + const GrRect &texDom = + fCurrDrawState.fSamplerStates[s].getTextureDomain(); + + if (((1 << s) & fDirtyFlags.fTextureChangedMask) || + fProgramData->fTextureDomain[s] != texDom) { + + fProgramData->fTextureDomain[s] = texDom; + + float values[4] = { + GrScalarToFloat(texDom.left()), + GrScalarToFloat(texDom.top()), + GrScalarToFloat(texDom.right()), + GrScalarToFloat(texDom.bottom()) + }; + + GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s]; + GrGLTexture::Orientation orientation = texture->orientation(); + + // vertical flip if necessary + if (GrGLTexture::kBottomUp_Orientation == orientation) { + values[1] = 1.0f - values[1]; + values[3] = 1.0f - values[3]; + // The top and bottom were just flipped, so correct the ordering + // of elements so that values = (l, t, r, b). + SkTSwap(values[1], values[3]); + } + + values[0] *= SkScalarToFloat(texture->contentScaleX()); + values[2] *= SkScalarToFloat(texture->contentScaleX()); + values[1] *= SkScalarToFloat(texture->contentScaleY()); + values[3] *= SkScalarToFloat(texture->contentScaleY()); + + GR_GL(Uniform4fv(uni, 1, values)); + } + } +} + void GrGpuGLShaders::flushTextureMatrix(int s) { - const int& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni; + const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni; GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s]; if (NULL != texture) { if (GrGLProgram::kUnusedUniform != uni && @@ -328,17 +398,18 @@ void GrGpuGLShaders::flushTextureMatrix(int s) { // ES doesn't allow you to pass true to the transpose param, // so do our own transpose - GrScalar mt[] = { - m[GrMatrix::kMScaleX], - m[GrMatrix::kMSkewY], - m[GrMatrix::kMPersp0], - m[GrMatrix::kMSkewX], - m[GrMatrix::kMScaleY], - m[GrMatrix::kMPersp1], - m[GrMatrix::kMTransX], - m[GrMatrix::kMTransY], - m[GrMatrix::kMPersp2] + GrGLfloat mt[] = { + GrScalarToFloat(m[GrMatrix::kMScaleX]), + GrScalarToFloat(m[GrMatrix::kMSkewY]), + GrScalarToFloat(m[GrMatrix::kMPersp0]), + GrScalarToFloat(m[GrMatrix::kMSkewX]), + GrScalarToFloat(m[GrMatrix::kMScaleY]), + GrScalarToFloat(m[GrMatrix::kMPersp1]), + GrScalarToFloat(m[GrMatrix::kMTransX]), + GrScalarToFloat(m[GrMatrix::kMTransY]), + GrScalarToFloat(m[GrMatrix::kMPersp2]) }; + if (GrGLProgram::kSetAsAttribute == fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) { int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s); @@ -399,16 +470,17 @@ void GrGpuGLShaders::flushTexelSize(int s) { void GrGpuGLShaders::flushEdgeAAData() { const int& uni = fProgramData->fUniLocations.fEdgesUni; if (GrGLProgram::kUnusedUniform != uni) { - float edges[18]; - memcpy(edges, fCurrDrawState.fEdgeAAEdges, sizeof(edges)); + int count = fCurrDrawState.fEdgeAANumEdges; + Edge edges[kMaxEdges]; // Flip the edges in Y float height = fCurrDrawState.fRenderTarget->height(); - for (int i = 0; i < 6; ++i) { - float b = edges[i * 3 + 1]; - edges[i * 3 + 1] = -b; - edges[i * 3 + 2] += b * height; + for (int i = 0; i < count; ++i) { + edges[i] = fCurrDrawState.fEdgeAAEdges[i]; + float b = edges[i].fY; + edges[i].fY = -b; + edges[i].fZ += b * height; } - GR_GL(Uniform3fv(uni, 6, edges)); + GR_GL(Uniform3fv(uni, count, &edges[0].fX)); } } @@ -489,10 +561,11 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) { GR_GL(UseProgram(fProgramData->fProgramID)); fHWProgramID = fProgramData->fProgramID; } - - if (!fCurrentProgram.doGLSetup(type, fProgramData)) { - return false; - } + GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend; + GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend; + + fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff); + this->flushBlend(type, srcCoeff, dstCoeff); this->flushColor(); @@ -515,6 +588,8 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) { this->flushRadial2(s); this->flushTexelSize(s); + + this->flushTextureDomain(s); } this->flushEdgeAAData(); resetDirtyFlags(); @@ -522,7 +597,6 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) { } void GrGpuGLShaders::postDraw() { - fCurrentProgram.doGLPost(); } void GrGpuGLShaders::setupGeometry(int* startVertex, @@ -634,6 +708,8 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) { // existing program in the cache. desc.fVertexLayout &= ~(kColor_VertexLayoutBit); + desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode; + #if GR_AGGRESSIVE_SHADER_OPTS if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) { desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType; @@ -649,24 +725,26 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) { desc.fColorType = GrGLProgram::ProgramDesc::kAttribute_ColorType; } - desc.fUsesEdgeAA = fCurrDrawState.fFlagBits & kEdgeAA_StateBit; + desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges; + + int lastEnabledStage = -1; for (int s = 0; s < kNumStages; ++s) { GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s]; - stage.fEnabled = this->isStageEnabled(s); + stage.fOptFlags = 0; + stage.setEnabled(this->isStageEnabled(s)); - if (stage.fEnabled) { + if (stage.isEnabled()) { + lastEnabledStage = s; GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s]; GrAssert(NULL != texture); // we matrix to invert when orientation is TopDown, so make sure // we aren't in that case before flagging as identity. if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) { - stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit; + stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit; } else if (!getSamplerMatrix(s).hasPerspective()) { - stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit; - } else { - stage.fOptFlags = 0; + stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit; } switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) { case GrSamplerState::kNormal_SampleMode: @@ -701,27 +779,62 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) { break; } + if (fCurrDrawState.fSamplerStates[s].hasTextureDomain()) { + GrAssert(GrSamplerState::kClamp_WrapMode == + fCurrDrawState.fSamplerStates[s].getWrapX() && + GrSamplerState::kClamp_WrapMode == + fCurrDrawState.fSamplerStates[s].getWrapY()); + stage.fOptFlags |= + GrGLProgram::ProgramDesc::StageDesc:: + kCustomTextureDomain_OptFlagBit; + } + if (GrPixelConfigIsAlphaOnly(texture->config())) { stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation; } else { stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation; } - - if (fCurrDrawState.fEffects[s]) { - fCurrentProgram.fStageEffects[s] = GrGLEffect::Create(fCurrDrawState.fEffects[s]); - } else { - delete fCurrentProgram.fStageEffects[s]; - fCurrentProgram.fStageEffects[s] = NULL; - } } else { stage.fOptFlags = 0; stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0; stage.fModulation = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0; - fCurrentProgram.fStageEffects[s] = NULL; } } - desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode; -} - + desc.fDualSrcOutput = GrGLProgram::ProgramDesc::kNone_DualSrcOutput; + // use canonical value when coverage/color distinction won't affect + // generated code to prevent duplicate programs. + desc.fFirstCoverageStage = kNumStages; + if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) { + // color filter is applied between color/coverage computation + if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) { + desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + } + // We could consider cases where the final color is solid (0xff alpha) + // and the dst coeff can correctly be set to a non-dualsrc gl value. + // (e.g. solid draw, and dst coeff is kZero. It's correct to make + // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be + // kOne). + if (fDualSourceBlendingSupport) { + if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) { + // write the coverage value to second color + desc.fDualSrcOutput = + GrGLProgram::ProgramDesc::kCoverage_DualSrcOutput; + desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) { + // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially + // cover + desc.fDualSrcOutput = + GrGLProgram::ProgramDesc::kCoverageISA_DualSrcOutput; + desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) { + // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially + // cover + desc.fDualSrcOutput = + GrGLProgram::ProgramDesc::kCoverageISC_DualSrcOutput; + desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage; + } + } + } +} diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h index bb3024faf9..9392d1c371 100644 --- a/gpu/src/GrGpuGLShaders.h +++ b/gpu/src/GrGpuGLShaders.h @@ -51,6 +51,9 @@ private: // sets the texture matrix uniform for currently bound program void flushTextureMatrix(int stage); + // sets the texture domain uniform for currently bound program + void flushTextureDomain(int stage); + // sets the color specified by GrDrawTarget::setColor() void flushColor(); diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp deleted file mode 100644 index aa89d37382..0000000000 --- a/gpu/src/GrPath.cpp +++ /dev/null @@ -1,523 +0,0 @@ -#include "GrPath.h" - -GrPath::GrPath() { - fConvexHint = kNone_ConvexHint; - fConservativeBounds.setLargestInverted(); -} - -GrPath::GrPath(const GrPath& src) : INHERITED() { - GrPath::Iter iter(src); - this->resetFromIter(&iter); -} - -GrPath::GrPath(GrPathIter& iter) { - this->resetFromIter(&iter); -} - -GrPath::~GrPath() { -} - -bool GrPath::operator ==(const GrPath& path) const { - if (fCmds.count() != path.fCmds.count() || - fPts.count() != path.fPts.count()) { - return false; - } - - for (int v = 0; v < fCmds.count(); ++v) { - if (fCmds[v] != path.fCmds[v]) { - return false; - } - } - - for (int p = 0; p < fPts.count(); ++p) { - if (fPts[p] != path.fPts[p]) { - return false; - } - } - return true; -} - -void GrPath::ensureMoveTo() { - if (fCmds.isEmpty() || this->wasLastVerb(kClose_PathCmd)) { - *fCmds.append() = kMove_PathCmd; - fPts.append()->set(0, 0); - fConservativeBounds.growToInclude(0,0); - } -} - -void GrPath::moveTo(GrScalar x, GrScalar y) { - if (this->wasLastVerb(kMove_PathCmd)) { - // overwrite prev kMove value - fPts[fPts.count() - 1].set(x, y); - } else { - *fCmds.append() = kMove_PathCmd; - fPts.append()->set(x, y); - } - fConservativeBounds.growToInclude(x,y); -} - -void GrPath::lineTo(GrScalar x, GrScalar y) { - this->ensureMoveTo(); - *fCmds.append() = kLine_PathCmd; - fPts.append()->set(x, y); - fConservativeBounds.growToInclude(x,y); -} - -void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) { - this->ensureMoveTo(); - *fCmds.append() = kQuadratic_PathCmd; - fPts.append()->set(x0, y0); - fPts.append()->set(x1, y1); - fConservativeBounds.growToInclude(x0,y0); - fConservativeBounds.growToInclude(x1,y1); -} - -void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1, - GrScalar x2, GrScalar y2) { - this->ensureMoveTo(); - *fCmds.append() = kCubic_PathCmd; - fPts.append()->set(x0, y0); - fPts.append()->set(x1, y1); - fPts.append()->set(x2, y2); - fConservativeBounds.growToInclude(x0,y0); - fConservativeBounds.growToInclude(x1,y1); - fConservativeBounds.growToInclude(x2,y2); -} - -void GrPath::close() { - if (!fCmds.isEmpty() && !this->wasLastVerb(kClose_PathCmd)) { - // should we allow kMove followed by kClose? - *fCmds.append() = kClose_PathCmd; - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void GrPath::offset(GrScalar tx, GrScalar ty) { - if (!tx && !ty) { - return; // nothing to do - } - - GrPoint* iter = fPts.begin(); - GrPoint* stop = fPts.end(); - while (iter < stop) { - iter->offset(tx, ty); - ++iter; - } - fConservativeBounds.offset(tx, ty); -} - -/////////////////////////////////////////////////////////////////////////////// - -static bool check_two_vecs(const GrVec& prevVec, - const GrVec& currVec, - GrScalar turnDir, - int* xDir, - int* yDir, - int* flipX, - int* flipY) { - if (currVec.fX * *xDir < 0) { - ++*flipX; - if (*flipX > 2) { - return false; - } - *xDir = -*xDir; - } - if (currVec.fY * *yDir < 0) { - ++*flipY; - if (*flipY > 2) { - return false; - } - *yDir = -*yDir; - } - GrScalar d = prevVec.cross(currVec); - return (d * turnDir) >= 0; -} - -static void init_from_two_vecs(const GrVec& firstVec, - const GrVec& secondVec, - GrScalar* turnDir, - int* xDir, int* yDir) { - *turnDir = firstVec.cross(secondVec); - if (firstVec.fX > 0) { - *xDir = 1; - } else if (firstVec.fX < 0) { - *xDir = -1; - } else { - *xDir = 0; - } - if (firstVec.fY > 0) { - *yDir = 1; - } else if (firstVec.fY < 0) { - *yDir = -1; - } else { - *yDir = 0; - } -} - -void GrPath::resetFromIter(GrPathIter* iter) { - fPts.reset(); - fCmds.reset(); - fConservativeBounds.setLargestInverted(); - - fConvexHint = iter->convexHint(); - - // first point of the subpath - GrPoint firstPt = { 0, 0 }; - // first edge of the subpath - GrVec firstVec = { 0, 0 }; - // vec of most recently processed edge, that wasn't degenerate - GrVec previousVec = { 0, 0 }; - // most recently processed point - GrPoint previousPt = { 0, 0 }; - - // sign indicates whether we're bending left or right - GrScalar turnDir = 0; - // number of times the direction has flipped in x or y - - // we track which direction we are moving in x/y and the - // number of times it changes. - int xDir = 0; - int yDir = 0; - int flipX = 0; - int flipY = 0; - - // counts number of sub path pts that didn't add a degenerate edge. - int subPathPts = 0; - bool subPathClosed = false; - - int numSubPaths = 0; - iter->rewind(); - GrPathCmd cmd; - GrPoint pts[4]; - do { - cmd = iter->next(pts); - // If the convexity test is ever updated to handle multiple subpaths - // the loop has to be adjusted to handle moving to a new subpath without - // closing the previous one. Currently the implicit closing vectors for a - // filled path would never be examined. - switch (cmd) { - case kMove_PathCmd: - this->moveTo(pts[0].fX, pts[0].fY); - subPathPts = 0; - subPathClosed = false; - break; - case kLine_PathCmd: - this->lineTo(pts[1].fX, pts[1].fY); - break; - case kQuadratic_PathCmd: - this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); - break; - case kCubic_PathCmd: - this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, - pts[3].fX, pts[3].fY); - break; - case kClose_PathCmd: - this->close(); - subPathClosed = true; - break; - case kEnd_PathCmd: - break; - } - int n = NumPathCmdPoints(cmd); - for (int i = 0; i < n; ++i) { - fConservativeBounds.growToInclude(pts[i].fX, pts[i].fY); - } - if (0 == subPathPts && n > 0) { - previousPt = pts[0]; - firstPt = previousPt; - flipX = 0; - flipY = 0; - turnDir = 0; - subPathPts = 1; - ++numSubPaths; - } - // either we skip the first pt because it is redundant with - // last point of the previous subpath cmd or we just ate it - // in the above if. - int consumed = 1; - if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) { - while (consumed < n) { - GrAssert(pts[consumed-1] == previousPt); - GrVec vec = pts[consumed] - previousPt; -// vec.setBetween(previousPt, pts[consumed]); - if (vec.fX || vec.fY) { - if (subPathPts >= 2) { - if (0 == turnDir) { - firstVec = previousVec; - init_from_two_vecs(firstVec, vec, - &turnDir, &xDir, &yDir); - // here we aren't checking whether the x/y dirs - // change between the first and second edge. It - // gets covered when the path is closed. - } else { - if (!check_two_vecs(previousVec, vec, turnDir, - &xDir, &yDir, - &flipX, &flipY)) { - fConvexHint = kConcave_ConvexHint; - break; - } - } - } - previousVec = vec; - previousPt = pts[consumed]; - ++subPathPts; - } - ++consumed; - } - if (subPathPts > 2 && (kClose_PathCmd == cmd || - (!subPathClosed && kEnd_PathCmd == cmd ))) { - // if an additional vector is needed to close the loop check - // that it validates against the previous vector. - GrVec vec = firstPt - previousPt; -// vec.setBetween(previousPt, firstPt); - if (vec.fX || vec.fY) { - if (!check_two_vecs(previousVec, vec, turnDir, - &xDir, &yDir, &flipX, &flipY)) { - fConvexHint = kConcave_ConvexHint; - break; - } - previousVec = vec; - } - // check that closing vector validates against the first vector. - if (!check_two_vecs(previousVec, firstVec, turnDir, - &xDir, &yDir, &flipX, &flipY)) { - fConvexHint = kConcave_ConvexHint; - break; - } - } - } - } while (cmd != kEnd_PathCmd); - if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) { - fConvexHint = kConvex_ConvexHint; - } else { - bool recurse = false; - if (recurse) { - this->resetFromIter(iter); - } - } -} - -void GrPath::ConvexUnitTest() { - GrPath testPath; - GrPath::Iter testIter; - - GrPath pt; - pt.moveTo(0, 0); - pt.close(); - - testIter.reset(pt); - testPath.resetFromIter(&testIter); - GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); - - GrPath line; - line.moveTo(GrIntToScalar(12), GrIntToScalar(20)); - line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20)); - line.close(); - - testIter.reset(line); - testPath.resetFromIter(&testIter); - GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); - - GrPath triLeft; - triLeft.moveTo(0, 0); - triLeft.lineTo(1, 0); - triLeft.lineTo(1, 1); - triLeft.close(); - - testIter.reset(triLeft); - testPath.resetFromIter(&testIter); - GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); - - GrPath triRight; - triRight.moveTo(0, 0); - triRight.lineTo(-1, 0); - triRight.lineTo(1, 1); - triRight.close(); - - testIter.reset(triRight); - testPath.resetFromIter(&testIter); - GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); - - GrPath square; - square.moveTo(0, 0); - square.lineTo(1, 0); - square.lineTo(1, 1); - square.lineTo(0, 1); - square.close(); - - testIter.reset(square); - testPath.resetFromIter(&testIter); - GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); - - GrPath redundantSquare; - square.moveTo(0, 0); - square.lineTo(0, 0); - square.lineTo(0, 0); - square.lineTo(1, 0); - square.lineTo(1, 0); - square.lineTo(1, 0); - square.lineTo(1, 1); - square.lineTo(1, 1); - square.lineTo(1, 1); - square.lineTo(0, 1); - square.lineTo(0, 1); - square.lineTo(0, 1); - square.close(); - - testIter.reset(redundantSquare); - testPath.resetFromIter(&testIter); - GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); - - GrPath bowTie; - bowTie.moveTo(0, 0); - bowTie.lineTo(0, 0); - bowTie.lineTo(0, 0); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 1); - bowTie.lineTo(1, 0); - bowTie.lineTo(1, 0); - bowTie.lineTo(1, 0); - bowTie.lineTo(0, 1); - bowTie.lineTo(0, 1); - bowTie.lineTo(0, 1); - bowTie.close(); - - testIter.reset(bowTie); - testPath.resetFromIter(&testIter); - GrAssert(kConcave_ConvexHint == testPath.getConvexHint()); - - GrPath spiral; - spiral.moveTo(0, 0); - spiral.lineTo(1, 0); - spiral.lineTo(1, 1); - spiral.lineTo(0, 1); - spiral.lineTo(0,.5); - spiral.lineTo(.5,.5); - spiral.lineTo(.5,.75); - spiral.close(); - - testIter.reset(spiral); - testPath.resetFromIter(&testIter); - GrAssert(kConcave_ConvexHint == testPath.getConvexHint()); - - GrPath dent; - dent.moveTo(0, 0); - dent.lineTo(1, 1); - dent.lineTo(0, 1); - dent.lineTo(-.5,2); - dent.lineTo(-2, 1); - dent.close(); - - testIter.reset(dent); - testPath.resetFromIter(&testIter); - GrAssert(kConcave_ConvexHint == testPath.getConvexHint()); -} -/////////////////////////////////////////////////////////////////////////////// - -GrPath::Iter::Iter() : fPath(NULL) { -} - -GrPath::Iter::Iter(const GrPath& path) : fPath(&path) { - this->rewind(); -} - -#ifdef SK_DEBUG -static bool containsInclusive(const GrRect& rect, const GrPoint& point) { - return point.fX >= rect.fLeft && point.fX <= rect.fRight && - point.fY >= rect.fTop && point.fY <= rect.fBottom; -} -#endif - -GrPathCmd GrPath::Iter::next(GrPoint points[]) { - if (fCmdIndex == fPath->fCmds.count()) { - GrAssert(fPtIndex == fPath->fPts.count()); - return kEnd_PathCmd; - } else { - GrAssert(fCmdIndex < fPath->fCmds.count()); - } - - GrPathCmd cmd = fPath->fCmds[fCmdIndex++]; - const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex; - - switch (cmd) { - case kMove_PathCmd: - if (points) { - points[0] = srcPts[0]; - } - fLastPt = srcPts[0]; - GrAssert(fPtIndex <= fPath->fPts.count() + 1); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0])); - fPtIndex += 1; - break; - case kLine_PathCmd: - if (points) { - points[0] = fLastPt; - points[1] = srcPts[0]; - } - fLastPt = srcPts[0]; - GrAssert(fPtIndex <= fPath->fPts.count() + 1); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0])); - fPtIndex += 1; - break; - case kQuadratic_PathCmd: - if (points) { - points[0] = fLastPt; - points[1] = srcPts[0]; - points[2] = srcPts[1]; - } - fLastPt = srcPts[1]; - GrAssert(fPtIndex <= fPath->fPts.count() + 2); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0])); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[1])); - fPtIndex += 2; - break; - case kCubic_PathCmd: - if (points) { - points[0] = fLastPt; - points[1] = srcPts[0]; - points[2] = srcPts[1]; - points[3] = srcPts[2]; - } - fLastPt = srcPts[2]; - GrAssert(fPtIndex <= fPath->fPts.count() + 3); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0])); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[1])); - GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[2])); - fPtIndex += 3; - break; - case kClose_PathCmd: - break; - default: - GrAssert(!"unknown grpath cmd"); - break; - } - return cmd; -} - -GrConvexHint GrPath::Iter::convexHint() const { - return fPath->getConvexHint(); -} - -GrPathCmd GrPath::Iter::next() { - return this->next(NULL); -} - -void GrPath::Iter::rewind() { - this->reset(*fPath); -} - -void GrPath::Iter::reset(const GrPath& path) { - fPath = &path; - fCmdIndex = fPtIndex = 0; -} - -bool GrPath::Iter::getConservativeBounds(GrRect* rect) const { - if (!fPath->getConservativeBounds().isEmpty()) { - *rect = fPath->getConservativeBounds(); - return true; - } - return false; -} - diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp index 317b7d3571..58bb12e01d 100644 --- a/gpu/src/GrPathRenderer.cpp +++ b/gpu/src/GrPathRenderer.cpp @@ -2,7 +2,6 @@ #include "GrPoint.h" #include "GrDrawTarget.h" -#include "GrPathIter.h" #include "GrPathUtils.h" #include "GrMemory.h" #include "GrTexture.h" @@ -147,20 +146,24 @@ static const GrStencilSettings gDirectToStencil = { //////////////////////////////////////////////////////////////////////////////// // Helpers for drawPath +static GrConvexHint getConvexHint(const SkPath& path) { + return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint; +} + #define STENCIL_OFF 0 // Always disable stencil (even when needed) static inline bool single_pass_path(const GrDrawTarget& target, - const GrPathIter& path, + const GrPath& path, GrPathFill fill) { #if STENCIL_OFF return true; #else if (kEvenOdd_PathFill == fill) { - GrConvexHint hint = path.convexHint(); + GrConvexHint hint = getConvexHint(path); return hint == kConvex_ConvexHint || hint == kNonOverlappingConvexPieces_ConvexHint; } else if (kWinding_PathFill == fill) { - GrConvexHint hint = path.convexHint(); + GrConvexHint hint = getConvexHint(path); return hint == kConvex_ConvexHint || hint == kNonOverlappingConvexPieces_ConvexHint || (hint == kSameWindingConvexPieces_ConvexHint && @@ -172,14 +175,14 @@ static inline bool single_pass_path(const GrDrawTarget& target, } bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill) const { - return !single_pass_path(*target, *path, fill); + return !single_pass_path(*target, path, fill); } void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const GrPath& path, GrPathFill fill, const GrPoint* translate, bool stencilOnly) { @@ -200,13 +203,10 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, // TODO: deal with perspective in some better way. tol /= 10; } else { - GrScalar sinv = GR_Scalar1 / stretch; - tol = GrMul(tol, sinv); + tol = GrScalarDiv(tol, stretch); } GrScalar tolSqd = GrMul(tol, tol); - path->rewind(); - int subpathCnt; int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol); @@ -226,8 +226,6 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt); - path->rewind(); - // TODO: use primitve restart if available rather than multiple draws GrPrimitiveType type; int passCount = 0; @@ -248,7 +246,7 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, drawFace[0] = GrDrawTarget::kBoth_DrawFace; } else { type = kTriangleFan_PrimitiveType; - if (single_pass_path(*target, *path, fill)) { + if (single_pass_path(*target, path, fill)) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; @@ -329,8 +327,10 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target, bool first = true; int subpath = 0; + SkPath::Iter iter(path, false); + for (;;) { - GrPathCmd cmd = path->next(pts); + GrPathCmd cmd = (GrPathCmd)iter.next(pts); switch (cmd) { case kMove_PathCmd: if (!first) { @@ -431,14 +431,14 @@ FINISHED: void GrDefaultPathRenderer::drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const GrPath& path, GrPathFill fill, const GrPoint* translate) { this->onDrawPath(target, stages, path, fill, translate, false); } void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target, - GrPathIter* path, + const GrPath& path, GrPathFill fill, const GrPoint* translate) { GrAssert(kInverseEvenOdd_PathFill != fill); diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp index 115b0f6f2f..8a72ba87a7 100644 --- a/gpu/src/GrPathUtils.cpp +++ b/gpu/src/GrPathUtils.cpp @@ -15,8 +15,6 @@ */ #include "GrPathUtils.h" - -#include "GrPathIter.h" #include "GrPoint.h" const GrScalar GrPathUtils::gTolerance = GR_Scalar1; @@ -33,8 +31,8 @@ uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[], // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x) // points. // 2^(log4(x)) = sqrt(x); - d = ceilf(sqrtf(d/tol)); - return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE); + int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol))); + return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE); } } @@ -67,12 +65,12 @@ uint32_t GrPathUtils::cubicPointCount(const GrPoint points[], GrScalar tol) { GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]), points[2].distanceToLineSegmentBetweenSqd(points[0], points[3])); - d = sqrtf(d); + d = SkScalarSqrt(d); if (d < tol) { return 1; } else { - d = ceilf(sqrtf(d/tol)); - return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE); + int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol))); + return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE); } } @@ -106,18 +104,18 @@ uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0, return a + b; } -int GrPathUtils::worstCasePointCount(GrPathIter* path, - int* subpaths, - GrScalar tol) { +int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths, + GrScalar tol) { int pointCount = 0; *subpaths = 1; bool first = true; + SkPath::Iter iter(path, true); GrPathCmd cmd; GrPoint pts[4]; - while ((cmd = path->next(pts)) != kEnd_PathCmd) { + while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) { switch (cmd) { case kLine_PathCmd: diff --git a/gpu/src/GrPathUtils.h b/gpu/src/GrPathUtils.h index af05682a4d..2cd00cbb84 100644 --- a/gpu/src/GrPathUtils.h +++ b/gpu/src/GrPathUtils.h @@ -19,15 +19,14 @@ #include "GrNoncopyable.h" #include "GrPoint.h" - -class GrPathIter; +#include "GrPath.h" /** * Utilities for evaluating paths. */ class GrPathUtils : public GrNoncopyable { public: - static int worstCasePointCount(GrPathIter* path, + static int worstCasePointCount(const GrPath&, int* subpaths, GrScalar tol); static uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol); diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp index a537e169fd..edf83fe94c 100644 --- a/gpu/src/GrStencil.cpp +++ b/gpu/src/GrStencil.cpp @@ -16,7 +16,14 @@ #include "GrStencil.h" -const GrStencilSettings GrStencilSettings::gDisabled = {}; +const GrStencilSettings GrStencilSettings::gDisabled = { + kKeep_StencilOp, kKeep_StencilOp, + kKeep_StencilOp, kKeep_StencilOp, + kAlways_StencilFunc, kAlways_StencilFunc, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0 +}; GR_STATIC_ASSERT(0 == kKeep_StencilOp); GR_STATIC_ASSERT(0 == kAlways_StencilFunc); diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp index 3993adb5d9..0e3389c25d 100644 --- a/gpu/src/GrTesselatedPathRenderer.cpp +++ b/gpu/src/GrTesselatedPathRenderer.cpp @@ -18,8 +18,10 @@ #include "GrMemory.h" #include "GrPathUtils.h" +#include "GrPoint.h" +#include "GrTDArray.h" -#include <internal_glu.h> +#include <sk_glu.h> struct PolygonData { PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices) @@ -83,24 +85,12 @@ static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) { GrTesselatedPathRenderer::GrTesselatedPathRenderer() { } -class Edge { - public: - Edge() {} - Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {} - GrPoint intersect(const Edge& other) { - return GrPoint::Make( - (fY * other.fZ - other.fY * fZ) / (fX * other.fY - other.fX * fY), - (fX * other.fZ - other.fX * fZ) / (other.fX * fY - fX * other.fY)); - } - float fX, fY, fZ; -}; - -typedef GrTDArray<Edge> EdgeArray; +typedef GrTDArray<GrDrawTarget::Edge> EdgeArray; -bool isCCW(const GrPoint* v) +bool isCCW(const GrPoint* pts) { - GrVec v1 = v[1] - v[0]; - GrVec v2 = v[2] - v[1]; + GrVec v1 = pts[1] - pts[0]; + GrVec v2 = pts[2] - pts[1]; return v1.cross(v2) < 0; } @@ -110,25 +100,24 @@ static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix, size_t numVertices, EdgeArray* edges) { + matrix.mapPoints(vertices, numVertices); GrPoint p = vertices[numVertices - 1]; - matrix.mapPoints(&p, 1); float sign = isCCW(vertices) ? -1.0f : 1.0f; for (size_t i = 0; i < numVertices; ++i) { GrPoint q = vertices[i]; - matrix.mapPoints(&q, 1); if (p == q) continue; GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX); float scale = sign / tangent.length(); float cross2 = p.fX * q.fY - q.fX * p.fY; - Edge edge(tangent.fX * scale, + GrDrawTarget::Edge edge(tangent.fX * scale, tangent.fY * scale, cross2 * scale + 0.5f); *edges->append() = edge; p = q; } - Edge prev_edge = *edges->back(); - for (size_t i = 0; i < edges->count(); ++i) { - Edge edge = edges->at(i); + GrDrawTarget::Edge prev_edge = *edges->back(); + for (int i = 0; i < edges->count(); ++i) { + GrDrawTarget::Edge edge = edges->at(i); vertices[i] = prev_edge.intersect(edge); inverse.mapPoints(&vertices[i], 1); prev_edge = edge; @@ -138,11 +127,10 @@ static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix, void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target, GrDrawTarget::StageBitfield stages, - GrPathIter* path, + const GrPath& path, GrPathFill fill, const GrPoint* translate) { GrDrawTarget::AutoStateRestore asr(target); - bool colorWritesWereDisabled = target->isColorWriteDisabled(); // face culling doesn't make sense here GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace()); @@ -157,13 +145,10 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target, // TODO: deal with perspective in some better way. tol /= 10; } else { - GrScalar sinv = GR_Scalar1 / stretch; - tol = GrMul(tol, sinv); + tol = GrScalarDiv(tol, stretch); } GrScalar tolSqd = GrMul(tol, tol); - path->rewind(); - int subpathCnt; int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol); @@ -185,16 +170,14 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target, GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt); - path->rewind(); - GrPoint pts[4]; + SkPath::Iter iter(path, true); bool first = true; int subpath = 0; for (;;) { - GrPathCmd cmd = path->next(pts); - switch (cmd) { + switch (iter.next(pts)) { case kMove_PathCmd: if (!first) { subpathVertCount[subpath] = vert-subpathBase; @@ -263,31 +246,32 @@ FINISHED: return; } - if (subpathCnt == 1 && !inverted && path->convexHint() == kConvex_ConvexHint) { + if (subpathCnt == 1 && !inverted && path.isConvex()) { if (target->isAntialiasState()) { - target->enableState(GrDrawTarget::kEdgeAA_StateBit); EdgeArray edges; GrMatrix inverse, matrix = target->getViewMatrix(); target->getViewInverse(&inverse); count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges); - GrPoint triangle[3]; - triangle[0] = base[0]; - Edge triangleEdges[6]; - triangleEdges[0] = *edges.back(); - triangleEdges[1] = edges[0]; - for (size_t i = 1; i < count - 1; i++) { - triangle[1] = base[i]; - triangle[2] = base[i + 1]; - triangleEdges[2] = edges[i - 1]; - triangleEdges[3] = edges[i]; - triangleEdges[4] = edges[i]; - triangleEdges[5] = edges[i + 1]; - target->setVertexSourceToArray(layout, triangle, 3); - target->setEdgeAAData(&triangleEdges[0].fX); - target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3); + size_t maxEdges = target->getMaxEdges(); + if (count <= maxEdges) { + // All edges fit; upload all edges and draw all verts as a fan + target->setVertexSourceToArray(layout, base, count); + target->setEdgeAAData(&edges[0], count); + target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count); + } else { + // Upload "maxEdges" edges and verts at a time, and draw as + // separate fans + for (size_t i = 0; i < count - 2; i += maxEdges - 2) { + edges[i] = edges[0]; + base[i] = base[0]; + int size = GR_CT_MIN(count - i, maxEdges); + target->setVertexSourceToArray(layout, &base[i], size); + target->setEdgeAAData(&edges[i], size); + target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size); + } } - target->disableState(GrDrawTarget::kEdgeAA_StateBit); + target->setEdgeAAData(NULL, 0); } else { target->setVertexSourceToArray(layout, base, count); target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count); @@ -305,34 +289,34 @@ FINISHED: inVertices[i * 3 + 2] = 1.0; } - GLUtesselator* tess = internal_gluNewTess(); + GLUtesselator* tess = Sk_gluNewTess(); unsigned windingRule = fill_type_to_glu_winding_rule(fill); - internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule); - internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData); - internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData); - internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData); - internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData); - internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData); + Sk_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule); + Sk_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData); + Sk_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData); + Sk_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData); + Sk_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData); + Sk_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData); GrTDArray<short> indices; GrTDArray<GrPoint> vertices; PolygonData data(&vertices, &indices); - internal_gluTessBeginPolygon(tess, &data); + Sk_gluTessBeginPolygon(tess, &data); size_t i = 0; for (int sp = 0; sp < subpathCnt; ++sp) { - internal_gluTessBeginContour(tess); + Sk_gluTessBeginContour(tess); int start = i; - int end = start + subpathVertCount[sp]; + size_t end = start + subpathVertCount[sp]; for (; i < end; ++i) { double* inVertex = &inVertices[i * 3]; *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]); - internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i)); + Sk_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i)); } - internal_gluTessEndContour(tess); + Sk_gluTessEndContour(tess); } - internal_gluTessEndPolygon(tess); - internal_gluDeleteTess(tess); + Sk_gluTessEndPolygon(tess); + Sk_gluDeleteTess(tess); if (indices.count() > 0) { target->setVertexSourceToArray(layout, vertices.begin(), vertices.count()); @@ -348,25 +332,25 @@ FINISHED: } bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill) const { return kHairLine_PathFill != fill; } void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill, const GrPoint* translate) { GrAlwaysAssert(!"multipass stencil should not be needed"); } bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target, - GrPathIter* path, + const SkPath& path, GrPathFill fill) { int subpathCnt = 0; int tol = GrPathUtils::gTolerance; GrPathUtils::worstCasePointCount(path, &subpathCnt, tol); return (subpathCnt == 1 && !IsFillInverted(fill) && - path->convexHint() == kConvex_ConvexHint); + path.isConvex()); } diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp index 0222042b37..2aacfa25ad 100644 --- a/gpu/src/GrTextContext.cpp +++ b/gpu/src/GrTextContext.cpp @@ -25,11 +25,9 @@ #include "GrGpuVertex.h" #include "GrDrawTarget.h" -static const int TEXT_STAGE = 1; - -static const GrVertexLayout BASE_VLAYOUT = - GrDrawTarget::kTextFormat_VertexLayoutBit | - GrDrawTarget::StageTexCoordVertexLayoutBit(TEXT_STAGE,0); +enum { + kGlyphMaskStage = GrPaint::kTotalStages, +}; void GrTextContext::flushGlyphs() { if (fCurrVertex > 0) { @@ -45,17 +43,17 @@ void GrTextContext::flushGlyphs() { GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode, GrSamplerState::kRepeat_WrapMode, filter); - fDrawTarget->setSamplerState(TEXT_STAGE, sampler); + fDrawTarget->setSamplerState(kGlyphMaskStage, sampler); GrAssert(GrIsALIGN4(fCurrVertex)); int nIndices = fCurrVertex + (fCurrVertex >> 1); GrAssert(fCurrTexture); - fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture); + fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture); if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff || kISA_BlendCoeff != fPaint.fDstBlendCoeff || - NULL != fPaint.getTexture()) { + fPaint.hasTexture()) { GrPrintf("LCD Text will not draw correctly.\n"); } // setup blend so that we get mask * paintColor + (1-mask)*dstColor @@ -117,18 +115,31 @@ GrTextContext::GrTextContext(GrContext* context, fOrigViewMatrix = fContext->getMatrix(); fContext->setMatrix(fExtMatrix); - fVertexLayout = BASE_VLAYOUT; - if (NULL != paint.getTexture()) { - fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0); + fDrawTarget = fContext->getTextTarget(fPaint); + + fVertices = NULL; + fMaxVertices = 0; + + fVertexLayout = + GrDrawTarget::kTextFormat_VertexLayoutBit | + GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0); + + int stageMask = paint.getActiveStageMask(); + if (stageMask) { GrMatrix inverseViewMatrix; if (fOrigViewMatrix.invert(&inverseViewMatrix)) { - fPaint.fSampler.preConcatMatrix(inverseViewMatrix); + fDrawTarget->preConcatSamplerMatrices(stageMask, + inverseViewMatrix); + } + for (int i = 0; i < GrPaint::kTotalStages; ++i) { + if ((1 << i) & stageMask) { + fVertexLayout |= + GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i); + GrAssert(i != kGlyphMaskStage); + } } } - fVertices = NULL; - fMaxVertices = 0; - fDrawTarget = fContext->getTextTarget(fPaint); } GrTextContext::~GrTextContext() { @@ -201,11 +212,11 @@ void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed, } glyph->fPath = path; } - GrPath::Iter iter(*glyph->fPath); + GrPoint translate; translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)), GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop))); - fContext->drawPath(fPaint, &iter, kWinding_PathFill, + fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill, &translate); return; } diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk index fef97844c8..bd6f06148e 100644 --- a/gpu/src/gr_files.mk +++ b/gpu/src/gr_files.mk @@ -19,10 +19,10 @@ SOURCE := \ GrInOrderDrawBuffer.cpp \ GrMatrix.cpp \ GrMemory.cpp \ - GrPath.cpp \ GrPathUtils.cpp \ GrRectanizer_fifo.cpp \ GrResource.cpp \ + GrTesselatedPathRenderer.cpp \ GrTexture.cpp \ GrTextureCache.cpp \ GrTextContext.cpp \ diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp index 320dd15bd7..338f2cc35e 100644 --- a/gpu/src/gr_unittests.cpp +++ b/gpu/src/gr_unittests.cpp @@ -68,7 +68,7 @@ static void test_bsearch() { for (size_t n = 0; n < GR_ARRAY_COUNT(array); n++) { for (size_t i = 0; i < n; i++) { int index = GrTBSearch<int, int>(array, n, array[i]); - GrAssert(index == i); + GrAssert(index == (int) i); index = GrTBSearch<int, int>(array, n, -array[i]); GrAssert(index < 0); } @@ -80,10 +80,10 @@ class BogusEntry {}; static void test_binHashKey() { - const char* testStringA = "abcdA"; - const char* testStringB = "abcdB"; + const char* testStringA = "abcdABCD"; + const char* testStringB = "abcdBBCD"; enum { - kDataLenUsedForKey = 5 + kDataLenUsedForKey = 8 }; typedef GrBinHashKey<BogusEntry, kDataLenUsedForKey> KeyType; @@ -92,7 +92,7 @@ static void test_binHashKey() int passCnt = 0; while (keyA.doPass()) { ++passCnt; - keyA.keyData(reinterpret_cast<const uint8_t*>(testStringA), kDataLenUsedForKey); + keyA.keyData(reinterpret_cast<const uint32_t*>(testStringA), kDataLenUsedForKey); } GrAssert(passCnt == 1); //We expect the static allocation to suffice GrBinHashKey<BogusEntry, kDataLenUsedForKey-1> keyBust; @@ -100,7 +100,7 @@ static void test_binHashKey() while (keyBust.doPass()) { ++passCnt; // Exceed static storage by 1 - keyBust.keyData(reinterpret_cast<const uint8_t*>(testStringA), kDataLenUsedForKey); + keyBust.keyData(reinterpret_cast<const uint32_t*>(testStringA), kDataLenUsedForKey); } GrAssert(passCnt == 2); //We expect dynamic allocation to be necessary GrAssert(keyA.getHash() == keyBust.getHash()); @@ -109,14 +109,14 @@ static void test_binHashKey() // the same hash as with one chunk KeyType keyA2; while (keyA2.doPass()) { - keyA2.keyData(reinterpret_cast<const uint8_t*>(testStringA), 2); - keyA2.keyData(&reinterpret_cast<const uint8_t*>(testStringA)[2], kDataLenUsedForKey-2); + keyA2.keyData(reinterpret_cast<const uint32_t*>(testStringA), 4); + keyA2.keyData(&reinterpret_cast<const uint32_t*>(testStringA)[4], kDataLenUsedForKey-4); } GrAssert(keyA.getHash() == keyA2.getHash()); KeyType keyB; while (keyB.doPass()){ - keyB.keyData(reinterpret_cast<const uint8_t*>(testStringB), kDataLenUsedForKey); + keyB.keyData(reinterpret_cast<const uint32_t*>(testStringB), kDataLenUsedForKey); } GrAssert(keyA.compare(keyB) < 0); GrAssert(keyA.compare(keyA2) == 0); @@ -148,13 +148,130 @@ static void test_binHashKey() GrAssert(keyBust3.compare(keyBust2) == 0); } +static void test_convex() { +#if 0 + GrPath testPath; + GrPath::Iter testIter; + + GrPath pt; + pt.moveTo(0, 0); + pt.close(); + + testIter.reset(pt); + testPath.resetFromIter(&testIter); + GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); + + GrPath line; + line.moveTo(GrIntToScalar(12), GrIntToScalar(20)); + line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20)); + line.close(); + + testIter.reset(line); + testPath.resetFromIter(&testIter); + GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); + + GrPath triLeft; + triLeft.moveTo(0, 0); + triLeft.lineTo(1, 0); + triLeft.lineTo(1, 1); + triLeft.close(); + + testIter.reset(triLeft); + testPath.resetFromIter(&testIter); + GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); + + GrPath triRight; + triRight.moveTo(0, 0); + triRight.lineTo(-1, 0); + triRight.lineTo(1, 1); + triRight.close(); + + testIter.reset(triRight); + testPath.resetFromIter(&testIter); + GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); + + GrPath square; + square.moveTo(0, 0); + square.lineTo(1, 0); + square.lineTo(1, 1); + square.lineTo(0, 1); + square.close(); + + testIter.reset(square); + testPath.resetFromIter(&testIter); + GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); + + GrPath redundantSquare; + square.moveTo(0, 0); + square.lineTo(0, 0); + square.lineTo(0, 0); + square.lineTo(1, 0); + square.lineTo(1, 0); + square.lineTo(1, 0); + square.lineTo(1, 1); + square.lineTo(1, 1); + square.lineTo(1, 1); + square.lineTo(0, 1); + square.lineTo(0, 1); + square.lineTo(0, 1); + square.close(); + + testIter.reset(redundantSquare); + testPath.resetFromIter(&testIter); + GrAssert(kConvex_ConvexHint == testPath.getConvexHint()); + + GrPath bowTie; + bowTie.moveTo(0, 0); + bowTie.lineTo(0, 0); + bowTie.lineTo(0, 0); + bowTie.lineTo(1, 1); + bowTie.lineTo(1, 1); + bowTie.lineTo(1, 1); + bowTie.lineTo(1, 0); + bowTie.lineTo(1, 0); + bowTie.lineTo(1, 0); + bowTie.lineTo(0, 1); + bowTie.lineTo(0, 1); + bowTie.lineTo(0, 1); + bowTie.close(); + + testIter.reset(bowTie); + testPath.resetFromIter(&testIter); + GrAssert(kConcave_ConvexHint == testPath.getConvexHint()); + + GrPath spiral; + spiral.moveTo(0, 0); + spiral.lineTo(1, 0); + spiral.lineTo(1, 1); + spiral.lineTo(0, 1); + spiral.lineTo(0,.5); + spiral.lineTo(.5,.5); + spiral.lineTo(.5,.75); + spiral.close(); + + testIter.reset(spiral); + testPath.resetFromIter(&testIter); + GrAssert(kConcave_ConvexHint == testPath.getConvexHint()); + + GrPath dent; + dent.moveTo(0, 0); + dent.lineTo(1, 1); + dent.lineTo(0, 1); + dent.lineTo(-.5,2); + dent.lineTo(-2, 1); + dent.close(); + + testIter.reset(dent); + testPath.resetFromIter(&testIter); + GrAssert(kConcave_ConvexHint == testPath.getConvexHint()); +#endif +} + void gr_run_unittests() { test_tdarray(); test_bsearch(); test_binHashKey(); + test_convex(); GrRedBlackTree<int>::UnitTest(); - GrPath::ConvexUnitTest(); GrDrawTarget::VertexLayoutUnitTest(); } - - diff --git a/gpu/src/mac/GrGLDefaultInterface_mac.cpp b/gpu/src/mac/GrGLDefaultInterface_mac.cpp index b9396faace..fb5b182609 100644 --- a/gpu/src/mac/GrGLDefaultInterface_mac.cpp +++ b/gpu/src/mac/GrGLDefaultInterface_mac.cpp @@ -155,6 +155,8 @@ void GrGLSetDefaultGLInterface() { gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT; #endif #endif + gDefaultInterface.fBindFragDataLocationIndexed = NULL; + gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding; gDefaultInterfaceInit = true; diff --git a/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp new file mode 100644 index 0000000000..0350c30dbf --- /dev/null +++ b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp @@ -0,0 +1,184 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "GrGLInterface.h" + +#include "GL/osmesa.h" +#include <GL/glext.h> +#include <GL/glu.h> + +#define GR_GL_GET_PROC(F) gDefaultInterface.f ## F = (GrGL ## F ## Proc) \ + OSMesaGetProcAddress("gl" #F); +#define GR_GL_GET_PROC_SUFFIX(F, S) gDefaultInterface.f ## F = (GrGL ## F ## Proc) \ + OSMesaGetProcAddress("gl" #F #S); + +void GrGLSetDefaultGLInterface() { + static GrGLInterface gDefaultInterface; + static bool gDefaultInterfaceInit; + if (!gDefaultInterfaceInit && NULL != OSMesaGetCurrentContext()) { + int major, minor; + const char* versionString = (const char*) glGetString(GL_VERSION); + const char* extString = (const char*) glGetString(GL_EXTENSIONS); + gl_version_from_string(&major, &minor, versionString); + + if (major == 1 && minor < 5) { + // We must have array and element_array buffer objects. + return; + } + + gDefaultInterface.fActiveTexture = glActiveTexture; + GR_GL_GET_PROC(AttachShader); + GR_GL_GET_PROC(BindAttribLocation); + GR_GL_GET_PROC(BindBuffer); + gDefaultInterface.fBindTexture = glBindTexture; + gDefaultInterface.fBlendColor = glBlendColor; + gDefaultInterface.fBlendFunc = glBlendFunc; + GR_GL_GET_PROC(BufferData); + GR_GL_GET_PROC(BufferSubData); + gDefaultInterface.fClear = glClear; + gDefaultInterface.fClearColor = glClearColor; + gDefaultInterface.fClearStencil = glClearStencil; + gDefaultInterface.fClientActiveTexture = glClientActiveTexture; + gDefaultInterface.fColorMask = glColorMask; + gDefaultInterface.fColorPointer = glColorPointer; + gDefaultInterface.fColor4ub = glColor4ub; + GR_GL_GET_PROC(CompileShader); + gDefaultInterface.fCompressedTexImage2D = glCompressedTexImage2D; + GR_GL_GET_PROC(CreateProgram); + GR_GL_GET_PROC(CreateShader); + gDefaultInterface.fCullFace = glCullFace; + GR_GL_GET_PROC(DeleteBuffers); + GR_GL_GET_PROC(DeleteProgram); + GR_GL_GET_PROC(DeleteShader); + gDefaultInterface.fDeleteTextures = glDeleteTextures; + gDefaultInterface.fDepthMask = glDepthMask; + gDefaultInterface.fDisable = glDisable; + gDefaultInterface.fDisableClientState = glDisableClientState; + GR_GL_GET_PROC(DisableVertexAttribArray); + gDefaultInterface.fDrawArrays = glDrawArrays; + gDefaultInterface.fDrawElements = glDrawElements; + gDefaultInterface.fEnable = glEnable; + gDefaultInterface.fEnableClientState = glEnableClientState; + GR_GL_GET_PROC(EnableVertexAttribArray); + gDefaultInterface.fFrontFace = glFrontFace; + GR_GL_GET_PROC(GenBuffers); + GR_GL_GET_PROC(GetBufferParameteriv); + gDefaultInterface.fGetError = glGetError; + gDefaultInterface.fGetIntegerv = glGetIntegerv; + GR_GL_GET_PROC(GetProgramInfoLog); + GR_GL_GET_PROC(GetProgramiv); + GR_GL_GET_PROC(GetShaderInfoLog); + GR_GL_GET_PROC(GetShaderiv); + gDefaultInterface.fGetString = glGetString; + gDefaultInterface.fGenTextures = glGenTextures; + GR_GL_GET_PROC(GetUniformLocation); + gDefaultInterface.fLineWidth = glLineWidth; + GR_GL_GET_PROC(LinkProgram); + gDefaultInterface.fLoadMatrixf = glLoadMatrixf; + GR_GL_GET_PROC(MapBuffer); + gDefaultInterface.fMatrixMode = glMatrixMode; + gDefaultInterface.fPointSize = glPointSize; + gDefaultInterface.fPixelStorei = glPixelStorei; + gDefaultInterface.fReadPixels = glReadPixels; + gDefaultInterface.fScissor = glScissor; + gDefaultInterface.fShadeModel = glShadeModel; + GR_GL_GET_PROC(ShaderSource); + gDefaultInterface.fStencilFunc = glStencilFunc; + GR_GL_GET_PROC(StencilFuncSeparate); + gDefaultInterface.fStencilMask = glStencilMask; + GR_GL_GET_PROC(StencilMaskSeparate); + gDefaultInterface.fStencilOp = glStencilOp; + GR_GL_GET_PROC(StencilOpSeparate); + gDefaultInterface.fTexCoordPointer = glTexCoordPointer; + gDefaultInterface.fTexEnvi = glTexEnvi; + //OSMesa on Mac's glTexImage2D takes a GLenum for internalFormat rather than a GLint. + gDefaultInterface.fTexImage2D = reinterpret_cast<GrGLTexImage2DProc>(glTexImage2D); + gDefaultInterface.fTexParameteri = glTexParameteri; + gDefaultInterface.fTexSubImage2D = glTexSubImage2D; + GR_GL_GET_PROC(Uniform1f); + GR_GL_GET_PROC(Uniform1i); + GR_GL_GET_PROC(Uniform1fv); + GR_GL_GET_PROC(Uniform1iv); + GR_GL_GET_PROC(Uniform2f); + GR_GL_GET_PROC(Uniform2i); + GR_GL_GET_PROC(Uniform2fv); + GR_GL_GET_PROC(Uniform2iv); + GR_GL_GET_PROC(Uniform3f); + GR_GL_GET_PROC(Uniform3i); + GR_GL_GET_PROC(Uniform3fv); + GR_GL_GET_PROC(Uniform3iv); + GR_GL_GET_PROC(Uniform4f); + GR_GL_GET_PROC(Uniform4i); + GR_GL_GET_PROC(Uniform4fv); + GR_GL_GET_PROC(Uniform4iv); + GR_GL_GET_PROC(UniformMatrix2fv); + GR_GL_GET_PROC(UniformMatrix3fv); + GR_GL_GET_PROC(UniformMatrix4fv); + GR_GL_GET_PROC(UnmapBuffer); + GR_GL_GET_PROC(UseProgram); + GR_GL_GET_PROC(VertexAttrib4fv); + GR_GL_GET_PROC(VertexAttribPointer); + gDefaultInterface.fVertexPointer = glVertexPointer; + gDefaultInterface.fViewport = glViewport; + + // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since + // GL_ARB_framebuffer_object doesn't use ARB suffix.) + if (major >= 3 || has_gl_extension_from_string( + "GL_ARB_framebuffer_object", extString)) { + GR_GL_GET_PROC(GenFramebuffers); + GR_GL_GET_PROC(BindFramebuffer); + GR_GL_GET_PROC(FramebufferTexture2D); + GR_GL_GET_PROC(CheckFramebufferStatus); + GR_GL_GET_PROC(DeleteFramebuffers); + GR_GL_GET_PROC(RenderbufferStorage); + GR_GL_GET_PROC(GenRenderbuffers); + GR_GL_GET_PROC(DeleteRenderbuffers); + GR_GL_GET_PROC(FramebufferRenderbuffer); + GR_GL_GET_PROC(BindRenderbuffer); + GR_GL_GET_PROC(RenderbufferStorageMultisample); + GR_GL_GET_PROC(BlitFramebuffer); + } else if (has_gl_extension_from_string("GL_EXT_framebuffer_object", + extString)) { + GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT); + GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT); + GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT); + GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT); + GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT); + GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT); + GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT); + GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT); + GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT); + GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT); + if (has_gl_extension_from_string("GL_EXT_framebuffer_multisample", + extString)) { + GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT); + } + if (has_gl_extension_from_string("GL_EXT_framebuffer_blit", + extString)) { + GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT); + } + } else { + // we must have FBOs + return; + } + GR_GL_GET_PROC(BindFragDataLocationIndexed); + gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding; + + gDefaultInterfaceInit = true; + } + if (gDefaultInterfaceInit) + GrGLSetGLInterface(&gDefaultInterface); +} diff --git a/gpu/src/unix/GrGLDefaultInterface_unix.cpp b/gpu/src/unix/GrGLDefaultInterface_unix.cpp index ba670657c8..3e9b9754d6 100644 --- a/gpu/src/unix/GrGLDefaultInterface_unix.cpp +++ b/gpu/src/unix/GrGLDefaultInterface_unix.cpp @@ -133,6 +133,7 @@ void GrGLSetDefaultGLInterface() { GR_GL_GET_PROC(VertexAttribPointer); gDefaultInterface.fVertexPointer = glVertexPointer; gDefaultInterface.fViewport = glViewport; + GR_GL_GET_PROC(BindFragDataLocationIndexed); // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since // GL_ARB_framebuffer_object doesn't use ARB suffix.) diff --git a/gpu/src/win/GrGLDefaultInterface_win.cpp b/gpu/src/win/GrGLDefaultInterface_win.cpp index 53fb26a17a..428abb1c2e 100644 --- a/gpu/src/win/GrGLDefaultInterface_win.cpp +++ b/gpu/src/win/GrGLDefaultInterface_win.cpp @@ -139,6 +139,7 @@ void GrGLSetDefaultGLInterface() { GR_GL_GET_PROC(UseProgram); GR_GL_GET_PROC(VertexAttrib4fv); GR_GL_GET_PROC(VertexAttribPointer); + GR_GL_GET_PROC(BindFragDataLocationIndexed); // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since // GL_ARB_framebuffer_object doesn't use ARB suffix.) diff --git a/gyp/common.gypi b/gyp/common.gypi deleted file mode 100644 index aadaffb543..0000000000 --- a/gyp/common.gypi +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{ - 'conditions' : [ - ['OS == "win"', - { - 'target_defaults': { - 'msvs_cygwin_shell': 0, - 'msvs_settings': { - 'VCCLCompilerTool': { - 'WarningLevel': '1', - 'WarnAsError': 'false', - 'DebugInformationFormat': '3', - 'AdditionalOptions': '/MP', - }, - }, - 'configurations': { - 'Debug': { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'Optimization': '0', # 0 = /Od - 'PreprocessorDefinitions': ['_DEBUG'], - 'RuntimeLibrary': '3', # 3 = /MDd (debug DLL) - }, - 'VCLinkerTool': { - 'GenerateDebugInformation': 'true', - }, - }, - }, - 'Release': { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'Optimization': '2', # 2 = /Os - 'PreprocessorDefinitions': ['NDEBUG'], - 'RuntimeLibrary': '2', # 2 = /MD (nondebug DLL) - }, - 'VCLinkerTool': { - 'GenerateDebugInformation': 'false', - }, - }, - }, - }, - }, - }, - ], - ['OS == "linux"', - { - 'target_defaults': { - 'configurations': { - 'Debug': { - 'cflags': ['-g'] - }, - 'Release': { - 'cflags': ['-O2'] - }, - }, - }, - }, - ], - ['OS == "mac"', - { - 'target_defaults': { - 'configurations': { - 'Debug': { - 'cflags': ['-g'] - }, - 'Release': { - 'cflags': ['-O2'] - }, - }, - }, - 'xcode_settings': { - 'SYMROOT': '<(DEPTH)/xcodebuild', - }, - }, - ], - ], -} -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/gyp/gyp_skia b/gyp/gyp_skia deleted file mode 100755 index 72b4879457..0000000000 --- a/gyp/gyp_skia +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script is a wrapper which invokes gyp with the correct --depth argument, -# and supports the automatic regeneration of build files if skia.gyp is -# changed (Linux-only). - -import glob -import os -import shlex -import sys - -script_dir = os.path.dirname(__file__) - -gyp_dir = os.path.normpath(os.path.join(script_dir, os.pardir, 'third_party')) - -sys.path.append(os.path.join(gyp_dir, 'gyp', 'pylib')) -import gyp - -def additional_include_files(args=[]): - # Determine the include files specified on the command line. - # This doesn't cover all the different option formats you can use, - # but it's mainly intended to avoid duplicating flags on the automatic - # makefile regeneration which only uses this format. - specified_includes = set() - for arg in args: - if arg.startswith('-I') and len(arg) > 2: - specified_includes.add(os.path.realpath(arg[2:])) - - result = [] - def AddInclude(path): - if os.path.realpath(path) not in specified_includes: - result.append(path) - - # Always include common.gypi - AddInclude(os.path.join(script_dir, 'common.gypi')) - - return result - -if __name__ == '__main__': - args = sys.argv[1:] - - # This could give false positives since it doesn't actually do real option - # parsing. Oh well. - gyp_file_specified = False - for arg in args: - if arg.endswith('.gyp'): - gyp_file_specified = True - break - - # If we didn't get a file, then fall back to assuming 'skia.gyp' from the - # same directory as the script. - if not gyp_file_specified: - args.append(os.path.join(script_dir, 'skia.gyp')) - - args.extend(['-I' + i for i in additional_include_files(args)]) - - args.extend(['--depth']) - args.extend([os.path.abspath(script_dir)]) - print 'Updating projects from gyp files...' - sys.stdout.flush() - - # Off we go... - sys.exit(gyp.main(args)) diff --git a/gyp/skia.gyp b/gyp/skia.gyp deleted file mode 100644 index 7247828bb1..0000000000 --- a/gyp/skia.gyp +++ /dev/null @@ -1,1589 +0,0 @@ -{ - 'target_defaults': { - 'configurations': { - 'Debug': { - 'defines': [ - 'SK_DEBUG', - 'GR_DEBUG=1', - ], - }, - 'Release': { - 'defines': [ - 'SK_RELEASE', - 'GR_RELEASE=1', - ], - }, - }, - 'conditions': [ - [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { - 'include_dirs' : [ - '/usr/include/freetype2', - ], - }], - [ 'OS == "mac"', { - 'defines': [ - 'SK_BUILD_FOR_MAC', - ], - }], - [ 'OS == "win"', { - 'defines': [ - 'SK_BUILD_FOR_WIN32', - 'SK_IGNORE_STDINT_DOT_H', - ], - }], - [ 'OS == "linux"', { - 'defines': [ - 'SK_SAMPLES_FOR_X', - ], - }], - ], - 'direct_dependent_settings': { - 'conditions': [ - [ 'OS == "mac"', { - 'defines': [ - 'SK_BUILD_FOR_MAC', - ], - }], - [ 'OS == "win"', { - 'defines': [ - 'SK_BUILD_FOR_WIN32', - ], - }], - ], - }, - }, - 'targets': [ - { - 'target_name': 'skia', - 'type': 'static_library', - 'msvs_guid': 'B7760B5E-BFA8-486B-ACFD-49E3A6DE8E76', - 'sources': [ - '../src/core/ARGB32_Clamp_Bilinear_BitmapShader.h', - '../src/core/Sk64.cpp', - '../src/core/SkAdvancedTypefaceMetrics.cpp', - '../src/core/SkAlphaRuns.cpp', - '../src/core/SkAntiRun.h', - '../src/core/SkBitmap.cpp', - '../src/core/SkBitmapProcShader.cpp', - '../src/core/SkBitmapProcShader.h', - '../src/core/SkBitmapProcState.cpp', - '../src/core/SkBitmapProcState.h', - '../src/core/SkBitmapProcState_matrix.h', - '../src/core/SkBitmapProcState_matrixProcs.cpp', - '../src/core/SkBitmapProcState_sample.h', - '../src/core/SkBitmapSampler.cpp', - '../src/core/SkBitmapSampler.h', - '../src/core/SkBitmapSamplerTemplate.h', - '../src/core/SkBitmapShader16BilerpTemplate.h', - '../src/core/SkBitmapShaderTemplate.h', - '../src/core/SkBitmap_scroll.cpp', - '../src/core/SkBlitBWMaskTemplate.h', - '../src/core/SkBlitRow_D16.cpp', - '../src/core/SkBlitRow_D32.cpp', - '../src/core/SkBlitRow_D4444.cpp', - '../src/core/SkBlitter.cpp', - '../src/core/SkBlitter_4444.cpp', - '../src/core/SkBlitter_A1.cpp', - '../src/core/SkBlitter_A8.cpp', - '../src/core/SkBlitter_ARGB32.cpp', - '../src/core/SkBlitter_RGB16.cpp', - '../src/core/SkBlitter_Sprite.cpp', - '../src/core/SkBuffer.cpp', - '../src/core/SkCanvas.cpp', - '../src/core/SkChunkAlloc.cpp', - '../src/core/SkClampRange.cpp', - '../src/core/SkClipStack.cpp', - '../src/core/SkColor.cpp', - '../src/core/SkColorFilter.cpp', - '../src/core/SkColorTable.cpp', - '../src/core/SkComposeShader.cpp', - '../src/core/SkConcaveToTriangles.cpp', - '../src/core/SkConcaveToTriangles.h', - '../src/core/SkCordic.cpp', - '../src/core/SkCordic.h', - '../src/core/SkCoreBlitters.h', - '../src/core/SkCubicClipper.cpp', - '../src/core/SkCubicClipper.h', - '../src/core/SkDebug.cpp', - '../src/core/SkDeque.cpp', - '../src/core/SkDevice.cpp', - '../src/core/SkDither.cpp', - '../src/core/SkDraw.cpp', - '../src/core/SkDrawProcs.h', - '../src/core/SkEdgeBuilder.cpp', - '../src/core/SkEdgeClipper.cpp', - '../src/core/SkEdge.cpp', - '../src/core/SkEdge.h', - '../src/core/SkFP.h', - '../src/core/SkFilterProc.cpp', - '../src/core/SkFilterProc.h', - '../src/core/SkFlattenable.cpp', - '../src/core/SkFloat.cpp', - '../src/core/SkFloat.h', - '../src/core/SkFloatBits.cpp', - '../src/core/SkGeometry.cpp', - '../src/core/SkGlobals.cpp', - '../src/core/SkGlyphCache.cpp', - '../src/core/SkGlyphCache.h', - '../src/core/SkGraphics.cpp', - '../src/core/SkLineClipper.cpp', - '../src/core/SkMallocPixelRef.cpp', - '../src/core/SkMask.cpp', - '../src/core/SkMaskFilter.cpp', - '../src/core/SkMath.cpp', - '../src/core/SkMatrix.cpp', - '../src/core/SkMetaData.cpp', - '../src/core/SkPackBits.cpp', - '../src/core/SkPaint.cpp', - '../src/core/SkPath.cpp', - '../src/core/SkPathEffect.cpp', - '../src/core/SkPathHeap.cpp', - '../src/core/SkPathHeap.h', - '../src/core/SkPathMeasure.cpp', - '../src/core/SkPicture.cpp', - '../src/core/SkPictureFlat.cpp', - '../src/core/SkPictureFlat.h', - '../src/core/SkPicturePlayback.cpp', - '../src/core/SkPicturePlayback.h', - '../src/core/SkPictureRecord.cpp', - '../src/core/SkPictureRecord.h', - '../src/core/SkPixelRef.cpp', - '../src/core/SkPoint.cpp', - '../src/core/SkProcSpriteBlitter.cpp', - '../src/core/SkPtrRecorder.cpp', - '../src/core/SkQuadClipper.cpp', - '../src/core/SkQuadClipper.h', - '../src/core/SkRasterizer.cpp', - '../src/core/SkRect.cpp', - '../src/core/SkRefDict.cpp', - '../src/core/SkRegion.cpp', - '../src/core/SkRegionPriv.h', - '../src/core/SkRegion_path.cpp', - '../src/core/SkScalar.cpp', - '../src/core/SkScalerContext.cpp', - '../src/core/SkScan.cpp', - '../src/core/SkScanPriv.h', - '../src/core/SkScan_AntiPath.cpp', - '../src/core/SkScan_Antihair.cpp', - '../src/core/SkScan_Hairline.cpp', - '../src/core/SkScan_Path.cpp', - '../src/core/SkShader.cpp', - '../src/core/SkShape.cpp', - '../src/core/SkSpriteBlitter_ARGB32.cpp', - '../src/core/SkSpriteBlitter_RGB16.cpp', - '../src/core/SkSinTable.h', - '../src/core/SkSpriteBlitter.h', - '../src/core/SkSpriteBlitterTemplate.h', - '../src/core/SkStream.cpp', - '../src/core/SkString.cpp', - '../src/core/SkStroke.cpp', - '../src/core/SkStrokerPriv.cpp', - '../src/core/SkStrokerPriv.h', - '../src/core/SkTextFormatParams.h', - '../src/core/SkTSearch.cpp', - '../src/core/SkTSort.h', - '../src/core/SkTemplatesPriv.h', - '../src/core/SkTypeface.cpp', - '../src/core/SkTypefaceCache.cpp', - '../src/core/SkTypefaceCache.h', - '../src/core/SkUnPreMultiply.cpp', - '../src/core/SkUtils.cpp', - '../src/core/SkWriter32.cpp', - '../src/core/SkXfermode.cpp', - - '../src/opts/opts_check_SSE2.cpp', - - '../src/ports/SkDebug_stdio.cpp', - '../src/ports/SkDebug_win.cpp', - - '../src/ports/SkFontHost_tables.cpp', - '../src/ports/SkGlobals_global.cpp', - '../src/ports/SkMemory_malloc.cpp', - '../src/ports/SkOSFile_stdio.cpp', - '../src/ports/SkTime_Unix.cpp', - '../src/ports/SkXMLParser_empty.cpp', - '../src/ports/sk_predefined_gamma.h', - - '../include/core/Sk64.h', - '../include/core/SkAdvancedTypefaceMetrics.h', - '../include/core/SkAutoKern.h', - '../include/core/SkBitmap.h', - '../include/core/SkBlitRow.h', - '../include/core/SkBlitter.h', - '../include/core/SkBounder.h', - '../include/core/SkBuffer.h', - '../include/core/SkCanvas.h', - '../include/core/SkChunkAlloc.h', - '../include/core/SkClampRange.h', - '../include/core/SkClipStack.h', - '../include/core/SkColor.h', - '../include/core/SkColorFilter.h', - '../include/core/SkColorPriv.h', - '../include/core/SkColorShader.h', - '../include/core/SkComposeShader.h', - '../include/core/SkDeque.h', - '../include/core/SkDescriptor.h', - '../include/core/SkDevice.h', - '../include/core/SkDither.h', - '../include/core/SkDraw.h', - '../include/core/SkDrawFilter.h', - '../include/core/SkDrawLooper.h', - '../include/core/SkEndian.h', - '../include/core/SkFDot6.h', - '../include/core/SkFixed.h', - '../include/core/SkFlattenable.h', - '../include/core/SkFloatBits.h', - '../include/core/SkFloatingPoint.h', - '../include/core/SkFontHost.h', - '../include/core/SkGeometry.h', - '../include/core/SkGlobals.h', - '../include/core/SkGraphics.h', - '../include/core/SkMallocPixelRef.h', - '../include/core/SkMask.h', - '../include/core/SkMaskFilter.h', - '../include/core/SkMath.h', - '../include/core/SkMatrix.h', - '../include/core/SkMetaData.h', - '../include/core/SkOSFile.h', - '../include/core/SkPackBits.h', - '../include/core/SkPaint.h', - '../include/core/SkPath.h', - '../include/core/SkPathEffect.h', - '../include/core/SkPathMeasure.h', - '../include/core/SkPerspIter.h', - '../include/core/SkPicture.h', - '../include/core/SkPixelRef.h', - '../include/core/SkPoint.h', - '../include/core/SkPtrRecorder.h', - '../include/core/SkRandom.h', - '../include/core/SkRasterizer.h', - '../include/core/SkReader32.h', - '../include/core/SkRect.h', - '../include/core/SkRefCnt.h', - '../include/core/SkRefDict.h', - '../include/core/SkRegion.h', - '../include/core/SkScalar.h', - '../include/core/SkScalarCompare.h', - '../include/core/SkScalerContext.h', - '../include/core/SkScan.h', - '../include/core/SkShader.h', - '../include/core/SkStream.h', - '../include/core/SkString.h', - '../include/core/SkStroke.h', - '../include/core/SkTDArray.h', - '../include/core/SkTDStack.h', - '../include/core/SkTDict.h', - '../include/core/SkTRegistry.h', - '../include/core/SkTScopedPtr.h', - '../include/core/SkTSearch.h', - '../include/core/SkTemplates.h', - '../include/core/SkThread.h', - '../include/core/SkThread_platform.h', - '../include/core/SkTime.h', - '../include/core/SkTypeface.h', - '../include/core/SkTypes.h', - '../include/core/SkUnPreMultiply.h', - '../include/core/SkUnitMapper.h', - '../include/core/SkUtils.h', - '../include/core/SkWriter32.h', - '../include/core/SkXfermode.h', - ], - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/ports', - '../include/xml', - '../src/core', - ], - 'msvs_disabled_warnings': [4244, 4267,4345, 4390, 4554, 4800], - 'conditions': [ - [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { - 'cflags': [ - '-Wno-unused', - '-Wno-unused-function', - ], - 'sources': [ - '../include/core/SkMMapStream.h', - '../src/core/SkMMapStream.cpp', - '../src/core/SkBlitter_ARGB32_Subpixel.cpp', - '../src/core/SkFontHost.cpp', - '../src/ports/SkThread_pthread.cpp', - '../src/ports/SkTime_Unix.cpp', - '../src/ports/SkFontHost_FreeType_Subpixel.cpp', - '../src/ports/SkFontHost_FreeType.cpp', - '../src/ports/SkFontHost_gamma_none.cpp', - '../src/ports/SkFontHost_linux.cpp', - ], - 'link_settings': { - 'libraries': [ - '-lfreetype', - '-lpthread', - ], - }, - }], - [ 'OS == "mac"', { - 'include_dirs': [ - '../include/utils/mac', - ], - 'sources': [ - '../include/core/SkMMapStream.h', - '../include/utils/mac/SkCGUtils.h', - - '../src/core/SkMMapStream.cpp', - '../src/ports/SkFontHost_mac_coretext.cpp', - - '../src/ports/SkThread_pthread.cpp', - '../src/ports/SkTime_Unix.cpp', - - '../src/utils/mac/SkCreateCGImageRef.cpp', - ], - }], - [ 'OS == "win"', { - 'include_dirs': [ - 'config/win', - ], - 'sources': [ - '../src/ports/SkFontHost_win.cpp', - '../src/ports/SkThread_win.cpp', - ], - 'sources!': [ - '../src/ports/SkDebug_stdio.cpp', - ], - }], - [ 'OS != "win"', { - 'sources!': [ - '../src/ports/SkDebug_win.cpp', - ], - }], - ], - 'direct_dependent_settings': { - 'include_dirs': [ - 'config', - '../include/config', - '../include/core', - 'ext', - ], - }, - 'dependencies': [ - 'skia_opts' - ], - }, - - # Due to an unfortunate intersection of lameness between gcc and gyp, - # we have to build the *_SSE2.cpp files in a separate target. The - # gcc lameness is that, in order to compile SSE2 intrinsics code, it - # must be passed the -msse2 flag. However, with this flag, it may - # emit SSE2 instructions even for scalar code, such as the CPUID - # test used to test for the presence of SSE2. So that, and all other - # code must be compiled *without* -msse2. The gyp lameness is that it - # does not allow file-specific CFLAGS, so we must create this extra - # target for those files to be compiled with -msse2. - # - # This is actually only a problem on 32-bit Linux (all Intel Macs have - # SSE2, Linux x86_64 has SSE2 by definition, and MSC will happily emit - # SSE2 from instrinsics, while generating plain ol' 386 for everything - # else). However, to keep the .gyp file simple and avoid platform-specific - # build breakage, we do this on all platforms. - - # For about the same reason, we need to compile the ARM opts files - # separately as well. - { - 'target_name': 'skia_opts', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../src/core', - ], - 'conditions': [ - [ '(OS == "linux" or OS == "freebsd" or OS == "openbsd")', { - 'cflags': [ - '-msse2', - ], - }], - ], - 'sources': [ - '../src/opts/SkBitmapProcState_opts_SSE2.cpp', - '../src/opts/SkBlitRow_opts_SSE2.cpp', - '../src/opts/SkUtils_opts_SSE2.cpp', - ], - }, - { - 'target_name': 'effects', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/effects', - ], - 'sources': [ - '../include/effects/Sk1DPathEffect.h', - '../include/effects/Sk2DPathEffect.h', - '../include/effects/SkAvoidXfermode.h', - '../include/effects/SkBlurDrawLooper.h', - '../include/effects/SkBlurMaskFilter.h', - '../include/effects/SkColorMatrix.h', - '../include/effects/SkColorMatrixFilter.h', - '../include/effects/SkCornerPathEffect.h', - '../include/effects/SkDashPathEffect.h', - '../include/effects/SkDiscretePathEffect.h', - '../include/effects/SkDrawExtraPathEffect.h', - '../include/effects/SkEmbossMaskFilter.h', - '../include/effects/SkGradientShader.h', - '../include/effects/SkGroupShape.h', - '../include/effects/SkKernel33MaskFilter.h', - '../include/effects/SkLayerDrawLooper.h', - '../include/effects/SkLayerRasterizer.h', - '../include/effects/SkPaintFlagsDrawFilter.h', - '../include/effects/SkPixelXorXfermode.h', - '../include/effects/SkPorterDuff.h', - '../include/effects/SkRectShape.h', - '../include/effects/SkTableMaskFilter.h', - '../include/effects/SkTransparentShader.h', - - '../src/effects/Sk1DPathEffect.cpp', - '../src/effects/Sk2DPathEffect.cpp', - '../src/effects/SkAvoidXfermode.cpp', - '../src/effects/SkBitmapCache.cpp', - '../src/effects/SkBitmapCache.h', - '../src/effects/SkBlurDrawLooper.cpp', - '../src/effects/SkBlurMask.cpp', - '../src/effects/SkBlurMask.h', - '../src/effects/SkBlurMaskFilter.cpp', - '../src/effects/SkColorFilters.cpp', - '../src/effects/SkColorMatrixFilter.cpp', - '../src/effects/SkCornerPathEffect.cpp', - '../src/effects/SkDashPathEffect.cpp', - '../src/effects/SkDiscretePathEffect.cpp', - '../src/effects/SkEmbossMask.cpp', - '../src/effects/SkEmbossMask.h', - '../src/effects/SkEmbossMask_Table.h', - '../src/effects/SkEmbossMaskFilter.cpp', - '../src/effects/SkGradientShader.cpp', - '../src/effects/SkGroupShape.cpp', - '../src/effects/SkKernel33MaskFilter.cpp', - '../src/effects/SkLayerDrawLooper.cpp', - '../src/effects/SkLayerRasterizer.cpp', - '../src/effects/SkPaintFlagsDrawFilter.cpp', - '../src/effects/SkPixelXorXfermode.cpp', - '../src/effects/SkPorterDuff.cpp', - '../src/effects/SkRadialGradient_Table.h', - '../src/effects/SkRectShape.cpp', - '../src/effects/SkTableMaskFilter.cpp', - '../src/effects/SkTransparentShader.cpp', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/effects', - ], - }, - }, - { - 'target_name': 'images', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/images', - ], - 'sources': [ - '../include/images/SkFlipPixelRef.h', - '../include/images/SkImageDecoder.h', - '../include/images/SkImageEncoder.h', - '../include/images/SkImageRef.h', - '../include/images/SkImageRef_GlobalPool.h', - '../include/images/SkJpegUtility.h', - '../include/images/SkMovie.h', - '../include/images/SkPageFlipper.h', - - '../src/images/bmpdecoderhelper.cpp', - '../src/images/bmpdecoderhelper.h', - '../src/images/SkBitmap_RLEPixels.h', - '../src/images/SkCreateRLEPixelRef.cpp', - '../src/images/SkFDStream.cpp', - '../src/images/SkFlipPixelRef.cpp', - '../src/images/SkImageDecoder.cpp', - '../src/images/SkImageDecoder_Factory.cpp', - '../src/images/SkImageDecoder_libbmp.cpp', - '../src/images/SkImageDecoder_libgif.cpp', - '../src/images/SkImageDecoder_libico.cpp', - '../src/images/SkImageDecoder_libjpeg.cpp', - '../src/images/SkImageDecoder_libpng.cpp', - '../src/images/SkImageDecoder_libpvjpeg.c', - '../src/images/SkImageDecoder_wbmp.cpp', - '../src/images/SkImageEncoder.cpp', - '../src/images/SkImageEncoder_Factory.cpp', - '../src/images/SkImageRef.cpp', - '../src/images/SkImageRefPool.cpp', - '../src/images/SkImageRefPool.h', - '../src/images/SkImageRef_GlobalPool.cpp', - '../src/images/SkJpegUtility.cpp', - '../src/images/SkMovie.cpp', - '../src/images/SkMovie_gif.cpp', - '../src/images/SkPageFlipper.cpp', - '../src/images/SkScaledBitmapSampler.cpp', - '../src/images/SkScaledBitmapSampler.h', - ], - 'conditions': [ - [ 'OS == "win"', { - 'sources!': [ - '../include/images/SkJpegUtility.h', - - '../src/images/SkFDStream.cpp', - '../src/images/SkImageDecoder_libgif.cpp', - '../src/images/SkImageDecoder_libjpeg.cpp', - '../src/images/SkImageDecoder_libpng.cpp', - '../src/images/SkImageDecoder_libpvjpeg.c', - '../src/images/SkJpegUtility.cpp', - '../src/images/SkMovie_gif.cpp', - ], - }], - [ 'OS == "mac"', { - 'sources!': [ - '../include/images/SkJpegUtility.h', - - '../src/images/SkImageDecoder_libgif.cpp', - '../src/images/SkImageDecoder_libjpeg.cpp', - '../src/images/SkImageDecoder_libpng.cpp', - '../src/images/SkImageDecoder_libpvjpeg.c', - '../src/images/SkJpegUtility.cpp', - '../src/images/SkMovie_gif.cpp', - ], - }], - [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { - 'sources!': [ - '../include/images/SkJpegUtility.h', - - '../src/images/SkImageDecoder_libjpeg.cpp', - '../src/images/SkImageDecoder_libgif.cpp', - '../src/images/SkImageDecoder_libpvjpeg.c', - '../src/images/SkJpegUtility.cpp', - '../src/images/SkMovie_gif.cpp', - ], - }], - - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/images', - ], - }, - }, - { - 'target_name': 'xml', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/xml', - '../include/utils', - ], - 'sources': [ - '../include/xml/SkBML_WXMLParser.h', - '../include/xml/SkBML_XMLParser.h', - '../include/xml/SkDOM.h', - '../include/xml/SkJS.h', - '../include/xml/SkXMLParser.h', - '../include/xml/SkXMLWriter.h', - - '../src/xml/SkBML_Verbs.h', - '../src/xml/SkBML_XMLParser.cpp', - '../src/xml/SkDOM.cpp', - '../src/xml/SkJS.cpp', - '../src/xml/SkJSDisplayable.cpp', - '../src/xml/SkXMLParser.cpp', - '../src/xml/SkXMLPullParser.cpp', - '../src/xml/SkXMLWriter.cpp', - ], - 'sources!': [ - '../src/xml/SkXMLPullParser.cpp', #if 0 around class decl in header - ], - 'conditions': [ - [ 'OS == "win" or OS == "mac" or OS == "linux" or OS == "openbsd" or OS == "solaris"', { - 'sources!': [ - # no jsapi.h by default on system - '../include/xml/SkJS.h', - '../src/xml/SkJS.cpp', - '../src/xml/SkJSDisplayable.cpp', - ], - }], - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/xml', - ], - }, - }, - { - 'target_name': 'pdf', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/pdf', - '../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h - ], - 'sources': [ - '../include/pdf/SkPDFCatalog.h', - '../include/pdf/SkPDFDevice.h', - '../include/pdf/SkPDFDocument.h', - '../include/pdf/SkPDFFont.h', - '../include/pdf/SkPDFFormXObject.h', - '../include/pdf/SkPDFGraphicState.h', - '../include/pdf/SkPDFImage.h', - '../include/pdf/SkPDFPage.h', - '../include/pdf/SkPDFShader.h', - '../include/pdf/SkPDFStream.h', - '../include/pdf/SkPDFTypes.h', - '../include/pdf/SkPDFUtils.h', - - '../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', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/pdf', - ], - }, - }, - { - 'target_name': 'utils', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/utils', - '../include/views', - '../include/effects', - '../include/xml', - ], - 'sources': [ - '../include/utils/SkBoundaryPatch.h', - '../include/utils/SkCamera.h', - '../include/utils/SkCubicInterval.h', - '../include/utils/SkCullPoints.h', - '../include/utils/SkDumpCanvas.h', - '../include/utils/SkEGLContext.h', - '../include/utils/SkGLCanvas.h', - '../include/utils/SkInterpolator.h', - '../include/utils/SkLayer.h', - '../include/utils/SkMeshUtils.h', - '../include/utils/SkNinePatch.h', - '../include/utils/SkNWayCanvas.h', - '../include/utils/SkParse.h', - '../include/utils/SkParsePaint.h', - '../include/utils/SkParsePath.h', - '../include/utils/SkProxyCanvas.h', - '../include/utils/SkSfntUtils.h', - '../include/utils/SkTextBox.h', - '../include/utils/SkUnitMappers.h', - - '../src/utils/SkBoundaryPatch.cpp', - '../src/utils/SkCamera.cpp', - '../src/utils/SkColorMatrix.cpp', - '../src/utils/SkCubicInterval.cpp', - '../src/utils/SkCullPoints.cpp', - '../src/utils/SkDumpCanvas.cpp', - '../src/utils/SkEGLContext_none.cpp', - '../src/utils/SkInterpolator.cpp', - '../src/utils/SkLayer.cpp', - '../src/utils/SkMeshUtils.cpp', - '../src/utils/SkNinePatch.cpp', - '../src/utils/SkNWayCanvas.cpp', - '../src/utils/SkOSFile.cpp', - '../src/utils/SkParse.cpp', - '../src/utils/SkParseColor.cpp', - '../src/utils/SkParsePath.cpp', - '../src/utils/SkProxyCanvas.cpp', - '../src/utils/SkSfntUtils.cpp', - '../src/utils/SkUnitMappers.cpp', - ], - 'conditions': [ - [ 'OS == "mac"', { - 'sources': [ - '../include/utils/SkCGUtils.h', - '../src/utils/mac/SkCreateCGImageRef.cpp', - '../src/utils/mac/SkEGLContext_mac.cpp', - ], - }], - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/utils', - ], - }, - }, - { - 'target_name': 'views', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/views', - '../include/xml', - '../include/utils', - '../include/images', - '../include/animator', - '../include/effects', - ], - 'sources': [ - '../include/views/SkApplication.h', - '../include/views/SkBGViewArtist.h', - '../include/views/SkBorderView.h', - '../include/views/SkEvent.h', - '../include/views/SkEventSink.h', - '../include/views/SkImageView.h', - '../include/views/SkKey.h', - '../include/views/SkOSMenu.h', - '../include/views/SkOSWindow_Mac.h', - '../include/views/SkOSWindow_SDL.h', - '../include/views/SkOSWindow_Unix.h', - '../include/views/SkOSWindow_Win.h', - #'../include/views/SkOSWindow_wxwidgets.h', - '../include/views/SkProgressBarView.h', - '../include/views/SkScrollBarView.h', - '../include/views/SkStackViewLayout.h', - '../include/views/SkSystemEventTypes.h', - '../include/views/SkTouchGesture.h', - '../include/views/SkView.h', - '../include/views/SkViewInflate.h', - '../include/views/SkWidget.h', - '../include/views/SkWidgetViews.h', - '../include/views/SkWindow.h', - - '../src/views/SkBGViewArtist.cpp', - '../src/views/SkBorderView.cpp', - '../src/views/SkEvent.cpp', - '../src/views/SkEventSink.cpp', - '../src/views/SkImageView.cpp', - '../src/views/SkListView.cpp', - '../src/views/SkListWidget.cpp', - '../src/views/SkOSMenu.cpp', - '../src/views/SkParsePaint.cpp', - '../src/views/SkProgressBarView.cpp', - '../src/views/SkProgressView.cpp', - '../src/views/SkScrollBarView.cpp', - '../src/views/SkStackViewLayout.cpp', - '../src/views/SkStaticTextView.cpp', - '../src/views/SkTagList.cpp', - '../src/views/SkTagList.h', - '../src/views/SkTextBox.cpp', - '../src/views/SkTouchGesture.cpp', - '../src/views/SkView.cpp', - '../src/views/SkViewInflate.cpp', - '../src/views/SkViewPriv.cpp', - '../src/views/SkViewPriv.h', - '../src/views/SkWidget.cpp', - '../src/views/SkWidgets.cpp', - '../src/views/SkWidgetViews.cpp', - '../src/views/SkWindow.cpp', - ], - 'sources!' : [ - '../src/views/SkListView.cpp', #depends on missing SkListSource implementation - '../src/views/SkListWidget.cpp', #depends on missing SkListSource implementation - ], - 'conditions': [ - [ 'OS == "win"', { - 'sources': [ - '../src/utils/win/SkOSWindow_Win.cpp', - '../src/utils/win/skia_win.cpp', - ], - }], - [ 'OS == "mac"', { - 'sources': [ - '../include/utils/SkCGUtils.h', - #'../src/utils/mac/SkBitmap_Mac.cpp', - '../src/utils/mac/SkCreateCGImageRef.cpp', - '../src/utils/mac/SkEGLContext_mac.cpp', - '../src/utils/mac/skia_mac.cpp', - '../src/utils/mac/SkOSWindow_Mac.cpp', - ], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/Carbon.framework', - '$(SDKROOT)/System/Library/Frameworks/AGL.framework', - ], - }, - }], - [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { - 'include_dirs' : [ - '../include/utils/unix', - ], - 'sources': [ - '../src/utils/unix/keysym2ucs.c', - '../src/utils/unix/SkOSWindow_Unix.cpp', - '../unix_test_app/main.cpp', - ], - }], - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/views', - ], - }, - }, - { - 'target_name': 'skgr', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../src/core', - '../include/gpu', - '../gpu/include', - ], - 'sources': [ - '../include/gpu/SkGpuCanvas.h', - '../include/gpu/SkGpuDevice.h', - '../include/gpu/SkGpuDeviceFactory.h', - '../include/gpu/SkGr.h', - '../include/gpu/SkGrTexturePixelRef.h', - - '../src/gpu/GrPrintf_skia.cpp', - '../src/gpu/SkGpuCanvas.cpp', - '../src/gpu/SkGpuDevice.cpp', - '../src/gpu/SkGr.cpp', - '../src/gpu/SkGrFontScaler.cpp', - '../src/gpu/SkGrTexturePixelRef.cpp', - ], - 'conditions': [ - [ 'OS == "linux"', { - 'defines': [ - 'GR_LINUX_BUILD=1', - ], - }], - [ 'OS == "mac"', { - 'defines': [ - 'GR_MAC_BUILD=1', - ], - }], - [ 'OS == "win"', { - 'defines': [ - 'GR_WIN32_BUILD=1', - ], - }], - ], - 'direct_dependent_settings': { - 'conditions': [ - [ 'OS == "linux"', { - 'defines': [ - 'GR_LINUX_BUILD=1', - ], - }], - [ 'OS == "mac"', { - 'defines': [ - 'GR_MAC_BUILD=1', - ], - }], - [ 'OS == "win"', { - 'defines': [ - 'GR_WIN32_BUILD=1', - ], - }], - ], - 'include_dirs': [ - '../include/gpu', - ], - }, - }, - { - 'target_name': 'gr', - 'type': 'static_library', - 'include_dirs': [ - '../gpu/include', - '../include/core', - '../include/config', - ], - 'dependencies': [ - 'libtess', - ], - 'sources': [ - '../gpu/include/GrAllocator.h', - '../gpu/include/GrAllocPool.h', - '../gpu/include/GrAtlas.h', - '../gpu/include/GrClip.h', - '../gpu/include/GrClipIterator.h', - '../gpu/include/GrColor.h', - '../gpu/include/GrConfig.h', - '../gpu/include/GrContext.h', - '../gpu/include/GrContext_impl.h', - '../gpu/include/GrDrawTarget.h', - '../gpu/include/GrFontScaler.h', - '../gpu/include/GrGeometryBuffer.h', - '../gpu/include/GrGLConfig.h', - '../gpu/include/GrGLConfig_chrome.h', - '../gpu/include/GrGLIndexBuffer.h', - '../gpu/include/GrGLInterface.h', - '../gpu/include/GrGLIRect.h', - '../gpu/include/GrGLTexture.h', - '../gpu/include/GrGLVertexBuffer.h', - '../gpu/include/GrGlyph.h', - '../gpu/include/GrGpu.h', - '../gpu/include/GrGpuVertex.h', - '../gpu/include/GrIndexBuffer.h', - '../gpu/include/GrInOrderDrawBuffer.h', - '../gpu/include/GrInstanceCounter.h', - '../gpu/include/GrIPoint.h', - '../gpu/include/GrKey.h', - '../gpu/include/GrMatrix.h', - '../gpu/include/GrMemory.h', - '../gpu/include/GrMesh.h', - '../gpu/include/GrNoncopyable.h', - '../gpu/include/GrPaint.h', - '../gpu/include/GrPath.h', - '../gpu/include/GrPathIter.h', - '../gpu/include/GrPathRenderer.h', - '../gpu/include/GrPathSink.h', - '../gpu/include/GrPlotMgr.h', - '../gpu/include/GrPoint.h', - '../gpu/include/GrRandom.h', - '../gpu/include/GrRect.h', - '../gpu/include/GrRectanizer.h', - '../gpu/include/GrRefCnt.h', - '../gpu/include/GrResource.h', - '../gpu/include/GrSamplerState.h', - '../gpu/include/GrScalar.h', - '../gpu/include/GrStencil.h', - '../gpu/include/GrStopwatch.h', - '../gpu/include/GrStringBuilder.h', - '../gpu/include/GrTArray.h', - '../gpu/include/GrTBSearch.h', - '../gpu/include/GrTDArray.h', - '../gpu/include/GrTesselatedPathRenderer.h', - '../gpu/include/GrTextContext.h', - '../gpu/include/GrTextStrike.h', - '../gpu/include/GrTexture.h', - '../gpu/include/GrTextureCache.h', - '../gpu/include/GrTHashCache.h', - '../gpu/include/GrTLList.h', - '../gpu/include/GrTouchGesture.h', - '../gpu/include/GrTypes.h', - '../gpu/include/GrUserConfig.h', - '../gpu/include/GrVertexBuffer.h', - - '../gpu/src/GrAllocPool.cpp', - '../gpu/src/GrAtlas.cpp', - '../gpu/src/GrBinHashKey.h', - '../gpu/src/GrBufferAllocPool.cpp', - '../gpu/src/GrBufferAllocPool.h', - '../gpu/src/GrClip.cpp', - '../gpu/src/GrContext.cpp', - '../gpu/src/GrCreatePathRenderer_none.cpp', - '../gpu/src/GrDrawTarget.cpp', - '../gpu/src/GrGLEffect.h', - '../gpu/src/GrGLDefaultInterface_none.cpp', - '../gpu/src/GrGLIndexBuffer.cpp', - '../gpu/src/GrGLInterface.cpp', - '../gpu/src/GrGLProgram.cpp', - '../gpu/src/GrGLProgram.h', - '../gpu/src/GrGLTexture.cpp', - '../gpu/src/GrGLUtil.cpp', - '../gpu/src/GrGLVertexBuffer.cpp', - '../gpu/src/GrGpu.cpp', - '../gpu/src/GrGpuFactory.cpp', - '../gpu/src/GrGpuGL.cpp', - '../gpu/src/GrGpuGL.h', - '../gpu/src/GrGpuGLFixed.cpp', - '../gpu/src/GrGpuGLFixed.h', - '../gpu/src/GrGpuGLShaders.cpp', - '../gpu/src/GrGpuGLShaders.h', - '../gpu/src/GrInOrderDrawBuffer.cpp', - '../gpu/src/GrMatrix.cpp', - '../gpu/src/GrMemory.cpp', - '../gpu/src/GrPath.cpp', - '../gpu/src/GrPathRenderer.cpp', - '../gpu/src/GrPathUtils.cpp', - '../gpu/src/GrPathUtils.h', - '../gpu/src/GrRectanizer.cpp', - '../gpu/src/GrRedBlackTree.h', - '../gpu/src/GrResource.cpp', - '../gpu/src/GrStencil.cpp', - '../gpu/src/GrTesselatedPathRenderer.cpp', - '../gpu/src/GrTextContext.cpp', - '../gpu/src/GrTextStrike.cpp', - '../gpu/src/GrTextStrike_impl.h', - '../gpu/src/GrTexture.cpp', - '../gpu/src/GrTextureCache.cpp', - '../gpu/src/gr_unittests.cpp', - - '../gpu/src/mac/GrGLDefaultInterface_mac.cpp', - - '../gpu/src/win/GrGLDefaultInterface_win.cpp', - - '../gpu/src/unix/GrGLDefaultInterface_unix.cpp', - ], - 'defines': [ - 'GR_IMPLEMENTATION=1', - ], - 'conditions': [ - [ 'OS == "linux"', { - 'defines': [ - 'GR_LINUX_BUILD=1', - ], - 'sources!': [ - '../gpu/src/GrGLDefaultInterface_none.cpp', - ], - 'link_settings': { - 'libraries': [ - '-lGL', - '-lX11', - ], - }, - }], - [ 'OS == "mac"', { - 'defines': [ - 'GR_MAC_BUILD=1', - ], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', - ], - }, - 'sources!': [ - '../gpu/src/GrGLDefaultInterface_none.cpp', - ], - }], - [ 'OS == "win"', { - 'defines': [ - 'GR_WIN32_BUILD=1', - 'GR_GL_FUNCTION_TYPE=__stdcall', - ], - 'sources!': [ - '../gpu/src/GrGLDefaultInterface_none.cpp', - ], - }], - [ 'OS != "win"', { - 'sources!': [ - '../gpu/src/win/GrGLDefaultInterface_win.cpp', - ], - }], - [ 'OS != "mac"', { - 'sources!': [ - '../gpu/src/mac/GrGLDefaultInterface_mac.cpp', - ], - }], - [ 'OS != "linux"', { - 'sources!': [ - '../gpu/src/unix/GrGLDefaultInterface_unix.cpp', - ], - }], - ], - 'direct_dependent_settings': { - 'conditions': [ - [ 'OS == "linux"', { - 'defines': [ - 'GR_LINUX_BUILD=1', - ], - }], - [ 'OS == "mac"', { - 'defines': [ - 'GR_MAC_BUILD=1', - ], - }], - [ 'OS == "win"', { - 'defines': [ - 'GR_WIN32_BUILD=1', - 'GR_GL_FUNCTION_TYPE=__stdcall', - ], - }], - ], - 'include_dirs': [ - '../gpu/include', - ], - }, - }, - { - 'target_name': 'animator', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/effects', - '../include/animator', - '../include/views', - '../include/xml', - '../include/utils', - '../include/images', - ], - 'sources': [ - '../include/animator/SkAnimator.h', - '../include/animator/SkAnimatorView.h', - - '../src/animator/SkAnimate.h', - '../src/animator/SkAnimateActive.cpp', - '../src/animator/SkAnimateActive.h', - '../src/animator/SkAnimateBase.cpp', - '../src/animator/SkAnimateBase.h', - '../src/animator/SkAnimateField.cpp', - '../src/animator/SkAnimateMaker.cpp', - '../src/animator/SkAnimateMaker.h', - '../src/animator/SkAnimateProperties.h', - '../src/animator/SkAnimateSet.cpp', - '../src/animator/SkAnimateSet.h', - '../src/animator/SkAnimator.cpp', - '../src/animator/SkAnimatorScript.cpp', - '../src/animator/SkAnimatorScript.h', - #'../src/animator/SkAnimatorScript2.cpp', fails on windows - #'../src/animator/SkAnimatorScript2.h', - '../src/animator/SkBase64.cpp', - '../src/animator/SkBase64.h', - '../src/animator/SkBoundable.cpp', - '../src/animator/SkBoundable.h', - '../src/animator/SkBuildCondensedInfo.cpp', - #'../src/animator/SkCondensedDebug.cpp', fails on windows - #'../src/animator/SkCondensedRelease.cpp', - '../src/animator/SkDisplayable.cpp', - '../src/animator/SkDisplayable.h', - '../src/animator/SkDisplayAdd.cpp', - '../src/animator/SkDisplayAdd.h', - '../src/animator/SkDisplayApply.cpp', - '../src/animator/SkDisplayApply.h', - '../src/animator/SkDisplayBounds.cpp', - '../src/animator/SkDisplayBounds.h', - '../src/animator/SkDisplayEvent.cpp', - '../src/animator/SkDisplayEvent.h', - '../src/animator/SkDisplayEvents.cpp', - '../src/animator/SkDisplayEvents.h', - '../src/animator/SkDisplayInclude.cpp', - '../src/animator/SkDisplayInclude.h', - '../src/animator/SkDisplayInput.cpp', - '../src/animator/SkDisplayInput.h', - '../src/animator/SkDisplayList.cpp', - '../src/animator/SkDisplayList.h', - '../src/animator/SkDisplayMath.cpp', - '../src/animator/SkDisplayMath.h', - '../src/animator/SkDisplayMovie.cpp', - '../src/animator/SkDisplayMovie.h', - '../src/animator/SkDisplayNumber.cpp', - '../src/animator/SkDisplayNumber.h', - '../src/animator/SkDisplayPost.cpp', - '../src/animator/SkDisplayPost.h', - '../src/animator/SkDisplayRandom.cpp', - '../src/animator/SkDisplayRandom.h', - '../src/animator/SkDisplayScreenplay.cpp', - '../src/animator/SkDisplayScreenplay.h', - '../src/animator/SkDisplayType.cpp', - '../src/animator/SkDisplayType.h', - '../src/animator/SkDisplayTypes.cpp', - '../src/animator/SkDisplayTypes.h', - '../src/animator/SkDisplayXMLParser.cpp', - '../src/animator/SkDisplayXMLParser.h', - '../src/animator/SkDraw3D.cpp', - '../src/animator/SkDraw3D.h', - '../src/animator/SkDrawable.cpp', - '../src/animator/SkDrawable.h', - '../src/animator/SkDrawBitmap.cpp', - '../src/animator/SkDrawBitmap.h', - '../src/animator/SkDrawBlur.cpp', - '../src/animator/SkDrawBlur.h', - '../src/animator/SkDrawClip.cpp', - '../src/animator/SkDrawClip.h', - '../src/animator/SkDrawColor.cpp', - '../src/animator/SkDrawColor.h', - '../src/animator/SkDrawDash.cpp', - '../src/animator/SkDrawDash.h', - '../src/animator/SkDrawDiscrete.cpp', - '../src/animator/SkDrawDiscrete.h', - '../src/animator/SkDrawEmboss.cpp', - '../src/animator/SkDrawEmboss.h', - '../src/animator/SkDrawExtraPathEffect.cpp', - '../src/animator/SkDrawFull.cpp', - '../src/animator/SkDrawFull.h', - '../src/animator/SkDrawGradient.cpp', - '../src/animator/SkDrawGradient.h', - '../src/animator/SkDrawGroup.cpp', - '../src/animator/SkDrawGroup.h', - '../src/animator/SkDrawLine.cpp', - '../src/animator/SkDrawLine.h', - '../src/animator/SkDrawMatrix.cpp', - '../src/animator/SkDrawMatrix.h', - '../src/animator/SkDrawOval.cpp', - '../src/animator/SkDrawOval.h', - '../src/animator/SkDrawPaint.cpp', - '../src/animator/SkDrawPaint.h', - '../src/animator/SkDrawPath.cpp', - '../src/animator/SkDrawPath.h', - '../src/animator/SkDrawPoint.cpp', - '../src/animator/SkDrawPoint.h', - '../src/animator/SkDrawRectangle.cpp', - '../src/animator/SkDrawRectangle.h', - '../src/animator/SkDrawSaveLayer.cpp', - '../src/animator/SkDrawSaveLayer.h', - '../src/animator/SkDrawShader.cpp', - '../src/animator/SkDrawShader.h', - '../src/animator/SkDrawText.cpp', - '../src/animator/SkDrawText.h', - '../src/animator/SkDrawTextBox.cpp', - '../src/animator/SkDrawTextBox.h', - '../src/animator/SkDrawTo.cpp', - '../src/animator/SkDrawTo.h', - '../src/animator/SkDrawTransparentShader.cpp', - '../src/animator/SkDrawTransparentShader.h', - '../src/animator/SkDump.cpp', - '../src/animator/SkDump.h', - '../src/animator/SkExtras.h', - '../src/animator/SkGetCondensedInfo.cpp', - '../src/animator/SkHitClear.cpp', - '../src/animator/SkHitClear.h', - '../src/animator/SkHitTest.cpp', - '../src/animator/SkHitTest.h', - '../src/animator/SkIntArray.h', - '../src/animator/SkMatrixParts.cpp', - '../src/animator/SkMatrixParts.h', - '../src/animator/SkMemberInfo.cpp', - '../src/animator/SkMemberInfo.h', - '../src/animator/SkOpArray.cpp', - '../src/animator/SkOpArray.h', - '../src/animator/SkOperand.h', - '../src/animator/SkOperand2.h', - '../src/animator/SkOperandInterpolator.h', - '../src/animator/SkOperandIterpolator.cpp', - '../src/animator/SkPaintParts.cpp', - '../src/animator/SkPaintParts.h', - '../src/animator/SkParseSVGPath.cpp', - '../src/animator/SkPathParts.cpp', - '../src/animator/SkPathParts.h', - '../src/animator/SkPostParts.cpp', - '../src/animator/SkPostParts.h', - '../src/animator/SkScript.cpp', - '../src/animator/SkScript.h', - '../src/animator/SkScript2.h', - '../src/animator/SkScriptCallBack.h', - '../src/animator/SkScriptDecompile.cpp', - '../src/animator/SkScriptRuntime.cpp', - '../src/animator/SkScriptRuntime.h', - '../src/animator/SkScriptTokenizer.cpp', - '../src/animator/SkSnapshot.cpp', - '../src/animator/SkSnapshot.h', - '../src/animator/SkTDArray_Experimental.h', - '../src/animator/SkTextOnPath.cpp', - '../src/animator/SkTextOnPath.h', - '../src/animator/SkTextToPath.cpp', - '../src/animator/SkTextToPath.h', - '../src/animator/SkTime.cpp', - '../src/animator/SkTypedArray.cpp', - '../src/animator/SkTypedArray.h', - '../src/animator/SkXMLAnimatorWriter.cpp', - '../src/animator/SkXMLAnimatorWriter.h', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/animator', - ], - }, - }, - - { - 'target_name': 'svg', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - '../include/xml', - '../include/utils', - '../include/svg', - ], - 'sources': [ - '../include/svg/SkSVGAttribute.h', - '../include/svg/SkSVGBase.h', - '../include/svg/SkSVGPaintState.h', - '../include/svg/SkSVGParser.h', - '../include/svg/SkSVGTypes.h', - - '../src/svg/SkSVGCircle.cpp', - '../src/svg/SkSVGCircle.h', - '../src/svg/SkSVGClipPath.cpp', - '../src/svg/SkSVGClipPath.h', - '../src/svg/SkSVGDefs.cpp', - '../src/svg/SkSVGDefs.h', - '../src/svg/SkSVGElements.cpp', - '../src/svg/SkSVGElements.h', - '../src/svg/SkSVGEllipse.cpp', - '../src/svg/SkSVGEllipse.h', - '../src/svg/SkSVGFeColorMatrix.cpp', - '../src/svg/SkSVGFeColorMatrix.h', - '../src/svg/SkSVGFilter.cpp', - '../src/svg/SkSVGFilter.h', - '../src/svg/SkSVGG.cpp', - '../src/svg/SkSVGG.h', - '../src/svg/SkSVGGradient.cpp', - '../src/svg/SkSVGGradient.h', - '../src/svg/SkSVGGroup.cpp', - '../src/svg/SkSVGGroup.h', - '../src/svg/SkSVGImage.cpp', - '../src/svg/SkSVGImage.h', - '../src/svg/SkSVGLine.cpp', - '../src/svg/SkSVGLine.h', - '../src/svg/SkSVGLinearGradient.cpp', - '../src/svg/SkSVGLinearGradient.h', - '../src/svg/SkSVGMask.cpp', - '../src/svg/SkSVGMask.h', - '../src/svg/SkSVGMetadata.cpp', - '../src/svg/SkSVGMetadata.h', - '../src/svg/SkSVGPaintState.cpp', - '../src/svg/SkSVGParser.cpp', - '../src/svg/SkSVGPath.cpp', - '../src/svg/SkSVGPath.h', - '../src/svg/SkSVGPolygon.cpp', - '../src/svg/SkSVGPolygon.h', - '../src/svg/SkSVGPolyline.cpp', - '../src/svg/SkSVGPolyline.h', - '../src/svg/SkSVGRadialGradient.cpp', - '../src/svg/SkSVGRadialGradient.h', - '../src/svg/SkSVGRect.cpp', - '../src/svg/SkSVGRect.h', - '../src/svg/SkSVGStop.cpp', - '../src/svg/SkSVGStop.h', - '../src/svg/SkSVGSVG.cpp', - '../src/svg/SkSVGSVG.h', - '../src/svg/SkSVGSymbol.cpp', - '../src/svg/SkSVGSymbol.h', - '../src/svg/SkSVGText.cpp', - '../src/svg/SkSVGText.h', - '../src/svg/SkSVGUse.cpp', - ], - 'sources!' : [ - '../src/svg/SkSVG.cpp', # doesn't compile, maybe this is test code? - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include/svg', - ], - }, - }, - - { - 'target_name': 'experimental', - 'type': 'static_library', - 'include_dirs': [ - '../include/config', - '../include/core', - ], - 'sources': [ - '../experimental/SkMatrix44.cpp', - '../experimental/SkMatrix44.h', - '../experimental/SkSetPoly3To3.cpp', - '../experimental/SkSetPoly3To3_A.cpp', - '../experimental/SkSetPoly3To3_D.cpp', - ], - 'sources!': [ - '../experimental/SkMatrix44.cpp', #doesn't compile - '../experimental/SkMatrix44.h', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../experimental', - ], - }, - }, - - { - 'target_name': 'SampleApp', - 'type': 'executable', - 'mac_bundle' : 1, - 'include_dirs' : [ - '../src/core', # needed to get SkConcaveToTriangle, maybe this should be moved to include dir? - '../gm', # SampleGM.cpp pulls gm.h - ], - 'sources': [ - # gm files needed for SampleGM.cpp - '../gm/bitmapfilters.cpp', - '../gm/blurs.cpp', - '../gm/complexclip.cpp', - '../gm/filltypes.cpp', - '../gm/gm.h', - '../gm/gradients.cpp', - '../gm/points.cpp', - '../gm/poly2poly.cpp', - '../gm/shadertext.cpp', - '../gm/shadows.cpp', - '../gm/shapes.cpp', - '../gm/tilemodes.cpp', - '../gm/xfermodes.cpp', - - '../samplecode/ClockFaceView.cpp', - '../samplecode/OverView.cpp', - '../samplecode/SampleAll.cpp', - '../samplecode/SampleAnimator.cpp', - '../samplecode/SampleApp.cpp', - '../samplecode/SampleArc.cpp', - '../samplecode/SampleAvoid.cpp', - '../samplecode/SampleBigGradient.cpp', - '../samplecode/SampleBitmapRect.cpp', - '../samplecode/SampleBlur.cpp', - '../samplecode/SampleCamera.cpp', - '../samplecode/SampleCircle.cpp', - '../samplecode/SampleCode.h', - '../samplecode/SampleColorFilter.cpp', - '../samplecode/SampleComplexClip.cpp', - '../samplecode/SampleCull.cpp', - '../samplecode/SampleDecode.cpp', - '../samplecode/SampleDither.cpp', - '../samplecode/SampleDitherBitmap.cpp', - '../samplecode/SampleDrawLooper.cpp', - '../samplecode/SampleEffects.cpp', - '../samplecode/SampleEmboss.cpp', - '../samplecode/SampleEncode.cpp', - '../samplecode/SampleExtractAlpha.cpp', - '../samplecode/SampleFillType.cpp', - '../samplecode/SampleFilter.cpp', - '../samplecode/SampleFilter2.cpp', - '../samplecode/SampleFontCache.cpp', - '../samplecode/SampleFontScalerTest.cpp', - '../samplecode/SampleFuzz.cpp', - '../samplecode/SampleGM.cpp', - '../samplecode/SampleGradients.cpp', - '../samplecode/SampleHairline.cpp', - '../samplecode/SampleImage.cpp', - '../samplecode/SampleImageDir.cpp', - '../samplecode/SampleLayerMask.cpp', - '../samplecode/SampleLayers.cpp', - '../samplecode/SampleLCD.cpp', - '../samplecode/SampleLineClipper.cpp', - '../samplecode/SampleLines.cpp', - '../samplecode/SampleMeasure.cpp', - '../samplecode/SampleMipMap.cpp', - '../samplecode/SampleMovie.cpp', - '../samplecode/SampleNinePatch.cpp', - '../samplecode/SampleOvalTest.cpp', - '../samplecode/SampleOverflow.cpp', - '../samplecode/SamplePageFlip.cpp', - '../samplecode/SamplePatch.cpp', - '../samplecode/SamplePath.cpp', - '../samplecode/SamplePathClip.cpp', - '../samplecode/SamplePathEffects.cpp', - '../samplecode/SamplePicture.cpp', - '../samplecode/SamplePoints.cpp', - '../samplecode/SamplePolyToPoly.cpp', - '../samplecode/SampleAARects.cpp', - '../samplecode/SampleRegion.cpp', - '../samplecode/SampleRepeatTile.cpp', - '../samplecode/SampleShaders.cpp', - '../samplecode/SampleShaderText.cpp', - '../samplecode/SampleShapes.cpp', - '../samplecode/SampleSkLayer.cpp', - '../samplecode/SampleSlides.cpp', - '../samplecode/SampleStrokePath.cpp', - '../samplecode/SampleStrokeText.cpp', - '../samplecode/SampleSVG.cpp', - '../samplecode/SampleTests.cpp', - '../samplecode/SampleText.cpp', - '../samplecode/SampleTextAlpha.cpp', - '../samplecode/SampleTextBox.cpp', - '../samplecode/SampleTextEffects.cpp', - '../samplecode/SampleTextOnPath.cpp', - '../samplecode/SampleTiling.cpp', - '../samplecode/SampleTinyBitmap.cpp', - '../samplecode/SampleTriangles.cpp', - '../samplecode/SampleTypeface.cpp', - '../samplecode/SampleUnitMapper.cpp', - '../samplecode/SampleVertices.cpp', - '../samplecode/SampleXfermodes.cpp', - ], - 'sources!': [ - '../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile - '../samplecode/SampleTests.cpp', #includes unknown file SkShaderExtras.h - '../samplecode/SampleWarp.cpp', - '../samplecode/SampleFontCache.cpp', - ], - 'dependencies': [ - 'skia', - 'effects', - 'images', - 'views', - 'utils', - 'animator', - 'xml', - 'svg', - 'experimental', - 'gr', - 'skgr', - ], - 'conditions' : [ - [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', { - 'sources!': [ - '../samplecode/SampleDecode.cpp', - ], - }], - [ 'OS == "win"', { - 'sources!': [ - # require UNIX functions - '../samplecode/SampleEncode.cpp', - '../samplecode/SamplePageFlip.cpp', - ], - }], - [ 'OS == "mac"', { - 'sources!': [ - '../samplecode/SampleDecode.cpp', - ], - }], - - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '2', - 'AdditionalDependencies': [ - 'OpenGL32.lib', - 'usp10.lib', - 'd3d9.lib', - ], - }, - }, - }, - { - 'target_name': 'libtess', - 'type': 'static_library', - 'include_dirs': [ - '../third_party/glu', - ], - 'sources': [ - '../third_party/glu/internal_glu.h', - '../third_party/glu/gluos.h', - '../third_party/glu/libtess/dict-list.h', - '../third_party/glu/libtess/dict.c', - '../third_party/glu/libtess/dict.h', - '../third_party/glu/libtess/geom.c', - '../third_party/glu/libtess/geom.h', - '../third_party/glu/libtess/memalloc.c', - '../third_party/glu/libtess/memalloc.h', - '../third_party/glu/libtess/mesh.c', - '../third_party/glu/libtess/mesh.h', - '../third_party/glu/libtess/normal.c', - '../third_party/glu/libtess/normal.h', - '../third_party/glu/libtess/priorityq-heap.h', - '../third_party/glu/libtess/priorityq-sort.h', - '../third_party/glu/libtess/priorityq.c', - '../third_party/glu/libtess/priorityq.h', - '../third_party/glu/libtess/render.c', - '../third_party/glu/libtess/render.h', - '../third_party/glu/libtess/sweep.c', - '../third_party/glu/libtess/sweep.h', - '../third_party/glu/libtess/tess.c', - '../third_party/glu/libtess/tess.h', - '../third_party/glu/libtess/tessmono.c', - '../third_party/glu/libtess/tessmono.h', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../third_party/glu', - ], - }, - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h index aa2e6cf40b..c56d8cfa78 100644 --- a/include/config/SkUserConfig.h +++ b/include/config/SkUserConfig.h @@ -54,7 +54,7 @@ /* Somewhat independent of how SkScalar is implemented, Skia also wants to know if it can use floats at all. Naturally, if SK_SCALAR_IS_FLOAT is defined, - then so muse SK_CAN_USE_FLOAT, but if scalars are fixed, SK_CAN_USE_FLOAT + SK_CAN_USE_FLOAT must be too; but if scalars are fixed, SK_CAN_USE_FLOAT can go either way. */ //#define SK_CAN_USE_FLOAT @@ -151,4 +151,3 @@ #endif #endif - diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h index ae0b974d6e..6e8da76d29 100644 --- a/include/core/SkClipStack.h +++ b/include/core/SkClipStack.h @@ -43,6 +43,7 @@ public: struct Clip { friend bool operator==(const Clip& a, const Clip& b); + friend bool operator!=(const Clip& a, const Clip& b); const SkRect* fRect; // if non-null, this is a rect clip const SkPath* fPath; // if non-null, this is a path clip SkRegion::Op fOp; diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h index f9e02a29c7..6fa9df365d 100644 --- a/include/core/SkColorPriv.h +++ b/include/core/SkColorPriv.h @@ -155,31 +155,13 @@ static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[], #define SkRGB16Add(a, b) ((a) + (b)) #endif -///////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// #define SK_A32_BITS 8 #define SK_R32_BITS 8 #define SK_G32_BITS 8 #define SK_B32_BITS 8 -/* we check to see if the SHIFT value has already been defined (SkUserConfig.h) - if not, we define it ourself to some default values. We default to OpenGL - order (in memory: r,g,b,a) -*/ -#ifndef SK_A32_SHIFT - #ifdef SK_CPU_BENDIAN - #define SK_R32_SHIFT 24 - #define SK_G32_SHIFT 16 - #define SK_B32_SHIFT 8 - #define SK_A32_SHIFT 0 - #else - #define SK_R32_SHIFT 0 - #define SK_G32_SHIFT 8 - #define SK_B32_SHIFT 16 - #define SK_A32_SHIFT 24 - #endif -#endif - #define SK_A32_MASK ((1 << SK_A32_BITS) - 1) #define SK_R32_MASK ((1 << SK_R32_BITS) - 1) #define SK_G32_MASK ((1 << SK_G32_BITS) - 1) diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h index 0b198f61da..ea37549652 100644 --- a/include/core/SkComposeShader.h +++ b/include/core/SkComposeShader.h @@ -27,7 +27,7 @@ class SkXfermode; This subclass of shader returns the coposition of two other shaders, combined by a xfermode. */ -class SkComposeShader : public SkShader { +class SK_API SkComposeShader : public SkShader { public: /** Create a new compose shader, given shaders A, B, and a combining xfermode mode. When the xfermode is called, it will be given the result from shader A as its diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index 46bcf1a105..d9a4fde4c2 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -155,31 +155,6 @@ public: virtual void setMatrixClip(const SkMatrix&, const SkRegion&, const SkClipStack&); - /** - * Observer interface for listening to the calls to - * SkDevice::setMatrixClip(...). Users of SkDevice instances should - * implement matrixClipChanged(...) to receive notifications. - */ - class SkMatrixClipObserver : public SkRefCnt { - public: - virtual void matrixClipChanged(const SkMatrix&, const SkRegion&, - const SkClipStack&) = 0; - }; - - /** Assign the clip observer. Note that an extra reference is added to the - * observer, and removed at SkDevice construction, or re-assignment of a - * different observer. - */ - void setMatrixClipObserver(SkMatrixClipObserver* observer); - - /** Return the device's associated SkMatrixClipObserver, or NULL. - * If non-null is returned, the reference count of the object is not - * modified. - */ - SkMatrixClipObserver* getMatrixClipObserver() const { - return fMatrixClipObserver; - } - /** Called when this device gains focus (i.e becomes the current device for drawing). */ @@ -306,8 +281,6 @@ private: SkIPoint fOrigin; SkMetaData* fMetaData; - SkMatrixClipObserver* fMatrixClipObserver; - SkDeviceFactory* fCachedDeviceFactory; }; diff --git a/include/core/SkMath.h b/include/core/SkMath.h index 3e729042e3..efe378a733 100644 --- a/include/core/SkMath.h +++ b/include/core/SkMath.h @@ -115,7 +115,7 @@ static inline unsigned SkClampUMax(unsigned value, unsigned max) { /////////////////////////////////////////////////////////////////////////////// -#if defined(__arm__) && !defined(__thumb__) +#if defined(__arm__) #define SkCLZ(x) __builtin_clz(x) #endif diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h index 7c779027be..480e0777ac 100644 --- a/include/core/SkMatrix.h +++ b/include/core/SkMatrix.h @@ -438,6 +438,7 @@ public: */ bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; +#ifdef SK_SCALAR_IS_FIXED friend bool operator==(const SkMatrix& a, const SkMatrix& b) { return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0; } @@ -445,6 +446,12 @@ public: friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0; } +#else + friend bool operator==(const SkMatrix& a, const SkMatrix& b); + friend bool operator!=(const SkMatrix& a, const SkMatrix& b) { + return !(a == b); + } +#endif enum { // flatten/unflatten will never return a value larger than this @@ -488,7 +495,12 @@ private: kRectStaysRect_Mask = 0x10, kUnknown_Mask = 0x80, - + + kORableMasks = kTranslate_Mask | + kScale_Mask | + kAffine_Mask | + kPerspective_Mask, + kAllMasks = kTranslate_Mask | kScale_Mask | kAffine_Mask | @@ -506,7 +518,12 @@ private: SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask); fTypeMask = SkToU8(mask); } - + + void orTypeMask(int mask) { + SkASSERT((mask & kORableMasks) == mask); + fTypeMask = SkToU8(fTypeMask | mask); + } + void clearTypeMask(int mask) { // only allow a valid mask SkASSERT((mask & kAllMasks) == mask); diff --git a/include/core/SkPath.h b/include/core/SkPath.h index 18dcd11070..7120d3fa95 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -88,9 +88,10 @@ public: /** Returns true if the filltype is one of the Inverse variants */ bool isInverseFillType() const { return (fFillType & 2) != 0; } - /** Toggle between inverse and normal filltypes. This reverse the return - value of isInverseFillType() - */ + /** + * Toggle between inverse and normal filltypes. This reverse the return + * value of isInverseFillType() + */ void toggleInverseFillType() { fFillType ^= 2; GEN_ID_INC; @@ -103,14 +104,33 @@ public: }; /** - * Return the path's convexity, as stored in the path. + * Return the path's convexity, as stored in the path. If it is currently + * unknown, and the computeIfUnknown bool is true, then this will first + * call ComputeConvexity() and then return that (cached) value. + */ + Convexity getConvexity() const { + if (kUnknown_Convexity == fConvexity) { + fConvexity = (uint8_t)ComputeConvexity(*this); + } + return (Convexity)fConvexity; + } + + /** + * Return the currently cached value for convexity, even if that is set to + * kUnknown_Convexity. Note: getConvexity() will automatically call + * ComputeConvexity and cache its return value if the current setting is + * kUnknown. */ - Convexity getConvexity() const { return (Convexity)fConvexity; } + Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; } /** * Store a convexity setting in the path. There is no automatic check to * see if this value actually agress with the return value from * ComputeConvexity(). + * + * Note: even if this is set to a "known" value, if the path is later + * changed (e.g. lineTo(), addRect(), etc.) then the cached value will be + * reset to kUnknown_Convexity. */ void setConvexity(Convexity); @@ -118,9 +138,11 @@ public: * Compute the convexity of the specified path. This does not look at the * value stored in the path, but computes it directly from the path's data. * + * This never returns kUnknown_Convexity. + * * If there is more than one contour, this returns kConcave_Convexity. - * If the contour is degenerate (i.e. all segements are colinear, - * then this returns kUnknown_Convexity. + * If the contour is degenerate (e.g. there are fewer than 3 non-degenerate + * segments), then this returns kConvex_Convexity. * The contour is treated as if it were closed, even if there is no kClose * verb. */ @@ -635,7 +657,7 @@ private: mutable SkRect fBounds; mutable uint8_t fBoundsIsDirty; uint8_t fFillType; - uint8_t fConvexity; + mutable uint8_t fConvexity; #ifdef ANDROID uint32_t fGenerationID; #endif diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h index 57cc3682f5..23ce02beb3 100644 --- a/include/core/SkPostConfig.h +++ b/include/core/SkPostConfig.h @@ -132,6 +132,25 @@ #endif #endif +/* + * We check to see if the SHIFT value has already been defined. + * if not, we define it ourself to some default values. We default to OpenGL + * order (in memory: r,g,b,a) + */ +#ifndef SK_A32_SHIFT + #ifdef SK_CPU_BENDIAN + #define SK_R32_SHIFT 24 + #define SK_G32_SHIFT 16 + #define SK_B32_SHIFT 8 + #define SK_A32_SHIFT 0 + #else + #define SK_R32_SHIFT 0 + #define SK_G32_SHIFT 8 + #define SK_B32_SHIFT 16 + #define SK_A32_SHIFT 24 + #endif +#endif + // stdlib macros #if 0 diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h index 8d47d461ff..6ec73cec38 100644 --- a/include/core/SkPreConfig.h +++ b/include/core/SkPreConfig.h @@ -41,14 +41,16 @@ #define SK_BUILD_FOR_UNIX #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #define SK_BUILD_FOR_IOS - #elif defined(ANDROID_NDK) - #define SK_BUILD_FOR_ANDROID_NDK - #elif defined(ANROID) - #define SK_BUILD_FOR_ANDROID #else #define SK_BUILD_FOR_MAC #endif + #if defined(ANDROID) + #define SK_BUILD_FOR_ANDROID + #endif + #if defined(ANDROID_NDK) + #define SK_BUILD_FOR_ANDROID_NDK + #endif #endif ////////////////////////////////////////////////////////////////////// diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h index 03a34c7ca6..654ebd5f19 100644 --- a/include/core/SkReader32.h +++ b/include/core/SkReader32.h @@ -101,7 +101,14 @@ public: uint16_t readU16() { return (uint16_t)this->readInt(); } int32_t readS32() { return this->readInt(); } uint32_t readU32() { return this->readInt(); } - + + /** + * Read the length of a string written by SkWriter32::writeString() + * (if len is not NULL) and return the null-ternimated address of the + * string. + */ + const char* readString(size_t* len = NULL); + private: // these are always 4-byte aligned const char* fCurr; // current position within buffer diff --git a/include/core/SkRect.h b/include/core/SkRect.h index 550c5d1d46..19ee12a596 100644 --- a/include/core/SkRect.h +++ b/include/core/SkRect.h @@ -290,7 +290,7 @@ struct SK_API SkIRect { void sort(); static const SkIRect& EmptyIRect() { - static const SkIRect gEmpty = {0,0,0,0}; + static const SkIRect gEmpty = { 0, 0, 0, 0 }; return gEmpty; } }; diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h index ebe621be4f..5dbf6841a0 100644 --- a/include/core/SkScalar.h +++ b/include/core/SkScalar.h @@ -66,9 +66,47 @@ int exponent = bits << 1 >> 24; return exponent != 0xFF; } +#ifdef SK_DEBUG + /** SkIntToScalar(n) returns its integer argument as an SkScalar + * + * If we're compiling in DEBUG mode, and can thus afford some extra runtime + * cycles, check to make sure that the parameter passed in has not already + * been converted to SkScalar. (A double conversion like this is harmless + * for SK_SCALAR_IS_FLOAT, but for SK_SCALAR_IS_FIXED this causes trouble.) + * + * Note that we need all of these method signatures to properly handle the + * various types that we pass into SkIntToScalar() to date: + * int, size_t, U8CPU, etc., even though what we really mean is "anything + * but a float". + */ + static inline float SkIntToScalar(signed int param) { + return (float)param; + } + static inline float SkIntToScalar(unsigned int param) { + return (float)param; + } + static inline float SkIntToScalar(signed long param) { + return (float)param; + } + static inline float SkIntToScalar(unsigned long param) { + return (float)param; + } + static inline float SkIntToScalar(float param) { + /* If the parameter passed into SkIntToScalar is a float, + * one of two things has happened: + * 1. the parameter was an SkScalar (which is typedef'd to float) + * 2. the parameter was a float instead of an int + * + * Either way, it's not good. + */ + SkASSERT(!"looks like you passed an SkScalar into SkIntToScalar"); + return (float)0; + } +#else // not SK_DEBUG /** SkIntToScalar(n) returns its integer argument as an SkScalar */ #define SkIntToScalar(n) ((float)(n)) +#endif // not SK_DEBUG /** SkFixedToScalar(n) returns its SkFixed argument as an SkScalar */ #define SkFixedToScalar(x) SkFixedToFloat(x) @@ -282,4 +320,3 @@ SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], const SkScalar values[], int length); #endif - diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index 55dfcef049..3f818a3112 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -179,6 +179,9 @@ public: kEmbolden_Flag = 0x80, kSubpixelPositioning_Flag = 0x100, kAutohinting_Flag = 0x200, + // these should only ever be set if fMaskFormat is LCD + kLCD_Vertical_Flag = 0x400, // else Horizontal + kLCD_BGROrder_Flag = 0x800, // else RGB order }; private: enum { diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h index 8e133c2202..c8ebb6a6ef 100644 --- a/include/core/SkWriter32.h +++ b/include/core/SkWriter32.h @@ -100,7 +100,22 @@ public: } void writePad(const void* src, size_t size); - + + /** + * Writes a string to the writer, which can be retrieved with + * SkReader32::readString(). + * The length can be specified, or if -1 is passed, it will be computed by + * calling strlen(). The length must be < 0xFFFF + */ + void writeString(const char* str, size_t len = (size_t)-1); + + /** + * Computes the size (aligned to multiple of 4) need to write the string + * in a call to writeString(). If the length is not specified, it will be + * computed by calling strlen(). + */ + static size_t WriteStringSize(const char* str, size_t len = (size_t)-1); + // return the current offset (will always be a multiple of 4) uint32_t size() const { return fSize; } void reset(); diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index 4d46bb919a..6ab9d6d562 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -103,11 +103,15 @@ public: kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)] kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] - // these modes are defined in the SVG Compositing standard + // all remaining modes are defined in the SVG Compositing standard // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/ kPlus_Mode, - kMultiply_Mode, - kScreen_Mode, + kMultiply_Mode, + + // all above modes can be expressed as pair of src/dst Coeffs + kCoeffModesCnt, + + kScreen_Mode = kCoeffModesCnt, kOverlay_Mode, kDarken_Mode, kLighten_Mode, diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 15def87942..601da09427 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -163,17 +163,26 @@ private: // doesn't set the texture/sampler/matrix state // caller needs to null out GrPaint's texture if // non-textured drawing is desired. + // Set constantColor to true if a constant color + // will be used. This is an optimization, and can + // always be set to false. constantColor should + // never be true if justAlpha is true. bool skPaint2GrPaintNoShader(const SkPaint& skPaint, bool justAlpha, - GrPaint* grPaint); + GrPaint* grPaint, + bool constantColor); // uses the SkShader to setup paint, act used to // hold lock on cached texture and free it when // destroyed. + // If there is no shader, constantColor will + // be passed to skPaint2GrPaintNoShader. Otherwise + // it is ignored. bool skPaint2GrPaintShader(const SkPaint& skPaint, SkAutoCachedTexture* act, const SkMatrix& ctm, - GrPaint* grPaint); + GrPaint* grPaint, + bool constantColor); SkDrawProcs* initDrawForText(GrTextContext*); bool bindDeviceAsTexture(GrPaint* paint); diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h index 75099b2df6..65565c9b68 100644 --- a/include/gpu/SkGr.h +++ b/include/gpu/SkGr.h @@ -24,8 +24,8 @@ #include "GrConfig.h" #include "GrContext.h" #include "GrFontScaler.h" -#include "GrPathIter.h" #include "GrClipIterator.h" +#include "GrPath.h" // skia headers #include "SkBitmap.h" @@ -130,29 +130,6 @@ public: //////////////////////////////////////////////////////////////////////////////// // Classes -class SkGrPathIter : public GrPathIter { -public: - SkGrPathIter() { fPath = NULL; } - SkGrPathIter(const SkPath& path) { reset(path); } - virtual GrPathCmd next(GrPoint pts[]); - virtual GrPathCmd next(); - virtual void rewind(); - virtual GrConvexHint convexHint() const; - virtual bool getConservativeBounds(GrRect* rect) const; - - void reset(const SkPath& path) { - fPath = &path; - fIter.setPath(path, false); - } -private: - -#if !SK_SCALAR_IS_GR_SCALAR - SkPoint fPoints[4]; -#endif - SkPath::Iter fIter; - const SkPath* fPath; -}; - class SkGrClipIterator : public GrClipIterator { public: SkGrClipIterator() { fClipStack = NULL; fCurr = NULL; } @@ -176,9 +153,8 @@ public: } } - virtual GrPathIter* getPathIter() { - fPathIter.reset(*fCurr->fPath); - return &fPathIter; + virtual const GrPath* getPath() { + return fCurr->fPath; } virtual GrPathFill getPathFill() const; @@ -186,7 +162,6 @@ public: private: const SkClipStack* fClipStack; SkClipStack::B2FIter fIter; - SkGrPathIter fPathIter; // SkClipStack's auto advances on each get // so we store the current pos here. const SkClipStack::B2FIter::Clip* fCurr; @@ -218,7 +193,7 @@ public: rect->fBottom = GrIntToScalar(r.fBottom); } - virtual GrPathIter* getPathIter() { + virtual const GrPath* getPath() { SkASSERT(0); return NULL; } diff --git a/include/pdf/SkPDFCatalog.h b/include/pdf/SkPDFCatalog.h new file mode 100644 index 0000000000..e02ffa1a47 --- /dev/null +++ b/include/pdf/SkPDFCatalog.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFCatalog_DEFINED +#define SkPDFCatalog_DEFINED + +#include <sys/types.h> + +#include "SkPDFTypes.h" +#include "SkRefCnt.h" +#include "SkTDArray.h" + +/** \class SkPDFCatalog + + The PDF catalog manages object numbers and file offsets. It is used + to create the PDF cross reference table. +*/ +class SK_API SkPDFCatalog { +public: + /** Create a PDF catalog. + */ + SkPDFCatalog(); + ~SkPDFCatalog(); + + /** Add the passed object to the catalog. Refs obj. + * @param obj The object to add. + * @param onFirstPage Is the object on the first page. + * @return The obj argument is returned. + */ + SkPDFObject* addObject(SkPDFObject* obj, bool onFirstPage); + + /** Inform the catalog of the object's position in the final stream. + * The object should already have been added to the catalog. Returns + * the object's size. + * @param obj The object to add. + * @param offset The byte offset in the output stream of this object. + */ + size_t setFileOffset(SkPDFObject* obj, size_t offset); + + /** Output the object number for the passed object. + * @param obj The object of interest. + * @param stream The writable output stream to send the output to. + */ + void emitObjectNumber(SkWStream* stream, SkPDFObject* obj); + + /** Return the number of bytes that would be emitted for the passed + * object's object number. + * @param obj The object of interest + */ + size_t getObjectNumberSize(SkPDFObject* obj); + + /** Output the cross reference table for objects in the catalog. + * Returns the total number of objects. + * @param stream The writable output stream to send the output to. + * @param firstPage If true, include first page objects only, otherwise + * include all objects not on the first page. + */ + int32_t emitXrefTable(SkWStream* stream, bool firstPage); + +private: + struct Rec { + Rec(SkPDFObject* object, bool onFirstPage) + : fObject(object), + fFileOffset(0), + fObjNumAssigned(false), + fOnFirstPage(onFirstPage) { + } + SkPDFObject* fObject; + off_t fFileOffset; + bool fObjNumAssigned; + bool fOnFirstPage; + }; + + // TODO(vandebo) Make this a hash if it's a performance problem. + SkTDArray<struct Rec> fCatalog; + + // Number of objects on the first page. + uint32_t fFirstPageCount; + // Next object number to assign (on page > 1). + uint32_t fNextObjNum; + // Next object number to assign on the first page. + uint32_t fNextFirstPageObjNum; + + int findObjectIndex(SkPDFObject* obj) const; + + int assignObjNum(SkPDFObject* obj); +}; + +#endif diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h new file mode 100644 index 0000000000..6e4d8db725 --- /dev/null +++ b/include/pdf/SkPDFDevice.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFDevice_DEFINED +#define SkPDFDevice_DEFINED + +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTScopedPtr.h" + +class SkPDFArray; +class SkPDFDevice; +class SkPDFDict; +class SkPDFFont; +class SkPDFFormXObject; +class SkPDFGraphicState; +class SkPDFObject; +class SkPDFShader; +class SkPDFStream; + +// Private classes. +struct ContentEntry; +struct GraphicStateEntry; + +class SkPDFDeviceFactory : public SkDeviceFactory { +public: + virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width, + int height, bool isOpaque, bool isForLayer); +}; + +/** \class SkPDFDevice + + The drawing context for the PDF backend. +*/ +class SkPDFDevice : public SkDevice { +public: + /** Create a PDF drawing context with the given width and height. + * 72 points/in means letter paper is 612x792. + * @param pageSize Page size in points. + * @param contentSize The content size of the page in points. This will be + * combined with the initial transform to determine the drawing area + * (as reported by the width and height methods). Anything outside + * of the drawing area will be clipped. + * @param initialTransform The initial transform to apply to the page. + * This may be useful to, for example, move the origin in and + * over a bit to account for a margin, scale the canvas, + * or apply a rotation. Note1: the SkPDFDevice also applies + * a scale+translate transform to move the origin from the + * bottom left (PDF default) to the top left. Note2: drawDevice + * (used by layer restore) draws the device after this initial + * transform is applied, so the PDF device factory does an + * inverse scale+translate to accommodate the one that SkPDFDevice + * always does. + */ + // TODO(vandebo) The sizes should be SkSize and not SkISize. + SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize, + const SkMatrix& initialTransform); + SK_API virtual ~SkPDFDevice(); + + virtual uint32_t getDeviceCapabilities() { return kVector_Capability; } + + virtual void clear(SkColor color); + + virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { + return false; + } + + /** These are called inside the per-device-layer loop for each draw call. + When these are called, we have already applied any saveLayer operations, + and are handling any looping from the paint, and any effects from the + DrawFilter. + */ + virtual void drawPaint(const SkDraw&, const SkPaint& paint); + virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, + size_t count, const SkPoint[], + const SkPaint& paint); + virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint); + virtual void drawPath(const SkDraw&, const SkPath& origpath, + const SkPaint& paint, const SkMatrix* prePathMatrix, + bool pathIsMutable); + virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, + const SkIRect* srcRectOrNull, + const SkMatrix& matrix, const SkPaint& paint); + virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y, + const SkPaint& paint); + virtual void drawText(const SkDraw&, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint); + virtual void drawPosText(const SkDraw&, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint); + virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, + int vertexCount, const SkPoint verts[], + const SkPoint texs[], const SkColor colors[], + SkXfermode* xmode, const uint16_t indices[], + int indexCount, const SkPaint& paint); + virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, + const SkPaint&); + + // PDF specific methods. + + /** Returns a reference to the resource dictionary for this device. + */ + SK_API const SkRefPtr<SkPDFDict>& getResourceDict(); + + /** Get the list of resources (PDF objects) used on this page. + * @param resourceList A list to append the resources to. + */ + SK_API void getResources(SkTDArray<SkPDFObject*>* resourceList) const; + + /** Get the fonts used on this device. + */ + SK_API const SkTDArray<SkPDFFont*>& getFontResources() const; + + /** Returns the media box for this device. + */ + SK_API SkRefPtr<SkPDFArray> getMediaBox() const; + + /** Returns a SkStream with the page contents. The caller is responsible + for a reference to the returned value. + */ + SK_API SkStream* content() const; + + SK_API const SkMatrix& initialTransform() const { + return fInitialTransform; + } + +protected: + // override + virtual SkDeviceFactory* onNewDeviceFactory(); + +private: + friend class SkPDFDeviceFactory; + // TODO(vandebo) push most of SkPDFDevice's state into a core object in + // order to get the right access levels without using friend. + friend class ScopedContentEntry; + + SkISize fPageSize; + SkISize fContentSize; + SkMatrix fInitialTransform; + SkClipStack fExistingClipStack; + SkRegion fExistingClipRegion; + SkRefPtr<SkPDFDict> fResourceDict; + + SkTDArray<SkPDFGraphicState*> fGraphicStateResources; + SkTDArray<SkPDFObject*> fXObjectResources; + SkTDArray<SkPDFFont*> fFontResources; + SkTDArray<SkPDFShader*> fShaderResources; + + SkTScopedPtr<ContentEntry> fContentEntries; + ContentEntry* fLastContentEntry; + + // For use by the DeviceFactory. + SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack, + const SkRegion& existingClipRegion); + + void init(); + void cleanUp(); + void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject); + + // Clear the passed clip from all existing content entries. + void clearClipFromContent(const SkClipStack* clipStack, + const SkRegion& clipRegion); + void drawFormXObjectWithClip(SkPDFFormXObject* form, + const SkClipStack* clipStack, + const SkRegion& clipRegion, + bool invertClip); + + // If the paint or clip is such that we shouldn't draw anything, this + // returns NULL and does not create a content entry. + // setUpContentEntry and finishContentEntry can be used directly, but + // the preferred method is to use the ScopedContentEntry helper class. + ContentEntry* setUpContentEntry(const SkClipStack* clipStack, + const SkRegion& clipRegion, + const SkMatrix& matrix, + const SkPaint& paint, + bool hasText, + SkRefPtr<SkPDFFormXObject>* dst); + void finishContentEntry(SkXfermode::Mode xfermode, + SkPDFFormXObject* dst); + bool isContentEmpty(); + + void populateGraphicStateEntryFromPaint(const SkMatrix& matrix, + const SkClipStack& clipStack, + const SkRegion& clipRegion, + const SkPaint& paint, + bool hasText, + GraphicStateEntry* entry); + int addGraphicStateResource(SkPDFGraphicState* gs); + + void updateFont(const SkPaint& paint, uint16_t glyphID, + ContentEntry* contentEntry); + int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID); + + void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry); + void internalDrawBitmap(const SkMatrix& matrix, + const SkClipStack* clipStack, + const SkRegion& clipRegion, + const SkBitmap& bitmap, + const SkIRect* srcRect, + const SkPaint& paint); + + // Disable the default copy and assign implementation. + SkPDFDevice(const SkPDFDevice&); + void operator=(const SkPDFDevice&); +}; + +#endif diff --git a/include/pdf/SkPDFDocument.h b/include/pdf/SkPDFDocument.h new file mode 100644 index 0000000000..0a76ea26aa --- /dev/null +++ b/include/pdf/SkPDFDocument.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFDocument_DEFINED +#define SkPDFDocument_DEFINED + +#include "SkPDFCatalog.h" +#include "SkPDFTypes.h" +#include "SkRefCnt.h" +#include "SkTDArray.h" + +class SkPDFDevice; +class SkPDFPage; +class SkWSteam; + +/** \class SkPDFDocument + + A SkPDFDocument assembles pages together and generates the final PDF file. +*/ +class SkPDFDocument { +public: + /** Create a PDF document. + */ + SK_API SkPDFDocument(); + SK_API ~SkPDFDocument(); + + /** Output the PDF to the passed stream. + * @param stream The writable output stream to send the PDF to. + */ + SK_API bool emitPDF(SkWStream* stream); + + /** Append the passed pdf device to the document as a new page. Returns + * true if successful. Will fail if the document has already been emitted. + * + * @param pdfDevice The page to add to this document. + */ + SK_API bool appendPage(const SkRefPtr<SkPDFDevice>& pdfDevice); + + /** Get the list of pages in this document. + */ + SK_API const SkTDArray<SkPDFPage*>& getPages(); + +private: + SkPDFCatalog fCatalog; + int64_t fXRefFileOffset; + + SkTDArray<SkPDFPage*> fPages; + SkTDArray<SkPDFDict*> fPageTree; + SkRefPtr<SkPDFDict> fDocCatalog; + SkTDArray<SkPDFObject*> fPageResources; + int fSecondPageFirstResourceIndex; + + SkRefPtr<SkPDFDict> fTrailerDict; + + /** Output the PDF header to the passed stream. + * @param stream The writable output stream to send the header to. + */ + void emitHeader(SkWStream* stream); + + /** Get the size of the header. + */ + size_t headerSize(); + + /** Output the PDF footer to the passed stream. + * @param stream The writable output stream to send the footer to. + * @param objCount The number of objects in the PDF. + */ + void emitFooter(SkWStream* stream, int64_t objCount); +}; + +#endif diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h new file mode 100644 index 0000000000..93a72f173b --- /dev/null +++ b/include/pdf/SkPDFFont.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFFont_DEFINED +#define SkPDFFont_DEFINED + +#include "SkAdvancedTypefaceMetrics.h" +#include "SkPDFTypes.h" +#include "SkTDArray.h" +#include "SkThread.h" + +class SkPaint; + +/** \class SkPDFFont + A PDF Object class representing a font. The font may have resources + attached to it in order to embed the font. SkPDFFonts are canonicalized + so that resource deduplication will only include one copy of a font. + This class uses the same pattern as SkPDFGraphicState, a static weak + reference to each instantiated class. +*/ +class SkPDFFont : public SkPDFDict { +public: + SK_API virtual ~SkPDFFont(); + + SK_API virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + + /** Returns the typeface represented by this class. Returns NULL for the + * default typeface. + */ + SK_API SkTypeface* typeface(); + + /** Returns the font type represented in this font. For Type0 fonts, + * returns the type of the decendant font. + */ + SK_API SkAdvancedTypefaceMetrics::FontType getType(); + + /** Return true if this font has an encoding for the passed glyph id. + */ + SK_API bool hasGlyph(uint16_t glyphID); + + /** Returns true if this font encoding supports glyph IDs above 255. + */ + SK_API bool multiByteGlyphs(); + + /** Convert (in place) the input glyph IDs into the font encoding. If the + * font has more glyphs than can be encoded (like a type 1 font with more + * than 255 glyphs) this method only converts up to the first out of range + * glyph ID. + * @param glyphIDs The input text as glyph IDs. + * @param numGlyphs The number of input glyphs. + * @return Returns the number of glyphs consumed. + */ + SK_API size_t glyphsToPDFFontEncoding(uint16_t* glyphIDs, size_t numGlyphs); + + /** Get the font resource for the passed typeface and glyphID. The + * reference count of the object is incremented and it is the caller's + * responsibility to unreference it when done. This is needed to + * accommodate the weak reference pattern used when the returned object + * is new and has no other references. + * @param typeface The typeface to find. + * @param glyphID Specify which section of a large font is of interest. + */ + SK_API static SkPDFFont* getFontResource(SkTypeface* typeface, + uint16_t glyphID); + +private: + SkRefPtr<SkTypeface> fTypeface; + SkAdvancedTypefaceMetrics::FontType fType; +#ifdef SK_DEBUG + bool fDescendant; +#endif + bool fMultiByteGlyphs; + + // The glyph IDs accessible with this font. For Type1 (non CID) fonts, + // this will be a subset if the font has more than 255 glyphs. + uint16_t fFirstGlyphID; + uint16_t fLastGlyphID; + // The font info is only kept around after construction for large + // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs. + SkRefPtr<SkAdvancedTypefaceMetrics> fFontInfo; + SkTDArray<SkPDFObject*> fResources; + SkRefPtr<SkPDFDict> fDescriptor; + + class FontRec { + public: + SkPDFFont* fFont; + uint32_t fFontID; + uint16_t fGlyphID; + + // A fGlyphID of 0 with no fFont always matches. + bool operator==(const FontRec& b) const; + FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID); + }; + + // This should be made a hash table if performance is a problem. + static SkTDArray<FontRec>& canonicalFonts(); + static SkMutex& canonicalFontsMutex(); + + /** Construct a new font dictionary and support objects. + * @param fontInfo Information about the to create. + * @param typeface The typeface for the font. + * @param glyphID The glyph ID the caller is interested in. This + * is important only for Type1 fonts, which have + * more than 255 glyphs. + * @param descendantFont If this is the descendant (true) or root + * (Type 0 font - false) font dictionary. Only True + * Type and CID encoded fonts will use a true value. + * @param fontDescriptor If the font descriptor has already have generated + * for this font, pass it in here, otherwise pass + * NULL. + */ + SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface, + uint16_t glyphID, bool descendantFont, SkPDFDict* fontDescriptor); + + void populateType0Font(); + void populateCIDFont(); + bool populateType1Font(int16_t glyphID); + + /** Populate the PDF font dictionary as Type3 font which includes glyph + * descriptions with instructions for painting the glyphs. This function + * doesn't use any fields from SkAdvancedTypefaceMetrics (fFontInfo). Font + * information including glyph paths are queried from the platform + * dependent SkGlyphCache. + */ + void populateType3Font(int16_t glyphID); + bool addFontDescriptor(int16_t defaultWidth); + void populateToUnicodeTable(); + void addWidthInfoFromRange(int16_t defaultWidth, + const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry); + /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs, + * including the passed glyphID. + */ + void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID); + + static bool find(uint32_t fontID, uint16_t glyphID, int* index); +}; + +#endif diff --git a/include/pdf/SkPDFFormXObject.h b/include/pdf/SkPDFFormXObject.h new file mode 100644 index 0000000000..41719f0820 --- /dev/null +++ b/include/pdf/SkPDFFormXObject.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFFormXObject_DEFINED +#define SkPDFFormXObject_DEFINED + +#include "SkPDFStream.h" +#include "SkPDFTypes.h" +#include "SkRefCnt.h" +#include "SkString.h" + +class SkMatrix; +class SkPDFDevice; +class SkPDFCatalog; + +/** \class SkPDFFormXObject + + A form XObject; a self contained description of graphics objects. A form + XObject is basically a page object with slightly different syntax, that + can be drawn onto a page. +*/ + +// The caller could keep track of the form XObjects it creates and +// canonicalize them, but the Skia API doesn't provide enough context to +// automatically do it (trivially). +class SkPDFFormXObject : public SkPDFObject { +public: + /** Create a PDF form XObject. Entries for the dictionary entries are + * automatically added. + * @param device The set of graphical elements on this form. + */ + explicit SkPDFFormXObject(SkPDFDevice* device); + virtual ~SkPDFFormXObject(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + + /** Add the value to the stream dictionary with the given key. Refs value. + * @param key The key for this dictionary entry. + * @param value The value for this dictionary entry. + * @return The value argument is returned. + */ + SkPDFObject* insert(SkPDFName* key, SkPDFObject* value); + + /** Add the value to the stream dictionary with the given key. Refs value. + * @param key The text of the key for this dictionary entry. + * @param value The value for this dictionary entry. + * @return The value argument is returned. + */ + SkPDFObject* insert(const char key[], SkPDFObject* value); + +private: + SkRefPtr<SkPDFStream> fStream; + SkTDArray<SkPDFObject*> fResources; +}; + +#endif diff --git a/include/pdf/SkPDFGraphicState.h b/include/pdf/SkPDFGraphicState.h new file mode 100644 index 0000000000..49809a4c03 --- /dev/null +++ b/include/pdf/SkPDFGraphicState.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFGraphicState_DEFINED +#define SkPDFGraphicState_DEFINED + +#include "SkPaint.h" +#include "SkPDFTypes.h" +#include "SkTemplates.h" +#include "SkThread.h" + +class SkPDFFormXObject; + +/** \class SkPDFGraphicState + SkPaint objects roughly correspond to graphic state dictionaries that can + be installed. So that a given dictionary is only output to the pdf file + once, we want to canonicalize them. Static methods in this class manage + a weakly referenced set of SkPDFGraphicState objects: when the last + reference to a SkPDFGraphicState is removed, it removes itself from the + static set of objects. + +*/ +class SkPDFGraphicState : public SkPDFDict { +public: + virtual ~SkPDFGraphicState(); + + virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + + // Override emitObject and getOutputSize so that we can populate + // the dictionary on demand. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + + /** Get the graphic state for the passed SkPaint. The reference count of + * the object is incremented and it is the caller's responsibility to + * unreference it when done. This is needed to accommodate the weak + * reference pattern used when the returned object is new and has no + * other references. + * @param paint The SkPaint to emulate. + */ + static SkPDFGraphicState* getGraphicStateForPaint(const SkPaint& paint); + + /** Make a graphic state that only sets the passed soft mask. The + * reference count of the object is incremented and it is the caller's + * responsibility to unreference it when done. + * @param sMask The form xobject to use as a soft mask. + * @param invert Indicates if the alpha of the sMask should be inverted. + */ + static SkPDFGraphicState* getSMaskGraphicState(SkPDFFormXObject* sMask, + bool invert); + + /** Get a graphic state that only unsets the soft mask. The reference + * count of the object is incremented and it is the caller's responsibility + * to unreference it when done. This is needed to accommodate the weak + * reference pattern used when the returned object is new and has no + * other references. + */ + static SkPDFGraphicState* getNoSMaskGraphicState(); + +private: + const SkPaint fPaint; + SkTDArray<SkPDFObject*> fResources; + bool fPopulated; + bool fSMask; + + class GSCanonicalEntry { + public: + SkPDFGraphicState* fGraphicState; + const SkPaint* fPaint; + + bool operator==(const GSCanonicalEntry& b) const; + explicit GSCanonicalEntry(SkPDFGraphicState* gs) + : fGraphicState(gs), + fPaint(&gs->fPaint) {} + explicit GSCanonicalEntry(const SkPaint* paint) : fPaint(paint) {} + }; + + // This should be made a hash table if performance is a problem. + static SkTDArray<GSCanonicalEntry>& canonicalPaints(); + static SkMutex& canonicalPaintsMutex(); + + SkPDFGraphicState(); + explicit SkPDFGraphicState(const SkPaint& paint); + + void populateDict(); + + static SkPDFObject* GetInvertFunction(); + + static int find(const SkPaint& paint); +}; + +#endif diff --git a/include/pdf/SkPDFImage.h b/include/pdf/SkPDFImage.h new file mode 100644 index 0000000000..945ff9e541 --- /dev/null +++ b/include/pdf/SkPDFImage.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFImage_DEFINED +#define SkPDFImage_DEFINED + +#include "SkPDFStream.h" +#include "SkPDFTypes.h" +#include "SkRefCnt.h" + +class SkBitmap; +class SkPaint; +class SkPDFCatalog; +struct SkIRect; + +/** \class SkPDFImage + + An image XObject. +*/ + +// We could play the same trick here as is done in SkPDFGraphicState, storing +// a copy of the Bitmap object (not the pixels), the pixel generation number, +// and settings used from the paint to canonicalize image objects. +class SkPDFImage : public SkPDFObject { +public: + /** Create a new Image XObject to represent the passed bitmap. + * @param bitmap The image to encode. + * @param srcRect The rectangle to cut out of bitmap. + * @param paint Used to calculate alpha, masks, etc. + * @return The image XObject or NUll if there is nothing to draw for + * the given parameters. + */ + static SkPDFImage* CreateImage(const SkBitmap& bitmap, + const SkIRect& srcRect, + const SkPaint& paint); + + virtual ~SkPDFImage(); + + /** Add a Soft Mask (alpha or shape channel) to the image. Refs mask. + * @param mask A gray scale image representing the mask. + * @return The mask argument is returned. + */ + SkPDFImage* addSMask(SkPDFImage* mask); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + +private: + SkRefPtr<SkPDFStream> fStream; + SkTDArray<SkPDFObject*> fResources; + + /** Create a PDF image XObject. Entries for the image properties are + * automatically added to the stream dictionary. + * @param imageData The final raw bits representing the image. + * @param bitmap The image parameters to use (Config, etc). + * @param srcRect The clipping applied to bitmap before generating + * imageData. + * @param alpha Is this the alpha channel of the bitmap. + * @param paint Used to calculate alpha, masks, etc. + */ + SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, + const SkIRect& srcRect, bool alpha, const SkPaint& paint); + + /** Add the value to the stream dictionary with the given key. Refs value. + * @param key The key for this dictionary entry. + * @param value The value for this dictionary entry. + * @return The value argument is returned. + */ + SkPDFObject* insert(SkPDFName* key, SkPDFObject* value); + + /** Add the value to the stream dictionary with the given key. Refs value. + * @param key The text of the key for this dictionary entry. + * @param value The value for this dictionary entry. + * @return The value argument is returned. + */ + SkPDFObject* insert(const char key[], SkPDFObject* value); +}; + +#endif diff --git a/include/pdf/SkPDFPage.h b/include/pdf/SkPDFPage.h new file mode 100644 index 0000000000..0e30028ae7 --- /dev/null +++ b/include/pdf/SkPDFPage.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFPage_DEFINED +#define SkPDFPage_DEFINED + +#include "SkPDFTypes.h" +#include "SkPDFStream.h" +#include "SkRefCnt.h" +#include "SkTDArray.h" + +class SkPDFCatalog; +class SkPDFDevice; +class SkWStream; + +/** \class SkPDFPage + + A SkPDFPage contains meta information about a page, is used in the page + tree and points to the content of the page. +*/ +class SkPDFPage : public SkPDFDict { +public: + /** Create a PDF page with the passed PDF device. The device need not + * have content on it yet. + * @param content The page content. + */ + explicit SkPDFPage(const SkRefPtr<SkPDFDevice>& content); + ~SkPDFPage(); + + /** Before a page and its contents can be sized and emitted, it must + * be finalized. No changes to the PDFDevice will be honored after + * finalizePage has been called. This function adds the page content + * to the passed catalog, so it must be called for each document + * that the page is part of. + * @param catalog The catalog to add page content objects to. + * @param firstPage Indicate if this is the first page of a document. + * @param resourceObjects The resource objects used on the page are added + * to this array. This gives the caller a chance + * to deduplicate resources across pages. + */ + void finalizePage(SkPDFCatalog* catalog, bool firstPage, + SkTDArray<SkPDFObject*>* resourceObjects); + + /** Determine the size of the page content and store to the catalog + * the offsets of all nonresource-indirect objects that make up the page + * content. This must be called before emitPage(), but after finalizePage. + * @param catalog The catalog to add the object offsets to. + * @param fileOffset The file offset where the page content will be + * emitted. + */ + off_t getPageSize(SkPDFCatalog* catalog, off_t fileOffset); + + /** Output the page content to the passed stream. + * @param stream The writable output stream to send the content to. + * @param catalog The active object catalog. + */ + void emitPage(SkWStream* stream, SkPDFCatalog* catalog); + + /** Generate a page tree for the passed vector of pages. New objects are + * added to the catalog. The pageTree vector is populated with all of + * the 'Pages' dictionaries as well as the 'Page' objects. Page trees + * have both parent and children links, creating reference cycles, so + * it must be torn down explicitly. The first page is not added to + * the pageTree dictionary array so the caller can handle it specially. + * @param pages The ordered vector of page objects. + * @param catalog The catalog to add new objects into. + * @param pageTree An output vector with all of the internal and leaf + * nodes of the pageTree. + * @param rootNode An output parameter set to the root node. + */ + static void generatePageTree(const SkTDArray<SkPDFPage*>& pages, + SkPDFCatalog* catalog, + SkTDArray<SkPDFDict*>* pageTree, + SkPDFDict** rootNode); + + /** Get the fonts used on this page. + */ + SK_API const SkTDArray<SkPDFFont*>& getFontResources() const; + +private: + // Multiple pages may reference the content. + SkRefPtr<SkPDFDevice> fDevice; + + // Once the content is finalized, put it into a stream for output. + SkRefPtr<SkPDFStream> fContentStream; +}; + +#endif diff --git a/include/pdf/SkPDFShader.h b/include/pdf/SkPDFShader.h new file mode 100644 index 0000000000..17f7f03316 --- /dev/null +++ b/include/pdf/SkPDFShader.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFShader_DEFINED +#define SkPDFShader_DEFINED + +#include "SkPDFStream.h" +#include "SkPDFTypes.h" +#include "SkMatrix.h" +#include "SkRefCnt.h" +#include "SkShader.h" + +class SkObjRef; +class SkPDFCatalog; + +/** \class SkPDFShader + + In PDF parlance, this is a pattern, used in place of a color when the + pattern color space is selected. +*/ + +class SkPDFShader : public SkPDFObject { +public: + virtual ~SkPDFShader(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + + /** Get the PDF shader for the passed SkShader. If the SkShader is + * invalid in some way, returns NULL. The reference count of + * the object is incremented and it is the caller's responsibility to + * unreference it when done. This is needed to accommodate the weak + * reference pattern used when the returned object is new and has no + * other references. + * @param shader The SkShader to emulate. + * @param matrix The current transform. (PDF shaders are absolutely + * positioned, relative to where the page is drawn.) + * @param surfceBBox The bounding box of the drawing surface (with matrix + * already applied). + */ + static SkPDFShader* getPDFShader(const SkShader& shader, + const SkMatrix& matrix, + const SkIRect& surfaceBBox); + +private: + class State { + public: + SkShader::GradientType fType; + SkShader::GradientInfo fInfo; + SkAutoFree fColorData; + SkMatrix fCanvasTransform; + SkMatrix fShaderTransform; + SkIRect fBBox; + + SkBitmap fImage; + uint32_t fPixelGeneration; + SkShader::TileMode fImageTileModes[2]; + + explicit State(const SkShader& shader, const SkMatrix& canvasTransform, + const SkIRect& bbox); + bool operator==(const State& b) const; + }; + + SkRefPtr<SkPDFDict> fContent; + SkTDArray<SkPDFObject*> fResources; + SkAutoTDelete<const State> fState; + + class ShaderCanonicalEntry { + public: + SkPDFShader* fPDFShader; + const State* fState; + + bool operator==(const ShaderCanonicalEntry& b) const { + return fPDFShader == b.fPDFShader || *fState == *b.fState; + } + ShaderCanonicalEntry(SkPDFShader* pdfShader, const State* state) + : fPDFShader(pdfShader), + fState(state) { + } + }; + // This should be made a hash table if performance is a problem. + static SkTDArray<ShaderCanonicalEntry>& canonicalShaders(); + static SkMutex& canonicalShadersMutex(); + + static SkPDFObject* rangeObject(); + + SkPDFShader(State* state); + + void doFunctionShader(); + void doImageShader(); + SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); +}; + +#endif diff --git a/include/pdf/SkPDFStream.h b/include/pdf/SkPDFStream.h new file mode 100644 index 0000000000..a975ad6efc --- /dev/null +++ b/include/pdf/SkPDFStream.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFStream_DEFINED +#define SkPDFStream_DEFINED + +#include "SkPDFTypes.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTemplates.h" + +class SkPDFCatalog; + +/** \class SkPDFStream + + A stream object in a PDF. Note, all streams must be indirect objects (via + SkObjRef). +*/ +class SkPDFStream : public SkPDFDict { +public: + /** Create a PDF stream. A Length entry is automatically added to the + * stream dictionary. + * @param stream The data part of the stream. + */ + explicit SkPDFStream(SkStream* stream); + virtual ~SkPDFStream(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + +private: + size_t fLength; + // Only one of the two streams will be valid. + SkRefPtr<SkStream> fPlainData; + SkDynamicMemoryWStream fCompressedData; + + typedef SkPDFDict INHERITED; +}; + +#endif diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h new file mode 100644 index 0000000000..6b5146a2b7 --- /dev/null +++ b/include/pdf/SkPDFTypes.h @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFTypes_DEFINED +#define SkPDFTypes_DEFINED + +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkString.h" +#include "SkTDArray.h" +#include "SkTypes.h" + +class SkPDFCatalog; +class SkWStream; + +/** \class SkPDFObject + + A PDF Object is the base class for primitive elements in a PDF file. A + common subtype is used to ease the use of indirect object references, + which are common in the PDF format. +*/ +class SkPDFObject : public SkRefCnt { +public: + /** Create a PDF object. + */ + SkPDFObject(); + virtual ~SkPDFObject(); + + /** Subclasses must implement this method to print the object to the + * PDF file. + * @param catalog The object catalog to use. + * @param indirect If true, output an object identifier with the object. + * @param stream The writable output stream to send the output to. + */ + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) = 0; + + /** Return the size (number of bytes) of this object in the final output + * file. Compound objects or objects that are computationally intensive + * to output should override this method. + * @param catalog The object catalog to use. + * @param indirect If true, output an object identifier with the object. + */ + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + + /** If this object explicitly depends on other objects, add them to the + * end of the list. This only applies to higher level object, where + * the depenency is explicit and introduced by the class. i.e. an + * SkPDFImage added to an SkPDFDevice, but not an SkPDFObjRef added to + * an SkPDFArray. + * @param resourceList The list to append dependant resources to. + */ + virtual void getResources(SkTDArray<SkPDFObject*>* resourceList); + + /** Helper function to output an indirect object. + * @param catalog The object catalog to use. + * @param stream The writable output stream to send the output to. + */ + void emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog); + + /** Helper function to find the size of an indirect object. + * @param catalog The object catalog to use. + */ + size_t getIndirectOutputSize(SkPDFCatalog* catalog); +}; + +/** \class SkPDFObjRef + + An indirect reference to a PDF object. +*/ +class SkPDFObjRef : public SkPDFObject { +public: + /** Create a reference to an existing SkPDFObject. + * @param obj The object to reference. + */ + explicit SkPDFObjRef(SkPDFObject* obj); + virtual ~SkPDFObjRef(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + +private: + SkRefPtr<SkPDFObject> fObj; +}; + +/** \class SkPDFInt + + An integer object in a PDF. +*/ +class SkPDFInt : public SkPDFObject { +public: + /** Create a PDF integer (usually for indirect reference purposes). + * @param value An integer value between 2^31 - 1 and -2^31. + */ + explicit SkPDFInt(int32_t value); + virtual ~SkPDFInt(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + +private: + int32_t fValue; +}; + +/** \class SkPDFBool + + An boolean value in a PDF. +*/ +class SkPDFBool : public SkPDFObject { +public: + /** Create a PDF boolean. + * @param value true or false. + */ + explicit SkPDFBool(bool value); + virtual ~SkPDFBool(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + +private: + bool fValue; +}; + +/** \class SkPDFScalar + + A real number object in a PDF. +*/ +class SkPDFScalar : public SkPDFObject { +public: + /** Create a PDF real number. + * @param value A real value. + */ + explicit SkPDFScalar(SkScalar value); + virtual ~SkPDFScalar(); + + static void Append(SkScalar value, SkWStream* stream); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + +private: + SkScalar fValue; +}; + +/** \class SkPDFString + + A string object in a PDF. +*/ +class SkPDFString : public SkPDFObject { +public: + /** Create a PDF string. Maximum length (in bytes) is 65,535. + * @param value A string value. + */ + explicit SkPDFString(const char value[]); + explicit SkPDFString(const SkString& value); + + /** Create a PDF string. Maximum length (in bytes) is 65,535. + * @param value A string value. + * @param len The length of value. + * @param wideChars Indicates if the top byte in value is significant and + * should be encoded (true) or not (false). + */ + SkPDFString(const uint16_t* value, size_t len, bool wideChars); + virtual ~SkPDFString(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + + static SkString formatString(const char* input, size_t len); + static SkString formatString(const uint16_t* input, size_t len, + bool wideChars); +private: + static const size_t kMaxLen = 65535; + + const SkString fValue; + + static SkString doFormatString(const void* input, size_t len, + bool wideInput, bool wideOutput); +}; + +/** \class SkPDFName + + A name object in a PDF. +*/ +class SkPDFName : public SkPDFObject { +public: + /** Create a PDF name object. Maximum length is 127 bytes. + * @param value The name. + */ + explicit SkPDFName(const char name[]); + explicit SkPDFName(const SkString& name); + virtual ~SkPDFName(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + +private: + static const size_t kMaxLen = 127; + + const SkString fValue; + + static SkString formatName(const SkString& input); +}; + +/** \class SkPDFArray + + An array object in a PDF. +*/ +class SkPDFArray : public SkPDFObject { +public: + /** Create a PDF array. Maximum length is 8191. + */ + SkPDFArray(); + virtual ~SkPDFArray(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + + /** The size of the array. + */ + int size() { return fValue.count(); } + + /** Preallocate space for the given number of entries. + * @param length The number of array slots to preallocate. + */ + void reserve(int length); + + /** Returns the object at the given offset in the array. + * @param index The index into the array to retrieve. + */ + SkPDFObject* getAt(int index) { return fValue[index]; } + + /** Set the object at the given offset in the array. Ref's value. + * @param index The index into the array to set. + * @param value The value to add to the array. + * @return The value argument is returned. + */ + SkPDFObject* setAt(int index, SkPDFObject* value); + + /** Append the object to the end of the array and increments its ref count. + * @param value The value to add to the array. + * @return The value argument is returned. + */ + SkPDFObject* append(SkPDFObject* value); + +private: + static const int kMaxLen = 8191; + SkTDArray<SkPDFObject*> fValue; +}; + +/** \class SkPDFDict + + A dictionary object in a PDF. +*/ +class SkPDFDict : public SkPDFObject { +public: + /** Create a PDF dictionary. Maximum number of entries is 4095. + */ + SkPDFDict(); + + /** Create a PDF dictionary with a Type entry. + * @param type The value of the Type entry. + */ + explicit SkPDFDict(const char type[]); + + virtual ~SkPDFDict(); + + // The SkPDFObject interface. + virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect); + virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); + + /** The size of the dictionary. + */ + int size() { return fValue.count(); } + + /** Add the value to the dictionary with the given key. Refs value. + * @param key The key for this dictionary entry. + * @param value The value for this dictionary entry. + * @return The value argument is returned. + */ + SkPDFObject* insert(SkPDFName* key, SkPDFObject* value); + + /** Add the value to the dictionary with the given key. Refs value. The + * method will create the SkPDFName object. + * @param key The text of the key for this dictionary entry. + * @param value The value for this dictionary entry. + * @return The value argument is returned. + */ + SkPDFObject* insert(const char key[], SkPDFObject* value); + + /** Remove all entries from the dictionary. + */ + void clear(); + +private: + static const int kMaxLen = 4095; + + struct Rec { + SkPDFName* key; + SkPDFObject* value; + }; + + SkTDArray<struct Rec> fValue; +}; + +#endif diff --git a/include/pdf/SkPDFUtils.h b/include/pdf/SkPDFUtils.h new file mode 100644 index 0000000000..50da28cff0 --- /dev/null +++ b/include/pdf/SkPDFUtils.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkPDFUtils_DEFINED +#define SkPDFUtils_DEFINED + +#include "SkPath.h" + +class SkMatrix; +class SkPath; +class SkPDFArray; +struct SkRect; + +#if 0 +#define PRINT_NOT_IMPL(str) fprintf(stderr, str) +#else +#define PRINT_NOT_IMPL(str) +#endif + +#define NOT_IMPLEMENTED(condition, assert) \ + do { \ + if (condition) { \ + PRINT_NOT_IMPL("NOT_IMPLEMENTED: " #condition "\n"); \ + SkDEBUGCODE(SkASSERT(!assert);) \ + } \ + } while(0) + +class SkPDFUtils { +public: + static SkPDFArray* MatrixToArray(const SkMatrix& matrix); + static void AppendTransform(const SkMatrix& matrix, SkWStream* content); + + static void MoveTo(SkScalar x, SkScalar y, SkWStream* content); + static void AppendLine(SkScalar x, SkScalar y, SkWStream* content); + static void AppendCubic(SkScalar ctl1X, SkScalar ctl1Y, + SkScalar ctl2X, SkScalar ctl2Y, + SkScalar dstX, SkScalar dstY, SkWStream* content); + static void AppendRectangle(const SkRect& rect, SkWStream* content); + static void EmitPath(const SkPath& path, SkWStream* content); + static void ClosePath(SkWStream* content); + static void PaintPath(SkPaint::Style style, SkPath::FillType fill, + SkWStream* content); + static void StrokePath(SkWStream* content); + static void DrawFormXObject(int objectIndex, SkWStream* content); + static void ApplyGraphicState(int objectIndex, SkWStream* content); +}; + +#endif diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h new file mode 100644 index 0000000000..897766f08e --- /dev/null +++ b/include/pipe/SkGPipe.h @@ -0,0 +1,96 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#ifndef SkGPipe_DEFINED +#define SkGPipe_DEFINED + +#include "SkWriter32.h" +#include "SkFlattenable.h" + +class SkCanvas; + +class SkGPipeReader { +public: + SkGPipeReader(SkCanvas* target); + ~SkGPipeReader(); + + enum Status { + kDone_Status, //!< no more data expected from reader + kEOF_Status, //!< need more data from reader + kError_Status //!< encountered error + }; + + // data must be 4-byte aligned + // length must be a multiple of 4 + Status playback(const void* data, size_t length, size_t* bytesRead = NULL); + +private: + SkCanvas* fCanvas; + class SkGPipeState* fState; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SkGPipeController { +public: + /** + * Called periodically by the writer, to get a working buffer of RAM to + * write into. The actual size of the block is also returned, and must be + * actual >= minRequest. If NULL is returned, then actual is ignored and + * writing will stop. + * + * The returned block must be 4-byte aligned, and actual must be a + * multiple of 4. + * minRequest will always be a multiple of 4. + */ + virtual void* requestBlock(size_t minRequest, size_t* actual) = 0; + + /** + * This is called each time some atomic portion of the data has been + * written to the block (most recently returned by requestBlock()). + * If bytes == 0, then the writer has finished. + * + * bytes will always be a multiple of 4. + */ + virtual void notifyWritten(size_t bytes) = 0; +}; + +class SkGPipeWriter { +public: + SkGPipeWriter(); + ~SkGPipeWriter(); + + bool isRecording() const { return NULL != fCanvas; } + + enum Flags { + kCrossProcess_Flag = 1 << 0, + }; + + SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0); + + // called in destructor, but can be called sooner once you know there + // should be no more drawing calls made into the recording canvas. + void endRecording(); + +private: + class SkGPipeCanvas* fCanvas; + SkGPipeController* fController; + SkFactorySet fFactorySet; + SkWriter32 fWriter; +}; + +#endif diff --git a/include/utils/SkEGLContext.h b/include/utils/SkEGLContext.h index 4b17be1d4a..ced31a5b85 100644 --- a/include/utils/SkEGLContext.h +++ b/include/utils/SkEGLContext.h @@ -1,20 +1,53 @@ #ifndef SkEGLContext_DEFINED #define SkEGLContext_DEFINED -#include "SkTypes.h" +#if defined(SK_MESA) + #include "GL/osmesa.h" +#elif defined(SK_BUILD_FOR_MAC) + #include <AGL/agl.h> +#elif defined(SK_BUILD_FOR_ANDROID) + #include "GLES2/gl2.h" + #include "EGL/egl.h" +#elif defined(SK_BUILD_FOR_UNIX) + #include <X11/Xlib.h> + #include <GL/glx.h> +#elif defined(SK_BUILD_FOR_WIN32) + #include <Windows.h> + #include <GL/GL.h> +#else + +#endif /** * Create an offscreen opengl context */ class SkEGLContext { public: - SkEGLContext(); - ~SkEGLContext(); + SkEGLContext(); + ~SkEGLContext(); - bool init(int width, int height); + bool init(const int width, const int height); private: - void* fContext; +#if defined(SK_MESA) + OSMesaContext context; + GLfloat *image; +#elif defined(SK_BUILD_FOR_MAC) + AGLContext context; +#elif defined(SK_BUILD_FOR_ANDROID) + +#elif defined(SK_BUILD_FOR_UNIX) + GLXContext context; + Display *display; + Pixmap pixmap; + GLXPixmap glxPixmap; +#elif defined(SK_BUILD_FOR_WIN32) + HWND fWindow; + HDC fDeviceContext; + HGLRC fGlRenderContext; +#else + +#endif }; #endif diff --git a/include/utils/android/AndroidKeyToSkKey.h b/include/utils/android/AndroidKeyToSkKey.h new file mode 100644 index 0000000000..7b0a03570e --- /dev/null +++ b/include/utils/android/AndroidKeyToSkKey.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_TO_SKIA_KEYCODES_H +#define _ANDROID_TO_SKIA_KEYCODES_H + +#include "keycodes.h" +#include "SkKey.h" + +// Convert an Android keycode to an SkKey. This is an incomplete list, only +// including keys used by the sample app. +SkKey AndroidKeycodeToSkKey(int keycode) { + switch (keycode) { + case AKEYCODE_DPAD_LEFT: + return kLeft_SkKey; + case AKEYCODE_DPAD_RIGHT: + return kRight_SkKey; + case AKEYCODE_DPAD_UP: + return kUp_SkKey; + case AKEYCODE_DPAD_DOWN: + return kDown_SkKey; + default: + return kNONE_SkKey; + } +} + +#endif diff --git a/include/utils/unix/XkeysToSkKeys.h b/include/utils/unix/XkeysToSkKeys.h index 3d41a22e5c..1852d99346 100644 --- a/include/utils/unix/XkeysToSkKeys.h +++ b/include/utils/unix/XkeysToSkKeys.h @@ -8,6 +8,14 @@ SkKey XKeyToSkKey(KeySym keysym) { switch (keysym) { + case XK_BackSpace: + return kBack_SkKey; + case XK_Return: + return kOK_SkKey; + case XK_Home: + return kHome_SkKey; + case XK_End: + return kEnd_SkKey; case XK_Right: return kRight_SkKey; case XK_Left: diff --git a/include/views/SkApplication.h b/include/views/SkApplication.h new file mode 100644 index 0000000000..4c4a4fb0d1 --- /dev/null +++ b/include/views/SkApplication.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkApplication_DEFINED +#define SkApplication_DEFINED + +class SkOSWindow; + +extern SkOSWindow* create_sk_window(void* hwnd); +extern void application_init(); +extern void application_term(); + +#endif // SkApplication_DEFINED diff --git a/include/views/SkBGViewArtist.h b/include/views/SkBGViewArtist.h new file mode 100644 index 0000000000..1bca42fa52 --- /dev/null +++ b/include/views/SkBGViewArtist.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkBGViewArtist_DEFINED +#define SkBGViewArtist_DEFINED + +#include "SkView.h" +#include "SkPaint.h" + +class SkBGViewArtist : public SkView::Artist { +public: + SkBGViewArtist(SkColor c = SK_ColorWHITE); + virtual ~SkBGViewArtist(); + + const SkPaint& paint() const { return fPaint; } + SkPaint& paint() { return fPaint; } + +protected: + // overrides + virtual void onDraw(SkView*, SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkPaint fPaint; +}; + +#endif + diff --git a/include/views/SkBorderView.h b/include/views/SkBorderView.h new file mode 100644 index 0000000000..94ccc1f0a5 --- /dev/null +++ b/include/views/SkBorderView.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkBorderView_DEFINED +#define SkBorderView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkBorderView : public SkWidgetView { +public: + SkBorderView(); + ~SkBorderView(); + void setSkin(const char skin[]); + SkScalar getLeft() const { return fLeft; } + SkScalar getRight() const { return fRight; } + SkScalar getTop() const { return fTop; } + SkScalar getBottom() const { return fBottom; } +protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); +private: + SkAnimator fAnim; + SkScalar fLeft, fRight, fTop, fBottom; //margin on each side + SkRect fMargin; + + typedef SkWidgetView INHERITED; +}; + +#endif + diff --git a/include/views/SkEvent.h b/include/views/SkEvent.h new file mode 100644 index 0000000000..f6719d66da --- /dev/null +++ b/include/views/SkEvent.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkEvent_DEFINED +#define SkEvent_DEFINED + +#include "SkDOM.h" +#include "SkMetaData.h" +#include "SkString.h" + +/** Unique 32bit id used to identify an instance of SkEventSink. When events are + posted, they are posted to a specific sinkID. When it is time to dispatch the + event, the sinkID is used to find the specific SkEventSink object. If it is found, + its doEvent() method is called with the event. +*/ +typedef uint32_t SkEventSinkID; + +/** \class SkEvent + + SkEvents are used to communicate type-safe information to SkEventSinks. + SkEventSinks (including SkViews) each have a unique ID, which is stored + in an event. This ID is used to target the event once it has been "posted". +*/ +class SkEvent { +public: + /** Default construct, creating an empty event. + */ + SkEvent(); + /** Construct a new event with the specified type. + */ + explicit SkEvent(const SkString& type); + /** Construct a new event with the specified type. + */ + explicit SkEvent(const char type[]); + /** Construct a new event by copying the fields from the src event. + */ + SkEvent(const SkEvent& src); + ~SkEvent(); + +// /** Return the event's type (will never be null) */ +// const char* getType() const; + /** Copy the event's type into the specified SkString parameter */ + void getType(SkString* str) const; + /** Returns true if the event's type matches exactly the specified type (case sensitive) */ + bool isType(const SkString& str) const; + /** Returns true if the event's type matches exactly the specified type (case sensitive) */ + bool isType(const char type[], size_t len = 0) const; + /** Set the event's type to the specified string. + In XML, use the "type" attribute. + */ + void setType(const SkString&); + /** Set the event's type to the specified string. + In XML, use the "type" attribute. + */ + void setType(const char type[], size_t len = 0); + + /** Return the event's unnamed 32bit field. Default value is 0 */ + uint32_t getFast32() const { return f32; } + /** Set the event's unnamed 32bit field. In XML, use + the subelement <data fast32=... /> + */ + void setFast32(uint32_t x) { f32 = x; } + + /** Return true if the event contains the named 32bit field, and return the field + in value (if value is non-null). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findS32(const char name[], int32_t* value = NULL) const { return fMeta.findS32(name, value); } + /** Return true if the event contains the named SkScalar field, and return the field + in value (if value is non-null). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findScalar(const char name[], SkScalar* value = NULL) const { return fMeta.findScalar(name, value); } + /** Return true if the event contains the named SkScalar field, and return the fields + in value[] (if value is non-null), and return the number of SkScalars in count (if count is non-null). + If there is no matching named field, return false and ignore the value and count parameters. + */ + const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = NULL) const { return fMeta.findScalars(name, count, values); } + /** Return the value of the named string field, or if no matching named field exists, return null. + */ + const char* findString(const char name[]) const { return fMeta.findString(name); } + /** Return true if the event contains the named pointer field, and return the field + in value (if value is non-null). If there is no matching named field, return false + and ignore the value parameter. + */ + bool findPtr(const char name[], void** value) const { return fMeta.findPtr(name, value); } + bool findBool(const char name[], bool* value) const { return fMeta.findBool(name, value); } + const void* findData(const char name[], size_t* byteCount = NULL) const { + return fMeta.findData(name, byteCount); + } + + /** Returns true if ethe event contains the named 32bit field, and if it equals the specified value */ + bool hasS32(const char name[], int32_t value) const { return fMeta.hasS32(name, value); } + /** Returns true if ethe event contains the named SkScalar field, and if it equals the specified value */ + bool hasScalar(const char name[], SkScalar value) const { return fMeta.hasScalar(name, value); } + /** Returns true if ethe event contains the named string field, and if it equals (using strcmp) the specified value */ + bool hasString(const char name[], const char value[]) const { return fMeta.hasString(name, value); } + /** Returns true if ethe event contains the named pointer field, and if it equals the specified value */ + bool hasPtr(const char name[], void* value) const { return fMeta.hasPtr(name, value); } + bool hasBool(const char name[], bool value) const { return fMeta.hasBool(name, value); } + bool hasData(const char name[], const void* data, size_t byteCount) const { + return fMeta.hasData(name, data, byteCount); + } + + /** Add/replace the named 32bit field to the event. In XML use the subelement <data name=... s32=... /> */ + void setS32(const char name[], int32_t value) { fMeta.setS32(name, value); } + /** Add/replace the named SkScalar field to the event. In XML use the subelement <data name=... scalar=... /> */ + void setScalar(const char name[], SkScalar value) { fMeta.setScalar(name, value); } + /** Add/replace the named SkScalar[] field to the event. */ + SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL) { return fMeta.setScalars(name, count, values); } + /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */ + void setString(const char name[], const SkString& value) { fMeta.setString(name, value.c_str()); } + /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */ + void setString(const char name[], const char value[]) { fMeta.setString(name, value); } + /** Add/replace the named pointer field to the event. There is no XML equivalent for this call */ + void setPtr(const char name[], void* value) { fMeta.setPtr(name, value); } + void setBool(const char name[], bool value) { fMeta.setBool(name, value); } + void setData(const char name[], const void* data, size_t byteCount) { + fMeta.setData(name, data, byteCount); + } + + /** Return the underlying metadata object */ + SkMetaData& getMetaData() { return fMeta; } + /** Return the underlying metadata object */ + const SkMetaData& getMetaData() const { return fMeta; } + + void tron() { SkDEBUGCODE(fDebugTrace = true;) } + void troff() { SkDEBUGCODE(fDebugTrace = false;) } + bool isDebugTrace() const + { +#ifdef SK_DEBUG + return fDebugTrace; +#else + return false; +#endif + } + + /** Call this to initialize the event from the specified XML node */ + void inflate(const SkDOM&, const SkDOM::Node*); + + SkDEBUGCODE(void dump(const char title[] = NULL);) + + /** Post the specified event to the event queue, targeting the specified eventsink, with an optional + delay. The event must be dynamically allocated for this. It cannot be a global or on the stack. + After this call, ownership is transfered to the system, so the caller must not retain + the event's ptr. Returns false if the event could not be posted (which means it will have been deleted). + */ + static bool Post(SkEvent* evt, SkEventSinkID targetID, SkMSec delay = 0); + /** Post the specified event to the event queue, targeting the specified eventsink, to be delivered on/after the + specified millisecond time. The event must be dynamically allocated for this. It cannot be a global or on the stack. + After this call, ownership is transfered to the system, so the caller must not retain + the event's ptr. Returns false if the event could not be posted (which means it will have been deleted). + */ + static bool PostTime(SkEvent* evt, SkEventSinkID targetID, SkMSec time); + + /** Helper method for calling SkEvent::PostTime(this, ...), where the caller specifies a delay. + The real "time" will be computed automatically by sampling the clock and adding its value + to delay. + */ + bool post(SkEventSinkID sinkID, SkMSec delay = 0) + { + return SkEvent::Post(this, sinkID, delay); + } + + void postTime(SkEventSinkID sinkID, SkMSec time) + { + SkEvent::PostTime(this, sinkID, time); + } + + /////////////////////////////////////////////// + /** Porting layer must call these functions **/ + /////////////////////////////////////////////// + + /** Global initialization function for the SkEvent system. Should be called exactly + once before any other event method is called, and should be called after the + call to SkGraphics::Init(). + */ + static void Init(); + /** Global cleanup function for the SkEvent system. Should be called exactly once after + all event methods have been called, and should be called before calling SkGraphics::Term(). + */ + static void Term(); + + /** Call this to process one event from the queue. If it returns true, there are more events + to process. + */ + static bool ProcessEvent(); + /** Call this whenever the requested timer has expired (requested by a call to SetQueueTimer). + It will post any delayed events whose time as "expired" onto the event queue. + It may also call SignalQueueTimer() and SignalNonEmptyQueue(). + */ + static void ServiceQueueTimer(); + + /** Return the number of queued events. note that this value may be obsolete + upon return, since another thread may have called ProcessEvent() or + Post() after the count was made. + */ + static int CountEventsOnQueue(); + + //////////////////////////////////////////////////// + /** Porting layer must implement these functions **/ + //////////////////////////////////////////////////// + + /** Called whenever an SkEvent is posted to an empty queue, so that the OS + can be told to later call Dequeue(). + */ + static void SignalNonEmptyQueue(); + /** Called whenever the delay until the next delayed event changes. If zero is + passed, then there are no more queued delay events. + */ + static void SignalQueueTimer(SkMSec delay); + +#ifndef SK_USE_WXWIDGETS +#ifdef SK_BUILD_FOR_WIN + static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#elif defined(SK_BUILD_FOR_UNIXx) + static uint32_t HandleTimer(uint32_t, void*); + static bool WndProc(Display*, Window, XEvent&); +#endif +#else + // Don't know yet what this will be + //static bool CustomEvent(); +#endif + +private: + SkMetaData fMeta; + mutable char* fType; // may be characters with low bit set to know that it is not a pointer + uint32_t f32; + SkDEBUGCODE(bool fDebugTrace;) + + // these are for our implementation of the event queue + SkEventSinkID fTargetID; + SkMSec fTime; + SkEvent* fNextEvent; // either in the delay or normal event queue + void initialize(const char* type, size_t typeLen); + + static bool Enqueue(SkEvent* evt); + static SkMSec EnqueueTime(SkEvent* evt, SkMSec time); + static SkEvent* Dequeue(SkEventSinkID* targetID); + static bool QHasEvents(); +}; + +#endif + diff --git a/include/views/SkEventSink.h b/include/views/SkEventSink.h new file mode 100644 index 0000000000..27a67437c3 --- /dev/null +++ b/include/views/SkEventSink.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkEventSink_DEFINED +#define SkEventSink_DEFINED + +#include "SkRefCnt.h" +#include "SkEvent.h" + +struct SkTagList; + +/** \class SkEventSink + + SkEventSink is the base class for all objects that receive SkEvents. +*/ +class SkEventSink : public SkRefCnt { +public: + SkEventSink(); + virtual ~SkEventSink(); + + /** Returns this eventsink's unique ID. Use this to post SkEvents to + this eventsink. + */ + SkEventSinkID getSinkID() const { return fID; } + + /** Call this to pass an event to this object for processing. Returns true if the + event was handled. + */ + bool doEvent(const SkEvent&); + /** Returns true if the sink (or one of its subclasses) understands the event as a query. + If so, the sink may modify the event to communicate its "answer". + */ + bool doQuery(SkEvent* query); + + /** Add sinkID to the list of listeners, to receive events from calls to sendToListeners() + and postToListeners(). If sinkID already exists in the listener list, no change is made. + */ + void addListenerID(SkEventSinkID sinkID); + /** Copy listeners from one event sink to another, typically from parent to child. + @param from the event sink to copy the listeners from + */ + void copyListeners(const SkEventSink& from); + /** Remove sinkID from the list of listeners. If sinkID does not appear in the list, + no change is made. + */ + void removeListenerID(SkEventSinkID); + /** Returns true if there are 1 or more listeners attached to this eventsink + */ + bool hasListeners() const; + /** Posts a copy of evt to each of the eventsinks in the lisener list. + */ + void postToListeners(const SkEvent& evt, SkMSec delay = 0); + + enum EventResult { + kHandled_EventResult, //!< the eventsink returned true from its doEvent method + kNotHandled_EventResult, //!< the eventsink returned false from its doEvent method + kSinkNotFound_EventResult //!< no matching eventsink was found for the event's getSink(). + }; + /** DoEvent handles searching for an eventsink object that matches the targetID. + If one is found, it calls the sink's doEvent method, returning + either kHandled_EventResult or kNotHandled_EventResult. If no matching + eventsink is found, kSinkNotFound_EventResult is returned. + */ + static EventResult DoEvent(const SkEvent&, SkEventSinkID targetID); + + /** Returns the matching eventsink, or null if not found + */ + static SkEventSink* FindSink(SkEventSinkID); + +protected: + /** Override this to handle events in your subclass. Be sure to call the inherited version + for events that you don't handle. + */ + virtual bool onEvent(const SkEvent&); + virtual bool onQuery(SkEvent*); + + SkTagList* findTagList(U8CPU tag) const; + void addTagList(SkTagList*); + void removeTagList(U8CPU tag); + +private: + SkEventSinkID fID; + SkTagList* fTagHead; + + // for our private link-list + SkEventSink* fNextSink; +}; + +#endif + diff --git a/include/views/SkImageView.h b/include/views/SkImageView.h new file mode 100644 index 0000000000..57215c9dce --- /dev/null +++ b/include/views/SkImageView.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkImageView_DEFINED +#define SkImageView_DEFINED + +#include "SkView.h" +#include "SkString.h" + +class SkAnimator; +class SkBitmap; +class SkMatrix; + +class SkImageView : public SkView { +public: + SkImageView(); + virtual ~SkImageView(); + + void getUri(SkString*) const; + void setUri(const char []); + void setUri(const SkString&); + + + enum ScaleType { + kMatrix_ScaleType, + kFitXY_ScaleType, + kFitStart_ScaleType, + kFitCenter_ScaleType, + kFitEnd_ScaleType + }; + ScaleType getScaleType() const { return (ScaleType)fScaleType; } + void setScaleType(ScaleType); + + bool getImageMatrix(SkMatrix*) const; + void setImageMatrix(const SkMatrix*); + +protected: + // overrides + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOMNode*); + +private: + SkString fUri; + SkMatrix* fMatrix; // null or copy of caller's matrix ,,,,, + union { + SkAnimator* fAnim; + SkBitmap* fBitmap; + } fData; + uint8_t fScaleType; + SkBool8 fDataIsAnim; // as opposed to bitmap + SkBool8 fUriIsValid; + + void onUriChange(); + bool getDataBounds(SkRect* bounds); + bool freeData(); + bool ensureUriIsLoaded(); + + typedef SkView INHERITED; +}; + +#endif diff --git a/include/views/SkKey.h b/include/views/SkKey.h new file mode 100644 index 0000000000..3fd5114346 --- /dev/null +++ b/include/views/SkKey.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkKey_DEFINED +#define SkKey_DEFINED + +#include "SkTypes.h" + +enum SkKey { + //reordering these to match android.app.KeyEvent + kNONE_SkKey, //corresponds to android's UNKNOWN + + kLeftSoftKey_SkKey, + kRightSoftKey_SkKey, + + kHome_SkKey, //!< the home key - added to match android + kBack_SkKey, //!< (CLR) + kSend_SkKey, //!< the green (talk) key + kEnd_SkKey, //!< the red key + + k0_SkKey, + k1_SkKey, + k2_SkKey, + k3_SkKey, + k4_SkKey, + k5_SkKey, + k6_SkKey, + k7_SkKey, + k8_SkKey, + k9_SkKey, + kStar_SkKey, //!< the * key + kHash_SkKey, //!< the # key + + kUp_SkKey, + kDown_SkKey, + kLeft_SkKey, + kRight_SkKey, + + kOK_SkKey, //!< the center key + + kVolUp_SkKey, //!< volume up - match android + kVolDown_SkKey, //!< volume down - same + kPower_SkKey, //!< power button - same + kCamera_SkKey, //!< camera - same + + kSkKeyCount +}; + +#endif + diff --git a/include/views/SkOSMenu.h b/include/views/SkOSMenu.h new file mode 100644 index 0000000000..433a60155c --- /dev/null +++ b/include/views/SkOSMenu.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSMenu_DEFINED +#define SkOSMenu_DEFINED + +#include "SkEvent.h" +#include "SkTDArray.h" + +class SkOSMenu { +public: + explicit SkOSMenu(const char title[]); + ~SkOSMenu(); + + const char* getTitle() const { return fTitle; } + + void appendItem(const char title[], const char eventType[], int32_t eventData); + + // called by SkOSWindow when it receives an OS menu event + int countItems() const; + const char* getItem(int index, uint32_t* cmdID) const; + + SkEvent* createEvent(uint32_t os_cmd); + +private: + const char* fTitle; + + struct Item { + const char* fTitle; + const char* fEventType; + uint32_t fEventData; + uint32_t fOSCmd; // internal + }; + SkTDArray<Item> fItems; + + // illegal + SkOSMenu(const SkOSMenu&); + SkOSMenu& operator=(const SkOSMenu&); +}; + +#endif + diff --git a/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h new file mode 100644 index 0000000000..38a4cf8f5e --- /dev/null +++ b/include/views/SkOSWindow_Android.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_Android_DEFINED +#define SkOSWindow_Android_DEFINED + +#include "SkWindow.h" +#include "SkEvent.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void*) {} + ~SkOSWindow() {} + bool attachGL() { return false; } + void detachGL() {} + void presentGL() {} + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + virtual void onSetTitle(const char title[]); + +private: + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h new file mode 100644 index 0000000000..232a202205 --- /dev/null +++ b/include/views/SkOSWindow_Mac.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_Mac_DEFINED +#define SkOSWindow_Mac_DEFINED + +#include <Carbon/Carbon.h> +#include "SkWindow.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + + void* getHWND() const { return fHWND; } + void* getHVIEW() const { return fHVIEW; } + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + static OSStatus EventHandler(EventHandlerCallRef inHandler, + EventRef inEvent, void* userData); + + void doPaint(void* ctx); + + + bool attachGL(); + void detachGL(); + void presentGL(); + +protected: + // overrides from SkEventSink + virtual bool onEvent(const SkEvent& evt); + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + virtual void onSetTitle(const char[]); + + +private: + void* fHWND; + void* fHVIEW; + void* fAGLCtx; + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h new file mode 100644 index 0000000000..0ff24f31ff --- /dev/null +++ b/include/views/SkOSWindow_SDL.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_SDL_DEFINED +#define SkOSWindow_SDL_DEFINED + +#include "SDL.h" +#include "SkWindow.h" + +class SkGLCanvas; + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* screen); + virtual ~SkOSWindow(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + void handleSDLEvent(const SDL_Event& event); + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + virtual void onSetTitle(const char[]); + +private: + SDL_Surface* fScreen; + SDL_Surface* fSurface; + SkGLCanvas* fGLCanvas; + + void doDraw(); + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h new file mode 100644 index 0000000000..803ca1303d --- /dev/null +++ b/include/views/SkOSWindow_Unix.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_Unix_DEFINED +#define SkOSWindow_Unix_DEFINED + +#include "SkWindow.h" +#include <X11/Xlib.h> +#include <GL/glx.h> + +class SkBitmap; +class SkEvent; + +struct SkUnixWindow { + Display* fDisplay; + Window fWin; + size_t fOSWin; + GC fGc; + GLXContext fGLContext; + bool fGLCreated; +}; + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void*); + ~SkOSWindow(); + + void* getHWND() const { return (void*)fUnixWindow.fWin; } + void* getDisplay() const { return (void*)fUnixWindow.fDisplay; } + void* getUnixWindow() const { return (void*)&fUnixWindow; } + void loop(); + void post_linuxevent(); + bool attachGL(); + void detachGL(); + void presentGL(); + + //static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + //static bool WndProc(SkUnixWindow* w, XEvent &e); + +protected: + // overrides from SkWindow + virtual bool onEvent(const SkEvent&); + virtual void onHandleInval(const SkIRect&); + virtual bool onHandleChar(SkUnichar); + virtual bool onHandleKey(SkKey); + virtual bool onHandleKeyUp(SkKey); + virtual void onSetTitle(const char title[]); + +private: + SkUnixWindow fUnixWindow; + bool fGLAttached; + bool fRestart; + + // Needed for GL + XVisualInfo* fVi; + + void doPaint(); + void restartLoop(); + void mapWindowAndWait(); + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h new file mode 100644 index 0000000000..4b3e9164c3 --- /dev/null +++ b/include/views/SkOSWindow_Win.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_Win_DEFINED +#define SkOSWindow_Win_DEFINED + +#include "SkWindow.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + virtual ~SkOSWindow(); + + void* getHWND() const { return fHWND; } + void setSize(int width, int height); + void updateSize(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + bool attachGL(); + void detachGL(); + void presentGL(); + + bool attachD3D9(); + void detachD3D9(); + void presentD3D9(); + + void* d3d9Device() { return fD3D9Device; } + + bool wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + static bool QuitOnDeactivate(HWND hWnd); + + enum { + SK_WM_SkEvent = WM_APP + 1000, + SK_WM_SkTimerID = 0xFFFF // just need a non-zero value + }; + +protected: + virtual bool quitOnDeactivate() { return true; } + + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + + virtual void onSetTitle(const char title[]); + +private: + void* fHWND; + + void doPaint(void* ctx); + + void* fHGLRC; + + bool fGLAttached; + + void* fD3D9Device; + bool fD3D9Attached; + + HMENU fMBar; + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkOSWindow_wxwidgets.h b/include/views/SkOSWindow_wxwidgets.h new file mode 100644 index 0000000000..c5dfc7c45a --- /dev/null +++ b/include/views/SkOSWindow_wxwidgets.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * SkOSWindow_wxwidgets.h + * wxwidgets + * + * Copyright 2005 __MyCompanyName__. All rights reserved. + * + */ + +#ifndef SkOSWindow_wxwidgets_DEFINED +#define SkOSWindow_wxwidgets_DEFINED + +#include "SkWindow.h" +#include "wx/frame.h" + +class SkOSWindow: public SkWindow +{ +public: + SkOSWindow(); + SkOSWindow(const wxString& title, int x, int y, int width, int height); + ~SkOSWindow(); + + wxFrame* getWXFrame() const { return fFrame; } + + void updateSize(); + +protected: + virtual void onHandleInval(const SkIRect&); + virtual void onAddMenu(const SkOSMenu*); + +private: + wxFrame* fFrame; + typedef SkWindow INHERITED; + +}; + +#endifpedef SkWindow INHERITED; diff --git a/include/views/SkProgressBarView.h b/include/views/SkProgressBarView.h new file mode 100644 index 0000000000..6341fcb051 --- /dev/null +++ b/include/views/SkProgressBarView.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkProgressBarView_DEFINED +#define SkProgressBarView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkProgressBarView : public SkWidgetView { + public: + SkProgressBarView(); + //SkProgressBarView(int max); + + //inflate: "sk-progress" + + void reset(); //reset progress to zero + void setProgress(int progress); + void changeProgress(int diff); + void setMax(int max); + + int getProgress() const { return fProgress; } + int getMax() const { return fMax; } + + protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); + + private: + SkAnimator fAnim; + int fProgress; + int fMax; + + typedef SkWidgetView INHERITED; +}; + + + + +#endif diff --git a/include/views/SkScrollBarView.h b/include/views/SkScrollBarView.h new file mode 100644 index 0000000000..b8a5209ddb --- /dev/null +++ b/include/views/SkScrollBarView.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkScrollBarView_DEFINED +#define SkScrollBarView_DEFINED + +#include "SkView.h" +#include "SkWidgetViews.h" +#include "SkAnimator.h" + +class SkScrollBarView : public SkWidgetView { +public: + SkScrollBarView(); + + unsigned getStart() const { return fStartPoint; } + unsigned getShown() const { return fShownLength; } + unsigned getTotal() const { return fTotalLength; } + + void setStart(unsigned start); + void setShown(unsigned shown); + void setTotal(unsigned total); + +protected: + //overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual void onSizeChange(); + virtual void onDraw(SkCanvas* canvas); + virtual bool onEvent(const SkEvent& evt); + +private: + SkAnimator fAnim; + unsigned fTotalLength, fStartPoint, fShownLength; + + void adjust(); + + typedef SkWidgetView INHERITED; +}; +#endif + diff --git a/include/views/SkStackViewLayout.h b/include/views/SkStackViewLayout.h new file mode 100644 index 0000000000..8000319fcb --- /dev/null +++ b/include/views/SkStackViewLayout.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkStackViewLayout_DEFINED +#define SkStackViewLayout_DEFINED + +#include "SkView.h" + +class SkStackViewLayout : public SkView::Layout { +public: + SkStackViewLayout(); + + enum Orient { + kHorizontal_Orient, + kVertical_Orient, + + kOrientCount + }; + Orient getOrient() const { return (Orient)fOrient; } + void setOrient(Orient); + + void getMargin(SkRect*) const; + void setMargin(const SkRect&); + + SkScalar getSpacer() const { return fSpacer; } + void setSpacer(SkScalar); + + /** Controls the posititioning in the same direction as the orientation + */ + enum Pack { + kStart_Pack, + kCenter_Pack, + kEnd_Pack, + + kPackCount + }; + Pack getPack() const { return (Pack)fPack; } + void setPack(Pack); + + /** Controls the posititioning at right angles to the orientation + */ + enum Align { + kStart_Align, + kCenter_Align, + kEnd_Align, + kStretch_Align, + + kAlignCount + }; + Align getAlign() const { return (Align)fAlign; } + void setAlign(Align); + + bool getRound() const { return SkToBool(fRound); } + void setRound(bool); + +protected: + virtual void onLayoutChildren(SkView* parent); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkRect fMargin; + SkScalar fSpacer; + uint8_t fOrient, fPack, fAlign, fRound; +}; + +class SkFillViewLayout : public SkView::Layout { +public: + SkFillViewLayout(); + void getMargin(SkRect*) const; + void setMargin(const SkRect&); + +protected: + // overrides; + virtual void onLayoutChildren(SkView* parent); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkRect fMargin; + typedef SkView::Layout INHERITED; +}; + +#endif + diff --git a/include/views/SkSystemEventTypes.h b/include/views/SkSystemEventTypes.h new file mode 100644 index 0000000000..8dfe8bec07 --- /dev/null +++ b/include/views/SkSystemEventTypes.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkSystemEventTypes_DEFINED +#define SkSystemEventTypes_DEFINED + +/* + The goal of these strings is two-fold: + 1) make funny strings (containing at least one char < 32) to avoid colliding with "user" strings + 2) keep them <= 4 bytes, so we can avoid an allocation in SkEvent::setType() +*/ +#define SK_EventType_Delay "\xd" "lay" +#define SK_EventType_Inval "nv" "\xa" "l" +#define SK_EventType_Key "key" "\x1" +#define SK_EventType_OnEnd "on" "\xe" "n" +#define SK_EventType_Unichar "\xc" "har" +#define SK_EventType_KeyUp "key" "\xf" + +#endif diff --git a/include/views/SkTouchGesture.h b/include/views/SkTouchGesture.h new file mode 100644 index 0000000000..79d4e283df --- /dev/null +++ b/include/views/SkTouchGesture.h @@ -0,0 +1,72 @@ +#ifndef SkTouchGesture_DEFINED +#define SkTouchGesture_DEFINED + +#include "SkTDArray.h" +#include "SkMatrix.h" + +struct SkFlingState { + SkFlingState() : fActive(false) {} + + bool isActive() const { return fActive; } + void stop() { fActive = false; } + + void reset(float sx, float sy); + bool evaluateMatrix(SkMatrix* matrix); + +private: + SkPoint fDirection; + SkScalar fSpeed0; + double fTime0; + bool fActive; +}; + +class SkTouchGesture { +public: + SkTouchGesture(); + ~SkTouchGesture(); + + void touchBegin(void* owner, float x, float y); + void touchMoved(void* owner, float x, float y); + void touchEnd(void* owner); + void reset(); + + bool isActive() { return fFlinger.isActive(); } + void stop() { fFlinger.stop(); } + + const SkMatrix& localM(); + const SkMatrix& globalM() const { return fGlobalM; } + +private: + enum State { + kEmpty_State, + kTranslate_State, + kZoom_State, + }; + + struct Rec { + void* fOwner; + float fStartX, fStartY; + float fPrevX, fPrevY; + float fLastX, fLastY; + SkMSec fPrevT, fLastT; + }; + SkTDArray<Rec> fTouches; + + State fState; + SkMatrix fLocalM, fGlobalM; + SkFlingState fFlinger; + SkMSec fLastUpT; + SkPoint fLastUpP; + + + void flushLocalM(); + int findRec(void* owner) const; + void appendNewRec(void* owner, float x, float y); + float computePinch(const Rec&, const Rec&); + float limitTotalZoom(float scale) const; + bool handleDblTap(float, float); +}; + +#endif + + diff --git a/include/views/SkView.h b/include/views/SkView.h new file mode 100644 index 0000000000..d3633db160 --- /dev/null +++ b/include/views/SkView.h @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkView_DEFINED +#define SkView_DEFINED + +#include "SkEventSink.h" +#include "SkRect.h" +#include "SkDOM.h" +#include "SkTDict.h" + +class SkCanvas; +class SkLayerView; + +/** \class SkView + + SkView is the base class for screen management. All widgets and controls inherit + from SkView. +*/ +class SkView : public SkEventSink { +public: + enum Flag_Shift { + kVisible_Shift, + kEnabled_Shift, + kFocusable_Shift, + kFlexH_Shift, + kFlexV_Shift, + kNoClip_Shift, + + kFlagShiftCount + }; + enum Flag_Mask { + kVisible_Mask = 1 << kVisible_Shift, //!< set if the view is visible + kEnabled_Mask = 1 << kEnabled_Shift, //!< set if the view is enabled + kFocusable_Mask = 1 << kFocusable_Shift, //!< set if the view can receive focus + kFlexH_Mask = 1 << kFlexH_Shift, //!< set if the view's width is stretchable + kFlexV_Mask = 1 << kFlexV_Shift, //!< set if the view's height is stretchable + kNoClip_Mask = 1 << kNoClip_Shift, //!< set if the view is not clipped to its bounds + + kAllFlagMasks = (uint32_t)(0 - 1) >> (32 - kFlagShiftCount) + }; + + SkView(uint32_t flags = 0); + virtual ~SkView(); + + /** Return the flags associated with the view + */ + uint32_t getFlags() const { return fFlags; } + /** Set the flags associated with the view + */ + void setFlags(uint32_t flags); + + /** Helper that returns non-zero if the kVisible_Mask bit is set in the view's flags + */ + int isVisible() const { return fFlags & kVisible_Mask; } + int isEnabled() const { return fFlags & kEnabled_Mask; } + int isFocusable() const { return fFlags & kFocusable_Mask; } + int isClipToBounds() const { return !(fFlags & kNoClip_Mask); } + /** Helper to set/clear the view's kVisible_Mask flag */ + void setVisibleP(bool); + void setEnabledP(bool); + void setFocusableP(bool); + void setClipToBounds(bool); + + /** Return the view's width */ + SkScalar width() const { return fWidth; } + /** Return the view's height */ + SkScalar height() const { return fHeight; } + /** Set the view's width and height. These must both be >= 0. This does not affect the view's loc */ + void setSize(SkScalar width, SkScalar height); + void setSize(const SkPoint& size) { this->setSize(size.fX, size.fY); } + void setWidth(SkScalar width) { this->setSize(width, fHeight); } + void setHeight(SkScalar height) { this->setSize(fWidth, height); } + /** Return a rectangle set to [0, 0, width, height] */ + void getLocalBounds(SkRect* bounds) const; + + /** Return the view's left edge */ + SkScalar locX() const { return fLoc.fX; } + /** Return the view's top edge */ + SkScalar locY() const { return fLoc.fY; } + /** Set the view's left and top edge. This does not affect the view's size */ + void setLoc(SkScalar x, SkScalar y); + void setLoc(const SkPoint& loc) { this->setLoc(loc.fX, loc.fY); } + void setLocX(SkScalar x) { this->setLoc(x, fLoc.fY); } + void setLocY(SkScalar y) { this->setLoc(fLoc.fX, y); } + /** Offset (move) the view by the specified dx and dy. This does not affect the view's size */ + void offset(SkScalar dx, SkScalar dy); + + /** Call this to have the view draw into the specified canvas. */ + virtual void draw(SkCanvas* canvas); + + /** Call this to invalidate part of all of a view, requesting that the view's + draw method be called. The rectangle parameter specifies the part of the view + that should be redrawn. If it is null, it specifies the entire view bounds. + */ + void inval(SkRect* rectOrNull); + + // Focus management + + SkView* getFocusView() const; + bool hasFocus() const; + + enum FocusDirection { + kNext_FocusDirection, + kPrev_FocusDirection, + + kFocusDirectionCount + }; + bool acceptFocus(); + SkView* moveFocus(FocusDirection); + + // Click handling + + class Click { + public: + Click(SkView* target); + virtual ~Click(); + + const char* getType() const { return fType; } + bool isType(const char type[]) const; + void setType(const char type[]); // does NOT make a copy of the string + void copyType(const char type[]); // makes a copy of the string + + enum State { + kDown_State, + kMoved_State, + kUp_State + }; + SkPoint fOrig, fPrev, fCurr; + SkIPoint fIOrig, fIPrev, fICurr; + State fState; + private: + SkEventSinkID fTargetID; + char* fType; + bool fWeOwnTheType; + + void resetType(); + + friend class SkView; + }; + Click* findClickHandler(SkScalar x, SkScalar y); + + static void DoClickDown(Click*, int x, int y); + static void DoClickMoved(Click*, int x, int y); + static void DoClickUp(Click*, int x, int y); + + /** Send the event to the view's parent, and its parent etc. until one of them + returns true from its onEvent call. This view is returned. If no parent handles + the event, null is returned. + */ + SkView* sendEventToParents(const SkEvent&); + /** Send the query to the view's parent, and its parent etc. until one of them + returns true from its onQuery call. This view is returned. If no parent handles + the query, null is returned. + */ + SkView* sendQueryToParents(SkEvent*); + + /** Depricated helper function. Just call event->post(sinkID, delay); + */ + bool postEvent(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) { return evt->post(sinkID, delay); } + + // View hierarchy management + + /** Return the view's parent, or null if it has none. This does not affect the parent's reference count. */ + SkView* getParent() const { return fParent; } + SkView* attachChildToFront(SkView* child); + /** Attach the child view to this view, and increment the child's reference count. The child view is added + such that it will be drawn before all other child views. + The child view parameter is returned. + */ + SkView* attachChildToBack(SkView* child); + /** If the view has a parent, detach the view from its parent and decrement the view's reference count. + If the parent was the only owner of the view, this will cause the view to be deleted. + */ + void detachFromParent(); + /** Attach the child view to this view, and increment the child's reference count. The child view is added + such that it will be drawn after all other child views. + The child view parameter is returned. + */ + /** Detach all child views from this view. */ + void detachAllChildren(); + + /** Convert the specified point from global coordinates into view-local coordinates + */ + void globalToLocal(SkPoint* pt) const { if (pt) this->globalToLocal(pt->fX, pt->fY, pt); } + /** Convert the specified x,y from global coordinates into view-local coordinates, returning + the answer in the local parameter. + */ + void globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const; + + /** \class F2BIter + + Iterator that will return each of this view's children, in + front-to-back order (the order used for clicking). The first + call to next() returns the front-most child view. When + next() returns null, there are no more child views. + */ + class F2BIter { + public: + F2BIter(const SkView* parent); + SkView* next(); + private: + SkView* fFirstChild, *fChild; + }; + + /** \class B2FIter + + Iterator that will return each of this view's children, in + back-to-front order (the order they are drawn). The first + call to next() returns the back-most child view. When + next() returns null, there are no more child views. + */ + class B2FIter { + public: + B2FIter(const SkView* parent); + SkView* next(); + private: + SkView* fFirstChild, *fChild; + }; + + /** \class Artist + + Install a subclass of this in a view (calling setArtist()), and then the + default implementation of that view's onDraw() will invoke this object + automatically. + */ + class Artist : public SkRefCnt { + public: + void draw(SkView*, SkCanvas*); + void inflate(const SkDOM&, const SkDOM::Node*); + protected: + virtual void onDraw(SkView*, SkCanvas*) = 0; + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + }; + /** Return the artist attached to this view (or null). The artist's reference + count is not affected. + */ + Artist* getArtist() const; + /** Attach the specified artist (or null) to the view, replacing any existing + artist. If the new artist is not null, its reference count is incremented. + The artist parameter is returned. + */ + Artist* setArtist(Artist* artist); + + /** \class Layout + + Install a subclass of this in a view (calling setLayout()), and then the + default implementation of that view's onLayoutChildren() will invoke + this object automatically. + */ + class Layout : public SkRefCnt { + public: + void layoutChildren(SkView* parent); + void inflate(const SkDOM&, const SkDOM::Node*); + protected: + virtual void onLayoutChildren(SkView* parent) = 0; + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + }; + + /** Return the layout attached to this view (or null). The layout's reference + count is not affected. + */ + Layout* getLayout() const; + /** Attach the specified layout (or null) to the view, replacing any existing + layout. If the new layout is not null, its reference count is incremented. + The layout parameter is returned. + */ + Layout* setLayout(Layout*, bool invokeLayoutNow = true); + /** If a layout is attached to this view, call its layoutChildren() method + */ + void invokeLayout(); + + /** Call this to initialize this view based on the specified XML node + */ + void inflate(const SkDOM& dom, const SkDOM::Node* node); + /** After a view hierarchy is inflated, this may be called with a dictionary + containing pairs of <name, view*>, where the name string was the view's + "id" attribute when it was inflated. + + This will call the virtual onPostInflate for this view, and the recursively + call postInflate on all of the view's children. + */ + void postInflate(const SkTDict<SkView*>& ids); + + SkDEBUGCODE(void dump(bool recurse) const;) + +protected: + /** Override this to draw inside the view. Be sure to call the inherited version too */ + virtual void onDraw(SkCanvas*); + /** Override this to be notified when the view's size changes. Be sure to call the inherited version too */ + virtual void onSizeChange(); + /** Override this if you want to handle an inval request from this view or one of its children. + Tyically this is only overridden by the by the "window". If your subclass does handle the + request, return true so the request will not continue to propogate to the parent. + */ + virtual bool handleInval(const SkRect*); + //! called once before all of the children are drawn (or clipped/translated) + virtual SkCanvas* beforeChildren(SkCanvas* c) { return c; } + //! called once after all of the children are drawn (or clipped/translated) + virtual void afterChildren(SkCanvas* orig) {} + + //! called right before this child's onDraw is called + virtual void beforeChild(SkView* child, SkCanvas* canvas) {} + //! called right after this child's onDraw is called + virtual void afterChild(SkView* child, SkCanvas* canvas) {} + + /** Override this if you might handle the click + */ + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + /** Override this to decide if your children are targets for a click. + The default returns true, in which case your children views will be + candidates for onFindClickHandler. Returning false wil skip the children + and just call your onFindClickHandler. + */ + virtual bool onSendClickToChildren(SkScalar x, SkScalar y); + /** Override this to track clicks, returning true as long as you want to track + the pen/mouse. + */ + virtual bool onClick(Click*); + /** Override this to initialize your subclass from the XML node. Be sure to call the inherited version too */ + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + /** Override this if you want to perform post initialization work based on the ID dictionary built + during XML parsing. Be sure to call the inherited version too. + */ + virtual void onPostInflate(const SkTDict<SkView*>&); + +public: + // default action is to inval the view + virtual void onFocusChange(bool gainFocusP); +protected: + + // override these if you're acting as a layer/host + virtual bool onGetFocusView(SkView**) const { return false; } + virtual bool onSetFocusView(SkView*) { return false; } + +private: + SkScalar fWidth, fHeight; + SkPoint fLoc; + SkView* fParent; + SkView* fFirstChild; + SkView* fNextSibling; + SkView* fPrevSibling; + uint8_t fFlags; + uint8_t fContainsFocus; + + friend class B2FIter; + friend class F2BIter; + + friend class SkLayerView; + + bool setFocusView(SkView* fvOrNull); + SkView* acceptFocus(FocusDirection); + void detachFromParent_NoLayout(); +}; + +#endif + diff --git a/include/views/SkViewInflate.h b/include/views/SkViewInflate.h new file mode 100644 index 0000000000..3ec65a6f77 --- /dev/null +++ b/include/views/SkViewInflate.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkViewInflate_DEFINED +#define SkViewInflate_DEFINED + +#include "SkDOM.h" +#include "SkTDict.h" +#include "SkEvent.h" + +class SkView; + +class SkViewInflate { +public: + SkViewInflate(); + virtual ~SkViewInflate(); + + /** Return the tree of inflated views. If root is null, create the root element + as a view, otherwise assume root is that view, and just "inflate" it. + + Returns null if the tree cannot be built. + */ + SkView* inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root = NULL); + SkView* inflate(const char xml[], size_t len, SkView* root = NULL); + + /** Given an id attribute value, return the corresponding view, or null + if no match is found. + */ + SkView* findViewByID(const char id[]) const; + + SkDEBUGCODE(void dump() const;) + +protected: + /* Override this in your subclass to handle instantiating views + Call the inherited version for nodes you don't recognize. + + Do not call "inflate" on the view, just return it. This will + get called automatically after createView returns. + */ + virtual SkView* createView(const SkDOM& dom, const SkDOM::Node* node); + /** Base implementation calls view->inflate(dom, node). Subclasses may override this + to perform additional initializations to view, either before or after calling + the inherited version. + */ + virtual void inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node); + +private: + enum { + kMinIDStrAlloc = 64 + }; + SkTDict<SkView*> fIDs; + + struct IDStr { + SkView* fView; + char* fStr; + }; + SkTDArray<IDStr> fListenTo, fBroadcastTo; + SkChunkAlloc fStrings; + + void addIDStr(SkTDArray<IDStr>* list, SkView*, const char* str); + + void rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent); +}; + +#endif + diff --git a/include/views/SkWidget.h b/include/views/SkWidget.h new file mode 100644 index 0000000000..db85f01ff4 --- /dev/null +++ b/include/views/SkWidget.h @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkWidget_DEFINED +#define SkWidget_DEFINED + +#include "SkView.h" +#include "SkBitmap.h" +#include "SkDOM.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkTDArray.h" + +////////////////////////////////////////////////////////////////////////////// + +class SkWidget : public SkView { +public: + SkWidget(uint32_t flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {} + + /** Call this to post the widget's event to its listeners */ + void postWidgetEvent(); + + static void Init(); + static void Term(); +protected: + // override to add slots to an event before posting + virtual void prepareWidgetEvent(SkEvent*); + virtual void onEnabledChange(); + + // <event ...> to initialize the event from XML + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkEvent fEvent; + typedef SkView INHERITED; +}; + +class SkHasLabelWidget : public SkWidget { +public: + SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {} + + size_t getLabel(SkString* label = NULL) const; + size_t getLabel(char lable[] = NULL) const; + void setLabel(const SkString&); + void setLabel(const char label[]); + void setLabel(const char label[], size_t len); + +protected: + // called when the label changes + virtual void onLabelChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkString fLabel; + typedef SkWidget INHERITED; +}; + +class SkButtonWidget : public SkHasLabelWidget { +public: + SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {} + + enum State { + kOff_State, //!< XML: buttonState="off" + kOn_State, //!< XML: buttonState="on" + kUnknown_State //!< XML: buttonState="unknown" + }; + State getButtonState() const { return fState; } + void setButtonState(State); + +protected: + /** called when the label changes. default behavior is to inval the widget */ + virtual void onButtonStateChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + State fState; + typedef SkHasLabelWidget INHERITED; +}; + +class SkPushButtonWidget : public SkButtonWidget { +public: + SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {} + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + virtual bool onClick(Click* click); + +private: + typedef SkButtonWidget INHERITED; +}; + +class SkCheckBoxWidget : public SkButtonWidget { +public: + SkCheckBoxWidget(uint32_t flags = 0); + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + typedef SkButtonWidget INHERITED; +}; + +#include "SkTextBox.h" + +class SkStaticTextView : public SkView { +public: + SkStaticTextView(uint32_t flags = 0); + virtual ~SkStaticTextView(); + + enum Mode { + kFixedSize_Mode, + kAutoWidth_Mode, + kAutoHeight_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SkTextBox::SpacingAlign); + + void getMargin(SkPoint* margin) const; + void setMargin(SkScalar dx, SkScalar dy); + + size_t getText(SkString* text = NULL) const; + size_t getText(char text[] = NULL) const; + void setText(const SkString&); + void setText(const char text[]); + void setText(const char text[], size_t len); + + void getPaint(SkPaint*) const; + void setPaint(const SkPaint&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkPoint fMargin; + SkString fText; + SkPaint fPaint; + uint8_t fMode; + uint8_t fSpacingAlign; + + void computeSize(); + + typedef SkView INHERITED; +}; + +class SkBitmapView : public SkView { +public: + SkBitmapView(uint32_t flags = 0); + virtual ~SkBitmapView(); + + bool getBitmap(SkBitmap*) const; + void setBitmap(const SkBitmap*, bool viewOwnsPixels); + bool loadBitmapFromFile(const char path[]); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM&, const SkDOM::Node*); + +private: + SkBitmap fBitmap; + typedef SkView INHERITED; +}; + +///////////////////////////////////////////////////////////////////////////// + +class SkShader; +class SkInterpolator; + +class SkWidgetView : public SkView { +public: + SkWidgetView(uint32_t flags = 0); + virtual ~SkWidgetView(); + + static const char* GetEventType(); +}; + +class SkSliderView : public SkWidgetView { +public: + SkSliderView(uint32_t flags = 0); + + uint16_t getValue() const { return fValue; } + uint16_t getMax() const { return fMax; } + + void setMax(U16CPU max); + void setValue(U16CPU value); + +protected: + virtual void onDraw(SkCanvas*); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + virtual bool onClick(Click*); + +private: + uint16_t fValue, fMax; + + typedef SkWidgetView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +class SkHasLabelView : public SkView { +public: + void getLabel(SkString*) const; + void setLabel(const SkString&); + void setLabel(const char label[]); + +protected: + SkString fLabel; + + // called when the label changes + virtual void onLabelChange(); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); +}; + +class SkPushButtonView : public SkHasLabelView { +public: + SkPushButtonView(uint32_t flags = 0); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); +}; + +class SkCheckBoxView : public SkHasLabelView { +public: + SkCheckBoxView(uint32_t flags = 0); + + enum State { + kOff_State, + kOn_State, + kMaybe_State + }; + State getState() const { return fState; } + void setState(State); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + State fState; +}; + +class SkProgressView : public SkView { +public: + SkProgressView(uint32_t flags = 0); + virtual ~SkProgressView(); + + uint16_t getValue() const { return fValue; } + uint16_t getMax() const { return fMax; } + + void setMax(U16CPU max); + void setValue(U16CPU value); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + uint16_t fValue, fMax; + SkShader* fOnShader, *fOffShader; + SkInterpolator* fInterp; + bool fDoInterp; + + typedef SkView INHERITED; +}; + +class SkTextView : public SkView { +public: + SkTextView(uint32_t flags = 0); + virtual ~SkTextView(); + + enum AnimaDir { + kNeutral_AnimDir, + kForward_AnimDir, + kBackward_AnimDir, + kAnimDirCount + }; + + void getText(SkString*) const; + void setText(const SkString&, AnimaDir dir = kNeutral_AnimDir); + void setText(const char text[], AnimaDir dir = kNeutral_AnimDir); + void setText(const char text[], size_t len, AnimaDir dir = kNeutral_AnimDir); + + void getMargin(SkPoint* margin) const; + void setMargin(const SkPoint&); + + SkPaint& paint() { return fPaint; } + +protected: + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkString fText; + SkPaint fPaint; + SkPoint fMargin; + + class Interp; + Interp* fInterp; + bool fDoInterp; + // called by the other setText methods. This guy does not check for != + // before doing the assign, so the caller must check for us + void privSetText(const SkString&, AnimaDir dir); + + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////// + +class SkEvent; + +class SkListSource : public SkEventSink { +public: + virtual int countRows() = 0; + virtual void getRow(int index, SkString* left, SkString* right) = 0; + virtual SkEvent* getEvent(int index); + + static SkListSource* CreateFromDir(const char path[], const char suffix[], + const char targetPrefix[]); + static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node); +}; + +class SkListView : public SkWidgetView { +public: + SkListView(uint32_t flags = 0); + virtual ~SkListView(); + + SkScalar getRowHeight() const { return fRowHeight; } + void setRowHeight(SkScalar); + + /** Return the index of the selected row, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + + void moveSelectionUp(); + void moveSelectionDown(); + + enum Attr { + kBG_Attr, + kNormalText_Attr, + kHiliteText_Attr, + kHiliteCell_Attr, + kAttrCount + }; + SkPaint& paint(Attr); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + +#if 0 + enum Action { + kSelectionChange_Action, + kSelectionPicked_Action, + kActionCount + }; + /** If event is not null, it is retained by the view, and a copy + of the event will be posted to its listeners when the specified + action occurs. If event is null, then no event will be posted for + the specified action. + */ + void setActionEvent(Action, SkEvent* event); +#endif + +protected: + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkPaint fPaint[kAttrCount]; + SkListSource* fSource; + SkScalar fRowHeight; + int fCurrIndex; // logical index + int fScrollIndex; // logical index of top-most visible row + int fVisibleRowCount; + SkString* fStrCache; + + void dirtyStrCache(); + void ensureStrCache(int visibleCount); + + int logicalToVisualIndex(int index) const { return index - fScrollIndex; } + void invalSelection(); + bool getRowRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + + typedef SkWidgetView INHERITED; +}; + +////////////////////////////////////////////////////////// + +class SkGridView : public SkWidgetView { +public: + SkGridView(uint32_t flags = 0); + virtual ~SkGridView(); + + void getCellSize(SkPoint*) const; + void setCellSize(SkScalar x, SkScalar y); + + /** Return the index of the selected item, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + + void moveSelectionUp(); + void moveSelectionDown(); + + enum Attr { + kBG_Attr, + kHiliteCell_Attr, + kAttrCount + }; + SkPaint& paint(Attr); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + +protected: + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + +private: + SkView* fScrollBar; + SkPaint fPaint[kAttrCount]; + SkListSource* fSource; + int fCurrIndex; // logical index + + SkPoint fCellSize; + SkIPoint fVisibleCount; + + int logicalToVisualIndex(int index) const { return index; } + void invalSelection(); + bool getCellRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + + typedef SkWidgetView INHERITED; +}; + +#endif + diff --git a/include/views/SkWidgetViews.h b/include/views/SkWidgetViews.h new file mode 100644 index 0000000000..9b3a8160d5 --- /dev/null +++ b/include/views/SkWidgetViews.h @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkWidgetViews_DEFINED +#define SkWidgetViews_DEFINED + +#include "SkView.h" + + +enum SkWidgetEnum { + kBorder_WidgetEnum, //!< <sk-border> + kButton_WidgetEnum, //!< <sk-button> + kImage_WidgetEnum, //!< <sk-image> + kList_WidgetEnum, //!< <sk-list> + kProgress_WidgetEnum, //!< <sk-progress> + kScroll_WidgetEnum, //!< <sk-scroll> + kText_WidgetEnum, //!< <sk-text> + + kWidgetEnumCount +}; + +//determines which skin to use +enum SkinEnum { + kBorder_SkinEnum, + kButton_SkinEnum, + kProgress_SkinEnum, + kScroll_SkinEnum, + kStaticText_SkinEnum, + + kSkinEnumCount +}; + +#include "SkAnimator.h" +//used for inflates +const char* get_skin_enum_path(SkinEnum se); +void init_skin_anim(const char path[], SkAnimator* anim); +void init_skin_anim(SkinEnum se, SkAnimator* anim); +void init_skin_paint(SkinEnum se, SkPaint* paint); +void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint); + +/** Given an enum value, return an instance of the specified widget. + If the enum is out of range, returns null +*/ +SkView* SkWidgetFactory(SkWidgetEnum); +/** Given the inflate/element name of a widget, return an instance of + the specified widget, or null if name does not match any known + widget type. +*/ +SkView* SkWidgetFactory(const char name[]); + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkWidgetView : public SkView { +public: + SkWidgetView(); + + const char* getLabel() const; + void getLabel(SkString* label) const; + + void setLabel(const char[]); + void setLabel(const char[], size_t len); + void setLabel(const SkString&); + + SkEvent& event() { return fEvent; } + const SkEvent& event() const { return fEvent; } + + /** Returns true if the widget can post its event to its listeners. + */ + bool postWidgetEvent(); + + /** Returns the sinkID of the widgetview that posted the event, or 0 + */ + static SkEventSinkID GetWidgetEventSinkID(const SkEvent&); + +protected: + /** called when the label changes. override in subclasses. default action invals the view's bounds. + called with the old and new labels, before the label has actually changed. + */ + virtual void onLabelChange(const char oldLabel[], const char newLabel[]); + /** called before posting the event to our listeners. Override to add slots to the event + before posting. Return true to proceed with posting, or false to not post the event to any + listener. Note: the event passed in may not be the same as calling this->event(). + Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot + at modifying the event (and possibly returning false to abort). + */ + virtual bool onPrepareWidgetEvent(SkEvent* evt); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkString fLabel; + SkEvent fEvent; + + typedef SkView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkButtonView : public SkWidgetView { +public: + // inflate: "sk-button" + +protected: + // overrides + virtual bool onEvent(const SkEvent&); +private: + typedef SkWidgetView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkCheckButtonView : public SkWidgetView { +public: + SkCheckButtonView(); + + // inflate: "sk-checkbutton" + + enum CheckState { + kOff_CheckState, //!< inflate: check-state="off" + kOn_CheckState, //!< inflate: check-state="on" + kUnknown_CheckState //!< inflate: check-state="unknown" + }; + CheckState getCheckState() const { return (CheckState)fCheckState; } + void setCheckState(CheckState); + + /** use this to extract the CheckState from an event (i.e. one that as posted + by a SkCheckButtonView). Returns true if the proper slot was present in the event, + and sets state to that value. If no proper slot is found, returns false and does not + modify state. + */ + static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state); + +protected: + // called when the check-state is about to change, but before it actually has + virtual void onCheckStateChange(CheckState oldState, CheckState newState); + + // overrides + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + virtual bool onPrepareWidgetEvent(SkEvent* evt); + +private: + uint8_t fCheckState; + + typedef SkWidgetView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkTextBox.h" + +class SkStaticTextView : public SkView { +public: + SkStaticTextView(); + virtual ~SkStaticTextView(); + + enum Mode { + kFixedSize_Mode, + kAutoWidth_Mode, + kAutoHeight_Mode, + + kModeCount + }; + Mode getMode() const { return (Mode)fMode; } + void setMode(Mode); + + SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } + void setSpacingAlign(SkTextBox::SpacingAlign); + + void getMargin(SkPoint* margin) const; + void setMargin(SkScalar dx, SkScalar dy); + + size_t getText(SkString* text = NULL) const; + size_t getText(char text[] = NULL) const; + void setText(const SkString&); + void setText(const char text[]); + void setText(const char text[], size_t len); + + void getPaint(SkPaint*) const; + void setPaint(const SkPaint&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); + +private: + SkPoint fMargin; + SkString fText; + SkPaint fPaint; + uint8_t fMode; + uint8_t fSpacingAlign; + + void computeSize(); + + typedef SkView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +class SkAnimator; +class SkListSource; +class SkScrollBarView; + +class SkListView : public SkWidgetView { +public: + SkListView(); + virtual ~SkListView(); + + bool hasScrollBar() const { return fScrollBar != NULL; } + void setHasScrollBar(bool); + + /** Return the number of visible rows + */ + int getVisibleRowCount() const { return fVisibleRowCount; } + /** Return the index of the selected row, or -1 if none + */ + int getSelection() const { return fCurrIndex; } + /** Set the index of the selected row, or -1 for none + */ + void setSelection(int); + /** If possible, move the selection up and return true, + else do nothing and return false + If nothing is selected, select the last item (unless there are no items). + */ + bool moveSelectionUp(); + /** If possible, move the selection down and return true, + else do nothing and return false. + If nothing is selected, select the first item (unless there are no items). + */ + bool moveSelectionDown(); + + SkListSource* getListSource() const { return fSource; } + SkListSource* setListSource(SkListSource*); + + /** Call this in your event handler. If the specified event is from a SkListView, + then it returns the index of the selected item in this list, otherwise it + returns -1 + */ + static int GetWidgetEventListIndex(const SkEvent&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + virtual void onSizeChange(); + virtual bool onEvent(const SkEvent&); + virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); + virtual bool onPrepareWidgetEvent(SkEvent*); + +private: + enum DirtyFlags { + kAnimCount_DirtyFlag = 0x01, + kAnimContent_DirtyFlag = 0x02 + }; + void dirtyCache(unsigned dirtyFlags); + bool ensureCache(); + + int logicalToVisualIndex(int index) const { return index - fScrollIndex; } + void invalSelection(); + SkScalar getContentWidth() const; + bool getRowRect(int index, SkRect*) const; + void ensureSelectionIsVisible(); + void ensureVisibleRowCount(); + + struct BindingRec; + + enum Heights { + kNormal_Height, + kSelected_Height + }; + SkListSource* fSource; + SkScrollBarView* fScrollBar; + SkAnimator* fAnims; + BindingRec* fBindings; + SkString fSkinName; + SkScalar fHeights[2]; + int16_t fScrollIndex, fCurrIndex; + uint16_t fVisibleRowCount, fBindingCount; + SkBool8 fAnimContentDirty; + SkBool8 fAnimFocusDirty; + + typedef SkWidgetView INHERITED; +}; + +class SkListSource : public SkRefCnt { +public: + virtual int countFields(); + virtual void getFieldName(int index, SkString* field); + /** Return the index of the named field, or -1 if not found */ + virtual int findFieldIndex(const char field[]); + + virtual int countRecords(); + virtual void getRecord(int rowIndex, int fieldIndex, SkString* data); + + virtual bool prepareWidgetEvent(SkEvent*, int rowIndex); + + static SkListSource* Factory(const char name[]); +}; + +#endif diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h new file mode 100644 index 0000000000..fd4ce0a91c --- /dev/null +++ b/include/views/SkWindow.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkWindow_DEFINED +#define SkWindow_DEFINED + +#include "SkView.h" +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "SkRegion.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkTDArray.h" + +#ifdef SK_BUILD_FOR_WINCEx + #define SHOW_FPS +#endif +//#define USE_GX_SCREEN + +class SkCanvas; + +class SkOSMenu; + +class SkWindow : public SkView { +public: + SkWindow(); + virtual ~SkWindow(); + + const SkBitmap& getBitmap() const { return fBitmap; } + + void setConfig(SkBitmap::Config); + void resize(int width, int height, SkBitmap::Config config = SkBitmap::kNo_Config); + void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b); + void eraseRGB(U8CPU r, U8CPU g, U8CPU b); + + bool isDirty() const { return !fDirtyRgn.isEmpty(); } + bool update(SkIRect* updateArea, SkCanvas* = NULL); + // does not call through to onHandleInval(), but does force the fDirtyRgn + // to be wide open. Call before update() to ensure we redraw everything. + void forceInvalAll(); + // return the bounds of the dirty/inval rgn, or [0,0,0,0] if none + const SkIRect& getDirtyBounds() const { return fDirtyRgn.getBounds(); } + + bool handleClick(int x, int y, Click::State); + bool handleChar(SkUnichar); + bool handleKey(SkKey); + bool handleKeyUp(SkKey); + bool handleMenu(uint32_t os_cmd); + + void addMenu(SkOSMenu*); + + const char* getTitle() const { return fTitle.c_str(); } + void setTitle(const char title[]); + + const SkMatrix& getMatrix() const { return fMatrix; } + void setMatrix(const SkMatrix&); + void preConcat(const SkMatrix&); + void postConcat(const SkMatrix&); + +protected: + virtual bool onEvent(const SkEvent&); + virtual bool onDispatchClick(int x, int y, Click::State); + // called if part of our bitmap is invalidated + virtual void onHandleInval(const SkIRect&); + virtual bool onHandleChar(SkUnichar); + virtual bool onHandleKey(SkKey); + virtual bool onHandleKeyUp(SkKey); + virtual void onAddMenu(const SkOSMenu*) {} + virtual void onSetTitle(const char title[]) {} + + // overrides from SkView + virtual bool handleInval(const SkRect*); + virtual bool onGetFocusView(SkView** focus) const; + virtual bool onSetFocusView(SkView* focus); + +private: + SkBitmap::Config fConfig; + SkBitmap fBitmap; + SkRegion fDirtyRgn; + Click* fClick; // to track clicks + + SkTDArray<SkOSMenu*> fMenus; + + SkView* fFocusView; + bool fWaitingOnInval; + + SkString fTitle; + SkMatrix fMatrix; + + typedef SkView INHERITED; +}; + +/////////////////////////////////////////////////////////// + +#ifdef SK_USE_WXWIDGETS + #include "SkOSWindow_wxwidgets.h" +#elif defined(SK_BUILD_FOR_MAC) + #include "SkOSWindow_Mac.h" +#elif defined(SK_BUILD_FOR_WIN) + #include "SkOSWindow_Win.h" +#elif defined(ANDROID) + #include "SkOSWindow_Android.h" +#elif defined(SK_BUILD_FOR_UNIX) + #include "SkOSWindow_Unix.h" +#elif defined(SK_BUILD_FOR_SDL) + #include "SkOSWindow_SDL.h" +#elif defined(SK_BUILD_FOR_IOS) + #include "SkOSWindow_iOS.h" +#endif + +#endif + diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp new file mode 100644 index 0000000000..c829b69b60 --- /dev/null +++ b/samplecode/ClockFaceView.cpp @@ -0,0 +1,255 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTypeface.h" +#include "SkAvoidXfermode.h" + +static inline SkPMColor rgb2gray(SkPMColor c) +{ + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + unsigned x = (r * 5 + g * 7 + b * 4) >> 4; + + return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT)); +} + +class SkGrayScaleColorFilter : public SkColorFilter { +public: + virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) + { + for (int i = 0; i < count; i++) + result[i] = rgb2gray(src[i]); + } +}; + +class SkChannelMaskColorFilter : public SkColorFilter { +public: + SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) + { + fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask); + } + + virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) + { + SkPMColor mask = fMask; + for (int i = 0; i < count; i++) + result[i] = src[i] & mask; + } + +private: + SkPMColor fMask; +}; + +/////////////////////////////////////////////////////////// + +#include "SkGradientShader.h" +#include "SkLayerRasterizer.h" +#include "SkBlurMaskFilter.h" + +#include "Sk2DPathEffect.h" + +class Dot2DPathEffect : public Sk2DPathEffect { +public: + Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix, + SkTDArray<SkPoint>* pts) + : Sk2DPathEffect(matrix), fRadius(radius), fPts(pts) {} + + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + this->INHERITED::flatten(buffer); + + buffer.writeScalar(fRadius); + } + virtual Factory getFactory() { return CreateProc; } + +protected: + virtual void begin(const SkIRect& uvBounds, SkPath* dst) { + if (fPts) { + fPts->reset(); + } + this->INHERITED::begin(uvBounds, dst); + } +// virtual void end(SkPath* dst) {} + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) + { + if (fPts) { + *fPts->append() = loc; + } + dst->addCircle(loc.fX, loc.fY, fRadius); + } + + Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) + { + fRadius = buffer.readScalar(); + fPts = NULL; + } +private: + SkScalar fRadius; + SkTDArray<SkPoint>* fPts; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return new Dot2DPathEffect(buffer); + } + + typedef Sk2DPathEffect INHERITED; +}; + +class InverseFillPE : public SkPathEffect { +public: + InverseFillPE() {} + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) { + *dst = src; + dst->setFillType(SkPath::kInverseWinding_FillType); + return true; + } + virtual Factory getFactory() { return Factory; } +protected: +// InverseFillPE(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {} +private: + static SkFlattenable* Factory(SkFlattenableReadBuffer& buffer) { + return new InverseFillPE; + } + typedef SkPathEffect INHERITED; +}; + +static SkPathEffect* makepe(float interp, SkTDArray<SkPoint>* pts) { + SkMatrix lattice; + SkScalar rad = 3 + SkIntToScalar(4) * (1 - interp); + lattice.setScale(rad*2, rad*2, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + return new Dot2DPathEffect(rad, lattice, pts); +} + +static void r7(SkLayerRasterizer* rast, SkPaint& p, SkScalar interp) { + p.setPathEffect(makepe(interp, NULL))->unref(); + rast->addLayer(p); +#if 0 + p.setPathEffect(new InverseFillPE())->unref(); + p.setXfermodeMode(SkXfermode::kSrcIn_Mode); + p.setXfermodeMode(SkXfermode::kClear_Mode); + p.setAlpha((1 - interp) * 255); + rast->addLayer(p); +#endif +} + +typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&); + +#include "SkXfermode.h" + +static void apply_shader(SkPaint* paint, float scale) +{ + SkPaint p; + SkLayerRasterizer* rast = new SkLayerRasterizer; + + p.setAntiAlias(true); + r7(rast, p, scale); + paint->setRasterizer(rast)->unref(); + + paint->setColor(SK_ColorBLUE); +} + +class ClockFaceView : public SkView { + SkTypeface* fFace; + SkScalar fInterp; + SkScalar fDx; +public: + ClockFaceView() + { + fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb"); + fInterp = 0; + fDx = SK_Scalar1/64; + } + + virtual ~ClockFaceView() + { + SkSafeUnref(fFace); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) + { + if (SampleCode::TitleQ(*evt)) + { + SampleCode::TitleR(evt, "Text Effects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) + { +// canvas->drawColor(0xFFDDDDDD); + canvas->drawColor(SK_ColorWHITE); + } + + static void drawdots(SkCanvas* canvas, const SkPaint& orig) { + SkTDArray<SkPoint> pts; + SkPathEffect* pe = makepe(0, &pts); + + SkScalar width = -1; + SkPath path, dstPath; + orig.getTextPath("9", 1, 0, 0, &path); + pe->filterPath(&dstPath, path, &width); + + SkPaint p; + p.setAntiAlias(true); + p.setStrokeWidth(10); + p.setColor(SK_ColorRED); + canvas->drawPoints(SkCanvas::kPoints_PointMode, pts.count(), pts.begin(), + p); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + SkScalar x = SkIntToScalar(20); + SkScalar y = SkIntToScalar(300); + SkPaint paint; + + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(240)); + paint.setTypeface(SkTypeface::CreateFromName("sans-serif", + SkTypeface::kBold)); + + SkString str("9"); + + paint.setTypeface(fFace); + + apply_shader(&paint, fInterp); + canvas->drawText(str.c_str(), str.size(), x, y, paint); + + // drawdots(canvas, paint); + + if (false) { + fInterp += fDx; + if (fInterp > 1) { + fInterp = 1; + fDx = -fDx; + } else if (fInterp < 0) { + fInterp = 0; + fDx = -fDx; + } + this->inval(NULL); + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ClockFaceView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp new file mode 100644 index 0000000000..2ae21196e0 --- /dev/null +++ b/samplecode/OverView.cpp @@ -0,0 +1,94 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkView.h" + +static const int N = 8; +const SkScalar W = SkIntToScalar(640); +const SkScalar H = SkIntToScalar(480); + +class OverView : public SkView { +public: + OverView(int count, const SkViewFactory factories[]); + virtual ~OverView(); + +protected: + virtual bool onEvent(const SkEvent&); + virtual void onSizeChange(); + + virtual void onDraw(SkCanvas* canvas) { + canvas->drawColor(SK_ColorLTGRAY); + } + + virtual SkCanvas* beforeChildren(SkCanvas*); + + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Overview"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual bool onSendClickToChildren(SkScalar x, SkScalar y) { + return false; + } + + virtual Click* onFindClickHandler(SkScalar x, SkScalar y) { + int ix = (int)(SkScalarDiv(x * N, W)); + int iy = (int)(SkScalarDiv(y * N, H)); + if (ix >= 0 && iy >= 0) { + SkEvent evt("set-curr-index"); + evt.setFast32(iy * N + ix); + this->sendEventToParents(evt); + } + return NULL; + } + +private: + int fCount; + const SkViewFactory* fFactories; + + typedef SkView INHERITED; +}; + +SkView* create_overview(int count, const SkViewFactory factories[]); +SkView* create_overview(int count, const SkViewFactory factories[]) { + return SkNEW_ARGS(OverView, (count, factories)); +}; + +OverView::OverView(int count, const SkViewFactory factories[]) { + fCount = count; + fFactories = factories; +} + +OverView::~OverView() { +} + +bool OverView::onEvent(const SkEvent& evt) { + return this->INHERITED::onEvent(evt); +} + +void OverView::onSizeChange() { + this->detachAllChildren(); + + SkScalar locX = 0; + SkScalar locY = 0; + for (int i = 0; i < fCount; i++) { + SkView* view = fFactories[i](); + view->setVisibleP(true); + this->attachChildToBack(view)->unref(); + view->setLoc(locX, locY); + view->setSize(W, H); + locX += W; + if ((i % N) == N - 1) { + locY += H; + locX = 0; + } + } +} + +SkCanvas* OverView::beforeChildren(SkCanvas* canvas) { + canvas->scale(SK_Scalar1 / N, SK_Scalar1 / N); + return canvas; +} + diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp new file mode 100644 index 0000000000..34a33b0748 --- /dev/null +++ b/samplecode/SampleAARects.cpp @@ -0,0 +1,191 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkShader.h" + +static SkBitmap createBitmap(int n) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n); + bitmap.allocPixels(); + bitmap.eraseColor(SK_ColorGREEN); + + SkCanvas canvas(bitmap); + SkRect r; + r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n)); + SkPaint paint; + paint.setAntiAlias(true); + + paint.setColor(SK_ColorRED); + canvas.drawOval(r, paint); + paint.setColor(SK_ColorBLUE); + paint.setStrokeWidth(SkIntToScalar(n)/15); + paint.setStyle(SkPaint::kStroke_Style); + canvas.drawLine(0, 0, r.fRight, r.fBottom, paint); + canvas.drawLine(0, r.fBottom, r.fRight, 0, paint); + + return bitmap; +} + +class AARectView : public SampleView { + SkBitmap fBitmap; + enum { + N = 64 + }; +public: + AARectView() { + fBitmap = createBitmap(N); + + fWidth = N; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "AA Rects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + SkPaint bluePaint; + bluePaint.setARGB(0xff, 0x0, 0x0, 0xff); + SkPaint bmpPaint; + SkShader* bmpShader = SkShader::CreateBitmapShader(fBitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + bmpPaint.setShader(bmpShader); + bmpShader->unref(); + + bluePaint.setStrokeWidth(3); + bmpPaint.setStrokeWidth(3); + + SkPaint paints[] = { bluePaint, bmpPaint }; + + SkRect rect; + + SkScalar dx = SkIntToScalar(80); + SkScalar dy = SkIntToScalar(100); + SkMatrix matrix; + for (size_t p = 0; p < SK_ARRAY_COUNT(paints); ++p) { + for (int stroke = 0; stroke < 2; ++stroke) { + paints[p].setStyle(stroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); + for (int a = 0; a < 3; ++ a) { + paints[p].setAntiAlias(a > 0); + paints[p].setAlpha(a > 1 ? 0x80 : 0xff); + + canvas->save(); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.f), + SkFloatToScalar(0.f), + SkFloatToScalar(40.f), + SkFloatToScalar(40.f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(40.5f), + SkFloatToScalar(40.5f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(40.f), + SkFloatToScalar(40.f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + rect = SkRect::MakeLTRB(SkFloatToScalar(0.75f), + SkFloatToScalar(0.75f), + SkFloatToScalar(40.75f), + SkFloatToScalar(40.75f)); + canvas->drawRect(rect, paints[p]); + canvas->translate(dx, 0); + + canvas->save(); + canvas->translate(SkFloatToScalar(.33f), SkFloatToScalar(.67f)); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f), + SkFloatToScalar(0.0f), + SkFloatToScalar(40.0f), + SkFloatToScalar(40.0f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + matrix.setRotate(SkFloatToScalar(45.f)); + canvas->concat(matrix); + canvas->translate(SkFloatToScalar(20.0f / sqrtf(2.f)), + SkFloatToScalar(20.0f / sqrtf(2.f))); + rect = SkRect::MakeLTRB(SkFloatToScalar(-20.0f), + SkFloatToScalar(-20.0f), + SkFloatToScalar(20.0f), + SkFloatToScalar(20.0f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + canvas->rotate(SkFloatToScalar(90.f)); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f), + SkFloatToScalar(0.0f), + SkFloatToScalar(40.0f), + SkFloatToScalar(-40.0f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + canvas->rotate(SkFloatToScalar(90.f)); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(40.5f), + SkFloatToScalar(-40.5f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + matrix.setScale(SkFloatToScalar(-1.f), SkFloatToScalar(-1.f)); + canvas->concat(matrix); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f), + SkFloatToScalar(0.5f), + SkFloatToScalar(-40.5f), + SkFloatToScalar(-40.5f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->save(); + matrix.setScale(SkFloatToScalar(2.1f), SkFloatToScalar(4.1f)); + canvas->concat(matrix); + rect = SkRect::MakeLTRB(SkFloatToScalar(0.1f), + SkFloatToScalar(0.1f), + SkFloatToScalar(19.1f), + SkFloatToScalar(9.1f)); + canvas->drawRect(rect, paints[p]); + canvas->restore(); + canvas->translate(dx, 0); + + canvas->restore(); + canvas->translate(0, dy); + } + } + } + } + +private: + int fWidth; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new AARectView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp new file mode 100644 index 0000000000..abbf8f991d --- /dev/null +++ b/samplecode/SampleAll.cpp @@ -0,0 +1,715 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkView.h" +#include "Sk1DPathEffect.h" +#include "Sk2DPathEffect.h" +#include "SkAvoidXfermode.h" +#include "SkBlurMaskFilter.h" +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkCornerPathEffect.h" +#include "SkDashPathEffect.h" +#include "SkDiscretePathEffect.h" +#include "SkEmbossMaskFilter.h" +#include "SkGradientShader.h" +#include "SkImageDecoder.h" +#include "SkLayerRasterizer.h" +#include "SkMath.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkComposeShader.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkPicture.h" +#include "SkRandom.h" +#include "SkTransparentShader.h" +#include "SkTypeface.h" +#include "SkUnitMappers.h" +#include "SkUtils.h" +#include "SkXfermode.h" + +#include <math.h> + +static inline SkPMColor rgb2gray(SkPMColor c) { + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + unsigned x = (r * 5 + g * 7 + b * 4) >> 4; + + return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT)); +} + +class SkGrayScaleColorFilter : public SkColorFilter { +public: + virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) { + for (int i = 0; i < count; i++) + result[i] = rgb2gray(src[i]); + } +}; + +class SkChannelMaskColorFilter : public SkColorFilter { +public: + SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) { + fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask); + } + + virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) { + SkPMColor mask = fMask; + for (int i = 0; i < count; i++) { + result[i] = src[i] & mask; + } + } + +private: + SkPMColor fMask; +}; + +/////////////////////////////////////////////////////////// + +static void r0(SkLayerRasterizer* rast, SkPaint& p) { + p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3), + SkBlurMaskFilter::kNormal_BlurStyle))->unref(); + rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); + + p.setMaskFilter(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); + + p.setAlpha(0x11); + p.setStyle(SkPaint::kFill_Style); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + rast->addLayer(p); +} + +static void r1(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + p.setAlpha(0x40); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*2); + rast->addLayer(p); +} + +static void r2(SkLayerRasterizer* rast, SkPaint& p) { + p.setStyle(SkPaint::kStrokeAndFill_Style); + p.setStrokeWidth(SK_Scalar1*4); + rast->addLayer(p); + + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*3/2); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); +} + +static void r3(SkLayerRasterizer* rast, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*3); + rast->addLayer(p); + + p.setAlpha(0x20); + p.setStyle(SkPaint::kFill_Style); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + rast->addLayer(p); +} + +static void r4(SkLayerRasterizer* rast, SkPaint& p) { + p.setAlpha(0x60); + rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); + + p.setAlpha(0xFF); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2); + + p.setXfermode(NULL); + rast->addLayer(p); +} + +static void r5(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref(); + p.setXfermodeMode(SkXfermode::kSrcOut_Mode); + rast->addLayer(p); +} + +static void r6(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + p.setAntiAlias(false); + SkLayerRasterizer* rast2 = new SkLayerRasterizer; + r5(rast2, p); + p.setRasterizer(rast2)->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); +} + +class Dot2DPathEffect : public Sk2DPathEffect { +public: + Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix) + : Sk2DPathEffect(matrix), fRadius(radius) {} + + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + buffer.writeScalar(fRadius); + } + virtual Factory getFactory() { return CreateProc; } + +protected: + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) { + dst->addCircle(loc.fX, loc.fY, fRadius); + } + + Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) { + fRadius = buffer.readScalar(); + } +private: + SkScalar fRadius; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return new Dot2DPathEffect(buffer); + } + + typedef Sk2DPathEffect INHERITED; +}; + +static void r7(SkLayerRasterizer* rast, SkPaint& p) { + SkMatrix lattice; + lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref(); + rast->addLayer(p); +} + +static void r8(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + SkMatrix lattice; + lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); + + p.setPathEffect(NULL); + p.setXfermode(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); +} + +class Line2DPathEffect : public Sk2DPathEffect { +public: + Line2DPathEffect(SkScalar width, const SkMatrix& matrix) + : Sk2DPathEffect(matrix), fWidth(width) {} + + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) { + if (this->INHERITED::filterPath(dst, src, width)) { + *width = fWidth; + return true; + } + return false; + } + + virtual Factory getFactory() { return CreateProc; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fWidth); + } +protected: + virtual void nextSpan(int u, int v, int ucount, SkPath* dst) { + if (ucount > 1) { + SkPoint src[2], dstP[2]; + + src[0].set(SkIntToScalar(u) + SK_ScalarHalf, + SkIntToScalar(v) + SK_ScalarHalf); + src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, + SkIntToScalar(v) + SK_ScalarHalf); + this->getMatrix().mapPoints(dstP, src, 2); + + dst->moveTo(dstP[0]); + dst->lineTo(dstP[1]); + } + } + + Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) { + fWidth = buffer.readScalar(); + } + +private: + SkScalar fWidth; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return new Line2DPathEffect(buffer); } + + typedef Sk2DPathEffect INHERITED; +}; + +static void r9(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + SkMatrix lattice; + lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0); + lattice.postRotate(SkIntToScalar(30), 0, 0); + p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); + + p.setPathEffect(NULL); + p.setXfermode(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); +} + +typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&); + +static const raster_proc gRastProcs[] = { + r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 +}; + +static const struct { + SkColor fMul, fAdd; +} gLightingColors[] = { + { 0x808080, 0x800000 }, // general case + { 0x707070, 0x707070 }, // no-pin case + { 0xFFFFFF, 0x800000 }, // just-add case + { 0x808080, 0x000000 }, // just-mul case + { 0xFFFFFF, 0x000000 } // identity case +}; + +static unsigned color_dist16(uint16_t a, uint16_t b) { + unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b)); + unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b)); + unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b)); + + return SkMax32(dr, SkMax32(dg, db)); +} + +static unsigned scale_dist(unsigned dist, unsigned scale) { + dist >>= 6; + dist = (dist << 2) | dist; + dist = (dist << 4) | dist; + return dist; + +// return SkAlphaMul(dist, scale); +} + +static void apply_shader(SkPaint* paint, int index) { + raster_proc proc = gRastProcs[index]; + if (proc) { + SkPaint p; + SkLayerRasterizer* rast = new SkLayerRasterizer; + + p.setAntiAlias(true); + proc(rast, p); + paint->setRasterizer(rast)->unref(); + } + +#if 1 + SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 }; + paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref(); + paint->setColor(SK_ColorBLUE); +#endif +} + +class DemoView : public SampleView { +public: + DemoView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Demo"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + + void makePath(SkPath& path) { + path.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(20), + SkPath::kCCW_Direction); + for (int index = 0; index < 10; index++) { + SkScalar x = SkFloatToScalar(cos(index / 10.0f * 2 * 3.1415925358f)); + SkScalar y = SkFloatToScalar(sin(index / 10.0f * 2 * 3.1415925358f)); + x *= index & 1 ? 7 : 14; + y *= index & 1 ? 7 : 14; + x += SkIntToScalar(20); + y += SkIntToScalar(20); + if (index == 0) + path.moveTo(x, y); + else + path.lineTo(x, y); + } + path.close(); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->save(); + drawPicture(canvas, 0); + canvas->restore(); + + { + SkPicture picture; + SkCanvas* record = picture.beginRecording(320, 480); + drawPicture(record, 120); + canvas->translate(0, SkIntToScalar(120)); + + SkRect clip; + clip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160)); + do { + canvas->save(); + canvas->clipRect(clip); + picture.draw(canvas); + canvas->restore(); + if (clip.fRight < SkIntToScalar(320)) + clip.offset(SkIntToScalar(160), 0); + else if (clip.fBottom < SkIntToScalar(480)) + clip.offset(-SkIntToScalar(320), SkIntToScalar(160)); + else + break; + } while (true); + } + } + + void drawPicture(SkCanvas* canvas, int spriteOffset) { + SkMatrix matrix; matrix.reset(); + SkPaint paint; + SkPath path; + SkPoint start = {0, 0}; + SkPoint stop = { SkIntToScalar(40), SkIntToScalar(40) }; + SkRect rect = {0, 0, SkIntToScalar(40), SkIntToScalar(40) }; + SkRect rect2 = {0, 0, SkIntToScalar(65), SkIntToScalar(20) }; + SkScalar left = 0, top = 0, x = 0, y = 0; + size_t index; + + char ascii[] = "ascii..."; + size_t asciiLength = sizeof(ascii) - 1; + char utf8[] = "utf8" "\xe2\x80\xa6"; + short utf16[] = {'u', 't', 'f', '1', '6', 0x2026 }; + short utf16simple[] = {'u', 't', 'f', '1', '6', '!' }; + + makePath(path); + SkTDArray<SkPoint>(pos); + pos.setCount(asciiLength); + for (index = 0; index < asciiLength; index++) + pos[index].set(SkIntToScalar(index * 10), SkIntToScalar(index * 2)); + SkTDArray<SkPoint>(pos2); + pos2.setCount(asciiLength); + for (index = 0; index < asciiLength; index++) + pos2[index].set(SkIntToScalar(index * 10), SkIntToScalar(20)); + + // shaders + SkPoint linearPoints[] = { { 0, 0, }, { SkIntToScalar(40), SkIntToScalar(40) } }; + SkColor linearColors[] = { SK_ColorRED, SK_ColorBLUE }; + SkScalar* linearPos = NULL; + int linearCount = 2; + SkShader::TileMode linearMode = SkShader::kMirror_TileMode; + SkUnitMapper* linearMapper = new SkDiscreteMapper(3); + SkAutoUnref unmapLinearMapper(linearMapper); + SkShader* linear = SkGradientShader::CreateLinear(linearPoints, + linearColors, linearPos, linearCount, linearMode, linearMapper); + + SkPoint radialCenter = { SkIntToScalar(25), SkIntToScalar(25) }; + SkScalar radialRadius = SkIntToScalar(25); + SkColor radialColors[] = { SK_ColorGREEN, SK_ColorGRAY, SK_ColorRED }; + SkScalar radialPos[] = { 0, SkIntToScalar(3) / 5, SkIntToScalar(1)}; + int radialCount = 3; + SkShader::TileMode radialMode = SkShader::kRepeat_TileMode; + SkUnitMapper* radialMapper = new SkCosineMapper(); + SkAutoUnref unmapRadialMapper(radialMapper); + SkShader* radial = SkGradientShader::CreateRadial(radialCenter, + radialRadius, radialColors, radialPos, radialCount, + radialMode, radialMapper); + + SkTransparentShader* transparentShader = new SkTransparentShader(); + SkEmbossMaskFilter::Light light; + light.fDirection[0] = SK_Scalar1/2; + light.fDirection[1] = SK_Scalar1/2; + light.fDirection[2] = SK_Scalar1/3; + light.fAmbient = 0x48; + light.fSpecular = 0x80; + SkScalar radius = SkIntToScalar(12)/5; + SkEmbossMaskFilter* embossFilter = new SkEmbossMaskFilter(light, + radius); + + SkXfermode* xfermode = SkXfermode::Create(SkXfermode::kXor_Mode); + SkColorFilter* lightingFilter = SkColorFilter::CreateLightingFilter( + 0xff89bc45, 0xff112233); + + canvas->save(); + canvas->translate(SkIntToScalar(0), SkIntToScalar(5)); + paint.setFlags(SkPaint::kAntiAlias_Flag | SkPaint::kFilterBitmap_Flag); + // !!! draw through a clip + paint.setColor(SK_ColorLTGRAY); + paint.setStyle(SkPaint::kFill_Style); + SkRect clip = {0, 0, SkIntToScalar(320), SkIntToScalar(120)}; + canvas->clipRect(clip); + paint.setShader(SkShader::CreateBitmapShader(fTx, + SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode))->unref(); + canvas->drawPaint(paint); + canvas->save(); + + // line (exercises xfermode, colorShader, colorFilter, filterShader) + paint.setColor(SK_ColorGREEN); + paint.setStrokeWidth(SkIntToScalar(10)); + paint.setStyle(SkPaint::kStroke_Style); + paint.setXfermode(xfermode)->unref(); + paint.setColorFilter(lightingFilter)->unref(); + canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green + paint.setXfermode(NULL); + paint.setColorFilter(NULL); + + // rectangle + paint.setStyle(SkPaint::kFill_Style); + canvas->translate(SkIntToScalar(50), 0); + paint.setColor(SK_ColorYELLOW); + paint.setShader(linear)->unref(); + paint.setPathEffect(pathEffectTest())->unref(); + canvas->drawRect(rect, paint); + paint.setPathEffect(NULL); + + // circle w/ emboss & transparent (exercises 3dshader) + canvas->translate(SkIntToScalar(50), 0); + paint.setMaskFilter(embossFilter)->unref(); + canvas->drawOval(rect, paint); + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + paint.setShader(transparentShader)->unref(); + canvas->drawOval(rect, paint); + canvas->translate(0, SkIntToScalar(-10)); + + // path + canvas->translate(SkIntToScalar(50), 0); + paint.setColor(SK_ColorRED); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(5)); + paint.setShader(radial)->unref(); + paint.setMaskFilter(NULL); + canvas->drawPath(path, paint); + + paint.setShader(NULL); + // bitmap, sprite + canvas->translate(SkIntToScalar(50), 0); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawBitmap(fBug, left, top, &paint); + canvas->translate(SkIntToScalar(30), 0); + canvas->drawSprite(fTb, + SkScalarRound(canvas->getTotalMatrix().getTranslateX()), + spriteOffset + 10, &paint); + + canvas->translate(-SkIntToScalar(30), SkIntToScalar(30)); + paint.setShader(shaderTest())->unref(); // test compose shader + canvas->drawRect(rect2, paint); + paint.setShader(NULL); + + canvas->restore(); + // text + canvas->translate(0, SkIntToScalar(60)); + canvas->save(); + paint.setColor(SK_ColorGRAY); + canvas->drawPosText(ascii, asciiLength, pos.begin(), paint); + canvas->drawPosText(ascii, asciiLength, pos2.begin(), paint); + + canvas->translate(SkIntToScalar(50), 0); + paint.setColor(SK_ColorCYAN); + canvas->drawText(utf8, sizeof(utf8) - 1, x, y, paint); + + canvas->translate(SkIntToScalar(30), 0); + paint.setColor(SK_ColorMAGENTA); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + matrix.setTranslate(SkIntToScalar(10), SkIntToScalar(10)); + canvas->drawTextOnPath((void*) utf16, sizeof(utf16), path, &matrix, paint); + canvas->translate(0, SkIntToScalar(20)); + canvas->drawTextOnPath((void*) utf16simple, sizeof(utf16simple), path, &matrix, paint); + canvas->restore(); + + canvas->translate(0, SkIntToScalar(60)); + paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); + canvas->restore(); + } + + /* +./SkColorFilter.h:25:class SkColorFilter : public SkFlattenable { -- abstract + static SkColorFilter* CreatXfermodeFilter() *** untested *** + static SkColorFilter* CreatePorterDuffFilter() *** untested *** + static SkColorFilter* CreateLightingFilter() -- tested +./SkDrawLooper.h:9:class SkDrawLooper : public SkFlattenable { -- virtually abstract + ./SkBlurDrawLooper.h:9:class SkBlurDrawLooper : public SkDrawLooper { *** untested *** +./SkMaskFilter.h:41:class SkMaskFilter : public SkFlattenable { -- abstract chmod +w .h + ./SkEmbossMaskFilter.h:27:class SkEmbossMaskFilter : public SkMaskFilter { -- tested +./SkPathEffect.h:33:class SkPathEffect : public SkFlattenable { -- abstract + ./Sk1DPathEffect.h:27:class Sk1DPathEffect : public SkPathEffect { -- abstract + ./Sk1DPathEffect.h:48:class SkPath1DPathEffect : public Sk1DPathEffect { -- tested + ./Sk2DPathEffect.h:25:class Sk2DPathEffect : public SkPathEffect { *** untested *** + ./SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect { *** untested *** + ./SkDashPathEffect.h:27:class SkDashPathEffect : public SkPathEffect { + ./SkDiscretePathEffect.h:27:class SkDiscretePathEffect : public SkPathEffect { + ./SkPaint.h:760:class SkStrokePathEffect : public SkPathEffect { + ./SkPathEffect.h:58:class SkPairPathEffect : public SkPathEffect { + ./SkPathEffect.h:78:class SkComposePathEffect : public SkPairPathEffect { + ./SkPathEffect.h:114:class SkSumPathEffect : public SkPairPathEffect { +./SkRasterizer.h:29:class SkRasterizer : public SkFlattenable { + ./SkLayerRasterizer.h:27:class SkLayerRasterizer : public SkRasterizer { +./SkShader.h:36:class SkShader : public SkFlattenable { + ./SkColorFilter.h:59:class SkFilterShader : public SkShader { + ./SkColorShader.h:26:class SkColorShader : public SkShader { + ./SkShaderExtras.h:31:class SkComposeShader : public SkShader { + ./SkTransparentShader.h:23:class SkTransparentShader : public SkShader { +./SkUnitMapper.h:24:class SkUnitMapper : public SkFlattenable { + ./SkUnitMapper.h:33:class SkDiscreteMapper : public SkUnitMapper { + ./SkUnitMapper.h:51:class SkFlipCosineMapper : public SkUnitMapper { +./SkXfermode.h:32:class SkXfermode : public SkFlattenable { + ./SkAvoidXfermode.h:28:class SkAvoidXfermode : public SkXfermode { *** not done *** chmod +w .h .cpp + ./SkXfermode.h:54:class SkProcXfermode : public SkXfermode { + */ + + /* +./SkBlurMaskFilter.h:25:class SkBlurMaskFilter { + chmod +w SkBlurMaskFilter.cpp +./SkGradientShader.h:30:class SkGradientShader { + */ + // save layer, bounder, looper + // matrix + // clip /path/region + // bitmap proc shader ? + +/* untested: +SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect { +*/ + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fClickPt.set(x, y); + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + SkPathEffect* pathEffectTest() { + static const int gXY[] = { 1, 0, 0, -1, 2, -1, 3, 0, 2, 1, 0, 1 }; + SkScalar gPhase = 0; + SkPath path; + path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1])); + for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) + path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1])); + path.close(); + path.offset(SkIntToScalar(-6), 0); + SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), + gPhase, SkPath1DPathEffect::kRotate_Style); + SkPathEffect* inner = new SkDiscretePathEffect(SkIntToScalar(2), + SkIntToScalar(1)/10); // SkCornerPathEffect(SkIntToScalar(2)); + SkPathEffect* result = new SkComposePathEffect(outer, inner); + outer->unref(); + inner->unref(); + return result; + } + + SkPathEffect* pathEffectTest2() { // unsure this works (has no visible effect) + SkPathEffect* outer = new SkStrokePathEffect(SkIntToScalar(4), + SkPaint::kStroke_Style, SkPaint::kMiter_Join, SkPaint::kButt_Cap); + static const SkScalar intervals[] = {SkIntToScalar(1), SkIntToScalar(2), + SkIntToScalar(2), SkIntToScalar(1)}; + SkPathEffect* inner = new SkDashPathEffect(intervals, + sizeof(intervals) / sizeof(intervals[0]), 0); + SkPathEffect* result = new SkSumPathEffect(outer, inner); + outer->unref(); + inner->unref(); + return result; + } + + SkShader* shaderTest() { + SkPoint pts[] = { { 0, 0, }, { SkIntToScalar(100), 0 } }; + SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; + SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, + 2, SkShader::kClamp_TileMode); + pts[1].set(0, SkIntToScalar(100)); + SkColor colors2[] = {SK_ColorBLACK, SkColorSetARGB(0x80, 0, 0, 0)}; + SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors2, NULL, + 2, SkShader::kClamp_TileMode); + SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode); + SkShader* result = new SkComposeShader(shaderA, shaderB, mode); + shaderA->unref(); + shaderB->unref(); + mode->unref(); + return result; + } + + virtual void startTest() { + SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/bugcirc.gif", &fBug); + SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/tbcirc.gif", &fTb); + SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/05psp04.gif", &fTx); + } + + void drawRaster(SkCanvas* canvas) { + for (size_t index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++) + drawOneRaster(canvas); + } + + void drawOneRaster(SkCanvas* canvas) { + canvas->save(); + + SkScalar x = SkIntToScalar(20); + SkScalar y = SkIntToScalar(40); + SkPaint paint; + + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(48)); + paint.setTypeface(SkTypeface::CreateFromName("sans-serif", + SkTypeface::kBold)); + + SkString str("GOOGLE"); + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) { + apply_shader(&paint, i); + + // paint.setMaskFilter(NULL); + // paint.setColor(SK_ColorBLACK); + +#if 01 + int index = i % SK_ARRAY_COUNT(gLightingColors); + paint.setColorFilter(SkColorFilter::CreateLightingFilter( + gLightingColors[index].fMul, + gLightingColors[index].fAdd))->unref(); +#endif + + canvas->drawText(str.c_str(), str.size(), x, y, paint); + SkRect oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y }; + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawOval(oval, paint); + paint.setStyle(SkPaint::kFill_Style); + + y += paint.getFontSpacing(); + } + + canvas->restore(); + + if (1) { + SkAvoidXfermode mode(SK_ColorWHITE, 0xFF, + SkAvoidXfermode::kTargetColor_Mode); + SkPaint paint; + x += SkIntToScalar(20); + SkRect r = { x, 0, x + SkIntToScalar(360), SkIntToScalar(700) }; + paint.setXfermode(&mode); + paint.setColor(SK_ColorGREEN); + paint.setAntiAlias(true); + canvas->drawOval(r, paint); + } + } + +private: + SkPoint fClickPt; + SkBitmap fBug, fTb, fTx; + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new DemoView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleAnimatedGradient.cpp b/samplecode/SampleAnimatedGradient.cpp new file mode 100644 index 0000000000..a7b2a4638f --- /dev/null +++ b/samplecode/SampleAnimatedGradient.cpp @@ -0,0 +1,90 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" + +class GradientView : public SampleView { +public: + GradientView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + struct GradData { + int fCount; + const SkColor* fColors; + const SkScalar* fPos; + }; + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Gradients"); + return true; + } + return this->INHERITED::onQuery(evt); + } + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3))); + paint.setStyle(SkPaint::kFill_Style); + + SkPoint p = SkPoint::Make(0,0); + SkPoint q = SkPoint::Make(100,100); + SkPoint pts[] = {p, q}; + + SkScalar t, temp, x, y; + SkColor gColors[] = { + SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK + }; + t = SampleCode::GetAnimScalar(SkIntToScalar(2), SkIntToScalar(20)); + temp = SampleCode::GetAnimScalar(SkIntToScalar(1), SkIntToScalar(8)); + SkScalar step = SK_ScalarPI / (10); + SkScalar angle = t * step; + x = SkScalarSinCos(angle, &y); + SkScalar colorPositions[] = { 0, 0.1 + x, 0.4 + y, 0.9 - x + y, 1.0}; + GradData data = { 5, gColors, colorPositions }; + + + SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(200) }; + SkShader* shader1 = SkGradientShader::CreateLinear( + pts, data.fColors, data.fPos,data.fCount, + SkShader::kMirror_TileMode); + paint.setShader(shader1)->unref(); + + canvas->drawRect(r, paint); + + + SkPoint s = SkPoint::Make(100,100); + SkShader* shader2 = SkGradientShader::CreateRadial( + s, 100, data.fColors, data.fPos, data.fCount, + SkShader::kMirror_TileMode); + paint.setShader(shader2)->unref(); + canvas->translate(250, 0); + canvas->drawRect(r, paint); + + SkShader* shader3 = SkGradientShader::CreateTwoPointRadial( + p, 0, q, 100, data.fColors, data.fPos, data.fCount, + SkShader::kMirror_TileMode); + paint.setShader(shader3)->unref(); + canvas->translate(0, 250); + canvas->drawRect(r, paint); + + SkShader* shader4 = SkGradientShader::CreateSweep( + 100, 100, data.fColors, data.fPos, data.fCount); + + paint.setShader(shader4)->unref(); + canvas->translate(-250, 0); + canvas->drawRect(r, paint); + + this->inval(NULL); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new GradientView; } +static SkViewRegister reg(MyFactory);
\ No newline at end of file diff --git a/samplecode/SampleAnimator.cpp b/samplecode/SampleAnimator.cpp new file mode 100644 index 0000000000..99173fc8ab --- /dev/null +++ b/samplecode/SampleAnimator.cpp @@ -0,0 +1,159 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" + +#include "SkAnimator.h" +#include "SkStream.h" +#include "SkDOM.h" + +/////////////////////////////////////////////////////////////////////////////// + +class SkAnimatorView : public SkView { +public: + SkAnimatorView(); + virtual ~SkAnimatorView(); + + void setURIBase(const char dir[]); + + SkAnimator* getAnimator() const { return fAnimator; } + + bool decodeFile(const char path[]); + bool decodeMemory(const void* buffer, size_t size); + bool decodeStream(SkStream* stream); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + +private: + SkString fBaseURI; + SkAnimator* fAnimator; + + typedef SkView INHERITED; +}; + +SkAnimatorView::SkAnimatorView() : fAnimator(NULL) {} + +SkAnimatorView::~SkAnimatorView() { + delete fAnimator; +} + +void SkAnimatorView::setURIBase(const char dir[]) { + fBaseURI.set(dir); +} + +bool SkAnimatorView::decodeFile(const char path[]) { + SkFILEStream* is = new SkFILEStream(path); + SkAutoUnref aur(is); + return is->isValid() && this->decodeStream(is); +} + +bool SkAnimatorView::decodeMemory(const void* buffer, size_t size) { + SkMemoryStream* is = new SkMemoryStream(buffer, size); + SkAutoUnref aur(is); + return this->decodeStream(is); +} + +static const SkDOMNode* find_nodeID(const SkDOM& dom, + const SkDOMNode* node, const char name[]) { + if (NULL == node) { + node = dom.getRootNode(); + } + do { + const char* idval = dom.findAttr(node, "id"); + if (idval && !strcmp(idval, name)) { + return node; + } + const SkDOMNode* child = dom.getFirstChild(node); + if (child) { + const SkDOMNode* found = find_nodeID(dom, child, name); + if (found) { + return found; + } + } + } while ((node = dom.getNextSibling(node)) != NULL); + return NULL; +} + +bool SkAnimatorView::decodeStream(SkStream* stream) { + delete fAnimator; + fAnimator = new SkAnimator; + fAnimator->setURIBase(fBaseURI.c_str()); +#if 0 + if (!fAnimator->decodeStream(stream)) { + delete fAnimator; + fAnimator = NULL; + return false; + } +#else + size_t len = stream->getLength(); + char* text = (char*)sk_malloc_throw(len); + stream->read(text, len); + SkDOM dom; + const SkDOM::Node* root = dom.build(text, len); + if (NULL == root) { + return false; + } + if (!fAnimator->decodeDOM(dom, root)) { + delete fAnimator; + fAnimator = NULL; + return false; + } + for (int i = 0; i <= 10; i++) { + SkString name("glyph"); + name.appendS32(i); + const SkDOM::Node* node = find_nodeID(dom, NULL, name.c_str()); + SkASSERT(node); + SkRect r; + dom.findScalar(node, "left", &r.fLeft); + dom.findScalar(node, "top", &r.fTop); + dom.findScalar(node, "width", &r.fRight); r.fRight += r.fLeft; + dom.findScalar(node, "height", &r.fBottom); r.fBottom += r.fTop; + SkDebugf("--- %s [%g %g %g %g]\n", name.c_str(), + r.fLeft, r.fTop, r.fRight, r.fBottom); + } +#endif + return true; +} + +#include "SkTime.h" + +void SkAnimatorView::onDraw(SkCanvas* canvas) { + if (fAnimator) { + canvas->drawColor(SK_ColorWHITE); + fAnimator->draw(canvas, 0); +#if 0 + canvas->save(); + canvas->translate(120, 30); + canvas->scale(0.5, 0.5); + fAnimator->draw(canvas, 0); + canvas->restore(); + + canvas->save(); + canvas->translate(190, 40); + canvas->scale(0.25, 0.25); + fAnimator->draw(canvas, 0); + canvas->restore(); + + this->inval(NULL); +#endif + } +} + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { + SkAnimatorView* av = new SkAnimatorView; +// av->decodeFile("/skimages/test.xml"); +#if 0 + av->setURIBase("/skia/trunk/animations/"); + av->decodeFile("/skia/trunk/animations/checkbox.xml"); +#else + av->setURIBase("/"); + av->decodeFile("/testanim.txt"); +#endif + return av; +} + +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp new file mode 100644 index 0000000000..3efffe6de7 --- /dev/null +++ b/samplecode/SampleApp.cpp @@ -0,0 +1,1647 @@ +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkGpuCanvas.h" +#include "SkGraphics.h" +#include "SkImageEncoder.h" +#include "SkPaint.h" +#include "SkPicture.h" +#include "SkStream.h" +#include "SkTime.h" +#include "SkWindow.h" + +#include "SampleCode.h" +#include "GrContext.h" +#include "SkTouchGesture.h" +#include "SkTypeface.h" + +#define TEST_GPIPEx + +#ifdef TEST_GPIPE +#define PIPE_FILE +#define FILE_PATH "/path/to/drawing.data" +#endif + +#define USE_ARROWS_FOR_ZOOM true +//#define DEFAULT_TO_GPU + +extern SkView* create_overview(int, const SkViewFactory[]); + +#define SK_SUPPORT_GL + +#define ANIMATING_EVENTTYPE "nextSample" +#define ANIMATING_DELAY 750 + +#ifdef SK_DEBUG + #define FPS_REPEAT_MULTIPLIER 1 +#else + #define FPS_REPEAT_MULTIPLIER 10 +#endif +#define FPS_REPEAT_COUNT (10 * FPS_REPEAT_MULTIPLIER) + +#ifdef SK_SUPPORT_GL + #include "GrGLConfig.h" +#endif + +/////////////// +static const char view_inval_msg[] = "view-inval-msg"; + +static void postInvalDelay(SkEventSinkID sinkID) { + SkEvent* evt = new SkEvent(view_inval_msg); + evt->post(sinkID, 1); +} + +static bool isInvalEvent(const SkEvent& evt) { + return evt.isType(view_inval_msg); +} +////////////////// + +SkViewRegister* SkViewRegister::gHead; +SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) { + static bool gOnce; + if (!gOnce) { + gHead = NULL; + gOnce = true; + } + + fChain = gHead; + gHead = this; +} + +#if defined(SK_SUPPORT_GL) + #define SK_USE_SHADERS +#endif + +#ifdef SK_BUILD_FOR_MAC +#include <CoreFoundation/CoreFoundation.h> +#include <CoreFoundation/CFURLAccess.h> + +static void testpdf() { + CFStringRef path = CFStringCreateWithCString(NULL, "/test.pdf", + kCFStringEncodingUTF8); + CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, + kCFURLPOSIXPathStyle, + false); + CFRelease(path); + CGRect box = CGRectMake(0, 0, 8*72, 10*72); + CGContextRef cg = CGPDFContextCreateWithURL(url, &box, NULL); + CFRelease(url); + + CGContextBeginPage(cg, &box); + CGRect r = CGRectMake(10, 10, 40 + 0.5, 50 + 0.5); + CGContextFillEllipseInRect(cg, r); + CGContextEndPage(cg); + CGContextRelease(cg); + + if (false) { + SkBitmap bm; + bm.setConfig(SkBitmap::kA8_Config, 64, 64); + bm.allocPixels(); + bm.eraseColor(0); + + SkCanvas canvas(bm); + + } +} +#endif + +////////////////////////////////////////////////////////////////////////////// + +enum FlipAxisEnum { + kFlipAxis_X = (1 << 0), + kFlipAxis_Y = (1 << 1) +}; + +enum SkTriState { + kFalse_SkTriState, + kTrue_SkTriState, + kUnknown_SkTriState, +}; + +static SkTriState cycle_tristate(SkTriState state) { + static const SkTriState gCycle[] = { + /* kFalse_SkTriState -> */ kUnknown_SkTriState, + /* kTrue_SkTriState -> */ kFalse_SkTriState, + /* kUnknown_SkTriState -> */ kTrue_SkTriState, + }; + return gCycle[state]; +} + +#include "SkDrawFilter.h" + +class FlagsDrawFilter : public SkDrawFilter { +public: + FlagsDrawFilter(SkTriState lcd, SkTriState aa, SkTriState filter, + SkTriState hinting) : + fLCDState(lcd), fAAState(aa), fFilterState(filter), fHintingState(hinting) {} + + virtual void filter(SkPaint* paint, Type t) { + if (kText_Type == t && kUnknown_SkTriState != fLCDState) { + paint->setLCDRenderText(kTrue_SkTriState == fLCDState); + } + if (kUnknown_SkTriState != fAAState) { + paint->setAntiAlias(kTrue_SkTriState == fAAState); + } + if (kUnknown_SkTriState != fFilterState) { + paint->setFilterBitmap(kTrue_SkTriState == fFilterState); + } + if (kUnknown_SkTriState != fHintingState) { + paint->setHinting(kTrue_SkTriState == fHintingState ? + SkPaint::kNormal_Hinting : + SkPaint::kSlight_Hinting); + } + } + +private: + SkTriState fLCDState; + SkTriState fAAState; + SkTriState fFilterState; + SkTriState fHintingState; +}; + +////////////////////////////////////////////////////////////////////////////// + +#define MAX_ZOOM_LEVEL 8 +#define MIN_ZOOM_LEVEL -8 + +static const char gCharEvtName[] = "SampleCode_Char_Event"; +static const char gKeyEvtName[] = "SampleCode_Key_Event"; +static const char gTitleEvtName[] = "SampleCode_Title_Event"; +static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event"; +static const char gFastTextEvtName[] = "SampleCode_FastText_Event"; + +bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) { + if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) { + if (outUni) { + *outUni = evt.getFast32(); + } + return true; + } + return false; +} + +bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) { + if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) { + if (outKey) { + *outKey = (SkKey)evt.getFast32(); + } + return true; + } + return false; +} + +bool SampleCode::TitleQ(const SkEvent& evt) { + return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1); +} + +void SampleCode::TitleR(SkEvent* evt, const char title[]) { + SkASSERT(evt && TitleQ(*evt)); + evt->setString(gTitleEvtName, title); +} + +bool SampleCode::PrefSizeQ(const SkEvent& evt) { + return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1); +} + +void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) { + SkASSERT(evt && PrefSizeQ(*evt)); + SkScalar size[2]; + size[0] = width; + size[1] = height; + evt->setScalars(gPrefSizeEvtName, 2, size); +} + +bool SampleCode::FastTextQ(const SkEvent& evt) { + return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1); +} + +/////////////////////////////////////////////////////////////////////////////// + +static SkMSec gAnimTime; +static SkMSec gAnimTimePrev; + +SkMSec SampleCode::GetAnimTime() { return gAnimTime; } +SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; } +SkScalar SampleCode::GetAnimSecondsDelta() { + return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0); +} + +SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) { + // since gAnimTime can be up to 32 bits, we can't convert it to a float + // or we'll lose the low bits. Hence we use doubles for the intermediate + // calculations + double seconds = (double)gAnimTime / 1000.0; + double value = SkScalarToDouble(speed) * seconds; + if (period) { + value = ::fmod(value, SkScalarToDouble(period)); + } + return SkDoubleToScalar(value); +} + +////////////////////////////////////////////////////////////////////////////// + +static SkView* curr_view(SkWindow* wind) { + SkView::F2BIter iter(wind); + return iter.next(); +} + +class SampleWindow : public SkOSWindow { + SkTDArray<SkViewFactory> fSamples; +public: + SampleWindow(void* hwnd); + virtual ~SampleWindow(); + + virtual void draw(SkCanvas* canvas); + +protected: + virtual void onDraw(SkCanvas* canvas); + virtual bool onHandleKey(SkKey key); + virtual bool onHandleChar(SkUnichar); + virtual void onSizeChange(); + + virtual SkCanvas* beforeChildren(SkCanvas*); + virtual void afterChildren(SkCanvas*); + virtual void beforeChild(SkView* child, SkCanvas* canvas); + virtual void afterChild(SkView* child, SkCanvas* canvas); + + virtual bool onEvent(const SkEvent& evt); + virtual bool onQuery(SkEvent* evt); + + virtual bool onDispatchClick(int x, int y, Click::State); + virtual bool onClick(Click* click); + virtual Click* onFindClickHandler(SkScalar x, SkScalar y); + +#if 0 + virtual bool handleChar(SkUnichar uni); + virtual bool handleEvent(const SkEvent& evt); + virtual bool handleKey(SkKey key); + virtual bool handleKeyUp(SkKey key); + virtual bool onHandleKeyUp(SkKey key); +#endif + +private: + int fCurrIndex; + + SkPicture* fPicture; + SkGpuCanvas* fGpuCanvas; + GrContext* fGrContext; + SkPath fClipPath; + + SkTouchGesture fGesture; + int fZoomLevel; + SkScalar fZoomScale; + + enum CanvasType { + kRaster_CanvasType, + kPicture_CanvasType, + kGPU_CanvasType + }; + CanvasType fCanvasType; + + bool fUseClip; + bool fNClip; + bool fRepeatDrawing; + bool fAnimating; + bool fRotate; + bool fScale; + bool fRequestGrabImage; + bool fUsePipe; + bool fMeasureFPS; + SkMSec fMeasureFPS_Time; + + // The following are for the 'fatbits' drawing + // Latest position of the mouse. + int fMouseX, fMouseY; + int fFatBitsScale; + // Used by the text showing position and color values. + SkTypeface* fTypeface; + bool fShowZoomer; + + SkTriState fLCDState; + SkTriState fAAState; + SkTriState fFilterState; + SkTriState fHintingState; + unsigned fFlipAxis; + + int fScrollTestX, fScrollTestY; + + bool make3DReady(); + void changeZoomLevel(int delta); + + void loadView(SkView*); + void updateTitle(); + bool nextSample(); + + void toggleZoomer(); + bool zoomIn(); + bool zoomOut(); + void updatePointer(int x, int y); + void showZoomer(SkCanvas* canvas); + + void postAnimatingEvent() { + if (fAnimating) { + SkEvent* evt = new SkEvent(ANIMATING_EVENTTYPE); + evt->post(this->getSinkID(), ANIMATING_DELAY); + } + } + + + static CanvasType cycle_canvastype(CanvasType); + + typedef SkOSWindow INHERITED; +}; + +bool SampleWindow::zoomIn() +{ + // Arbitrarily decided + if (fFatBitsScale == 25) return false; + fFatBitsScale++; + this->inval(NULL); + return true; +} + +bool SampleWindow::zoomOut() +{ + if (fFatBitsScale == 1) return false; + fFatBitsScale--; + this->inval(NULL); + return true; +} + +void SampleWindow::toggleZoomer() +{ + fShowZoomer = !fShowZoomer; + this->inval(NULL); +} + +void SampleWindow::updatePointer(int x, int y) +{ + fMouseX = x; + fMouseY = y; + if (fShowZoomer) { + this->inval(NULL); + } +} + +bool SampleWindow::make3DReady() { + +#if defined(SK_SUPPORT_GL) + if (attachGL()) { + if (NULL != fGrContext) { + // various gr lifecycle tests + #if 0 + fGrContext->freeGpuResources(); + #elif 0 + // this will leak resources. + fGrContext->contextLost(); + #elif 0 + GrAssert(1 == fGrContext->refcnt()); + fGrContext->unref(); + fGrContext = NULL; + #endif + } + + if (NULL == fGrContext) { + #if defined(SK_USE_SHADERS) + fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL); + #else + fGrContext = GrContext::Create(kOpenGL_Fixed_GrEngine, NULL); + #endif + SkDebugf("---- constructor\n"); + } + + if (NULL != fGrContext) { + return true; + } else { + detachGL(); + } + } +#endif + SkDebugf("Failed to setup 3D"); + return false; +} + +SampleWindow::CanvasType SampleWindow::cycle_canvastype(CanvasType ct) { + static const CanvasType gCT[] = { + kPicture_CanvasType, + kGPU_CanvasType, + kRaster_CanvasType + }; + return gCT[ct]; +} + +SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) { +#ifdef PIPE_FILE + //Clear existing file or create file if it doesn't exist + FILE* f = fopen(FILE_PATH, "wb"); + fclose(f); +#endif + + fPicture = NULL; + fGpuCanvas = NULL; + + fGrContext = NULL; + +#ifdef DEFAULT_TO_GPU + fCanvasType = kGPU_CanvasType; +#else + fCanvasType = kRaster_CanvasType; +#endif + fUseClip = false; + fNClip = false; + fRepeatDrawing = false; + fAnimating = false; + fRotate = false; + fScale = false; + fRequestGrabImage = false; + fUsePipe = false; + fMeasureFPS = false; + fLCDState = kUnknown_SkTriState; + fAAState = kUnknown_SkTriState; + fFilterState = kUnknown_SkTriState; + fHintingState = kUnknown_SkTriState; + fFlipAxis = 0; + fScrollTestX = fScrollTestY = 0; + + fMouseX = fMouseY = 0; + fFatBitsScale = 8; + fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold); + fShowZoomer = false; + + fZoomLevel = 0; + fZoomScale = SK_Scalar1; + +// this->setConfig(SkBitmap::kRGB_565_Config); + this->setConfig(SkBitmap::kARGB_8888_Config); + this->setVisibleP(true); + this->setClipToBounds(false); + + { + const SkViewRegister* reg = SkViewRegister::Head(); + while (reg) { + *fSamples.append() = reg->factory(); + reg = reg->next(); + } + } + fCurrIndex = 0; + this->loadView(fSamples[fCurrIndex]()); + +#ifdef SK_BUILD_FOR_MAC + testpdf(); +#endif +} + +SampleWindow::~SampleWindow() { + delete fPicture; + delete fGpuCanvas; + if (NULL != fGrContext) { + fGrContext->unref(); + } + fTypeface->unref(); +} + +static SkBitmap capture_bitmap(SkCanvas* canvas) { + SkBitmap bm; + const SkBitmap& src = canvas->getDevice()->accessBitmap(false); + src.copyTo(&bm, src.config()); + return bm; +} + +static bool bitmap_diff(SkCanvas* canvas, const SkBitmap& orig, + SkBitmap* diff) { + const SkBitmap& src = canvas->getDevice()->accessBitmap(false); + + SkAutoLockPixels alp0(src); + SkAutoLockPixels alp1(orig); + for (int y = 0; y < src.height(); y++) { + const void* srcP = src.getAddr(0, y); + const void* origP = orig.getAddr(0, y); + size_t bytes = src.width() * src.bytesPerPixel(); + if (memcmp(srcP, origP, bytes)) { + SkDebugf("---------- difference on line %d\n", y); + return true; + } + } + return false; +} + +static void drawText(SkCanvas* canvas, SkString string, SkScalar left, SkScalar top, SkPaint& paint) +{ + SkColor desiredColor = paint.getColor(); + paint.setColor(SK_ColorWHITE); + const char* c_str = string.c_str(); + size_t size = string.size(); + SkRect bounds; + paint.measureText(c_str, size, &bounds); + bounds.offset(left, top); + SkScalar inset = SkIntToScalar(-2); + bounds.inset(inset, inset); + canvas->drawRect(bounds, paint); + if (desiredColor != SK_ColorBLACK) { + paint.setColor(SK_ColorBLACK); + canvas->drawText(c_str, size, left + SK_Scalar1, top + SK_Scalar1, paint); + } + paint.setColor(desiredColor); + canvas->drawText(c_str, size, left, top, paint); +} + +#define XCLIP_N 8 +#define YCLIP_N 8 + +void SampleWindow::draw(SkCanvas* canvas) { + // update the animation time + gAnimTimePrev = gAnimTime; + gAnimTime = SkTime::GetMSecs(); + + SkScalar cx = SkScalarHalf(this->width()); + SkScalar cy = SkScalarHalf(this->height()); + + if (fZoomLevel) { + SkMatrix m; + SkPoint center; + m = canvas->getTotalMatrix();//.invert(&m); + m.mapXY(cx, cy, ¢er); + cx = center.fX; + cy = center.fY; + + m.setTranslate(-cx, -cy); + m.postScale(fZoomScale, fZoomScale); + m.postTranslate(cx, cy); + + canvas->concat(m); + } + + if (fFlipAxis) { + SkMatrix m; + m.setTranslate(cx, cy); + if (fFlipAxis & kFlipAxis_X) { + m.preScale(-SK_Scalar1, SK_Scalar1); + } + if (fFlipAxis & kFlipAxis_Y) { + m.preScale(SK_Scalar1, -SK_Scalar1); + } + m.preTranslate(-cx, -cy); + canvas->concat(m); + } + + // Apply any gesture matrix + if (true) { + const SkMatrix& localM = fGesture.localM(); + if (localM.getType() & SkMatrix::kScale_Mask) { + canvas->setExternalMatrix(&localM); + } + canvas->concat(localM); + canvas->concat(fGesture.globalM()); + + if (fGesture.isActive()) { + this->inval(NULL); + } + } + + if (fNClip) { + this->INHERITED::draw(canvas); + SkBitmap orig = capture_bitmap(canvas); + + const SkScalar w = this->width(); + const SkScalar h = this->height(); + const SkScalar cw = w / XCLIP_N; + const SkScalar ch = h / YCLIP_N; + for (int y = 0; y < YCLIP_N; y++) { + SkRect r; + r.fTop = y * ch; + r.fBottom = (y + 1) * ch; + if (y == YCLIP_N - 1) { + r.fBottom = h; + } + for (int x = 0; x < XCLIP_N; x++) { + SkAutoCanvasRestore acr(canvas, true); + r.fLeft = x * cw; + r.fRight = (x + 1) * cw; + if (x == XCLIP_N - 1) { + r.fRight = w; + } + canvas->clipRect(r); + this->INHERITED::draw(canvas); + } + } + + SkBitmap diff; + if (bitmap_diff(canvas, orig, &diff)) { + } + } else { + this->INHERITED::draw(canvas); + } + if (fShowZoomer && fCanvasType != kGPU_CanvasType) { + // 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 + // also called by draw), so we cannot show the zoomer here. + // Instead, we call it inside afterChildren. + showZoomer(canvas); + } +} + +void SampleWindow::showZoomer(SkCanvas* canvas) { + int count = canvas->save(); + canvas->resetMatrix(); + // Ensure the mouse position is on screen. + int width = SkScalarRound(this->width()); + int height = SkScalarRound(this->height()); + if (fMouseX >= width) fMouseX = width - 1; + else if (fMouseX < 0) fMouseX = 0; + if (fMouseY >= height) fMouseY = height - 1; + else if (fMouseY < 0) fMouseY = 0; + + SkBitmap bitmap = capture_bitmap(canvas); + bitmap.lockPixels(); + + // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle. + int zoomedWidth = (width >> 1) | 1; + int zoomedHeight = (height >> 1) | 1; + SkIRect src; + src.set(0, 0, zoomedWidth / fFatBitsScale, zoomedHeight / fFatBitsScale); + src.offset(fMouseX - (src.width()>>1), fMouseY - (src.height()>>1)); + SkRect dest; + dest.set(0, 0, SkIntToScalar(zoomedWidth), SkIntToScalar(zoomedHeight)); + dest.offset(SkIntToScalar(width - zoomedWidth), SkIntToScalar(height - zoomedHeight)); + SkPaint paint; + // Clear the background behind our zoomed in view + paint.setColor(SK_ColorWHITE); + canvas->drawRect(dest, paint); + canvas->drawBitmapRect(bitmap, &src, dest); + paint.setColor(SK_ColorBLACK); + paint.setStyle(SkPaint::kStroke_Style); + // Draw a border around the pixel in the middle + SkRect originalPixel; + originalPixel.set(SkIntToScalar(fMouseX), SkIntToScalar(fMouseY), SkIntToScalar(fMouseX + 1), SkIntToScalar(fMouseY + 1)); + SkMatrix matrix; + SkRect scalarSrc; + scalarSrc.set(src); + SkColor color = bitmap.getColor(fMouseX, fMouseY); + if (matrix.setRectToRect(scalarSrc, dest, SkMatrix::kFill_ScaleToFit)) { + SkRect pixel; + matrix.mapRect(&pixel, originalPixel); + // TODO Perhaps measure the values and make the outline white if it's "dark" + if (color == SK_ColorBLACK) { + paint.setColor(SK_ColorWHITE); + } + canvas->drawRect(pixel, paint); + } + paint.setColor(SK_ColorBLACK); + // Draw a border around the destination rectangle + canvas->drawRect(dest, paint); + paint.setStyle(SkPaint::kStrokeAndFill_Style); + // Identify the pixel and its color on screen + paint.setTypeface(fTypeface); + paint.setAntiAlias(true); + SkScalar lineHeight = paint.getFontMetrics(NULL); + SkString string; + string.appendf("(%i, %i)", fMouseX, fMouseY); + SkScalar left = dest.fLeft + SkIntToScalar(3); + SkScalar i = SK_Scalar1; + drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint); + // Alpha + i += SK_Scalar1; + string.reset(); + string.appendf("A: %X", SkColorGetA(color)); + drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint); + // Red + i += SK_Scalar1; + string.reset(); + string.appendf("R: %X", SkColorGetR(color)); + paint.setColor(SK_ColorRED); + drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint); + // Green + i += SK_Scalar1; + string.reset(); + string.appendf("G: %X", SkColorGetG(color)); + paint.setColor(SK_ColorGREEN); + drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint); + // Blue + i += SK_Scalar1; + string.reset(); + string.appendf("B: %X", SkColorGetB(color)); + paint.setColor(SK_ColorBLUE); + drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint); + canvas->restoreToCount(count); +} + +void SampleWindow::onDraw(SkCanvas* canvas) { + if (fRepeatDrawing) { + this->inval(NULL); + } +} + +#include "SkColorPriv.h" + +static void reverseRedAndBlue(const SkBitmap& bm) { + SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config); + uint8_t* p = (uint8_t*)bm.getPixels(); + uint8_t* stop = p + bm.getSize(); + while (p < stop) { + // swap red/blue (to go from ARGB(int) to RGBA(memory) and premultiply + unsigned scale = SkAlpha255To256(p[3]); + unsigned r = p[2]; + unsigned b = p[0]; + p[0] = SkAlphaMul(r, scale); + p[1] = SkAlphaMul(p[1], scale); + p[2] = SkAlphaMul(b, scale); + p += 4; + } +} + +SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) { + if (kGPU_CanvasType != fCanvasType) { +#ifdef SK_SUPPORT_GL + detachGL(); +#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; + renderTarget = fGrContext->createRenderTargetFrom3DApiState(); + fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget); + renderTarget->unref(); + + device = fGpuCanvas->createDevice(SkBitmap::kARGB_8888_Config, + bitmap.width(), bitmap.height(), + false, false); + fGpuCanvas->setDevice(device)->unref(); + + fGpuCanvas->concat(canvas->getTotalMatrix()); + canvas = fGpuCanvas; + + } else { + canvas = this->INHERITED::beforeChildren(canvas); + } + break; + } + } + + if (fUseClip) { + canvas->drawColor(0xFFFF88FF); + canvas->clipPath(fClipPath); + } + + return canvas; +} + +static void paint_rgn(const SkBitmap& bm, const SkIRect& r, + const SkRegion& rgn) { + SkCanvas canvas(bm); + SkRegion inval(rgn); + + inval.translate(r.fLeft, r.fTop); + canvas.clipRegion(inval); + canvas.drawColor(0xFFFF8080); +} + +void SampleWindow::afterChildren(SkCanvas* orig) { + if (fRequestGrabImage) { + fRequestGrabImage = false; + + SkCanvas* canvas = fGpuCanvas ? fGpuCanvas : orig; + SkDevice* device = canvas->getDevice(); + SkBitmap bmp; + if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) { + static int gSampleGrabCounter; + SkString name; + name.printf("sample_grab_%d", gSampleGrabCounter++); + SkImageEncoder::EncodeFile(name.c_str(), bmp, + SkImageEncoder::kPNG_Type, 100); + } + } + + switch (fCanvasType) { + case kRaster_CanvasType: + break; + case kPicture_CanvasType: + if (true) { + SkPicture* pict = new SkPicture(*fPicture); + fPicture->unref(); + orig->drawPicture(*pict); + pict->unref(); + } else if (true) { + SkDynamicMemoryWStream ostream; + fPicture->serialize(&ostream); + fPicture->unref(); + + SkMemoryStream istream(ostream.getStream(), ostream.getOffset()); + SkPicture pict(&istream); + orig->drawPicture(pict); + } else { + fPicture->draw(orig); + fPicture->unref(); + } + fPicture = NULL; + break; +#ifdef SK_SUPPORT_GL + case kGPU_CanvasType: + if (fShowZoomer) { + this->showZoomer(fGpuCanvas); + } + delete fGpuCanvas; + fGpuCanvas = NULL; + presentGL(); + break; +#endif + } + + // Do this after presentGL and other finishing, rather than in afterChild + if (fMeasureFPS && fMeasureFPS_Time) { + fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time; + this->updateTitle(); + postInvalDelay(this->getSinkID()); + } + + // if ((fScrollTestX | fScrollTestY) != 0) + if (false) { + const SkBitmap& bm = orig->getDevice()->accessBitmap(true); + int dx = fScrollTestX * 7; + int dy = fScrollTestY * 7; + SkIRect r; + SkRegion inval; + + r.set(50, 50, 50+100, 50+100); + bm.scrollRect(&r, dx, dy, &inval); + paint_rgn(bm, r, inval); + } +} + +void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) { + if (fScale) { + SkScalar scale = SK_Scalar1 * 7 / 10; + SkScalar cx = this->width() / 2; + SkScalar cy = this->height() / 2; + canvas->translate(cx, cy); + canvas->scale(scale, scale); + canvas->translate(-cx, -cy); + } + if (fRotate) { + SkScalar cx = this->width() / 2; + SkScalar cy = this->height() / 2; + canvas->translate(cx, cy); + canvas->rotate(SkIntToScalar(30)); + canvas->translate(-cx, -cy); + } + + canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState, + fFilterState, fHintingState))->unref(); + + if (fMeasureFPS) { + fMeasureFPS_Time = 0; // 0 means the child is not aware of repeat-draw + if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) { + fMeasureFPS_Time = SkTime::GetMSecs(); + } + } else { + (void)SampleView::SetRepeatDraw(child, 1); + } + (void)SampleView::SetUsePipe(child, fUsePipe); +} + +void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) { + canvas->setDrawFilter(NULL); +} + +static SkBitmap::Config gConfigCycle[] = { + SkBitmap::kNo_Config, // none -> none + SkBitmap::kNo_Config, // a1 -> none + SkBitmap::kNo_Config, // a8 -> none + SkBitmap::kNo_Config, // index8 -> none + SkBitmap::kARGB_4444_Config, // 565 -> 4444 + SkBitmap::kARGB_8888_Config, // 4444 -> 8888 + SkBitmap::kRGB_565_Config // 8888 -> 565 +}; + +static SkBitmap::Config cycle_configs(SkBitmap::Config c) { + return gConfigCycle[c]; +} + +void SampleWindow::changeZoomLevel(int delta) { + fZoomLevel += delta; + if (fZoomLevel > 0) { + fZoomLevel = SkMin32(fZoomLevel, MAX_ZOOM_LEVEL); + fZoomScale = SkIntToScalar(fZoomLevel + 1); + } else if (fZoomLevel < 0) { + fZoomLevel = SkMax32(fZoomLevel, MIN_ZOOM_LEVEL); + fZoomScale = SK_Scalar1 / (1 - fZoomLevel); + } else { + fZoomScale = SK_Scalar1; + } + + this->inval(NULL); +} + +bool SampleWindow::nextSample() { + fCurrIndex = (fCurrIndex + 1) % fSamples.count(); + this->loadView(fSamples[fCurrIndex]()); + return true; +} + +bool SampleWindow::onEvent(const SkEvent& evt) { + if (evt.isType(ANIMATING_EVENTTYPE)) { + if (fAnimating) { + this->nextSample(); + this->postAnimatingEvent(); + } + return true; + } + if (evt.isType("set-curr-index")) { + fCurrIndex = evt.getFast32() % fSamples.count(); + this->loadView(fSamples[fCurrIndex]()); + return true; + } + if (isInvalEvent(evt)) { + this->inval(NULL); + return true; + } + return this->INHERITED::onEvent(evt); +} + +bool SampleWindow::onQuery(SkEvent* query) { + if (query->isType("get-slide-count")) { + query->setFast32(fSamples.count()); + return true; + } + if (query->isType("get-slide-title")) { + SkView* view = fSamples[query->getFast32()](); + SkEvent evt(gTitleEvtName); + if (view->doQuery(&evt)) { + query->setString("title", evt.findString(gTitleEvtName)); + } + SkSafeUnref(view); + return true; + } + if (query->isType("use-fast-text")) { + SkEvent evt(gFastTextEvtName); + return curr_view(this)->doQuery(&evt); + } + return this->INHERITED::onQuery(query); +} + +static void cleanup_for_filename(SkString* name) { + char* str = name->writable_str(); + for (size_t i = 0; i < name->size(); i++) { + switch (str[i]) { + case ':': str[i] = '-'; break; + case '/': str[i] = '-'; break; + case ' ': str[i] = '_'; break; + default: break; + } + } +} + +bool SampleWindow::onHandleChar(SkUnichar uni) { + { + SkView* view = curr_view(this); + if (view) { + SkEvent evt(gCharEvtName); + evt.setFast32(uni); + if (view->doQuery(&evt)) { + return true; + } + } + } + + int dx = 0xFF; + int dy = 0xFF; + + switch (uni) { + case '5': dx = 0; dy = 0; break; + case '8': dx = 0; dy = -1; break; + case '6': dx = 1; dy = 0; break; + case '2': dx = 0; dy = 1; break; + case '4': dx = -1; dy = 0; break; + case '7': dx = -1; dy = -1; break; + case '9': dx = 1; dy = -1; break; + case '3': dx = 1; dy = 1; break; + case '1': dx = -1; dy = 1; break; + + default: + break; + } + + if (0xFF != dx && 0xFF != dy) { + if ((dx | dy) == 0) { + fScrollTestX = fScrollTestY = 0; + } else { + fScrollTestX += dx; + fScrollTestY += dy; + } + this->inval(NULL); + return true; + } + + switch (uni) { + case 'a': + fAnimating = !fAnimating; + this->postAnimatingEvent(); + this->updateTitle(); + return true; + case 'b': + fAAState = cycle_tristate(fAAState); + this->updateTitle(); + this->inval(NULL); + break; + case 'c': + fUseClip = !fUseClip; + this->inval(NULL); + this->updateTitle(); + return true; + case 'd': + SkGraphics::SetFontCacheUsed(0); + return true; + case 'f': + fMeasureFPS = !fMeasureFPS; + this->inval(NULL); + break; + case 'g': + fRequestGrabImage = true; + this->inval(NULL); + break; + case 'h': + fHintingState = cycle_tristate(fHintingState); + this->updateTitle(); + this->inval(NULL); + break; + case 'i': + this->zoomIn(); + break; + case 'l': + fLCDState = cycle_tristate(fLCDState); + this->updateTitle(); + this->inval(NULL); + break; + case 'n': + fFilterState = cycle_tristate(fFilterState); + this->updateTitle(); + this->inval(NULL); + break; + case 'o': + this->zoomOut(); + break; + case 'p': + fUsePipe = !fUsePipe; + this->updateTitle(); + this->inval(NULL); + break; + case 'r': + fRotate = !fRotate; + this->inval(NULL); + this->updateTitle(); + return true; + case 's': + fScale = !fScale; + this->inval(NULL); + this->updateTitle(); + return true; + case 'x': + fFlipAxis ^= kFlipAxis_X; + this->updateTitle(); + this->inval(NULL); + break; + case 'y': + fFlipAxis ^= kFlipAxis_Y; + this->updateTitle(); + this->inval(NULL); + break; + case 'z': + this->toggleZoomer(); + break; + default: + break; + } + + return this->INHERITED::onHandleChar(uni); +} + +#include "SkDumpCanvas.h" + +bool SampleWindow::onHandleKey(SkKey key) { + { + SkView* view = curr_view(this); + if (view) { + SkEvent evt(gKeyEvtName); + evt.setFast32(key); + if (view->doQuery(&evt)) { + return true; + } + } + } + + switch (key) { + case kRight_SkKey: + if (this->nextSample()) { + return true; + } + break; + case kLeft_SkKey: + fCanvasType = cycle_canvastype(fCanvasType); + this->updateTitle(); + this->inval(NULL); + return true; + case kUp_SkKey: + if (USE_ARROWS_FOR_ZOOM) { + this->changeZoomLevel(1); + } else { + fNClip = !fNClip; + this->inval(NULL); + } + this->updateTitle(); + return true; + case kDown_SkKey: + if (USE_ARROWS_FOR_ZOOM) { + this->changeZoomLevel(-1); + } else { + this->setConfig(cycle_configs(this->getBitmap().config())); + } + this->updateTitle(); + return true; + case kOK_SkKey: + if (false) { + SkDebugfDumper dumper; + SkDumpCanvas dc(&dumper); + this->draw(&dc); + } else { + fRepeatDrawing = !fRepeatDrawing; + if (fRepeatDrawing) { + this->inval(NULL); + } + } + return true; + case kBack_SkKey: + this->loadView(NULL); + return true; + default: + break; + } + return this->INHERITED::onHandleKey(key); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const char gGestureClickType[] = "GestureClickType"; + +bool SampleWindow::onDispatchClick(int x, int y, Click::State state) { + if (Click::kMoved_State == state) { + updatePointer(x, y); + } + int w = SkScalarRound(this->width()); + int h = SkScalarRound(this->height()); + + // check for the resize-box + if (w - x < 16 && h - y < 16) { + return false; // let the OS handle the click + } else { + return this->INHERITED::onDispatchClick(x, y, state); + } +} + +class GestureClick : public SkView::Click { +public: + GestureClick(SkView* target) : SkView::Click(target) { + this->setType(gGestureClickType); + } + + static bool IsGesture(Click* click) { + return click->isType(gGestureClickType); + } +}; + +SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) { + return new GestureClick(this); +} + +bool SampleWindow::onClick(Click* click) { + if (GestureClick::IsGesture(click)) { + float x = SkScalarToFloat(click->fCurr.fX); + float y = SkScalarToFloat(click->fCurr.fY); + switch (click->fState) { + case SkView::Click::kDown_State: + fGesture.touchBegin(click, x, y); + break; + case SkView::Click::kMoved_State: + fGesture.touchMoved(click, x, y); + this->inval(NULL); + break; + case SkView::Click::kUp_State: + fGesture.touchEnd(click); + this->inval(NULL); + break; + } + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SampleWindow::loadView(SkView* view) { + SkView::F2BIter iter(this); + SkView* prev = iter.next(); + if (prev) { + prev->detachFromParent(); + } + + if (NULL == view) { + view = create_overview(fSamples.count(), fSamples.begin()); + } + view->setVisibleP(true); + view->setClipToBounds(false); + this->attachChildToFront(view)->unref(); + view->setSize(this->width(), this->height()); + + this->updateTitle(); +} + +static const char* gConfigNames[] = { + "unknown config", + "A1", + "A8", + "Index8", + "565", + "4444", + "8888" +}; + +static const char* configToString(SkBitmap::Config c) { + return gConfigNames[c]; +} + +static const char* gCanvasTypePrefix[] = { + "raster: ", + "picture: ", + "opengl: " +}; + +static const char* trystate_str(SkTriState state, + const char trueStr[], const char falseStr[]) { + if (kTrue_SkTriState == state) { + return trueStr; + } else if (kFalse_SkTriState == state) { + return falseStr; + } + return NULL; +} + +void SampleWindow::updateTitle() { + SkString title; + + SkView::F2BIter iter(this); + SkView* view = iter.next(); + SkEvent evt(gTitleEvtName); + if (view->doQuery(&evt)) { + title.set(evt.findString(gTitleEvtName)); + } + if (title.size() == 0) { + title.set("<unknown>"); + } + + title.prepend(gCanvasTypePrefix[fCanvasType]); + + title.prepend(" "); + title.prepend(configToString(this->getBitmap().config())); + + if (fAnimating) { + title.prepend("<A> "); + } + if (fScale) { + title.prepend("<S> "); + } + if (fRotate) { + title.prepend("<R> "); + } + if (fNClip) { + title.prepend("<C> "); + } + + title.prepend(trystate_str(fLCDState, "LCD ", "lcd ")); + title.prepend(trystate_str(fAAState, "AA ", "aa ")); + title.prepend(trystate_str(fFilterState, "H ", "h ")); + title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL); + title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL); + + if (fZoomLevel) { + title.prependf("{%d} ", fZoomLevel); + } + + if (fMeasureFPS) { + title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER); + } + if (fUsePipe && SampleView::IsSampleView(view)) { + title.prepend("<P> "); + } + if (SampleView::IsSampleView(view)) { + title.prepend("! "); + } + + this->setTitle(title.c_str()); +} + +void SampleWindow::onSizeChange() { + this->INHERITED::onSizeChange(); + + SkView::F2BIter iter(this); + SkView* view = iter.next(); + view->setSize(this->width(), this->height()); + + // rebuild our clippath + { + const SkScalar W = this->width(); + const SkScalar H = this->height(); + + fClipPath.reset(); +#if 0 + for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) { + SkRect r; + r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30)); + for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0)) + fClipPath.addRect(r); + } +#else + SkRect r; + r.set(0, 0, W, H); + fClipPath.addRect(r, SkPath::kCCW_Direction); + r.set(W/4, H/4, W*3/4, H*3/4); + fClipPath.addRect(r, SkPath::kCW_Direction); +#endif + } + + this->updateTitle(); // to refresh our config +} + +/////////////////////////////////////////////////////////////////////////////// + +static const char is_sample_view_tag[] = "sample-is-sample-view"; +static const char repeat_count_tag[] = "sample-set-repeat-count"; +static const char set_use_pipe_tag[] = "sample-set-use-pipe"; + +bool SampleView::IsSampleView(SkView* view) { + SkEvent evt(is_sample_view_tag); + return view->doQuery(&evt); +} + +bool SampleView::SetRepeatDraw(SkView* view, int count) { + SkEvent evt(repeat_count_tag); + evt.setFast32(count); + return view->doEvent(evt); +} + +bool SampleView::SetUsePipe(SkView* view, bool pred) { + SkEvent evt(set_use_pipe_tag); + evt.setFast32(pred); + return view->doEvent(evt); +} + +bool SampleView::onEvent(const SkEvent& evt) { + if (evt.isType(repeat_count_tag)) { + fRepeatCount = evt.getFast32(); + return true; + } + if (evt.isType(set_use_pipe_tag)) { + fUsePipe = !!evt.getFast32(); + return true; + } + return this->INHERITED::onEvent(evt); +} + +bool SampleView::onQuery(SkEvent* evt) { + if (evt->isType(is_sample_view_tag)) { + return true; + } + return this->INHERITED::onQuery(evt); +} + +#ifdef TEST_GPIPE + #include "SkGPipe.h" + +class SimplePC : public SkGPipeController { +public: + SimplePC(SkCanvas* target); + ~SimplePC(); + + virtual void* requestBlock(size_t minRequest, size_t* actual); + virtual void notifyWritten(size_t bytes); + +private: + SkGPipeReader fReader; + void* fBlock; + size_t fBlockSize; + size_t fBytesWritten; + int fAtomsWritten; + SkGPipeReader::Status fStatus; + + size_t fTotalWritten; +}; + +SimplePC::SimplePC(SkCanvas* target) : fReader(target) { + fBlock = NULL; + fBlockSize = fBytesWritten = 0; + fStatus = SkGPipeReader::kDone_Status; + fTotalWritten = 0; + fAtomsWritten = 0; +} + +SimplePC::~SimplePC() { +// SkASSERT(SkGPipeReader::kDone_Status == fStatus); + sk_free(fBlock); + + if (fTotalWritten) { + SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten, + fAtomsWritten, fStatus); + } +} + +void* SimplePC::requestBlock(size_t minRequest, size_t* actual) { + sk_free(fBlock); + + fBlockSize = minRequest * 4; + fBlock = sk_malloc_throw(fBlockSize); + fBytesWritten = 0; + *actual = fBlockSize; + return fBlock; +} + +void SimplePC::notifyWritten(size_t bytes) { + SkASSERT(fBytesWritten + bytes <= fBlockSize); + +#ifdef PIPE_FILE + //File is open in append mode + FILE* f = fopen(FILE_PATH, "ab"); + SkASSERT(f != NULL); + fwrite((const char*)fBlock + fBytesWritten, 1, bytes, f); + fclose(f); +#endif + + fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes); + SkASSERT(SkGPipeReader::kError_Status != fStatus); + fBytesWritten += bytes; + fTotalWritten += bytes; + + fAtomsWritten += 1; +} + +#endif + + +void SampleView::onDraw(SkCanvas* canvas) { +#ifdef TEST_GPIPE + SimplePC controller(canvas); + SkGPipeWriter writer; + if (fUsePipe) { + uint32_t flags = SkGPipeWriter::kCrossProcess_Flag; +// flags = 0; + canvas = writer.startRecording(&controller, flags); + } +#endif + + this->onDrawBackground(canvas); + + for (int i = 0; i < fRepeatCount; i++) { + SkAutoCanvasRestore acr(canvas, true); + this->onDrawContent(canvas); + } +} + +void SampleView::onDrawBackground(SkCanvas* canvas) { + canvas->drawColor(fBGColor); +} + +/////////////////////////////////////////////////////////////////////////////// + +template <typename T> void SkTBSort(T array[], int count) { + for (int i = 1; i < count - 1; i++) { + bool didSwap = false; + for (int j = count - 1; j > i; --j) { + if (array[j] < array[j-1]) { + T tmp(array[j-1]); + array[j-1] = array[j]; + array[j] = tmp; + didSwap = true; + } + } + if (!didSwap) { + break; + } + } + + for (int k = 0; k < count - 1; k++) { + SkASSERT(!(array[k+1] < array[k])); + } +} + +#include "SkRandom.h" + +static void rand_rect(SkIRect* rect, SkRandom& rand) { + int bits = 8; + int shift = 32 - bits; + rect->set(rand.nextU() >> shift, rand.nextU() >> shift, + rand.nextU() >> shift, rand.nextU() >> shift); + rect->sort(); +} + +static void dumpRect(const SkIRect& r) { + SkDebugf(" { %d, %d, %d, %d },\n", + r.fLeft, r.fTop, + r.fRight, r.fBottom); +} + +static void test_rects(const SkIRect rect[], int count) { + SkRegion rgn0, rgn1; + + for (int i = 0; i < count; i++) { + rgn0.op(rect[i], SkRegion::kUnion_Op); + // dumpRect(rect[i]); + } + rgn1.setRects(rect, count); + + if (rgn0 != rgn1) { + SkDebugf("\n"); + for (int i = 0; i < count; i++) { + dumpRect(rect[i]); + } + SkDebugf("\n"); + } +} + +static void test() { + size_t i; + + const SkIRect r0[] = { + { 0, 0, 1, 1 }, + { 2, 2, 3, 3 }, + }; + const SkIRect r1[] = { + { 0, 0, 1, 3 }, + { 1, 1, 2, 2 }, + { 2, 0, 3, 3 }, + }; + const SkIRect r2[] = { + { 0, 0, 1, 2 }, + { 2, 1, 3, 3 }, + { 4, 0, 5, 1 }, + { 6, 0, 7, 4 }, + }; + + static const struct { + const SkIRect* fRects; + int fCount; + } gRecs[] = { + { r0, SK_ARRAY_COUNT(r0) }, + { r1, SK_ARRAY_COUNT(r1) }, + { r2, SK_ARRAY_COUNT(r2) }, + }; + + for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) { + test_rects(gRecs[i].fRects, gRecs[i].fCount); + } + + SkRandom rand; + for (i = 0; i < 10000; i++) { + SkRegion rgn0, rgn1; + + const int N = 8; + SkIRect rect[N]; + for (int j = 0; j < N; j++) { + rand_rect(&rect[j], rand); + } + test_rects(rect, N); + } +} + +SkOSWindow* create_sk_window(void* hwnd) { +// test(); + return new SampleWindow(hwnd); +} + +void get_preferred_size(int* x, int* y, int* width, int* height) { + *x = 10; + *y = 50; + *width = 640; + *height = 480; +} + +void application_init() { +// setenv("ANDROID_ROOT", "../../../data", 0); +#ifdef SK_BUILD_FOR_MAC + setenv("ANDROID_ROOT", "/android/device/data", 0); +#endif + SkGraphics::Init(); + SkEvent::Init(); +} + +void application_term() { + SkEvent::Term(); + SkGraphics::Term(); +} diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp new file mode 100644 index 0000000000..8e3ad88cb4 --- /dev/null +++ b/samplecode/SampleArc.cpp @@ -0,0 +1,184 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkComposeShader.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkLayerRasterizer.h" + +#include "SkParsePath.h" +static void testparse() { + SkRect r; + r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5)); + SkPath p, p2; + SkString str, str2; + + p.addRect(r); + SkParsePath::ToSVGString(p, &str); + SkParsePath::FromSVGString(str.c_str(), &p2); + SkParsePath::ToSVGString(p2, &str2); +} + +class ArcsView : public SampleView { +public: + ArcsView() { + testparse(); + fSweep = SkIntToScalar(100); + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Arcs"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static void drawRectWithLines(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { + canvas->drawRect(r, p); + canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, p); + canvas->drawLine(r.fLeft, r.fBottom, r.fRight, r.fTop, p); + canvas->drawLine(r.fLeft, r.centerY(), r.fRight, r.centerY(), p); + canvas->drawLine(r.centerX(), r.fTop, r.centerX(), r.fBottom, p); + } + + static void draw_label(SkCanvas* canvas, const SkRect& rect, + int start, int sweep) { + SkPaint paint; + + paint.setAntiAlias(true); + paint.setTextAlign(SkPaint::kCenter_Align); + + SkString str; + + str.appendS32(start); + str.append(", "); + str.appendS32(sweep); + canvas->drawText(str.c_str(), str.size(), rect.centerX(), + rect.fBottom + paint.getTextSize() * 5/4, paint); + } + + static void drawArcs(SkCanvas* canvas) { + SkPaint paint; + SkRect r; + SkScalar w = SkIntToScalar(75); + SkScalar h = SkIntToScalar(50); + + r.set(0, 0, w, h); + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + + canvas->save(); + canvas->translate(SkIntToScalar(10), SkIntToScalar(300)); + + paint.setStrokeWidth(SkIntToScalar(1)); + + static const int gAngles[] = { + 0, 360, + 0, 45, + 0, -45, + 720, 135, + -90, 269, + -90, 270, + -90, 271, + -180, -270, + 225, 90 + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gAngles); i += 2) { + paint.setColor(SK_ColorBLACK); + drawRectWithLines(canvas, r, paint); + + paint.setColor(SK_ColorRED); + canvas->drawArc(r, SkIntToScalar(gAngles[i]), + SkIntToScalar(gAngles[i+1]), false, paint); + + draw_label(canvas, r, gAngles[i], gAngles[i+1]); + + canvas->translate(w * 8 / 7, 0); + } + + canvas->restore(); + } + + virtual void onDrawContent(SkCanvas* canvas) { + fSweep = SampleCode::GetAnimScalar(SkIntToScalar(360)/24, + SkIntToScalar(360)); +// fSweep = SkFloatToScalar(359.99f); + + SkRect r; + SkPaint paint; + + paint.setAntiAlias(true); + paint.setStrokeWidth(SkIntToScalar(2)); + paint.setStyle(SkPaint::kStroke_Style); + + r.set(0, 0, SkIntToScalar(200), SkIntToScalar(200)); + r.offset(SkIntToScalar(20), SkIntToScalar(20)); + + if (false) { + const SkScalar d = SkIntToScalar(3); + const SkScalar rad[] = { d, d, d, d, d, d, d, d }; + SkPath path; + path.addRoundRect(r, rad); + canvas->drawPath(path, paint); + return; + } + + drawRectWithLines(canvas, r, paint); + + // printf("----- sweep %g %X\n", SkScalarToFloat(fSweep), SkDegreesToRadians(fSweep)); + + + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(0x800000FF); + canvas->drawArc(r, 0, fSweep, true, paint); + + paint.setColor(0x800FF000); + canvas->drawArc(r, 0, fSweep, false, paint); + + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(SK_ColorRED); + canvas->drawArc(r, 0, fSweep, true, paint); + + paint.setStrokeWidth(0); + paint.setColor(SK_ColorBLUE); + canvas->drawArc(r, 0, fSweep, false, paint); + + drawArcs(canvas); + this->inval(NULL); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + // fSweep += SK_Scalar1; + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + SkScalar fSweep; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ArcsView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleAvoid.cpp b/samplecode/SampleAvoid.cpp new file mode 100644 index 0000000000..868a67c62f --- /dev/null +++ b/samplecode/SampleAvoid.cpp @@ -0,0 +1,99 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkAvoidXfermode.h" + +/////////////////////////////////////////////////////////////////////////////// + +class AvoidView : public SampleView { + SkShader* fShader; + + enum { + W = 480, + H = 320 + }; +public: + AvoidView() { + SkColor colors[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorCYAN, SK_ColorBLUE }; + +#if 0 + SkPoint pts[] = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; + fShader = SkGradientShader::CreateLinear(pts, colors, NULL, + SK_ARRAY_COUNT(colors), + SkShader::kMirror_TileMode); +#else + SkPoint pts[] = { { SkIntToScalar(W)/2, SkIntToScalar(H)/2 } }; + fShader = SkGradientShader::CreateRadial(pts[0], SkIntToScalar(H)/5, + colors, NULL, + SK_ARRAY_COUNT(colors), + SkShader::kMirror_TileMode); +#endif + } + + virtual ~AvoidView() { + fShader->unref(); + } + +protected: + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "AvoidXfermode"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) }; + + canvas->translate(r.width() / 6, r.height() / 6); + + paint.setShader(fShader); + canvas->drawRect(r, paint); + + static const struct { + int fTolerance; + SkAvoidXfermode::Mode fMode; + float fDX, fDY; + } gData[] = { + { 16, SkAvoidXfermode::kAvoidColor_Mode, 0, 0 }, + { 255-16, SkAvoidXfermode::kAvoidColor_Mode, 1, 0 }, + { 16, SkAvoidXfermode::kTargetColor_Mode, 0, 1 }, + { 255-16, SkAvoidXfermode::kTargetColor_Mode, 1, 1 }, + }; + + paint.setShader(NULL); + paint.setColor(SK_ColorMAGENTA); + + SkPaint frameP; + frameP.setStyle(SkPaint::kStroke_Style); + + for (size_t i = 0; i < SK_ARRAY_COUNT(gData); i++) { + SkAvoidXfermode mode(SK_ColorGREEN, gData[i].fTolerance, + gData[i].fMode); + paint.setXfermode(&mode); + int div = 3; + SkRect rr = { 0, 0, r.width()/div, r.height()/div }; + rr.offset(r.width()/4 - rr.width()/2, r.height()/4 - rr.height()/2); + rr.offset(r.width() * gData[i].fDX/2, r.height() * gData[i].fDY/2); + canvas->drawRect(rr, paint); + paint.setXfermode(NULL); + + canvas->drawRect(rr, frameP); + } + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { + return new AvoidView; +} + +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleBigGradient.cpp b/samplecode/SampleBigGradient.cpp new file mode 100644 index 0000000000..5ebb5162f3 --- /dev/null +++ b/samplecode/SampleBigGradient.cpp @@ -0,0 +1,43 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" + +static SkShader* make_grad(SkScalar w, SkScalar h) { + SkColor colors[] = { 0xFF000000, 0xFF333333 }; + SkPoint pts[] = { { 0, 0 }, { w, h } }; + return SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kClamp_TileMode); +} + +class BigGradientView : public SampleView { +public: + BigGradientView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "BigGradient"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkRect r; + r.set(0, 0, this->width(), this->height()); + SkPaint p; + p.setShader(make_grad(this->width(), this->height()))->unref(); + canvas->drawRect(r, p); + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new BigGradientView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp new file mode 100644 index 0000000000..002b2b9b17 --- /dev/null +++ b/samplecode/SampleBitmapRect.cpp @@ -0,0 +1,92 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkOSFile.h" +#include "SkStream.h" + +static SkBitmap make_bitmap() { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 64, 64); + bm.allocPixels(); + SkCanvas canvas(bm); + canvas.drawColor(SK_ColorRED); + SkPaint paint; + paint.setAntiAlias(true); + const SkPoint pts[] = { { 0, 0 }, { 64, 64 } }; + const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE }; + paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kClamp_TileMode))->unref(); + canvas.drawCircle(32, 32, 32, paint); + return bm; +} + +class BitmapRectView : public SampleView { +public: + SkBitmap fBitmap; + + BitmapRectView() { + fBitmap = make_bitmap(); + this->setBGColor(SK_ColorGRAY); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "BitmapRect"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + const SkIRect src[] = { + { 0, 0, 32, 32 }, + { 0, 0, 80, 80 }, + { 32, 32, 96, 96 }, + { -32, -32, 32, 32, } + }; + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(SK_ColorGREEN); + + SkRect dstR = { 0, 200, 128, 380 }; + + canvas->translate(16, 40); + for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) { + SkRect srcR; + srcR.set(src[i]); + + canvas->drawBitmap(fBitmap, 0, 0, &paint); + canvas->drawBitmapRect(fBitmap, &src[i], dstR, &paint); + + canvas->drawRect(dstR, paint); + canvas->drawRect(srcR, paint); + + canvas->translate(160, 0); + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new BitmapRectView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp new file mode 100644 index 0000000000..d2ea2b0aeb --- /dev/null +++ b/samplecode/SampleBlur.cpp @@ -0,0 +1,131 @@ +#include "SampleCode.h" +#include "SkBlurMaskFilter.h" +#include "SkColorPriv.h" +#include "SkGradientShader.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkUtils.h" + +static SkBitmap make_bitmap() { + SkBitmap bm; + SkColorTable* ctable = new SkColorTable(256); + + SkPMColor* c = ctable->lockColors(); + for (int i = 0; i < 256; i++) { + c[i] = SkPackARGB32(255 - i, 0, 0, 0); + } + ctable->unlockColors(true); + bm.setConfig(SkBitmap::kIndex8_Config, 256, 256); + bm.allocPixels(ctable); + ctable->unref(); + + bm.lockPixels(); + const float cx = bm.width() * 0.5f; + const float cy = bm.height() * 0.5f; + for (int y = 0; y < bm.height(); y++) { + float dy = y - cy; + dy *= dy; + uint8_t* p = bm.getAddr8(0, y); + for (int x = 0; x < 256; x++) { + float dx = x - cx; + dx *= dx; + float d = (dx + dy) / (cx/2); + int id = (int)d; + if (id > 255) { + id = 255; + } + p[x] = id; + } + } + bm.unlockPixels(); + return bm; +} + +class BlurView : public SkView { + SkBitmap fBM; +public: + BlurView() { + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Blur"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); + } + + virtual void onDraw(SkCanvas* canvas) { + drawBG(canvas); + + SkBlurMaskFilter::BlurStyle NONE = SkBlurMaskFilter::BlurStyle(-999); + static const struct { + SkBlurMaskFilter::BlurStyle fStyle; + int fCx, fCy; + } gRecs[] = { + { NONE, 0, 0 }, + { SkBlurMaskFilter::kInner_BlurStyle, -1, 0 }, + { SkBlurMaskFilter::kNormal_BlurStyle, 0, 1 }, + { SkBlurMaskFilter::kSolid_BlurStyle, 0, -1 }, + { SkBlurMaskFilter::kOuter_BlurStyle, 1, 0 }, + }; + + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(25); + canvas->translate(-40, 0); + + SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag; + for (int j = 0; j < 2; j++) { + canvas->save(); + paint.setColor(SK_ColorBLUE); + for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) { + if (gRecs[i].fStyle != NONE) { + SkMaskFilter* mf = SkBlurMaskFilter::Create(20, + gRecs[i].fStyle, + flags); + paint.setMaskFilter(mf)->unref(); + } else { + paint.setMaskFilter(NULL); + } + canvas->drawCircle(200 + gRecs[i].fCx*100.f, + 200 + gRecs[i].fCy*100.f, 50, paint); + } + // draw text + { + SkMaskFilter* mf = SkBlurMaskFilter::Create(4, + SkBlurMaskFilter::kNormal_BlurStyle, + flags); + paint.setMaskFilter(mf)->unref(); + SkScalar x = SkIntToScalar(70); + SkScalar y = SkIntToScalar(400); + paint.setColor(SK_ColorBLACK); + canvas->drawText("Hamburgefons Style", 18, x, y, paint); + canvas->drawText("Hamburgefons Style", 18, x, y + SkIntToScalar(50), paint); + paint.setMaskFilter(NULL); + paint.setColor(SK_ColorWHITE); + x -= SkIntToScalar(2); + y -= SkIntToScalar(2); + canvas->drawText("Hamburgefons Style", 18, x, y, paint); + } + canvas->restore(); + flags = SkBlurMaskFilter::kHighQuality_BlurFlag; + canvas->translate(350, 0); + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new BlurView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleBox.cpp b/samplecode/SampleBox.cpp new file mode 100644 index 0000000000..d445df7596 --- /dev/null +++ b/samplecode/SampleBox.cpp @@ -0,0 +1,48 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" + +class SimpleView : public SampleView { +public: + SimpleView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Box Gradient"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3))); + paint.setStyle(SkPaint::kFill_Style); + + SkRect r; + SkScalar x,y; + x = 10; + y = 10; + + r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100)); + for (int i = 0; i < 256; ++i) { + canvas->translate(1, 1); + paint.setColor(0xFF000000 + i * 0x00010000); + canvas->drawRect(r, paint); + } + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new SimpleView; } +static SkViewRegister reg(MyFactory);
\ No newline at end of file diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp new file mode 100644 index 0000000000..2db396889d --- /dev/null +++ b/samplecode/SampleCamera.cpp @@ -0,0 +1,99 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkCamera.h" +#include "SkEmbossMaskFilter.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkRandom.h" +#include "SkImageDecoder.h" + +class CameraView : public SampleView { + SkTDArray<SkShader*> fShaders; + int fShaderIndex; + bool fFrontFace; +public: + CameraView() { + fRX = fRY = fRZ = 0; + fShaderIndex = 0; + fFrontFace = false; + + for (int i = 0;; i++) { + SkString str; + str.printf("/skimages/elephant%d.jpeg", i); + SkBitmap bm; + if (SkImageDecoder::DecodeFile(str.c_str(), &bm)) { + SkShader* s = SkShader::CreateBitmapShader(bm, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); + + SkRect src = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; + SkRect dst = { -150, -150, 150, 150 }; + SkMatrix matrix; + matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); + s->setLocalMatrix(matrix); + *fShaders.append() = s; + } else { + break; + } + } + this->setBGColor(0xFFDDDDDD); + } + + virtual ~CameraView() { + fShaders.unrefAll(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) + { + SampleCode::TitleR(evt, "Camera"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(this->width()/2, this->height()/2); + + Sk3DView view; + view.rotateX(fRX); + view.rotateY(fRY); + view.applyToCanvas(canvas); + + SkPaint paint; + if (fShaders.count() > 0) { + bool frontFace = view.dotWithNormal(0, 0, SK_Scalar1) < 0; + if (frontFace != fFrontFace) { + fFrontFace = frontFace; + fShaderIndex = (fShaderIndex + 1) % fShaders.count(); + } + + paint.setAntiAlias(true); + paint.setShader(fShaders[fShaderIndex]); + SkRect r = { -150, -150, 150, 150 }; + canvas->drawRoundRect(r, 30, 30, paint); + } + + fRY += SampleCode::GetAnimSecondsDelta() * 90; + if (fRY >= SkIntToScalar(360)) { + fRY = 0; + } + this->inval(NULL); + } + +private: + SkScalar fRX, fRY, fRZ; + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new CameraView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleCircle.cpp b/samplecode/SampleCircle.cpp new file mode 100644 index 0000000000..2abc28de3e --- /dev/null +++ b/samplecode/SampleCircle.cpp @@ -0,0 +1,126 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" + +// ensure that we don't accidentally screw up the bounds when the oval is +// fractional, and the impl computes the center and radii, and uses them to +// reconstruct the edges of the circle. +// see bug# 1504910 +static void test_circlebounds(SkCanvas* canvas) { +#ifdef SK_SCALAR_IS_FLOAT + SkRect r = { 1.39999998f, 1, 21.3999996f, 21 }; + SkPath p; + p.addOval(r); + SkASSERT(r == p.getBounds()); +#endif +} + +class CircleView : public SampleView { +public: + static const SkScalar ANIM_DX; + static const SkScalar ANIM_DY; + static const SkScalar ANIM_RAD; + SkScalar fDX, fDY, fRAD; + + CircleView() { + fDX = fDY = fRAD = 0; + fN = 3; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Circles"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void circle(SkCanvas* canvas, int width, bool aa) { + SkPaint paint; + + paint.setAntiAlias(aa); + if (width < 0) { + paint.setStyle(SkPaint::kFill_Style); + } else { + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(width)); + } + canvas->drawCircle(0, 0, SkIntToScalar(9) + fRAD, paint); + } + + void drawSix(SkCanvas* canvas, SkScalar dx, SkScalar dy) { + for (int width = -1; width <= 1; width++) { + canvas->save(); + circle(canvas, width, false); + canvas->translate(0, dy); + circle(canvas, width, true); + canvas->restore(); + canvas->translate(dx, 0); + } + } + + static void blowup(SkCanvas* canvas, const SkIRect& src, const SkRect& dst) { + SkDevice* device = canvas->getDevice(); + const SkBitmap& bm = device->accessBitmap(false); + canvas->drawBitmapRect(bm, &src, dst, NULL); + } + + static void make_poly(SkPath* path, int n) { + if (n <= 0) { + return; + } + path->incReserve(n + 1); + path->moveTo(SK_Scalar1, 0); + SkScalar step = SK_ScalarPI * 2 / n; + SkScalar angle = 0; + for (int i = 1; i < n; i++) { + angle += step; + SkScalar c, s = SkScalarSinCos(angle, &c); + path->lineTo(c, s); + } + path->close(); + } + + static void rotate(SkCanvas* canvas, SkScalar angle, SkScalar px, SkScalar py) { + canvas->translate(-px, -py); + canvas->rotate(angle); + canvas->translate(px, py); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); +// canvas->drawCircle(250, 250, 220, paint); + SkMatrix matrix; + matrix.setScale(SkIntToScalar(100), SkIntToScalar(100)); + matrix.postTranslate(SkIntToScalar(200), SkIntToScalar(200)); + canvas->concat(matrix); + for (int n = 3; n < 20; n++) { + SkPath path; + make_poly(&path, n); + SkAutoCanvasRestore acr(canvas, true); + canvas->rotate(SkIntToScalar(10) * (n - 3)); + canvas->translate(-SK_Scalar1, 0); + canvas->drawPath(path, paint); + } + } + +private: + int fN; + typedef SampleView INHERITED; +}; + +const SkScalar CircleView::ANIM_DX(SK_Scalar1 / 67); +const SkScalar CircleView::ANIM_DY(SK_Scalar1 / 29); +const SkScalar CircleView::ANIM_RAD(SK_Scalar1 / 19); + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new CircleView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleClamp.cpp b/samplecode/SampleClamp.cpp new file mode 100644 index 0000000000..88c1b9179b --- /dev/null +++ b/samplecode/SampleClamp.cpp @@ -0,0 +1,61 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" +#include "SkGradientShader.h" +#include "SkPicture.h" + +static SkShader* make_linear() { + SkPoint pts[] = { 0, 0, SK_Scalar1/500, SK_Scalar1/500 }; + SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; + return SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kClamp_TileMode); +} + +class ClampView : public SampleView { + SkShader* fGrad; + +public: + ClampView() { + fGrad = make_linear(); + } + + virtual ~ClampView() { + fGrad->unref(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Clamp"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setShader(fGrad); + +// canvas->translate(this->width()/2, this->height()/2); + canvas->translate(64, 64); + canvas->drawPaint(paint); + + SkPicture pic; + SkCanvas* c = pic.beginRecording(100, 100, 0); + SkCanvas::LayerIter layerIterator(c, false); + layerIterator.next(); + layerIterator.done(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ClampView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleCode.h b/samplecode/SampleCode.h new file mode 100644 index 0000000000..c42ee25801 --- /dev/null +++ b/samplecode/SampleCode.h @@ -0,0 +1,81 @@ +#ifndef SampleCode_DEFINED +#define SampleCode_DEFINED + +#include "SkColor.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkView.h" + +class SampleCode { +public: + static bool KeyQ(const SkEvent&, SkKey* outKey); + static bool CharQ(const SkEvent&, SkUnichar* outUni); + + static bool TitleQ(const SkEvent&); + static void TitleR(SkEvent*, const char title[]); + + static bool PrefSizeQ(const SkEvent&); + static void PrefSizeR(SkEvent*, SkScalar width, SkScalar height); + + static bool FastTextQ(const SkEvent&); + + static SkMSec GetAnimTime(); + static SkMSec GetAnimTimeDelta(); + static SkScalar GetAnimSecondsDelta(); + static SkScalar GetAnimScalar(SkScalar speedPerSec, SkScalar period = 0); +}; + +////////////////////////////////////////////////////////////////////////////// + +typedef SkView* (*SkViewFactory)(); + +class SkViewRegister : SkNoncopyable { +public: + explicit SkViewRegister(SkViewFactory); + + static const SkViewRegister* Head() { return gHead; } + + SkViewRegister* next() const { return fChain; } + SkViewFactory factory() const { return fFact; } + +private: + SkViewFactory fFact; + SkViewRegister* fChain; + + static SkViewRegister* gHead; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SampleView : public SkView { +public: + SampleView() : fRepeatCount(1), fBGColor(SK_ColorWHITE) { + fUsePipe = false; + } + + void setBGColor(SkColor color) { fBGColor = color; } + + static bool IsSampleView(SkView*); + static bool SetRepeatDraw(SkView*, int count); + static bool SetUsePipe(SkView*, bool); + +protected: + virtual void onDrawBackground(SkCanvas*); + virtual void onDrawContent(SkCanvas*) = 0; + + // overrides + virtual bool onEvent(const SkEvent& evt); + virtual bool onQuery(SkEvent* evt); + virtual void onDraw(SkCanvas*); + +private: + int fRepeatCount; + SkColor fBGColor; + + bool fUsePipe; + + typedef SkView INHERITED; +}; + +#endif + diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp new file mode 100644 index 0000000000..0e1fb11f5a --- /dev/null +++ b/samplecode/SampleColorFilter.cpp @@ -0,0 +1,210 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkColorFilter.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkShader.h" + +static int inflate5To8(int x) { + return (x << 3) | (x >> 2); +} + +static int trunc5(int x) { + return x >> 3; +} + +#define SK_R16_BITS 5 + +static int round5_slow(int x) { + int orig = x & 7; + int fake = x >> 5; + int trunc = x >> 3; + + int diff = fake - orig; + + int bias = 0; + if (diff > 4) { + bias = -1; + } else if (diff < -4) { + bias = 1; + } + return trunc + bias; +} + +static int round5_fast(int x) { + int result = x + 3 - (x >> 5) + (x >> 7); + result >>= 3; + + { + int r2 = round5_slow(x); + SkASSERT(r2 == result); + } + return result; +} + +static void test_5bits() { + int e0 = 0; + int e1 = 0; + int e2 = 0; + int ae0 = 0; + int ae1 = 0; + int ae2 = 0; + for (int i = 0; i < 256; i++) { + int t0 = trunc5(i); + int t1 = round5_fast(i); + int t2 = trunc5(i); + int v0 = inflate5To8(t0); + int v1 = inflate5To8(t1); + int v2 = inflate5To8(t2); + int err0 = i - v0; + int err1 = i - v1; + int err2 = i - v2; + SkDebugf("--- %3d : trunc=%3d (%2d) round:%3d (%2d) \n"/*new:%d (%2d)\n"*/, i, + v0, err0, v1, err1, v2, err2); + + + e0 += err0; + e1 += err1; + e2 += err2; + ae0 += SkAbs32(err0); + ae1 += SkAbs32(err1); + ae2 += SkAbs32(err2); + } + SkDebugf("--- trunc: %d %d round: %d %d new: %d %d\n", e0, ae0, e1, ae1, e2, ae2); +} + +static SkShader* createChecker() { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); + bm.allocPixels(); + bm.lockPixels(); + *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(0xFFFFFFFF); + *bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(0xFFCCCCCC); + SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + + SkMatrix m; + m.setScale(12, 12); + s->setLocalMatrix(m); + return s; +} + +static SkBitmap createBitmap(int n) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkCanvas canvas(bitmap); + SkRect r; + r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n)); + r.inset(SK_Scalar1, SK_Scalar1); + + SkPaint paint; + paint.setAntiAlias(true); + + paint.setColor(SK_ColorRED); + canvas.drawOval(r, paint); + + r.inset(SK_Scalar1*n/4, SK_Scalar1*n/4); + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + paint.setColor(0x800000FF); + canvas.drawOval(r, paint); + + return bitmap; +} + +class ColorFilterView : public SampleView { + SkBitmap fBitmap; + SkShader* fShader; + enum { + N = 64 + }; +public: + ColorFilterView() { + fBitmap = createBitmap(N); + fShader = createChecker(); + +// test_5bits(); + } + + virtual ~ColorFilterView() { + fShader->unref(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "ColorFilter"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawBackground(SkCanvas* canvas) { + SkPaint paint; + paint.setShader(fShader); + canvas->drawPaint(paint); + } + + virtual void onDrawContent(SkCanvas* canvas) { + if (false) { + SkPaint p; + p.setAntiAlias(true); + SkRect r = { 20.4f, 10, 20.6f, 20 }; + canvas->drawRect(r, p); + r.set(30.9f, 10, 31.1f, 20); + canvas->drawRect(r, p); + return; + } + + static const SkXfermode::Mode gModes[] = { + SkXfermode::kClear_Mode, + SkXfermode::kSrc_Mode, + SkXfermode::kDst_Mode, + SkXfermode::kSrcOver_Mode, + SkXfermode::kDstOver_Mode, + SkXfermode::kSrcIn_Mode, + SkXfermode::kDstIn_Mode, + SkXfermode::kSrcOut_Mode, + SkXfermode::kDstOut_Mode, + SkXfermode::kSrcATop_Mode, + SkXfermode::kDstATop_Mode, + SkXfermode::kXor_Mode, + SkXfermode::kPlus_Mode, + SkXfermode::kMultiply_Mode, + }; + + static const SkColor gColors[] = { + 0xFF000000, + 0x80000000, + 0xFF00FF00, + 0x8000FF00, + 0x00000000, + }; + + float scale = 1.5f; + SkPaint paint; + canvas->translate(N / 8, N / 8); + + for (size_t y = 0; y < SK_ARRAY_COUNT(gColors); y++) { + for (size_t x = 0; x < SK_ARRAY_COUNT(gModes); x++) { + SkColorFilter* cf = SkColorFilter::CreateModeFilter(gColors[y], gModes[x]); + SkSafeUnref(paint.setColorFilter(cf)); + canvas->drawBitmap(fBitmap, x * N * 1.25f, y * N * scale, &paint); + } + } + + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ColorFilterView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp new file mode 100644 index 0000000000..672d0555c5 --- /dev/null +++ b/samplecode/SampleComplexClip.cpp @@ -0,0 +1,147 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkPath.h" +#include "SkView.h" + +class ComplexClipView : public SampleView { +public: + ComplexClipView() { + this->setBGColor(0xFFA0DDA0); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "ComplexClip"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPath path; + path.moveTo(SkIntToScalar(0), SkIntToScalar(50)); + path.quadTo(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(50), SkIntToScalar(0)); + path.lineTo(SkIntToScalar(175), SkIntToScalar(0)); + path.quadTo(SkIntToScalar(200), SkIntToScalar(0), SkIntToScalar(200), SkIntToScalar(25)); + path.lineTo(SkIntToScalar(200), SkIntToScalar(150)); + path.quadTo(SkIntToScalar(200), SkIntToScalar(200), SkIntToScalar(150), SkIntToScalar(200)); + path.lineTo(SkIntToScalar(0), SkIntToScalar(200)); + path.close(); + path.moveTo(SkIntToScalar(50), SkIntToScalar(50)); + path.lineTo(SkIntToScalar(150), SkIntToScalar(50)); + path.lineTo(SkIntToScalar(150), SkIntToScalar(125)); + path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150)); + path.lineTo(SkIntToScalar(50), SkIntToScalar(150)); + path.close(); + path.setFillType(SkPath::kEvenOdd_FillType); + SkColor pathColor = SK_ColorBLACK; + SkPaint pathPaint; + pathPaint.setAntiAlias(true); + pathPaint.setColor(pathColor); + + SkPath clipA; + clipA.moveTo(SkIntToScalar(10), SkIntToScalar(20)); + clipA.lineTo(SkIntToScalar(165), SkIntToScalar(22)); + clipA.lineTo(SkIntToScalar(70), SkIntToScalar(105)); + clipA.lineTo(SkIntToScalar(165), SkIntToScalar(177)); + clipA.lineTo(SkIntToScalar(-5), SkIntToScalar(180)); + clipA.close(); + SkColor colorA = SK_ColorCYAN; + + SkPath clipB; + clipB.moveTo(SkIntToScalar(40), SkIntToScalar(10)); + clipB.lineTo(SkIntToScalar(190), SkIntToScalar(15)); + clipB.lineTo(SkIntToScalar(195), SkIntToScalar(190)); + clipB.lineTo(SkIntToScalar(40), SkIntToScalar(185)); + clipB.lineTo(SkIntToScalar(155), SkIntToScalar(100)); + clipB.close(); + SkColor colorB = SK_ColorRED; + + SkPaint paint; + paint.setAntiAlias(true); + + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(0); + + canvas->translate(SkIntToScalar(10),SkIntToScalar(10)); + canvas->drawPath(path, pathPaint); + paint.setColor(colorA); + canvas->drawPath(clipA, paint); + paint.setColor(colorB); + canvas->drawPath(clipB, paint); + + static const struct { + SkRegion::Op fOp; + const char* fName; + } gOps[] = { //extra spaces in names for measureText + {SkRegion::kIntersect_Op, "Isect "}, + {SkRegion::kDifference_Op, "Diff " }, + {SkRegion::kUnion_Op, "Union "}, + {SkRegion::kXOR_Op, "Xor " }, + {SkRegion::kReverseDifference_Op, "RDiff "} + }; + + canvas->translate(0, SkIntToScalar(40)); + canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4); + canvas->save(); + + for (int invA = 0; invA < 2; ++invA) { + for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { + int idx = invA * SK_ARRAY_COUNT(gOps) + op; + if (!(idx % 3)) { + canvas->restore(); + canvas->translate(0, SkIntToScalar(250)); + canvas->save(); + } + canvas->save(); + // set clip + clipA.setFillType(invA ? SkPath::kInverseEvenOdd_FillType : + SkPath::kEvenOdd_FillType); + canvas->clipPath(clipA); + canvas->clipPath(clipB, gOps[op].fOp); + + // draw path clipped + canvas->drawPath(path, pathPaint); + canvas->restore(); + + // draw path in hairline + paint.setColor(pathColor); + canvas->drawPath(path, paint); + + // draw clips in hair line + paint.setColor(colorA); + canvas->drawPath(clipA, paint); + paint.setColor(colorB); + canvas->drawPath(clipB, paint); + + paint.setTextSize(SkIntToScalar(20)); + + SkScalar txtX = SkIntToScalar(55); + paint.setColor(colorA); + const char* aTxt = invA ? "InverseA " : "A "; + canvas->drawText(aTxt, strlen(aTxt), txtX, SkIntToScalar(220), paint); + txtX += paint.measureText(aTxt, strlen(aTxt)); + paint.setColor(SK_ColorBLACK); + canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), + txtX, SkIntToScalar(220), paint); + txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName)); + paint.setColor(colorB); + canvas->drawText("B", 1, txtX, SkIntToScalar(220), paint); + + canvas->translate(SkIntToScalar(250),0); + } + } + canvas->restore(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ComplexClipView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleCull.cpp b/samplecode/SampleCull.cpp new file mode 100644 index 0000000000..7b4eab605d --- /dev/null +++ b/samplecode/SampleCull.cpp @@ -0,0 +1,189 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkCornerPathEffect.h" +#include "SkCullPoints.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkRandom.h" + +static void addbump(SkPath* path, const SkPoint pts[2], SkScalar bump) { + SkVector tang; + + tang.setLength(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY, bump); + + path->lineTo(SkScalarHalf(pts[0].fX + pts[1].fX) - tang.fY, + SkScalarHalf(pts[0].fY + pts[1].fY) + tang.fX); + path->lineTo(pts[1]); +} + +static void subdivide(SkPath* path, SkScalar bump) { + SkPath::Iter iter(*path, false); + SkPoint pts[4]; + SkPath tmp; + + for (;;) + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + tmp.moveTo(pts[0]); + break; + case SkPath::kLine_Verb: + addbump(&tmp, pts, bump); + bump = -bump; + break; + case SkPath::kDone_Verb: + goto FINISH; + default: + break; + } + +FINISH: + path->swap(tmp); +} + +static SkIPoint* getpts(const SkPath& path, int* count) { + SkPoint pts[4]; + int n = 1; + SkIPoint* array; + + { + SkPath::Iter iter(path, false); + for (;;) + switch (iter.next(pts)) { + case SkPath::kLine_Verb: + n += 1; + break; + case SkPath::kDone_Verb: + goto FINISHED; + default: + break; + } + } + +FINISHED: + array = new SkIPoint[n]; + n = 0; + + { + SkPath::Iter iter(path, false); + for (;;) + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + array[n++].set(SkScalarRound(pts[0].fX), SkScalarRound(pts[0].fY)); + break; + case SkPath::kLine_Verb: + array[n++].set(SkScalarRound(pts[1].fX), SkScalarRound(pts[1].fY)); + break; + case SkPath::kDone_Verb: + goto FINISHED2; + default: + break; + } + } + +FINISHED2: + *count = n; + return array; +} + +static SkScalar nextScalarRange(SkRandom& rand, SkScalar min, SkScalar max) { + return min + SkScalarMul(rand.nextUScalar1(), max - min); +} + +class CullView : public SampleView { +public: + CullView() { + fClip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160)); + + SkRandom rand; + + for (int i = 0; i < 50; i++) { + SkScalar x = nextScalarRange(rand, -fClip.width()*1, fClip.width()*2); + SkScalar y = nextScalarRange(rand, -fClip.height()*1, fClip.height()*2); + if (i == 0) + fPath.moveTo(x, y); + else + fPath.lineTo(x, y); + } + + SkScalar bump = fClip.width()/8; + subdivide(&fPath, bump); + subdivide(&fPath, bump); + subdivide(&fPath, bump); + fPoints = getpts(fPath, &fPtCount); + + this->setBGColor(0xFFDDDDDD); + } + + virtual ~CullView() { + delete[] fPoints; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Culling"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkAutoCanvasRestore ar(canvas, true); + + canvas->translate( SkScalarHalf(this->width() - fClip.width()), + SkScalarHalf(this->height() - fClip.height())); + + // canvas->scale(SK_Scalar1*3, SK_Scalar1*3, 0, 0); + + SkPaint paint; + + // paint.setAntiAliasOn(true); + paint.setStyle(SkPaint::kStroke_Style); + + canvas->drawRect(fClip, paint); + +#if 1 + paint.setColor(0xFF555555); + paint.setStrokeWidth(SkIntToScalar(2)); +// paint.setPathEffect(new SkCornerPathEffect(SkIntToScalar(30)))->unref(); + canvas->drawPath(fPath, paint); +// paint.setPathEffect(NULL); +#endif + + SkPath tmp; + SkIRect iclip; + fClip.round(&iclip); + + SkCullPointsPath cpp(iclip, &tmp); + + cpp.moveTo(fPoints[0].fX, fPoints[0].fY); + for (int i = 0; i < fPtCount; i++) + cpp.lineTo(fPoints[i].fX, fPoints[i].fY); + + paint.setColor(SK_ColorRED); + paint.setStrokeWidth(SkIntToScalar(3)); + paint.setStrokeJoin(SkPaint::kRound_Join); + canvas->drawPath(tmp, paint); + + this->inval(NULL); + } + +private: + SkRect fClip; + SkIPoint* fPoints; + SkPath fPath; + int fPtCount; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new CullView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleDash.cpp b/samplecode/SampleDash.cpp new file mode 100644 index 0000000000..4cef07f208 --- /dev/null +++ b/samplecode/SampleDash.cpp @@ -0,0 +1,88 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" +#include "SkDashPathEffect.h" +#include "SkShader.h" + +static void setBitmapDash(SkPaint* paint, int width) { + SkColor c = paint->getColor(); + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1); + bm.allocPixels(); + bm.lockPixels(); + *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c), + SkColorGetG(c), SkColorGetB(c)); + *bm.getAddr32(1, 0) = 0; + bm.unlockPixels(); + + SkMatrix matrix; + matrix.setScale(SkIntToScalar(width), SK_Scalar1); + + SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, + SkShader::kClamp_TileMode); + s->setLocalMatrix(matrix); + + paint->setShader(s)->unref(); +} + +class DashView : public SampleView { +public: + DashView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Dash"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + static const char* gStr[] = { + "11", + "44", + "112233", + "411327463524", + }; + + SkPaint paint; + paint.setStrokeWidth(SkIntToScalar(1)); + + SkScalar x0 = SkIntToScalar(10); + SkScalar y0 = SkIntToScalar(10); + SkScalar x1 = x0 + SkIntToScalar(1000); + for (size_t i = 0; i < SK_ARRAY_COUNT(gStr); i++) { + SkScalar interval[12]; + size_t len = SkMin32(strlen(gStr[i]), SK_ARRAY_COUNT(interval)); + for (size_t j = 0; j < len; j++) { + interval[j] = SkIntToScalar(gStr[i][j] - '0'); + } + + SkDashPathEffect dash(interval, len, 0); + paint.setPathEffect(&dash); + canvas->drawLine(x0, y0, x1, y0, paint); + paint.setPathEffect(NULL); + + y0 += paint.getStrokeWidth() * 3; + } + + setBitmapDash(&paint, 3); + canvas->drawLine(x0, y0, x1, y0, paint); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new DashView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleDecode.cpp b/samplecode/SampleDecode.cpp new file mode 100644 index 0000000000..b192c5d599 --- /dev/null +++ b/samplecode/SampleDecode.cpp @@ -0,0 +1,69 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkImageDecoder.h" +#include "SkStream.h" + +static const struct { + SkBitmap::Config fPrefConfig; + bool fDither; +} gRec[] = { + { SkBitmap::kIndex8_Config, false }, + { SkBitmap::kARGB_8888_Config, false }, + { SkBitmap::kARGB_4444_Config, false }, + { SkBitmap::kARGB_4444_Config, true }, + { SkBitmap::kRGB_565_Config, false }, + { SkBitmap::kRGB_565_Config, true }, +}; + +class DecodeView : public SkView { +public: + SkBitmap fBitmap[SK_ARRAY_COUNT(gRec)]; + + DecodeView() { + SkFILEStream stream("/skimages/index.png"); + SkImageDecoder* codec = SkImageDecoder::Factory(&stream); + if (codec) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { + stream.rewind(); + codec->setDitherImage(gRec[i].fDither); + codec->decode(&stream, &fBitmap[i], gRec[i].fPrefConfig, + SkImageDecoder::kDecodePixels_Mode); + } + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "ImageDecoder"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); +// canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); + + for (size_t i = 0; i < SK_ARRAY_COUNT(fBitmap); i++) { + canvas->drawBitmap(fBitmap[i], 0, 0); + canvas->translate(SkIntToScalar(fBitmap[i].width()), 0); + } + } +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new DecodeView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp new file mode 100644 index 0000000000..3e77a5deef --- /dev/null +++ b/samplecode/SampleDither.cpp @@ -0,0 +1,178 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkDither.h" + +static void draw_sweep(SkCanvas* c, int width, int height, SkScalar angle) { + SkRect r; + SkPaint p; + + p.setAntiAlias(true); +// p.setDither(true); + p.setStrokeWidth(SkIntToScalar(width/10)); + p.setStyle(SkPaint::kStroke_Style); + + r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height)); + + // SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN, SK_ColorCYAN }; + SkColor colors[] = { 0x4c737373, 0x4c737373, 0xffffd300 }; + SkShader* s = SkGradientShader::CreateSweep(r.centerX(), r.centerY(), + colors, NULL, SK_ARRAY_COUNT(colors)); + p.setShader(s)->unref(); + + SkAutoCanvasRestore acr(c, true); + + c->translate(r.centerX(), r.centerY()); + c->rotate(angle); + c->translate(-r.centerX(), -r.centerY()); + + SkRect bounds = r; + r.inset(p.getStrokeWidth(), p.getStrokeWidth()); + SkRect innerBounds = r; + + if (true) { + c->drawOval(r, p); + } else { + SkScalar x = r.centerX(); + SkScalar y = r.centerY(); + SkScalar radius = r.width() / 2; + SkScalar thickness = p.getStrokeWidth(); + SkScalar sweep = SkFloatToScalar(360.0f); + SkPath path; + + path.moveTo(x + radius, y); + // outer top + path.lineTo(x + radius + thickness, y); + // outer arc + path.arcTo(bounds, 0, sweep, false); + // inner arc + path.arcTo(innerBounds, sweep, -sweep, false); + path.close(); + } +} + +static void make_bm(SkBitmap* bm) { + bm->setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + bm->allocPixels(); +#if 0 + bm->eraseColor(SK_ColorBLUE); + return; +#else + bm->eraseColor(0); +#endif + + SkCanvas c(*bm); + draw_sweep(&c, bm->width(), bm->height(), 0); +} + +static void pre_dither(const SkBitmap& bm) { + SkAutoLockPixels alp(bm); + + for (int y = 0; y < bm.height(); y++) { + DITHER_4444_SCAN(y); + + SkPMColor* p = bm.getAddr32(0, y); + for (int x = 0; x < bm.width(); x++) { + SkPMColor c = *p; + + unsigned a = SkGetPackedA32(c); + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + unsigned d = DITHER_VALUE(x); + + a = SkDITHER_A32To4444(a, d); + r = SkDITHER_R32To4444(r, d); + g = SkDITHER_G32To4444(g, d); + b = SkDITHER_B32To4444(b, d); + + a = SkA4444ToA32(a); + r = SkR4444ToR32(r); + g = SkG4444ToG32(g); + b = SkB4444ToB32(b); + + *p++ = SkPackARGB32(a, r, g, b); + } + } +} + +class DitherView : public SampleView { +public: + SkBitmap fBM, fBMPreDither, fBM16; + SkScalar fAngle; + + DitherView() { + make_bm(&fBM); + make_bm(&fBMPreDither); + pre_dither(fBMPreDither); + fBM.copyTo(&fBM16, SkBitmap::kARGB_4444_Config); + + fAngle = 0; + + this->setBGColor(0xFF181818); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Dither"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(10); + const SkScalar DX = SkIntToScalar(fBM.width() + 10); + + paint.setAntiAlias(true); + + if (true) { + canvas->drawBitmap(fBM, x, y, &paint); + x += DX; + paint.setDither(true); + canvas->drawBitmap(fBM, x, y, &paint); + + x += DX; + paint.setDither(false); + canvas->drawBitmap(fBMPreDither, x, y, &paint); + + x += DX; + canvas->drawBitmap(fBM16, x, y, &paint); + } + + canvas->translate(DX, DX*2); + draw_sweep(canvas, fBM.width(), fBM.height(), fAngle); + canvas->translate(DX, 0); + draw_sweep(canvas, fBM.width()>>1, fBM.height()>>1, fAngle); + canvas->translate(DX, 0); + draw_sweep(canvas, fBM.width()>>2, fBM.height()>>2, fAngle); + + fAngle += SK_Scalar1/2; + this->inval(NULL); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new DitherView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleDitherBitmap.cpp b/samplecode/SampleDitherBitmap.cpp new file mode 100644 index 0000000000..0d62446dec --- /dev/null +++ b/samplecode/SampleDitherBitmap.cpp @@ -0,0 +1,141 @@ +#include "SampleCode.h" +#include "SkColorPriv.h" +#include "SkGradientShader.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkUtils.h" + +static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) { + canvas->drawRect(r, p); + + SkPaint frame(p); + frame.setShader(NULL); + frame.setStyle(SkPaint::kStroke_Style); + canvas->drawRect(r, frame); +} + +static void draw_gradient(SkCanvas* canvas) { + SkRect r = { 0, 0, SkIntToScalar(256), SkIntToScalar(32) }; + SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } }; + SkColor colors[] = { 0xFF000000, 0xFFFF0000 }; + SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kClamp_TileMode); + + SkPaint p; + p.setShader(s)->unref(); + draw_rect(canvas, r, p); + + canvas->translate(0, SkIntToScalar(40)); + p.setDither(true); + draw_rect(canvas, r, p); +} + +static void test_pathregion() { + SkPath path; + SkRegion region; + path.moveTo(25071800.f, -141823808.f); + path.lineTo(25075500.f, -141824000.f); + path.lineTo(25075400.f, -141827712.f); + path.lineTo(25071810.f, -141827600.f); + path.close(); + + SkIRect bounds; + path.getBounds().round(&bounds); + SkRegion clip(bounds); + bool result = region.setPath(path, clip); // <-- !! DOWN !! + SkDebugf("----- result %d\n", result); +} + +static SkBitmap make_bitmap() { + SkBitmap bm; + SkColorTable* ctable = new SkColorTable(256); + + SkPMColor* c = ctable->lockColors(); + for (int i = 0; i < 256; i++) { + c[i] = SkPackARGB32(0xFF, i, 0, 0); + } + ctable->unlockColors(true); + bm.setConfig(SkBitmap::kIndex8_Config, 256, 32); + bm.allocPixels(ctable); + ctable->unref(); + + bm.lockPixels(); + for (int y = 0; y < bm.height(); y++) { + uint8_t* p = bm.getAddr8(0, y); + for (int x = 0; x < 256; x++) { + p[x] = x; + } + } + bm.unlockPixels(); + return bm; +} + +class DitherBitmapView : public SampleView { + SkBitmap fBM8; + SkBitmap fBM32; +public: + DitherBitmapView() { + test_pathregion(); + fBM8 = make_bitmap(); + fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "DitherBitmap"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) { + SkAutoLockPixels alp(*bm); // needed for ctable + bm->setIsOpaque(isOpaque); + SkColorTable* ctable = bm->getColorTable(); + if (ctable) { + ctable->setIsOpaque(isOpaque); + } + } + + static void draw2(SkCanvas* canvas, const SkBitmap& bm) { + SkPaint paint; + SkBitmap bitmap(bm); + + setBitmapOpaque(&bitmap, false); + paint.setDither(false); + canvas->drawBitmap(bitmap, 0, 0, &paint); + paint.setDither(true); + canvas->drawBitmap(bitmap, 0, SkIntToScalar(bm.height() + 10), &paint); + + setBitmapOpaque(&bitmap, true); + SkScalar x = SkIntToScalar(bm.width() + 10); + paint.setDither(false); + canvas->drawBitmap(bitmap, x, 0, &paint); + paint.setDither(true); + canvas->drawBitmap(bitmap, x, SkIntToScalar(bm.height() + 10), &paint); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); + + draw2(canvas, fBM8); + canvas->translate(0, SkIntToScalar(fBM8.height() *3)); + draw2(canvas, fBM32); + + canvas->translate(0, SkIntToScalar(fBM8.height() *3)); + draw_gradient(canvas); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new DitherBitmapView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleDraw.cpp b/samplecode/SampleDraw.cpp new file mode 100644 index 0000000000..deb1fb2728 --- /dev/null +++ b/samplecode/SampleDraw.cpp @@ -0,0 +1,373 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" + +static void test_clearonlayers(SkCanvas* canvas) { + SkCanvas& c = *canvas; + + SkPaint paint; + paint.setColor(SK_ColorBLUE); + paint.setStyle(SkPaint::kStrokeAndFill_Style); + SkRect rect = SkRect::MakeXYWH(25, 25, 50, 50); + c.drawRect(rect, paint); + + c.clipRect(rect); + + c.saveLayer(NULL, NULL); + rect = SkRect::MakeXYWH(50, 10, 40, 80); + c.clipRect(rect, SkRegion::kUnion_Op); + + rect = SkRect::MakeXYWH(50, 0, 50, 100); + // You might draw something here, but it's not necessary. + // paint.setColor(SK_ColorRED); + // c.drawRect(rect, paint); + paint.setXfermodeMode(SkXfermode::kClear_Mode); + c.drawRect(rect, paint); + c.restore(); +} + +static void test_strokerect(SkCanvas* canvas, const SkRect& r) { + SkPaint p; + + p.setAntiAlias(true); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(4); + + canvas->drawRect(r, p); + + SkPath path; + SkRect r2(r); + r2.offset(18, 0); + path.addRect(r2); + + canvas->drawPath(path, p); +} + +static void test_strokerect(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + + SkRect r; + + r.set(10, 10, 14, 14); + r.offset(0.25f, 0.3333f); + test_strokerect(canvas, r); + canvas->translate(0, 20); + + r.set(10, 10, 14.5f, 14.5f); + r.offset(0.25f, 0.3333f); + test_strokerect(canvas, r); + canvas->translate(0, 20); + + r.set(10, 10, 14.5f, 20); + r.offset(0.25f, 0.3333f); + test_strokerect(canvas, r); + canvas->translate(0, 20); + + r.set(10, 10, 20, 14.5f); + r.offset(0.25f, 0.3333f); + test_strokerect(canvas, r); + canvas->translate(0, 20); + + r.set(10, 10, 20, 20); + r.offset(0.25f, 0.3333f); + test_strokerect(canvas, r); + canvas->translate(0, 20); + +} + +class Draw : public SkRefCnt { +public: + Draw() : fFlags(0) {} + + enum Flags { + kSelected_Flag = 1 << 0 + }; + int getFlags() const { return fFlags; } + void setFlags(int flags); + + bool isSelected() const { return SkToBool(fFlags & kSelected_Flag); } + void setSelected(bool pred) { + if (pred) { + fFlags |= kSelected_Flag; + } else { + fFlags &= ~kSelected_Flag; + } + } + + void draw(SkCanvas* canvas) { + int sc = canvas->save(); + this->onDraw(canvas); + canvas->restoreToCount(sc); + + if (this->isSelected()) { + this->drawSelection(canvas); + } + } + + void drawSelection(SkCanvas* canvas) { + int sc = canvas->save(); + this->onDrawSelection(canvas); + canvas->restoreToCount(sc); + } + + void getBounds(SkRect* bounds) { + this->onGetBounds(bounds); + } + + bool hitTest(SkScalar x, SkScalar y) { + return this->onHitTest(x, y); + } + + void offset(SkScalar dx, SkScalar dy) { + if (dx || dy) { + this->onOffset(dx, dy); + } + } + +protected: + virtual void onDraw(SkCanvas*) = 0; + virtual void onGetBounds(SkRect*) = 0; + virtual void onOffset(SkScalar dx, SkScalar dy) = 0; + virtual void onDrawSelection(SkCanvas* canvas) { + SkRect r; + this->getBounds(&r); + SkPaint paint; + SkPoint pts[4]; + r.toQuad(pts); + paint.setStrokeWidth(SkIntToScalar(10)); + paint.setColor(0x80FF8844); + paint.setStrokeCap(SkPaint::kRound_Cap); + canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, pts, paint); + } + virtual bool onHitTest(SkScalar x, SkScalar y) { + SkRect bounds; + this->getBounds(&bounds); + return bounds.contains(x, y); + } + +private: + int fFlags; +}; + +class RDraw : public Draw { +public: + enum Style { + kRect_Style, + kOval_Style, + kRRect_Style, + kFrame_Style + }; + + RDraw(const SkRect& r, Style s) : fRect(r), fStyle(s) {} + + void setRect(const SkRect& r) { + fRect = r; + } + + void setPaint(const SkPaint& p) { + fPaint = p; + } + +protected: + virtual void onDraw(SkCanvas* canvas) { + switch (fStyle) { + case kRect_Style: + canvas->drawRect(fRect, fPaint); + break; + case kOval_Style: + canvas->drawOval(fRect, fPaint); + break; + case kRRect_Style: { + SkScalar rx = fRect.width() / 5; + SkScalar ry = fRect.height() / 5; + if (rx < ry) { + ry = rx; + } else { + rx = ry; + } + canvas->drawRoundRect(fRect, rx, ry, fPaint); + break; + } + case kFrame_Style: { + SkPath path; + path.addOval(fRect, SkPath::kCW_Direction); + SkRect r = fRect; + r.inset(fRect.width()/6, 0); + path.addOval(r, SkPath::kCCW_Direction); + canvas->drawPath(path, fPaint); + break; + } + } + } + + virtual void onGetBounds(SkRect* bounds) { + *bounds = fRect; + } + + virtual void onOffset(SkScalar dx, SkScalar dy) { + fRect.offset(dx, dy); + } + +private: + SkRect fRect; + SkPaint fPaint; + Style fStyle; +}; + +class DrawFactory { +public: + DrawFactory() { + fPaint.setAntiAlias(true); + } + + const SkPaint& getPaint() const { return fPaint; } + + void setPaint(const SkPaint& p) { + fPaint = p; + } + + virtual Draw* create(const SkPoint&, const SkPoint&) = 0; + +private: + SkPaint fPaint; +}; + +class RectFactory : public DrawFactory { +public: + virtual Draw* create(const SkPoint& p0, const SkPoint& p1) { + SkRect r; + r.set(p0.x(), p0.y(), p1.x(), p1.y()); + r.sort(); + +// RDraw* d = new RDraw(r, RDraw::kRRect_Style); + RDraw* d = new RDraw(r, RDraw::kFrame_Style); + d->setPaint(this->getPaint()); + return d; + } +}; + +class DrawView : public SkView { + Draw* fDraw; + DrawFactory* fFactory; + SkRandom fRand; + SkTDArray<Draw*> fList; + +public: + DrawView() : fDraw(NULL) { + fFactory = new RectFactory; + } + + virtual ~DrawView() { + fList.unrefAll(); + SkSafeUnref(fDraw); + delete fFactory; + } + + Draw* setDraw(Draw* d) { + SkRefCnt_SafeAssign(fDraw, d); + return d; + } + + SkColor randColor() { + return (SkColor)fRand.nextU() | 0xFF000000; + } + + Draw* hitTestList(SkScalar x, SkScalar y) const { + Draw** first = fList.begin(); + for (Draw** iter = fList.end(); iter > first;) { + --iter; + if ((*iter)->hitTest(x, y)) { + return *iter; + } + } + return NULL; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Draw"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); +// canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + test_clearonlayers(canvas); return; + // test_strokerect(canvas); return; + + for (Draw** iter = fList.begin(); iter < fList.end(); iter++) { + (*iter)->draw(canvas); + } + if (fDraw) { + fDraw->draw(canvas); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + for (Draw** iter = fList.begin(); iter < fList.end(); iter++) { + (*iter)->setSelected(false); + } + + Click* c = new Click(this); + Draw* d = this->hitTestList(x, y); + if (d) { + d->setSelected(true); + c->setType("dragger"); + } else { + c->setType("maker"); + } + return c; + } + + virtual bool onClick(Click* click) { + if (Click::kUp_State == click->fState) { + if (click->isType("maker")) { + if (SkPoint::Distance(click->fOrig, click->fCurr) > SkIntToScalar(3)) { + *fList.append() = fDraw; + } else { + fDraw->unref(); + } + fDraw = NULL; + } + return true; + } + + if (Click::kDown_State == click->fState) { + SkPaint p = fFactory->getPaint(); + p.setColor(this->randColor()); + fFactory->setPaint(p); + } + + if (click->isType("maker")) { + this->setDraw(fFactory->create(click->fOrig, click->fCurr))->unref(); + } else if (click->isType("dragger")) { + for (Draw** iter = fList.begin(); iter < fList.end(); iter++) { + if ((*iter)->isSelected()) { + (*iter)->offset(click->fCurr.x() - click->fPrev.x(), + click->fCurr.y() - click->fPrev.y()); + } + } + } + this->inval(NULL); + return true; + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new DrawView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleDrawLooper.cpp b/samplecode/SampleDrawLooper.cpp new file mode 100644 index 0000000000..30879f75fe --- /dev/null +++ b/samplecode/SampleDrawLooper.cpp @@ -0,0 +1,92 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" +#include "SkLayerDrawLooper.h" +#include "SkBlurMaskFilter.h" + +#define WIDTH 200 +#define HEIGHT 200 + +class LooperView : public SampleView { +public: + + SkLayerDrawLooper* fLooper; + + LooperView() { + static const struct { + SkColor fColor; + SkPaint::Style fStyle; + SkScalar fWidth; + SkScalar fOffset; + int fBlur; + } gParams[] = { + { SK_ColorWHITE, SkPaint::kStroke_Style, SkIntToScalar(1)*3/4, 0, 0 }, + { SK_ColorRED, SkPaint::kStroke_Style, SkIntToScalar(4), 0, 0 }, + { SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0 }, + { 0x88000000, SkPaint::kFill_Style, 0, SkIntToScalar(10), 3 } + }; + + fLooper = new SkLayerDrawLooper; + + SkLayerDrawLooper::LayerInfo info; + info.fFlagsMask = SkPaint::kAntiAlias_Flag; + info.fPaintBits = SkLayerDrawLooper::kStyle_Bit | SkLayerDrawLooper::kMaskFilter_Bit; + info.fColorMode = SkXfermode::kSrc_Mode; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gParams); i++) { + info.fOffset.set(gParams[i].fOffset, gParams[i].fOffset); + SkPaint* paint = fLooper->addLayer(info); + paint->setAntiAlias(true); + paint->setColor(gParams[i].fColor); + paint->setStyle(gParams[i].fStyle); + paint->setStrokeWidth(gParams[i].fWidth); + if (gParams[i].fBlur > 0) { + SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(gParams[i].fBlur), + SkBlurMaskFilter::kNormal_BlurStyle); + paint->setMaskFilter(mf)->unref(); + } + } + + this->setBGColor(0xFFDDDDDD); + } + + virtual ~LooperView() { + SkSafeUnref(fLooper); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "DrawLooper"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setTextSize(SkIntToScalar(72)); + paint.setLooper(fLooper); + + canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50), + SkIntToScalar(30), paint); + + canvas->drawRectCoords(SkIntToScalar(150), SkIntToScalar(50), + SkIntToScalar(200), SkIntToScalar(100), paint); + + canvas->drawText("Looper", 6, SkIntToScalar(230), SkIntToScalar(100), + paint); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new LooperView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleEffects.cpp b/samplecode/SampleEffects.cpp new file mode 100644 index 0000000000..a63c08d3f5 --- /dev/null +++ b/samplecode/SampleEffects.cpp @@ -0,0 +1,130 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkView.h" + +#include "SkBlurMaskFilter.h" +#include "SkColorMatrixFilter.h" +#include "SkDiscretePathEffect.h" +#include "SkGradientShader.h" + +#include "SkEdgeClipper.h" + +static void test_edgeclipper() { + SkPoint pts[] = { + { -8.38822452e+21f, -7.69721471e+19f }, + { 1.57645875e+23f, 1.44634003e+21f }, + { 1.61519691e+23f, 1.48208059e+21f }, + { 3.13963584e+23f, 2.88057438e+21f } + }; + SkRect clip = { 0, 0, 300, 200 }; + + SkEdgeClipper clipper; + clipper.clipCubic(pts, clip); +} + +/////////// + +//#define COLOR 0xFFFF8844 +#define COLOR 0xFF888888 + +static void paint_proc0(SkPaint* paint) { +} + +static void paint_proc1(SkPaint* paint) { + paint->setMaskFilter(SkBlurMaskFilter::Create(2, + SkBlurMaskFilter::kNormal_BlurStyle))->unref(); +} + +static void paint_proc2(SkPaint* paint) { + SkScalar dir[3] = { 1, 1, 1}; + paint->setMaskFilter( + SkBlurMaskFilter::CreateEmboss(dir, 0.1f, 0.05f, 1))->unref(); +} + +static void paint_proc3(SkPaint* paint) { + SkColor colors[] = { SK_ColorRED, COLOR, SK_ColorBLUE }; + SkPoint pts[] = { { 3, 0 }, { 7, 5 } }; + paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors), + SkShader::kMirror_TileMode))->unref(); +} + +static void paint_proc5(SkPaint* paint) { + paint_proc3(paint); + paint_proc2(paint); +} + +typedef void (*PaintProc)(SkPaint*); +const PaintProc gPaintProcs[] = { + paint_proc0, + paint_proc1, + paint_proc2, + paint_proc3, + paint_proc5, +}; + +/////////////////////////////////////////////////////////////////////////////// + +class EffectsView : public SampleView { +public: + SkPath fPath; + SkPaint fPaint[SK_ARRAY_COUNT(gPaintProcs)]; + + EffectsView() { + size_t i; + const float pts[] = { + 0, 0, + 10, 0, + 10, 5, + 20, -5, + 10, -15, + 10, -10, + 0, -10 + }; + fPath.moveTo(pts[0], pts[1]); + for (i = 2; i < SK_ARRAY_COUNT(pts); i += 2) { + fPath.lineTo(pts[i], pts[i+1]); + } + + for (i = 0; i < SK_ARRAY_COUNT(gPaintProcs); i++) { + fPaint[i].setAntiAlias(true); + fPaint[i].setColor(COLOR); + gPaintProcs[i](&fPaint[i]); + } + + test_edgeclipper(); + SkColorMatrix cm; + cm.setRotate(SkColorMatrix::kG_Axis, 180); + cm.setIdentity(); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Effects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->scale(3, 3); + canvas->translate(10, 30); + for (size_t i = 0; i < SK_ARRAY_COUNT(fPaint); i++) { + canvas->drawPath(fPath, fPaint[i]); + canvas->translate(32, 0); + } + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new EffectsView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleEmboss.cpp b/samplecode/SampleEmboss.cpp new file mode 100644 index 0000000000..8b3f194ccd --- /dev/null +++ b/samplecode/SampleEmboss.cpp @@ -0,0 +1,66 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkColorShader.h" +#include "SkEmbossMaskFilter.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +class EmbossView : public SampleView { + SkEmbossMaskFilter::Light fLight; +public: + EmbossView() { + fLight.fDirection[0] = SK_Scalar1; + fLight.fDirection[1] = SK_Scalar1; + fLight.fDirection[2] = SK_Scalar1; + fLight.fAmbient = 128; + fLight.fSpecular = 16*2; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Emboss"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(10)); + paint.setMaskFilter(new SkEmbossMaskFilter(fLight, SkIntToScalar(4)))->unref(); + paint.setShader(new SkColorShader(SK_ColorBLUE))->unref(); + paint.setDither(true); + + canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50), + SkIntToScalar(30), paint); + } + +private: + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new EmbossView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleEncode.cpp b/samplecode/SampleEncode.cpp new file mode 100644 index 0000000000..f4ea1950aa --- /dev/null +++ b/samplecode/SampleEncode.cpp @@ -0,0 +1,224 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkImageEncoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkStream.h" + +static void make_image(SkBitmap* bm, SkBitmap::Config config, int configIndex) { + const int width = 98; + const int height = 100; + SkBitmap device; + + device.setConfig(SkBitmap::kARGB_8888_Config, width, height); + device.allocPixels(); + + SkCanvas canvas(device); + SkPaint paint; + + paint.setAntiAlias(true); + canvas.drawColor(SK_ColorRED); + paint.setColor(SK_ColorBLUE); + canvas.drawCircle(SkIntToScalar(width)/2, SkIntToScalar(height)/2, + SkIntToScalar(width)/2, paint); + + bm->setConfig(config, width, height); + switch (config) { + case SkBitmap::kARGB_8888_Config: + bm->swap(device); + break; + case SkBitmap::kRGB_565_Config: { + bm->allocPixels(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + *bm->getAddr16(x, y) = SkPixel32ToPixel16(*device.getAddr32(x, y)); + } + } + break; + } + case SkBitmap::kIndex8_Config: { + SkPMColor colors[256]; + for (int i = 0; i < 256; i++) { + if (configIndex & 1) { + colors[i] = SkPackARGB32(255-i, 0, 0, 255-i); + } else { + colors[i] = SkPackARGB32(0xFF, i, 0, 255-i); + } + } + SkColorTable* ctable = new SkColorTable(colors, 256); + bm->allocPixels(ctable); + ctable->unref(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + *bm->getAddr8(x, y) = SkGetPackedR32(*device.getAddr32(x, y)); + } + } + break; + } + default: + break; + } +} + +// configs to build the original bitmap in. Can be at most these 3 +static const SkBitmap::Config gConfigs[] = { + SkBitmap::kARGB_8888_Config, + SkBitmap::kRGB_565_Config, + SkBitmap::kIndex8_Config, // opaque + SkBitmap::kIndex8_Config // alpha +}; + +static const char* const gConfigLabels[] = { + "8888", "565", "Index8", "Index8 alpha" +}; + +// types to encode into. Can be at most these 3. Must match up with gExt[] +static const SkImageEncoder::Type gTypes[] = { + SkImageEncoder::kJPEG_Type, + SkImageEncoder::kPNG_Type +}; + +// must match up with gTypes[] +static const char* const gExt[] = { + ".jpg", ".png" +}; + +static const char* gPath = "/encoded/"; + +static void make_name(SkString* name, int config, int ext) { + name->set(gPath); + name->append(gConfigLabels[config]); + name->append(gExt[ext]); +} + +#include <sys/stat.h> + +class EncodeView : public SampleView { +public: + SkBitmap* fBitmaps; + size_t fBitmapCount; + + EncodeView() { + #if 1 + (void)mkdir(gPath, S_IRWXU | S_IRWXG | S_IRWXO); + + fBitmapCount = SK_ARRAY_COUNT(gConfigs); + fBitmaps = new SkBitmap[fBitmapCount]; + for (size_t i = 0; i < fBitmapCount; i++) { + make_image(&fBitmaps[i], gConfigs[i], i); + + for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) { + SkString path; + make_name(&path, i, j); + + // remove any previous run of this file + remove(path.c_str()); + + SkImageEncoder* codec = SkImageEncoder::Create(gTypes[j]); + if (NULL == codec || + !codec->encodeFile(path.c_str(), fBitmaps[i], 100)) { + SkDebugf("------ failed to encode %s\n", path.c_str()); + remove(path.c_str()); // remove any partial file + } + delete codec; + } + } + #else + fBitmaps = NULL; + fBitmapCount = 0; + #endif + this->setBGColor(0xFFDDDDDD); + } + + virtual ~EncodeView() { + delete[] fBitmaps; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "ImageEncoder"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + if (fBitmapCount == 0) { + return; + } + + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextAlign(SkPaint::kCenter_Align); + + canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); + + SkScalar x = 0, y = 0, maxX = 0; + const int SPACER = 10; + + for (size_t i = 0; i < fBitmapCount; i++) { + canvas->drawText(gConfigLabels[i], strlen(gConfigLabels[i]), + x + SkIntToScalar(fBitmaps[i].width()) / 2, 0, + paint); + y = paint.getTextSize(); + + canvas->drawBitmap(fBitmaps[i], x, y); + + SkScalar yy = y; + for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) { + yy += SkIntToScalar(fBitmaps[i].height() + 10); + + SkBitmap bm; + SkString name; + + make_name(&name, i, j); + + SkImageDecoder::DecodeFile(name.c_str(), &bm); + canvas->drawBitmap(bm, x, yy); + } + + x += SkIntToScalar(fBitmaps[i].width() + SPACER); + if (x > maxX) { + maxX = x; + } + } + + y = (paint.getTextSize() + SkIntToScalar(fBitmaps[0].height())) * 3 / 2; + x = maxX + SkIntToScalar(10); + paint.setTextAlign(SkPaint::kLeft_Align); + + for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) { + canvas->drawText(gExt[j], strlen(gExt[j]), x, y, paint); + y += SkIntToScalar(fBitmaps[0].height() + SPACER); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new EncodeView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleExtractAlpha.cpp b/samplecode/SampleExtractAlpha.cpp new file mode 100644 index 0000000000..860272d892 --- /dev/null +++ b/samplecode/SampleExtractAlpha.cpp @@ -0,0 +1,90 @@ +#include "SampleCode.h" +#include "SkColorPriv.h" +#include "SkGradientShader.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkUtils.h" + +static SkBitmap make_bitmap() { + SkBitmap bm; + SkColorTable* ctable = new SkColorTable(256); + + SkPMColor* c = ctable->lockColors(); + for (int i = 0; i < 256; i++) { + c[i] = SkPackARGB32(255 - i, 0, 0, 0); + } + ctable->unlockColors(true); + bm.setConfig(SkBitmap::kIndex8_Config, 256, 256); + bm.allocPixels(ctable); + ctable->unref(); + + bm.lockPixels(); + const float cx = bm.width() * 0.5f; + const float cy = bm.height() * 0.5f; + for (int y = 0; y < bm.height(); y++) { + float dy = y - cy; + dy *= dy; + uint8_t* p = bm.getAddr8(0, y); + for (int x = 0; x < 256; x++) { + float dx = x - cx; + dx *= dx; + float d = (dx + dy) / (cx/2); + int id = (int)d; + if (id > 255) { + id = 255; + } + p[x] = id; + } + } + bm.unlockPixels(); + return bm; +} + +class ExtractAlphaView : public SampleView { + SkBitmap fBM8; + SkBitmap fBM32; + SkBitmap fBM4; +public: + ExtractAlphaView() { + fBM8 = make_bitmap(); + fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config); + fBM8.copyTo(&fBM4, SkBitmap::kARGB_4444_Config); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "DitherBitmap"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + + SkMatrix matrix; + matrix.setScale(3.55f, 80.f); + canvas->setMatrix(matrix); + + paint.setStrokeWidth(0.0588f); + canvas->drawLine(10, 5, 30, 4.8f, paint); + + paint.setStrokeWidth(0.06f); + canvas->drawLine(20, 5, 40, 4.8f, paint); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ExtractAlphaView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp new file mode 100644 index 0000000000..393c5f7ba3 --- /dev/null +++ b/samplecode/SampleFillType.cpp @@ -0,0 +1,90 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkCornerPathEffect.h" +#include "SkCullPoints.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" + +class FillTypeView : public SampleView { + SkPath fPath; +public: + FillTypeView() { + const SkScalar radius = SkIntToScalar(45); + fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius); + fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "FillType"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft, + SkScalar scale, const SkPaint& paint) { + + const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) }; + + canvas->save(); + canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas->clipRect(r); + canvas->drawColor(SK_ColorWHITE); + fPath.setFillType(ft); + canvas->translate(r.centerX(), r.centerY()); + canvas->scale(scale, scale); + canvas->translate(-r.centerX(), -r.centerY()); + canvas->drawPath(fPath, paint); + canvas->restore(); + } + + void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) { + showPath(canvas, 0, 0, SkPath::kWinding_FillType, + scale, paint); + showPath(canvas, 200, 0, SkPath::kEvenOdd_FillType, + scale, paint); + showPath(canvas, 00, 200, SkPath::kInverseWinding_FillType, + scale, paint); + showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType, + scale, paint); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); + + SkPaint paint; + const SkScalar scale = SkIntToScalar(5)/4; + + paint.setAntiAlias(false); + paint.setColor(0x8000FF00); + + showFour(canvas, SK_Scalar1, paint); + canvas->translate(SkIntToScalar(450), 0); + showFour(canvas, scale, paint); + + paint.setAntiAlias(true); + + canvas->translate(SkIntToScalar(-450), SkIntToScalar(450)); + showFour(canvas, SK_Scalar1, paint); + canvas->translate(SkIntToScalar(450), 0); + showFour(canvas, scale, paint); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new FillTypeView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp new file mode 100644 index 0000000000..a9089fa344 --- /dev/null +++ b/samplecode/SampleFilter.cpp @@ -0,0 +1,138 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkDither.h" + +static void make_bm(SkBitmap* bm) { + const SkColor colors[] = { + SK_ColorRED, SK_ColorGREEN, + SK_ColorBLUE, SK_ColorWHITE + }; + SkColorTable* ctable = new SkColorTable(colors, 4); + bm->setConfig(SkBitmap::kIndex8_Config, 2, 2); + bm->allocPixels(ctable); + ctable->unref(); + + *bm->getAddr8(0, 0) = 0; + *bm->getAddr8(1, 0) = 1; + *bm->getAddr8(0, 1) = 2; + *bm->getAddr8(1, 1) = 3; +} + +static SkScalar draw_bm(SkCanvas* canvas, const SkBitmap& bm, + SkScalar x, SkScalar y, SkPaint* paint) { +#if 1 + canvas->drawBitmap(bm, x, y, paint); + return SkIntToScalar(bm.width()) * 5/4; +#else + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(x, y); + + SkScalar w = SkIntToScalar(bm.width()); + SkScalar h = SkIntToScalar(bm.height()); + SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + paint->setShader(s)->unref(); + canvas->drawRect(SkRect::MakeWH(w, h), *paint); + paint->setShader(NULL); + return w * 5/4; +#endif +} + +static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x, SkPaint* p) { + x += draw_bm(c, bm, x, 0, p); + p->setFilterBitmap(true); + x += draw_bm(c, bm, x, 0, p); + p->setDither(true); + return x + draw_bm(c, bm, x, 0, p); +} + +static const char* gConfigNames[] = { + "unknown config", + "A1", + "A8", + "Index8", + "565", + "4444", + "8888" +}; + +static SkScalar draw_row(SkCanvas* canvas, const SkBitmap& bm) { + SkAutoCanvasRestore acr(canvas, true); + + SkPaint paint; + SkScalar x = 0; + const int scale = 32; + + paint.setAntiAlias(true); + const char* name = gConfigNames[bm.config()]; + canvas->drawText(name, strlen(name), x, SkIntToScalar(bm.height())*scale*5/8, + paint); + canvas->translate(SkIntToScalar(48), 0); + + canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale)); + + x += draw_set(canvas, bm, 0, &paint); + paint.reset(); + paint.setAlpha(0x80); + draw_set(canvas, bm, x, &paint); + return x * scale / 3; +} + +class FilterView : public SampleView { +public: + SkBitmap fBM8, fBM4444, fBM16, fBM32; + + FilterView() { + make_bm(&fBM8); + fBM8.copyTo(&fBM4444, SkBitmap::kARGB_4444_Config); + fBM8.copyTo(&fBM16, SkBitmap::kRGB_565_Config); + fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Filter"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(10); + + canvas->translate(x, y); + y = draw_row(canvas, fBM8); + canvas->translate(0, y); + y = draw_row(canvas, fBM4444); + canvas->translate(0, y); + y = draw_row(canvas, fBM16); + canvas->translate(0, y); + draw_row(canvas, fBM32); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new FilterView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleFilter2.cpp b/samplecode/SampleFilter2.cpp new file mode 100644 index 0000000000..c1a16a8d9f --- /dev/null +++ b/samplecode/SampleFilter2.cpp @@ -0,0 +1,116 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" + +static const char* gNames[] = { + "/skimages/background_01.png" +}; + +class Filter2View : public SampleView { +public: + SkBitmap* fBitmaps; + int fBitmapCount; + int fCurrIndex; + + Filter2View() { + fBitmapCount = SK_ARRAY_COUNT(gNames)*2; + fBitmaps = new SkBitmap[fBitmapCount]; + + for (int i = 0; i < fBitmapCount/2; i++) { + SkImageDecoder::DecodeFile(gNames[i], &fBitmaps[i], + SkBitmap::kARGB_8888_Config, + SkImageDecoder::kDecodePixels_Mode, NULL); + } + for (int i = fBitmapCount/2; i < fBitmapCount; i++) { + SkImageDecoder::DecodeFile(gNames[i-fBitmapCount/2], &fBitmaps[i], + SkBitmap::kRGB_565_Config, + SkImageDecoder::kDecodePixels_Mode, NULL); + } + fCurrIndex = 0; + + this->setBGColor(SK_ColorGRAY); + } + + virtual ~Filter2View() { + delete[] fBitmaps; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SkString str("Filter/Dither "); + str.append(gNames[fCurrIndex]); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(10), SkIntToScalar(50)); + + const SkScalar W = SkIntToScalar(fBitmaps[0].width() + 1); + const SkScalar H = SkIntToScalar(fBitmaps[0].height() + 1); + SkPaint paint; + + const SkScalar scale = SkFloatToScalar(0.897917f); + canvas->scale(SK_Scalar1, scale); + + for (int k = 0; k < 2; k++) { + paint.setFilterBitmap(k == 1); + for (int j = 0; j < 2; j++) { + paint.setDither(j == 1); + for (int i = 0; i < fBitmapCount; i++) { + SkScalar x = (k * fBitmapCount + j) * W; + SkScalar y = i * H; + x = SkIntToScalar(SkScalarRound(x)); + y = SkIntToScalar(SkScalarRound(y)); + canvas->drawBitmap(fBitmaps[i], x, y, &paint); + if (i == 0) { + SkPaint p; + p.setAntiAlias(true); + p.setTextAlign(SkPaint::kCenter_Align); + p.setTextSize(SkIntToScalar(18)); + SkString s("dither="); + s.appendS32(paint.isDither()); + s.append(" filter="); + s.appendS32(paint.isFilterBitmap()); + canvas->drawText(s.c_str(), s.size(), x + W/2, + y - p.getTextSize(), p); + } + if (k+j == 2) { + SkPaint p; + p.setAntiAlias(true); + p.setTextSize(SkIntToScalar(18)); + SkString s; + s.append(" depth="); + s.appendS32(fBitmaps[i].config() == SkBitmap::kRGB_565_Config ? 16 : 32); + canvas->drawText(s.c_str(), s.size(), x + W + SkIntToScalar(4), + y + H/2, p); + } + } + } + } + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new Filter2View; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleFontCache.cpp b/samplecode/SampleFontCache.cpp new file mode 100644 index 0000000000..0b8187a020 --- /dev/null +++ b/samplecode/SampleFontCache.cpp @@ -0,0 +1,135 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" + +#include <pthread.h> + +static void call_measure() { + SkPaint paint; + uint16_t text[32]; + SkRandom rand; + + paint.setAntiAlias(true); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + for (int j = 0; j < SK_ARRAY_COUNT(text); j++) + text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32); + + for (int i = 9; i < 36; i++) { + SkPaint::FontMetrics m; + + paint.setTextSize(SkIntToScalar(i)); + paint.getFontMetrics(&m); + paint.measureText(text, sizeof(text)); + } +} + +static void call_draw(SkCanvas* canvas) { + SkPaint paint; + uint16_t text[32]; + SkRandom rand; + + paint.setAntiAlias(true); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + for (int j = 0; j < SK_ARRAY_COUNT(text); j++) + text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32); + + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(20); + + canvas->drawColor(SK_ColorWHITE); + for (int i = 9; i < 36; i++) + { + SkPaint::FontMetrics m; + + paint.setTextSize(SkIntToScalar(i)); + paint.getFontMetrics(&m); + canvas->drawText(text, sizeof(text), x, y, paint); + y += m.fDescent - m.fAscent; + } +} + +static bool gDone; + +static void* measure_proc(void* context) { + while (!gDone) { + call_measure(); + } + return NULL; +} + +static void* draw_proc(void* context) { + SkBitmap* bm = (SkBitmap*)context; + SkCanvas canvas(*bm); + + while (!gDone) { + call_draw(&canvas); + } + return NULL; +} + +class FontCacheView : public SampleView { +public: + enum { N = 4 }; + + pthread_t fMThreads[N]; + pthread_t fDThreads[N]; + SkBitmap fBitmaps[N]; + + FontCacheView() { + gDone = false; + for (int i = 0; i < N; i++) { + int status; + + status = pthread_create(&fMThreads[i], NULL, measure_proc, NULL); + SkASSERT(0 == status); + + fBitmaps[i].setConfig(SkBitmap::kRGB_565_Config, 320, 240); + fBitmaps[i].allocPixels(); + status = pthread_create(&fDThreads[i], NULL, draw_proc, &fBitmaps[i]); + SkASSERT(0 == status); + } + this->setBGColor(0xFFDDDDDD); + } + + virtual ~FontCacheView() { + gDone = true; + for (int i = 0; i < N; i++) { + void* ret; + int status = pthread_join(fMThreads[i], &ret); + SkASSERT(0 == status); + status = pthread_join(fDThreads[i], &ret); + SkASSERT(0 == status); + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "FontCache"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkScalar x = 0; + SkScalar y = 0; + for (int i = 0; i < N; i++) { + canvas->drawBitmap(fBitmaps[i], x, y); + x += SkIntToScalar(fBitmaps[i].width()); + } + this->inval(NULL); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new FontCacheView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleFontScalerTest.cpp b/samplecode/SampleFontScalerTest.cpp new file mode 100644 index 0000000000..0b0d349fa8 --- /dev/null +++ b/samplecode/SampleFontScalerTest.cpp @@ -0,0 +1,117 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkTypeface.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkDither.h" + +static const struct { + const char* fName; + SkTypeface::Style fStyle; +} gFaces[] = { + { NULL, SkTypeface::kNormal }, + { NULL, SkTypeface::kBold }, + { "serif", SkTypeface::kNormal }, + { "serif", SkTypeface::kBold }, + { "serif", SkTypeface::kItalic }, + { "serif", SkTypeface::kBoldItalic }, + { "monospace", SkTypeface::kNormal } +}; + +static const int gFaceCount = SK_ARRAY_COUNT(gFaces); + +class FontScalerTestView : public SampleView { + SkTypeface* fFaces[gFaceCount]; + +public: + FontScalerTestView() { + for (int i = 0; i < gFaceCount; i++) { + fFaces[i] = SkTypeface::CreateFromName(gFaces[i].fName, + gFaces[i].fStyle); + } + this->setBGColor(0xFFDDDDDD); + } + + virtual ~FontScalerTestView() { + for (int i = 0; i < gFaceCount; i++) { + SkSafeUnref(fFaces[i]); + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "FontScaler Test"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + + // test handling of obscene cubic values (currently broken) + if (false) { + SkPoint pts[4]; + pts[0].set(1.61061274e+09f, 6291456); + pts[1].set(-7.18397061e+15f, -1.53091184e+13f); + pts[2].set(-1.30077315e+16f, -2.77196141e+13f); + pts[3].set(-1.30077315e+16f, -2.77196162e+13f); + + SkPath path; + path.moveTo(pts[0]); + path.cubicTo(pts[1], pts[2], pts[3]); + canvas->drawPath(path, paint); + } + + canvas->translate(200, 20); + canvas->rotate(30); + + paint.setAntiAlias(true); + paint.setLCDRenderText(true); + SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal))); + +// const char* text = "abcdefghijklmnopqrstuvwxyz"; + const char* text = "HnHnHnHnHnHnHnHnH"; + size_t textLen = strlen(text); + + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(20); + + { + SkPaint p; + p.setColor(SK_ColorRED); + SkRect r; + r.set(0, 0, x, y*20); + canvas->drawRect(r, p); + } + + int index = 0; + for (int ps = 9; ps <= 24; ps++) { + textLen = strlen(text); + paint.setTextSize(SkIntToScalar(ps)); + canvas->drawText(text, textLen, x, y, paint); + y += paint.getFontMetrics(NULL); + index += 1; + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new FontScalerTestView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleFuzz.cpp b/samplecode/SampleFuzz.cpp new file mode 100644 index 0000000000..5c41886b8b --- /dev/null +++ b/samplecode/SampleFuzz.cpp @@ -0,0 +1,363 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkBlurMaskFilter.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkXfermode.h" +#include "SkMatrix.h" +#include "SkColor.h" +#include "SkRandom.h" + +static void set2x3(SkMatrix* m, float a, float b, float c, float d, float e, float f) { + m->reset(); + m->set(0, a); + m->set(1, b); + m->set(2, c); + m->set(3, d); + m->set(4, e); + m->set(5, f); +} + +static SkRandom gRand; +static bool return_large; +static bool return_undef; +static bool quick; +static bool scale_large; +static int scval = 1; +static float transval = 0; + +static int R(float x) { + return (int)floor(SkScalarToFloat(gRand.nextUScalar1()) * x); +} + +static float huge() { + double d = 1e100; + float f = (float)d; + return f; +} + +static float make_number() { + float v; + int sel; + + if (return_large == true && R(3) == 1) sel = R(6); else sel = R(4); + if (return_undef == false && sel == 0) sel = 1; + + if (R(2) == 1) v = (float)R(100); else + + switch (sel) { + case 0: break; + case 1: v = 0; break; + case 2: v = 0.000001f; break; + case 3: v = 10000; break; + case 4: v = 2000000000; break; + case 5: v = huge(); break; + } + + if (R(4) == 1) v = -v; + return v; +} + +static SkColor make_color() { + if (R(2) == 1) return 0xFFC0F0A0; else return 0xFF000090; +} + + +static SkColor make_fill() { +#if 0 + int sel; + + if (quick == true) sel = 0; else sel = R(6); + + switch (sel) { + + case 0: + case 1: + case 2: + return make_color(); + break; + + case 3: + var r = ctx.createLinearGradient(make_number(),make_number(),make_number(),make_number()); + for (i=0;i<4;i++) + r.addColorStop(make_number(),make_color()); + return r; + break; + + case 4: + var r = ctx.createRadialGradient(make_number(),make_number(),make_number(),make_number(),make_number(),make_number()); + for (i=0;i<4;i++) + r.addColorStop(make_number(),make_color()); + return r; + break; + + case 5: + var r = ctx.createPattern(imgObj,"repeat"); + if (R(6) == 0) + r.addColorStop(make_number(),make_color()); + return r; + break; + } +#else + return make_color(); +#endif +} + + +static void do_fuzz(SkCanvas* canvas) { + SkPath path; + SkPaint paint; + paint.setAntiAlias(true); + + for (int i=0;i<100;i++) { + switch (R(33)) { + + case 0: + paint.setColor(make_fill()); + break; + + case 1: + paint.setAlpha(gRand.nextU() & 0xFF); + break; + + case 2: { + SkXfermode::Mode mode; + switch (R(3)) { + case 0: mode = SkXfermode::kSrc_Mode; break; + case 1: mode = SkXfermode::kXor_Mode; break; + case 2: mode = SkXfermode::kSrcOver_Mode; break; + } + paint.setXfermodeMode(mode); + } + break; + + case 3: + switch (R(2)) { + case 0: paint.setStrokeCap(SkPaint::kRound_Cap); break; + case 1: paint.setStrokeCap(SkPaint::kButt_Cap); break; + } + break; + + case 4: + switch (R(2)) { + case 0: paint.setStrokeJoin(SkPaint::kRound_Join); break; + case 1: paint.setStrokeJoin(SkPaint::kMiter_Join); break; + } + break; + + case 5: + paint.setStrokeWidth(make_number()); + break; + + case 6: + paint.setStrokeMiter(make_number()); + break; + + case 7: + if (quick == true) break; + SkSafeUnref(paint.setMaskFilter(SkBlurMaskFilter::Create(make_number(), SkBlurMaskFilter::kNormal_BlurStyle))); + break; + + case 8: + if (quick == true) break; + //ctx.shadowColor = make_fill(); + break; + + case 9: + if (quick == true) break; + //ctx.shadowOffsetX = make_number(); + //ctx.shadowOffsetY = make_number(); + break; + + case 10: + canvas->restore(); + break; + + case 11: + canvas->rotate(make_number()); + break; + + case 12: + canvas->save(); + break; + + case 13: + canvas->scale(-1,-1); + break; + + case 14: + + if (quick == true) break; + + if (transval == 0) { + transval = make_number(); + canvas->translate(transval,0); + } else { + canvas->translate(-transval,0); + transval = 0; + } + + break; + + case 15: { + SkRect r; + r.set(make_number(),make_number(),make_number(),make_number()); + SkPaint::Style s = paint.getStyle(); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawRect(r, paint); + paint.setStyle(s); + // clearrect + } break; + + case 16: + if (quick == true) break; +// ctx.drawImage(imgObj,make_number(),make_number(),make_number(),make_number(),make_number(),make_number(),make_number(),make_number()); + break; + + case 17: { + SkRect r; + r.set(make_number(),make_number(),make_number(),make_number()); + SkPaint::Style s = paint.getStyle(); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawRect(r, paint); + paint.setStyle(s); + } break; + + case 18: + path.reset(); + break; + + case 19: + // ctx.clip() is evil. + break; + + case 20: + path.close(); + break; + + case 21: { + SkPaint::Style s = paint.getStyle(); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawPath(path, paint); + paint.setStyle(s); + } break; + + case 22: { + SkPaint::Style s = paint.getStyle(); + paint.setStyle(SkPaint::kFill_Style); + canvas->drawPath(path, paint); + paint.setStyle(s); + } break; + + case 23: { + SkRect r; + r.set(make_number(),make_number(),make_number(),make_number()); + SkPaint::Style s = paint.getStyle(); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawRect(r, paint); + paint.setStyle(s); + } break; + + case 24: + if (quick == true) break; + //ctx.arc(make_number(),make_number(),make_number(),make_number(),make_number(),true); + break; + + case 25: + if (quick == true) break; + //ctx.arcTo(make_number(),make_number(),make_number(),make_number(),make_number()); + break; + + case 26: + if (quick == true) break; + //ctx.bezierCurveTo(make_number(),make_number(),make_number(),make_number(),make_number(),make_number()); + break; + + case 27: + path.lineTo(make_number(),make_number()); + break; + + case 28: + path.moveTo(make_number(),make_number()); + break; + + case 29: + if (quick == true) break; + path.quadTo(make_number(),make_number(),make_number(),make_number()); + break; + + case 30: { + if (quick == true) break; + SkMatrix matrix; + set2x3(&matrix, make_number(),make_number(),make_number(),make_number(),make_number(),make_number()); + canvas->concat(matrix); + } break; + + case 31: { + if (quick == true) break; + SkMatrix matrix; + set2x3(&matrix, make_number(),make_number(),make_number(),make_number(),make_number(),make_number()); + canvas->setMatrix(matrix); + } break; + + case 32: + + if (scale_large == true) { + + switch (scval) { + case 0: canvas->scale(-1000000000,1); + canvas->scale(-1000000000,1); + scval = 1; break; + case 1: canvas->scale(-.000000001f,1); scval = 2; break; + case 2: canvas->scale(-.000000001f,1); scval = 0; break; + } + + } + + break; + + + + } + } + +} + +////////////////////////////////////////////////////////////////////////////// + +class FuzzView : public SampleView { +public: + FuzzView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Fuzzer"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkIRect r = canvas->getTotalClip().getBounds(); + do_fuzz(canvas); + this->inval(NULL); + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new FuzzView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleGM.cpp b/samplecode/SampleGM.cpp new file mode 100644 index 0000000000..ec5b22a936 --- /dev/null +++ b/samplecode/SampleGM.cpp @@ -0,0 +1,114 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" + +#include "gm.h" + +using namespace skiagm; + +// need to explicitly declare this, or we get some weird infinite loop llist +template GMRegistry* GMRegistry::gHead; + +class Iter { +public: + Iter() { + fReg = GMRegistry::Head(); + } + + void reset() { + fReg = GMRegistry::Head(); + } + + GM* next() { + if (fReg) { + GMRegistry::Factory fact = fReg->factory(); + fReg = fReg->next(); + return fact(0); + } + return NULL; + } + + static int Count() { + const GMRegistry* reg = GMRegistry::Head(); + int count = 0; + while (reg) { + count += 1; + reg = reg->next(); + } + return count; + } + +private: + const GMRegistry* fReg; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GMView : public SampleView { + Iter fIter; + GM* fGM; +public: + GMView() { + fGM = fIter.next(); + this->postNextGM(); + + this->setBGColor(0xFFDDDDDD); + } + + virtual ~GMView() { + delete fGM; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "GM"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual bool onEvent(const SkEvent& evt) { + if (evt.isType("next-gm")) { + delete fGM; + if (!(fGM = fIter.next())) { + fIter.reset(); + fGM = fIter.next(); + } + this->inval(NULL); + this->postNextGM(); + return true; + } + return this->INHERITED::onEvent(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + fGM->draw(canvas); + } + +private: + void postNextGM() { + (new SkEvent("next-gm"))->post(this->getSinkID(), 1500); + } + + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new GMView; } +static SkViewRegister reg(MyFactory); + +/////////////////////////////////////////////////////////////////////////////// + +using namespace skiagm; + +GM::GM() {} +GM::~GM() {} + +void GM::draw(SkCanvas* canvas) { + this->onDraw(canvas); +} + + diff --git a/samplecode/SampleGradients.cpp b/samplecode/SampleGradients.cpp new file mode 100644 index 0000000000..902b0bd6e0 --- /dev/null +++ b/samplecode/SampleGradients.cpp @@ -0,0 +1,176 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" + +static SkShader* setgrad(const SkRect& r, SkColor c0, SkColor c1) { + SkColor colors[] = { c0, c1 }; + SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } }; + return SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kClamp_TileMode, NULL); +} + +static void test_alphagradients(SkCanvas* canvas) { + SkRect r; + r.set(SkIntToScalar(10), SkIntToScalar(10), + SkIntToScalar(410), SkIntToScalar(30)); + SkPaint p, p2; + p2.setStyle(SkPaint::kStroke_Style); + + p.setShader(setgrad(r, 0xFF00FF00, 0x0000FF00))->unref(); + canvas->drawRect(r, p); + canvas->drawRect(r, p2); + + r.offset(0, r.height() + SkIntToScalar(4)); + p.setShader(setgrad(r, 0xFF00FF00, 0x00000000))->unref(); + canvas->drawRect(r, p); + canvas->drawRect(r, p2); + + r.offset(0, r.height() + SkIntToScalar(4)); + p.setShader(setgrad(r, 0xFF00FF00, 0x00FF0000))->unref(); + canvas->drawRect(r, p); + canvas->drawRect(r, p2); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct GradData { + int fCount; + const SkColor* fColors; + const SkScalar* fPos; +}; + +static const SkColor gColors[] = { + SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK +}; +static const SkScalar gPos0[] = { 0, SK_Scalar1 }; +static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; +static const SkScalar gPos2[] = { + 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1 +}; + +static const GradData gGradData[] = { + { 2, gColors, NULL }, + { 2, gColors, gPos0 }, + { 2, gColors, gPos1 }, + { 5, gColors, NULL }, + { 5, gColors, gPos2 } +}; + +static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, + data.fCount, tm, mapper); +} + +static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateRadial(center, center.fX, data.fColors, + data.fPos, data.fCount, tm, mapper); +} + +static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, + data.fPos, data.fCount, mapper); +} + +static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center0, center1; + center0.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), + SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); + return SkGradientShader::CreateTwoPointRadial( + center1, (pts[1].fX - pts[0].fX) / 7, + center0, (pts[1].fX - pts[0].fX) / 2, + data.fColors, data.fPos, data.fCount, tm, mapper); +} + +static SkShader* Make2RadialConcentric(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateTwoPointRadial( + center, (pts[1].fX - pts[0].fX) / 7, + center, (pts[1].fX - pts[0].fX) / 2, + data.fColors, data.fPos, data.fCount, tm, mapper); +} + +typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper); +static const GradMaker gGradMakers[] = { + MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2RadialConcentric +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GradientsView : public SampleView { +public: + GradientsView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Gradients"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPoint pts[2] = { + { 0, 0 }, + { SkIntToScalar(100), SkIntToScalar(100) } + }; + SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; + SkPaint paint; + paint.setDither(true); + + canvas->save(); + canvas->translate(SkIntToScalar(20), SkIntToScalar(10)); + + for (int tm = 0; tm < SkShader::kTileModeCount; ++tm) { + canvas->save(); + for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { + canvas->save(); + for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { + SkShader* shader; + shader = gGradMakers[j](pts, gGradData[i], (SkShader::TileMode)tm, NULL); + paint.setShader(shader)->unref(); + canvas->drawRect(r, paint); + canvas->translate(0, SkIntToScalar(120)); + } + canvas->restore(); + canvas->translate(SkIntToScalar(120), 0); + } + canvas->restore(); + canvas->translate(SK_ARRAY_COUNT(gGradData)*SkIntToScalar(120), 0); + } + canvas->restore(); + + canvas->translate(0, SkIntToScalar(370)); + // test_alphagradients(canvas); + this->inval(NULL); + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new GradientsView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp new file mode 100644 index 0000000000..8368f5be43 --- /dev/null +++ b/samplecode/SampleHairline.cpp @@ -0,0 +1,272 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkCornerPathEffect.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" +#include "SkColorPriv.h" +#include "SkImageDecoder.h" + +static SkRandom gRand; + +static void test_chromium_9005() { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 800, 600); + bm.allocPixels(); + + SkCanvas canvas(bm); + + SkPoint pt0 = { SkFloatToScalar(799.33374f), SkFloatToScalar(1.2360189f) }; + SkPoint pt1 = { SkFloatToScalar(808.49969f), SkFloatToScalar(-7.4338055f) }; + + SkPaint paint; + paint.setAntiAlias(true); + canvas.drawLine(pt0.fX, pt0.fY, pt1.fX, pt1.fY, paint); +} + +static void generate_pts(SkPoint pts[], int count, int w, int h) { + for (int i = 0; i < count; i++) { + pts[i].set(gRand.nextUScalar1() * 3 * w - SkIntToScalar(w), + gRand.nextUScalar1() * 3 * h - SkIntToScalar(h)); + } +} + +static bool check_zeros(const SkPMColor pixels[], int count, int skip) { + for (int i = 0; i < count; i++) { + if (*pixels) { + return false; + } + pixels += skip; + } + return true; +} + +static bool check_bitmap_margin(const SkBitmap& bm, int margin) { + size_t rb = bm.rowBytes(); + for (int i = 0; i < margin; i++) { + if (!check_zeros(bm.getAddr32(0, i), bm.width(), 1)) { + return false; + } + int bottom = bm.height() - i - 1; + if (!check_zeros(bm.getAddr32(0, bottom), bm.width(), 1)) { + return false; + } + // left column + if (!check_zeros(bm.getAddr32(i, 0), bm.height(), rb >> 2)) { + return false; + } + int right = bm.width() - margin + i; + if (!check_zeros(bm.getAddr32(right, 0), bm.height(), rb >> 2)) { + return false; + } + } + return true; +} + +#define WIDTH 620 +#define HEIGHT 460 +#define MARGIN 10 + +static void line_proc(SkCanvas* canvas, const SkPaint& paint, + const SkBitmap& bm) { + const int N = 2; + SkPoint pts[N]; + for (int i = 0; i < 400; i++) { + generate_pts(pts, N, WIDTH, HEIGHT); + + canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); + if (!check_bitmap_margin(bm, MARGIN)) { + SkDebugf("---- hairline failure (%g %g) (%g %g)\n", + pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY); + break; + } + } +} + +static void poly_proc(SkCanvas* canvas, const SkPaint& paint, + const SkBitmap& bm) { + const int N = 8; + SkPoint pts[N]; + for (int i = 0; i < 50; i++) { + generate_pts(pts, N, WIDTH, HEIGHT); + + SkPath path; + path.moveTo(pts[0]); + for (int j = 1; j < N; j++) { + path.lineTo(pts[j]); + } + canvas->drawPath(path, paint); + } +} + +static SkPoint ave(const SkPoint& a, const SkPoint& b) { + SkPoint c = a + b; + c.fX = SkScalarHalf(c.fX); + c.fY = SkScalarHalf(c.fY); + return c; +} + +static void quad_proc(SkCanvas* canvas, const SkPaint& paint, + const SkBitmap& bm) { + const int N = 30; + SkPoint pts[N]; + for (int i = 0; i < 10; i++) { + generate_pts(pts, N, WIDTH, HEIGHT); + + SkPath path; + path.moveTo(pts[0]); + for (int j = 1; j < N - 2; j++) { + path.quadTo(pts[j], ave(pts[j], pts[j+1])); + } + path.quadTo(pts[N - 2], pts[N - 1]); + + canvas->drawPath(path, paint); + } +} + +static void add_cubic(SkPath* path, const SkPoint& mid, const SkPoint& end) { + SkPoint start; + path->getLastPt(&start); + path->cubicTo(ave(start, mid), ave(mid, end), end); +} + +static void cube_proc(SkCanvas* canvas, const SkPaint& paint, + const SkBitmap& bm) { + const int N = 30; + SkPoint pts[N]; + for (int i = 0; i < 10; i++) { + generate_pts(pts, N, WIDTH, HEIGHT); + + SkPath path; + path.moveTo(pts[0]); + for (int j = 1; j < N - 2; j++) { + add_cubic(&path, pts[j], ave(pts[j], pts[j+1])); + } + add_cubic(&path, pts[N - 2], pts[N - 1]); + + canvas->drawPath(path, paint); + } +} + +typedef void (*HairProc)(SkCanvas*, const SkPaint&, const SkBitmap&); + +static const struct { + const char* fName; + HairProc fProc; +} gProcs[] = { + { "line", line_proc }, + { "poly", poly_proc }, + { "quad", quad_proc }, + { "cube", cube_proc }, +}; + +static int cycle_hairproc_index(int index) { + return (index + 1) % SK_ARRAY_COUNT(gProcs); +} + +class HairlineView : public SampleView { + SkMSec fNow; + int fProcIndex; + bool fDoAA; +public: + HairlineView() { + fCounter = 0; + fProcIndex = 0; + fDoAA = true; + fNow = 0; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SkString str; + str.printf("Hair-%s", gProcs[fProcIndex].fName); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void show_bitmaps(SkCanvas* canvas, const SkBitmap& b0, const SkBitmap& b1, + const SkIRect& inset) { + canvas->drawBitmap(b0, 0, 0, NULL); + canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, NULL); + } + + int fCounter; + + virtual void onDrawContent(SkCanvas* canvas) { + gRand.setSeed(fNow); + + if (false) { + test_chromium_9005(); + } + + SkBitmap bm, bm2; + bm.setConfig(SkBitmap::kARGB_8888_Config, + WIDTH + MARGIN*2, + HEIGHT + MARGIN*2); + bm.allocPixels(); + // this will erase our margin, which we want to always stay 0 + bm.eraseColor(0); + + bm2.setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT, + bm.rowBytes()); + bm2.setPixels(bm.getAddr32(MARGIN, MARGIN)); + + SkCanvas c2(bm2); + SkPaint paint; + paint.setAntiAlias(fDoAA); + paint.setStyle(SkPaint::kStroke_Style); + + bm2.eraseColor(0); + gProcs[fProcIndex].fProc(&c2, paint, bm); + canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), NULL); + + SkMSec now = SampleCode::GetAnimTime(); + if (fNow != now) { + fNow = now; + fCounter += 1; + fDoAA = !fDoAA; + if (fCounter > 50) { + fProcIndex = cycle_hairproc_index(fProcIndex); + // todo: signal that we want to rebuild our TITLE + fCounter = 0; + } + this->inval(NULL); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fDoAA = !fDoAA; + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new HairlineView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleImage.cpp b/samplecode/SampleImage.cpp new file mode 100644 index 0000000000..29442991a7 --- /dev/null +++ b/samplecode/SampleImage.cpp @@ -0,0 +1,156 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkImageRef_GlobalPool.h" +#include "SkStream.h" + +static const char* gNames[] = { + "1.bmp", "1.gif", "1.jpg", "1.png", + "2.bmp", "2.gif", "2.jpg", "2.png" +}; + +static bool SetImageRef(SkBitmap* bitmap, SkStream* stream, + SkBitmap::Config pref, const char name[] = NULL) +{ + if (SkImageDecoder::DecodeStream(stream, bitmap, pref, + SkImageDecoder::kDecodeBounds_Mode, NULL)) { + SkASSERT(bitmap->config() != SkBitmap::kNo_Config); + + SkImageRef* ref = new SkImageRef_GlobalPool(stream, bitmap->config()); + ref->setURI(name); + bitmap->setPixelRef(ref)->unref(); + return true; + } else { + return false; + } +} + +class ImageView : public SkView { +public: + SkBitmap* fBitmaps; + SkShader* fShader; + + ImageView() { + SkImageRef_GlobalPool::SetRAMBudget(32 * 1024); + + int i, N = SK_ARRAY_COUNT(gNames); + fBitmaps = new SkBitmap[N]; + + for (i = 0; i < N; i++) { + SkString str("/skimages/"); + str.append(gNames[i]); + SkFILEStream* stream = new SkFILEStream(str.c_str()); + + SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config, gNames[i]); + if (i & 1) + fBitmaps[i].buildMipMap(); + stream->unref(); + } + + fShader = SkShader::CreateBitmapShader(fBitmaps[5], + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + + if (true) { + SkMatrix m; + + m.setRotate(SkIntToScalar(30)); + fShader->setLocalMatrix(m); + } + +#if 0 + SkImageRef::DumpPool(); + for (i = 0; i < N; i++) { + SkBitmap& bm = fBitmaps[i]; + + SkDebugf("<%s> addr=%p", gNames[i], bm.getPixels()); + bool success = bm.lockPixels(); + SkDebugf(" addr=%d", bm.getPixels()); + if (success) + bm.unlockPixels(); + SkDebugf(" addr=%p", bm.getPixels()); + success = bm.lockPixels(); + SkDebugf(" addr=%d", bm.getPixels()); + if (success) + bm.unlockPixels(); + SkDebugf("\n"); + } + SkImageRef::DumpPool(); +#endif + } + + virtual ~ImageView() { + delete[] fBitmaps; + delete fShader; + + SkImageRef_GlobalPool::DumpPool(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Image"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); +// canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + SkScalar x = 0, y = 0; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) { + canvas->drawBitmap(fBitmaps[i], x, y); + x += SkIntToScalar(fBitmaps[i].width() + 10); + } + + canvas->translate(0, SkIntToScalar(120)); + + SkPaint paint; + paint.setShader(fShader); + paint.setFilterBitmap(true); + SkRect r = { 0, 0, SkIntToScalar(300), SkIntToScalar(100) }; + + canvas->drawRect(r, paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ImageView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleImageDir.cpp b/samplecode/SampleImageDir.cpp new file mode 100644 index 0000000000..8ef59ad77e --- /dev/null +++ b/samplecode/SampleImageDir.cpp @@ -0,0 +1,310 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkComposeShader.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkImageRef_GlobalPool.h" +#include "SkOSFile.h" +#include "SkStream.h" + +#include "SkBlurDrawLooper.h" +#include "SkColorMatrixFilter.h" + +static void drawmarshmallow(SkCanvas* canvas) { + SkBitmap bitmap; + SkPaint paint; + SkRect r; + SkMatrix m; + + SkImageDecoder::DecodeFile("/Users/reed/Downloads/3elfs.jpg", &bitmap); + SkShader* s = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + paint.setShader(s)->unref(); + m.setTranslate(SkIntToScalar(250), SkIntToScalar(134)); + s->setLocalMatrix(m); + + r.set(SkIntToScalar(250), + SkIntToScalar(134), + SkIntToScalar(250 + 449), + SkIntToScalar(134 + 701)); + paint.setFlags(2); + + canvas->drawRect(r, paint); +} + +static void DrawRoundRect(SkCanvas& canvas) { + bool ret = false; + SkPaint paint; + SkBitmap bitmap; + SkMatrix matrix; + matrix.reset(); + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812); + bitmap.allocPixels(); +#if 0 + SkCanvas canvas; + canvas.setBitmapDevice(bitmap); +#endif + + // set up clipper + SkRect skclip; + skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708)); + +// ret = canvas.clipRect(skclip); +// SkASSERT(ret); + + matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28)); + matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50)); + + matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171)); + matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043)); + + matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968)); + matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876)); + + matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0)); + matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0)); + + ret = canvas.concat(matrix); + + paint.setAntiAlias(true); + paint.setColor(0xb2202020); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkFloatToFixed(68.13)); + + SkRect r; + r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541)); + canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint); +} + +static bool SetImageRef(SkBitmap* bitmap, SkStream* stream, + SkBitmap::Config pref, const char name[] = NULL) { +#if 0 + // test buffer streams + SkStream* str = new SkBufferStream(stream, 717); + stream->unref(); + stream = str; +#endif + + SkImageRef* ref = new SkImageRef_GlobalPool(stream, pref, 1); + ref->setURI(name); + if (!ref->getInfo(bitmap)) { + delete ref; + return false; + } + bitmap->setPixelRef(ref)->unref(); + return true; +} + +//#define SPECIFIC_IMAGE "/skimages/72.jpg" +#define SPECIFIC_IMAGE "/Users/reed/Downloads/3elfs.jpg" + +#define IMAGE_DIR "/skimages/" +#define IMAGE_SUFFIX ".gif" + +class ImageDirView : public SkView { +public: + SkBitmap* fBitmaps; + SkString* fStrings; + int fBitmapCount; + int fCurrIndex; + SkScalar fSaturation; + SkScalar fAngle; + + ImageDirView() { + SkImageRef_GlobalPool::SetRAMBudget(320 * 1024); + +#ifdef SPECIFIC_IMAGE + fBitmaps = new SkBitmap[3]; + fStrings = new SkString[3]; + fBitmapCount = 3; + const SkBitmap::Config configs[] = { + SkBitmap::kARGB_8888_Config, + SkBitmap::kRGB_565_Config, + SkBitmap::kARGB_4444_Config + }; + for (int i = 0; i < fBitmapCount; i++) { +#if 1 + SkStream* stream = new SkFILEStream(SPECIFIC_IMAGE); + SetImageRef(&fBitmaps[i], stream, configs[i], SPECIFIC_IMAGE); + stream->unref(); +#else + SkImageDecoder::DecodeFile(SPECIFIC_IMAGE, &fBitmaps[i]); +#endif + } +#else + int i, N = 0; + SkOSFile::Iter iter(IMAGE_DIR, IMAGE_SUFFIX); + SkString name; + while (iter.next(&name)) { + N += 1; + } + fBitmaps = new SkBitmap[N]; + fStrings = new SkString[N]; + iter.reset(IMAGE_DIR, IMAGE_SUFFIX); + for (i = 0; i < N; i++) { + iter.next(&name); + SkString path(IMAGE_DIR); + path.append(name); + SkStream* stream = new SkFILEStream(path.c_str()); + + SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config, + name.c_str()); + stream->unref(); + fStrings[i] = name; + } + fBitmapCount = N; +#endif + fCurrIndex = 0; + fDX = fDY = 0; + + fSaturation = SK_Scalar1; + fAngle = 0; + + fScale = SK_Scalar1; + } + + virtual ~ImageDirView() { + delete[] fBitmaps; + delete[] fStrings; + + SkImageRef_GlobalPool::DumpPool(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SkString str("ImageDir: "); +#ifdef SPECIFIC_IMAGE + str.append(SPECIFIC_IMAGE); +#else + str.append(IMAGE_DIR); +#endif + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { +// canvas->drawColor(0xFFDDDDDD); + canvas->drawColor(SK_ColorGRAY); + canvas->drawColor(SK_ColorWHITE); + } + + SkScalar fScale; + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + if (true) { + canvas->scale(SkIntToScalar(2), SkIntToScalar(2)); + drawmarshmallow(canvas); + return; + } + + if (false) { + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SkIntToScalar(4)); + canvas->drawCircle(SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(50), p); + p.setAntiAlias(true); + canvas->drawCircle(SkIntToScalar(300), SkIntToScalar(100), SkIntToScalar(50), p); + } + if (false) { + SkScalar cx = this->width()/2; + SkScalar cy = this->height()/2; + canvas->translate(cx, cy); + canvas->scale(fScale, fScale); + canvas->translate(-cx, -cy); + DrawRoundRect(*canvas); + return; + } + + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + SkScalar x = SkIntToScalar(32), y = SkIntToScalar(32); + SkPaint paint; + +#if 0 + for (int i = 0; i < fBitmapCount; i++) { + SkPaint p; + +#if 1 + const SkScalar cm[] = { + SkIntToScalar(2), 0, 0, 0, SkIntToScalar(-255), + 0, SkIntToScalar(2), 0, 0, SkIntToScalar(-255), + 0, 0, SkIntToScalar(2), 0, SkIntToScalar(-255), + 0, 0, 0, SkIntToScalar(1), 0 + }; + SkColorFilter* cf = new SkColorMatrixFilter(cm); + p.setColorFilter(cf)->unref(); +#endif + + canvas->drawBitmap(fBitmaps[i], x, y, &p); + x += SkIntToScalar(fBitmaps[i].width() + 10); + } + return; +#endif + + canvas->drawBitmap(fBitmaps[fCurrIndex], x, y, &paint); +#ifndef SPECIFIC_IMAGE + if (true) { + fCurrIndex += 1; + if (fCurrIndex >= fBitmapCount) { + fCurrIndex = 0; + } + this->inval(NULL); + } +#endif + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + if (true) { + fCurrIndex += 1; + if (fCurrIndex >= fBitmapCount) + fCurrIndex = 0; + this->inval(NULL); + } + return new Click(this); + } + + virtual bool onClick(Click* click) { + SkScalar center = this->width()/2; + fSaturation = SkScalarDiv(click->fCurr.fX - center, center/2); + center = this->height()/2; + fAngle = SkScalarDiv(click->fCurr.fY - center, center) * 180; + + fDX += click->fCurr.fX - click->fPrev.fX; + fDY += click->fCurr.fY - click->fPrev.fY; + + fScale = SkScalarDiv(click->fCurr.fX, this->width()); + + this->inval(NULL); + return true; + return this->INHERITED::onClick(click); + } + +private: + SkScalar fDX, fDY; + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ImageDirView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleLCD.cpp b/samplecode/SampleLCD.cpp new file mode 100644 index 0000000000..098958f4e0 --- /dev/null +++ b/samplecode/SampleLCD.cpp @@ -0,0 +1,61 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkShader.h" + +class LCDView : public SkView { +public: + LCDView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "LCD Text"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + SkPaint paint; + paint.setAntiAlias(true); + + SkScalar textSize = SkIntToScalar(6); + SkScalar delta = SK_Scalar1; + const char* text = "HHHamburgefonts iii"; + size_t len = strlen(text); + SkScalar x0 = SkIntToScalar(10); + SkScalar x1 = SkIntToScalar(310); + SkScalar y = SkIntToScalar(20); + + for (int i = 0; i < 20; i++) { + paint.setTextSize(textSize); + textSize += delta; + + paint.setLCDRenderText(false); + canvas->drawText(text, len, x0, y, paint); + paint.setLCDRenderText(true); + canvas->drawText(text, len, x1, y, paint); + + y += paint.getFontSpacing(); + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new LCDView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleLayerMask.cpp b/samplecode/SampleLayerMask.cpp new file mode 100644 index 0000000000..9bd00ae5af --- /dev/null +++ b/samplecode/SampleLayerMask.cpp @@ -0,0 +1,68 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkView.h" + +/////////////////////////////////////////////////////////////////////////////// + +class LayerMaskView : public SampleView { +public: + LayerMaskView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "LayerMask"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawMask(SkCanvas* canvas, const SkRect& r) { + SkPaint paint; + paint.setAntiAlias(true); + + if (true) { + SkBitmap mask; + int w = SkScalarRound(r.width()); + int h = SkScalarRound(r.height()); + mask.setConfig(SkBitmap::kARGB_8888_Config, w, h); + mask.allocPixels(); + mask.eraseColor(0); + SkCanvas c(mask); + SkRect bounds = r; + bounds.offset(-bounds.fLeft, -bounds.fTop); + c.drawOval(bounds, paint); + + paint.setXfermodeMode(SkXfermode::kDstIn_Mode); + canvas->drawBitmap(mask, r.fLeft, r.fTop, &paint); + } else { + SkPath p; + p.addOval(r); + p.setFillType(SkPath::kInverseWinding_FillType); + paint.setXfermodeMode(SkXfermode::kDstOut_Mode); + canvas->drawPath(p, paint); + } + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkRect r; + r.set(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(120), SkIntToScalar(120)); + canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag); + canvas->drawColor(SK_ColorRED); + drawMask(canvas, r); + canvas->restore(); + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new LayerMaskView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp new file mode 100644 index 0000000000..6fc9c83492 --- /dev/null +++ b/samplecode/SampleLayers.cpp @@ -0,0 +1,271 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkBlurMaskFilter.h" +#include "SkCamera.h" +#include "SkColorFilter.h" +#include "SkColorPriv.h" +#include "SkDevice.h" +#include "SkGradientShader.h" +#include "SkImageDecoder.h" +#include "SkInterpolator.h" +#include "SkMaskFilter.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkUtils.h" +#include "SkKey.h" +#include "SkXfermode.h" +#include "SkDrawFilter.h" + +static void make_paint(SkPaint* paint) { + SkColor colors[] = { 0, SK_ColorWHITE }; + SkPoint pts[] = { { 0, 0 }, { 0, SK_Scalar1*20 } }; + SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode); + + paint->setShader(s)->unref(); + paint->setXfermodeMode(SkXfermode::kDstIn_Mode); +} + +static void dump_layers(const char label[], SkCanvas* canvas) { + SkDebugf("Dump Layers(%s)\n", label); + + SkCanvas::LayerIter iter(canvas, true); + int index = 0; + while (!iter.done()) { + const SkBitmap& bm = iter.device()->accessBitmap(false); + const SkIRect& clip = iter.clip().getBounds(); + SkDebugf("Layer[%d] bitmap [%d %d] X=%d Y=%d clip=[%d %d %d %d] alpha=%d\n", index++, + bm.width(), bm.height(), iter.x(), iter.y(), + clip.fLeft, clip.fTop, clip.fRight, clip.fBottom, + iter.paint().getAlpha()); + iter.next(); + } +} + +// test drawing with strips of fading gradient above and below +static void test_fade(SkCanvas* canvas) { + SkAutoCanvasRestore ar(canvas, true); + + SkRect r; + + SkPaint p; + p.setAlpha(0x88); + + SkAutoCanvasRestore(canvas, false); + + // create the layers + + r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100)); + canvas->clipRect(r); + + r.fBottom = SkIntToScalar(20); + canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); + + r.fTop = SkIntToScalar(80); + r.fBottom = SkIntToScalar(100); + canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); + + // now draw the "content" + + if (true) { + r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100)); + + canvas->saveLayerAlpha(&r, 0x80); + + SkPaint p; + p.setColor(SK_ColorRED); + p.setAntiAlias(true); + canvas->drawOval(r, p); + + dump_layers("inside layer alpha", canvas); + + canvas->restore(); + } else { + r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100)); + + SkPaint p; + p.setColor(SK_ColorRED); + p.setAntiAlias(true); + canvas->drawOval(r, p); + } + +// return; + + dump_layers("outside layer alpha", canvas); + + // now apply an effect + + SkPaint paint; + make_paint(&paint); + r.set(0, 0, SkIntToScalar(100), SkIntToScalar(20)); +// SkDebugf("--------- draw top grad\n"); + canvas->drawRect(r, paint); + + SkMatrix m; + SkShader* s = paint.getShader(); + m.setScale(SK_Scalar1, -SK_Scalar1); + m.postTranslate(0, SkIntToScalar(100)); + s->setLocalMatrix(m); + + r.fTop = SkIntToScalar(80); + r.fBottom = SkIntToScalar(100); +// SkDebugf("--------- draw bot grad\n"); + canvas->drawRect(r, paint); +} + +class RedFilter : public SkDrawFilter { +public: + virtual bool filter(SkCanvas*, SkPaint* p, SkDrawFilter::Type) { + fColor = p->getColor(); + if (fColor == SK_ColorRED) { + p->setColor(SK_ColorGREEN); + } + return true; + } + virtual void restore(SkCanvas*, SkPaint* p, SkDrawFilter::Type) { + p->setColor(fColor); + } + +private: + SkColor fColor; +}; + +class LayersView : public SkView { +public: + LayersView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Layers"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorGRAY); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + if (true) { + SkRect r; + r.set(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(220), SkIntToScalar(120)); + SkPaint p; + canvas->saveLayer(&r, &p); + canvas->drawColor(0xFFFF0000); + p.setAlpha(0); // or 0 + p.setXfermodeMode(SkXfermode::kSrc_Mode); + canvas->drawOval(r, p); + canvas->restore(); + return; + } + + if (false) { + SkRect r; + r.set(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(220), SkIntToScalar(120)); + SkPaint p; + p.setAlpha(0x88); + p.setAntiAlias(true); + + if (true) { + canvas->saveLayer(&r, &p); + p.setColor(0xFFFF0000); + canvas->drawOval(r, p); + canvas->restore(); + } + + p.setColor(0xFF0000FF); + r.offset(SkIntToScalar(20), SkIntToScalar(50)); + canvas->drawOval(r, p); + } + + if (false) { + SkPaint p; + p.setAlpha(0x88); + p.setAntiAlias(true); + + canvas->translate(SkIntToScalar(300), 0); + + SkRect r; + r.set(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(220), SkIntToScalar(60)); + + canvas->saveLayer(&r, &p, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); +// canvas->clipRect(r, SkRegion::kDifference_Op); +// canvas->clipRect(r, SkRegion::kIntersect_Op); + + r.set(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(220), SkIntToScalar(120)); + p.setColor(SK_ColorBLUE); + canvas->drawOval(r, p); + canvas->restore(); + return; + } + + //canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); + test_fade(canvas); + return; + + // canvas->setDrawFilter(new RedFilter)->unref(); + + SkRect r; + SkPaint p; + + canvas->translate(SkIntToScalar(220), SkIntToScalar(20)); + + p.setAntiAlias(true); + r.set(SkIntToScalar(20), SkIntToScalar(20), + SkIntToScalar(220), SkIntToScalar(120)); + + p.setColor(SK_ColorBLUE); + // p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(8), SkBlurMaskFilter::kNormal_BlurStyle))->unref(); + canvas->drawRect(r, p); + p.setMaskFilter(NULL); + + SkRect bounds = r; + bounds.fBottom = bounds.centerY(); + canvas->saveLayer(&bounds, NULL, SkCanvas::kARGB_NoClipLayer_SaveFlag); + + p.setColor(SK_ColorRED); + canvas->drawOval(r, p); + + p.setAlpha(0x80); + p.setXfermodeMode(SkXfermode::kDstIn_Mode); + canvas->drawRect(bounds, p); + + canvas->restore(); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + + virtual bool handleKey(SkKey key) { + this->inval(NULL); + return true; + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new LayersView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleLineClipper.cpp b/samplecode/SampleLineClipper.cpp new file mode 100644 index 0000000000..ac6b013ac8 --- /dev/null +++ b/samplecode/SampleLineClipper.cpp @@ -0,0 +1,255 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkRandom.h" + +#include "SkLineClipper.h" +#include "SkEdgeClipper.h" + +#define AUTO_ANIMATE true + +static int test0(SkPoint pts[], SkRect* clip) { + pts[0].set(200000, 140); + pts[1].set(-740000, 483); + pts[2].set(1.00000102e-06f, 9.10000017e-05f); + clip->set(0, 0, 640, 480); + return 2; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void drawQuad(SkCanvas* canvas, const SkPoint pts[3], const SkPaint& p) { + SkPath path; + path.moveTo(pts[0]); + path.quadTo(pts[1], pts[2]); + canvas->drawPath(path, p); +} + +static void drawCubic(SkCanvas* canvas, const SkPoint pts[4], const SkPaint& p) { + SkPath path; + path.moveTo(pts[0]); + path.cubicTo(pts[1], pts[2], pts[3]); + canvas->drawPath(path, p); +} + +typedef void (*clipper_proc)(const SkPoint src[], const SkRect& clip, + SkCanvas*, const SkPaint&, const SkPaint&); + +static void check_clipper(int count, const SkPoint pts[], const SkRect& clip) { + for (int i = 0; i < count; i++) { + SkASSERT(pts[i].fX >= clip.fLeft); + SkASSERT(pts[i].fX <= clip.fRight); + SkASSERT(pts[i].fY >= clip.fTop); + SkASSERT(pts[i].fY <= clip.fBottom); + } + + if (count > 1) { + sk_assert_monotonic_y(pts, count); + } +} + +static void line_intersector(const SkPoint src[], const SkRect& clip, + SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1); + + SkPoint dst[2]; + if (SkLineClipper::IntersectLine(src, clip, dst)) { + check_clipper(2, dst, clip); + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, dst, p0); + } +} + +static void line_clipper(const SkPoint src[], const SkRect& clip, + SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1); + + SkPoint dst[SkLineClipper::kMaxPoints]; + int count = SkLineClipper::ClipLine(src, clip, dst); + for (int i = 0; i < count; i++) { + check_clipper(2, &dst[i], clip); + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, &dst[i], p0); + } +} + +static void quad_clipper(const SkPoint src[], const SkRect& clip, + SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { + drawQuad(canvas, src, p1); + + SkEdgeClipper clipper; + if (clipper.clipQuad(src, clip)) { + SkPoint pts[4]; + SkPath::Verb verb; + while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kLine_Verb: + check_clipper(2, pts, clip); + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0); + break; + case SkPath::kQuad_Verb: + check_clipper(3, pts, clip); + drawQuad(canvas, pts, p0); + break; + default: + SkASSERT(!"unexpected verb"); + } + } + } +} + +static void cubic_clipper(const SkPoint src[], const SkRect& clip, + SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) { + drawCubic(canvas, src, p1); + + SkEdgeClipper clipper; + if (clipper.clipCubic(src, clip)) { + SkPoint pts[4]; + SkPath::Verb verb; + while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kLine_Verb: + check_clipper(2, pts, clip); + canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0); + break; + case SkPath::kCubic_Verb: + // check_clipper(4, pts, clip); + drawCubic(canvas, pts, p0); + break; + default: + SkASSERT(!"unexpected verb"); + } + } + } +} + +static const clipper_proc gProcs[] = { + line_intersector, + line_clipper, + quad_clipper, + cubic_clipper +}; + +/////////////////////////////////////////////////////////////////////////////// + +enum { + W = 640/3, + H = 480/3 +}; + +class LineClipperView : public SkView { + SkMSec fNow; + int fCounter; + int fProcIndex; + SkRect fClip; + SkRandom fRand; + SkPoint fPts[4]; + + void randPts() { + for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) { + fPts[i].set(fRand.nextUScalar1() * 640, + fRand.nextUScalar1() * 480); + } + fCounter += 1; + } + +public: + LineClipperView() { + fProcIndex = 0; + fCounter = 0; + fNow = 0; + + int x = (640 - W)/2; + int y = (480 - H)/2; + fClip.set(SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + W), SkIntToScalar(y + H)); + this->randPts(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "LineClipper"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + static void drawVLine(SkCanvas* canvas, SkScalar x, const SkPaint& paint) { + canvas->drawLine(x, -999, x, 999, paint); + } + + static void drawHLine(SkCanvas* canvas, SkScalar y, const SkPaint& paint) { + canvas->drawLine(-999, y, 999, y, paint); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + SkMSec now = SampleCode::GetAnimTime(); + if (fNow != now) { + fNow = now; + this->randPts(); + this->inval(NULL); + } + + // fProcIndex = test0(fPts, &fClip); + + SkPaint paint, paint1; + + drawVLine(canvas, fClip.fLeft + SK_ScalarHalf, paint); + drawVLine(canvas, fClip.fRight - SK_ScalarHalf, paint); + drawHLine(canvas, fClip.fTop + SK_ScalarHalf, paint); + drawHLine(canvas, fClip.fBottom - SK_ScalarHalf, paint); + + paint.setColor(SK_ColorLTGRAY); + canvas->drawRect(fClip, paint); + + paint.setAntiAlias(true); + paint.setColor(SK_ColorBLUE); + paint.setStyle(SkPaint::kStroke_Style); + // paint.setStrokeWidth(SkIntToScalar(3)); + paint.setStrokeCap(SkPaint::kRound_Cap); + + paint1.setAntiAlias(true); + paint1.setColor(SK_ColorRED); + paint1.setStyle(SkPaint::kStroke_Style); + gProcs[fProcIndex](fPts, fClip, canvas, paint, paint1); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + // fProcIndex = (fProcIndex + 1) % SK_ARRAY_COUNT(gProcs); + if (x < 50 && y < 50) { + this->randPts(); + } + this->inval(NULL); + return NULL; + } + + virtual bool onClick(Click* click) { + return false; + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new LineClipperView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp new file mode 100644 index 0000000000..03dd30feed --- /dev/null +++ b/samplecode/SampleLines.cpp @@ -0,0 +1,109 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkCornerPathEffect.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" +#include "SkColorPriv.h" +#include "SkImageDecoder.h" + +class LinesView : public SampleView { +public: + LinesView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Lines"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + /* + 0x1F * x + 0x1F * (32 - x) + */ + void drawRings(SkCanvas* canvas) { + canvas->scale(SkIntToScalar(1)/2, SkIntToScalar(1)/2); + + SkRect r; + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(10); + r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100)); + + SkPaint paint; + // paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3))); + paint.setColor(0xFFFF8800); + // paint.setColor(0xFFFFFFFF); + canvas->drawRect(r, paint); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkBitmap bm; + SkImageDecoder::DecodeFile("/kill.gif", &bm); + canvas->drawBitmap(bm, 0, 0, NULL); + + this->drawRings(canvas); + return; + + SkPaint paint; + + // fAlpha = 0x80; + paint.setColor(SK_ColorWHITE); + paint.setAlpha(fAlpha & 0xFF); + SkRect r; + + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(10); + r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100)); + canvas->drawRect(r, paint); + return; + + paint.setColor(0xffffff00); // yellow + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(2)); + +// y += SK_Scalar1/2; + + canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint); + + paint.setAntiAlias(true); // with anti-aliasing + y += SkIntToScalar(10); + canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fAlpha = SkScalarRound(y); + this->inval(NULL); + return NULL; + } +private: + + int fAlpha; + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new LinesView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleMeasure.cpp b/samplecode/SampleMeasure.cpp new file mode 100644 index 0000000000..8078e03df1 --- /dev/null +++ b/samplecode/SampleMeasure.cpp @@ -0,0 +1,115 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkDither.h" + +// exercise scale/linear/devkern +struct Setting { + SkScalar fScale; + bool fLinearText; + bool fDevKernText; +}; + +static const SkScalar ONE = SkIntToScalar(9999)/10000; + +static const Setting gSettings[] = { + { 0, false, false }, + { 0, false, true }, + { 0, true, false }, + { 0, true, true }, + { ONE, false, false }, + { ONE, false, true }, + { ONE, true, false }, + { ONE, true, true } +}; + +static void doMeasure(SkCanvas* canvas, const SkPaint& paint, const char text[]) { + SkScalar dy = paint.getFontMetrics(NULL); + + size_t len = strlen(text); + SkAutoTMalloc<SkScalar> autoWidths(len); + SkScalar* widths = autoWidths.get(); + SkAutoTMalloc<SkRect> autoRects(len); + SkRect* rects = autoRects.get(); + SkRect bounds; + + SkPaint p(paint); + for (size_t i = 0; i < SK_ARRAY_COUNT(gSettings); i++) { + p.setLinearText(gSettings[i].fLinearText); + p.setDevKernText(gSettings[i].fDevKernText); + SkScalar scale = gSettings[i].fScale; + + int n = p.getTextWidths(text, len, widths, rects); + SkScalar w = p.measureText(text, len, &bounds, scale); + + p.setStyle(SkPaint::kFill_Style); + p.setColor(0x8888FF88); + canvas->drawRect(bounds, p); + p.setColor(0xFF000000); + canvas->drawText(text, len, 0, 0, p); + + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(0); + p.setColor(0xFFFF0000); + SkScalar x = 0; + for (int j = 0; j < n; j++) { + SkRect r = rects[j]; + r.offset(x, 0); + canvas->drawRect(r, p); + x += widths[j]; + } + + p.setColor(0xFF0000FF); + canvas->drawLine(0, 0, w, 0, p); + p.setStrokeWidth(SkIntToScalar(4)); + canvas->drawPoint(x, 0, p); + + canvas->translate(0, dy); + } +} + +class MeasureView : public SampleView { +public: + SkPaint fPaint; + + MeasureView() { + fPaint.setAntiAlias(true); + fPaint.setTextSize(SkIntToScalar(64)); + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Measure"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(fPaint.getTextSize(), fPaint.getTextSize()); + doMeasure(canvas, fPaint, "Hamburgefons"); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new MeasureView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleMipMap.cpp b/samplecode/SampleMipMap.cpp new file mode 100644 index 0000000000..3d9515691e --- /dev/null +++ b/samplecode/SampleMipMap.cpp @@ -0,0 +1,143 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkShader.h" + +static SkBitmap createBitmap(int n) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkCanvas canvas(bitmap); + SkRect r; + r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n)); + SkPaint paint; + paint.setAntiAlias(true); + + paint.setColor(SK_ColorRED); + canvas.drawOval(r, paint); + paint.setColor(SK_ColorBLUE); + paint.setStrokeWidth(SkIntToScalar(n)/15); + paint.setStyle(SkPaint::kStroke_Style); + canvas.drawLine(0, 0, r.fRight, r.fBottom, paint); + canvas.drawLine(0, r.fBottom, r.fRight, 0, paint); + + return bitmap; +} + +class MipMapView : public SampleView { + SkBitmap fBitmap; + enum { + N = 64 + }; +public: + MipMapView() { + fBitmap = createBitmap(N); + + fWidth = N; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "MipMaps"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawN(SkCanvas* canvas, const SkBitmap& bitmap) { + SkAutoCanvasRestore acr(canvas, true); + for (int i = N; i > 1; i >>= 1) { + canvas->drawBitmap(bitmap, 0, 0, NULL); + canvas->translate(SkIntToScalar(N + 8), 0); + canvas->scale(SK_ScalarHalf, SK_ScalarHalf); + } + } + + void drawN2(SkCanvas* canvas, const SkBitmap& bitmap) { + SkBitmap bg; + bg.setConfig(SkBitmap::kARGB_8888_Config, N, N); + bg.allocPixels(); + + SkAutoCanvasRestore acr(canvas, true); + for (int i = 0; i < 6; i++) { + bg.eraseColor(0); + SkCanvas c(bg); + c.scale(SK_Scalar1 / (1 << i), SK_Scalar1 / (1 << i)); + c.drawBitmap(bitmap, 0, 0, NULL); + + canvas->save(); + canvas->scale(SkIntToScalar(1 << i), SkIntToScalar(1 << i)); + canvas->drawBitmap(bg, 0, 0, NULL); + canvas->restore(); + canvas->translate(SkIntToScalar(N + 8), 0); + } + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + canvas->scale(1.00000001f, 0.9999999f); + + drawN2(canvas, fBitmap); + + canvas->translate(0, SkIntToScalar(N + 8)); + SkBitmap bitmap(fBitmap); + bitmap.buildMipMap(); + drawN2(canvas, bitmap); + + SkScalar time = SampleCode::GetAnimScalar(SkIntToScalar(1)/4, + SkIntToScalar(2)); + if (time >= SK_Scalar1) { + time = SkIntToScalar(2) - time; + } + fWidth = 8 + SkScalarRound(N * time); + + SkRect dst; + dst.set(0, 0, SkIntToScalar(fWidth), SkIntToScalar(fWidth)); + + SkPaint paint; + paint.setFilterBitmap(true); + paint.setAntiAlias(true); + + canvas->translate(0, SkIntToScalar(N + 8)); + canvas->drawBitmapRect(fBitmap, NULL, dst, NULL); + canvas->translate(SkIntToScalar(N + 8), 0); + canvas->drawBitmapRect(fBitmap, NULL, dst, &paint); + canvas->translate(-SkIntToScalar(N + 8), SkIntToScalar(N + 8)); + canvas->drawBitmapRect(bitmap, NULL, dst, NULL); + canvas->translate(SkIntToScalar(N + 8), 0); + canvas->drawBitmapRect(bitmap, NULL, dst, &paint); + + SkShader* s = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + paint.setShader(s)->unref(); + SkMatrix m; + m.setScale(SkIntToScalar(fWidth) / N, + SkIntToScalar(fWidth) / N); + s->setLocalMatrix(m); + SkRect r; + r.set(0, 0, SkIntToScalar(4*N), SkIntToScalar(5*N/2)); + r.offset(SkIntToScalar(N + 12), -SkIntToScalar(N + 4)); + canvas->drawRect(r, paint); + + this->inval(NULL); + } + +private: + int fWidth; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new MipMapView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleMovie.cpp b/samplecode/SampleMovie.cpp new file mode 100644 index 0000000000..af34198191 --- /dev/null +++ b/samplecode/SampleMovie.cpp @@ -0,0 +1,61 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkMovie.h" +#include "SkTime.h" +#include <new> + +class AnimGifView : public SkView { + SkMovie* fMovie; +public: + AnimGifView() { + fMovie = SkMovie::DecodeFile("/skimages/dollarblk.gif"); + } + + virtual ~AnimGifView() { + SkSafeUnref(fMovie); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Animated Gif"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + if (fMovie) { + if (fMovie->duration()) { + fMovie->setTime(SkTime::GetMSecs() % fMovie->duration()); + } else { + fMovie->setTime(0); + } + canvas->drawBitmap(fMovie->bitmap(), SkIntToScalar(20), + SkIntToScalar(20)); + this->inval(NULL); + } + } + +private: + SkRect fClip; + SkIPoint* fPoints; + SkPath fPath; + int fPtCount; + + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new AnimGifView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleNinePatch.cpp b/samplecode/SampleNinePatch.cpp new file mode 100644 index 0000000000..e158287523 --- /dev/null +++ b/samplecode/SampleNinePatch.cpp @@ -0,0 +1,114 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkImageDecoder.h" +#include "SkNinePatch.h" +#include "SkPaint.h" +#include "SkUnPreMultiply.h" + +class NinePatchView : public SampleView { +public: + SkBitmap fBM; + + NinePatchView() { + SkImageDecoder::DecodeFile("/skimages/btn_default_normal_disable.9.png", &fBM); + + // trim off the edge guide-lines + SkBitmap tmp; + SkIRect r; + r.set(1, 1, fBM.width() - 1, fBM.height() - 1); + fBM.extractSubset(&tmp, r); + fBM.swap(tmp); + + fX = SkIntToScalar(fBM.width()); + fY = 0; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "NinePatch"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawBackground(SkCanvas* canvas) { + SkPaint p; + p.setDither(true); + p.setColor(0xFF909090); + canvas->drawPaint(p); + } + + static void test_rects(SkCanvas* canvas, const SkBitmap& bm, const SkPaint* paint) { + static const SkIRect src[] = { + { 0, 0, 18, 34 }, + { 18, 0, 19, 34 }, + { 19, 0, 36, 34 }, + { 0, 34, 18, 35 }, + { 18, 34, 19, 35 }, + { 19, 34, 36, 35 }, + { 0, 35, 18, 72 }, + { 18, 35, 19, 72 }, + { 19, 35, 36, 72 }, + }; + static const SkRect dst[] = { + { 0, 0, 18, 34 }, + { 18, 0, 283, 34 }, + { 283, 0, 300, 34 }, + { 0, 34, 18, 163 }, + { 18, 34, 283, 163 }, + { 283, 34, 300, 163 }, + { 0, 163, 18, 200 }, + { 18, 163, 283, 200 }, + { 283, 163, 300, 200 }, + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) { + canvas->drawBitmapRect(bm, &src[i], dst[i], paint); + } + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->drawBitmap(fBM, 0, 0); + + SkIRect margins; + SkRect dst; + int d = 25; + + margins.set(d, d, d, d); + margins.fLeft = fBM.width()/2 - 1; + margins.fTop = fBM.height()/2 - 1; + margins.fRight = fBM.width() - margins.fLeft - 1; + margins.fBottom = fBM.height() - margins.fTop - 1; + + // canvas->translate(fX/5, fY/5); + canvas->translate(0, 76); + + dst.set(0, 0, SkIntToScalar(200), SkIntToScalar(200)); + + SkPaint paint; + paint.setAntiAlias(false); + paint.setDither(true); + paint.setFilterBitmap(false); + // SkNinePatch::DrawNine(canvas, dst, fBM, margins, &paint); + test_rects(canvas, fBM, &paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fX = x / 1.5f; + fY = y / 1.5f; + fX = x; fY = y; + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } +private: + SkScalar fX, fY; + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new NinePatchView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleOvalTest.cpp b/samplecode/SampleOvalTest.cpp new file mode 100644 index 0000000000..f62552946a --- /dev/null +++ b/samplecode/SampleOvalTest.cpp @@ -0,0 +1,110 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" + +static const int kILimit = 101; +static const SkScalar kLimit = SK_Scalar1 * kILimit; + +class OvalTestView : public SampleView { +public: + SkSize fSize; + SkPMColor fInsideColor; // signals an interior pixel that was not set + SkPMColor fOutsideColor; // signals an exterior pixels that was set + SkBitmap fBitmap; + + OvalTestView() { + fSize.set(SK_Scalar1, SK_Scalar1); + + fBitmap.setConfig(SkBitmap::kARGB_8888_Config, kILimit, kILimit); + fBitmap.allocPixels(); + + fInsideColor = SkPreMultiplyColor(SK_ColorRED); + fOutsideColor = SkPreMultiplyColor(SK_ColorGREEN); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "OvalTest"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawOval() { + SkCanvas canvas(fBitmap); + SkPaint p; + + fBitmap.eraseColor(0); + canvas.drawOval(SkRect::MakeSize(fSize), p); + } + + int checkOval(int* flatCount, int* buldgeCount) { + int flatc = 0; + int buldgec = 0; + const SkScalar rad = SkScalarHalf(fSize.width()); + SkScalar cx = SkScalarHalf(fSize.width()); + SkScalar cy = SkScalarHalf(fSize.height()); + for (int y = 0; y < kILimit; y++) { + for (int x = 0; x < kILimit; x++) { + // measure from pixel centers + SkScalar px = SkIntToScalar(x) + SK_ScalarHalf; + SkScalar py = SkIntToScalar(y) + SK_ScalarHalf; + + SkPMColor* ptr = fBitmap.getAddr32(x, y); + SkScalar dist = SkPoint::Length(px - cx, py - cy); + if (dist <= rad && !*ptr) { + flatc++; + *ptr = fInsideColor; + } else if (dist > rad && *ptr) { + buldgec++; + *ptr = fOutsideColor; + } + } + } + if (flatCount) *flatCount = flatc; + if (buldgeCount) *buldgeCount = buldgec; + return flatc + buldgec; + } + + virtual void onDrawContent(SkCanvas* canvas) { + this->drawOval(); + int flatCount, buldgeCount; + this->checkOval(&flatCount, &buldgeCount); + this->inval(NULL); + + canvas->drawBitmap(fBitmap, SkIntToScalar(20), SkIntToScalar(20), NULL); + + + static int gFlatCount; + static int gBuldgeCount; + gFlatCount += flatCount; + gBuldgeCount += buldgeCount; + + if (fSize.fWidth < kLimit) { + SkDebugf("--- width=%g, flat=%d buldge=%d total: flat=%d buldge=%d\n", fSize.fWidth, + flatCount, buldgeCount, gFlatCount, gBuldgeCount); + fSize.fWidth += SK_Scalar1; + fSize.fHeight += SK_Scalar1; + } else { + // fSize.set(SK_Scalar1, SK_Scalar1); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return NULL; + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new OvalTestView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleOverflow.cpp b/samplecode/SampleOverflow.cpp new file mode 100644 index 0000000000..d3ecff751b --- /dev/null +++ b/samplecode/SampleOverflow.cpp @@ -0,0 +1,100 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" + +static void DrawRoundRect() { +#ifdef SK_SCALAR_IS_FIXED + bool ret = false; + SkPaint paint; + SkBitmap bitmap; + SkCanvas canvas; + SkMatrix matrix; + matrix.reset(); + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812); + bitmap.allocPixels(); + canvas.setBitmapDevice(bitmap); + + // set up clipper + SkRect skclip; + skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708)); + + ret = canvas.clipRect(skclip); + SkASSERT(ret); + + matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28)); + matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50)); + + matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171)); + matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043)); + + matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968)); + matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876)); + + matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0)); + matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0)); + + ret = canvas.concat(matrix); + + paint.setAntiAlias(true); + paint.setColor(0xb2202020); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkFloatToFixed(68.13)); + + SkRect r; + r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541)); + canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint); +#endif +} + +static bool HitTestPath(const SkPath& path, SkScalar x, SkScalar y) { + SkRegion rgn, clip; + + int ix = SkScalarFloor(x); + int iy = SkScalarFloor(y); + + clip.setRect(ix, iy, ix + 1, iy + 1); + + bool contains = rgn.setPath(path, clip); + return contains; +} + +static void TestOverflowHitTest() { + SkPath path; + +#ifdef SK_SCALAR_IS_FLOATx + path.addCircle(0, 0, 70000, SkPath::kCCW_Direction); + SkASSERT(HitTestPath(path, 40000, 40000)); +#endif +} + +class OverflowView : public SampleView { +public: + OverflowView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Circles"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + DrawRoundRect(); + TestOverflowHitTest(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new OverflowView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePageFlip.cpp b/samplecode/SamplePageFlip.cpp new file mode 100644 index 0000000000..7c5bf480e8 --- /dev/null +++ b/samplecode/SamplePageFlip.cpp @@ -0,0 +1,167 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" +#include "SkFlipPixelRef.h" +#include "SkPageFlipper.h" + +#include <pthread.h> + +#define WIDTH 200 +#define HEIGHT 200 + +static bool gDone; + +static void bounce(SkScalar* x, SkScalar* dx, const int max) { + *x += *dx; + if (*x < 0) { + *x = 0; + if (*dx < 0) { + *dx = -*dx; + } + } else if (*x > SkIntToScalar(max)) { + *x = SkIntToScalar(max); + if (*dx > 0) { + *dx = -*dx; + } + } +} + +static void* draw_proc(void* context) { + const int OVALW = 32; + const int OVALH = 32; + + const SkBitmap* bm = static_cast<const SkBitmap*>(context); + SkFlipPixelRef* ref = static_cast<SkFlipPixelRef*>(bm->pixelRef()); + + const int DSCALE = 1; + SkScalar dx = SkIntToScalar(7) / DSCALE; + SkScalar dy = SkIntToScalar(5) / DSCALE; + SkScalar x = 0; + SkScalar y = 0; + + SkPaint paint; + + paint.setAntiAlias(true); + paint.setColor(SK_ColorRED); + + SkRect oval; + oval.setEmpty(); + + while (!gDone) { + ref->inval(oval, true); + oval.set(x, y, x + SkIntToScalar(OVALW), y + SkIntToScalar(OVALH)); + ref->inval(oval, true); + + SkAutoFlipUpdate update(ref); + + if (!update.dirty().isEmpty()) { + // this must be local to the loop, since it needs to forget the pixels + // its writing to after each iteration, since we do the swap + SkCanvas canvas(update.bitmap()); + +// SkDebugf("----- dirty [%d %d %d %d]\n", dirty.getBounds().fLeft, dirty.getBounds().fTop, dirty.getBounds().width(), dirty.getBounds().height()); + canvas.clipRegion(update.dirty()); + + canvas.drawColor(0, SkXfermode::kClear_Mode); + canvas.drawOval(oval, paint); + } + bounce(&x, &dx, WIDTH-OVALW); + bounce(&y, &dy, HEIGHT-OVALH); + +#if 1 + for (int i = 0; i < 1000; i++) { + for (int j = 0; j < 10000; j++) { + SkFixedMul(j, 10); + } + } +#endif + } + return NULL; +} + +static const SkBitmap::Config gConfigs[] = { + SkBitmap::kARGB_8888_Config, +#if 1 + SkBitmap::kRGB_565_Config, + SkBitmap::kARGB_4444_Config, + SkBitmap::kA8_Config +#endif +}; + +class PageFlipView : public SampleView { +public: + + enum { N = SK_ARRAY_COUNT(gConfigs) }; + + pthread_t fThreads[N]; + SkBitmap fBitmaps[N]; + + PageFlipView() { + gDone = false; + for (int i = 0; i < N; i++) { + int status; + pthread_attr_t attr; + + status = pthread_attr_init(&attr); + SkASSERT(0 == status); + + fBitmaps[i].setConfig(gConfigs[i], WIDTH, HEIGHT); + SkFlipPixelRef* pr = new SkFlipPixelRef(gConfigs[i], WIDTH, HEIGHT); + fBitmaps[i].setPixelRef(pr)->unref(); + fBitmaps[i].eraseColor(0); + + status = pthread_create(&fThreads[i], &attr, draw_proc, &fBitmaps[i]); + SkASSERT(0 == status); + } + this->setBGColor(0xFFDDDDDD); + } + + virtual ~PageFlipView() { + gDone = true; + for (int i = 0; i < N; i++) { + void* ret; + int status = pthread_join(fThreads[i], &ret); + SkASSERT(0 == status); + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "PageFlip"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(10); + for (int i = 0; i < N; i++) { + canvas->drawBitmap(fBitmaps[i], x, y); + x += SkIntToScalar(fBitmaps[i].width() + 20); + } + this->inval(NULL); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PageFlipView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp new file mode 100644 index 0000000000..ea365c719d --- /dev/null +++ b/samplecode/SamplePatch.cpp @@ -0,0 +1,342 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkOSFile.h" +#include "SkStream.h" + +#include "SkGeometry.h" // private include :( + +static void drawtriangle(SkCanvas* canvas, const SkPaint& paint, + const SkPoint pts[3]) { + SkPath path; + + path.moveTo(pts[0]); + path.lineTo(pts[1]); + path.lineTo(pts[2]); + + canvas->drawPath(path, paint); +} + +static SkShader* make_shader0(SkIPoint* size) { + SkBitmap bm; + +// SkImageDecoder::DecodeFile("/skimages/progressivejpg.jpg", &bm); + SkImageDecoder::DecodeFile("/skimages/logo.png", &bm); + size->set(bm.width(), bm.height()); + return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); +} + +static SkShader* make_shader1(const SkIPoint& size) { + SkPoint pts[] = { { 0, 0, }, + { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } }; + SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED }; + return SkGradientShader::CreateLinear(pts, colors, NULL, + SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// + +class Patch { +public: + Patch() { sk_bzero(fPts, sizeof(fPts)); } + ~Patch() {} + + void setPatch(const SkPoint pts[12]) { + memcpy(fPts, pts, 12 * sizeof(SkPoint)); + fPts[12] = pts[0]; // the last shall be first + } + void setBounds(int w, int h) { fW = w; fH = h; } + + void draw(SkCanvas*, const SkPaint&, int segsU, int segsV, + bool doTextures, bool doColors); + +private: + SkPoint fPts[13]; + int fW, fH; +}; + +static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) { + SkScalar t = 0; + SkScalar dt = SK_Scalar1 / segs; + + samples[0] = cubic[0]; + for (int i = 1; i < segs; i++) { + t += dt; + SkEvalCubicAt(cubic, t, &samples[i], NULL, NULL); + } +} + +static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv, + SkPoint* pt) { + const int TL = 0; + const int TR = nu; + const int BR = TR + nv; + const int BL = BR + nu; + + SkScalar u = SkIntToScalar(iu) / nu; + SkScalar v = SkIntToScalar(iv) / nv; + + SkScalar uv = SkScalarMul(u, v); + SkScalar Uv = SkScalarMul(SK_Scalar1 - u, v); + SkScalar uV = SkScalarMul(u, SK_Scalar1 - v); + SkScalar UV = SkScalarMul(SK_Scalar1 - u, SK_Scalar1 - v); + + SkScalar x0 = SkScalarMul(UV, edge[TL].fX) + SkScalarMul(uV, edge[TR].fX) + + SkScalarMul(Uv, edge[BL].fX) + SkScalarMul(uv, edge[BR].fX); + SkScalar y0 = SkScalarMul(UV, edge[TL].fY) + SkScalarMul(uV, edge[TR].fY) + + SkScalarMul(Uv, edge[BL].fY) + SkScalarMul(uv, edge[BR].fY); + + SkScalar x = SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fX) + + SkScalarMul(u, edge[TR+iv].fX) + + SkScalarMul(v, edge[BR+nu-iu].fX) + + SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fX) - x0; + SkScalar y = SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fY) + + SkScalarMul(u, edge[TR+iv].fY) + + SkScalarMul(v, edge[BR+nu-iu].fY) + + SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fY) - y0; + pt->set(x, y); +} + +static int ScalarTo255(SkScalar v) { + int scale = SkScalarToFixed(v) >> 8; + if (scale < 0) { + scale = 0; + } else if (scale > 255) { + scale = 255; + } + return scale; +} + +static SkColor make_color(SkScalar s, SkScalar t) { + int cs = ScalarTo255(s); + int ct = ScalarTo255(t); + return SkColorSetARGB(0xFF, cs, 0, 0) + SkColorSetARGB(0, 0, ct, 0); +} + +void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv, + bool doTextures, bool doColors) { + if (nu < 1 || nv < 1) { + return; + } + + int i, npts = (nu + nv) * 2; + SkAutoSTMalloc<16, SkPoint> storage(npts + 1); + SkPoint* edge0 = storage.get(); + SkPoint* edge1 = edge0 + nu; + SkPoint* edge2 = edge1 + nv; + SkPoint* edge3 = edge2 + nu; + + // evaluate the edge points + eval_patch_edge(fPts + 0, edge0, nu); + eval_patch_edge(fPts + 3, edge1, nv); + eval_patch_edge(fPts + 6, edge2, nu); + eval_patch_edge(fPts + 9, edge3, nv); + edge3[nv] = edge0[0]; // the last shall be first + + for (i = 0; i < npts; i++) { +// canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint); + } + + int row, vertCount = (nu + 1) * (nv + 1); + SkAutoTMalloc<SkPoint> vertStorage(vertCount); + SkPoint* verts = vertStorage.get(); + + // first row + memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint)); + // rows + SkPoint* r = verts; + for (row = 1; row < nv; row++) { + r += nu + 1; + r[0] = edge3[nv - row]; + for (int col = 1; col < nu; col++) { + eval_sheet(edge0, nu, nv, col, row, &r[col]); + } + r[nu] = edge1[row]; + } + // last row + SkPoint* last = verts + nv * (nu + 1); + for (i = 0; i <= nu; i++) { + last[i] = edge2[nu - i]; + } + +// canvas->drawPoints(verts, vertCount, paint); + + int stripCount = (nu + 1) * 2; + SkAutoTMalloc<SkPoint> stripStorage(stripCount * 2); + SkAutoTMalloc<SkColor> colorStorage(stripCount); + SkPoint* strip = stripStorage.get(); + SkPoint* tex = strip + stripCount; + SkColor* colors = colorStorage.get(); + SkScalar t = 0; + const SkScalar ds = SK_Scalar1 * fW / nu; + const SkScalar dt = SK_Scalar1 * fH / nv; + r = verts; + for (row = 0; row < nv; row++) { + SkPoint* upper = r; + SkPoint* lower = r + nu + 1; + r = lower; + SkScalar s = 0; + for (i = 0; i <= nu; i++) { + strip[i*2 + 0] = *upper++; + strip[i*2 + 1] = *lower++; + tex[i*2 + 0].set(s, t); + tex[i*2 + 1].set(s, t + dt); + colors[i*2 + 0] = make_color(s/fW, t/fH); + colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH); + s += ds; + } + t += dt; + canvas->drawVertices(SkCanvas::kTriangleStrip_VertexMode, stripCount, + strip, doTextures ? tex : NULL, + doColors ? colors : NULL, NULL, + NULL, 0, paint); + } +} + +static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv, + Patch* patch) { + + SkAutoCanvasRestore ar(canvas, true); + + patch->draw(canvas, paint, 10, 10, false, false); + canvas->translate(SkIntToScalar(180), 0); + patch->draw(canvas, paint, 10, 10, true, false); + canvas->translate(SkIntToScalar(180), 0); + patch->draw(canvas, paint, 10, 10, false, true); + canvas->translate(SkIntToScalar(180), 0); + patch->draw(canvas, paint, 10, 10, true, true); +} + +class PatchView : public SampleView { + SkShader* fShader0; + SkShader* fShader1; + SkIPoint fSize0, fSize1; + SkPoint fPts[12]; + +public: + PatchView() { + fShader0 = make_shader0(&fSize0); + fSize1 = fSize0; + if (fSize0.fX == 0 || fSize0.fY == 0) { + fSize1.set(2, 2); + } + fShader1 = make_shader1(fSize1); + + const SkScalar S = SkIntToScalar(50); + const SkScalar T = SkIntToScalar(40); + fPts[0].set(S*0, T); + fPts[1].set(S*1, T); + fPts[2].set(S*2, T); + fPts[3].set(S*3, T); + fPts[4].set(S*3, T*2); + fPts[5].set(S*3, T*3); + fPts[6].set(S*3, T*4); + fPts[7].set(S*2, T*4); + fPts[8].set(S*1, T*4); + fPts[9].set(S*0, T*4); + fPts[10].set(S*0, T*3); + fPts[11].set(S*0, T*2); + + this->setBGColor(SK_ColorGRAY); + } + + virtual ~PatchView() { + SkSafeUnref(fShader0); + SkSafeUnref(fShader1); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) + { + SkString str("Patch"); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setDither(true); + paint.setFilterBitmap(true); + + canvas->translate(SkIntToScalar(20), 0); + + Patch patch; + + paint.setShader(fShader0); + if (fSize0.fX == 0) { + fSize0.fX = 1; + } + if (fSize0.fY == 0) { + fSize0.fY = 1; + } + patch.setBounds(fSize0.fX, fSize0.fY); + + patch.setPatch(fPts); + drawpatches(canvas, paint, 10, 10, &patch); + + paint.setShader(NULL); + paint.setAntiAlias(true); + paint.setStrokeWidth(SkIntToScalar(5)); + canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint); + + canvas->translate(0, SkIntToScalar(300)); + + paint.setAntiAlias(false); + paint.setShader(fShader1); + patch.setBounds(fSize1.fX, fSize1.fY); + drawpatches(canvas, paint, 10, 10, &patch); + } + + class PtClick : public Click { + public: + int fIndex; + PtClick(SkView* view, int index) : Click(view), fIndex(index) {} + }; + + static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) { + return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) { + if (hittest(fPts[i], x, y)) { + return new PtClick(this, i); + } + } + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX, click->fCurr.fY); + this->inval(NULL); + return true; + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PatchView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp new file mode 100644 index 0000000000..cd45ed95e0 --- /dev/null +++ b/samplecode/SamplePath.cpp @@ -0,0 +1,200 @@ + +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkParsePath.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkGeometry.h" + +// http://code.google.com/p/skia/issues/detail?id=32 +static void test_cubic() { + SkPoint src[4] = { + { 556.25000f, 523.03003f }, + { 556.23999f, 522.96002f }, + { 556.21997f, 522.89001f }, + { 556.21997f, 522.82001f } + }; + SkPoint dst[11]; + dst[10].set(42, -42); // one past the end, that we don't clobber these + SkScalar tval[] = { 0.33333334f, 0.99999994f }; + + SkChopCubicAt(src, dst, tval, 2); + +#if 0 + for (int i = 0; i < 11; i++) { + SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY); + } +#endif +} + +static void test_cubic2() { + const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z"; + SkPath path; + SkParsePath::FromSVGString(str, &path); + + { +#ifdef SK_BUILD_FOR_WIN + // windows doesn't have strtof + float x = (float)strtod("9.94099e+07", NULL); +#else + float x = strtof("9.94099e+07", NULL); +#endif + int ix = (int)x; + int fx = (int)(x * 65536); + int ffx = SkScalarToFixed(x); + printf("%g %x %x %x\n", x, ix, fx, ffx); + + SkRect r = path.getBounds(); + SkIRect ir; + r.round(&ir); + printf("[%g %g %g %g] [%x %x %x %x]\n", + r.fLeft, r.fTop, r.fRight, r.fBottom, + ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); + } + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200); + bitmap.allocPixels(); + + SkCanvas canvas(bitmap); + SkPaint paint; + paint.setAntiAlias(true); + canvas.drawPath(path, paint); +} + +class PathView : public SampleView { +public: + int fDStroke, fStroke, fMinStroke, fMaxStroke; + SkPath fPath[6]; + bool fShowHairline; + + PathView() { + test_cubic(); + test_cubic2(); + + fShowHairline = false; + + fDStroke = 1; + fStroke = 10; + fMinStroke = 10; + fMaxStroke = 180; + + const int V = 85; + + fPath[0].moveTo(SkIntToScalar(40), SkIntToScalar(70)); + fPath[0].lineTo(SkIntToScalar(70), SkIntToScalar(70) + SK_Scalar1/1); + fPath[0].lineTo(SkIntToScalar(110), SkIntToScalar(70)); + + fPath[1].moveTo(SkIntToScalar(40), SkIntToScalar(70)); + fPath[1].lineTo(SkIntToScalar(70), SkIntToScalar(70) - SK_Scalar1/1); + fPath[1].lineTo(SkIntToScalar(110), SkIntToScalar(70)); + + fPath[2].moveTo(SkIntToScalar(V), SkIntToScalar(V)); + fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(V)); + fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(50)); + + fPath[3].moveTo(SkIntToScalar(50), SkIntToScalar(50)); + fPath[3].lineTo(SkIntToScalar(50), SkIntToScalar(V)); + fPath[3].lineTo(SkIntToScalar(V), SkIntToScalar(V)); + + fPath[4].moveTo(SkIntToScalar(50), SkIntToScalar(50)); + fPath[4].lineTo(SkIntToScalar(50), SkIntToScalar(V)); + fPath[4].lineTo(SkIntToScalar(52), SkIntToScalar(50)); + + fPath[5].moveTo(SkIntToScalar(52), SkIntToScalar(50)); + fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(V)); + fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(50)); + + this->setBGColor(0xFFDDDDDD); + } + + void nextStroke() { + fStroke += fDStroke; + if (fStroke > fMaxStroke || fStroke < fMinStroke) + fDStroke = -fDStroke; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Paths"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) { + SkPaint paint; + + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeJoin(j); + paint.setStrokeWidth(SkIntToScalar(fStroke)); + + if (fShowHairline) { + SkPath fill; + + paint.getFillPath(path, &fill); + paint.setStrokeWidth(0); + canvas->drawPath(fill, paint); + } else { + canvas->drawPath(path, paint); + } + + paint.setColor(SK_ColorRED); + paint.setStrokeWidth(0); + canvas->drawPath(path, paint); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(50), SkIntToScalar(50)); + + static const SkPaint::Join gJoins[] = { + SkPaint::kBevel_Join, + SkPaint::kMiter_Join, + SkPaint::kRound_Join + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) { + canvas->save(); + for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) { + this->drawPath(canvas, fPath[j], gJoins[i]); + canvas->translate(SkIntToScalar(200), 0); + } + canvas->restore(); + + canvas->translate(0, SkIntToScalar(200)); + } + + this->nextStroke(); + this->inval(NULL); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fShowHairline = !fShowHairline; + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PathView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp new file mode 100644 index 0000000000..81391717e2 --- /dev/null +++ b/samplecode/SamplePathClip.cpp @@ -0,0 +1,84 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +class PathClipView : public SampleView { +public: + SkRect fOval; + SkPoint fCenter; + + PathClipView() { + fOval.set(0, 0, SkIntToScalar(200), SkIntToScalar(50)); + fCenter.set(SkIntToScalar(250), SkIntToScalar(250)); + +// test_ats(); + } + + virtual ~PathClipView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "PathClip"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkRect oval = fOval; + oval.offset(fCenter.fX - oval.centerX(), fCenter.fY - oval.centerY()); + + SkPaint p; + p.setAntiAlias(true); + + p.setStyle(SkPaint::kStroke_Style); + canvas->drawOval(oval, p); + + SkRect r; + r.set(SkIntToScalar(200), SkIntToScalar(200), + SkIntToScalar(300), SkIntToScalar(300)); + canvas->clipRect(r); + + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorRED); + canvas->drawRect(r, p); + + p.setColor(0x800000FF); + r.set(SkIntToScalar(150), SkIntToScalar(10), + SkIntToScalar(250), SkIntToScalar(400)); + canvas->drawOval(oval, p); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + return new Click(this); + } + + virtual bool onClick(Click* click) { + fCenter.set(click->fCurr.fX, click->fCurr.fY); + this->inval(NULL); + return NULL; + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PathClipView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp new file mode 100644 index 0000000000..75566b0412 --- /dev/null +++ b/samplecode/SamplePathEffects.cpp @@ -0,0 +1,184 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkPixelXorXfermode.h" + +#define CORNER_RADIUS 12 +static SkScalar gPhase; + +static const int gXY[] = { + 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4 +}; + +static SkPathEffect* make_pe(int flags) { + if (flags == 1) + return new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS)); + + SkPath path; + path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1])); + for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) + path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1])); + path.close(); + path.offset(SkIntToScalar(-6), 0); + + SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), gPhase, SkPath1DPathEffect::kRotate_Style); + + if (flags == 2) + return outer; + + SkPathEffect* inner = new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS)); + + SkPathEffect* pe = new SkComposePathEffect(outer, inner); + outer->unref(); + inner->unref(); + return pe; +} + +static SkPathEffect* make_warp_pe() { + SkPath path; + path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1])); + for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) + path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1])); + path.close(); + path.offset(SkIntToScalar(-6), 0); + + SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), gPhase, SkPath1DPathEffect::kMorph_Style); + SkPathEffect* inner = new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS)); + + SkPathEffect* pe = new SkComposePathEffect(outer, inner); + outer->unref(); + inner->unref(); + return pe; +} + +/////////////////////////////////////////////////////////// + +#include "SkColorFilter.h" +#include "SkLayerRasterizer.h" + +class testrast : public SkLayerRasterizer { +public: + testrast() { + SkPaint paint; + paint.setAntiAlias(true); + +#if 0 + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SK_Scalar1*4); + this->addLayer(paint); + + paint.setStrokeWidth(SK_Scalar1*1); + paint.setXfermode(SkXfermode::kClear_Mode); + this->addLayer(paint); +#else + paint.setAlpha(0x66); + this->addLayer(paint, SkIntToScalar(4), SkIntToScalar(4)); + + paint.setAlpha(0xFF); + this->addLayer(paint); +#endif + } +}; + +class PathEffectView : public SampleView { + SkPath fPath; + SkPoint fClickPt; +public: + PathEffectView() { + SkRandom rand; + int steps = 20; + SkScalar dist = SkIntToScalar(400); + SkScalar x = SkIntToScalar(20); + SkScalar y = SkIntToScalar(50); + + fPath.moveTo(x, y); + for (int i = 0; i < steps; i++) { + x += dist/steps; + SkScalar tmpY = y + SkIntToScalar(rand.nextS() % 25); + if (i == steps/2) { + fPath.moveTo(x, tmpY); + } else { + fPath.lineTo(x, tmpY); + } + } + + { + SkRect oval; + oval.set(SkIntToScalar(20), SkIntToScalar(30), + SkIntToScalar(100), SkIntToScalar(60)); + oval.offset(x, 0); + fPath.addRoundRect(oval, SkIntToScalar(8), SkIntToScalar(8)); + } + + fClickPt.set(SkIntToScalar(200), SkIntToScalar(200)); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "PathEffects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + gPhase -= SampleCode::GetAnimSecondsDelta() * 40; + this->inval(NULL); + + SkPaint paint; + +#if 0 + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(5)); + canvas->drawPath(fPath, paint); + paint.setStrokeWidth(0); + + paint.setColor(SK_ColorWHITE); + paint.setPathEffect(make_pe(1))->unref(); + canvas->drawPath(fPath, paint); +#endif + + canvas->translate(0, SkIntToScalar(50)); + + paint.setColor(SK_ColorBLUE); + paint.setPathEffect(make_pe(2))->unref(); + canvas->drawPath(fPath, paint); + + canvas->translate(0, SkIntToScalar(50)); + + paint.setARGB(0xFF, 0, 0xBB, 0); + paint.setPathEffect(make_pe(3))->unref(); + canvas->drawPath(fPath, paint); + + canvas->translate(0, SkIntToScalar(50)); + + paint.setARGB(0xFF, 0, 0, 0); + paint.setPathEffect(make_warp_pe())->unref(); + paint.setRasterizer(new testrast)->unref(); + canvas->drawPath(fPath, paint); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PathEffectView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePathFill.cpp b/samplecode/SamplePathFill.cpp new file mode 100644 index 0000000000..845b7a88f9 --- /dev/null +++ b/samplecode/SamplePathFill.cpp @@ -0,0 +1,140 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkRandom.h" +#include "SkBlurDrawLooper.h" +#include "SkGradientShader.h" + +typedef SkScalar (*MakePathProc)(SkPath*); + +static SkScalar make_frame(SkPath* path) { + SkRect r = { 10, 10, 630, 470 }; + path->addRoundRect(r, 15, 15); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(5); + paint.getFillPath(*path, path); + return 15; +} + +static SkScalar make_triangle(SkPath* path) { + static const int gCoord[] = { + 10, 20, 15, 5, 30, 30 + }; + path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1])); + path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3])); + path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5])); + path->close(); + path->offset(10, 0); + return SkIntToScalar(30); +} + +static SkScalar make_rect(SkPath* path) { + SkRect r = { 10, 10, 30, 30 }; + path->addRect(r); + path->offset(10, 0); + return SkIntToScalar(30); +} + +static SkScalar make_oval(SkPath* path) { + SkRect r = { 10, 10, 30, 30 }; + path->addOval(r); + path->offset(10, 0); + return SkIntToScalar(30); +} + +static SkScalar make_sawtooth(SkPath* path) { + SkScalar x = SkIntToScalar(20); + SkScalar y = SkIntToScalar(20); + const SkScalar x0 = x; + const SkScalar dx = SK_Scalar1 * 5; + const SkScalar dy = SK_Scalar1 * 10; + + path->moveTo(x, y); + for (int i = 0; i < 32; i++) { + x += dx; + path->lineTo(x, y - dy); + x += dx; + path->lineTo(x, y + dy); + } + path->lineTo(x, y + 2 * dy); + path->lineTo(x0, y + 2 * dy); + path->close(); + return SkIntToScalar(30); +} + +static SkScalar make_star(SkPath* path, int n) { + const SkScalar c = SkIntToScalar(45); + const SkScalar r = SkIntToScalar(20); + + SkScalar rad = -SK_ScalarPI / 2; + const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n; + + path->moveTo(c, c - r); + for (int i = 1; i < n; i++) { + rad += drad; + SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV); + path->lineTo(c + SkScalarMul(cosV, r), c + SkScalarMul(sinV, r)); + } + path->close(); + return r * 2 * 6 / 5; +} + +static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); } +static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); } + +static const MakePathProc gProcs[] = { + make_frame, + make_triangle, + make_rect, + make_oval, + make_sawtooth, + make_star_5, + make_star_13 +}; + +#define N SK_ARRAY_COUNT(gProcs) + +class PathFillView : public SampleView { + SkPath fPath[N]; + SkScalar fDY[N]; + +public: + PathFillView() { + for (size_t i = 0; i < N; i++) { + fDY[i] = gProcs[i](&fPath[i]); + } + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "PathFill"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + + for (size_t i = 0; i < N; i++) { + canvas->drawPath(fPath[i], paint); + canvas->translate(0, fDY[i]); + } + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PathFillView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp new file mode 100644 index 0000000000..d7b6b229f4 --- /dev/null +++ b/samplecode/SamplePicture.cpp @@ -0,0 +1,254 @@ +#include "SampleCode.h" +#include "SkDumpCanvas.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkPicture.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkShape.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" + +class SignalShape : public SkShape { +public: + SignalShape() : fSignal(0) {} + + SkShape* setSignal(int n) { + fSignal = n; + return this; + } + +protected: + virtual void onDraw(SkCanvas* canvas) { + // SkDebugf("---- sc %d\n", canvas->getSaveCount() - 1); + } + +private: + int fSignal; +}; + +static SkPMColor SignalProc(SkPMColor src, SkPMColor dst) { + return dst; +} + +/* Picture playback will skip blocks of draw calls that follow a clip() call + that returns empty, and jump down to the corresponding restore() call. + + This is a great preformance win for drawing very large/tall pictures with + a small visible window (think scrolling a long document). These tests make + sure that (a) we are performing the culling, and (b) we don't get confused + by nested save() calls, nor by calls to restoreToCount(). + */ +static void test_saveRestoreCulling() { + SkPaint signalPaint; + SignalShape signalShape; + + SkPicture pic; + SkRect r = SkRect::MakeWH(0, 0); + int n; + SkCanvas* canvas = pic.beginRecording(100, 100); + int startN = canvas->getSaveCount(); + SkDebugf("---- start sc %d\n", startN); + canvas->drawShape(signalShape.setSignal(1)); + canvas->save(); + canvas->drawShape(signalShape.setSignal(2)); + n = canvas->save(); + canvas->drawShape(signalShape.setSignal(3)); + canvas->save(); + canvas->clipRect(r); + canvas->drawShape(signalShape.setSignal(4)); + canvas->restoreToCount(n); + canvas->drawShape(signalShape.setSignal(5)); + canvas->restore(); + canvas->drawShape(signalShape.setSignal(6)); + SkASSERT(canvas->getSaveCount() == startN); + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); + bm.allocPixels(); + SkCanvas c(bm); + c.drawPicture(pic); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkImageRef_GlobalPool.h" + +static SkBitmap load_bitmap() { + SkStream* stream = new SkFILEStream("/skimages/sesame_street_ensemble-hp.jpg"); + SkAutoUnref aur(stream); + + SkBitmap bm; + if (SkImageDecoder::DecodeStream(stream, &bm, SkBitmap::kNo_Config, + SkImageDecoder::kDecodeBounds_Mode)) { + SkPixelRef* pr = new SkImageRef_GlobalPool(stream, bm.config(), 1); + bm.setPixelRef(pr)->unref(); + } + return bm; +} + +static void drawCircle(SkCanvas* canvas, int r, SkColor color) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(color); + + canvas->drawCircle(SkIntToScalar(r), SkIntToScalar(r), SkIntToScalar(r), + paint); +} + +class PictureView : public SampleView { + SkBitmap fBitmap; +public: + PictureView() { + SkImageRef_GlobalPool::SetRAMBudget(16 * 1024); + + fBitmap = load_bitmap(); + + fPicture = new SkPicture; + SkCanvas* canvas = fPicture->beginRecording(100, 100); + SkPaint paint; + paint.setAntiAlias(true); + + canvas->drawBitmap(fBitmap, 0, 0, NULL); + + drawCircle(canvas, 50, SK_ColorBLACK); + fSubPicture = new SkPicture; + canvas->drawPicture(*fSubPicture); + canvas->translate(SkIntToScalar(50), 0); + canvas->drawPicture(*fSubPicture); + canvas->translate(0, SkIntToScalar(50)); + canvas->drawPicture(*fSubPicture); + canvas->translate(SkIntToScalar(-50), 0); + canvas->drawPicture(*fSubPicture); + // fPicture now has (4) references to us. We can release ours, and just + // unref fPicture in our destructor, and it will in turn take care of + // the other references to fSubPicture + fSubPicture->unref(); + + test_saveRestoreCulling(); + } + + virtual ~PictureView() { + fPicture->unref(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Picture"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawSomething(SkCanvas* canvas) { + SkPaint paint; + + canvas->save(); + canvas->scale(0.5f, 0.5f); + canvas->drawBitmap(fBitmap, 0, 0, NULL); + canvas->restore(); + + const char beforeStr[] = "before circle"; + const char afterStr[] = "after circle"; + + paint.setAntiAlias(true); + + paint.setColor(SK_ColorRED); + canvas->drawData(beforeStr, sizeof(beforeStr)); + canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50), + SkIntToScalar(40), paint); + canvas->drawData(afterStr, sizeof(afterStr)); + paint.setColor(SK_ColorBLACK); + paint.setTextSize(SkIntToScalar(40)); + canvas->drawText("Picture", 7, SkIntToScalar(50), SkIntToScalar(62), + paint); + + } + + virtual void onDrawContent(SkCanvas* canvas) { + drawSomething(canvas); + + SkPicture* pict = new SkPicture; + SkAutoUnref aur(pict); + + drawSomething(pict->beginRecording(100, 100)); + pict->endRecording(); + + canvas->save(); + canvas->translate(SkIntToScalar(300), SkIntToScalar(50)); + canvas->scale(-SK_Scalar1, -SK_Scalar1); + canvas->translate(-SkIntToScalar(100), -SkIntToScalar(50)); + canvas->drawPicture(*pict); + canvas->restore(); + + canvas->save(); + canvas->translate(SkIntToScalar(200), SkIntToScalar(150)); + canvas->scale(SK_Scalar1, -SK_Scalar1); + canvas->translate(0, -SkIntToScalar(50)); + canvas->drawPicture(*pict); + canvas->restore(); + + canvas->save(); + canvas->translate(SkIntToScalar(100), SkIntToScalar(100)); + canvas->scale(-SK_Scalar1, SK_Scalar1); + canvas->translate(-SkIntToScalar(100), 0); + canvas->drawPicture(*pict); + canvas->restore(); + + if (false) { + SkDebugfDumper dumper; + SkDumpCanvas dumpCanvas(&dumper); + dumpCanvas.drawPicture(*pict); + } + + // test that we can re-record a subpicture, and see the results + + SkRandom rand(SampleCode::GetAnimTime()); + canvas->translate(SkIntToScalar(10), SkIntToScalar(250)); + drawCircle(fSubPicture->beginRecording(50, 50), 25, + rand.nextU() | 0xFF000000); + canvas->drawPicture(*fPicture); + delayInval(500); + } + +private: + #define INVAL_ALL_TYPE "inval-all" + + void delayInval(SkMSec delay) { + (new SkEvent(INVAL_ALL_TYPE))->post(this->getSinkID(), delay); + } + + virtual bool onEvent(const SkEvent& evt) { + if (evt.isType(INVAL_ALL_TYPE)) { + this->inval(NULL); + return true; + } + return this->INHERITED::onEvent(evt); + } + + SkPicture* fPicture; + SkPicture* fSubPicture; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PictureView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePoints.cpp b/samplecode/SamplePoints.cpp new file mode 100644 index 0000000000..a2804b42b1 --- /dev/null +++ b/samplecode/SamplePoints.cpp @@ -0,0 +1,78 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" + +class PointsView : public SampleView { +public: + PointsView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Points"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) { + for (size_t i = 0; i < n; i++) + pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SK_Scalar1, SK_Scalar1); + + SkRandom rand; + SkPaint p0, p1, p2, p3; + const size_t n = 99; + + p0.setColor(SK_ColorRED); + p1.setColor(SK_ColorGREEN); + p2.setColor(SK_ColorBLUE); + p3.setColor(SK_ColorWHITE); + + p0.setStrokeWidth(SkIntToScalar(4)); + p2.setStrokeCap(SkPaint::kRound_Cap); + p2.setStrokeWidth(SkIntToScalar(6)); + + SkPoint* pts = new SkPoint[n]; + fill_pts(pts, n, &rand); + + canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts, p0); + canvas->drawPoints(SkCanvas::kLines_PointMode, n, pts, p1); + canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, p2); + canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, p3); + + delete[] pts; + } + +private: + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PointsView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SamplePolyToPoly.cpp b/samplecode/SamplePolyToPoly.cpp new file mode 100644 index 0000000000..aea0cb4457 --- /dev/null +++ b/samplecode/SamplePolyToPoly.cpp @@ -0,0 +1,161 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGraphics.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkTime.h" + +extern bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]); + +class PolyToPolyView : public SampleView { +public: + PolyToPolyView() { + // tests + { + SkPoint src[] = { { 0, 0 }, + { SK_Scalar1, 0 }, + { 0, SK_Scalar1 } }; + SkPoint dst[] = { { 0, 0 }, + { 2*SK_Scalar1, 0 }, + { 0, 2*SK_Scalar1 } }; + SkMatrix m1, m2; + bool success; + + success = m1.setPolyToPoly(src, dst, 3); + + m2.reset(); + m2.set(SkMatrix::kMScaleX, dst[1].fX - dst[0].fX); + m2.set(SkMatrix::kMSkewX, dst[2].fX - dst[0].fX); + m2.set(SkMatrix::kMTransX, dst[0].fX); + m2.set(SkMatrix::kMSkewY, dst[1].fY - dst[0].fY); + m2.set(SkMatrix::kMScaleY, dst[2].fY - dst[0].fY); + m2.set(SkMatrix::kMTransY, dst[0].fY); + + m1.reset(); + + const SkScalar src1[] = { + 0, 0, 0, SkFloatToScalar(427), SkFloatToScalar(316), SkFloatToScalar(427), SkFloatToScalar(316), 0 + }; + const SkScalar dst1[] = { + SkFloatToScalar(158), SkFloatToScalar(177.5f), SkFloatToScalar(158), SkFloatToScalar(249.5f), + SkFloatToScalar(158), SkFloatToScalar(604.5f), SkFloatToScalar(158), SkFloatToScalar(-177.5f) + }; + + success = m2.setPolyToPoly((const SkPoint*)src1, (SkPoint*)dst1, 4); + + { + const SkPoint src[] = { + { SkIntToScalar(1), SkIntToScalar(0) }, + { SkIntToScalar(4), SkIntToScalar(7) }, + { SkIntToScalar(10), SkIntToScalar(2) } + }; + const SkPoint dst[] = { + { SkIntToScalar(4), SkIntToScalar(2) }, + { SkIntToScalar(45), SkIntToScalar(26) }, + { SkIntToScalar(32), SkIntToScalar(17) } + }; + + SkMatrix m0, m1; + m0.setPolyToPoly(src, dst, 3); + // SkSetPoly3To3(&m1, src, dst); + // m0.dump(); + // m1.dump(); + } + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SkString str("PolyToPolyView"); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static void doDraw(SkCanvas* canvas, SkPaint* paint, const int isrc[], + const int idst[], int count) { + SkMatrix matrix; + SkPoint src[4], dst[4]; + + for (int i = 0; i < count; i++) { + src[i].set(SkIntToScalar(isrc[2*i+0]), SkIntToScalar(isrc[2*i+1])); + dst[i].set(SkIntToScalar(idst[2*i+0]), SkIntToScalar(idst[2*i+1])); + } + + canvas->save(); + matrix.setPolyToPoly(src, dst, count); + canvas->concat(matrix); + + paint->setColor(SK_ColorGRAY); + paint->setStyle(SkPaint::kStroke_Style); + const SkScalar D = SkIntToScalar(64); + canvas->drawRectCoords(0, 0, D, D, *paint); + canvas->drawLine(0, 0, D, D, *paint); + canvas->drawLine(0, D, D, 0, *paint); + + SkPaint::FontMetrics fm; + paint->getFontMetrics(&fm); + paint->setColor(SK_ColorRED); + paint->setStyle(SkPaint::kFill_Style); + SkScalar x = D/2; + float y = D/2 - (fm.fAscent + fm.fDescent)/2; + SkString str; + str.appendS32(count); + canvas->drawText(str.c_str(), str.size(), x, y, *paint); + + canvas->restore(); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStrokeWidth(SkIntToScalar(4)); + paint.setTextSize(SkIntToScalar(40)); + paint.setTextAlign(SkPaint::kCenter_Align); + + canvas->save(); + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + // translate (1 point) + const int src1[] = { 0, 0 }; + const int dst1[] = { 5, 5 }; + doDraw(canvas, &paint, src1, dst1, 1); + canvas->restore(); + + canvas->save(); + canvas->translate(SkIntToScalar(160), SkIntToScalar(10)); + // rotate/uniform-scale (2 points) + const int src2[] = { 32, 32, 64, 32 }; + const int dst2[] = { 32, 32, 64, 48 }; + doDraw(canvas, &paint, src2, dst2, 2); + canvas->restore(); + + canvas->save(); + canvas->translate(SkIntToScalar(10), SkIntToScalar(110)); + // rotate/skew (3 points) + const int src3[] = { 0, 0, 64, 0, 0, 64 }; + const int dst3[] = { 0, 0, 96, 0, 24, 64 }; + doDraw(canvas, &paint, src3, dst3, 3); + canvas->restore(); + + canvas->save(); + canvas->translate(SkIntToScalar(160), SkIntToScalar(110)); + // perspective (4 points) + const int src4[] = { 0, 0, 64, 0, 64, 64, 0, 64 }; + const int dst4[] = { 0, 0, 96, 0, 64, 96, 0, 64 }; + doDraw(canvas, &paint, src4, dst4, 4); + canvas->restore(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PolyToPolyView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp new file mode 100644 index 0000000000..822bd6fa47 --- /dev/null +++ b/samplecode/SampleRegion.cpp @@ -0,0 +1,273 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkImageDecoder.h" + +#ifdef SK_BUILD_FOR_WIN +// windows doesn't have roundf +inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); } +#endif + +#ifdef SK_DEBUG +static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom, + size_t count, int32_t runs[]) { + SkIRect r; + r.set(left, top, right, bottom); + + rgn->debugSetRuns(runs, count); + SkASSERT(rgn->getBounds() == r); +} + +static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) { + static int32_t dataA[] = { + 0x00000001, 0x000001dd, + 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, + 0x7fffffff, 0x000001de, 0x00000001, 0x00000025, + 0x7fffffff, 0x000004b3, 0x00000001, 0x00000026, + 0x7fffffff, 0x000004b4, 0x0000000c, 0x00000026, + 0x7fffffff, 0x00000579, 0x00000000, 0x0000013a, + 0x7fffffff, 0x000005d8, 0x00000000, 0x0000013b, + 0x7fffffff, 0x7fffffff + }; + make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA); + + static int32_t dataB[] = { + 0x000000b6, 0x000000c4, + 0x000000a1, 0x000000f0, 0x7fffffff, 0x000000d6, + 0x7fffffff, 0x000000e4, 0x00000070, 0x00000079, + 0x000000a1, 0x000000b0, 0x7fffffff, 0x000000e6, + 0x7fffffff, 0x000000f4, 0x00000070, 0x00000079, + 0x000000a1, 0x000000b0, 0x7fffffff, 0x000000f6, + 0x7fffffff, 0x00000104, 0x000000a1, 0x000000b0, + 0x7fffffff, 0x7fffffff + }; + make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB); + + rc->op(*ra, *rb, SkRegion::kUnion_Op); +} +#endif + +static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) { + dst->fLeft = (int)::roundf(src.fLeft * scale); + dst->fTop = (int)::roundf(src.fTop * scale); + dst->fRight = (int)::roundf(src.fRight * scale); + dst->fBottom = (int)::roundf(src.fBottom * scale); +} + +static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) { + SkRegion tmp; + SkRegion::Iterator iter(src); + + for (; !iter.done(); iter.next()) { + SkIRect r; + scale_rect(&r, iter.rect(), scale); + tmp.op(r, SkRegion::kUnion_Op); + } + dst->swap(tmp); +} + +static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn, + const SkPaint& paint) { + SkRegion scaled; + scale_rgn(&scaled, rgn, 0.5f); + + SkRegion::Iterator iter(rgn); + + for (; !iter.done(); iter.next()) + { + SkRect r; + r.set(iter.rect()); + canvas->drawRect(r, paint); + } +} + +class RegionView : public SampleView { +public: + RegionView() { + fBase.set(100, 100, 150, 150); + fRect = fBase; + fRect.inset(5, 5); + fRect.offset(25, 25); + this->setBGColor(0xFFDDDDDD); + } + + void build_rgn(SkRegion* rgn, SkRegion::Op op) { + rgn->setRect(fBase); + SkIRect r = fBase; + r.offset(75, 20); + rgn->op(r, SkRegion::kUnion_Op); + rgn->op(fRect, op); + } + + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Regions"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawOrig(SkCanvas* canvas, bool bg) { + SkRect r; + SkPaint paint; + + paint.setStyle(SkPaint::kStroke_Style); + if (bg) + paint.setColor(0xFFBBBBBB); + + r.set(fBase); + canvas->drawRect(r, paint); + r.set(fRect); + canvas->drawRect(r, paint); + } + + void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { + SkRegion rgn; + + this->build_rgn(&rgn, op); + + { + SkRegion tmp, tmp2(rgn); + + tmp = tmp2; + tmp.translate(5, -3); + + { + char buffer[1000]; + size_t size = tmp.flatten(NULL); + SkASSERT(size <= sizeof(buffer)); + size_t size2 = tmp.flatten(buffer); + SkASSERT(size == size2); + + SkRegion tmp3; + size2 = tmp3.unflatten(buffer); + SkASSERT(size == size2); + + SkASSERT(tmp3 == tmp); + } + + rgn.translate(20, 30, &tmp); + SkASSERT(rgn.isEmpty() || tmp != rgn); + tmp.translate(-20, -30); + SkASSERT(tmp == rgn); + } + + this->drawOrig(canvas, true); + + SkPaint paint; + paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24)); + paint_rgn(canvas, rgn, paint); + + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(color); + paint_rgn(canvas, rgn, paint); + } + + void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { + SkRegion rgn; + SkPath path; + + this->build_rgn(&rgn, op); + rgn.getBoundaryPath(&path); + + this->drawOrig(canvas, true); + + SkPaint paint; + + paint.setStyle(SkPaint::kFill_Style); + paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24)); + canvas->drawPath(path, paint); + paint.setColor(color); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(path, paint); + } + + virtual void onDrawContent(SkCanvas* canvas) { +#ifdef SK_DEBUG + if (true) { + SkRegion a, b, c; + test_union_bug_1505668(&a, &b, &c); + + if (false) { // draw the result of the test + SkPaint paint; + + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + paint.setColor(SK_ColorRED); + paint_rgn(canvas, a, paint); + paint.setColor(0x800000FF); + paint_rgn(canvas, b, paint); + paint.setColor(SK_ColorBLACK); + paint.setStyle(SkPaint::kStroke_Style); + // paint_rgn(canvas, c, paint); + return; + } + } +#endif + + static const struct { + SkColor fColor; + const char* fName; + SkRegion::Op fOp; + } gOps[] = { + { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op }, + { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op }, + { 0xFF008800, "Union", SkRegion::kUnion_Op }, + { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op } + }; + + SkPaint textPaint; + textPaint.setAntiAlias(true); + textPaint.setTextSize(SK_Scalar1*24); + + this->drawOrig(canvas, false); + canvas->save(); + canvas->translate(SkIntToScalar(200), 0); + this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK); + canvas->restore(); + + canvas->translate(0, SkIntToScalar(200)); + + for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) { + canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint); + + this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor); + + canvas->save(); + canvas->translate(0, SkIntToScalar(200)); + this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor); + canvas->restore(); + + canvas->translate(SkIntToScalar(200), 0); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL; + } + + virtual bool onClick(Click* click) { + fRect.offset(click->fICurr.fX - click->fIPrev.fX, + click->fICurr.fY - click->fIPrev.fY); + this->inval(NULL); + return true; + } + +private: + SkIRect fBase, fRect; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new RegionView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleRepeatTile.cpp b/samplecode/SampleRepeatTile.cpp new file mode 100644 index 0000000000..9867074a1a --- /dev/null +++ b/samplecode/SampleRepeatTile.cpp @@ -0,0 +1,86 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkShader.h" +#include "SkKey.h" + +static void make_bitmap(SkBitmap* bm) { + const int W = 100; + const int H = 100; + bm->setConfig(SkBitmap::kARGB_8888_Config, W, H); + bm->allocPixels(); + + SkPaint paint; + SkCanvas canvas(*bm); + canvas.drawColor(SK_ColorWHITE); + + const SkColor colors[] = { + SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE + }; + + for (int ix = 0; ix < W; ix += 1) { + SkScalar x = SkIntToScalar(ix) + SK_ScalarHalf; + paint.setColor(colors[ix & 3]); + canvas.drawLine(x, 0, x, SkIntToScalar(H - 1), paint); + } + paint.setColor(SK_ColorGRAY); + canvas.drawLine(0, 0, SkIntToScalar(W), 0, paint); +} + +static void make_paint(SkPaint* paint, SkShader::TileMode tm) { + SkBitmap bm; + make_bitmap(&bm); + + SkShader* shader = SkShader::CreateBitmapShader(bm, tm, tm); + paint->setShader(shader)->unref(); +} + +class RepeatTileView : public SampleView { +public: + RepeatTileView() { + this->setBGColor(SK_ColorGRAY); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "RepeatTile"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + make_paint(&paint, SkShader::kRepeat_TileMode); + +// canvas->scale(SK_Scalar1*2, SK_Scalar1); + canvas->translate(SkIntToScalar(100), SkIntToScalar(100)); + canvas->drawPaint(paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + + virtual bool handleKey(SkKey key) { + this->inval(NULL); + return true; + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new RepeatTileView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleShaderText.cpp b/samplecode/SampleShaderText.cpp new file mode 100644 index 0000000000..2748b559a2 --- /dev/null +++ b/samplecode/SampleShaderText.cpp @@ -0,0 +1,195 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkUnitMappers.h" + +static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) { + bm->setConfig(config, w, h); + bm->allocPixels(); + bm->eraseColor(0); + + SkCanvas canvas(*bm); + SkScalar s = SkIntToScalar(w < h ? w : h); + SkPoint pts[] = { { 0, 0 }, { s, s } }; + SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; + SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 }; + SkPaint paint; + + SkUnitMapper* um = NULL; + + um = new SkCosineMapper; + + SkAutoUnref au(um); + + paint.setDither(true); + paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos, + SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref(); + canvas.drawPaint(paint); +} + +SkShader* MakeBitmapShader(SkShader::TileMode tx, SkShader::TileMode ty, + int w, int h) { + static SkBitmap bmp; + if (bmp.isNull()) { + makebm(&bmp, SkBitmap::kARGB_8888_Config, w/2, h/4); + } + return SkShader::CreateBitmapShader(bmp, tx, ty); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct GradData { + int fCount; + const SkColor* fColors; + const SkScalar* fPos; +}; + +static const SkColor gColors[] = { + SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK +}; + +static const GradData gGradData[] = { + { 2, gColors, NULL }, + { 5, gColors, NULL }, +}; + +static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, + data.fCount, tm, mapper); +} + +static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateRadial(center, center.fX, data.fColors, + data.fPos, data.fCount, tm, mapper); +} + +static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, + data.fPos, data.fCount, mapper); +} + +static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center0, center1; + center0.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), + SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); + return SkGradientShader::CreateTwoPointRadial( + center1, (pts[1].fX - pts[0].fX) / 7, + center0, (pts[1].fX - pts[0].fX) / 2, + data.fColors, data.fPos, data.fCount, tm, mapper); +} + +typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper); +static const GradMaker gGradMakers[] = { + MakeLinear, MakeRadial, MakeSweep, Make2Radial +}; + +/////////////////////////////////////////////////////////////////////////////// + +class ShaderTextView : public SampleView { +public: + ShaderTextView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Shader Text"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + const char text[] = "Shaded Text"; + const int textLen = SK_ARRAY_COUNT(text) - 1; + static int pointSize = 48; + + int w = pointSize * textLen; + int h = pointSize; + + SkPoint pts[2] = { + { 0, 0 }, + { SkIntToScalar(w), SkIntToScalar(h) } + }; + SkScalar textBase = SkIntToScalar(h/2); + + SkShader::TileMode tileModes[] = { + SkShader::kClamp_TileMode, + SkShader::kRepeat_TileMode, + SkShader::kMirror_TileMode + }; + + static const int gradCount = SK_ARRAY_COUNT(gGradData) * + SK_ARRAY_COUNT(gGradMakers); + static const int bmpCount = SK_ARRAY_COUNT(tileModes) * + SK_ARRAY_COUNT(tileModes); + SkShader* shaders[gradCount + bmpCount]; + + int shdIdx = 0; + for (size_t d = 0; d < SK_ARRAY_COUNT(gGradData); ++d) { + for (size_t m = 0; m < SK_ARRAY_COUNT(gGradMakers); ++m) { + shaders[shdIdx++] = gGradMakers[m](pts, + gGradData[d], + SkShader::kClamp_TileMode, + NULL); + } + } + for (size_t tx = 0; tx < SK_ARRAY_COUNT(tileModes); ++tx) { + for (size_t ty = 0; ty < SK_ARRAY_COUNT(tileModes); ++ty) { + shaders[shdIdx++] = MakeBitmapShader(tileModes[tx], + tileModes[ty], + w/8, h); + } + } + + SkPaint paint; + paint.setDither(true); + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(pointSize)); + + canvas->save(); + canvas->translate(SkIntToScalar(20), SkIntToScalar(10)); + + static const int testsPerCol = 8; + static const int rowHeight = 60; + static const int colWidth = 300; + canvas->save(); + for (size_t s = 0; s < SK_ARRAY_COUNT(shaders); s++) { + canvas->save(); + canvas->translate(SkIntToScalar((s / testsPerCol) * colWidth), + SkIntToScalar((s % testsPerCol) * rowHeight)); + paint.setShader(shaders[s])->unref(); + canvas->drawText(text, textLen, 0, textBase, paint); + canvas->restore(); + } + canvas->restore(); + + canvas->translate(0, SkIntToScalar(370)); + this->inval(NULL); + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ShaderTextView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleShaders.cpp b/samplecode/SampleShaders.cpp new file mode 100644 index 0000000000..c1bb0fda21 --- /dev/null +++ b/samplecode/SampleShaders.cpp @@ -0,0 +1,134 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkComposeShader.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTransparentShader.h" +#include "SkTypeface.h" + +static SkShader* make_bitmapfade(const SkBitmap& bm) +{ + SkPoint pts[2]; + SkColor colors[2]; + + pts[0].set(0, 0); + pts[1].set(0, SkIntToScalar(bm.height())); + colors[0] = SK_ColorBLACK; + colors[1] = SkColorSetARGB(0, 0, 0, 0); + SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode); + + SkShader* shaderB = SkShader::CreateBitmapShader(bm, + SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); + + SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode); + + SkShader* shader = new SkComposeShader(shaderB, shaderA, mode); + shaderA->unref(); + shaderB->unref(); + mode->unref(); + + return shader; +} + +class ShaderView : public SampleView { +public: + SkShader* fShader; + SkBitmap fBitmap; + + ShaderView() { + SkImageDecoder::DecodeFile("/skimages/logo.gif", &fBitmap); + + SkPoint pts[2]; + SkColor colors[2]; + + pts[0].set(0, 0); + pts[1].set(SkIntToScalar(100), 0); + colors[0] = SK_ColorRED; + colors[1] = SK_ColorBLUE; + SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode); + + pts[0].set(0, 0); + pts[1].set(0, SkIntToScalar(100)); + colors[0] = SK_ColorBLACK; + colors[1] = SkColorSetARGB(0x80, 0, 0, 0); + SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode); + + SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode); + + fShader = new SkComposeShader(shaderA, shaderB, mode); + shaderA->unref(); + shaderB->unref(); + mode->unref(); + } + virtual ~ShaderView() { + SkSafeUnref(fShader); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Shaders"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->drawBitmap(fBitmap, 0, 0); + + canvas->translate(SkIntToScalar(20), SkIntToScalar(120)); + + SkPaint paint; + SkRect r; + + paint.setColor(SK_ColorGREEN); + canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint); + paint.setShader(fShader); + canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint); + + canvas->translate(SkIntToScalar(110), 0); + + int w = fBitmap.width(); + int h = fBitmap.height(); + w = 120; + h = 80; + r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h)); + + paint.setShader(NULL); + canvas->drawRect(r, paint); + paint.setShader(make_bitmapfade(fBitmap))->unref(); + canvas->drawRect(r, paint); + + paint.setShader(new SkTransparentShader)->unref(); + canvas->drawRect(r, paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ShaderView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleShapes.cpp b/samplecode/SampleShapes.cpp new file mode 100644 index 0000000000..dc10f1a454 --- /dev/null +++ b/samplecode/SampleShapes.cpp @@ -0,0 +1,160 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkPicture.h" +#include "SkStream.h" +#include "SkView.h" + +#define DO_AA true + +#include "SkRectShape.h" +#include "SkGroupShape.h" + +static SkRect make_rect(int l, int t, int r, int b) { + SkRect rect; + rect.set(SkIntToScalar(l), SkIntToScalar(t), + SkIntToScalar(r), SkIntToScalar(b)); + return rect; +} + +static SkShape* make_shape0(bool red) { + SkRectShape* s = new SkRectShape; + s->setRect(make_rect(10, 10, 90, 90)); + if (red) { + s->paint().setColor(SK_ColorRED); + } + s->paint().setAntiAlias(DO_AA); + return s; +} + +static SkShape* make_shape1() { + SkRectShape* s = new SkRectShape; + s->setOval(make_rect(10, 10, 90, 90)); + s->paint().setColor(SK_ColorBLUE); + s->paint().setAntiAlias(DO_AA); + return s; +} + +static SkShape* make_shape2() { + SkRectShape* s = new SkRectShape; + s->setRRect(make_rect(10, 10, 90, 90), + SkIntToScalar(20), SkIntToScalar(20)); + s->paint().setColor(SK_ColorGREEN); + s->paint().setAntiAlias(DO_AA); + return s; +} + +/////////////////////////////////////////////////////////////////////////////// + +class ShapesView : public SampleView { + SkGroupShape fGroup; + SkMatrixRef* fMatrixRefs[4]; +public: + ShapesView() { + SkMatrix m; + fGroup.appendShape(make_shape0(false))->unref(); + m.setRotate(SkIntToScalar(30), SkIntToScalar(50), SkIntToScalar(50)); + m.postTranslate(0, SkIntToScalar(120)); + fGroup.appendShape(make_shape0(true), m)->unref(); + + m.setTranslate(SkIntToScalar(120), 0); + fGroup.appendShape(make_shape1(), m)->unref(); + m.postTranslate(0, SkIntToScalar(120)); + fGroup.appendShape(make_shape2(), m)->unref(); + + for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) { + SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i)); + } + + this->setBGColor(0xFFDDDDDD); + } + + virtual ~ShapesView() { + for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) { + SkSafeUnref(fMatrixRefs[i]); + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Shapes"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawpicture(SkCanvas* canvas, SkPicture& pict) { +#if 0 + SkDynamicMemoryWStream ostream; + pict.serialize(&ostream); + + SkMemoryStream istream(ostream.getStream(), ostream.getOffset()); + SkPicture* newPict = new SkPicture(&istream); + canvas->drawPicture(*newPict); + newPict->unref(); +#else + canvas->drawPicture(pict); +#endif + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkScalar angle = SampleCode::GetAnimScalar(SkIntToScalar(180), + SkIntToScalar(360)); + + SkMatrix saveM = *fMatrixRefs[3]; + SkScalar c = SkIntToScalar(50); + fMatrixRefs[3]->preRotate(angle, c, c); + + const SkScalar dx = 350; + const SkScalar dy = 500; + const int N = 1; + for (int v = -N; v <= N; v++) { + for (int h = -N; h <= N; h++) { + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(h * dx, v * dy); + + SkMatrix matrix; + + SkGroupShape* gs = new SkGroupShape; + SkAutoUnref aur(gs); + gs->appendShape(&fGroup); + matrix.setScale(-SK_Scalar1, SK_Scalar1); + matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240)); + gs->appendShape(&fGroup, matrix); + matrix.setTranslate(SkIntToScalar(240), 0); + matrix.preScale(SK_Scalar1*2, SK_Scalar1*2); + gs->appendShape(&fGroup, matrix); + +#if 0 + canvas->drawShape(gs); +#else + SkPicture* pict = new SkPicture; + SkCanvas* cv = pict->beginRecording(1000, 1000); + cv->scale(SK_ScalarHalf, SK_ScalarHalf); + cv->drawShape(gs); + cv->translate(SkIntToScalar(680), SkIntToScalar(480)); + cv->scale(-SK_Scalar1, SK_Scalar1); + cv->drawShape(gs); + pict->endRecording(); + + drawpicture(canvas, *pict); + pict->unref(); +#endif + + }} + + *fMatrixRefs[3] = saveM; + this->inval(NULL); +} + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new ShapesView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleSkLayer.cpp b/samplecode/SampleSkLayer.cpp new file mode 100644 index 0000000000..11976e9da6 --- /dev/null +++ b/samplecode/SampleSkLayer.cpp @@ -0,0 +1,239 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkView.h" +#include "SkLayer.h" + +#include "SkMatrix44.h" +static void test_inv(const char label[], const SkMatrix44& mat) { + SkDebugf("%s\n", label); + mat.dump(); + + SkMatrix44 inv; + if (mat.invert(&inv)) { + inv.dump(); + } else { + SkDebugf("--- invert failed\n"); + } + + SkMatrix44 a, b; + a.setConcat(mat, inv); + b.setConcat(inv, mat); + SkDebugf("concat mat with inverse pre=%d post=%d\n", a.isIdentity(), b.isIdentity()); + if (!a.isIdentity()) { + a.dump(); + } + if (!b.isIdentity()) { + b.dump(); + } + SkDebugf("\n"); +} + +static void test_map(SkScalar x0, SkScalar y0, SkScalar z0, + const SkMatrix44& mat, + SkScalar x1, SkScalar y1, SkScalar z1) { + SkVector4 src, dst; + src.set(x0, y0, z0); + dst = mat * src; + SkDebugf("map: src: %g %g %g dst: %g %g %g (%g) expected: %g %g %g match: %d\n", + x0, y0, z0, + dst.fData[0], dst.fData[1], dst.fData[2], dst.fData[3], + x1, y1, z1, + dst.fData[0] == x1 && dst.fData[1] == y1 && dst.fData[2] == z1); +} + +static void test_33(const SkMatrix44& mat, + SkScalar x0, SkScalar x1, SkScalar x2, + SkScalar y0, SkScalar y1, SkScalar y2) { + SkMatrix dst = mat; + if (dst[0] != x0 || dst[1] != x1 || dst[2] != x2 || + dst[3] != y0 || dst[4] != y1 || dst[5] != y2) { + SkString str; + dst.toDumpString(&str); + SkDebugf("3x3: expected 3x3 [%g %g %g] [%g %g %g] bug got %s\n", + x0, x1, x2, y0, y1, y2, str.c_str()); + } +} + +static void test44() { + SkMatrix44 m0, m1, m2; + + test_inv("identity", m0); + m0.setTranslate(2,3,4); + test_inv("translate", m0); + m0.setScale(2,3,4); + test_inv("scale", m0); + m0.postTranslate(5, 6, 7); + test_inv("postTranslate", m0); + m0.setScale(2,3,4); + m1.setTranslate(5, 6, 7); + m0.setConcat(m0, m1); + test_inv("postTranslate2", m0); + m0.setScale(2,3,4); + m0.preTranslate(5, 6, 7); + test_inv("preTranslate", m0); + + m0.setScale(2, 4, 6); + m0.postScale(SkDoubleToMScalar(0.5)); + test_inv("scale/postscale to 1,2,3", m0); + + m0.reset(); + test_map(1, 0, 0, m0, 1, 0, 0); + test_map(0, 1, 0, m0, 0, 1, 0); + test_map(0, 0, 1, m0, 0, 0, 1); + m0.setScale(2, 3, 4); + test_map(1, 0, 0, m0, 2, 0, 0); + test_map(0, 1, 0, m0, 0, 3, 0); + test_map(0, 0, 1, m0, 0, 0, 4); + m0.setTranslate(2, 3, 4); + test_map(0, 0, 0, m0, 2, 3, 4); + m0.preScale(5, 6, 7); + test_map(1, 0, 0, m0, 7, 3, 4); + test_map(0, 1, 0, m0, 2, 9, 4); + test_map(0, 0, 1, m0, 2, 3, 11); + + SkMScalar deg = 45; + m0.setRotateDegreesAbout(0, 0, 1, deg); + test_map(1, 0, 0, m0, 0.707106769, -0.707106769, 0); + + m0.reset(); + test_33(m0, 1, 0, 0, 0, 1, 0); + m0.setTranslate(3, 4, 5); + test_33(m0, 1, 0, 3, 0, 1, 4); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void dump_layers(const SkLayer* layer, int tab = 0) { + SkMatrix matrix; + SkString matrixStr; + + layer->getLocalTransform(&matrix); + matrix.toDumpString(&matrixStr); + + for (int j = 0; j < tab; j++) { + SkDebugf(" "); + } + SkDebugf("layer=%p parent=%p size=[%g %g] transform=%s\n", + layer, layer->getParent(), layer->getWidth(), layer->getHeight(), + matrixStr.c_str()); + for (int i = 0; i < layer->countChildren(); i++) { + dump_layers(layer->getChild(i), tab + 4); + } +} + +class TestLayer : public SkLayer { +public: + TestLayer(SkColor c) : fColor(c) {} + +protected: + virtual void onDraw(SkCanvas* canvas, SkScalar opacity) { + SkRect r; + r.set(0, 0, this->getWidth(), this->getHeight()); + + SkPaint paint; + paint.setColor(fColor); + paint.setAlpha(SkScalarRound(opacity * 255)); + + canvas->drawRect(r, paint); + } + +private: + SkColor fColor; +}; + +class SkLayerView : public SkView { +private: + SkLayer* fRootLayer; + SkLayer* fLastChild; +public: + SkLayerView() { + test44(); + static const int W = 600; + static const int H = 440; + static const struct { + int fWidth; + int fHeight; + SkColor fColor; + int fPosX; + int fPosY; + } gData[] = { + { 120, 80, SK_ColorRED, 0, 0 }, + { 120, 80, SK_ColorGREEN, W - 120, 0 }, + { 120, 80, SK_ColorBLUE, 0, H - 80 }, + { 120, 80, SK_ColorMAGENTA, W - 120, H - 80 }, + }; + + fRootLayer = new TestLayer(0xFFDDDDDD); + fRootLayer->setSize(W, H); + for (size_t i = 0; i < SK_ARRAY_COUNT(gData); i++) { + SkLayer* child = new TestLayer(gData[i].fColor); + child->setSize(gData[i].fWidth, gData[i].fHeight); + child->setPosition(gData[i].fPosX, gData[i].fPosY); + fRootLayer->addChild(child)->unref(); + } + + SkLayer* child = new TestLayer(0xFFDD8844); + child->setSize(120, 80); + child->setPosition(fRootLayer->getWidth()/2 - child->getWidth()/2, + fRootLayer->getHeight()/2 - child->getHeight()/2); + child->setAnchorPoint(SK_ScalarHalf, SK_ScalarHalf); + { + SkMatrix m; + m.setRotate(SkIntToScalar(30)); + child->setMatrix(m); + } + fLastChild = child; + fRootLayer->addChild(child)->unref(); + + if (false) { + SkMatrix matrix; + matrix.setScale(0.5, 0.5); + fRootLayer->setMatrix(matrix); + } + +// dump_layers(fRootLayer); + } + + virtual ~SkLayerView() { + SkSafeUnref(fRootLayer); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "SkLayer"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDraw(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + + canvas->translate(20, 20); + fRootLayer->draw(canvas); + + // visual test of getLocalTransform + if (true) { + SkMatrix matrix; + fLastChild->localToGlobal(&matrix); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(5); + paint.setColor(0x88FF0000); + canvas->concat(matrix); + canvas->drawRect(SkRect::MakeSize(fLastChild->getSize()), paint); + } + } + +private: + typedef SkView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new SkLayerView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp new file mode 100644 index 0000000000..3b7d05b0d2 --- /dev/null +++ b/samplecode/SampleSlides.cpp @@ -0,0 +1,804 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" + +#define BG_COLOR 0xFFDDDDDD + +typedef void (*SlideProc)(SkCanvas*); + +/////////////////////////////////////////////////////////////////////////////// + +#include "Sk1DPathEffect.h" +#include "Sk2DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkDashPathEffect.h" +#include "SkDiscretePathEffect.h" + +static void compose_pe(SkPaint* paint) { + SkPathEffect* pe = paint->getPathEffect(); + SkPathEffect* corner = new SkCornerPathEffect(25); + SkPathEffect* compose; + if (pe) { + compose = new SkComposePathEffect(pe, corner); + corner->unref(); + } else { + compose = corner; + } + paint->setPathEffect(compose)->unref(); +} + +static void hair_pe(SkPaint* paint) { + paint->setStrokeWidth(0); +} + +static void hair2_pe(SkPaint* paint) { + paint->setStrokeWidth(0); + compose_pe(paint); +} + +static void stroke_pe(SkPaint* paint) { + paint->setStrokeWidth(12); + compose_pe(paint); +} + +static void dash_pe(SkPaint* paint) { + SkScalar inter[] = { 20, 10, 10, 10 }; + paint->setStrokeWidth(12); + paint->setPathEffect(new SkDashPathEffect(inter, SK_ARRAY_COUNT(inter), + 0))->unref(); + compose_pe(paint); +} + +static const int gXY[] = { +4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4 +}; + +static void scale(SkPath* path, SkScalar scale) { + SkMatrix m; + m.setScale(scale, scale); + path->transform(m); +} + +static void one_d_pe(SkPaint* paint) { + SkPath path; + path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1])); + for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) + path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1])); + path.close(); + path.offset(SkIntToScalar(-6), 0); + scale(&path, 1.5); + + paint->setPathEffect(new SkPath1DPathEffect(path, SkIntToScalar(21), 0, + SkPath1DPathEffect::kRotate_Style))->unref(); + compose_pe(paint); +} + +typedef void (*PE_Proc)(SkPaint*); +static const PE_Proc gPE[] = { hair_pe, hair2_pe, stroke_pe, dash_pe, one_d_pe }; + +static void fill_pe(SkPaint* paint) { + paint->setStyle(SkPaint::kFill_Style); + paint->setPathEffect(NULL); +} + +static void discrete_pe(SkPaint* paint) { + paint->setPathEffect(new SkDiscretePathEffect(10, 4))->unref(); +} + +class TilePathEffect : public Sk2DPathEffect { + static SkMatrix make_mat() { + SkMatrix m; + m.setScale(12, 12); + return m; + } +public: + TilePathEffect() : Sk2DPathEffect(make_mat()) {} + +protected: + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) { + dst->addCircle(loc.fX, loc.fY, 5); + } +}; + +static void tile_pe(SkPaint* paint) { + paint->setPathEffect(new TilePathEffect)->unref(); +} + +static const PE_Proc gPE2[] = { fill_pe, discrete_pe, tile_pe }; + +static void patheffect_slide(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + + SkPath path; + path.moveTo(20, 20); + path.lineTo(70, 120); + path.lineTo(120, 30); + path.lineTo(170, 80); + path.lineTo(240, 50); + + size_t i; + canvas->save(); + for (i = 0; i < SK_ARRAY_COUNT(gPE); i++) { + gPE[i](&paint); + canvas->drawPath(path, paint); + canvas->translate(0, 75); + } + canvas->restore(); + + path.reset(); + SkRect r = { 0, 0, 250, 120 }; + path.addOval(r, SkPath::kCW_Direction); + r.inset(50, 50); + path.addRect(r, SkPath::kCCW_Direction); + + canvas->translate(320, 20); + for (i = 0; i < SK_ARRAY_COUNT(gPE2); i++) { + gPE2[i](&paint); + canvas->drawPath(path, paint); + canvas->translate(0, 160); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkGradientShader.h" + +struct GradData { + int fCount; + const SkColor* fColors; + const SkScalar* fPos; +}; + +static const SkColor gColors[] = { +SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK +}; +static const SkScalar gPos0[] = { 0, SK_Scalar1 }; +static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; +static const SkScalar gPos2[] = { +0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1 +}; + +static const GradData gGradData[] = { +{ 2, gColors, NULL }, +{ 2, gColors, gPos0 }, +{ 2, gColors, gPos1 }, +{ 5, gColors, NULL }, +{ 5, gColors, gPos2 } +}; + +static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, + data.fCount, tm, mapper); +} + +static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateRadial(center, center.fX, data.fColors, + data.fPos, data.fCount, tm, mapper); +} + +static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center; + center.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, + data.fPos, data.fCount, mapper); +} + +static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper) { + SkPoint center0, center1; + center0.set(SkScalarAve(pts[0].fX, pts[1].fX), + SkScalarAve(pts[0].fY, pts[1].fY)); + center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), + SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); + return SkGradientShader::CreateTwoPointRadial( + center1, (pts[1].fX - pts[0].fX) / 7, + center0, (pts[1].fX - pts[0].fX) / 2, + data.fColors, data.fPos, data.fCount, tm, mapper); +} + +typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, + SkShader::TileMode tm, SkUnitMapper* mapper); +static const GradMaker gGradMakers[] = { + MakeLinear, MakeRadial, MakeSweep, Make2Radial +}; + +static void gradient_slide(SkCanvas* canvas) { + SkPoint pts[2] = { + { 0, 0 }, + { SkIntToScalar(100), SkIntToScalar(100) } + }; + SkShader::TileMode tm = SkShader::kClamp_TileMode; + SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; + SkPaint paint; + paint.setAntiAlias(true); + paint.setDither(true); + + canvas->translate(SkIntToScalar(20), SkIntToScalar(10)); + for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { + canvas->save(); + for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { + SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL); + paint.setShader(shader); + canvas->drawRect(r, paint); + shader->unref(); + canvas->translate(0, SkIntToScalar(120)); + } + canvas->restore(); + canvas->translate(SkIntToScalar(120), 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkPathMeasure.h" + +static SkScalar getpathlen(const SkPath& path) { + SkPathMeasure meas(path, false); + return meas.getLength(); +} + +static void textonpath_slide(SkCanvas* canvas) { + const char* text = "Displacement"; + size_t len =strlen(text); + SkPath path; + path.moveTo(100, 300); + path.quadTo(300, 100, 500, 300); + path.offset(0, -100); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(40); + + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(path, paint); + paint.setStyle(SkPaint::kFill_Style); + + SkScalar x = 50; + paint.setColor(0xFF008800); + canvas->drawTextOnPathHV(text, len, path, + x, paint.getTextSize()*2/3, paint); + paint.setColor(SK_ColorRED); + canvas->drawTextOnPathHV(text, len, path, + x + 60, 0, paint); + paint.setColor(SK_ColorBLUE); + canvas->drawTextOnPathHV(text, len, path, + x + 120, -paint.getTextSize()*2/3, paint); + + path.offset(0, 200); + paint.setTextAlign(SkPaint::kRight_Align); + + text = "Matrices"; + len = strlen(text); + SkScalar pathLen = getpathlen(path); + SkMatrix matrix; + + paint.setColor(SK_ColorBLACK); + paint.setStyle(SkPaint::kStroke_Style); + canvas->drawPath(path, paint); + paint.setStyle(SkPaint::kFill_Style); + + paint.setTextSize(50); + canvas->drawTextOnPath(text, len, path, NULL, paint); + + paint.setColor(SK_ColorRED); + matrix.setScale(-SK_Scalar1, SK_Scalar1); + matrix.postTranslate(pathLen, 0); + canvas->drawTextOnPath(text, len, path, &matrix, paint); + + paint.setColor(SK_ColorBLUE); + matrix.setScale(SK_Scalar1, -SK_Scalar1); + canvas->drawTextOnPath(text, len, path, &matrix, paint); + + paint.setColor(0xFF008800); + matrix.setScale(-SK_Scalar1, -SK_Scalar1); + matrix.postTranslate(pathLen, 0); + canvas->drawTextOnPath(text, len, path, &matrix, paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkImageDecoder.h" +#include "SkOSFile.h" +#include "SkRandom.h" +#include "SkStream.h" +#include "SkNinePatch.h" + +static SkShader* make_shader0(SkIPoint* size) { + SkBitmap bm; + + SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm); + size->set(bm.width(), bm.height()); + return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); +} + +static SkShader* make_shader1(const SkIPoint& size) { + SkPoint pts[] = { { 0, 0 }, + { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } }; + SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED }; + return SkGradientShader::CreateLinear(pts, colors, NULL, + SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL); +} + +class Rec { +public: + SkCanvas::VertexMode fMode; + int fCount; + SkPoint* fVerts; + SkPoint* fTexs; + + Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {} + ~Rec() { delete[] fVerts; delete[] fTexs; } +}; + +void make_tris(Rec* rec) { + int n = 10; + SkRandom rand; + + rec->fMode = SkCanvas::kTriangles_VertexMode; + rec->fCount = n * 3; + rec->fVerts = new SkPoint[rec->fCount]; + + for (int i = 0; i < n; i++) { + SkPoint* v = &rec->fVerts[i*3]; + for (int j = 0; j < 3; j++) { + v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250); + } + } +} + +void make_fan(Rec* rec, int texWidth, int texHeight) { + const SkScalar tx = SkIntToScalar(texWidth); + const SkScalar ty = SkIntToScalar(texHeight); + const int n = 24; + + rec->fMode = SkCanvas::kTriangleFan_VertexMode; + rec->fCount = n + 2; + rec->fVerts = new SkPoint[rec->fCount]; + rec->fTexs = new SkPoint[rec->fCount]; + + SkPoint* v = rec->fVerts; + SkPoint* t = rec->fTexs; + + v[0].set(0, 0); + t[0].set(0, 0); + for (int i = 0; i < n; i++) { + SkScalar cos; + SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos); + v[i+1].set(cos, sin); + t[i+1].set(i*tx/n, ty); + } + v[n+1] = v[1]; + t[n+1].set(tx, ty); + + SkMatrix m; + m.setScale(SkIntToScalar(100), SkIntToScalar(100)); + m.postTranslate(SkIntToScalar(110), SkIntToScalar(110)); + m.mapPoints(v, rec->fCount); +} + +void make_strip(Rec* rec, int texWidth, int texHeight) { + const SkScalar tx = SkIntToScalar(texWidth); + const SkScalar ty = SkIntToScalar(texHeight); + const int n = 24; + + rec->fMode = SkCanvas::kTriangleStrip_VertexMode; + rec->fCount = 2 * (n + 1); + rec->fVerts = new SkPoint[rec->fCount]; + rec->fTexs = new SkPoint[rec->fCount]; + + SkPoint* v = rec->fVerts; + SkPoint* t = rec->fTexs; + + for (int i = 0; i < n; i++) { + SkScalar cos; + SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos); + v[i*2 + 0].set(cos/2, sin/2); + v[i*2 + 1].set(cos, sin); + + t[i*2 + 0].set(tx * i / n, ty); + t[i*2 + 1].set(tx * i / n, 0); + } + v[2*n + 0] = v[0]; + v[2*n + 1] = v[1]; + + t[2*n + 0].set(tx, ty); + t[2*n + 1].set(tx, 0); + + SkMatrix m; + m.setScale(SkIntToScalar(100), SkIntToScalar(100)); + m.postTranslate(SkIntToScalar(110), SkIntToScalar(110)); + m.mapPoints(v, rec->fCount); +} + +static void mesh_slide(SkCanvas* canvas) { + Rec fRecs[3]; + SkIPoint size; + + SkShader* fShader0 = make_shader0(&size); + SkShader* fShader1 = make_shader1(size); + + SkAutoUnref aur0(fShader0); + SkAutoUnref aur1(fShader1); + + make_strip(&fRecs[0], size.fX, size.fY); + make_fan(&fRecs[1], size.fX, size.fY); + make_tris(&fRecs[2]); + + SkPaint paint; + paint.setDither(true); + paint.setFilterBitmap(true); + + for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) { + canvas->save(); + + paint.setShader(NULL); + canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, + fRecs[i].fVerts, fRecs[i].fTexs, + NULL, NULL, NULL, 0, paint); + + canvas->translate(SkIntToScalar(210), 0); + + paint.setShader(fShader0); + canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, + fRecs[i].fVerts, fRecs[i].fTexs, + NULL, NULL, NULL, 0, paint); + + canvas->translate(SkIntToScalar(210), 0); + + paint.setShader(fShader1); + canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, + fRecs[i].fVerts, fRecs[i].fTexs, + NULL, NULL, NULL, 0, paint); + canvas->restore(); + + canvas->translate(0, SkIntToScalar(250)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkGradientShader.h" +#include "SkLayerRasterizer.h" +#include "SkBlurMaskFilter.h" + +static void r0(SkLayerRasterizer* rast, SkPaint& p) +{ + p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3), + SkBlurMaskFilter::kNormal_BlurStyle))->unref(); + rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); + + p.setMaskFilter(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); + + p.setAlpha(0x11); + p.setStyle(SkPaint::kFill_Style); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + rast->addLayer(p); +} + +static void r1(SkLayerRasterizer* rast, SkPaint& p) +{ + rast->addLayer(p); + + p.setAlpha(0x40); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*2); + rast->addLayer(p); +} + +static void r2(SkLayerRasterizer* rast, SkPaint& p) +{ + p.setStyle(SkPaint::kStrokeAndFill_Style); + p.setStrokeWidth(SK_Scalar1*4); + rast->addLayer(p); + + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*3/2); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); +} + +static void r3(SkLayerRasterizer* rast, SkPaint& p) +{ + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*3); + rast->addLayer(p); + + p.setAlpha(0x20); + p.setStyle(SkPaint::kFill_Style); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + rast->addLayer(p); +} + +static void r4(SkLayerRasterizer* rast, SkPaint& p) +{ + p.setAlpha(0x60); + rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); + + p.setAlpha(0xFF); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2); + + p.setXfermode(NULL); + rast->addLayer(p); +} + +#include "SkDiscretePathEffect.h" + +static void r5(SkLayerRasterizer* rast, SkPaint& p) +{ + rast->addLayer(p); + + p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref(); + p.setXfermodeMode(SkXfermode::kSrcOut_Mode); + rast->addLayer(p); +} + +static void r6(SkLayerRasterizer* rast, SkPaint& p) +{ + rast->addLayer(p); + + p.setAntiAlias(false); + SkLayerRasterizer* rast2 = new SkLayerRasterizer; + r5(rast2, p); + p.setRasterizer(rast2)->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); +} + +#include "Sk2DPathEffect.h" + +class Dot2DPathEffect : public Sk2DPathEffect { +public: + Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix) + : Sk2DPathEffect(matrix), fRadius(radius) {} + + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + this->INHERITED::flatten(buffer); + + buffer.writeScalar(fRadius); + } + virtual Factory getFactory() { return CreateProc; } + +protected: + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) + { + dst->addCircle(loc.fX, loc.fY, fRadius); + } + + Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) + { + fRadius = buffer.readScalar(); + } +private: + SkScalar fRadius; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return new Dot2DPathEffect(buffer); + } + + typedef Sk2DPathEffect INHERITED; +}; + +static void r7(SkLayerRasterizer* rast, SkPaint& p) +{ + SkMatrix lattice; + lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref(); + rast->addLayer(p); +} + +static void r8(SkLayerRasterizer* rast, SkPaint& p) +{ + rast->addLayer(p); + + SkMatrix lattice; + lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); + + p.setPathEffect(NULL); + p.setXfermode(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); +} + +class Line2DPathEffect : public Sk2DPathEffect { +public: + Line2DPathEffect(SkScalar width, const SkMatrix& matrix) + : Sk2DPathEffect(matrix), fWidth(width) {} + + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) + { + if (this->INHERITED::filterPath(dst, src, width)) + { + *width = fWidth; + return true; + } + return false; + } + + virtual Factory getFactory() { return CreateProc; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) + { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fWidth); + } +protected: + virtual void nextSpan(int u, int v, int ucount, SkPath* dst) + { + if (ucount > 1) + { + SkPoint src[2], dstP[2]; + + src[0].set(SkIntToScalar(u) + SK_ScalarHalf, + SkIntToScalar(v) + SK_ScalarHalf); + src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, + SkIntToScalar(v) + SK_ScalarHalf); + this->getMatrix().mapPoints(dstP, src, 2); + + dst->moveTo(dstP[0]); + dst->lineTo(dstP[1]); + } + } + + Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) + { + fWidth = buffer.readScalar(); + } + +private: + SkScalar fWidth; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) + { + return new Line2DPathEffect(buffer); + } + + typedef Sk2DPathEffect INHERITED; +}; + +static void r9(SkLayerRasterizer* rast, SkPaint& p) +{ + rast->addLayer(p); + + SkMatrix lattice; + lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0); + lattice.postRotate(SkIntToScalar(30), 0, 0); + p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); + + p.setPathEffect(NULL); + p.setXfermode(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); +} + +typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&); + +static const raster_proc gRastProcs[] = { + r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 +}; + +static void apply_shader(SkPaint* paint, int index) { + raster_proc proc = gRastProcs[index]; + SkPaint p; + SkLayerRasterizer* rast = new SkLayerRasterizer; + + p.setAntiAlias(true); + proc(rast, p); + paint->setRasterizer(rast)->unref(); + paint->setColor(SK_ColorBLUE); +} + +#include "SkTypeface.h" + +static void texteffect_slide(SkCanvas* canvas) { + const char* str = "Google"; + size_t len = strlen(str); + SkScalar x = 20; + SkScalar y = 80; + SkPaint paint; + paint.setTypeface(SkTypeface::CreateFromName("Georgia", SkTypeface::kItalic)); + paint.setTextSize(75); + paint.setAntiAlias(true); + paint.setColor(SK_ColorBLUE); + for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) { + apply_shader(&paint, i); + canvas->drawText(str, len, x, y, paint); + y += 80; + if (i == 4) { + x += 320; + y = 80; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkImageEncoder.h" + +static const SlideProc gProc[] = { + patheffect_slide, + gradient_slide, + textonpath_slide, + mesh_slide, + texteffect_slide +}; + +class SlideView : public SampleView { + int fIndex; +public: + SlideView() { + fIndex = 0; + + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 1024, 768); + bm.allocPixels(); + SkCanvas canvas(bm); + SkScalar s = SkIntToScalar(1024) / 640; + canvas.scale(s, s); + for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); i++) { + canvas.save(); + canvas.drawColor(BG_COLOR); + gProc[i](&canvas); + canvas.restore(); + SkString str; + str.printf("/skimages/slide_%d.png", i); + SkImageEncoder::EncodeFile(str.c_str(), bm, SkImageEncoder::kPNG_Type, 100); + } + this->setBGColor(BG_COLOR); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Slides"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + gProc[fIndex](canvas); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fIndex = (fIndex + 1) % SK_ARRAY_COUNT(gProc); + this->inval(NULL); + return NULL; + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new SlideView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleSpiral.cpp b/samplecode/SampleSpiral.cpp new file mode 100644 index 0000000000..1a41440c6a --- /dev/null +++ b/samplecode/SampleSpiral.cpp @@ -0,0 +1,56 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" + +class SpiralView : public SampleView { +public: + SpiralView() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Spiral"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3))); + paint.setStyle(SkPaint::kFill_Style); + + SkRect r; + SkScalar l,t,x,y; + l = SampleCode::GetAnimScalar(SkIntToScalar(10), + SkIntToScalar(400)); + t = SampleCode::GetAnimScalar(SkIntToScalar(5), + SkIntToScalar(200)); + + canvas->translate(320,240); + for (int i = 0; i < 35; i++) { + paint.setColor(0xFFF00FF0 - i * 0x04000000); + SkScalar step = SK_ScalarPI / (55 - i); + SkScalar angle = t * step; + x = (20 + SkIntToScalar(i) * 5) * SkScalarSinCos(angle, &y); + y *= (20 + SkIntToScalar(i) * 5); + r.set(x, y, x + SkIntToScalar(10), y + SkIntToScalar(10)); + canvas->drawRect(r, paint); + } + + this->inval(NULL); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new SpiralView; } +static SkViewRegister reg(MyFactory);
\ No newline at end of file diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp new file mode 100644 index 0000000000..ae630ef30e --- /dev/null +++ b/samplecode/SampleStrokePath.cpp @@ -0,0 +1,217 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkParsePath.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkView.h" + +#include "SkBlurMaskFilter.h" + +static void test_huge_stroke(SkCanvas* canvas) { + SkRect srcR = { 0, 0, 72000, 54000 }; + SkRect dstR = { 0, 0, 640, 480 }; + + SkPath path; + path.moveTo(17600, 8000); + path.lineTo(52800, 8000); + path.lineTo(52800, 41600); + path.lineTo(17600, 41600); + path.close(); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setStrokeWidth(8000); + paint.setStrokeMiter(10); + paint.setStrokeCap(SkPaint::kButt_Cap); + paint.setStrokeJoin(SkPaint::kRound_Join); + paint.setStyle(SkPaint::kStroke_Style); + + SkMatrix matrix; + matrix.setRectToRect(srcR, dstR, SkMatrix::kCenter_ScaleToFit); + canvas->concat(matrix); + + canvas->drawPath(path, paint); +} + +#if 0 +#include "SkBlurMask.h" +static void test_blur() { + uint8_t cell[9]; + memset(cell, 0xFF, sizeof(cell)); + SkMask src; + src.fImage = cell; + src.fFormat = SkMask::kA8_Format; + SkMask dst; + + for (int y = 1; y <= 3; y++) { + for (int x = 1; x <= 3; x++) { + src.fBounds.set(0, 0, x, y); + src.fRowBytes = src.fBounds.width(); + + SkScalar radius = 1.f; + + printf("src [%d %d %d %d] radius %g\n", src.fBounds.fLeft, src.fBounds.fTop, + src.fBounds.fRight, src.fBounds.fBottom, radius); + + SkBlurMask::Blur(&dst, src, radius, SkBlurMask::kNormal_Style); + uint8_t* dstPtr = dst.fImage; + + for (int y = 0; y < dst.fBounds.height(); y++) { + for (int x = 0; x < dst.fBounds.width(); x++) { + printf(" %02X", dstPtr[x]); + } + printf("\n"); + dstPtr += dst.fRowBytes; + } + } + } +} +#endif + +static void scale_to_width(SkPath* path, SkScalar dstWidth) { + const SkRect& bounds = path->getBounds(); + SkScalar scale = dstWidth / bounds.width(); + SkMatrix matrix; + + matrix.setScale(scale, scale); + path->transform(matrix); +} + +static const struct { + SkPaint::Style fStyle; + SkPaint::Join fJoin; + int fStrokeWidth; +} gRec[] = { + { SkPaint::kFill_Style, SkPaint::kMiter_Join, 0 }, + { SkPaint::kStroke_Style, SkPaint::kMiter_Join, 0 }, + { SkPaint::kStroke_Style, SkPaint::kMiter_Join, 10 }, + { SkPaint::kStrokeAndFill_Style, SkPaint::kMiter_Join, 10 }, +}; + +class StrokePathView : public SampleView { + SkScalar fWidth; + SkPath fPath; +public: + StrokePathView() { +// test_blur(); + fWidth = SkIntToScalar(120); + +#if 0 + const char str[] = + "M 0, 3" + "C 10, -10, 30, -10, 0, 28" + "C -30, -10, -10, -10, 0, 3" + "Z"; + SkParsePath::FromSVGString(str, &fPath); +#else + fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction); + fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction); +#endif + + scale_to_width(&fPath, fWidth); + const SkRect& bounds = fPath.getBounds(); + fPath.offset(-bounds.fLeft, -bounds.fTop); + + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "StrokePath"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + SkRandom rand; + + void drawSet(SkCanvas* canvas, SkPaint* paint) { + SkAutoCanvasRestore acr(canvas, true); + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { + paint->setStyle(gRec[i].fStyle); + paint->setStrokeJoin(gRec[i].fJoin); + paint->setStrokeWidth(SkIntToScalar(gRec[i].fStrokeWidth)); + canvas->drawPath(fPath, *paint); + canvas->translate(fWidth * 5 / 4, 0); + } + } + + virtual void onDrawContent(SkCanvas* canvas) { + test_huge_stroke(canvas); return; + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + SkPaint paint; + paint.setAntiAlias(true); + + if (true) { + canvas->drawColor(SK_ColorBLACK); + + paint.setTextSize(24); + paint.setColor(SK_ColorWHITE); + canvas->translate(10, 30); + + static const SkBlurMaskFilter::BlurStyle gStyle[] = { + SkBlurMaskFilter::kNormal_BlurStyle, + SkBlurMaskFilter::kInner_BlurStyle, + SkBlurMaskFilter::kOuter_BlurStyle, + SkBlurMaskFilter::kSolid_BlurStyle, + }; + for (int x = 0; x < 5; x++) { + SkMaskFilter* mf; + SkScalar radius = 4; + for (int y = 0; y < 10; y++) { + if (x) { + mf = SkBlurMaskFilter::Create(radius, gStyle[x - 1]); + paint.setMaskFilter(mf)->unref(); + } + canvas->drawText("Title Bar", 9, x*SkIntToScalar(100), y*SkIntToScalar(30), paint); + radius *= 0.75f; + } + + } + return; + } + + paint.setColor(SK_ColorBLUE); + +#if 1 + SkPath p; + float r = rand.nextUScalar1() + 0.5f; + SkScalar x = 0, y = 0; + p.moveTo(x, y); +#if 0 + p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r); + p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y); +#else + p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r); + p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y); +#endif + p.close(); + fPath = p; + fPath.offset(100, 0); +#endif + + fPath.setFillType(SkPath::kWinding_FillType); + drawSet(canvas, &paint); + + canvas->translate(0, fPath.getBounds().height() * 5 / 4); + fPath.setFillType(SkPath::kEvenOdd_FillType); + drawSet(canvas, &paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new StrokePathView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleStrokeRect.cpp b/samplecode/SampleStrokeRect.cpp new file mode 100644 index 0000000000..20c9e2ce88 --- /dev/null +++ b/samplecode/SampleStrokeRect.cpp @@ -0,0 +1,69 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" + +class StrokeRectSample : public SampleView { +public: + StrokeRectSample() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Stroke Rects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(20)); + + SkPaint hair; + hair.setStyle(SkPaint::kStroke_Style); + hair.setColor(SK_ColorRED); + + static const SkISize gSize[] = { + { 100, 50 }, + { 100, 0 }, + { 0, 50 }, + { 0, 0 } + }; + + static const SkPaint::Join gJoin[] = { + SkPaint::kMiter_Join, + SkPaint::kRound_Join, + SkPaint::kBevel_Join + }; + + canvas->translate(paint.getStrokeWidth(), paint.getStrokeWidth()); + for (size_t i = 0; i < SK_ARRAY_COUNT(gJoin); ++i) { + paint.setStrokeJoin(gJoin[i]); + + canvas->save(); + for (size_t j = 0; j < SK_ARRAY_COUNT(gSize); ++j) { + SkRect r = SkRect::MakeWH(SkIntToScalar(gSize[j].fWidth), + SkIntToScalar(gSize[j].fHeight)); + canvas->drawRect(r, paint); + canvas->drawRect(r, hair); + canvas->translate(0, SkIntToScalar(100)); + } + canvas->restore(); + canvas->translate(SkIntToScalar(150), 0); + } + } + +private: + typedef SampleView INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new StrokeRectSample; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleStrokeText.cpp b/samplecode/SampleStrokeText.cpp new file mode 100644 index 0000000000..bcb9e4fca0 --- /dev/null +++ b/samplecode/SampleStrokeText.cpp @@ -0,0 +1,140 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +static void lettersToBitmap(SkBitmap* dst, const char chars[], + const SkPaint& original, SkBitmap::Config config) { + SkPath path; + SkScalar x = 0; + SkScalar width; + SkPath p; + for (size_t i = 0; i < strlen(chars); i++) { + original.getTextPath(&chars[i], 1, x, 0, &p); + path.addPath(p); + original.getTextWidths(&chars[i], 1, &width); + x += width; + } + SkRect bounds = path.getBounds(); + SkScalar sw = -original.getStrokeWidth(); + bounds.inset(sw, sw); + path.offset(-bounds.fLeft, -bounds.fTop); + bounds.offset(-bounds.fLeft, -bounds.fTop); + + int w = SkScalarRound(bounds.width()); + int h = SkScalarRound(bounds.height()); + SkPaint paint(original); + SkBitmap src; + src.setConfig(config, w, h); + src.allocPixels(); + src.eraseColor(0); + { + SkCanvas canvas(src); + paint.setAntiAlias(true); + paint.setColor(SK_ColorBLACK); + paint.setStyle(SkPaint::kFill_Style); + canvas.drawPath(path, paint); + } + + dst->setConfig(config, w, h); + dst->allocPixels(); + dst->eraseColor(SK_ColorWHITE); + { + SkCanvas canvas(*dst); + paint.setXfermodeMode(SkXfermode::kDstATop_Mode); + canvas.drawBitmap(src, 0, 0, &paint); + paint.setColor(original.getColor()); + paint.setStyle(SkPaint::kStroke_Style); + canvas.drawPath(path, paint); + } +} + +static void lettersToBitmap2(SkBitmap* dst, const char chars[], + const SkPaint& original, SkBitmap::Config config) { + SkPath path; + SkScalar x = 0; + SkScalar width; + SkPath p; + for (size_t i = 0; i < strlen(chars); i++) { + original.getTextPath(&chars[i], 1, x, 0, &p); + path.addPath(p); + original.getTextWidths(&chars[i], 1, &width); + x += width; + } + SkRect bounds = path.getBounds(); + SkScalar sw = -original.getStrokeWidth(); + bounds.inset(sw, sw); + path.offset(-bounds.fLeft, -bounds.fTop); + bounds.offset(-bounds.fLeft, -bounds.fTop); + + int w = SkScalarRound(bounds.width()); + int h = SkScalarRound(bounds.height()); + SkPaint paint(original); + + paint.setAntiAlias(true); + paint.setXfermodeMode(SkXfermode::kDstATop_Mode); + paint.setColor(original.getColor()); + paint.setStyle(SkPaint::kStroke_Style); + + dst->setConfig(config, w, h); + dst->allocPixels(); + dst->eraseColor(SK_ColorWHITE); + + SkCanvas canvas(*dst); + canvas.drawPath(path, paint); +} + +class StrokeTextView : public SampleView { + bool fAA; +public: + StrokeTextView() : fAA(false) { + this->setBGColor(0xFFCC8844); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "StrokeText"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkBitmap bm; + SkPaint paint; + + paint.setStrokeWidth(SkIntToScalar(6)); + paint.setTextSize(SkIntToScalar(80)); +// paint.setTypeface(Typeface.DEFAULT_BOLD); + + lettersToBitmap(&bm, "Test Case", paint, SkBitmap::kARGB_4444_Config); + + canvas->drawBitmap(bm, 0, 0); + } + +private: + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new StrokeTextView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTests.cpp b/samplecode/SampleTests.cpp new file mode 100644 index 0000000000..4ce86403f6 --- /dev/null +++ b/samplecode/SampleTests.cpp @@ -0,0 +1,111 @@ +utils#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" + +#include "test.h" + +namespace skiatest { + +class MyReporter : public Reporter { +protected: + virtual void onStart(Test* test) {} + virtual void onReport(const char desc[], Reporter::Result result) { + SkASSERT(Reporter::kPassed == result); + } + virtual void onEnd(Test* test) {} +}; + +class Iter { +public: + Iter(Reporter* r) : fReporter(r) { + r->ref(); + fReg = TestRegistry::Head(); + } + + ~Iter() { + fReporter->unref(); + } + + Test* next() { + if (fReg) { + TestRegistry::Factory fact = fReg->factory(); + fReg = fReg->next(); + Test* test = fact(NULL); + test->setReporter(fReporter); + return test; + } + return NULL; + } + + static int Count() { + const TestRegistry* reg = TestRegistry::Head(); + int count = 0; + while (reg) { + count += 1; + reg = reg->next(); + } + return count; + } + +private: + Reporter* fReporter; + const TestRegistry* fReg; +}; +} + +class TestsView : public SkView { +public: + TestsView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Tests"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + skiatest::MyReporter reporter; + skiatest::Iter iter(&reporter); + skiatest::Test* test; + + while ((test = iter.next()) != NULL) { + test->run(); + SkDELETE(test); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + this->inval(NULL); + return this->INHERITED::onClick(click); + } + + virtual bool handleKey(SkKey key) { + this->inval(NULL); + return true; + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TestsView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleText.cpp b/samplecode/SampleText.cpp new file mode 100644 index 0000000000..267653006c --- /dev/null +++ b/samplecode/SampleText.cpp @@ -0,0 +1,396 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" + +static const int gKernel[3][3] = { +// { -1, -2, -1 }, { -2, 12, -2 }, { -1, -2, -1 } + { 1, 2, 1 }, { 2, 64-12, 2 }, { 1, 2, 1 } +}; +static const int gShift = 6; + +class ReduceNoise : public SkKernel33ProcMaskFilter { +public: + ReduceNoise(int percent256) : SkKernel33ProcMaskFilter(percent256) {} + virtual uint8_t computeValue(uint8_t* const* srcRows) + { + int c = srcRows[1][1]; + int min = 255, max = 0; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + if (i != 1 || j != 1) + { + int v = srcRows[i][j]; + if (max < v) + max = v; + if (min > v) + min = v; + } + if (c > max) c = max; + // if (c < min) c = min; + return c; + } + virtual Factory getFactory() { return Create; } +private: + ReduceNoise(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {} + static SkFlattenable* Create(SkFlattenableReadBuffer& rb) { + return new ReduceNoise(rb); + } +}; + +class Darken : public SkKernel33ProcMaskFilter { +public: + Darken(int percent256) : SkKernel33ProcMaskFilter(percent256) {} + virtual uint8_t computeValue(uint8_t* const* srcRows) + { + int c = srcRows[1][1]; + float f = c / 255.f; + + if (c >= 0) { + f = sqrtf(f); + } else { + f *= f; + } + SkASSERT(f >= 0 && f <= 1); + return (int)(f * 255); + } + virtual Factory getFactory() { return Create; } +private: + Darken(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {} + static SkFlattenable* Create(SkFlattenableReadBuffer& rb) { + return new Darken(rb); + } +}; + +static SkMaskFilter* makemf() { return new Darken(0x30); } + +static void test_breakText() { + SkPaint paint; + const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj"; + size_t length = strlen(text); + SkScalar width = paint.measureText(text, length); + + SkScalar mm = 0; + SkScalar nn = 0; + for (SkScalar w = 0; w <= width; w += SK_Scalar1) { + SkScalar m; + size_t n = paint.breakText(text, length, w, &m, + SkPaint::kBackward_TextBufferDirection); + + SkASSERT(n <= length); + SkASSERT(m <= width); + + if (n == 0) { + SkASSERT(m == 0); + } else { + // now assert that we're monotonic + if (n == nn) { + SkASSERT(m == mm); + } else { + SkASSERT(n > nn); + SkASSERT(m > mm); + } + } + nn = SkIntToScalar(n); + mm = m; + } + + SkDEBUGCODE(size_t length2 =) paint.breakText(text, length, width, &mm); + SkASSERT(length2 == length); + SkASSERT(mm == width); +} + +static SkRandom gRand; + +class SkPowerMode : public SkXfermode { +public: + SkPowerMode(SkScalar exponent) { this->init(exponent); } + + virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]); + + typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&); + + // overrides for SkFlattenable + virtual Factory getFactory() { return Create; } + virtual void flatten(SkFlattenableWriteBuffer& b) { + // this->INHERITED::flatten(b); How can we know if this is legal???? + b.write32(SkScalarToFixed(fExp)); + } + +private: + SkScalar fExp; // user's value + uint8_t fTable[256]; // cache + + void init(SkScalar exponent); + SkPowerMode(SkFlattenableReadBuffer& b) : SkXfermode(b) { + // read the exponent + this->init(SkFixedToScalar(b.readS32())); + } + static SkFlattenable* Create(SkFlattenableReadBuffer& b) { + return SkNEW_ARGS(SkPowerMode, (b)); + } + + typedef SkXfermode INHERITED; +}; + +void SkPowerMode::init(SkScalar e) { + fExp = e; + float ee = SkScalarToFloat(e); + + printf("------ %g\n", ee); + for (int i = 0; i < 256; i++) { + float x = i / 255.f; + // printf(" %d %g", i, x); + x = powf(x, ee); + // printf(" %g", x); + int xx = SkScalarRound(SkFloatToScalar(x * 255)); + // printf(" %d\n", xx); + fTable[i] = SkToU8(xx); + } +} + +void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count, + const SkAlpha aa[]) { + for (int i = 0; i < count; i++) { + SkPMColor c = src[i]; + int r = SkGetPackedR32(c); + int g = SkGetPackedG32(c); + int b = SkGetPackedB32(c); + r = fTable[r]; + g = fTable[g]; + b = fTable[b]; + dst[i] = SkPack888ToRGB16(r, g, b); + } +} + +static const struct { + const char* fName; + uint32_t fFlags; + bool fFlushCache; +} gHints[] = { + { "Linear", SkPaint::kLinearText_Flag, false }, + { "Normal", 0, true }, + { "Subpixel", SkPaint::kSubpixelText_Flag, true } +}; + +static int count_char_points(const SkPaint& paint, char c) { + SkPath path; + + paint.getTextPath(&c, 1, 0, 0, &path); + return path.getPoints(NULL, 0); +} + +static int gOld, gNew, gCount; + +static void dump(int c, int oldc, int newc) { + if (oldc != newc) { + gOld += oldc; + gNew += newc; + gCount += 1; + printf("char %c: old = %3d, new = %3d, reduction %g%%\n", c, oldc, newc, 100. * (oldc - newc) / oldc); + } +} + +static void tab(int n) { +// printf("[%d] ", n); return; + SkASSERT(n >= 0); + for (int i = 0; i < n; i++) + printf(" "); +} + +static void draw_rgn(const SkRegion& rgn, SkCanvas* canvas, const SkPaint& paint) { + SkRect r; + SkRegion::Iterator iter(rgn); + + for (; !iter.done(); iter.next()) { + r.set(iter.rect()); + canvas->drawRect(r, paint); + } +} + +static void test_break(SkCanvas* canvas, const char text[], size_t length, + SkScalar x, SkScalar y, const SkPaint& paint, + SkScalar clickX) { + SkPaint linePaint; + + linePaint.setAntiAlias(true); + + SkScalar measured; + + if (paint.breakText(text, length, clickX - x, &measured, + SkPaint::kForward_TextBufferDirection)) { + linePaint.setColor(SK_ColorRED); + canvas->drawLine(x, y, x + measured, y, linePaint); + } + + x += paint.measureText(text, length); + if (paint.breakText(text, length, x - clickX, &measured, + SkPaint::kBackward_TextBufferDirection)) { + linePaint.setColor(SK_ColorBLUE); + canvas->drawLine(x - measured, y, x, y, linePaint); + } +} + +static void DrawTheText(SkCanvas* canvas, const char text[], size_t length, + SkScalar x, SkScalar y, const SkPaint& paint, + SkScalar clickX, SkMaskFilter* mf) { + SkPaint p(paint); + +#if 0 + canvas->drawText(text, length, x, y, paint); +#else + { + SkPoint pts[1000]; + SkScalar xpos = x; + SkASSERT(length <= SK_ARRAY_COUNT(pts)); + for (size_t i = 0; i < length; i++) { + pts[i].set(xpos, y), xpos += paint.getTextSize(); + } + canvas->drawPosText(text, length, pts, paint); + } +#endif + + p.setSubpixelText(true); + x += SkIntToScalar(180); + canvas->drawText(text, length, x, y, p); + +#ifdef SK_DEBUG + if (true) { + // p.setMaskFilter(mf); + p.setSubpixelText(false); + p.setLinearText(true); + x += SkIntToScalar(180); + canvas->drawText(text, length, x, y, p); + } +#endif +} + +class TextSpeedView : public SampleView { +public: + TextSpeedView() { + fMF = makemf(); + + fHints = 0; + fClickX = 0; + + test_breakText(); + } + + virtual ~TextSpeedView() { + SkSafeUnref(fMF); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Text"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static void make_textstrip(SkBitmap* bm) { + bm->setConfig(SkBitmap::kRGB_565_Config, 200, 18); + bm->allocPixels(); + bm->eraseColor(SK_ColorWHITE); + + SkCanvas canvas(*bm); + SkPaint paint; + const char* s = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit"; + + paint.setFlags(paint.getFlags() | SkPaint::kAntiAlias_Flag + | SkPaint::kDevKernText_Flag); + paint.setTextSize(SkIntToScalar(14)); + canvas.drawText(s, strlen(s), SkIntToScalar(8), SkIntToScalar(14), paint); + } + + static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) { + for (size_t i = 0; i < n; i++) + pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkAutoCanvasRestore restore(canvas, false); + { + SkRect r; + r.set(0, 0, SkIntToScalar(1000), SkIntToScalar(20)); + // canvas->saveLayer(&r, NULL, SkCanvas::kHasAlphaLayer_SaveFlag); + } + + SkPaint paint; +// const uint16_t glyphs[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }; + int index = fHints % SK_ARRAY_COUNT(gHints); + index = 1; +// const char* style = gHints[index].fName; + +// canvas->translate(0, SkIntToScalar(50)); + + // canvas->drawText(style, strlen(style), SkIntToScalar(20), SkIntToScalar(20), paint); + + SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromFile("/skimages/samplefont.ttf"))); + paint.setAntiAlias(true); + paint.setFlags(paint.getFlags() | gHints[index].fFlags); + + SkRect clip; + clip.set(SkIntToScalar(25), SkIntToScalar(34), SkIntToScalar(88), SkIntToScalar(155)); + + const char* text = "Hamburgefons"; + size_t length = strlen(text); + + SkScalar y = SkIntToScalar(0); + for (int i = 9; i <= 24; i++) { + paint.setTextSize(SkIntToScalar(i) /*+ (gRand.nextU() & 0xFFFF)*/); + for (SkScalar dx = 0; dx <= SkIntToScalar(3)/4; + dx += SkIntToScalar(1) /* /4 */) { + y += paint.getFontSpacing(); + DrawTheText(canvas, text, length, SkIntToScalar(20) + dx, y, + paint, fClickX, fMF); + } + } + if (gHints[index].fFlushCache) { +// SkGraphics::SetFontCacheUsed(0); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fClickX = x; + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + int fHints; + SkScalar fClickX; + SkMaskFilter* fMF; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TextSpeedView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp new file mode 100644 index 0000000000..ccfed68e6d --- /dev/null +++ b/samplecode/SampleTextAlpha.cpp @@ -0,0 +1,114 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkBlurMaskFilter.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkOSFile.h" +#include "SkStream.h" + +static void check_for_nonwhite(const SkBitmap& bm, int alpha) { + if (bm.config() != SkBitmap::kRGB_565_Config) { + return; + } + + for (int y = 0; y < bm.height(); y++) { + for (int x = 0; x < bm.width(); x++) { + uint16_t c = *bm.getAddr16(x, y); + if (c != 0xFFFF) { + SkDebugf("------ nonwhite alpha=%x [%d %d] %x\n", alpha, x, y, c); + return; + } + } + } +} + +class TextAlphaView : public SampleView { +public: + TextAlphaView() { + fByte = 0xFF; + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SkString str("TextAlpha"); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + SkPaint paint; + SkScalar x = SkIntToScalar(10); + SkScalar y = SkIntToScalar(20); + + paint.setFlags(0x105); + + paint.setARGB(fByte, 0xFF, 0xFF, 0xFF); + + paint.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3), + SkBlurMaskFilter::kNormal_BlurStyle)); + paint.getMaskFilter()->unref(); + + SkRandom rand; + + for (int ps = 6; ps <= 35; ps++) { + paint.setColor(rand.nextU() | (0xFF << 24)); + paint.setTextSize(SkIntToScalar(ps)); + paint.setTextSize(SkIntToScalar(24)); + canvas->drawText(str, strlen(str), x, y, paint); + y += paint.getFontMetrics(NULL); + } + //check_for_nonwhite(canvas->getDevice()->accessBitmap(), fByte); + //SkDebugf("------ byte %x\n", fByte); + + if (false) { + fByte += 1; + fByte &= 0xFF; + this->inval(NULL); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + return new Click(this); + } + + virtual bool onClick(Click* click) { + int y = click->fICurr.fY; + if (y < 0) { + y = 0; + } else if (y > 255) { + y = 255; + } + fByte = y; + this->inval(NULL); + return true; + } + +private: + int fByte; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TextAlphaView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTextBox.cpp b/samplecode/SampleTextBox.cpp new file mode 100644 index 0000000000..37a6be0936 --- /dev/null +++ b/samplecode/SampleTextBox.cpp @@ -0,0 +1,91 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkBlurMaskFilter.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkTextBox.h" +#include "SkOSFile.h" +#include "SkStream.h" +#include "SkKey.h" + +#ifdef SK_BUILD_FOR_WIN +extern SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&); +#endif + +static const char gText[] = + "When in the Course of human events it becomes necessary for one people " + "to dissolve the political bands which have connected them with another " + "and to assume among the powers of the earth, the separate and equal " + "station to which the Laws of Nature and of Nature's God entitle them, " + "a decent respect to the opinions of mankind requires that they should " + "declare the causes which impel them to the separation."; + +class TextBoxView : public SampleView { +public: + TextBoxView() { +#ifdef SK_BUILD_FOR_WIN + LOGFONT lf; + sk_bzero(&lf, sizeof(lf)); + lf.lfHeight = 9; + SkTypeface* tf0 = SkCreateTypefaceFromLOGFONT(lf); + lf.lfHeight = 12; + SkTypeface* tf1 = SkCreateTypefaceFromLOGFONT(lf); + // we assert that different sizes should not affect which face we get + SkASSERT(tf0 == tf1); + tf0->unref(); + tf1->unref(); +#endif + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SkString str("TextBox"); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkScalar margin = 20; + SkTextBox tbox; + tbox.setMode(SkTextBox::kLineBreak_Mode); + tbox.setBox(margin, margin, + this->width() - margin, this->height() - margin); + tbox.setSpacing(SkIntToScalar(3)/3, 0); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setLCDRenderText(true); + tbox.setText(gText, strlen(gText), paint); + + for (int i = 9; i < 24; i += 2) { + paint.setTextSize(SkIntToScalar(i)); + tbox.draw(canvas); + canvas->translate(0, tbox.getTextHeight() + paint.getFontSpacing()); + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TextBoxView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTextEffects.cpp b/samplecode/SampleTextEffects.cpp new file mode 100644 index 0000000000..f256b2ef18 --- /dev/null +++ b/samplecode/SampleTextEffects.cpp @@ -0,0 +1,397 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTypeface.h" +#include "SkAvoidXfermode.h" + +static inline SkPMColor rgb2gray(SkPMColor c) { + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + + unsigned x = (r * 5 + g * 7 + b * 4) >> 4; + + return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT)); +} + +class SkGrayScaleColorFilter : public SkColorFilter { +public: + virtual void filterSpan(const SkPMColor src[], int count, + SkPMColor result[]) { + for (int i = 0; i < count; i++) { + result[i] = rgb2gray(src[i]); + } + } +}; + +class SkChannelMaskColorFilter : public SkColorFilter { +public: + SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) { + fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask); + } + + virtual void filterSpan(const SkPMColor src[], int count, + SkPMColor result[]) { + SkPMColor mask = fMask; + for (int i = 0; i < count; i++) { + result[i] = src[i] & mask; + } + } + +private: + SkPMColor fMask; +}; + +/////////////////////////////////////////////////////////// + +#include "SkGradientShader.h" +#include "SkLayerRasterizer.h" +#include "SkBlurMaskFilter.h" + +static void r0(SkLayerRasterizer* rast, SkPaint& p) { + p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3), + SkBlurMaskFilter::kNormal_BlurStyle))->unref(); + rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); + + p.setMaskFilter(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); + + p.setAlpha(0x11); + p.setStyle(SkPaint::kFill_Style); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + rast->addLayer(p); +} + +static void r1(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + p.setAlpha(0x40); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*2); + rast->addLayer(p); +} + +static void r2(SkLayerRasterizer* rast, SkPaint& p) { + p.setStyle(SkPaint::kStrokeAndFill_Style); + p.setStrokeWidth(SK_Scalar1*4); + rast->addLayer(p); + + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*3/2); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); +} + +static void r3(SkLayerRasterizer* rast, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1*3); + rast->addLayer(p); + + p.setAlpha(0x20); + p.setStyle(SkPaint::kFill_Style); + p.setXfermodeMode(SkXfermode::kSrc_Mode); + rast->addLayer(p); +} + +static void r4(SkLayerRasterizer* rast, SkPaint& p) { + p.setAlpha(0x60); + rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); + + p.setAlpha(0xFF); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2); + + p.setXfermode(NULL); + rast->addLayer(p); +} + +#include "SkDiscretePathEffect.h" + +static void r5(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref(); + p.setXfermodeMode(SkXfermode::kSrcOut_Mode); + rast->addLayer(p); +} + +static void r6(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + p.setAntiAlias(false); + SkLayerRasterizer* rast2 = new SkLayerRasterizer; + r5(rast2, p); + p.setRasterizer(rast2)->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); +} + +#include "Sk2DPathEffect.h" + +class Dot2DPathEffect : public Sk2DPathEffect { +public: + Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix) + : Sk2DPathEffect(matrix), fRadius(radius) {} + + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + buffer.writeScalar(fRadius); + } + virtual Factory getFactory() { return CreateProc; } + +protected: + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) { + dst->addCircle(loc.fX, loc.fY, fRadius); + } + + Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) { + fRadius = buffer.readScalar(); + } +private: + SkScalar fRadius; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return new Dot2DPathEffect(buffer); + } + + typedef Sk2DPathEffect INHERITED; +}; + +static void r7(SkLayerRasterizer* rast, SkPaint& p) { + SkMatrix lattice; + lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref(); + rast->addLayer(p); +} + +static void r8(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + SkMatrix lattice; + lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); + lattice.postSkew(SK_Scalar1/3, 0, 0, 0); + p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); + + p.setPathEffect(NULL); + p.setXfermode(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); +} + +class Line2DPathEffect : public Sk2DPathEffect { +public: + Line2DPathEffect(SkScalar width, const SkMatrix& matrix) + : Sk2DPathEffect(matrix), fWidth(width) {} + + virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) { + if (this->INHERITED::filterPath(dst, src, width)) { + *width = fWidth; + return true; + } + return false; + } + + virtual Factory getFactory() { return CreateProc; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fWidth); + } + +protected: + virtual void nextSpan(int u, int v, int ucount, SkPath* dst) { + if (ucount > 1) { + SkPoint src[2], dstP[2]; + + src[0].set(SkIntToScalar(u) + SK_ScalarHalf, + SkIntToScalar(v) + SK_ScalarHalf); + src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, + SkIntToScalar(v) + SK_ScalarHalf); + this->getMatrix().mapPoints(dstP, src, 2); + + dst->moveTo(dstP[0]); + dst->lineTo(dstP[1]); + } + } + + Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) { + fWidth = buffer.readScalar(); + } + +private: + SkScalar fWidth; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return new Line2DPathEffect(buffer); + } + + typedef Sk2DPathEffect INHERITED; +}; + +static void r9(SkLayerRasterizer* rast, SkPaint& p) { + rast->addLayer(p); + + SkMatrix lattice; + lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0); + lattice.postRotate(SkIntToScalar(30), 0, 0); + p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref(); + p.setXfermodeMode(SkXfermode::kClear_Mode); + rast->addLayer(p); + + p.setPathEffect(NULL); + p.setXfermode(NULL); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + rast->addLayer(p); +} + +typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&); + +static const raster_proc gRastProcs[] = { + r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 +}; + +static const struct { + SkColor fMul, fAdd; +} gLightingColors[] = { + { 0x808080, 0x800000 }, // general case + { 0x707070, 0x707070 }, // no-pin case + { 0xFFFFFF, 0x800000 }, // just-add case + { 0x808080, 0x000000 }, // just-mul case + { 0xFFFFFF, 0x000000 } // identity case +}; + +#include "SkXfermode.h" + +static unsigned color_dist16(uint16_t a, uint16_t b) { + unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b)); + unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b)); + unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b)); + + return SkMax32(dr, SkMax32(dg, db)); +} + +static unsigned scale_dist(unsigned dist, unsigned scale) { + dist >>= 6; + dist = (dist << 2) | dist; + dist = (dist << 4) | dist; + return dist; + +// return SkAlphaMul(dist, scale); +} + +static void apply_shader(SkPaint* paint, int index) { + raster_proc proc = gRastProcs[index]; + if (proc) + { + SkPaint p; + SkLayerRasterizer* rast = new SkLayerRasterizer; + + p.setAntiAlias(true); + proc(rast, p); + paint->setRasterizer(rast)->unref(); + } + +#if 0 + SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 }; + paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref(); +#endif + paint->setColor(SK_ColorBLUE); +} + +static int gRastIndex; + +class TextEffectView : public SampleView { + SkTypeface* fFace; +public: + TextEffectView() { + fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb"); + } + + virtual ~TextEffectView() { + SkSafeUnref(fFace); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Text Effects"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->save(); +// canvas->scale(SK_Scalar1*2, SK_Scalar1*2, 0, 0); + + SkPaint paint; + + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(56)); + paint.setTypeface(SkTypeface::CreateFromName("sans-serif", + SkTypeface::kBold)); + + SkScalar x = SkIntToScalar(20); + SkScalar y = paint.getTextSize(); + + SkString str("TextEffects"); + + paint.setTypeface(fFace); + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) { + apply_shader(&paint, i); + + // paint.setMaskFilter(NULL); + // paint.setColor(SK_ColorBLACK); + +#if 1 + int index = i % SK_ARRAY_COUNT(gLightingColors); + paint.setColorFilter(SkColorFilter::CreateLightingFilter( + gLightingColors[index].fMul, + gLightingColors[index].fAdd))->unref(); +#endif + + canvas->drawText(str.c_str(), str.size(), x, y, paint); + + y += paint.getFontSpacing(); + } + + canvas->restore(); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + gRastIndex = (gRastIndex + 1) % SK_ARRAY_COUNT(gRastProcs); + this->inval(NULL); + + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TextEffectView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp new file mode 100644 index 0000000000..96e8c9a9f7 --- /dev/null +++ b/samplecode/SampleTextOnPath.cpp @@ -0,0 +1,284 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPackBits.h" +#include "SkPath.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTypeface.h" +#include "SkAvoidXfermode.h" + +#define REPEAT_COUNT 0 + +static const char gText[] = "Hamburgefons"; + +static bool gDevKern; + +static void rand_text(char text[], SkRandom& rand, size_t count) { + for (size_t i = 0; i < count; i++) { + text[i] = rand.nextU() & 0x7F; + } +} + +static SkScalar sum_widths(const SkScalar widths[], int count) { + SkScalar w = 0; + for (int i = 0; i < count; i++) { + w += widths[i]; + } + return w; +} + +static void test_measure(const SkPaint& paint) { + char text[256]; + SkScalar widths[256]; + SkRect rects[256]; + SkRect bounds; + int count = 256; + + SkRandom rand; + + for (int i = 0; i < 100; i++) { + rand_text(text, rand, 256); + paint.getTextWidths(text, count, widths, NULL); + SkDEBUGCODE(SkScalar tw0 = sum_widths(widths, count);) + paint.getTextWidths(text, count, widths, rects); + SkDEBUGCODE(SkScalar tw1 = sum_widths(widths, count);) + SkASSERT(tw0 == tw1); + + SkDEBUGCODE(SkScalar w0 = paint.measureText(text, count, NULL);) + SkDEBUGCODE(SkScalar w1 = paint.measureText(text, count, &bounds);) + SkASSERT(w0 == w1); + SkASSERT(w0 == tw0); + + SkRect r = rects[0]; + SkScalar x = 0; + for (int j = 1; j < count; j++) { + x += widths[j-1]; + rects[j].offset(x, 0); + r.join(rects[j]); + } + SkASSERT(r == bounds); + + if (r != bounds) { + printf("flags=%x i=%d [%g %g %g %g] [%g %g %g %g]\n", + paint.getFlags(), i, + SkScalarToFloat(r.fLeft), + SkScalarToFloat(r.fTop), + SkScalarToFloat(r.fRight), + SkScalarToFloat(r.fBottom), + SkScalarToFloat(bounds.fLeft), + SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), + SkScalarToFloat(bounds.fBottom)); + } + } +} + +static void test_measure() { + SkPaint paint; + + for (int i = 0; i <= SkPaint::kAllFlags; i++) { + paint.setFlags(i); + test_measure(paint); + } +} + +////////////////////////////////////////////////////////////////////////////// + +static void test_textBounds(SkCanvas* canvas) { +// canvas->scale(SK_Scalar1/2, SK_Scalar1/2); + +// canvas->rotate(SkIntToScalar(30)); + + gDevKern = !gDevKern; + + SkScalar x = SkIntToScalar(50); + SkScalar y = SkIntToScalar(150); + SkScalar w[100]; + SkRect r[100], bounds; + + SkPaint paint; + paint.setTextSize(SkIntToScalar(64)); + paint.setAntiAlias(true); + paint.setDevKernText(gDevKern); + + (void)paint.measureText(gText, strlen(gText), &bounds, NULL); + paint.setColor(SK_ColorGREEN); + bounds.offset(x, y); + canvas->drawRect(bounds, paint); + + int count = paint.getTextWidths(gText, strlen(gText), w, r); + + paint.setColor(SK_ColorRED); + for (int i = 0; i < count; i++) { + r[i].offset(x, y); + canvas->drawRect(r[i], paint); + x += w[i]; + } + x = SkIntToScalar(50); + paint.setColor(gDevKern ? SK_ColorDKGRAY : SK_ColorBLACK); + canvas->drawText(gText, strlen(gText), x, y, paint); +} + +static void create_src(SkBitmap* bitmap, SkBitmap::Config config) { + bitmap->setConfig(config, 100, 100); + bitmap->allocPixels(); + bitmap->eraseColor(0); + + SkCanvas canvas(*bitmap); + SkPaint paint; + + paint.setAntiAlias(true); + canvas.drawCircle(SkIntToScalar(50), SkIntToScalar(50), + SkIntToScalar(50), paint); +} + +static void blur(SkBitmap* dst, const SkBitmap& src, SkScalar radius) { + *dst = src; +} + +static void test_bitmap_blur(SkCanvas* canvas) { + SkBitmap src, dst; + + create_src(&src, SkBitmap::kARGB_8888_Config); + blur(&dst, src, SkIntToScalar(4)); + + SkPaint paint; + + paint.setColor(SK_ColorRED); + + canvas->drawBitmap(dst, SkIntToScalar(30), SkIntToScalar(60), &paint); +} + +static SkScalar getpathlen(const SkPath& path) { + SkPathMeasure meas(path, false); + return meas.getLength(); +} + +static void test_textpathmatrix(SkCanvas* canvas) { + SkPaint paint; + SkPath path; + SkMatrix matrix; + + path.moveTo(SkIntToScalar(200), SkIntToScalar(300)); + path.quadTo(SkIntToScalar(400), SkIntToScalar(100), + SkIntToScalar(600), SkIntToScalar(300)); + + paint.setAntiAlias(true); + + paint.setStyle(SkPaint::kStroke_Style); + // canvas->drawPath(path, paint); + paint.setStyle(SkPaint::kFill_Style); + paint.setTextSize(SkIntToScalar(48)); + paint.setTextAlign(SkPaint::kRight_Align); + + const char* text = "Reflection"; + size_t len = strlen(text); + SkScalar pathLen = getpathlen(path); + + canvas->drawTextOnPath(text, len, path, NULL, paint); + + paint.setColor(SK_ColorRED); + matrix.setScale(-SK_Scalar1, SK_Scalar1); + matrix.postTranslate(pathLen, 0); + canvas->drawTextOnPath(text, len, path, &matrix, paint); + + paint.setColor(SK_ColorBLUE); + matrix.setScale(SK_Scalar1, -SK_Scalar1); + canvas->drawTextOnPath(text, len, path, &matrix, paint); + + paint.setColor(SK_ColorGREEN); + matrix.setScale(-SK_Scalar1, -SK_Scalar1); + matrix.postTranslate(pathLen, 0); + canvas->drawTextOnPath(text, len, path, &matrix, paint); +} + +class TextOnPathView : public SampleView { +public: + SkPath fPath; + SkScalar fHOffset; + + TextOnPathView() { + SkRect r; + r.set(SkIntToScalar(100), SkIntToScalar(100), + SkIntToScalar(300), SkIntToScalar(300)); + fPath.addOval(r); + fPath.offset(SkIntToScalar(200), 0); + + fHOffset = SkIntToScalar(50); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Text On Path"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(50)); + + for (int j = 0; j < REPEAT_COUNT; j++) { + SkScalar x = fHOffset; + + paint.setColor(SK_ColorBLACK); + canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath, + x, paint.getTextSize()/2, paint); + + paint.setColor(SK_ColorRED); + canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath, + x + SkIntToScalar(50), 0, paint); + + paint.setColor(SK_ColorBLUE); + canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath, + x + SkIntToScalar(100), -paint.getTextSize()/2, paint); + } + + paint.setColor(SK_ColorGREEN); + paint.setStyle(SkPaint::kStroke_Style); +// canvas->drawPath(fPath, paint); + + canvas->translate(0, SkIntToScalar(100)); + test_textpathmatrix(canvas); + + if (REPEAT_COUNT > 1) + this->inval(NULL); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fHints += 1; + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } + + virtual bool onClick(Click* click) { + return this->INHERITED::onClick(click); + } + +private: + int fHints; + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { + return new TextOnPathView; +} + +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp new file mode 100755 index 0000000000..be000f95f2 --- /dev/null +++ b/samplecode/SampleTextureDomain.cpp @@ -0,0 +1,80 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkDevice.h" + +namespace { +SkBitmap make_bitmap() { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config , 5, 5); + bm.allocPixels(); + + for (int y = 0; y < bm.height(); y++) { + uint32_t* p = bm.getAddr32(0, y); + for (int x = 0; x < bm.width(); x++) { + p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK; + } + } + bm.unlockPixels(); + return bm; +} +} // unnamed namespace + +class TextureDomainView : public SampleView { + SkBitmap fBM; + +public: + TextureDomainView(){ + fBM = make_bitmap(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Texture Domian"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkIRect srcRect; + SkRect dstRect; + SkPaint paint; + paint.setFilterBitmap(true); + + // Test that bitmap draws from malloc-backed bitmaps respect + // the constrained texture domain. + srcRect.setXYWH(1, 1, 3, 3); + dstRect.setXYWH(5.0f, 5.0f, 305.0f, 305.0f); + canvas->drawBitmapRect(fBM, &srcRect, dstRect, &paint); + + // Test that bitmap draws across separate devices also respect + // the constrainted texture domain. + // Note: GPU-backed bitmaps follow a different rendering path + // when copying from one GPU device to another. + SkRefPtr<SkDevice> primaryDevice(canvas->getDevice()); + SkRefPtr<SkDevice> secondDevice(canvas->createDevice( + SkBitmap::kARGB_8888_Config, 5, 5, true, true)); + secondDevice->unref(); + SkCanvas secondCanvas(secondDevice.get()); + + srcRect.setXYWH(1, 1, 3, 3); + dstRect.setXYWH(1.0f, 1.0f, 3.0f, 3.0f); + secondCanvas.drawBitmapRect(fBM, &srcRect, dstRect, &paint); + + SkBitmap deviceBitmap = secondDevice->accessBitmap(false); + + srcRect.setXYWH(1, 1, 3, 3); + dstRect.setXYWH(405.0f, 5.0f, 305.0f, 305.0f); + canvas->drawBitmapRect(deviceBitmap, &srcRect, dstRect, &paint); + } +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TextureDomainView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp new file mode 100644 index 0000000000..4752ed1eba --- /dev/null +++ b/samplecode/SampleTiling.cpp @@ -0,0 +1,162 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkPicture.h" +#include "SkTypeface.h" + +// effects +#include "SkGradientShader.h" +#include "SkUnitMappers.h" +#include "SkBlurDrawLooper.h" + +static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) { + bm->setConfig(config, w, h); + bm->allocPixels(); + bm->eraseColor(0); + + SkCanvas canvas(*bm); + SkPoint pts[] = { { 0, 0 }, { SkIntToScalar(w), SkIntToScalar(h) } }; + SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; + SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 }; + SkPaint paint; + + SkUnitMapper* um = NULL; + + um = new SkCosineMapper; +// um = new SkDiscreteMapper(12); + + SkAutoUnref au(um); + + paint.setDither(true); + paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos, + SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref(); + canvas.drawPaint(paint); +} + +static void setup(SkPaint* paint, const SkBitmap& bm, bool filter, + SkShader::TileMode tmx, SkShader::TileMode tmy) { + SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy); + paint->setShader(shader)->unref(); + paint->setFilterBitmap(filter); +} + +static const SkBitmap::Config gConfigs[] = { + SkBitmap::kARGB_8888_Config, + SkBitmap::kRGB_565_Config, + SkBitmap::kARGB_4444_Config +}; +static const int gWidth = 32; +static const int gHeight = 32; + +class TilingView : public SampleView { + SkPicture fTextPicture; + SkBlurDrawLooper fLooper; +public: + TilingView() + : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2), + 0x88000000) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) { + makebm(&fTexture[i], gConfigs[i], gWidth, gHeight); + } + } + + SkBitmap fTexture[SK_ARRAY_COUNT(gConfigs)]; + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Tiling"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) }; + + static const char* gConfigNames[] = { "8888", "565", "4444" }; + + static const bool gFilters[] = { false, true }; + static const char* gFilterNames[] = { "point", "bilinear" }; + + static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode }; + static const char* gModeNames[] = { "C", "R", "M" }; + + SkScalar y = SkIntToScalar(24); + SkScalar x = SkIntToScalar(10); + + SkCanvas* textCanvas = NULL; + if (fTextPicture.width() == 0) { + textCanvas = fTextPicture.beginRecording(1000, 1000); + } + + if (textCanvas) { + for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) { + for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) { + SkPaint p; + SkString str; + p.setAntiAlias(true); + p.setDither(true); + p.setLooper(&fLooper); + str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]); + + p.setTextAlign(SkPaint::kCenter_Align); + textCanvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p); + + x += r.width() * 4 / 3; + } + } + } + + y += SkIntToScalar(16); + + for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) { + for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) { + x = SkIntToScalar(10); + for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) { + for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) { + SkPaint paint; + setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]); + paint.setDither(true); + + canvas->save(); + canvas->translate(x, y); + canvas->drawRect(r, paint); + canvas->restore(); + + x += r.width() * 4 / 3; + } + } + if (textCanvas) { + SkPaint p; + SkString str; + p.setAntiAlias(true); + p.setLooper(&fLooper); + str.printf("%s, %s", gConfigNames[i], gFilterNames[j]); + textCanvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p); + } + + y += r.height() * 4 / 3; + } + } + + canvas->drawPicture(fTextPicture); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TilingView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTinyBitmap.cpp b/samplecode/SampleTinyBitmap.cpp new file mode 100644 index 0000000000..0841474db0 --- /dev/null +++ b/samplecode/SampleTinyBitmap.cpp @@ -0,0 +1,76 @@ +#include "SampleCode.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkUtils.h" + +static SkBitmap make_bitmap() { + SkBitmap bm; + const int N = 1; + SkColorTable* ctable = new SkColorTable(N); + + SkPMColor* c = ctable->lockColors(); + for (int i = 0; i < N; i++) { + c[i] = SkPackARGB32(0x80, 0x80, 0, 0); + } + ctable->unlockColors(true); + bm.setConfig(SkBitmap::kIndex8_Config, 1, 1); + bm.allocPixels(ctable); + ctable->unref(); + + bm.lockPixels(); + for (int y = 0; y < bm.height(); y++) { + uint8_t* p = bm.getAddr8(0, y); + for (int x = 0; x < bm.width(); x++) { + p[x] = 0; + } + } + bm.unlockPixels(); + return bm; +} + +class TinyBitmapView : public SampleView { + SkBitmap fBM; +public: + TinyBitmapView() { + fBM = make_bitmap(); + this->setBGColor(0xFFDDDDDD); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "TinyBitmap"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) { + SkAutoLockPixels alp(*bm); // needed for ctable + bm->setIsOpaque(isOpaque); + SkColorTable* ctable = bm->getColorTable(); + if (ctable) { + ctable->setIsOpaque(isOpaque); + } + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkShader* s = SkShader::CreateBitmapShader(fBM, SkShader::kRepeat_TileMode, + SkShader::kMirror_TileMode); + SkPaint paint; + paint.setShader(s)->unref(); + canvas->drawPaint(paint); + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TinyBitmapView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTriangles.cpp b/samplecode/SampleTriangles.cpp new file mode 100644 index 0000000000..be9da8fb1d --- /dev/null +++ b/samplecode/SampleTriangles.cpp @@ -0,0 +1,118 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkConcaveToTriangles.h" + +#define SIZE SkIntToScalar(150) + +typedef void (*PathProc)(SkPath*); + +static void make_path0(SkPath* path) { + SkRect r; + r.set(0, 0, SIZE, SIZE); + path->addRect(r); +} + +static void make_path1(SkPath* path) { + SkRect r; + r.set(0, 0, SIZE, SIZE); + path->addRoundRect(r, SIZE/4, SIZE/4); +} + +static void make_path2(SkPath* path) { + SkRect r; + r.set(0, 0, SIZE, SIZE); + path->addOval(r); +} + +static const PathProc gProcs[] = { + make_path0, + make_path1, + make_path2, +}; + +#define COUNT_PROCS SK_ARRAY_COUNT(gProcs) + +class TriangleView : public SkView { +public: + SkPath fPaths[COUNT_PROCS]; + + TriangleView() { + for (size_t i = 0; i < COUNT_PROCS; i++) { + gProcs[i](&fPaths[i]); + } + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Triangles"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorGRAY); + } + + static void draw_path(SkCanvas* canvas, const SkPaint& pathPaint, + const SkPath& path, const SkPaint& triPaint) { + canvas->drawPath(path, pathPaint); + + int n = path.getPoints(NULL, 0); + SkPoint* pts = new SkPoint[n]; + path.getPoints(pts, n); + + SkTDArray<SkPoint> triangles; + if (SkConcaveToTriangles(n, pts, &triangles)) { + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, + triangles.count(), triangles.begin(), NULL, + NULL, NULL, NULL, 0, triPaint); + } + + SkPaint paint; + paint.setColor(SK_ColorGREEN); + paint.setStrokeWidth(SkIntToScalar(4)); + canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, paint); + delete[] pts; + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + canvas->translate(SIZE/2, SIZE/2); + + SkPaint pathPaint, triPaint; + + pathPaint.setColor(SK_ColorBLUE); + pathPaint.setStrokeWidth(SIZE / 12); + + triPaint.setColor(SK_ColorRED); + triPaint.setStyle(SkPaint::kStroke_Style); + + for (size_t i = 0; i < COUNT_PROCS; i++) { + pathPaint.setStyle(SkPaint::kFill_Style); + draw_path(canvas, pathPaint, fPaths[i], triPaint); + + canvas->save(); + canvas->translate(0, SIZE * 6 / 5); + + pathPaint.setStyle(SkPaint::kStroke_Style); + draw_path(canvas, pathPaint, fPaths[i], triPaint); + + canvas->restore(); + canvas->translate(SIZE * 6 / 5, 0); + } + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TriangleView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleTypeface.cpp b/samplecode/SampleTypeface.cpp new file mode 100644 index 0000000000..63f1d5adba --- /dev/null +++ b/samplecode/SampleTypeface.cpp @@ -0,0 +1,128 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkTypeface.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "Sk1DPathEffect.h" +#include "SkCornerPathEffect.h" +#include "SkPathMeasure.h" +#include "SkRandom.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkDither.h" +#include "SkTypefaceCache.h" + +static int dither_4444(int x) { + return ((x << 1) - ((x >> 4 << 4) | (x >> 4))) >> 4; +} + +/** Ensure that the max of the original and dithered value (for alpha) is always + >= any other dithered value. We apply this "max" in colorpriv.h when we + predither down to 4444, to be sure that we stay in legal premultiplied form + */ +static void test_4444_dither() { + int buckets[16]; + sk_bzero(buckets, sizeof(buckets)); + + for (int a = 0; a <= 0xFF; a++) { + int da = dither_4444(a); + int maxa = SkMax32(a >> 4, da); + // SkDebugf("--- %02X %X\n", a, da); + buckets[da] += 1; + for (int c = 0; c <= a; c++) { + int dc = dither_4444(c); + if (maxa < dc) { + SkDebugf("------------ error a=%d da=%d c=%d dc=%d\n", a, da, + c, dc); + } + } + } + for (int i = 0; i < 16; i++) { + // SkDebugf("[%d] = %d\n", i, buckets[i]); + } +} + +static const struct { + const char* fName; + SkTypeface::Style fStyle; +} gFaces[] = { + { "sans-serif", SkTypeface::kNormal }, + { "sans-serif", SkTypeface::kBold }, + { "sans-serif", SkTypeface::kItalic }, + { "sans-serif", SkTypeface::kBoldItalic }, + { "serif", SkTypeface::kNormal }, + { "serif", SkTypeface::kBold }, + { "serif", SkTypeface::kItalic }, + { "serif", SkTypeface::kBoldItalic }, + { "monospace", SkTypeface::kNormal }, + { "monospace", SkTypeface::kBold }, + { "monospace", SkTypeface::kItalic }, + { "monospace", SkTypeface::kBoldItalic }, +}; + +static const int gFaceCount = SK_ARRAY_COUNT(gFaces); + +class TypefaceView : public SampleView { + SkTypeface* fFaces[gFaceCount]; + +public: + TypefaceView() { + test_4444_dither(); + for (int i = 0; i < gFaceCount; i++) { + fFaces[i] = SkTypeface::CreateFromName(gFaces[i].fName, + gFaces[i].fStyle); + } + + this->setBGColor(0xFFDDDDDD); + } + + virtual ~TypefaceView() { + for (int i = 0; i < gFaceCount; i++) { + SkSafeUnref(fFaces[i]); + } + + SkTypefaceCache::Dump(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Typefaces"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(30)); + + const char* text = "Hamburgefons"; + const size_t textLen = strlen(text); + + SkScalar x = SkIntToScalar(10); + SkScalar dy = paint.getFontMetrics(NULL); + SkScalar y = dy; + + paint.setLinearText(true); + for (int i = 0; i < gFaceCount; i++) { + paint.setTypeface(fFaces[i]); + canvas->drawText(text, textLen, x, y, paint); + y += dy; + } + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new TypefaceView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleUnitMapper.cpp b/samplecode/SampleUnitMapper.cpp new file mode 100644 index 0000000000..b20aece316 --- /dev/null +++ b/samplecode/SampleUnitMapper.cpp @@ -0,0 +1,157 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkUnitMappers.h" +#include "SkCubicInterval.h" + +#include "SkWidgetViews.h" + +static SkStaticTextView* make_textview(SkView* parent, + const SkRect& bounds, + const SkPaint& paint) { + SkStaticTextView* view = new SkStaticTextView; + view->setMode(SkStaticTextView::kFixedSize_Mode); + view->setPaint(paint); + view->setVisibleP(true); + view->setSize(bounds.width(), bounds.height()); + view->setLoc(bounds.fLeft, bounds.fTop); + parent->attachChildToFront(view)->unref(); + return view; +} + +static void set_scalar(SkStaticTextView* view, SkScalar value) { + SkString str; + str.appendScalar(value); + view->setText(str); +} + +class UnitMapperView : public SampleView { + SkPoint fPts[4]; + SkMatrix fMatrix; + SkStaticTextView* fViews[4]; + + void setViews() { + set_scalar(fViews[0], fPts[1].fX); + set_scalar(fViews[1], fPts[1].fY); + set_scalar(fViews[2], fPts[2].fX); + set_scalar(fViews[3], fPts[2].fY); + } + +public: + UnitMapperView() { + fPts[0].set(0, 0); + fPts[1].set(SK_Scalar1 / 3, SK_Scalar1 / 3); + fPts[2].set(SK_Scalar1 * 2 / 3, SK_Scalar1 * 2 / 3); + fPts[3].set(SK_Scalar1, SK_Scalar1); + + fMatrix.setScale(SK_Scalar1 * 200, -SK_Scalar1 * 200); + fMatrix.postTranslate(SkIntToScalar(100), SkIntToScalar(300)); + + SkRect r = { + SkIntToScalar(350), SkIntToScalar(100), + SkIntToScalar(500), SkIntToScalar(130) + }; + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(SkIntToScalar(25)); + for (int i = 0; i < 4; i++) { + fViews[i] = make_textview(this, r, paint); + r.offset(0, r.height()); + } + this->setViews(); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "UnitMapper"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(0xFF8888FF); + + SkRect r = { 0, 0, SK_Scalar1, SK_Scalar1 }; + + canvas->concat(fMatrix); + canvas->drawRect(r, paint); + + paint.setColor(SK_ColorBLACK); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(0); + paint.setStrokeCap(SkPaint::kRound_Cap); + + SkPath path; + path.moveTo(fPts[0]); + path.cubicTo(fPts[1], fPts[2], fPts[3]); + canvas->drawPath(path, paint); + + paint.setColor(SK_ColorRED); + paint.setStrokeWidth(0); + canvas->drawLine(0, 0, SK_Scalar1, SK_Scalar1, paint); + + paint.setColor(SK_ColorBLUE); + paint.setStrokeWidth(SK_Scalar1 / 60); + for (int i = 0; i < 50; i++) { + SkScalar x = i * SK_Scalar1 / 49; + canvas->drawPoint(x, SkEvalCubicInterval(&fPts[1], x), paint); + } + + paint.setStrokeWidth(SK_Scalar1 / 20); + paint.setColor(SK_ColorGREEN); + canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, &fPts[1], paint); + } + + SkPoint invertPt(SkScalar x, SkScalar y) { + SkPoint pt; + SkMatrix m; + fMatrix.invert(&m); + m.mapXY(x, y, &pt); + return pt; + } + + int hittest(SkScalar x, SkScalar y) { + SkPoint target = { x, y }; + SkPoint pts[2] = { fPts[1], fPts[2] }; + fMatrix.mapPoints(pts, 2); + for (int i = 0; i < 2; i++) { + if (SkPoint::Distance(pts[i], target) < SkIntToScalar(4)) { + return i + 1; + } + } + return -1; + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fDragIndex = hittest(x, y); + return fDragIndex >= 0 ? new Click(this) : NULL; + } + + virtual bool onClick(Click* click) { + if (fDragIndex >= 0) { + fPts[fDragIndex] = invertPt(click->fCurr.fX, click->fCurr.fY); + this->setViews(); + this->inval(NULL); + return true; + } + return false; + } + +private: + int fDragIndex; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new UnitMapperView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp new file mode 100644 index 0000000000..74e757f43e --- /dev/null +++ b/samplecode/SampleVertices.cpp @@ -0,0 +1,230 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +#include "SkOSFile.h" +#include "SkStream.h" +#include "SkNinePatch.h" + +static SkShader* make_shader0(SkIPoint* size) { + SkBitmap bm; + size->set(2, 2); + bm.setConfig(SkBitmap::kARGB_8888_Config, size->fX, size->fY); + SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80); + SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff); + bm.allocPixels(); + bm.eraseColor(color0); + bm.lockPixels(); + uint32_t* pixels = (uint32_t*) bm.getPixels(); + pixels[0] = pixels[2] = color0; + pixels[1] = pixels[3] = color1; + bm.unlockPixels(); + + return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); +} + +static SkShader* make_shader1(const SkIPoint& size) { + SkPoint pts[] = { { 0, 0 }, + { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } }; + SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED }; + return SkGradientShader::CreateLinear(pts, colors, NULL, + SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL); +} + +class VerticesView : public SampleView { + SkShader* fShader0; + SkShader* fShader1; + +public: + VerticesView() { + SkIPoint size; + + fShader0 = make_shader0(&size); + fShader1 = make_shader1(size); + + make_strip(&fRecs[0], size.fX, size.fY); + make_fan(&fRecs[1], size.fX, size.fY); + make_tris(&fRecs[2]); + + fScale = SK_Scalar1; + + this->setBGColor(SK_ColorGRAY); + } + + virtual ~VerticesView() { + SkSafeUnref(fShader0); + SkSafeUnref(fShader1); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) + { + SkString str("Vertices"); + SampleCode::TitleR(evt, str.c_str()); + return true; + } + return this->INHERITED::onQuery(evt); + } + + SkScalar fScale; + + virtual void onDrawContent(SkCanvas* canvas) { + SkPaint paint; + paint.setDither(true); + paint.setFilterBitmap(true); + + for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) { + canvas->save(); + + paint.setShader(NULL); + canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, + fRecs[i].fVerts, fRecs[i].fTexs, + NULL, NULL, NULL, 0, paint); + + canvas->translate(SkIntToScalar(250), 0); + + paint.setShader(fShader0); + canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, + fRecs[i].fVerts, fRecs[i].fTexs, + NULL, NULL, NULL, 0, paint); + + canvas->translate(SkIntToScalar(250), 0); + + paint.setShader(fShader1); + canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, + fRecs[i].fVerts, fRecs[i].fTexs, + NULL, NULL, NULL, 0, paint); + canvas->restore(); + + canvas->translate(0, SkIntToScalar(250)); + } + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + return new Click(this); + } + + virtual bool onClick(Click* click) { + // fCurrX = click->fICurr.fX; + // fCurrY = click->fICurr.fY; + this->inval(NULL); + return true; + } + +private: + struct Rec { + SkCanvas::VertexMode fMode; + int fCount; + SkPoint* fVerts; + SkPoint* fTexs; + + Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {} + ~Rec() { delete[] fVerts; delete[] fTexs; } + }; + + void make_tris(Rec* rec) { + int n = 10; + SkRandom rand; + + rec->fMode = SkCanvas::kTriangles_VertexMode; + rec->fCount = n * 3; + rec->fVerts = new SkPoint[rec->fCount]; + + for (int i = 0; i < n; i++) { + SkPoint* v = &rec->fVerts[i*3]; + for (int j = 0; j < 3; j++) { + v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250); + } + } + } + + void make_fan(Rec* rec, int texWidth, int texHeight) { + const SkScalar tx = SkIntToScalar(texWidth); + const SkScalar ty = SkIntToScalar(texHeight); + const int n = 24; + + rec->fMode = SkCanvas::kTriangleFan_VertexMode; + rec->fCount = n + 2; + rec->fVerts = new SkPoint[rec->fCount]; + rec->fTexs = new SkPoint[rec->fCount]; + + SkPoint* v = rec->fVerts; + SkPoint* t = rec->fTexs; + + v[0].set(0, 0); + t[0].set(0, 0); + for (int i = 0; i < n; i++) { + SkScalar cos; + SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos); + v[i+1].set(cos, sin); + t[i+1].set(i*tx/n, ty); + } + v[n+1] = v[1]; + t[n+1].set(tx, ty); + + SkMatrix m; + m.setScale(SkIntToScalar(100), SkIntToScalar(100)); + m.postTranslate(SkIntToScalar(110), SkIntToScalar(110)); + m.mapPoints(v, rec->fCount); + } + + void make_strip(Rec* rec, int texWidth, int texHeight) { + const SkScalar tx = SkIntToScalar(texWidth); + const SkScalar ty = SkIntToScalar(texHeight); + const int n = 24; + + rec->fMode = SkCanvas::kTriangleStrip_VertexMode; + rec->fCount = 2 * (n + 1); + rec->fVerts = new SkPoint[rec->fCount]; + rec->fTexs = new SkPoint[rec->fCount]; + + SkPoint* v = rec->fVerts; + SkPoint* t = rec->fTexs; + + for (int i = 0; i < n; i++) { + SkScalar cos; + SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos); + v[i*2 + 0].set(cos/2, sin/2); + v[i*2 + 1].set(cos, sin); + + t[i*2 + 0].set(tx * i / n, ty); + t[i*2 + 1].set(tx * i / n, 0); + } + v[2*n + 0] = v[0]; + v[2*n + 1] = v[1]; + + t[2*n + 0].set(tx, ty); + t[2*n + 1].set(tx, 0); + + SkMatrix m; + m.setScale(SkIntToScalar(100), SkIntToScalar(100)); + m.postTranslate(SkIntToScalar(110), SkIntToScalar(110)); + m.mapPoints(v, rec->fCount); + } + + Rec fRecs[3]; + + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new VerticesView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleWarp.cpp b/samplecode/SampleWarp.cpp new file mode 100644 index 0000000000..bf4ef0dbed --- /dev/null +++ b/samplecode/SampleWarp.cpp @@ -0,0 +1,467 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkImageDecoder.h" + +#include "SkBlurMaskFilter.h" +#include "SkTableMaskFilter.h" + +#define kNearlyZero (SK_Scalar1 / 8092) + +static void test_bigblur(SkCanvas* canvas) { + canvas->drawColor(SK_ColorBLACK); + + SkBitmap orig, mask; + SkImageDecoder::DecodeFile("/skimages/app_icon.png", &orig); + + SkMaskFilter* mf = SkBlurMaskFilter::Create(8, SkBlurMaskFilter::kNormal_BlurStyle); + SkPaint paint; + paint.setMaskFilter(mf)->unref(); + SkIPoint offset; + orig.extractAlpha(&mask, &paint, &offset); + + paint.setColor(0xFFBB8800); + paint.setColor(SK_ColorWHITE); + + int i; + canvas->save(); + float gamma = 0.8; + for (i = 0; i < 5; i++) { + paint.setMaskFilter(SkTableMaskFilter::CreateGamma(gamma))->unref(); + canvas->drawBitmap(mask, 0, 0, &paint); + paint.setMaskFilter(NULL); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); + gamma -= 0.1; + canvas->translate(120, 0); + } + canvas->restore(); + canvas->translate(0, 160); + + for (i = 0; i < 5; i++) { + paint.setMaskFilter(SkTableMaskFilter::CreateClip(i*30, 255 - 20))->unref(); + canvas->drawBitmap(mask, 0, 0, &paint); + paint.setMaskFilter(NULL); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); + canvas->translate(120, 0); + } + +#if 0 + paint.setColor(0xFFFFFFFF); + canvas->drawBitmap(mask, 0, 0, &paint); + paint.setMaskFilter(NULL); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); + + canvas->translate(120, 0); + + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); + + canvas->translate(120, 0); + + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); + + canvas->translate(120, 0); + + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); + + canvas->translate(120, 0); + + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(mask, 0, 0, &paint); + canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint); +#endif +} + +#include "SkMeshUtils.h" + +static SkPoint SkMakePoint(SkScalar x, SkScalar y) { + SkPoint pt; + pt.set(x, y); + return pt; +} + +static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) { + return SkMakePoint(SkScalarInterp(a.fX, b.fX, t), + SkScalarInterp(a.fY, b.fY, t)); +} + +#include "SkBoundaryPatch.h" + +static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0, + SkScalar x3, SkScalar y3, SkScalar scale = 1) { + SkPoint tmp, tmp2; + + pts[0].set(x0, y0); + pts[3].set(x3, y3); + + tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3); + tmp2 = pts[0] - tmp; + tmp2.rotateCW(); + tmp2.scale(scale); + pts[1] = tmp + tmp2; + + tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3); + tmp2 = pts[3] - tmp; + tmp2.rotateCW(); + tmp2.scale(scale); + pts[2] = tmp + tmp2; +} + +static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) { + SkCubicBoundary cubic; + set_cubic(cubic.fPts + 0, 0, 0, 100, 0, scale); + set_cubic(cubic.fPts + 3, 100, 0, 100, 100, scale); + set_cubic(cubic.fPts + 6, 100, 100, 0, 100, -scale); + set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0); + + SkBoundaryPatch patch; + patch.setBoundary(&cubic); + + const int Rows = 16; + const int Cols = 16; + SkPoint pts[Rows * Cols]; + patch.evalPatch(pts, Rows, Cols); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setFilterBitmap(true); + paint.setStrokeWidth(1); + paint.setStrokeCap(SkPaint::kRound_Cap); + + canvas->translate(50, 50); + canvas->scale(3, 3); + + SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint); +} + +static void test_drag(SkCanvas* canvas, const SkBitmap& bm, + const SkPoint& p0, const SkPoint& p1) { + SkCubicBoundary cubic; + set_cubic(cubic.fPts + 0, 0, 0, 100, 0, 0); + set_cubic(cubic.fPts + 3, 100, 0, 100, 100, 0); + set_cubic(cubic.fPts + 6, 100, 100, 0, 100, 0); + set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0); + +#if 0 + cubic.fPts[1] += p1 - p0; + cubic.fPts[2] += p1 - p0; +#else + SkScalar dx = p1.fX - p0.fX; + if (dx > 0) dx = 0; + SkScalar dy = p1.fY - p0.fY; + if (dy > 0) dy = 0; + + cubic.fPts[1].fY += dy; + cubic.fPts[2].fY += dy; + cubic.fPts[10].fX += dx; + cubic.fPts[11].fX += dx; +#endif + + SkBoundaryPatch patch; + patch.setBoundary(&cubic); + + const int Rows = 16; + const int Cols = 16; + SkPoint pts[Rows * Cols]; + patch.evalPatch(pts, Rows, Cols); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setFilterBitmap(true); + paint.setStrokeWidth(1); + paint.setStrokeCap(SkPaint::kRound_Cap); + + canvas->translate(50, 50); + canvas->scale(3, 3); + + SkAutoCanvasRestore acr(canvas, true); + + SkRect r = { 0, 0, 100, 100 }; + canvas->clipRect(r); + SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +class Mesh { +public: + Mesh(); + ~Mesh(); + + Mesh& operator=(const Mesh& src); + + void init(const SkRect& bounds, int rows, int cols, + const SkRect& texture); + + const SkRect& bounds() const { return fBounds; } + + int rows() const { return fRows; } + int cols() const { return fCols; } + SkPoint& pt(int row, int col) { + return fPts[row * (fRows + 1) + col]; + } + + void draw(SkCanvas*, const SkPaint&); + void drawWireframe(SkCanvas* canvas, const SkPaint& paint); + +private: + SkRect fBounds; + int fRows, fCols; + SkPoint* fPts; + SkPoint* fTex; // just points into fPts, not separately allocated + int fCount; + uint16_t* fIndices; + int fIndexCount; +}; + +Mesh::Mesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {} + +Mesh::~Mesh() { + delete[] fPts; + delete[] fIndices; +} + +Mesh& Mesh::operator=(const Mesh& src) { + delete[] fPts; + delete[] fIndices; + + fBounds = src.fBounds; + fRows = src.fRows; + fCols = src.fCols; + + fCount = src.fCount; + fPts = new SkPoint[fCount * 2]; + fTex = fPts + fCount; + memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint)); + + delete[] fIndices; + fIndexCount = src.fIndexCount; + fIndices = new uint16_t[fIndexCount]; + memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t)); + + return *this; +} + +void Mesh::init(const SkRect& bounds, int rows, int cols, + const SkRect& texture) { + SkASSERT(rows > 0 && cols > 0); + + fBounds = bounds; + fRows = rows; + fCols = cols; + + delete[] fPts; + fCount = (rows + 1) * (cols + 1); + fPts = new SkPoint[fCount * 2]; + fTex = fPts + fCount; + + delete[] fIndices; + fIndexCount = rows * cols * 6; + fIndices = new uint16_t[fIndexCount]; + + SkPoint* pts = fPts; + const SkScalar dx = bounds.width() / rows; + const SkScalar dy = bounds.height() / cols; + SkPoint* tex = fTex; + const SkScalar dtx = texture.width() / rows; + const SkScalar dty = texture.height() / cols; + uint16_t* idx = fIndices; + int index = 0; + for (int y = 0; y <= cols; y++) { + for (int x = 0; x <= rows; x++) { + pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy); + pts += 1; + tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty); + tex += 1; + + if (y < cols && x < rows) { + *idx++ = index; + *idx++ = index + rows + 1; + *idx++ = index + 1; + + *idx++ = index + 1; + *idx++ = index + rows + 1; + *idx++ = index + rows + 2; + + index += 1; + } + } + index += 1; + } +} + +void Mesh::draw(SkCanvas* canvas, const SkPaint& paint) { + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount, + fPts, fTex, NULL, NULL, fIndices, fIndexCount, + paint); +} + +void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) { + canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount, + fPts, NULL, NULL, NULL, fIndices, fIndexCount, + paint); +} + +/////////////////////////////////////////////////////////////////////////////// + +class WarpView : public SkView { + Mesh fMesh, fOrig; + SkBitmap fBitmap; + SkMatrix fMatrix, fInverse; +public: + WarpView() { + SkBitmap bm; +// SkImageDecoder::DecodeFile("/skimages/marker.png", &bm); + SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm); + // SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm); + fBitmap = bm; + + SkRect bounds, texture; + texture.set(0, 0, SkIntToScalar(fBitmap.width()), + SkIntToScalar(fBitmap.height())); + bounds = texture; + +// fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture); + fMesh.init(bounds, fBitmap.width()/16, fBitmap.height()/16, texture); + fOrig = fMesh; + + fP0.set(0, 0); + fP1 = fP0; + + fMatrix.setScale(2, 2); + fMatrix.invert(&fInverse); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Warp"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + static SkPoint apply_warp(const SkVector& drag, SkScalar dragLength, + const SkPoint& dragStart, const SkPoint& dragCurr, + const SkPoint& orig) { + SkVector delta = orig - dragCurr; + SkScalar length = SkPoint::Normalize(&delta); + if (length <= kNearlyZero) { + return orig; + } + + const SkScalar period = 20; + const SkScalar mag = dragLength / 3; + + SkScalar d = length / (period); + d = mag * SkScalarSin(d) / d; + SkScalar dx = delta.fX * d; + SkScalar dy = delta.fY * d; + SkScalar px = orig.fX + dx; + SkScalar py = orig.fY + dy; + return SkPoint::Make(px, py); + } + + static SkPoint apply_warp2(const SkVector& drag, SkScalar dragLength, + const SkPoint& dragStart, const SkPoint& dragCurr, + const SkPoint& orig) { + SkVector delta = orig - dragCurr; + SkScalar length = SkPoint::Normalize(&delta); + if (length <= kNearlyZero) { + return orig; + } + + const SkScalar period = 10 + dragLength/4; + const SkScalar mag = dragLength / 3; + + SkScalar d = length / (period); + if (d > SK_ScalarPI) { + d = SK_ScalarPI; + } + + d = -mag * SkScalarSin(d); + + SkScalar dx = delta.fX * d; + SkScalar dy = delta.fY * d; + SkScalar px = orig.fX + dx; + SkScalar py = orig.fY + dy; + return SkPoint::Make(px, py); + } + + typedef SkPoint (*WarpProc)(const SkVector& drag, SkScalar dragLength, + const SkPoint& dragStart, const SkPoint& dragCurr, + const SkPoint& orig); + + void warp(const SkPoint& p0, const SkPoint& p1) { + WarpProc proc = apply_warp2; + SkPoint delta = p1 - p0; + SkScalar length = SkPoint::Normalize(&delta); + for (int y = 0; y < fMesh.rows(); y++) { + for (int x = 0; x < fMesh.cols(); x++) { + fMesh.pt(x, y) = proc(delta, length, p0, p1, fOrig.pt(x, y)); + } + } + fP0 = p0; + fP1 = p1; + } + + virtual void onDraw(SkCanvas* canvas) { + canvas->drawColor(SK_ColorLTGRAY); + // test_bigblur(canvas); return; + + canvas->concat(fMatrix); + + SkPaint paint; + paint.setFilterBitmap(true); + paint.setShader(SkShader::CreateBitmapShader(fBitmap, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode))->unref(); + fMesh.draw(canvas, paint); //return; + + paint.setShader(NULL); + paint.setColor(SK_ColorRED); + fMesh.draw(canvas, paint); + + // test_drag(canvas, fBitmap, fP0, fP1); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + return new Click(this); + } + + virtual bool onClick(Click* click) { + SkPoint pts[2] = { click->fOrig, click->fCurr }; + fInverse.mapPoints(pts, 2); + this->warp(pts[0], pts[1]); + this->inval(NULL); + return true; + } + +private: + SkIRect fBase, fRect; + SkPoint fP0, fP1; + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new WarpView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleXfermodes.cpp b/samplecode/SampleXfermodes.cpp new file mode 100644 index 0000000000..0a3c4c721e --- /dev/null +++ b/samplecode/SampleXfermodes.cpp @@ -0,0 +1,250 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkCornerPathEffect.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" +#include "SkColorPriv.h" +#include "SkImageDecoder.h" + +static void setNamedTypeface(SkPaint* paint, const char name[]) { + SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal); + paint->setTypeface(face); + SkSafeUnref(face); +} + +#if 0 +static int newscale(U8CPU a, U8CPU b, int shift) { + unsigned prod = a * b + (1 << (shift - 1)); + return (prod + (prod >> shift)) >> shift; +} + +static void test_srcover565(SkCanvas* canvas) { + const int width = 32; + SkBitmap bm1, bm2, bm3; + bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL); + bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL); + bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL); + + int rgb = 0x18; + int r = rgb >> 3; + int g = rgb >> 2; + uint16_t dst = SkPackRGB16(r, g, r); + for (int alpha = 0; alpha <= 255; alpha++) { + SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb); + uint16_t newdst = SkSrcOver32To16(pm, dst); + sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width()); + + int ia = 255 - alpha; + int iscale = SkAlpha255To256(ia); + int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3; + int dg = (SkGetPackedG32(pm) + (g * iscale >> 6)) >> 2; + + sk_memset16(bm2.getAddr16(0, alpha), SkPackRGB16(dr, dg, dr), bm2.width()); + + int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3; + int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2; + + sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width()); + +// if (mr != dr || mg != dg) + { +// SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2); + } + } + + SkScalar dx = SkIntToScalar(width+4); + + canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0); + canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0); + canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0); + + SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) }; + SkPaint p; + p.setARGB(0xFF, rgb, rgb, rgb); + canvas->drawRect(rect, p); +} +#endif + +static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) { + src->setConfig(SkBitmap::kARGB_8888_Config, w, h); + src->allocPixels(); + src->eraseColor(0); + + SkCanvas c(*src); + SkPaint p; + SkRect r; + SkScalar ww = SkIntToScalar(w); + SkScalar hh = SkIntToScalar(h); + + p.setAntiAlias(true); + p.setColor(0xFFFFCC44); + r.set(0, 0, ww*3/4, hh*3/4); + c.drawOval(r, p); + + dst->setConfig(SkBitmap::kARGB_8888_Config, w, h); + dst->allocPixels(); + dst->eraseColor(0); + c.setBitmapDevice(*dst); + + p.setColor(0xFF66AAFF); + r.set(ww/3, hh/3, ww*19/20, hh*19/20); + c.drawRect(r, p); +} + +static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF }; + +class XfermodesView : public SampleView { + SkBitmap fBG; + SkBitmap fSrcB, fDstB; + + void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha, + SkScalar x, SkScalar y) { + SkPaint p; + + canvas->drawBitmap(fSrcB, x, y, &p); + p.setAlpha(alpha); + p.setXfermode(mode); + canvas->drawBitmap(fDstB, x, y, &p); + } + +public: + const static int W = 64; + const static int H = 64; + XfermodesView() { + const int W = 64; + const int H = 64; + + fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4); + fBG.setPixels(gBG); + fBG.setIsOpaque(true); + + make_bitmaps(W, H, &fSrcB, &fDstB); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "Xfermodes"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); + + const struct { + SkXfermode::Mode fMode; + const char* fLabel; + } gModes[] = { + { SkXfermode::kClear_Mode, "Clear" }, + { SkXfermode::kSrc_Mode, "Src" }, + { SkXfermode::kDst_Mode, "Dst" }, + { SkXfermode::kSrcOver_Mode, "SrcOver" }, + { SkXfermode::kDstOver_Mode, "DstOver" }, + { SkXfermode::kSrcIn_Mode, "SrcIn" }, + { SkXfermode::kDstIn_Mode, "DstIn" }, + { SkXfermode::kSrcOut_Mode, "SrcOut" }, + { SkXfermode::kDstOut_Mode, "DstOut" }, + { SkXfermode::kSrcATop_Mode, "SrcATop" }, + { SkXfermode::kDstATop_Mode, "DstATop" }, + { SkXfermode::kXor_Mode, "Xor" }, + + { SkXfermode::kPlus_Mode, "Plus" }, + { SkXfermode::kMultiply_Mode, "Multiply" }, + { SkXfermode::kScreen_Mode, "Screen" }, + { SkXfermode::kOverlay_Mode, "Overlay" }, + { SkXfermode::kDarken_Mode, "Darken" }, + { SkXfermode::kLighten_Mode, "Lighten" }, + { SkXfermode::kColorDodge_Mode, "ColorDodge" }, + { SkXfermode::kColorBurn_Mode, "ColorBurn" }, + { SkXfermode::kHardLight_Mode, "HardLight" }, + { SkXfermode::kSoftLight_Mode, "SoftLight" }, + { SkXfermode::kDifference_Mode, "Difference" }, + { SkXfermode::kExclusion_Mode, "Exclusion" }, + }; + + const SkScalar w = SkIntToScalar(W); + const SkScalar h = SkIntToScalar(H); + SkShader* s = SkShader::CreateBitmapShader(fBG, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + SkMatrix m; + m.setScale(SkIntToScalar(6), SkIntToScalar(6)); + s->setLocalMatrix(m); + + SkPaint labelP; + labelP.setAntiAlias(true); + labelP.setLCDRenderText(true); + labelP.setTextAlign(SkPaint::kCenter_Align); + setNamedTypeface(&labelP, "Menlo Regular"); +// labelP.setTextSize(SkIntToScalar(11)); + + const int W = 5; + + SkScalar x0 = 0; + for (int twice = 0; twice < 2; twice++) { + SkScalar x = x0, y = 0; + for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) { + SkXfermode* mode = SkXfermode::Create(gModes[i].fMode); + SkAutoUnref aur(mode); + SkRect r; + r.set(x, y, x+w, y+h); + + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setShader(s); + canvas->drawRect(r, p); + + canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag); + // canvas->save(); + draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop); + canvas->restore(); + + r.inset(-SK_ScalarHalf, -SK_ScalarHalf); + p.setStyle(SkPaint::kStroke_Style); + p.setShader(NULL); + canvas->drawRect(r, p); + +#if 1 + canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel), + x + w/2, y - labelP.getTextSize()/2, labelP); +#endif + x += w + SkIntToScalar(10); + if ((i % W) == W - 1) { + x = x0; + y += h + SkIntToScalar(30); + } + } + x0 += SkIntToScalar(400); + } + s->unref(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new XfermodesView; } +static SkViewRegister reg(MyFactory); + diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp new file mode 100644 index 0000000000..166e4e5c7c --- /dev/null +++ b/samplecode/SampleXfermodesBlur.cpp @@ -0,0 +1,182 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "Sk64.h" +#include "SkCornerPathEffect.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkKernel33MaskFilter.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +#include "SkStream.h" +#include "SkXMLParser.h" +#include "SkColorPriv.h" +#include "SkImageDecoder.h" +#include "SkBlurMaskFilter.h" + +static void setNamedTypeface(SkPaint* paint, const char name[]) { + SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal); + paint->setTypeface(face); + SkSafeUnref(face); +} + +static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF }; + +class XfermodesBlurView : public SampleView { + SkBitmap fBG; + SkBitmap fSrcB, fDstB; + + void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha, + SkScalar x, SkScalar y) { + SkPaint p; + SkMaskFilter* mf = SkBlurMaskFilter::Create(5, SkBlurMaskFilter::kNormal_BlurStyle, 0); + p.setMaskFilter(mf)->unref(); + + SkScalar ww = SkIntToScalar(W); + SkScalar hh = SkIntToScalar(H); + + // draw a circle covering the upper + // left three quarters of the canvas + p.setColor(0xFFCC44FF); + SkRect r; + r.set(0, 0, ww*3/4, hh*3/4); + r.offset(x, y); + canvas->drawOval(r, p); + + p.setXfermode(mode); + + // draw a square overlapping the circle + // in the lower right of the canvas + p.setColor(0x00AA6633 | alpha << 24); + r.set(ww/3, hh/3, ww*19/20, hh*19/20); + r.offset(x, y); + canvas->drawRect(r, p); + } + +public: + const static int W = 64; + const static int H = 64; + XfermodesBlurView() { + const int W = 64; + const int H = 64; + + fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4); + fBG.setPixels(gBG); + fBG.setIsOpaque(true); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "XfermodesBlur"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + virtual void onDrawContent(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); + + const struct { + SkXfermode::Mode fMode; + const char* fLabel; + } gModes[] = { + { SkXfermode::kClear_Mode, "Clear" }, + { SkXfermode::kSrc_Mode, "Src" }, + { SkXfermode::kDst_Mode, "Dst" }, + { SkXfermode::kSrcOver_Mode, "SrcOver" }, + { SkXfermode::kDstOver_Mode, "DstOver" }, + { SkXfermode::kSrcIn_Mode, "SrcIn" }, + { SkXfermode::kDstIn_Mode, "DstIn" }, + { SkXfermode::kSrcOut_Mode, "SrcOut" }, + { SkXfermode::kDstOut_Mode, "DstOut" }, + { SkXfermode::kSrcATop_Mode, "SrcATop" }, + { SkXfermode::kDstATop_Mode, "DstATop" }, + { SkXfermode::kXor_Mode, "Xor" }, + + { SkXfermode::kPlus_Mode, "Plus" }, + /*{ SkXfermode::kMultiply_Mode, "Multiply" }, + { SkXfermode::kScreen_Mode, "Screen" }, + { SkXfermode::kOverlay_Mode, "Overlay" }, + { SkXfermode::kDarken_Mode, "Darken" }, + { SkXfermode::kLighten_Mode, "Lighten" }, + { SkXfermode::kColorDodge_Mode, "ColorDodge" }, + { SkXfermode::kColorBurn_Mode, "ColorBurn" }, + { SkXfermode::kHardLight_Mode, "HardLight" }, + { SkXfermode::kSoftLight_Mode, "SoftLight" }, + { SkXfermode::kDifference_Mode, "Difference" }, + { SkXfermode::kExclusion_Mode, "Exclusion" },*/ + }; + + const SkScalar w = SkIntToScalar(W); + const SkScalar h = SkIntToScalar(H); + SkShader* s = SkShader::CreateBitmapShader(fBG, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + SkMatrix m; + m.setScale(SkIntToScalar(6), SkIntToScalar(6)); + s->setLocalMatrix(m); + + SkPaint labelP; + labelP.setAntiAlias(true); + labelP.setLCDRenderText(true); + labelP.setTextAlign(SkPaint::kCenter_Align); + setNamedTypeface(&labelP, "Menlo Regular"); + + const int W = 5; + + SkScalar x0 = 0; + for (int twice = 0; twice < 2; twice++) { + SkScalar x = x0, y = 0; + for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) { + SkXfermode* mode = SkXfermode::Create(gModes[i].fMode); + SkAutoUnref aur(mode); + SkRect r; + r.set(x, y, x+w, y+h); + + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setShader(s); + canvas->drawRect(r, p); + + canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag); + draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop); + canvas->restore(); + + r.inset(-SK_ScalarHalf, -SK_ScalarHalf); + p.setStyle(SkPaint::kStroke_Style); + p.setShader(NULL); + canvas->drawRect(r, p); + + canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel), + x + w/2, y - labelP.getTextSize()/2, labelP); + x += w + SkIntToScalar(10); + if ((i % W) == W - 1) { + x = x0; + y += h + SkIntToScalar(30); + } + } + x0 += SkIntToScalar(400); + } + s->unref(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new XfermodesBlurView; } +static SkViewRegister reg(MyFactory); diff --git a/samplecode/samplecode_files.mk b/samplecode/samplecode_files.mk new file mode 100644 index 0000000000..4c660e5bb3 --- /dev/null +++ b/samplecode/samplecode_files.mk @@ -0,0 +1,69 @@ +SOURCE := \ + SampleBitmapRect.cpp \ + SamplePathClip.cpp \ + SampleComplexClip.cpp \ + SampleNinePatch.cpp \ + SampleAvoid.cpp \ + SampleMeasure.cpp \ + SampleArc.cpp \ + SampleRepeatTile.cpp \ + SampleApp.cpp \ + vertexdump.cpp \ + SampleShapes.cpp \ + SampleMipMap.cpp \ + SampleLCD.cpp \ + SampleCamera.cpp \ + SampleVertices.cpp \ + SampleFontScalerTest.cpp \ + SampleBigGradient.cpp \ + SampleAll.cpp \ + SampleShaderText.cpp \ + SamplePolyToPoly.cpp \ + SampleBlur.cpp \ + SampleHairline.cpp \ + SampleCircle.cpp \ + SampleOvalTest.cpp \ + SampleLines.cpp \ + SampleOverflow.cpp \ + SampleStrokePath.cpp \ + SampleSlides.cpp \ + SampleLayers.cpp \ + SampleTiling.cpp \ + SampleTinyBitmap.cpp \ + SampleXfermodes.cpp \ + SampleDrawLooper.cpp \ + SampleTextEffects.cpp \ + SampleTextOnPath.cpp \ + SampleDitherBitmap.cpp \ + SampleExtractAlpha.cpp \ + SampleDither.cpp \ + SampleEncode.cpp \ + SampleFontCache.cpp \ + SampleGradients.cpp \ + SampleTypeface.cpp \ + SampleFillType.cpp \ + SamplePath.cpp \ + SampleLayerMask.cpp \ + SampleStrokeText.cpp \ + SamplePathEffects.cpp \ + SampleTextAlpha.cpp \ + ClockFaceView.cpp \ + SampleEmboss.cpp \ + SamplePoints.cpp \ + SampleFilter2.cpp \ + SamplePatch.cpp \ + SampleFilter.cpp \ + OverView.cpp \ + SampleFuzz.cpp \ + SampleShaders.cpp \ + SampleText.cpp \ + SampleTextBox.cpp \ + SampleImage.cpp \ + SampleMovie.cpp \ + SampleImageDir.cpp \ + SampleWarp.cpp \ + SamplePageFlip.cpp \ + SamplePicture.cpp \ + SampleLineClipper.cpp \ + SampleRegion.cpp \ + SampleDecode.cpp \ Crashes diff --git a/samplecode/vertexdump.cpp b/samplecode/vertexdump.cpp new file mode 100644 index 0000000000..c124ad85b0 --- /dev/null +++ b/samplecode/vertexdump.cpp @@ -0,0 +1,88 @@ +#include "SkPoint.h" + +void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]); + +void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]) { + verts[0].set(SkFloatToScalar(107), SkFloatToScalar(189)); + texs[0].set(SkFloatToScalar(0), SkFloatToScalar(0)); + verts[1].set(SkFloatToScalar(116), SkFloatToScalar(189)); + texs[1].set(SkFloatToScalar(9), SkFloatToScalar(0)); + verts[2].set(SkFloatToScalar(203), SkFloatToScalar(189)); + texs[2].set(SkFloatToScalar(35), SkFloatToScalar(0)); + verts[3].set(SkFloatToScalar(212), SkFloatToScalar(189)); + texs[3].set(SkFloatToScalar(44), SkFloatToScalar(0)); + verts[4].set(SkFloatToScalar(107), SkFloatToScalar(198)); + texs[4].set(SkFloatToScalar(0), SkFloatToScalar(9)); + verts[5].set(SkFloatToScalar(116), SkFloatToScalar(198)); + texs[5].set(SkFloatToScalar(9), SkFloatToScalar(9)); + verts[6].set(SkFloatToScalar(203), SkFloatToScalar(198)); + texs[6].set(SkFloatToScalar(35), SkFloatToScalar(9)); + verts[7].set(SkFloatToScalar(212), SkFloatToScalar(198)); + texs[7].set(SkFloatToScalar(44), SkFloatToScalar(9)); + verts[8].set(SkFloatToScalar(107), SkFloatToScalar(224)); + texs[8].set(SkFloatToScalar(0), SkFloatToScalar(39)); + verts[9].set(SkFloatToScalar(116), SkFloatToScalar(224)); + texs[9].set(SkFloatToScalar(9), SkFloatToScalar(39)); + verts[10].set(SkFloatToScalar(203), SkFloatToScalar(224)); + texs[10].set(SkFloatToScalar(35), SkFloatToScalar(39)); + verts[11].set(SkFloatToScalar(212), SkFloatToScalar(224)); + texs[11].set(SkFloatToScalar(44), SkFloatToScalar(39)); + verts[12].set(SkFloatToScalar(107), SkFloatToScalar(233)); + texs[12].set(SkFloatToScalar(0), SkFloatToScalar(48)); + verts[13].set(SkFloatToScalar(116), SkFloatToScalar(233)); + texs[13].set(SkFloatToScalar(9), SkFloatToScalar(48)); + verts[14].set(SkFloatToScalar(203), SkFloatToScalar(233)); + texs[14].set(SkFloatToScalar(35), SkFloatToScalar(48)); + verts[15].set(SkFloatToScalar(212), SkFloatToScalar(233)); + texs[15].set(SkFloatToScalar(44), SkFloatToScalar(48)); + index[0] = 0; index[1] = 5; index[2] = 1; + index[3] = 0; index[4] = 4; index[5] = 5; +#if 0 + index[6] = 1; index[7] = 6; index[8] = 2; +#else + index[6] = 6; index[7] = 2; index[8] = 1; +#endif + index[9] = 1; index[10] = 5; index[11] = 6; + index[12] = 2; + index[13] = 7; + index[14] = 3; + index[15] = 2; + index[16] = 6; + index[17] = 7; + index[18] = 4; + index[19] = 9; + index[20] = 5; + index[21] = 4; + index[22] = 8; + index[23] = 9; + index[24] = 5; + index[25] = 10; + index[26] = 6; + index[27] = 5; + index[28] = 9; + index[29] = 10; + index[30] = 6; + index[31] = 11; + index[32] = 7; + index[33] = 6; + index[34] = 10; + index[35] = 11; + index[36] = 8; + index[37] = 13; + index[38] = 9; + index[39] = 8; + index[40] = 12; + index[41] = 13; + index[42] = 9; + index[43] = 14; + index[44] = 10; + index[45] = 9; + index[46] = 13; + index[47] = 14; + index[48] = 10; + index[49] = 15; + index[50] = 11; + index[51] = 10; + index[52] = 14; + index[53] = 15; +} diff --git a/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp index b9e65f741f..e456f26118 100644 --- a/src/animator/SkDisplayApply.cpp +++ b/src/animator/SkDisplayApply.cpp @@ -294,7 +294,7 @@ bool SkApply::enable(SkAnimateMaker& maker) { if ((mode == kMode_immediate || mode == kMode_create) && scope == NULL) return false; // !!! error? bool enableMe = scope && (scope->hasEnable() || scope->isApply() || scope->isDrawable() == false); - if (mode == kMode_immediate && enableMe || mode == kMode_create) + if ((mode == kMode_immediate && enableMe) || mode == kMode_create) activate(maker); // for non-drawables like post, prime them here if (mode == kMode_immediate && enableMe) fActive->enable(); @@ -356,7 +356,7 @@ bool SkApply::enable(SkAnimateMaker& maker) { if (old < 0) goto append; else if (fContainsScope) { - if ((*parentList)[old] != this || restore == true) { + if ((*parentList)[old] != this || restore) { append: if (parentGroup) parentGroup->markCopySize(old); @@ -479,7 +479,7 @@ void SkApply::endSave(int index) { } else { SkScriptValue scriptValue; bool success = target->getProperty(info->propertyIndex(), &scriptValue); - SkASSERT(success = true); + SkASSERT(success == true); last[0] = scriptValue.fOperand; scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0]; target->setProperty(info->propertyIndex(), scriptValue); @@ -624,8 +624,8 @@ bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) { SkInterpolatorBase::Result interpResult = fActive->fInterpolators[inner]->timeToValues( innerTime, values.get()); result |= (interpResult != SkInterpolatorBase::kFreezeEnd_Result); - if ((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result || - transition == SkApply::kTransition_reverse && fLastTime == 0) && state.fUnpostedEndEvent) { + if (((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result) || + (transition == SkApply::kTransition_reverse && fLastTime == 0)) && state.fUnpostedEndEvent) { // SkDEBUGF(("interpolate: post on end\n")); state.fUnpostedEndEvent = false; maker.postOnEnd(animate, state.fBegin + state.fDuration); diff --git a/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp index a94b8480b0..db6c83848c 100644 --- a/src/animator/SkDisplayXMLParser.cpp +++ b/src/animator/SkDisplayXMLParser.cpp @@ -304,8 +304,8 @@ const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* info } return info; next: - if (type == SkType_Drawable || type == SkType_Displayable && - container->fDisplayable->isDrawable()) { + if (type == SkType_Drawable || (type == SkType_Displayable && + container->fDisplayable->isDrawable())) { rectNext: if (fParents.count() > 1) { Parent* parent = fParents.end() - 2; diff --git a/src/animator/SkDump.cpp b/src/animator/SkDump.cpp index eac956c136..426a1e649f 100644 --- a/src/animator/SkDump.cpp +++ b/src/animator/SkDump.cpp @@ -58,7 +58,7 @@ bool SkDump::enable(SkAnimateMaker& maker ) { maker.fEvents.dump(maker); if ((hasAttr |= (name.size() > 0)) == true) maker.dump(name.c_str()); - if (displayList > 0 || displayList != 0 && hasAttr == false) + if (displayList > 0 || (displayList != 0 && hasAttr == false)) maker.fDisplayList.dump(&maker); return true; } diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp index f81147d4a2..96b73cdb2e 100644 --- a/src/animator/SkScript.cpp +++ b/src/animator/SkScript.cpp @@ -305,8 +305,8 @@ twoChar: } while (true); signed char topPrecedence = gPrecedence[compare]; SkASSERT(topPrecedence != -1); - if (topPrecedence > precedence || topPrecedence == precedence && - gOpAttributes[op].fLeftType == kNoType) { + if (topPrecedence > precedence || (topPrecedence == precedence && + gOpAttributes[op].fLeftType == kNoType)) { break; } if (processOp() == false) @@ -1207,7 +1207,7 @@ noMatch: } break; case kElse: flipSuppress: - if (fSuppressStack.top().fElse == true) + if (fSuppressStack.top().fElse) fSuppressStack.pop(); fSuppressStack.top().fElse = true; fSuppressStack.top().fSuppress ^= true; diff --git a/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp index d582d335d4..98db1fb327 100644 --- a/src/animator/SkScriptDecompile.cpp +++ b/src/animator/SkScriptDecompile.cpp @@ -114,7 +114,7 @@ static size_t gOperandNamesSize = sizeof(gOperandNames) / sizeof(gOperandNames[0 // check to see that there are no missing or duplicate entries void SkScriptEngine2::ValidateDecompileTable() { SkScriptEngine2::TypeOp op = SkScriptEngine2::kNop; - int index; + size_t index; for (index = 0; index < gOpNamesSize; index++) { SkASSERT(gOpNames[index].fOp == op); op = (SkScriptEngine2::TypeOp) (op + 1); @@ -132,9 +132,9 @@ void SkScriptEngine2::decompile(const unsigned char* start, size_t length) { SkASSERT(length > 0); const unsigned char* opCode = start; do { - SkASSERT(opCode - start < length); + SkASSERT((size_t)(opCode - start) < length); SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++; - SkASSERT(op < gOpNamesSize); + SkASSERT((size_t)op < gOpNamesSize); SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName); switch (op) { case SkScriptEngine2::kCallback: { @@ -184,7 +184,7 @@ void SkScriptEngine2::decompile(const unsigned char* start, size_t length) { SkOperand2::OpType type; memcpy(&type, opCode, sizeof(type)); opCode += sizeof(type); - int index = 0; + size_t index = 0; if (type == 0) SkDebugf(" type: %s", gOperandNames[index].fName); else { @@ -211,6 +211,8 @@ void SkScriptEngine2::decompile(const unsigned char* start, size_t length) { goto done; case SkScriptEngine2::kNop: SkASSERT(0); + default: + break; } SkDebugf("\n"); } while (true); diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp index 6d8c208d2d..12841983bd 100644 --- a/src/animator/SkScriptRuntime.cpp +++ b/src/animator/SkScriptRuntime.cpp @@ -304,6 +304,8 @@ bool SkScriptRuntime::executeTokens(unsigned char* opCode) { goto done; case SkScriptEngine2::kNop: SkASSERT(0); + default: + break; } } while (true); done: diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp index efd187291c..edcc2af441 100644 --- a/src/animator/SkScriptTokenizer.cpp +++ b/src/animator/SkScriptTokenizer.cpp @@ -8,34 +8,34 @@ #include "SkOpArray.h" const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = { -{ SkOperand2::kNoType }, +{ SkOperand2::kNoType, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean }, { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString }, // kAdd -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitAnd -{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kBitNot -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitOr + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString, kResultIsNotBoolean }, // kAdd +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitAnd +{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitNot +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitOr { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kDivide + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kDivide { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber, kResultIsBoolean }, // kEqual -{ SkOperand2::kS32 }, // kFlipOps +{ SkOperand2::kS32, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean }, // kFlipOps { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsNumber, kResultIsBoolean }, // kGreaterEqual -{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalAnd (really, ToBool) -{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalNot -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kLogicalOr -{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMinus +{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalAnd (really, ToBool) +{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalNot +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalOr +{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMinus { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), - SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias }, // kModulo + SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias, kResultIsNotBoolean }, // kModulo { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMultiply -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftLeft -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftRight + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMultiply +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftLeft +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftRight { SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), - SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kSubtract -{ SkOperand2::kS32, SkOperand2::kS32, kNoBias } // kXor + SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kSubtract +{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean } // kXor }; #define kBracketPrecedence 16 @@ -308,8 +308,8 @@ twoChar: } while (true); signed char topPrecedence = gPrecedence[compare]; SkASSERT(topPrecedence != -1); - if (topPrecedence > precedence || topPrecedence == precedence && - gOpAttributes[op].fLeftType == SkOperand2::kNoType) { + if (topPrecedence > precedence || (topPrecedence == precedence && + gOpAttributes[op].fLeftType == SkOperand2::kNoType)) { break; } processOp(); @@ -1051,7 +1051,8 @@ bool SkScriptEngine2::processOp() { fOpStack.pop(&op); op = (Op) (op & ~kArtificialOp); const OperatorAttributes* attributes = &gOpAttributes[op]; - SkScriptValue2 value1 = { 0 }; + SkScriptValue2 value1; + memset(&value1, 0, sizeof(SkScriptValue2)); SkScriptValue2 value2; fValueStack.pop(&value2); value2.fIsWritten = SkScriptValue2::kUnwritten; @@ -1230,7 +1231,7 @@ bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toTy SkScalar SkScriptEngine2::IntToScalar(int32_t s32) { SkScalar scalar; - if (s32 == SK_NaN32) + if (s32 == (int32_t) SK_NaN32) scalar = SK_ScalarNaN; else if (SkAbs32(s32) == SK_MaxS32) scalar = SkSign32(s32) * SK_ScalarMax; @@ -1261,21 +1262,21 @@ bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* strin #ifdef SK_DEBUG -#define testInt(expression) { #expression, SkOperand2::kS32, expression } +#define testInt(expression) { #expression, SkOperand2::kS32, expression, 0, NULL } #ifdef SK_SCALAR_IS_FLOAT -#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression } -#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2) } +#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression, NULL } +#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2), NULL } #else #ifdef SK_CAN_USE_FLOAT -#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f) } -#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2) * 65536.0f) } +#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f), NULL } +#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2) * 65536.0f), NULL } #endif #endif -#define testTrue(expression) { #expression, SkOperand2::kS32, 1 } -#define testFalse(expression) { #expression, SkOperand2::kS32, 0 } +#define testTrue(expression) { #expression, SkOperand2::kS32, 1, 0, NULL } +#define testFalse(expression) { #expression, SkOperand2::kS32, 0, 0, NULL } static const SkScriptNAnswer2 scriptTests[] = { - testInt(1||0&&3), + testInt(1||(0&&3)), #ifdef SK_CAN_USE_FLOAT testScalar(- -5.5- -1.5), testScalar(1.0+5), @@ -1307,12 +1308,12 @@ static const SkScriptNAnswer2 scriptTests[] = { { "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" }, { "123+\"456\"", SkOperand2::kString, 0, 0, "123456" }, { "'123'+456", SkOperand2::kString, 0, 0, "123456" }, - { "'123'|\"456\"", SkOperand2::kS32, 123|456 }, - { "123|\"456\"", SkOperand2::kS32, 123|456 }, - { "'123'|456", SkOperand2::kS32, 123|456 }, - { "'2'<11", SkOperand2::kS32, 1 }, - { "2<'11'", SkOperand2::kS32, 1 }, - { "'2'<'11'", SkOperand2::kS32, 0 }, + { "'123'|\"456\"", SkOperand2::kS32, 123|456, 0, NULL }, + { "123|\"456\"", SkOperand2::kS32, 123|456, 0, NULL }, + { "'123'|456", SkOperand2::kS32, 123|456, 0, NULL }, + { "'2'<11", SkOperand2::kS32, 1, 0, NULL }, + { "2<'11'", SkOperand2::kS32, 1, 0, NULL }, + { "'2'<'11'", SkOperand2::kS32, 0, 0, NULL }, testInt(123), testInt(-345), testInt(+678), @@ -1461,15 +1462,15 @@ static const SkScriptNAnswer2 scriptTests[] = { // logic testInt(1?2:3), testInt(0?2:3), - testInt(1&&2||3), - testInt(1&&0||3), - testInt(1&&0||0), - testInt(1||0&&3), - testInt(0||0&&3), - testInt(0||1&&3), + testInt((1&&2)||3), + testInt((1&&0)||3), + testInt((1&&0)||0), + testInt(1||(0&&3)), + testInt(0||(0&&3)), + testInt(0||(1&&3)), testInt(0&&1?2:3) #ifdef SK_CAN_USE_FLOAT - , { "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2 } + , { "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2, NULL } #endif }; diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp index 7e8a03087b..05d5c28e6a 100644 --- a/src/core/SkAdvancedTypefaceMetrics.cpp +++ b/src/core/SkAdvancedTypefaceMetrics.cpp @@ -22,6 +22,10 @@ #include FT_FREETYPE_H #endif +#ifdef SK_BUILD_FOR_MAC +#include <Carbon/Carbon.h> +#endif + namespace skia_advanced_typeface_metrics_utils { template <typename Data> @@ -142,6 +146,11 @@ template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( FT_Face face, int num_glyphs, bool (*getAdvance)(FT_Face face, int gId, int16_t* data)); +#elif defined(SK_BUILD_FOR_MAC) +template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( + CTFontRef ctFont, + int num_glyphs, + bool (*getAdvance)(CTFontRef ctFont, int gId, int16_t* data)); #endif template void resetRange( SkAdvancedTypefaceMetrics::WidthRange* range, diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp index 4125b583fd..a5fc3c9f9f 100644 --- a/src/core/SkAlphaRuns.cpp +++ b/src/core/SkAlphaRuns.cpp @@ -16,10 +16,14 @@ */ #include "SkAntiRun.h" +#include "SkUtils.h" void SkAlphaRuns::reset(int width) { SkASSERT(width > 0); +#ifdef SK_DEBUG + sk_memset16((uint16_t*)fRuns, (uint16_t)(-42), width); +#endif fRuns[0] = SkToS16(width); fRuns[width] = 0; fAlpha[0] = 0; @@ -75,13 +79,17 @@ void SkAlphaRuns::Break(int16_t runs[], uint8_t alpha[], int x, int count) { } } -void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, - U8CPU maxValue) { +int SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, + U8CPU maxValue, int offsetX) { SkASSERT(middleCount >= 0); SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth); - int16_t* runs = fRuns; - uint8_t* alpha = fAlpha; + SkASSERT(fRuns[offsetX] >= 0); + + int16_t* runs = fRuns + offsetX; + uint8_t* alpha = fAlpha + offsetX; + uint8_t* lastAlpha = alpha; + x -= offsetX; if (startAlpha) { SkAlphaRuns::Break(runs, alpha, x, 1); @@ -97,6 +105,7 @@ void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, runs += x + 1; alpha += x + 1; x = 0; + lastAlpha += x; // we don't want the +1 SkDEBUGCODE(this->validate();) } @@ -114,13 +123,18 @@ void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, middleCount -= n; } while (middleCount > 0); SkDEBUGCODE(this->validate();) + lastAlpha = alpha; } if (stopAlpha) { SkAlphaRuns::Break(runs, alpha, x, 1); - alpha[x] = SkToU8(alpha[x] + stopAlpha); + alpha += x; + alpha[0] = SkToU8(alpha[0] + stopAlpha); SkDEBUGCODE(this->validate();) + lastAlpha = alpha; } + + return lastAlpha - fAlpha; // new offsetX } #ifdef SK_DEBUG diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h index 89e54819c4..669e5d227a 100644 --- a/src/core/SkAntiRun.h +++ b/src/core/SkAntiRun.h @@ -31,7 +31,15 @@ public: } void reset(int width); - void add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue); + + /** + * Returns the offsetX value that should be passed on the next call, + * assuming we're on the same scanline. If the caller is switching + * scanlines, then offsetX should be 0 when this is called. + */ + int add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, + U8CPU maxValue, int offsetX); + SkDEBUGCODE(void assertValid(int y, int maxStep) const;) SkDEBUGCODE(void dump() const;) diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h index 9ae8b178e7..553bd898ad 100644 --- a/src/core/SkBitmapProcState_matrix.h +++ b/src/core/SkBitmapProcState_matrix.h @@ -1,3 +1,19 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + #define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale) #define SCALE_FILTER_NAME MAKENAME(_filter_scale) diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h index 15831b67bf..9a9e8c40be 100644 --- a/src/core/SkBitmapProcState_shaderproc.h +++ b/src/core/SkBitmapProcState_shaderproc.h @@ -1,3 +1,20 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + #define SCALE_FILTER_NAME MAKENAME(_filter_DX_shaderproc) static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y, diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp index 5ffa5b1d95..4ad4d41926 100644 --- a/src/core/SkClipStack.cpp +++ b/src/core/SkClipStack.cpp @@ -188,6 +188,11 @@ bool operator==(const SkClipStack::B2FIter::Clip& a, ((a.fPath == NULL && b.fPath == NULL) || *a.fPath == *b.fPath); } +bool operator!=(const SkClipStack::B2FIter::Clip& a, + const SkClipStack::B2FIter::Clip& b) { + return !(a == b); +} + SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) { this->reset(stack); } diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 4a7693673f..aee5a5ea2a 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -25,13 +25,13 @@ SkDeviceFactory::~SkDeviceFactory() { /////////////////////////////////////////////////////////////////////////////// -SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas), fMetaData(NULL), fMatrixClipObserver(NULL) { +SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas), fMetaData(NULL) { fOrigin.setZero(); fCachedDeviceFactory = NULL; } SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer) - : fCanvas(canvas), fBitmap(bitmap), fMetaData(NULL), fMatrixClipObserver(NULL) { + : fCanvas(canvas), fBitmap(bitmap), fMetaData(NULL) { fOrigin.setZero(); // auto-allocate if we're for offscreen drawing if (isForLayer) { @@ -48,7 +48,6 @@ SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer) SkDevice::~SkDevice() { delete fMetaData; SkSafeUnref(fCachedDeviceFactory); - SkSafeUnref(fMatrixClipObserver); } SkDeviceFactory* SkDevice::onNewDeviceFactory() { @@ -108,13 +107,6 @@ void SkDevice::onAccessBitmap(SkBitmap* bitmap) {} void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region, const SkClipStack& clipStack) { - if (fMatrixClipObserver) { - fMatrixClipObserver->matrixClipChanged(matrix, region, clipStack); - } -} - -void SkDevice::setMatrixClipObserver(SkMatrixClipObserver* observer) { - SkRefCnt_SafeAssign(fMatrixClipObserver, observer); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp index 983c8d21f2..06bca1e59d 100644 --- a/src/core/SkMatrix.cpp +++ b/src/core/SkMatrix.cpp @@ -119,6 +119,21 @@ uint8_t SkMatrix::computeTypeMask() const { /////////////////////////////////////////////////////////////////////////////// +#ifdef SK_SCALAR_IS_FLOAT + +bool operator==(const SkMatrix& a, const SkMatrix& b) { + const SkScalar* SK_RESTRICT ma = a.fMat; + const SkScalar* SK_RESTRICT mb = b.fMat; + + return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] && + ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] && + ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8]; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// + void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) { if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) { fMat[kMTransX] = dx; @@ -202,9 +217,27 @@ bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { } bool SkMatrix::preScale(SkScalar sx, SkScalar sy) { +#ifdef SK_SCALAR_IS_FIXED SkMatrix m; m.setScale(sx, sy); return this->preConcat(m); +#else + // the assumption is that these multiplies are very cheap, and that + // a full concat and/or just computing the matrix type is more expensive. + // Also, the fixed-point case checks for overflow, but the float doesn't, + // so we can get away with these blind multiplies. + + fMat[kMScaleX] = SkScalarMul(fMat[kMScaleX], sx); + fMat[kMSkewY] = SkScalarMul(fMat[kMSkewY], sx); + fMat[kMPersp0] = SkScalarMul(fMat[kMPersp0], sx); + + fMat[kMSkewX] = SkScalarMul(fMat[kMSkewX], sy); + fMat[kMScaleY] = SkScalarMul(fMat[kMScaleY], sy); + fMat[kMPersp1] = SkScalarMul(fMat[kMPersp1], sy); + + this->orTypeMask(kScale_Mask); + return true; +#endif } bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) { diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 52b0163afa..a60742449a 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -1303,17 +1303,36 @@ void SkScalerContext::MakeRec(const SkPaint& paint, } rec->fMaskFormat = SkToU8(computeMaskFormat(paint)); - rec->fFlags = SkToU8(flags); - rec->setHinting(computeHinting(paint)); + + if (SkMask::kLCD16_Format == rec->fMaskFormat) { + SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder(); + SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation(); + if (SkFontHost::kNONE_LCDOrder == order) { + // eeek, can't support LCD + rec->fMaskFormat = SkMask::kA8_Format; + } else { + if (SkFontHost::kVertical_LCDOrientation == orient) { + flags |= SkScalerContext::kLCD_Vertical_Flag; + } + if (SkFontHost::kBGR_LCDOrder == order) { + flags |= SkScalerContext::kLCD_BGROrder_Flag; + } + } + } + if (paint.isEmbeddedBitmapText()) { - rec->fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag; + flags |= SkScalerContext::kEmbeddedBitmapText_Flag; } if (paint.isSubpixelText()) { - rec->fFlags |= SkScalerContext::kSubpixelPositioning_Flag; + flags |= SkScalerContext::kSubpixelPositioning_Flag; } if (paint.isAutohinted()) { - rec->fFlags |= SkScalerContext::kAutohinting_Flag; + flags |= SkScalerContext::kAutohinting_Flag; } + rec->fFlags = SkToU16(flags); + + // setHinting modifies fFlags, so do this last + rec->setHinting(computeHinting(paint)); /* Allow the fonthost to modify our rec before we use it as a key into the cache. This way if we're asking for something that they will ignore, diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 68eb35d1b1..ca57237a53 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -254,6 +254,12 @@ void SkPath::setConvexity(Convexity c) { ////////////////////////////////////////////////////////////////////////////// // Construction methods +#define DIRTY_AFTER_EDIT \ + do { \ + fBoundsIsDirty = true; \ + fConvexity = kUnknown_Convexity;\ + } while (0) + void SkPath::incReserve(U16CPU inc) { SkDEBUGCODE(this->validate();) @@ -278,7 +284,7 @@ void SkPath::moveTo(SkScalar x, SkScalar y) { pt->set(x, y); GEN_ID_INC; - fBoundsIsDirty = true; + DIRTY_AFTER_EDIT; } void SkPath::rMoveTo(SkScalar x, SkScalar y) { @@ -298,7 +304,7 @@ void SkPath::lineTo(SkScalar x, SkScalar y) { *fVerbs.append() = kLine_Verb; GEN_ID_INC; - fBoundsIsDirty = true; + DIRTY_AFTER_EDIT; } void SkPath::rLineTo(SkScalar x, SkScalar y) { @@ -321,7 +327,7 @@ void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { *fVerbs.append() = kQuad_Verb; GEN_ID_INC; - fBoundsIsDirty = true; + DIRTY_AFTER_EDIT; } void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { @@ -345,7 +351,7 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, *fVerbs.append() = kCubic_Verb; GEN_ID_INC; - fBoundsIsDirty = true; + DIRTY_AFTER_EDIT; } void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, @@ -1293,7 +1299,7 @@ void SkPath::unflatten(SkReader32& buffer) { buffer.read(fVerbs.begin(), fVerbs.count()); GEN_ID_INC; - fBoundsIsDirty = true; + DIRTY_AFTER_EDIT; SkDEBUGCODE(this->validate();) } @@ -1410,7 +1416,7 @@ static int CrossProductSign(const SkVector& a, const SkVector& b) { // only valid for a single contour struct Convexicator { - Convexicator() : fPtCount(0), fConvexity(SkPath::kUnknown_Convexity) { + Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) { fSign = 0; // warnings fCurrPt.set(0, 0); @@ -1472,9 +1478,7 @@ private: if (0 == fSign) { fSign = sign; } else if (sign) { - if (fSign == sign) { - fConvexity = SkPath::kConvex_Convexity; - } else { + if (fSign != sign) { fConvexity = SkPath::kConcave_Convexity; } } diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp index 5c5ea0b7c9..105df3ce83 100644 --- a/src/core/SkPictureFlat.cpp +++ b/src/core/SkPictureFlat.cpp @@ -28,9 +28,9 @@ SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap, } SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) { - int32_t size = sizeof(SkMatrix); + size_t size = matrix.flatten(NULL); SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index); - memcpy(&result->fMatrixData, &matrix, sizeof(SkMatrix)); + matrix.flatten(&result->fMatrixData); return result; } diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index dae7b8aed0..697b399a6a 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -147,7 +147,7 @@ public: static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index); void unflatten(SkMatrix* result) const { - memcpy(result, fMatrixData, sizeof(SkMatrix)); + result->unflatten(fMatrixData); } #ifdef SK_DEBUG_DUMP diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp index 5457b59cdc..398f786c95 100644 --- a/src/core/SkScan_AntiPath.cpp +++ b/src/core/SkScan_AntiPath.cpp @@ -53,7 +53,7 @@ protected: int fWidth, fLeft, fSuperLeft; SkDEBUGCODE(int fCurrX;) - SkDEBUGCODE(int fCurrY;) + int fCurrY; }; BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, @@ -69,7 +69,8 @@ BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, fSuperLeft = left << SHIFT; fWidth = right - left; fCurrIY = -1; - SkDEBUGCODE(fCurrX = -1; fCurrY = -1;) + fCurrY = -1; + SkDEBUGCODE(fCurrX = -1;) } class SuperBlitter : public BaseSuperBlitter { @@ -89,6 +90,7 @@ public: private: SkAlphaRuns fRuns; + int fOffsetX; }; SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, @@ -100,6 +102,8 @@ SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, fRuns.fRuns = (int16_t*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(int16_t)); fRuns.fAlpha = (uint8_t*)(fRuns.fRuns + width + 1); fRuns.reset(width); + + fOffsetX = 0; } void SuperBlitter::flush() { @@ -108,6 +112,7 @@ void SuperBlitter::flush() { // SkDEBUGCODE(fRuns.dump();) fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns); fRuns.reset(fWidth); + fOffsetX = 0; } fCurrIY = -1; SkDEBUGCODE(fCurrX = -1;) @@ -134,11 +139,14 @@ void SuperBlitter::blitH(int x, int y, int width) { } #ifdef SK_DEBUG - SkASSERT(y >= fCurrY); SkASSERT(y != fCurrY || x >= fCurrX); - fCurrY = y; #endif - + SkASSERT(y >= fCurrY); + if (fCurrY != y) { + fOffsetX = 0; + fCurrY = y; + } + if (iy != fCurrIY) { // new scanline this->flush(); fCurrIY = iy; @@ -148,36 +156,29 @@ void SuperBlitter::blitH(int x, int y, int width) { // hit 256 as a summed max, but 255. // int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT); -#if 0 - SkAntiRun<SHIFT> arun; - arun.set(x, x + width); - fRuns.add(x >> SHIFT, arun.getStartAlpha(), arun.getMiddleCount(), - arun.getStopAlpha(), maxValue); -#else - { - int start = x; - int stop = x + width; - - SkASSERT(start >= 0 && stop > start); - int fb = start & SUPER_Mask; - int fe = stop & SUPER_Mask; - int n = (stop >> SHIFT) - (start >> SHIFT) - 1; - - if (n < 0) { - fb = fe - fb; - n = 0; - fe = 0; + int start = x; + int stop = x + width; + + SkASSERT(start >= 0 && stop > start); + int fb = start & SUPER_Mask; + int fe = stop & SUPER_Mask; + int n = (stop >> SHIFT) - (start >> SHIFT) - 1; + + if (n < 0) { + fb = fe - fb; + n = 0; + fe = 0; + } else { + if (fb == 0) { + n += 1; } else { - if (fb == 0) { - n += 1; - } else { - fb = (1 << SHIFT) - fb; - } + fb = (1 << SHIFT) - fb; } - fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe), - (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT)); } -#endif + + fOffsetX = fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe), + (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT), + fOffsetX); #ifdef SK_DEBUG fRuns.assertValid(y & MASK, (1 << (8 - SHIFT))); diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp index b84c576c15..52f2a321aa 100644 --- a/src/core/SkScan_Antihair.cpp +++ b/src/core/SkScan_Antihair.cpp @@ -653,6 +653,8 @@ void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip, } } +#endif // SK_SCALAR_IS_FLOAT + /////////////////////////////////////////////////////////////////////////////// #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b) @@ -811,7 +813,3 @@ void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, innerstrokedot8(L, T, R, B, blitter); } } - -#endif - - diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp index 141f751982..9a60b8e66f 100644 --- a/src/core/SkWriter32.cpp +++ b/src/core/SkWriter32.cpp @@ -185,3 +185,72 @@ bool SkWriter32::writeToStream(SkWStream* stream) { return true; } +/////////////////////////////////////////////////////////////////////////////// + +#include "SkReader32.h" + +const char* SkReader32::readString(size_t* outLen) { + // we need to read at least 1-4 bytes + SkASSERT(this->isAvailable(4)); + const uint8_t* base = (const uint8_t*)this->peek(); + const uint8_t* ptr = base; + + size_t len = *ptr++; + if (0xFF == len) { + len = (ptr[0] << 8) | ptr[1]; + ptr += 2; + SkASSERT(len < 0xFFFF); + } + + // skip what we've read, and 0..3 pad bytes + // add 1 for the terminating 0 that writeString() included + size_t alignedSize = SkAlign4(len + (ptr - base) + 1); + this->skip(alignedSize); + + if (outLen) { + *outLen = len; + } + return (const char*)ptr; +} + +void SkWriter32::writeString(const char str[], size_t len) { + if ((long)len < 0) { + SkASSERT(str); + len = strlen(str); + } + size_t lenBytes = 1; + if (len >= 0xFF) { + lenBytes = 3; + SkASSERT(len < 0xFFFF); + } + // add 1 since we also write a terminating 0 + size_t alignedLen = SkAlign4(lenBytes + len + 1); + uint8_t* ptr = (uint8_t*)this->reserve(alignedLen); + if (1 == lenBytes) { + *ptr++ = SkToU8(len); + } else { + *ptr++ = 0xFF; + *ptr++ = SkToU8(len >> 8); + *ptr++ = len & 0xFF; + } + memcpy(ptr, str, len); + ptr[len] = 0; + // we may have left 0,1,2,3 bytes uninitialized, since we reserved align4 + // number of bytes. That's ok, since the reader will know to skip those +} + +size_t SkWriter32::WriteStringSize(const char* str, size_t len) { + if ((long)len < 0) { + SkASSERT(str); + len = strlen(str); + } + size_t lenBytes = 1; + if (len >= 0xFF) { + lenBytes = 3; + SkASSERT(len < 0xFFFF); + } + // add 1 since we also write a terminating 0 + return SkAlign4(lenBytes + len + 1); +} + + diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp index 603deb7ce4..1dfc24d1a1 100644 --- a/src/effects/Sk2DPathEffect.cpp +++ b/src/effects/Sk2DPathEffect.cpp @@ -82,12 +82,19 @@ void Sk2DPathEffect::end(SkPath* dst) {} void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) { - buffer.writeMul4(&fMatrix, sizeof(fMatrix)); + char storage[SkMatrix::kMaxFlattenSize]; + uint32_t size = fMatrix.flatten(storage); + buffer.write32(size); + buffer.write(storage, size); } Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer) { - buffer.read(&fMatrix, sizeof(fMatrix)); + char storage[SkMatrix::kMaxFlattenSize]; + uint32_t size = buffer.readS32(); + SkASSERT(size <= sizeof(storage)); + buffer.read(storage, size); + fMatrix.unflatten(storage); fMatrix.invert(&fInverse); } diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp index c6caba3165..6e68b5e9ef 100644 --- a/src/effects/SkTransparentShader.cpp +++ b/src/effects/SkTransparentShader.cpp @@ -53,7 +53,10 @@ void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) { switch (fDevice->getConfig()) { case SkBitmap::kARGB_8888_Config: if (scale == 256) { - memcpy(span, fDevice->getAddr32(x, y), count * sizeof(SkPMColor)); + SkPMColor* src = fDevice->getAddr32(x, y); + if (src != span) { + memcpy(span, src, count * sizeof(SkPMColor)); + } } else { const SkPMColor* src = fDevice->getAddr32(x, y); for (int i = count - 1; i >= 0; --i) { @@ -125,6 +128,9 @@ void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) { void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count) { SkASSERT(fDevice->getConfig() == SkBitmap::kRGB_565_Config); - memcpy(span, fDevice->getAddr16(x, y), count << 1); + uint16_t* src = fDevice->getAddr16(x, y); + if (src != span) { + memcpy(span, src, count << 1); + } } diff --git a/src/gpu/GrPrintf_skia.cpp b/src/gpu/GrPrintf_skia.cpp index fa8b6a7647..6da8822cc4 100644 --- a/src/gpu/GrPrintf_skia.cpp +++ b/src/gpu/GrPrintf_skia.cpp @@ -23,7 +23,7 @@ #include "SkTypes.h" void GrPrintf(const char format[], ...) { - const size_t MAX_BUFFER_SIZE = 512; + const size_t MAX_BUFFER_SIZE = 2048; char buffer[MAX_BUFFER_SIZE + 1]; va_list args; diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 5b4f529bdb..7166a07f51 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -40,6 +40,13 @@ #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw) #endif +// we use the same texture slot on GrPaint for bitmaps and shaders +// (since drawBitmap, drawSprite, and drawDevice ignore skia's shader) +enum { + kBitmapTextureIdx = 0, + kShaderTextureIdx = 0 +}; + /////////////////////////////////////////////////////////////////////////////// SkGpuDevice::SkAutoCachedTexture:: @@ -130,9 +137,9 @@ SkGpuDevice::SkGpuDevice(GrContext* context, SkASSERT(NULL != fTexture->asRenderTarget()); } #else - const GrGpu::TextureDesc desc = { - GrGpu::kRenderTarget_TextureFlag, - GrGpu::kNone_AALevel, + const GrTextureDesc desc = { + kRenderTarget_GrTextureFlagBit, + kNone_GrAALevel, this->width(), this->height(), SkGr::Bitmap2PixelConfig(bm) @@ -316,7 +323,7 @@ void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix, bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) { if (NULL != fTexture) { - paint->setTexture(fTexture); + paint->setTexture(kBitmapTextureIdx, fTexture); return true; } return false; @@ -342,7 +349,8 @@ static const GrSamplerState::SampleMode sk_bmp_type_to_sample_mode[] = { bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint, bool justAlpha, - GrPaint* grPaint) { + GrPaint* grPaint, + bool constantColor) { grPaint->fDither = skPaint.isDither(); grPaint->fAntiAlias = skPaint.isAntiAlias(); @@ -365,33 +373,44 @@ bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint, if (justAlpha) { uint8_t alpha = skPaint.getAlpha(); grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha); + // justAlpha is currently set to true only if there is a texture, + // so constantColor should not also be true. + GrAssert(!constantColor); } else { grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor()); - grPaint->setTexture(NULL); + grPaint->setTexture(kShaderTextureIdx, NULL); } SkColorFilter* colorFilter = skPaint.getColorFilter(); SkColor color; SkXfermode::Mode filterMode; if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) { - grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color); - grPaint->fColorFilterXfermode = filterMode; - } else { - grPaint->resetColorFilter(); + if (!constantColor) { + grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color); + grPaint->fColorFilterXfermode = filterMode; + return true; + } + SkColor filtered = colorFilter->filterColor(skPaint.getColor()); + grPaint->fColor = SkGr::SkColor2GrColor(filtered); } + grPaint->resetColorFilter(); return true; } bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint, SkAutoCachedTexture* act, const SkMatrix& ctm, - GrPaint* grPaint) { + GrPaint* grPaint, + bool constantColor) { SkASSERT(NULL != act); SkShader* shader = skPaint.getShader(); if (NULL == shader) { - return this->skPaint2GrPaintNoShader(skPaint, false, grPaint); - } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint)) { + return this->skPaint2GrPaintNoShader(skPaint, + false, + grPaint, + constantColor); + } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint, false)) { return false; } @@ -411,26 +430,27 @@ bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint, SkDebugf("shader->asABitmap() == kNone_BitmapType\n"); return false; } - grPaint->fSampler.setSampleMode(sampleMode); + GrSamplerState* sampler = grPaint->getTextureSampler(kShaderTextureIdx); + sampler->setSampleMode(sampleMode); if (skPaint.isFilterBitmap()) { - grPaint->fSampler.setFilter(GrSamplerState::kBilinear_Filter); + sampler->setFilter(GrSamplerState::kBilinear_Filter); } else { - grPaint->fSampler.setFilter(GrSamplerState::kNearest_Filter); + sampler->setFilter(GrSamplerState::kNearest_Filter); } - grPaint->fSampler.setWrapX(sk_tile_mode_to_grwrap(tileModes[0])); - grPaint->fSampler.setWrapY(sk_tile_mode_to_grwrap(tileModes[1])); + sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0])); + sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1])); if (GrSamplerState::kRadial2_SampleMode == sampleMode) { - grPaint->fSampler.setRadial2Params(twoPointParams[0], - twoPointParams[1], - twoPointParams[2] < 0); + sampler->setRadial2Params(twoPointParams[0], + twoPointParams[1], + twoPointParams[2] < 0); } - GrTexture* texture = act->set(this, bitmap, grPaint->fSampler); + GrTexture* texture = act->set(this, bitmap, *sampler); if (NULL == texture) { SkDebugf("Couldn't convert bitmap to texture.\n"); return false; } - grPaint->setTexture(texture); + grPaint->setTexture(kShaderTextureIdx, texture); // since our texture coords will be in local space, we wack the texture // matrix to map them back into 0...1 before we load it @@ -449,7 +469,7 @@ bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint, GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width()); matrix.postScale(s, s); } - grPaint->fSampler.setMatrix(matrix); + sampler->setMatrix(matrix); return true; } @@ -595,7 +615,11 @@ void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { GrPaint grPaint; SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + if (!this->skPaint2GrPaintShader(paint, + &act, + *draw.fMatrix, + &grPaint, + true)) { return; } @@ -626,7 +650,11 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, GrPaint grPaint; SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + if (!this->skPaint2GrPaintShader(paint, + &act, + *draw.fMatrix, + &grPaint, + true)) { return; } @@ -665,6 +693,10 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, if (paint.getMaskFilter()) { usePath = true; } + // until we aa rotated rects... + if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) { + usePath = true; + } if (usePath) { SkPath path; @@ -675,7 +707,11 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, GrPaint grPaint; SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + if (!this->skPaint2GrPaintShader(paint, + &act, + *draw.fMatrix, + &grPaint, + true)) { return; } fContext->drawRect(grPaint, rect, doStroke ? width : -1); @@ -713,6 +749,9 @@ static bool drawWithMaskFilter(GrContext* context, const SkPath& path, // we now have a device-aligned 8bit mask in dstM, ready to be drawn using // the current clip (and identity matrix) and grpaint settings + // used to compute inverse view, if necessary + GrMatrix ivm = context->getMatrix(); + GrAutoMatrix avm(context, GrMatrix::I()); const GrTextureDesc desc = { @@ -729,18 +768,29 @@ static bool drawWithMaskFilter(GrContext* context, const SkPath& path, return false; } - grp->setTexture(texture); + if (grp->hasTextureOrMask() && ivm.invert(&ivm)) { + grp->preConcatActiveSamplerMatrices(ivm); + } + + static const int MASK_IDX = GrPaint::kMaxMasks - 1; + // we assume the last mask index is available for use + GrAssert(NULL == grp->getMask(MASK_IDX)); + grp->setMask(MASK_IDX, texture); texture->unref(); - grp->fSampler.setClampNoFilter(); + grp->getMaskSampler(MASK_IDX)->setClampNoFilter(); GrRect d; d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft), GrIntToScalar(dstM.fBounds.fTop), GrIntToScalar(dstM.fBounds.fRight), GrIntToScalar(dstM.fBounds.fBottom)); - GrRect s; - s.setLTRB(0, 0, GR_Scalar1, GR_Scalar1); - context->drawRectToRect(*grp, d, s); + + GrMatrix m; + m.setTranslate(-dstM.fBounds.fLeft, -dstM.fBounds.fTop); + m.postIDiv(dstM.fBounds.width(), dstM.fBounds.height()); + grp->getMaskSampler(MASK_IDX)->setMatrix(m); + + context->drawRect(*grp, d); return true; } @@ -751,7 +801,11 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, GrPaint grPaint; SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + if (!this->skPaint2GrPaintShader(paint, + &act, + *draw.fMatrix, + &grPaint, + true)) { return; } @@ -832,8 +886,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, } } - SkGrPathIter iter(*pathPtr); - fContext->drawPath(grPaint, &iter, fill); + fContext->drawPath(grPaint, *pathPtr, fill); } void SkGpuDevice::drawBitmap(const SkDraw& draw, @@ -851,15 +904,15 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw, } GrPaint grPaint; - if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint)) { + if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) { return; } + GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx); if (paint.isFilterBitmap()) { - grPaint.fSampler.setFilter(GrSamplerState::kBilinear_Filter); + sampler->setFilter(GrSamplerState::kBilinear_Filter); } else { - grPaint.fSampler.setFilter(GrSamplerState::kNearest_Filter); + sampler->setFilter(GrSamplerState::kNearest_Filter); } - const int maxTextureDim = fContext->getMaxTextureDimension(); if (bitmap.getTexture() || (bitmap.width() <= maxTextureDim && @@ -941,27 +994,54 @@ void SkGpuDevice::internalDrawBitmap(const SkDraw& draw, return; } - grPaint->fSampler.setWrapX(GrSamplerState::kClamp_WrapMode); - grPaint->fSampler.setWrapY(GrSamplerState::kClamp_WrapMode); - grPaint->fSampler.setSampleMode(GrSamplerState::kNormal_SampleMode); - grPaint->fSampler.setMatrix(GrMatrix::I()); + GrSamplerState* sampler = grPaint->getTextureSampler(kBitmapTextureIdx); + + sampler->setWrapX(GrSamplerState::kClamp_WrapMode); + sampler->setWrapY(GrSamplerState::kClamp_WrapMode); + sampler->setSampleMode(GrSamplerState::kNormal_SampleMode); + sampler->setMatrix(GrMatrix::I()); GrTexture* texture; - SkAutoCachedTexture act(this, bitmap, grPaint->fSampler, &texture); + SkAutoCachedTexture act(this, bitmap, *sampler, &texture); if (NULL == texture) { return; } - grPaint->setTexture(texture); + grPaint->setTexture(kShaderTextureIdx, texture); GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()), GrIntToScalar(srcRect.height())); GrRect paintRect; - paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()), - GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()), - GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()), + paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()), + GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()), + GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()), GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height())); + if (GrSamplerState::kNearest_Filter != sampler->getFilter() && + (srcRect.width() < bitmap.width() || + srcRect.height() < bitmap.height())) { + // If drawing a subrect of the bitmap and filtering is enabled, + // use a constrained texture domain to avoid color bleeding + GrScalar left, top, right, bottom; + if (srcRect.width() > 1) { + GrScalar border = GR_ScalarHalf / bitmap.width(); + left = paintRect.left() + border; + right = paintRect.right() - border; + } else { + left = right = GrScalarHalf(paintRect.left() + paintRect.right()); + } + if (srcRect.height() > 1) { + GrScalar border = GR_ScalarHalf / bitmap.height(); + top = paintRect.top() + border; + bottom = paintRect.bottom() - border; + } else { + top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom()); + } + GrRect textureDomain; + textureDomain.setLTRB(left, top, right, bottom); + sampler->setTextureDomain(textureDomain); + } + fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m); } @@ -975,17 +1055,19 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, } GrPaint grPaint; - if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint)) { + if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) { return; } GrAutoMatrix avm(fContext, GrMatrix::I()); + GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx); + GrTexture* texture; - grPaint.fSampler.setClampNoFilter(); - SkAutoCachedTexture act(this, bitmap, grPaint.fSampler, &texture); + sampler->setClampNoFilter(); + SkAutoCachedTexture act(this, bitmap, *sampler, &texture); - grPaint.setTexture(texture); + grPaint.setTexture(kBitmapTextureIdx, texture); fContext->drawRectToRect(grPaint, GrRect::MakeXYWH(GrIntToScalar(left), @@ -1001,11 +1083,11 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev, GrPaint grPaint; if (!((SkGpuDevice*)dev)->bindDeviceAsTexture(&grPaint) || - !this->skPaint2GrPaintNoShader(paint, true, &grPaint)) { + !this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) { return; } - SkASSERT(NULL != grPaint.getTexture()); + SkASSERT(NULL != grPaint.getTexture(0)); const SkBitmap& bm = dev->accessBitmap(false); int w = bm.width(); @@ -1013,7 +1095,7 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev, GrAutoMatrix avm(fContext, GrMatrix::I()); - grPaint.fSampler.setClampNoFilter(); + grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter(); fContext->drawRectToRect(grPaint, GrRect::MakeXYWH(GrIntToScalar(x), @@ -1044,13 +1126,17 @@ void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, SkAutoCachedTexture act; // we ignore the shader if texs is null. if (NULL == texs) { - if (!this->skPaint2GrPaintNoShader(paint, false, &grPaint)) { + if (!this->skPaint2GrPaintNoShader(paint, + false, + &grPaint, + NULL == colors)) { return; } } else { if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, - &grPaint)) { + &grPaint, + NULL == colors)) { return; } } @@ -1167,7 +1253,11 @@ void SkGpuDevice::drawText(const SkDraw& draw, const void* text, GrPaint grPaint; SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + if (!this->skPaint2GrPaintShader(paint, + &act, + *draw.fMatrix, + &grPaint, + true)) { return; } GrTextContext context(fContext, grPaint, draw.fExtMatrix); @@ -1191,7 +1281,11 @@ void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, GrPaint grPaint; SkAutoCachedTexture act; - if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) { + if (!this->skPaint2GrPaintShader(paint, + &act, + *draw.fMatrix, + &grPaint, + true)) { return; } diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index f65cf1ee14..600c3361cb 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -112,42 +112,6 @@ GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx, bitmap->rowBytes()); } -//////////////////////////////////////////////////////////////////////////////// - - -GrPathCmd SkGrPathIter::next(GrPoint pts[]) { - GrAssert(NULL != pts); -#if SK_SCALAR_IS_GR_SCALAR - return sk_path_verb_to_gr_path_command(fIter.next((SkPoint*)pts)); -#else - Command cmd = sk_path_verb_to_gr_path_command(fIter.next(fPoints)); - int n = NumCommandPoints(cmd); - for (int i = 0; i < n; ++i) { - pts[i].fX = SkScalarToGrScalar(fPoints[i].fX); - pts[i].fY = SkScalarToGrScalar(fPoints[i].fY); - } - return cmd; -#endif -} - -GrPathCmd SkGrPathIter::next() { - return sk_path_verb_to_gr_path_command(fIter.next(NULL)); -} - -void SkGrPathIter::rewind() { - fIter.setPath(*fPath, false); -} - -GrConvexHint SkGrPathIter::convexHint() const { - return fPath->isConvex() ? kConvex_ConvexHint : - kNone_ConvexHint; -} - -bool SkGrPathIter::getConservativeBounds(GrRect* rect) const { - *rect = fPath->getBounds(); - return true; -} - /////////////////////////////////////////////////////////////////////////////// void SkGrClipIterator::reset(const SkClipStack& clipStack) { diff --git a/src/gpu/SkGrFontScaler.cpp b/src/gpu/SkGrFontScaler.cpp index e58f035fc7..eb260fb826 100644 --- a/src/gpu/SkGrFontScaler.cpp +++ b/src/gpu/SkGrFontScaler.cpp @@ -163,13 +163,13 @@ bool SkGrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed, return true; } +// we should just return const SkPath* (NULL means false) bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, GrPath* path) { const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID); const SkPath* skPath = fStrike->findPath(glyph); if (skPath) { - SkGrPathIter iter(*skPath); - path->resetFromIter(&iter); + *path = *skPath; return true; } return false; diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp new file mode 100644 index 0000000000..afa9d9a1c9 --- /dev/null +++ b/src/pdf/SkPDFCatalog.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFCatalog.h" +#include "SkPDFTypes.h" +#include "SkStream.h" +#include "SkTypes.h" + +SkPDFCatalog::SkPDFCatalog() + : fFirstPageCount(0), + fNextObjNum(1), + fNextFirstPageObjNum(0) { +} + +SkPDFCatalog::~SkPDFCatalog() {} + +SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) { + SkASSERT(findObjectIndex(obj) == -1); + SkASSERT(fNextFirstPageObjNum == 0); + if (onFirstPage) + fFirstPageCount++; + + struct Rec newEntry(obj, onFirstPage); + fCatalog.append(1, &newEntry); + return obj; +} + +size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, size_t offset) { + int objIndex = assignObjNum(obj) - 1; + SkASSERT(fCatalog[objIndex].fObjNumAssigned); + SkASSERT(fCatalog[objIndex].fFileOffset == 0); + fCatalog[objIndex].fFileOffset = offset; + + return obj->getOutputSize(this, true); +} + +void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) { + stream->writeDecAsText(assignObjNum(obj)); + stream->writeText(" 0"); // Generation number is always 0. +} + +size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) { + SkDynamicMemoryWStream buffer; + emitObjectNumber(&buffer, obj); + return buffer.getOffset(); +} + +int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const { + for (int i = 0; i < fCatalog.count(); i++) { + if (fCatalog[i].fObject == obj) + return i; + } + return -1; +} + +int SkPDFCatalog::assignObjNum(SkPDFObject* obj) { + int pos = findObjectIndex(obj); + // If this assert fails, it means you probably forgot to add an object + // to the resource list. + SkASSERT(pos >= 0); + uint32_t currentIndex = pos; + if (fCatalog[currentIndex].fObjNumAssigned) + return currentIndex + 1; + + // First assignment. + if (fNextFirstPageObjNum == 0) + fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1; + + uint32_t objNum; + if (fCatalog[currentIndex].fOnFirstPage) { + objNum = fNextFirstPageObjNum; + fNextFirstPageObjNum++; + } else { + objNum = fNextObjNum; + fNextObjNum++; + } + + // When we assign an object an object number, we put it in that array + // offset (minus 1 because object number 0 is reserved). + SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned); + if (objNum - 1 != currentIndex) + SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]); + fCatalog[objNum - 1].fObjNumAssigned = true; + return objNum; +} + +int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) { + int first = -1; + int last = fCatalog.count() - 1; + // TODO(vandebo) support linearized format. + //int last = fCatalog.count() - fFirstPageCount - 1; + //if (firstPage) { + // first = fCatalog.count() - fFirstPageCount; + // last = fCatalog.count() - 1; + //} + + stream->writeText("xref\n"); + stream->writeDecAsText(first + 1); + stream->writeText(" "); + stream->writeDecAsText(last - first + 1); + stream->writeText("\n"); + + if (first == -1) { + stream->writeText("0000000000 65535 f \n"); + first++; + } + for (int i = first; i <= last; i++) { + SkASSERT(fCatalog[i].fFileOffset > 0); + SkASSERT(fCatalog[i].fFileOffset <= 9999999999LL); + stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10); + stream->writeText(" 00000 n \n"); + } + + return fCatalog.count() + 1; +} diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp new file mode 100644 index 0000000000..c6ddf39966 --- /dev/null +++ b/src/pdf/SkPDFDevice.cpp @@ -0,0 +1,1488 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFDevice.h" + +#include "SkColor.h" +#include "SkClipStack.h" +#include "SkDraw.h" +#include "SkGlyphCache.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPDFFont.h" +#include "SkPDFFormXObject.h" +#include "SkPDFGraphicState.h" +#include "SkPDFImage.h" +#include "SkPDFShader.h" +#include "SkPDFStream.h" +#include "SkPDFTypes.h" +#include "SkPDFUtils.h" +#include "SkRect.h" +#include "SkString.h" +#include "SkTextFormatParams.h" +#include "SkTypeface.h" +#include "SkTypes.h" + +// Utility functions + +static void emit_pdf_color(SkColor color, SkWStream* result) { + SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere. + SkScalar colorMax = SkIntToScalar(0xFF); + SkPDFScalar::Append( + SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result); + result->writeText(" "); + SkPDFScalar::Append( + SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result); + result->writeText(" "); + SkPDFScalar::Append( + SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result); + result->writeText(" "); +} + +static SkPaint calculate_text_paint(const SkPaint& paint) { + SkPaint result = paint; + if (result.isFakeBoldText()) { + SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(), + kStdFakeBoldInterpKeys, + kStdFakeBoldInterpValues, + kStdFakeBoldInterpLength); + SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale); + if (result.getStyle() == SkPaint::kFill_Style) + result.setStyle(SkPaint::kStrokeAndFill_Style); + else + width += result.getStrokeWidth(); + result.setStrokeWidth(width); + } + return result; +} + +// Stolen from measure_text in SkDraw.cpp and then tweaked. +static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint, + const uint16_t* glyphs, size_t len, SkScalar* x, + SkScalar* y, SkScalar* width) { + if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL) + return; + + SkMatrix ident; + ident.reset(); + SkAutoGlyphCache autoCache(paint, &ident); + SkGlyphCache* cache = autoCache.getCache(); + + const char* start = (char*)glyphs; + const char* stop = (char*)(glyphs + len); + SkFixed xAdv = 0, yAdv = 0; + + // TODO(vandebo) This probably needs to take kerning into account. + while (start < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0); + xAdv += glyph.fAdvanceX; + yAdv += glyph.fAdvanceY; + }; + if (width) + *width = SkFixedToScalar(xAdv); + if (paint.getTextAlign() == SkPaint::kLeft_Align) + return; + + SkScalar xAdj = SkFixedToScalar(xAdv); + SkScalar yAdj = SkFixedToScalar(yAdv); + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + xAdj = SkScalarHalf(xAdj); + yAdj = SkScalarHalf(yAdj); + } + *x = *x - xAdj; + *y = *y - yAdj; +} + +static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX, + SkWStream* content) { + // Flip the text about the x-axis to account for origin swap and include + // the passed parameters. + content->writeText("1 0 "); + SkPDFScalar::Append(0 - textSkewX, content); + content->writeText(" -1 "); + SkPDFScalar::Append(x, content); + content->writeText(" "); + SkPDFScalar::Append(y, content); + content->writeText(" Tm\n"); +} + +// It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the +// later being our representation of an object in the PDF file. +struct GraphicStateEntry { + GraphicStateEntry(); + + // Compare the fields we care about when setting up a new content entry. + bool compareInitialState(const GraphicStateEntry& b); + + SkMatrix fMatrix; + // We can't do set operations on Paths, though PDF natively supports + // intersect. If the clip stack does anything other than intersect, + // we have to fall back to the region. Treat fClipStack as authoritative. + // See http://code.google.com/p/skia/issues/detail?id=221 + SkClipStack fClipStack; + SkRegion fClipRegion; + + // When emitting the content entry, we will ensure the graphic state + // is set to these values first. + SkColor fColor; + SkScalar fTextScaleX; // Zero means we don't care what the value is. + SkPaint::Style fTextFill; // Only if TextScaleX is non-zero. + int fShaderIndex; + int fGraphicStateIndex; + + // We may change the font (i.e. for Type1 support) within a + // ContentEntry. This is the one currently in effect, or NULL if none. + SkPDFFont* fFont; + // In PDF, text size has no default value. It is only valid if fFont is + // not NULL. + SkScalar fTextSize; +}; + +GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK), + fTextScaleX(SK_Scalar1), + fTextFill(SkPaint::kFill_Style), + fShaderIndex(-1), + fGraphicStateIndex(-1), + fFont(NULL), + fTextSize(SK_ScalarNaN) { + fMatrix.reset(); +} + +bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) { + return fColor == b.fColor && + fShaderIndex == b.fShaderIndex && + fGraphicStateIndex == b.fGraphicStateIndex && + fMatrix == b.fMatrix && + fClipStack == b.fClipStack && + (fTextScaleX == 0 || + b.fTextScaleX == 0 || + (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill)); +} + +class GraphicStackState { +public: + GraphicStackState(const SkClipStack& existingClipStack, + const SkRegion& existingClipRegion, + SkWStream* contentStream) + : fStackDepth(0), + fContentStream(contentStream) { + fEntries[0].fClipStack = existingClipStack; + fEntries[0].fClipRegion = existingClipRegion; + } + + void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion, + const SkIPoint& translation); + void updateMatrix(const SkMatrix& matrix); + void updateDrawingState(const GraphicStateEntry& state); + + void drainStack(); + +private: + void push(); + void pop(); + GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; } + + // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. + static const int kMaxStackDepth = 12; + GraphicStateEntry fEntries[kMaxStackDepth + 1]; + int fStackDepth; + SkWStream* fContentStream; +}; + +void GraphicStackState::drainStack() { + while (fStackDepth) { + pop(); + } +} + +void GraphicStackState::push() { + SkASSERT(fStackDepth < kMaxStackDepth); + fContentStream->writeText("q\n"); + fStackDepth++; + fEntries[fStackDepth] = fEntries[fStackDepth - 1]; +} + +void GraphicStackState::pop() { + SkASSERT(fStackDepth > 0); + fContentStream->writeText("Q\n"); + fStackDepth--; +} + +// This function initializes iter to be an interator on the "stack" argument +// and then skips over the leading entries as specified in prefix. It requires +// and asserts that "prefix" will be a prefix to "stack." +static void skip_clip_stack_prefix(const SkClipStack& prefix, + const SkClipStack& stack, + SkClipStack::B2FIter* iter) { + SkClipStack::B2FIter prefixIter(prefix); + iter->reset(stack); + + const SkClipStack::B2FIter::Clip* prefixEntry; + const SkClipStack::B2FIter::Clip* iterEntry; + + int count = 0; + for (prefixEntry = prefixIter.next(); prefixEntry; + prefixEntry = prefixIter.next(), count++) { + iterEntry = iter->next(); + SkASSERT(iterEntry); + // Because of SkClipStack does internal intersection, the last clip + // entry may differ. + if(*prefixEntry != *iterEntry) { + SkASSERT(prefixEntry->fOp == SkRegion::kIntersect_Op); + SkASSERT(iterEntry->fOp == SkRegion::kIntersect_Op); + SkASSERT((iterEntry->fRect == NULL) == + (prefixEntry->fRect == NULL)); + SkASSERT((iterEntry->fPath == NULL) == + (prefixEntry->fPath == NULL)); + // We need to back up the iterator by one but don't have that + // function, so reset and go forward by one less. + iter->reset(stack); + for (int i = 0; i < count; i++) { + iter->next(); + } + prefixEntry = prefixIter.next(); + break; + } + } + + SkASSERT(prefixEntry == NULL); +} + +static void emit_clip(SkPath* clipPath, SkRect* clipRect, + SkWStream* contentStream) { + SkASSERT(clipPath || clipRect); + + SkPath::FillType clipFill; + if (clipPath) { + SkPDFUtils::EmitPath(*clipPath, contentStream); + clipFill = clipPath->getFillType(); + } else if (clipRect) { + SkPDFUtils::AppendRectangle(*clipRect, contentStream); + clipFill = SkPath::kWinding_FillType; + } + + NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); + NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); + if (clipFill == SkPath::kEvenOdd_FillType) { + contentStream->writeText("W* n\n"); + } else { + contentStream->writeText("W n\n"); + } +} + +// TODO(vandebo) Take advantage of SkClipStack::getSaveCount(), the PDF +// graphic state stack, and the fact that we can know all the clips used +// on the page to optimize this. +void GraphicStackState::updateClip(const SkClipStack& clipStack, + const SkRegion& clipRegion, + const SkIPoint& translation) { + if (clipStack == currentEntry()->fClipStack) { + return; + } + + while (fStackDepth > 0) { + pop(); + if (clipStack == currentEntry()->fClipStack) { + return; + } + } + push(); + + // gsState->initialEntry()->fClipStack/Region specifies the clip that has + // already been applied. (If this is a top level device, then it specifies + // a clip to the content area. If this is a layer, then it specifies + // the clip in effect when the layer was created.) There's no need to + // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the + // initial clip on the parent layer. (This means there's a bug if the user + // expands the clip and then uses any xfer mode that uses dst: + // http://code.google.com/p/skia/issues/detail?id=228 ) + SkClipStack::B2FIter iter; + skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); + + // If the clip stack does anything other than intersect or if it uses + // an inverse fill type, we have to fall back to the clip region. + bool needRegion = false; + const SkClipStack::B2FIter::Clip* clipEntry; + for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { + if (clipEntry->fOp != SkRegion::kIntersect_Op || + (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) { + needRegion = true; + break; + } + } + + if (needRegion) { + SkPath clipPath; + SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); + emit_clip(&clipPath, NULL, fContentStream); + } else { + skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); + SkMatrix transform; + transform.setTranslate(translation.fX, translation.fY); + const SkClipStack::B2FIter::Clip* clipEntry; + for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { + SkASSERT(clipEntry->fOp == SkRegion::kIntersect_Op); + if (clipEntry->fRect) { + SkRect translatedClip; + transform.mapRect(&translatedClip, *clipEntry->fRect); + emit_clip(NULL, &translatedClip, fContentStream); + } else if (clipEntry->fPath) { + SkPath translatedPath; + clipEntry->fPath->transform(transform, &translatedPath); + emit_clip(&translatedPath, NULL, fContentStream); + } else { + SkASSERT(false); + } + } + } + currentEntry()->fClipStack = clipStack; + currentEntry()->fClipRegion = clipRegion; +} + +void GraphicStackState::updateMatrix(const SkMatrix& matrix) { + if (matrix == currentEntry()->fMatrix) { + return; + } + + if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { + SkASSERT(fStackDepth > 0); + SkASSERT(fEntries[fStackDepth].fClipStack == + fEntries[fStackDepth -1].fClipStack); + pop(); + + SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask); + } + if (matrix.getType() == SkMatrix::kIdentity_Mask) { + return; + } + + push(); + SkPDFUtils::AppendTransform(matrix, fContentStream); + currentEntry()->fMatrix = matrix; +} + +void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) { + // PDF treats a shader as a color, so we only set one or the other. + if (state.fShaderIndex >= 0) { + if (state.fShaderIndex != currentEntry()->fShaderIndex) { + fContentStream->writeText("/Pattern CS /Pattern cs /P"); + fContentStream->writeDecAsText(state.fShaderIndex); + fContentStream->writeText(" SCN /P"); + fContentStream->writeDecAsText(state.fShaderIndex); + fContentStream->writeText(" scn\n"); + currentEntry()->fShaderIndex = state.fShaderIndex; + } + } else { + if (state.fColor != currentEntry()->fColor || + currentEntry()->fShaderIndex >= 0) { + emit_pdf_color(state.fColor, fContentStream); + fContentStream->writeText("RG "); + emit_pdf_color(state.fColor, fContentStream); + fContentStream->writeText("rg\n"); + currentEntry()->fColor = state.fColor; + currentEntry()->fShaderIndex = -1; + } + } + + if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) { + SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream); + currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex; + } + + if (state.fTextScaleX) { + if (state.fTextScaleX != currentEntry()->fTextScaleX) { + SkScalar pdfScale = SkScalarMul(state.fTextScaleX, + SkIntToScalar(100)); + SkPDFScalar::Append(pdfScale, fContentStream); + fContentStream->writeText(" Tz\n"); + currentEntry()->fTextScaleX = state.fTextScaleX; + } + if (state.fTextFill != currentEntry()->fTextFill) { + SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value); + SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1, + enum_must_match_value); + SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2, + enum_must_match_value); + fContentStream->writeDecAsText(state.fTextFill); + fContentStream->writeText(" Tr\n"); + currentEntry()->fTextFill = state.fTextFill; + } + } +} + +struct ContentEntry { + GraphicStateEntry fState; + SkDynamicMemoryWStream fContent; + SkTScopedPtr<ContentEntry> fNext; +}; + +// A helper class to automatically finish a ContentEntry at the end of a +// drawing method and maintain the state needed between set up and finish. +class ScopedContentEntry { +public: + ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw, + const SkPaint& paint, bool hasText = false) + : fDevice(device), + fContentEntry(NULL), + fXfermode(SkXfermode::kSrcOver_Mode) { + init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText); + } + ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack, + const SkRegion& clipRegion, const SkMatrix& matrix, + const SkPaint& paint, bool hasText = false) + : fDevice(device), + fContentEntry(NULL), + fXfermode(SkXfermode::kSrcOver_Mode) { + init(clipStack, clipRegion, matrix, paint, hasText); + } + + ~ScopedContentEntry() { + if (fContentEntry) { + fDevice->finishContentEntry(fXfermode, fDstFormXObject.get()); + } + } + + ContentEntry* entry() { return fContentEntry; } +private: + SkPDFDevice* fDevice; + ContentEntry* fContentEntry; + SkXfermode::Mode fXfermode; + SkRefPtr<SkPDFFormXObject> fDstFormXObject; + + void init(const SkClipStack* clipStack, const SkRegion& clipRegion, + const SkMatrix& matrix, const SkPaint& paint, bool hasText) { + if (paint.getXfermode()) { + paint.getXfermode()->asMode(&fXfermode); + } + fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion, + matrix, paint, hasText, + &fDstFormXObject); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas* c, SkBitmap::Config config, + int width, int height, bool isOpaque, + bool isForLayer) { + SkMatrix initialTransform; + initialTransform.reset(); + SkISize size = SkISize::Make(width, height); + if (isForLayer) { + return SkNEW_ARGS(SkPDFDevice, (size, c->getTotalClipStack(), + c->getTotalClip())); + } else { + return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform)); + } +} + +static inline SkBitmap makeContentBitmap(const SkISize& contentSize, + const SkMatrix* initialTransform) { + SkBitmap bitmap; + if (initialTransform) { + // Compute the size of the drawing area. + SkVector drawingSize; + SkMatrix inverse; + drawingSize.set(contentSize.fWidth, contentSize.fHeight); + initialTransform->invert(&inverse); + inverse.mapVectors(&drawingSize, 1); + SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound(); + bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth), + abs(size.fHeight)); + } else { + bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth), + abs(contentSize.fHeight)); + } + + return bitmap; +} + +SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize, + const SkMatrix& initialTransform) + : SkDevice(NULL, makeContentBitmap(contentSize, &initialTransform), false), + fPageSize(pageSize), + fContentSize(contentSize), + fLastContentEntry(NULL) { + // Skia generally uses the top left as the origin but PDF natively has the + // origin at the bottom left. This matrix corrects for that. But that only + // needs to be done once, we don't do it when layering. + fInitialTransform.setTranslate(0, pageSize.fHeight); + fInitialTransform.preScale(1, -1); + fInitialTransform.preConcat(initialTransform); + + SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height()); + fExistingClipStack.clipDevRect(existingClip); + fExistingClipRegion.setRect(existingClip); + + this->init(); +} + +SkPDFDevice::SkPDFDevice(const SkISize& layerSize, + const SkClipStack& existingClipStack, + const SkRegion& existingClipRegion) + : SkDevice(NULL, makeContentBitmap(layerSize, NULL), false), + fPageSize(layerSize), + fContentSize(layerSize), + fExistingClipStack(existingClipStack), + fExistingClipRegion(existingClipRegion), + fLastContentEntry(NULL) { + fInitialTransform.reset(); + this->init(); +} + +SkPDFDevice::~SkPDFDevice() { + this->cleanUp(); +} + +void SkPDFDevice::init() { + fResourceDict = NULL; + fContentEntries.reset(); + fLastContentEntry = NULL; +} + +SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() { + return SkNEW(SkPDFDeviceFactory); +} + +void SkPDFDevice::cleanUp() { + fGraphicStateResources.unrefAll(); + fXObjectResources.unrefAll(); + fFontResources.unrefAll(); + fShaderResources.unrefAll(); +} + +void SkPDFDevice::clear(SkColor color) { + this->cleanUp(); + this->init(); + + SkPaint paint; + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + SkMatrix identity; + identity.reset(); + ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion, + identity, paint); + internalDrawPaint(paint, content.entry()); +} + +void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { + SkPaint newPaint = paint; + newPaint.setStyle(SkPaint::kFill_Style); + ScopedContentEntry content(this, d, newPaint); + internalDrawPaint(newPaint, content.entry()); +} + +void SkPDFDevice::internalDrawPaint(const SkPaint& paint, + ContentEntry* contentEntry) { + if (!contentEntry) { + return; + } + SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()), + SkIntToScalar(this->height())); + SkMatrix totalTransform = fInitialTransform; + totalTransform.preConcat(contentEntry->fState.fMatrix); + SkMatrix inverse; + inverse.reset(); + totalTransform.invert(&inverse); + inverse.mapRect(&bbox); + + SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent); + SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, + &contentEntry->fContent); +} + +void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, + size_t count, const SkPoint* points, + const SkPaint& passedPaint) { + if (count == 0) { + return; + } + + // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. + // We only use this when there's a path effect because of the overhead + // of multiple calls to setUpContentEntry it causes. + if (passedPaint.getPathEffect()) { + if (d.fClip->isEmpty()) { + return; + } + SkDraw pointDraw(d); + pointDraw.fDevice = this; + pointDraw.drawPoints(mode, count, points, passedPaint, true); + return; + } + + const SkPaint* paint = &passedPaint; + SkPaint modifiedPaint; + + if (mode == SkCanvas::kPoints_PointMode && + paint->getStrokeCap() != SkPaint::kRound_Cap) { + modifiedPaint = *paint; + paint = &modifiedPaint; + if (paint->getStrokeWidth()) { + // PDF won't draw a single point with square/butt caps because the + // orientation is ambiguous. Draw a rectangle instead. + modifiedPaint.setStyle(SkPaint::kFill_Style); + SkScalar strokeWidth = paint->getStrokeWidth(); + SkScalar halfStroke = SkScalarHalf(strokeWidth); + for (size_t i = 0; i < count; i++) { + SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0); + r.inset(-halfStroke, -halfStroke); + drawRect(d, r, modifiedPaint); + } + return; + } else { + modifiedPaint.setStrokeCap(SkPaint::kRound_Cap); + } + } + + ScopedContentEntry content(this, d, *paint); + if (!content.entry()) { + return; + } + + switch (mode) { + case SkCanvas::kPolygon_PointMode: + SkPDFUtils::MoveTo(points[0].fX, points[0].fY, + &content.entry()->fContent); + for (size_t i = 1; i < count; i++) { + SkPDFUtils::AppendLine(points[i].fX, points[i].fY, + &content.entry()->fContent); + } + SkPDFUtils::StrokePath(&content.entry()->fContent); + break; + case SkCanvas::kLines_PointMode: + for (size_t i = 0; i < count/2; i++) { + SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, + &content.entry()->fContent); + SkPDFUtils::AppendLine(points[i * 2 + 1].fX, + points[i * 2 + 1].fY, + &content.entry()->fContent); + SkPDFUtils::StrokePath(&content.entry()->fContent); + } + break; + case SkCanvas::kPoints_PointMode: + SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap); + for (size_t i = 0; i < count; i++) { + SkPDFUtils::MoveTo(points[i].fX, points[i].fY, + &content.entry()->fContent); + SkPDFUtils::ClosePath(&content.entry()->fContent); + SkPDFUtils::StrokePath(&content.entry()->fContent); + } + break; + default: + SkASSERT(false); + } +} + +void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r, + const SkPaint& paint) { + if (paint.getPathEffect()) { + if (d.fClip->isEmpty()) { + return; + } + SkPath path; + path.addRect(r); + drawPath(d, path, paint, NULL, true); + return; + } + + ScopedContentEntry content(this, d, paint); + if (!content.entry()) { + return; + } + SkPDFUtils::AppendRectangle(r, &content.entry()->fContent); + SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, + &content.entry()->fContent); +} + +void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath, + const SkPaint& paint, const SkMatrix* prePathMatrix, + bool pathIsMutable) { + SkPath modifiedPath; + SkPath* pathPtr = const_cast<SkPath*>(&origPath); + + SkMatrix matrix = *d.fMatrix; + if (prePathMatrix) { + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { + if (!pathIsMutable) { + pathPtr = &modifiedPath; + pathIsMutable = true; + } + origPath.transform(*prePathMatrix, pathPtr); + } else { + if (!matrix.preConcat(*prePathMatrix)) { + return; + } + } + } + + if (paint.getPathEffect()) { + if (d.fClip->isEmpty()) { + return; + } + if (!pathIsMutable) { + pathPtr = &modifiedPath; + pathIsMutable = true; + } + bool fill = paint.getFillPath(origPath, pathPtr); + + SkPaint noEffectPaint(paint); + noEffectPaint.setPathEffect(NULL); + if (fill) { + noEffectPaint.setStyle(SkPaint::kFill_Style); + } else { + noEffectPaint.setStyle(SkPaint::kStroke_Style); + noEffectPaint.setStrokeWidth(0); + } + drawPath(d, *pathPtr, noEffectPaint, NULL, true); + return; + } + + ScopedContentEntry content(this, d, paint); + if (!content.entry()) { + return; + } + SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent); + SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), + &content.entry()->fContent); +} + +void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, + const SkIRect* srcRect, const SkMatrix& matrix, + const SkPaint& paint) { + if (d.fClip->isEmpty()) { + return; + } + + SkMatrix transform = matrix; + transform.postConcat(*d.fMatrix); + internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, srcRect, + paint); +} + +void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint) { + if (d.fClip->isEmpty()) { + return; + } + + SkMatrix matrix; + matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); + internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint); +} + +void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint) { + SkPaint textPaint = calculate_text_paint(paint); + ScopedContentEntry content(this, d, textPaint, true); + if (!content.entry()) { + return; + } + + // We want the text in glyph id encoding and a writable buffer, so we end + // up making a copy either way. + size_t numGlyphs = paint.textToGlyphs(text, len, NULL); + uint16_t* glyphIDs = + (uint16_t*)sk_malloc_flags(numGlyphs * 2, + SK_MALLOC_TEMP | SK_MALLOC_THROW); + SkAutoFree autoFreeGlyphIDs(glyphIDs); + if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { + paint.textToGlyphs(text, len, glyphIDs); + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + } else { + SkASSERT((len & 1) == 0); + SkASSERT(len / 2 == numGlyphs); + memcpy(glyphIDs, text, len); + } + + SkScalar width; + SkScalar* widthPtr = NULL; + if (textPaint.isUnderlineText() || textPaint.isStrikeThruText()) + widthPtr = &width; + + SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); + align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y, + widthPtr); + content.entry()->fContent.writeText("BT\n"); + set_text_transform(x, y, textPaint.getTextSkewX(), + &content.entry()->fContent); + size_t consumedGlyphCount = 0; + while (numGlyphs > consumedGlyphCount) { + updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry()); + SkPDFFont* font = content.entry()->fState.fFont; + size_t availableGlyphs = + font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount, + numGlyphs - consumedGlyphCount); + SkString encodedString = + SkPDFString::formatString(glyphIDs + consumedGlyphCount, + availableGlyphs, font->multiByteGlyphs()); + content.entry()->fContent.writeText(encodedString.c_str()); + consumedGlyphCount += availableGlyphs; + content.entry()->fContent.writeText(" Tj\n"); + } + content.entry()->fContent.writeText("ET\n"); + + // Draw underline and/or strikethrough if the paint has them. + // drawPosText() and drawTextOnPath() don't draw underline or strikethrough + // because the raster versions don't. Use paint instead of textPaint + // because we may have changed strokeWidth to do fakeBold text. + if (paint.isUnderlineText() || paint.isStrikeThruText()) { + SkScalar textSize = paint.getTextSize(); + SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness); + + if (paint.isUnderlineText()) { + SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y); + SkRect r = SkRect::MakeXYWH(x, top - height, width, height); + drawRect(d, r, paint); + } + if (paint.isStrikeThruText()) { + SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y); + SkRect r = SkRect::MakeXYWH(x, top - height, width, height); + drawRect(d, r, paint); + } + } +} + +void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint) { + SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos); + SkPaint textPaint = calculate_text_paint(paint); + ScopedContentEntry content(this, d, textPaint, true); + if (!content.entry()) { + return; + } + + // Make sure we have a glyph id encoding. + SkAutoFree glyphStorage; + uint16_t* glyphIDs; + size_t numGlyphs; + if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) { + numGlyphs = paint.textToGlyphs(text, len, NULL); + glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2, + SK_MALLOC_TEMP | SK_MALLOC_THROW); + glyphStorage.set(glyphIDs); + paint.textToGlyphs(text, len, glyphIDs); + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + } else { + SkASSERT((len & 1) == 0); + numGlyphs = len / 2; + glyphIDs = (uint16_t*)text; + } + + SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc(); + content.entry()->fContent.writeText("BT\n"); + updateFont(textPaint, glyphIDs[0], content.entry()); + for (size_t i = 0; i < numGlyphs; i++) { + SkPDFFont* font = content.entry()->fState.fFont; + uint16_t encodedValue = glyphIDs[i]; + if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) { + updateFont(textPaint, glyphIDs[i], content.entry()); + i--; + continue; + } + SkScalar x = pos[i * scalarsPerPos]; + SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1]; + align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL); + set_text_transform(x, y, textPaint.getTextSkewX(), + &content.entry()->fContent); + SkString encodedString = + SkPDFString::formatString(&encodedValue, 1, + font->multiByteGlyphs()); + content.entry()->fContent.writeText(encodedString.c_str()); + content.entry()->fContent.writeText(" Tj\n"); + } + content.entry()->fContent.writeText("ET\n"); +} + +void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) { + if (d.fClip->isEmpty()) { + return; + } + NOT_IMPLEMENTED("drawTextOnPath", true); +} + +void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode, + int vertexCount, const SkPoint verts[], + const SkPoint texs[], const SkColor colors[], + SkXfermode* xmode, const uint16_t indices[], + int indexCount, const SkPaint& paint) { + if (d.fClip->isEmpty()) { + return; + } + NOT_IMPLEMENTED("drawVerticies", true); +} + +void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y, + const SkPaint& paint) { + if ((device->getDeviceCapabilities() & kVector_Capability) == 0) { + // If we somehow get a raster device, do what our parent would do. + SkDevice::drawDevice(d, device, x, y, paint); + return; + } + + // Assume that a vector capable device means that it's a PDF Device. + SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device); + if (pdfDevice->isContentEmpty()) { + return; + } + + SkMatrix matrix; + matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); + ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint); + if (!content.entry()) { + return; + } + + SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice); + fXObjectResources.push(xobject); // Transfer reference. + SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, + &content.entry()->fContent); +} + +const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() { + if (fResourceDict.get() == NULL) { + fResourceDict = new SkPDFDict; + fResourceDict->unref(); // SkRefPtr and new both took a reference. + + if (fGraphicStateResources.count()) { + SkRefPtr<SkPDFDict> extGState = new SkPDFDict(); + extGState->unref(); // SkRefPtr and new both took a reference. + for (int i = 0; i < fGraphicStateResources.count(); i++) { + SkString nameString("G"); + nameString.appendS32(i); + extGState->insert( + nameString.c_str(), + new SkPDFObjRef(fGraphicStateResources[i]))->unref(); + } + fResourceDict->insert("ExtGState", extGState.get()); + } + + if (fXObjectResources.count()) { + SkRefPtr<SkPDFDict> xObjects = new SkPDFDict(); + xObjects->unref(); // SkRefPtr and new both took a reference. + for (int i = 0; i < fXObjectResources.count(); i++) { + SkString nameString("X"); + nameString.appendS32(i); + xObjects->insert( + nameString.c_str(), + new SkPDFObjRef(fXObjectResources[i]))->unref(); + } + fResourceDict->insert("XObject", xObjects.get()); + } + + if (fFontResources.count()) { + SkRefPtr<SkPDFDict> fonts = new SkPDFDict(); + fonts->unref(); // SkRefPtr and new both took a reference. + for (int i = 0; i < fFontResources.count(); i++) { + SkString nameString("F"); + nameString.appendS32(i); + fonts->insert(nameString.c_str(), + new SkPDFObjRef(fFontResources[i]))->unref(); + } + fResourceDict->insert("Font", fonts.get()); + } + + if (fShaderResources.count()) { + SkRefPtr<SkPDFDict> patterns = new SkPDFDict(); + patterns->unref(); // SkRefPtr and new both took a reference. + for (int i = 0; i < fShaderResources.count(); i++) { + SkString nameString("P"); + nameString.appendS32(i); + patterns->insert(nameString.c_str(), + new SkPDFObjRef(fShaderResources[i]))->unref(); + } + fResourceDict->insert("Pattern", patterns.get()); + } + + // For compatibility, add all proc sets (only used for output to PS + // devices). + const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; + SkRefPtr<SkPDFArray> procSets = new SkPDFArray(); + procSets->unref(); // SkRefPtr and new both took a reference. + procSets->reserve(SK_ARRAY_COUNT(procs)); + for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) + procSets->append(new SkPDFName(procs[i]))->unref(); + fResourceDict->insert("ProcSet", procSets.get()); + } + return fResourceDict; +} + +void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const { + resourceList->setReserve(resourceList->count() + + fGraphicStateResources.count() + + fXObjectResources.count() + + fFontResources.count() + + fShaderResources.count()); + for (int i = 0; i < fGraphicStateResources.count(); i++) { + resourceList->push(fGraphicStateResources[i]); + fGraphicStateResources[i]->ref(); + fGraphicStateResources[i]->getResources(resourceList); + } + for (int i = 0; i < fXObjectResources.count(); i++) { + resourceList->push(fXObjectResources[i]); + fXObjectResources[i]->ref(); + fXObjectResources[i]->getResources(resourceList); + } + for (int i = 0; i < fFontResources.count(); i++) { + resourceList->push(fFontResources[i]); + fFontResources[i]->ref(); + fFontResources[i]->getResources(resourceList); + } + for (int i = 0; i < fShaderResources.count(); i++) { + resourceList->push(fShaderResources[i]); + fShaderResources[i]->ref(); + fShaderResources[i]->getResources(resourceList); + } +} + +const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const { + return fFontResources; +} + +SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const { + SkRefPtr<SkPDFInt> zero = new SkPDFInt(0); + zero->unref(); // SkRefPtr and new both took a reference. + + SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray(); + mediaBox->unref(); // SkRefPtr and new both took a reference. + mediaBox->reserve(4); + mediaBox->append(zero.get()); + mediaBox->append(zero.get()); + mediaBox->append(new SkPDFInt(fPageSize.fWidth))->unref(); + mediaBox->append(new SkPDFInt(fPageSize.fHeight))->unref(); + return mediaBox; +} + +SkStream* SkPDFDevice::content() const { + SkDynamicMemoryWStream data; + if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { + SkPDFUtils::AppendTransform(fInitialTransform, &data); + } + // If the content area is the entire page, then we don't need to clip + // the content area (PDF area clips to the page size). Otherwise, + // we have to clip to the content area; we've already applied the + // initial transform, so just clip to the device size. + if (fPageSize != fContentSize) { + SkRect r = SkRect::MakeWH(this->width(), this->height()); + emit_clip(NULL, &r, &data); + } + + GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, &data); + for (ContentEntry* entry = fContentEntries.get(); + entry != NULL; + entry = entry->fNext.get()) { + SkIPoint translation = this->getOrigin(); + translation.negate(); + gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion, + translation); + gsState.updateMatrix(entry->fState.fMatrix); + gsState.updateDrawingState(entry->fState); + data.write(entry->fContent.getStream(), entry->fContent.getOffset()); + } + gsState.drainStack(); + + SkMemoryStream* result = new SkMemoryStream; + result->setMemoryOwned(data.detach(), data.getOffset()); + return result; +} + +void SkPDFDevice::createFormXObjectFromDevice( + SkRefPtr<SkPDFFormXObject>* xobject) { + *xobject = new SkPDFFormXObject(this); + (*xobject)->unref(); // SkRefPtr and new both took a reference. + cleanUp(); // Reset this device to have no content. + init(); +} + +void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack, + const SkRegion& clipRegion) { + if (clipRegion.isEmpty() || isContentEmpty()) { + return; + } + SkRefPtr<SkPDFFormXObject> curContent; + createFormXObjectFromDevice(&curContent); + + // Redraw what we already had, but with the clip as a mask. + drawFormXObjectWithClip(curContent.get(), clipStack, clipRegion, true); +} + +void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject, + const SkClipStack* clipStack, + const SkRegion& clipRegion, + bool invertClip) { + if (clipRegion.isEmpty() && !invertClip) { + return; + } + + // Create the mask. + SkMatrix identity; + identity.reset(); + SkDraw draw; + draw.fMatrix = &identity; + draw.fClip = &clipRegion; + draw.fClipStack = clipStack; + SkPaint stockPaint; + this->drawPaint(draw, stockPaint); + SkRefPtr<SkPDFFormXObject> maskFormXObject; + createFormXObjectFromDevice(&maskFormXObject); + SkRefPtr<SkPDFGraphicState> sMaskGS = + SkPDFGraphicState::getSMaskGraphicState(maskFormXObject.get(), + invertClip); + sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. + + // Draw the xobject with the clip as a mask. + ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion, + identity, stockPaint); + if (!content.entry()) { + return; + } + SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), + &content.entry()->fContent); + SkPDFUtils::DrawFormXObject(fXObjectResources.count(), + &content.entry()->fContent); + fXObjectResources.push(xobject); + xobject->ref(); + + sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState(); + sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. + SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), + &content.entry()->fContent); +} + +ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack, + const SkRegion& clipRegion, + const SkMatrix& matrix, + const SkPaint& paint, + bool hasText, + SkRefPtr<SkPDFFormXObject>* dst) { + if (clipRegion.isEmpty()) { + return NULL; + } + + // The clip stack can come from an SkDraw where it is technically optional. + SkClipStack synthesizedClipStack; + if (clipStack == NULL) { + if (clipRegion == fExistingClipRegion) { + clipStack = &fExistingClipStack; + } else { + // GraphicStackState::updateClip expects the clip stack to have + // fExistingClip as a prefix, so start there, then set the clip + // to the passed region. + synthesizedClipStack = fExistingClipStack; + SkPath clipPath; + clipRegion.getBoundaryPath(&clipPath); + synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op); + clipStack = &synthesizedClipStack; + } + } + + SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; + if (paint.getXfermode()) { + paint.getXfermode()->asMode(&xfermode); + } + + if (xfermode == SkXfermode::kClear_Mode || + xfermode == SkXfermode::kSrc_Mode) { + this->clearClipFromContent(clipStack, clipRegion); + } else if (xfermode == SkXfermode::kSrcIn_Mode || + xfermode == SkXfermode::kDstIn_Mode || + xfermode == SkXfermode::kSrcOut_Mode || + xfermode == SkXfermode::kDstOut_Mode) { + // For the following modes, we use both source and destination, but + // we use one as a smask for the other, so we have to make form xobjects + // out of both of them: SrcIn, DstIn, SrcOut, DstOut. + if (isContentEmpty()) { + return NULL; + } else { + createFormXObjectFromDevice(dst); + } + } + // TODO(vandebo) Figure out how/if we can handle the following modes: + // SrcAtop, DestAtop, Xor, Plus. + + // These xfer modes don't draw source at all. + if (xfermode == SkXfermode::kClear_Mode || + xfermode == SkXfermode::kDst_Mode) { + return NULL; + } + + ContentEntry* entry; + SkTScopedPtr<ContentEntry> newEntry; + if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) { + entry = fLastContentEntry; + } else { + newEntry.reset(new ContentEntry); + entry = newEntry.get(); + } + + populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint, + hasText, &entry->fState); + if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode && + entry->fState.compareInitialState(fLastContentEntry->fState)) { + return fLastContentEntry; + } + + if (!fLastContentEntry) { + fContentEntries.reset(entry); + fLastContentEntry = entry; + } else if (xfermode == SkXfermode::kDstOver_Mode) { + entry->fNext.reset(fContentEntries.release()); + fContentEntries.reset(entry); + } else { + fLastContentEntry->fNext.reset(entry); + fLastContentEntry = entry; + } + newEntry.release(); + return entry; +} + +void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode, + SkPDFFormXObject* dst) { + if (xfermode != SkXfermode::kSrcIn_Mode && + xfermode != SkXfermode::kDstIn_Mode && + xfermode != SkXfermode::kSrcOut_Mode && + xfermode != SkXfermode::kDstOut_Mode) { + SkASSERT(!dst); + return; + } + SkASSERT(dst); + SkASSERT(!fContentEntries->fNext.get()); + + // We have to make a copy of these here because changing the current + // content into a form xobject will destroy them. + SkClipStack clipStack = fContentEntries->fState.fClipStack; + SkRegion clipRegion = fContentEntries->fState.fClipRegion; + + SkRefPtr<SkPDFFormXObject> srcFormXObject; + if (!isContentEmpty()) { + createFormXObjectFromDevice(&srcFormXObject); + } + + drawFormXObjectWithClip(dst, &clipStack, clipRegion, true); + + // We've redrawn dst minus the clip area, if there's no src, we're done. + if (!srcFormXObject.get()) { + return; + } + + SkMatrix identity; + identity.reset(); + SkPaint stockPaint; + ScopedContentEntry inClipContentEntry(this, &fExistingClipStack, + fExistingClipRegion, identity, + stockPaint); + if (!inClipContentEntry.entry()) { + return; + } + + SkRefPtr<SkPDFGraphicState> sMaskGS; + if (xfermode == SkXfermode::kSrcIn_Mode || + xfermode == SkXfermode::kSrcOut_Mode) { + sMaskGS = SkPDFGraphicState::getSMaskGraphicState( + dst, xfermode == SkXfermode::kSrcOut_Mode); + fXObjectResources.push(srcFormXObject.get()); + srcFormXObject->ref(); + } else { + sMaskGS = SkPDFGraphicState::getSMaskGraphicState( + srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode); + // dst already added to fXObjectResources in drawFormXObjectWithClip. + } + sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. + SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), + &inClipContentEntry.entry()->fContent); + + SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, + &inClipContentEntry.entry()->fContent); + + sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState(); + sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref. + SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), + &inClipContentEntry.entry()->fContent); +} + +bool SkPDFDevice::isContentEmpty() { + if (!fContentEntries.get() || fContentEntries->fContent.getOffset() == 0) { + SkASSERT(!fContentEntries.get() || !fContentEntries->fNext.get()); + return true; + } + return false; +} + + +void SkPDFDevice::populateGraphicStateEntryFromPaint( + const SkMatrix& matrix, + const SkClipStack& clipStack, + const SkRegion& clipRegion, + const SkPaint& paint, + bool hasText, + GraphicStateEntry* entry) { + SkASSERT(paint.getPathEffect() == NULL); + + NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false); + NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false); + + entry->fMatrix = matrix; + entry->fClipStack = clipStack; + entry->fClipRegion = clipRegion; + + // PDF treats a shader as a color, so we only set one or the other. + SkRefPtr<SkPDFShader> pdfShader; + const SkShader* shader = paint.getShader(); + SkColor color = paint.getColor(); + if (shader) { + // PDF positions patterns relative to the initial transform, so + // we need to apply the current transform to the shader parameters. + SkMatrix transform = matrix; + transform.postConcat(fInitialTransform); + + // PDF doesn't support kClamp_TileMode, so we simulate it by making + // a pattern the size of the current clip. + SkIRect bounds = clipRegion.getBounds(); + pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds); + SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref + + // A color shader is treated as an invalid shader so we don't have + // to set a shader just for a color. + if (pdfShader.get() == NULL) { + entry->fColor = 0; + color = 0; + + // Check for a color shader. + SkShader::GradientInfo gradientInfo; + SkColor gradientColor; + gradientInfo.fColors = &gradientColor; + gradientInfo.fColorOffsets = NULL; + gradientInfo.fColorCount = 1; + if (shader->asAGradient(&gradientInfo) == + SkShader::kColor_GradientType) { + entry->fColor = SkColorSetA(gradientColor, 0xFF); + color = gradientColor; + } + } + } + + if (pdfShader) { + // pdfShader has been canonicalized so we can directly compare + // pointers. + int resourceIndex = fShaderResources.find(pdfShader.get()); + if (resourceIndex < 0) { + resourceIndex = fShaderResources.count(); + fShaderResources.push(pdfShader.get()); + pdfShader->ref(); + } + entry->fShaderIndex = resourceIndex; + } else { + entry->fShaderIndex = -1; + entry->fColor = SkColorSetA(paint.getColor(), 0xFF); + color = paint.getColor(); + } + + SkRefPtr<SkPDFGraphicState> newGraphicState; + if (color == paint.getColor()) { + newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(paint); + } else { + SkPaint newPaint = paint; + newPaint.setColor(color); + newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint); + } + newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref. + int resourceIndex = addGraphicStateResource(newGraphicState.get()); + entry->fGraphicStateIndex = resourceIndex; + + if (hasText) { + entry->fTextScaleX = paint.getTextScaleX(); + entry->fTextFill = paint.getStyle(); + } else { + entry->fTextScaleX = 0; + } +} + +int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) { + // Assumes that gs has been canonicalized (so we can directly compare + // pointers). + int result = fGraphicStateResources.find(gs); + if (result < 0) { + result = fGraphicStateResources.count(); + fGraphicStateResources.push(gs); + gs->ref(); + } + return result; +} + +void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID, + ContentEntry* contentEntry) { + SkTypeface* typeface = paint.getTypeface(); + if (contentEntry->fState.fFont == NULL || + contentEntry->fState.fTextSize != paint.getTextSize() || + !contentEntry->fState.fFont->hasGlyph(glyphID)) { + int fontIndex = getFontResourceIndex(typeface, glyphID); + contentEntry->fContent.writeText("/F"); + contentEntry->fContent.writeDecAsText(fontIndex); + contentEntry->fContent.writeText(" "); + SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent); + contentEntry->fContent.writeText(" Tf\n"); + contentEntry->fState.fFont = fFontResources[fontIndex]; + } +} + +int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) { + SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID); + newFont->unref(); // getFontResource and SkRefPtr both took a ref. + int resourceIndex = fFontResources.find(newFont.get()); + if (resourceIndex < 0) { + resourceIndex = fFontResources.count(); + fFontResources.push(newFont.get()); + newFont->ref(); + } + return resourceIndex; +} + +void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix, + const SkClipStack* clipStack, + const SkRegion& clipRegion, + const SkBitmap& bitmap, + const SkIRect* srcRect, + const SkPaint& paint) { + SkMatrix scaled; + // Adjust for origin flip. + scaled.setScale(1, -1); + scaled.postTranslate(0, 1); + // Scale the image up from 1x1 to WxH. + SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + scaled.postScale(SkIntToScalar(subset.width()), + SkIntToScalar(subset.height())); + scaled.postConcat(matrix); + ScopedContentEntry content(this, clipStack, clipRegion, scaled, paint); + if (!content.entry()) { + return; + } + + if (srcRect && !subset.intersect(*srcRect)) { + return; + } + + SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint); + if (!image) { + return; + } + + fXObjectResources.push(image); // Transfer reference. + SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1, + &content.entry()->fContent); +} diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp new file mode 100644 index 0000000000..95370b4642 --- /dev/null +++ b/src/pdf/SkPDFDocument.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFDevice.h" +#include "SkPDFDocument.h" +#include "SkPDFPage.h" +#include "SkStream.h" + +// Add the resources, starting at firstIndex to the catalog, removing any dupes. +// A hash table would be really nice here. +void addResourcesToCatalog(int firstIndex, bool firstPage, + SkTDArray<SkPDFObject*>* resourceList, + SkPDFCatalog* catalog) { + for (int i = firstIndex; i < resourceList->count(); i++) { + int index = resourceList->find((*resourceList)[i]); + if (index != i) { + (*resourceList)[i]->unref(); + resourceList->removeShuffle(i); + i--; + } else { + catalog->addObject((*resourceList)[i], firstPage); + } + } +} + +SkPDFDocument::SkPDFDocument() : fXRefFileOffset(0) { + fDocCatalog = new SkPDFDict("Catalog"); + fDocCatalog->unref(); // SkRefPtr and new both took a reference. + fCatalog.addObject(fDocCatalog.get(), true); +} + +SkPDFDocument::~SkPDFDocument() { + fPages.safeUnrefAll(); + + // The page tree has both child and parent pointers, so it creates a + // reference cycle. We must clear that cycle to properly reclaim memory. + for (int i = 0; i < fPageTree.count(); i++) + fPageTree[i]->clear(); + fPageTree.safeUnrefAll(); + fPageResources.safeUnrefAll(); +} + +bool SkPDFDocument::emitPDF(SkWStream* stream) { + if (fPages.isEmpty()) + return false; + + // We haven't emitted the document before if fPageTree is empty. + if (fPageTree.count() == 0) { + SkPDFDict* pageTreeRoot; + SkPDFPage::generatePageTree(fPages, &fCatalog, &fPageTree, + &pageTreeRoot); + fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref(); + + /* TODO(vandebo) output intent + SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent"); + outputIntent->unref(); // SkRefPtr and new both took a reference. + outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref(); + outputIntent->insert("OutputConditionIdentifier", + new SkPDFString("sRGB"))->unref(); + SkRefPtr<SkPDFArray> intentArray = new SkPDFArray; + intentArray->unref(); // SkRefPtr and new both took a reference. + intentArray->append(outputIntent.get()); + fDocCatalog->insert("OutputIntent", intentArray.get()); + */ + + bool first_page = true; + for (int i = 0; i < fPages.count(); i++) { + int resourceCount = fPageResources.count(); + fPages[i]->finalizePage(&fCatalog, first_page, &fPageResources); + addResourcesToCatalog(resourceCount, first_page, &fPageResources, + &fCatalog); + if (i == 0) { + first_page = false; + fSecondPageFirstResourceIndex = fPageResources.count(); + } + } + + // Figure out the size of things and inform the catalog of file offsets. + off_t fileOffset = headerSize(); + fileOffset += fCatalog.setFileOffset(fDocCatalog.get(), fileOffset); + fileOffset += fCatalog.setFileOffset(fPages[0], fileOffset); + fileOffset += fPages[0]->getPageSize(&fCatalog, fileOffset); + for (int i = 0; i < fSecondPageFirstResourceIndex; i++) + fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset); + if (fPages.count() > 1) { + // TODO(vandebo) For linearized format, save the start of the + // first page xref table and calculate the size. + } + + for (int i = 0; i < fPageTree.count(); i++) + fileOffset += fCatalog.setFileOffset(fPageTree[i], fileOffset); + + for (int i = 1; i < fPages.count(); i++) + fileOffset += fPages[i]->getPageSize(&fCatalog, fileOffset); + + for (int i = fSecondPageFirstResourceIndex; + i < fPageResources.count(); + i++) + fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset); + + fXRefFileOffset = fileOffset; + } + + emitHeader(stream); + fDocCatalog->emitObject(stream, &fCatalog, true); + fPages[0]->emitObject(stream, &fCatalog, true); + fPages[0]->emitPage(stream, &fCatalog); + for (int i = 0; i < fSecondPageFirstResourceIndex; i++) + fPageResources[i]->emitObject(stream, &fCatalog, true); + // TODO(vandebo) support linearized format + //if (fPages.size() > 1) { + // // TODO(vandebo) save the file offset for the first page xref table. + // fCatalog.emitXrefTable(stream, true); + //} + + for (int i = 0; i < fPageTree.count(); i++) + fPageTree[i]->emitObject(stream, &fCatalog, true); + + for (int i = 1; i < fPages.count(); i++) + fPages[i]->emitPage(stream, &fCatalog); + + for (int i = fSecondPageFirstResourceIndex; i < fPageResources.count(); i++) + fPageResources[i]->emitObject(stream, &fCatalog, true); + + int64_t objCount = fCatalog.emitXrefTable(stream, fPages.count() > 1); + emitFooter(stream, objCount); + return true; +} + +bool SkPDFDocument::appendPage(const SkRefPtr<SkPDFDevice>& pdfDevice) { + if (fPageTree.count() != 0) + return false; + + SkPDFPage* page = new SkPDFPage(pdfDevice); + fPages.push(page); // Reference from new passed to fPages. + // The rest of the pages will be added to the catalog along with the rest + // of the page tree. But the first page has to be marked as such, so we + // handle it here. + if (fPages.count() == 1) + fCatalog.addObject(page, true); + return true; +} + +const SkTDArray<SkPDFPage*>& SkPDFDocument::getPages() { + return fPages; +} + +void SkPDFDocument::emitHeader(SkWStream* stream) { + stream->writeText("%PDF-1.4\n%"); + // The PDF spec recommends including a comment with four bytes, all + // with their high bits set. This is "Skia" with the high bits set. + stream->write32(0xD3EBE9E1); + stream->writeText("\n"); +} + +size_t SkPDFDocument::headerSize() { + SkDynamicMemoryWStream buffer; + emitHeader(&buffer); + return buffer.getOffset(); +} + +void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) { + if (fTrailerDict.get() == NULL) { + fTrailerDict = new SkPDFDict(); + fTrailerDict->unref(); // SkRefPtr and new both took a reference. + + // TODO(vandebo) Linearized format will take a Prev entry too. + // TODO(vandebo) PDF/A requires an ID entry. + fTrailerDict->insert("Size", new SkPDFInt(objCount))->unref(); + fTrailerDict->insert("Root", + new SkPDFObjRef(fDocCatalog.get()))->unref(); + } + + stream->writeText("trailer\n"); + fTrailerDict->emitObject(stream, &fCatalog, false); + stream->writeText("\nstartxref\n"); + stream->writeBigDecAsText(fXRefFileOffset); + stream->writeText("\n%%EOF"); +} diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp new file mode 100755 index 0000000000..277ed12ac3 --- /dev/null +++ b/src/pdf/SkPDFFont.cpp @@ -0,0 +1,976 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> + +#include "SkFontHost.h" +#include "SkGlyphCache.h" +#include "SkPaint.h" +#include "SkPDFDevice.h" +#include "SkPDFFont.h" +#include "SkPDFStream.h" +#include "SkPDFTypes.h" +#include "SkPDFUtils.h" +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkStream.h" +#include "SkTypeface.h" +#include "SkTypes.h" +#include "SkUtils.h" + +namespace { + +bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType, + size_t* size) { + // PFB sections have a two or six bytes header. 0x80 and a one byte + // section type followed by a four byte section length. Type one is + // an ASCII section (includes a length), type two is a binary section + // (includes a length) and type three is an EOF marker with no length. + const uint8_t* buf = *src; + if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType) + return false; + if (buf[1] == 3) + return true; + if (*len < 6) + return false; + + *size = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24); + size_t consumed = *size + 6; + if (consumed > *len) + return false; + *src = *src + consumed; + *len = *len - consumed; + return true; +} + +bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen, + size_t* dataLen, size_t* trailerLen) { + const uint8_t* srcPtr = src; + size_t remaining = size; + + return parsePFBSection(&srcPtr, &remaining, 1, headerLen) && + parsePFBSection(&srcPtr, &remaining, 2, dataLen) && + parsePFBSection(&srcPtr, &remaining, 1, trailerLen) && + parsePFBSection(&srcPtr, &remaining, 3, NULL); +} + +/* The sections of a PFA file are implicitly defined. The body starts + * after the line containing "eexec," and the trailer starts with 512 + * literal 0's followed by "cleartomark" (plus arbitrary white space). + * + * This function assumes that src is NUL terminated, but the NUL + * termination is not included in size. + * + */ +bool parsePFA(const char* src, size_t size, size_t* headerLen, + size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) { + const char* end = src + size; + + const char* dataPos = strstr(src, "eexec"); + if (!dataPos) + return false; + dataPos += strlen("eexec"); + while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') && + dataPos < end) + dataPos++; + *headerLen = dataPos - src; + + const char* trailerPos = strstr(dataPos, "cleartomark"); + if (!trailerPos) + return false; + int zeroCount = 0; + for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) { + if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') { + continue; + } else if (*trailerPos == '0') { + zeroCount++; + } else { + return false; + } + } + if (zeroCount != 512) + return false; + + *hexDataLen = trailerPos - src - *headerLen; + *trailerLen = size - *headerLen - *hexDataLen; + + // Verify that the data section is hex encoded and count the bytes. + int nibbles = 0; + for (; dataPos < trailerPos; dataPos++) { + if (isspace(*dataPos)) + continue; + if (!isxdigit(*dataPos)) + return false; + nibbles++; + } + *dataLen = (nibbles + 1) / 2; + + return true; +} + +int8_t hexToBin(uint8_t c) { + if (!isxdigit(c)) + return -1; + if (c <= '9') return c - '0'; + if (c <= 'F') return c - 'A' + 10; + if (c <= 'f') return c - 'a' + 10; + return -1; +} + +SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen, + size_t* dataLen, size_t* trailerLen) { + // srcStream may be backed by a file or a unseekable fd, so we may not be + // able to use skip(), rewind(), or getMemoryBase(). read()ing through + // the input only once is doable, but very ugly. Furthermore, it'd be nice + // if the data was NUL terminated so that we can use strstr() to search it. + // Make as few copies as possible given these constraints. + SkDynamicMemoryWStream dynamicStream; + SkRefPtr<SkMemoryStream> staticStream; + const uint8_t* src; + size_t srcLen; + if ((srcLen = srcStream->getLength()) > 0) { + staticStream = new SkMemoryStream(srcLen + 1); + staticStream->unref(); // new and SkRefPtr both took a ref. + src = (const uint8_t*)staticStream->getMemoryBase(); + if (srcStream->getMemoryBase() != NULL) { + memcpy((void *)src, srcStream->getMemoryBase(), srcLen); + } else { + size_t read = 0; + while (read < srcLen) { + size_t got = srcStream->read((void *)staticStream->getAtPos(), + srcLen - read); + if (got == 0) + return NULL; + read += got; + staticStream->seek(read); + } + } + ((uint8_t *)src)[srcLen] = 0; + } else { + static const size_t bufSize = 4096; + uint8_t buf[bufSize]; + size_t amount; + while ((amount = srcStream->read(buf, bufSize)) > 0) + dynamicStream.write(buf, amount); + amount = 0; + dynamicStream.write(&amount, 1); // NULL terminator. + // getStream makes another copy, but we couldn't do any better. + src = (const uint8_t*)dynamicStream.getStream(); + srcLen = dynamicStream.getOffset() - 1; + } + + if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) { + SkMemoryStream* result = + new SkMemoryStream(*headerLen + *dataLen + *trailerLen); + memcpy((char*)result->getAtPos(), src + 6, *headerLen); + result->seek(*headerLen); + memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen); + result->seek(*headerLen + *dataLen); + memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen, + *trailerLen); + result->rewind(); + return result; + } + + // A PFA has to be converted for PDF. + size_t hexDataLen; + if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen, + trailerLen)) { + SkMemoryStream* result = + new SkMemoryStream(*headerLen + *dataLen + *trailerLen); + memcpy((char*)result->getAtPos(), src, *headerLen); + result->seek(*headerLen); + + const uint8_t* hexData = src + *headerLen; + const uint8_t* trailer = hexData + hexDataLen; + size_t outputOffset = 0; + uint8_t dataByte = 0; // To hush compiler. + bool highNibble = true; + for (; hexData < trailer; hexData++) { + char curNibble = hexToBin(*hexData); + if (curNibble < 0) + continue; + if (highNibble) { + dataByte = curNibble << 4; + highNibble = false; + } else { + dataByte |= curNibble; + highNibble = true; + ((char *)result->getAtPos())[outputOffset++] = dataByte; + } + } + if (!highNibble) + ((char *)result->getAtPos())[outputOffset++] = dataByte; + SkASSERT(outputOffset == *dataLen); + result->seek(*headerLen + outputOffset); + + memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen, + *trailerLen); + result->rewind(); + return result; + } + + return NULL; +} + +// scale from em-units to base-1000, returning as a SkScalar +SkScalar scaleFromFontUnits(int16_t val, uint16_t emSize) { + SkScalar scaled = SkIntToScalar(val); + if (emSize == 1000) { + return scaled; + } else { + return SkScalarMulDiv(scaled, 1000, emSize); + } +} + +void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box, + SkWStream* content) { + // Specify width and bounding box for the glyph. + SkPDFScalar::Append(width, content); + content->writeText(" 0 "); + content->writeDecAsText(box.fLeft); + content->writeText(" "); + content->writeDecAsText(box.fTop); + content->writeText(" "); + content->writeDecAsText(box.fRight); + content->writeText(" "); + content->writeDecAsText(box.fBottom); + content->writeText(" d1\n"); +} + +SkPDFArray* makeFontBBox(SkIRect glyphBBox, uint16_t emSize) { + SkPDFArray* bbox = new SkPDFArray; + bbox->reserve(4); + bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fLeft, + emSize)))->unref(); + bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fBottom, + emSize)))->unref(); + bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fRight, + emSize)))->unref(); + bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fTop, + emSize)))->unref(); + return bbox; +} + +SkPDFArray* appendWidth(const int16_t& width, uint16_t emSize, + SkPDFArray* array) { + array->append(new SkPDFScalar(scaleFromFontUnits(width, emSize)))->unref(); + return array; +} + +SkPDFArray* appendVerticalAdvance( + const SkAdvancedTypefaceMetrics::VerticalMetric& advance, + uint16_t emSize, SkPDFArray* array) { + appendWidth(advance.fVerticalAdvance, emSize, array); + appendWidth(advance.fOriginXDisp, emSize, array); + appendWidth(advance.fOriginYDisp, emSize, array); + return array; +} + +template <typename Data> +SkPDFArray* composeAdvanceData( + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* advanceInfo, + uint16_t emSize, + SkPDFArray* (*appendAdvance)(const Data& advance, uint16_t emSize, + SkPDFArray* array), + Data* defaultAdvance) { + SkPDFArray* result = new SkPDFArray(); + for (; advanceInfo != NULL; advanceInfo = advanceInfo->fNext.get()) { + switch (advanceInfo->fType) { + case SkAdvancedTypefaceMetrics::WidthRange::kDefault: { + SkASSERT(advanceInfo->fAdvance.count() == 1); + *defaultAdvance = advanceInfo->fAdvance[0]; + break; + } + case SkAdvancedTypefaceMetrics::WidthRange::kRange: { + SkRefPtr<SkPDFArray> advanceArray = new SkPDFArray(); + advanceArray->unref(); // SkRefPtr and new both took a ref. + for (int j = 0; j < advanceInfo->fAdvance.count(); j++) + appendAdvance(advanceInfo->fAdvance[j], emSize, + advanceArray.get()); + result->append(new SkPDFInt(advanceInfo->fStartId))->unref(); + result->append(advanceArray.get()); + break; + } + case SkAdvancedTypefaceMetrics::WidthRange::kRun: { + SkASSERT(advanceInfo->fAdvance.count() == 1); + result->append(new SkPDFInt(advanceInfo->fStartId))->unref(); + result->append(new SkPDFInt(advanceInfo->fEndId))->unref(); + appendAdvance(advanceInfo->fAdvance[0], emSize, result); + break; + } + } + } + return result; +} + +} // namespace + +static void append_tounicode_header(SkDynamicMemoryWStream* cmap) { + // 12 dict begin: 12 is an Adobe-suggested value. Shall not change. + // It's there to prevent old version Adobe Readers from malfunctioning. + const char* kHeader = + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n"; + cmap->writeText(kHeader); + + // The /CIDSystemInfo must be consistent to the one in + // SkPDFFont::populateCIDFont(). + // We can not pass over the system info object here because the format is + // different. This is not a reference object. + const char* kSysInfo = + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n"; + cmap->writeText(kSysInfo); + + // The CMapName must be consistent to /CIDSystemInfo above. + // /CMapType 2 means ToUnicode. + // We specify codespacerange from 0x0000 to 0xFFFF because we convert our + // code table from unsigned short (16-bits). Codespace range just tells the + // PDF processor the valid range. It does not matter whether a complete + // mapping is provided or not. + const char* kTypeInfo = + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n" + "<0000> <FFFF>\n" + "endcodespacerange\n"; + cmap->writeText(kTypeInfo); +} + +static void append_cmap_bfchar_table(uint16_t* glyph_id, SkUnichar* unicode, + size_t count, + SkDynamicMemoryWStream* cmap) { + cmap->writeDecAsText(count); + cmap->writeText(" beginbfchar\n"); + for (size_t i = 0; i < count; ++i) { + cmap->writeText("<"); + cmap->writeHexAsText(glyph_id[i], 4); + cmap->writeText("> <"); + cmap->writeHexAsText(unicode[i], 4); + cmap->writeText(">\n"); + } + cmap->writeText("endbfchar\n"); +} + +static void append_cmap_footer(SkDynamicMemoryWStream* cmap) { + const char* kFooter = + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end"; + cmap->writeText(kFooter); +} + +// Generate <bfchar> table according to PDF spec 1.4 and Adobe Technote 5014. +static void append_cmap_bfchar_sections( + const SkTDArray<SkUnichar>& glyphUnicode, + SkDynamicMemoryWStream* cmap) { + // PDF spec defines that every bf* list can have at most 100 entries. + const size_t kMaxEntries = 100; + uint16_t glyphId[kMaxEntries]; + SkUnichar unicode[kMaxEntries]; + size_t index = 0; + for (int i = 0; i < glyphUnicode.count(); i++) { + if (glyphUnicode[i]) { + glyphId[index] = i; + unicode[index] = glyphUnicode[i]; + ++index; + } + if (index == kMaxEntries) { + append_cmap_bfchar_table(glyphId, unicode, index, cmap); + index = 0; + } + } + + if (index) { + append_cmap_bfchar_table(glyphId, unicode, index, cmap); + } +} + +/* Font subset design: It would be nice to be able to subset fonts + * (particularly type 3 fonts), but it's a lot of work and not a priority. + * + * Resources are canonicalized and uniqueified by pointer so there has to be + * some additional state indicating which subset of the font is used. It + * must be maintained at the page granularity and then combined at the document + * granularity. a) change SkPDFFont to fill in its state on demand, kind of + * like SkPDFGraphicState. b) maintain a per font glyph usage class in each + * page/pdf device. c) in the document, retrieve the per font glyph usage + * from each page and combine it and ask for a resource with that subset. + */ + +SkPDFFont::~SkPDFFont() { + SkAutoMutexAcquire lock(canonicalFontsMutex()); + int index; + if (find(SkTypeface::UniqueID(fTypeface.get()), fFirstGlyphID, &index)) { + canonicalFonts().removeShuffle(index); +#ifdef SK_DEBUG + SkASSERT(!fDescendant); + } else { + SkASSERT(fDescendant); +#endif + } + fResources.unrefAll(); +} + +void SkPDFFont::getResources(SkTDArray<SkPDFObject*>* resourceList) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + fResources[i]->getResources(resourceList); + } +} + +SkTypeface* SkPDFFont::typeface() { + return fTypeface.get(); +} + +SkAdvancedTypefaceMetrics::FontType SkPDFFont::getType() { + return fType; +} + +bool SkPDFFont::hasGlyph(uint16_t id) { + return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0; +} + +bool SkPDFFont::multiByteGlyphs() { + return fMultiByteGlyphs; +} + +size_t SkPDFFont::glyphsToPDFFontEncoding(uint16_t* glyphIDs, + size_t numGlyphs) { + // A font with multibyte glyphs will support all glyph IDs in a single font. + if (fMultiByteGlyphs) { + return numGlyphs; + } + + for (size_t i = 0; i < numGlyphs; i++) { + if (glyphIDs[i] == 0) { + continue; + } + if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) { + return i; + } + glyphIDs[i] -= (fFirstGlyphID - 1); + } + + return numGlyphs; +} + +// static +SkPDFFont* SkPDFFont::getFontResource(SkTypeface* typeface, uint16_t glyphID) { + SkAutoMutexAcquire lock(canonicalFontsMutex()); + const uint32_t fontID = SkTypeface::UniqueID(typeface); + int index; + if (find(fontID, glyphID, &index)) { + canonicalFonts()[index].fFont->ref(); + return canonicalFonts()[index].fFont; + } + + SkRefPtr<SkAdvancedTypefaceMetrics> fontInfo; + SkPDFDict* fontDescriptor = NULL; + if (index >= 0) { + SkPDFFont* relatedFont = canonicalFonts()[index].fFont; + SkASSERT(relatedFont->fFontInfo.get()); + fontInfo = relatedFont->fFontInfo; + fontDescriptor = relatedFont->fDescriptor.get(); + } else { + SkAdvancedTypefaceMetrics::PerGlyphInfo info; + info = SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo; + info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>( + info, SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo); + info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>( + info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo); + fontInfo = SkFontHost::GetAdvancedTypefaceMetrics(fontID, info); + SkSafeUnref(fontInfo.get()); // SkRefPtr and Get both took a reference. + } + + SkPDFFont* font = new SkPDFFont(fontInfo.get(), typeface, glyphID, false, + fontDescriptor); + FontRec newEntry(font, fontID, font->fFirstGlyphID); + index = canonicalFonts().count(); + canonicalFonts().push(newEntry); + return font; // Return the reference new SkPDFFont() created. +} + +// static +SkTDArray<SkPDFFont::FontRec>& SkPDFFont::canonicalFonts() { + // This initialization is only thread safe with gcc. + static SkTDArray<FontRec> gCanonicalFonts; + return gCanonicalFonts; +} + +// static +SkMutex& SkPDFFont::canonicalFontsMutex() { + // This initialization is only thread safe with gcc. + static SkMutex gCanonicalFontsMutex; + return gCanonicalFontsMutex; +} + +// static +bool SkPDFFont::find(uint32_t fontID, uint16_t glyphID, int* index) { + // TODO(vandebo) optimize this, do only one search? + FontRec search(NULL, fontID, glyphID); + *index = canonicalFonts().find(search); + if (*index >= 0) + return true; + search.fGlyphID = 0; + *index = canonicalFonts().find(search); + return false; +} + +SkPDFFont::SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo, + SkTypeface* typeface, + uint16_t glyphID, + bool descendantFont, + SkPDFDict* fontDescriptor) + : SkPDFDict("Font"), + fTypeface(typeface), + fType(fontInfo ? fontInfo->fType : + SkAdvancedTypefaceMetrics::kNotEmbeddable_Font), +#ifdef SK_DEBUG + fDescendant(descendantFont), +#endif + fMultiByteGlyphs(false), + fFirstGlyphID(1), + fLastGlyphID(fontInfo ? fontInfo->fLastGlyphID : 0), + fFontInfo(fontInfo), + fDescriptor(fontDescriptor) { + if (fontInfo && fontInfo->fMultiMaster) { + NOT_IMPLEMENTED(true, true); + fType = SkAdvancedTypefaceMetrics::kOther_Font; + } + if (fType == SkAdvancedTypefaceMetrics::kType1CID_Font || + fType == SkAdvancedTypefaceMetrics::kTrueType_Font) { + if (descendantFont) { + populateCIDFont(); + } else { + populateType0Font(); + } + // No need to hold onto the font info for fonts types that + // support multibyte glyphs. + fFontInfo = NULL; + return; + } + + if (fType == SkAdvancedTypefaceMetrics::kType1_Font && + populateType1Font(glyphID)) { + return; + } + + SkASSERT(fType == SkAdvancedTypefaceMetrics::kType1_Font || + fType == SkAdvancedTypefaceMetrics::kCFF_Font || + fType == SkAdvancedTypefaceMetrics::kOther_Font || + fType == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font); + populateType3Font(glyphID); +} + +void SkPDFFont::populateType0Font() { + fMultiByteGlyphs = true; + + insert("Subtype", new SkPDFName("Type0"))->unref(); + insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref(); + insert("Encoding", new SkPDFName("Identity-H"))->unref(); + + SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray(); + descendantFonts->unref(); // SkRefPtr and new took a reference. + + // Pass ref new created to fResources. + fResources.push( + new SkPDFFont(fFontInfo.get(), fTypeface.get(), 1, true, NULL)); + descendantFonts->append(new SkPDFObjRef(fResources.top()))->unref(); + insert("DescendantFonts", descendantFonts.get()); + + populateToUnicodeTable(); +} + +void SkPDFFont::populateToUnicodeTable() { + if (fFontInfo.get() == NULL || + fFontInfo->fGlyphToUnicode.begin() == NULL) { + return; + } + + SkDynamicMemoryWStream cmap; + append_tounicode_header(&cmap); + append_cmap_bfchar_sections(fFontInfo->fGlyphToUnicode, &cmap); + append_cmap_footer(&cmap); + SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream(); + cmapStream->unref(); // SkRefPtr and new took a reference. + cmapStream->setMemoryOwned(cmap.detach(), cmap.getOffset()); + SkRefPtr<SkPDFStream> pdfCmap = new SkPDFStream(cmapStream.get()); + fResources.push(pdfCmap.get()); // Pass reference from new. + insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref(); +} + +void SkPDFFont::populateCIDFont() { + fMultiByteGlyphs = true; + insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref(); + + if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kType1CID_Font) { + insert("Subtype", new SkPDFName("CIDFontType0"))->unref(); + } else if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) { + insert("Subtype", new SkPDFName("CIDFontType2"))->unref(); + insert("CIDToGIDMap", new SkPDFName("Identity"))->unref(); + } else { + SkASSERT(false); + } + + SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict; + sysInfo->unref(); // SkRefPtr and new both took a reference. + sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref(); + sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref(); + sysInfo->insert("Supplement", new SkPDFInt(0))->unref(); + insert("CIDSystemInfo", sysInfo.get()); + + addFontDescriptor(0); + + if (fFontInfo->fGlyphWidths.get()) { + int16_t defaultWidth = 0; + SkRefPtr<SkPDFArray> widths = + composeAdvanceData(fFontInfo->fGlyphWidths.get(), + fFontInfo->fEmSize, &appendWidth, &defaultWidth); + widths->unref(); // SkRefPtr and compose both took a reference. + if (widths->size()) + insert("W", widths.get()); + if (defaultWidth != 0) { + insert("DW", new SkPDFScalar(scaleFromFontUnits( + defaultWidth, fFontInfo->fEmSize)))->unref(); + } + } + if (fFontInfo->fVerticalMetrics.get()) { + struct SkAdvancedTypefaceMetrics::VerticalMetric defaultAdvance; + defaultAdvance.fVerticalAdvance = 0; + defaultAdvance.fOriginXDisp = 0; + defaultAdvance.fOriginYDisp = 0; + SkRefPtr<SkPDFArray> advances = + composeAdvanceData(fFontInfo->fVerticalMetrics.get(), + fFontInfo->fEmSize, &appendVerticalAdvance, + &defaultAdvance); + advances->unref(); // SkRefPtr and compose both took a ref. + if (advances->size()) + insert("W2", advances.get()); + if (defaultAdvance.fVerticalAdvance || + defaultAdvance.fOriginXDisp || + defaultAdvance.fOriginYDisp) { + insert("DW2", appendVerticalAdvance(defaultAdvance, + fFontInfo->fEmSize, + new SkPDFArray))->unref(); + } + } +} + +bool SkPDFFont::populateType1Font(int16_t glyphID) { + SkASSERT(!fFontInfo->fVerticalMetrics.get()); + SkASSERT(fFontInfo->fGlyphWidths.get()); + + adjustGlyphRangeForSingleByteEncoding(glyphID); + + int16_t defaultWidth = 0; + const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = NULL; + const SkAdvancedTypefaceMetrics::WidthRange* widthEntry; + for (widthEntry = fFontInfo.get()->fGlyphWidths.get(); + widthEntry != NULL; + widthEntry = widthEntry->fNext.get()) { + switch (widthEntry->fType) { + case SkAdvancedTypefaceMetrics::WidthRange::kDefault: + defaultWidth = widthEntry->fAdvance[0]; + break; + case SkAdvancedTypefaceMetrics::WidthRange::kRun: + SkASSERT(false); + break; + case SkAdvancedTypefaceMetrics::WidthRange::kRange: + SkASSERT(widthRangeEntry == NULL); + widthRangeEntry = widthEntry; + break; + } + } + + if (!addFontDescriptor(defaultWidth)) + return false; + + insert("Subtype", new SkPDFName("Type1"))->unref(); + insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref(); + + addWidthInfoFromRange(defaultWidth, widthRangeEntry); + + SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding"); + encoding->unref(); // SkRefPtr and new both took a reference. + insert("Encoding", encoding.get()); + + SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray; + encDiffs->unref(); // SkRefPtr and new both took a reference. + encoding->insert("Differences", encDiffs.get()); + + encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2); + encDiffs->append(new SkPDFInt(1))->unref(); + for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) { + encDiffs->append( + new SkPDFName(fFontInfo->fGlyphNames->get()[gID]))->unref(); + } + + if (fFontInfo->fLastGlyphID <= 255) + fFontInfo = NULL; + return true; +} + +void SkPDFFont::populateType3Font(int16_t glyphID) { + SkPaint paint; + paint.setTypeface(fTypeface.get()); + paint.setTextSize(1000); + SkAutoGlyphCache autoCache(paint, NULL); + SkGlyphCache* cache = autoCache.getCache(); + // If fLastGlyphID isn't set (because there is not fFontInfo), look it up. + if (fLastGlyphID == 0) { + fLastGlyphID = cache->getGlyphCount() - 1; + } + + adjustGlyphRangeForSingleByteEncoding(glyphID); + + insert("Subtype", new SkPDFName("Type3"))->unref(); + // Flip about the x-axis and scale by 1/1000. + SkMatrix fontMatrix; + fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000)); + insert("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix))->unref(); + + SkRefPtr<SkPDFDict> charProcs = new SkPDFDict; + charProcs->unref(); // SkRefPtr and new both took a reference. + insert("CharProcs", charProcs.get()); + + SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding"); + encoding->unref(); // SkRefPtr and new both took a reference. + insert("Encoding", encoding.get()); + + SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray; + encDiffs->unref(); // SkRefPtr and new both took a reference. + encoding->insert("Differences", encDiffs.get()); + encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2); + encDiffs->append(new SkPDFInt(1))->unref(); + + SkRefPtr<SkPDFArray> widthArray = new SkPDFArray(); + widthArray->unref(); // SkRefPtr and new both took a ref. + + SkIRect bbox = SkIRect::MakeEmpty(); + for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) { + SkString characterName; + characterName.printf("gid%d", gID); + encDiffs->append(new SkPDFName(characterName))->unref(); + + const SkGlyph& glyph = cache->getGlyphIDMetrics(gID); + widthArray->append(new SkPDFScalar(SkFixedToScalar(glyph.fAdvanceX)))->unref(); + SkIRect glyphBBox = SkIRect::MakeXYWH(glyph.fLeft, glyph.fTop, + glyph.fWidth, glyph.fHeight); + bbox.join(glyphBBox); + + SkDynamicMemoryWStream content; + setGlyphWidthAndBoundingBox(SkFixedToScalar(glyph.fAdvanceX), glyphBBox, + &content); + const SkPath* path = cache->findPath(glyph); + if (path) { + SkPDFUtils::EmitPath(*path, &content); + SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(), + &content); + } + SkRefPtr<SkMemoryStream> glyphStream = new SkMemoryStream(); + glyphStream->unref(); // SkRefPtr and new both took a ref. + glyphStream->setMemoryOwned(content.detach(), content.getOffset()); + + SkRefPtr<SkPDFStream> glyphDescription = + new SkPDFStream(glyphStream.get()); + // SkRefPtr and new both ref()'d charProcs, pass one. + fResources.push(glyphDescription.get()); + charProcs->insert(characterName.c_str(), + new SkPDFObjRef(glyphDescription.get()))->unref(); + } + + insert("FontBBox", makeFontBBox(bbox, 1000))->unref(); + insert("FirstChar", new SkPDFInt(fFirstGlyphID))->unref(); + insert("LastChar", new SkPDFInt(fLastGlyphID))->unref(); + insert("Widths", widthArray.get()); + insert("CIDToGIDMap", new SkPDFName("Identity"))->unref(); + + if (fFontInfo && fFontInfo->fLastGlyphID <= 255) + fFontInfo = NULL; + + populateToUnicodeTable(); +} + +bool SkPDFFont::addFontDescriptor(int16_t defaultWidth) { + if (fDescriptor.get() != NULL) { + fResources.push(fDescriptor.get()); + fDescriptor->ref(); + insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref(); + return true; + } + + fDescriptor = new SkPDFDict("FontDescriptor"); + fDescriptor->unref(); // SkRefPtr and new both took a ref. + + switch (fFontInfo->fType) { + case SkAdvancedTypefaceMetrics::kType1_Font: { + size_t header SK_INIT_TO_AVOID_WARNING; + size_t data SK_INIT_TO_AVOID_WARNING; + size_t trailer SK_INIT_TO_AVOID_WARNING; + SkRefPtr<SkStream> rawFontData = + SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get())); + rawFontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkStream* fontData = handleType1Stream(rawFontData.get(), &header, + &data, &trailer); + if (fontData == NULL) + return false; + SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData); + // SkRefPtr and new both ref()'d fontStream, pass one. + fResources.push(fontStream.get()); + fontStream->insert("Length1", new SkPDFInt(header))->unref(); + fontStream->insert("Length2", new SkPDFInt(data))->unref(); + fontStream->insert("Length3", new SkPDFInt(trailer))->unref(); + fDescriptor->insert("FontFile", + new SkPDFObjRef(fontStream.get()))->unref(); + break; + } + case SkAdvancedTypefaceMetrics::kTrueType_Font: { + SkRefPtr<SkStream> fontData = + SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get())); + fontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get()); + // SkRefPtr and new both ref()'d fontStream, pass one. + fResources.push(fontStream.get()); + + fontStream->insert("Length1", + new SkPDFInt(fontData->getLength()))->unref(); + fDescriptor->insert("FontFile2", + new SkPDFObjRef(fontStream.get()))->unref(); + break; + } + case SkAdvancedTypefaceMetrics::kCFF_Font: + case SkAdvancedTypefaceMetrics::kType1CID_Font: { + SkRefPtr<SkStream> fontData = + SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get())); + fontData->unref(); // SkRefPtr and OpenStream both took a ref. + SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get()); + // SkRefPtr and new both ref()'d fontStream, pass one. + fResources.push(fontStream.get()); + + if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kCFF_Font) { + fontStream->insert("Subtype", new SkPDFName("Type1C"))->unref(); + } else { + fontStream->insert("Subtype", + new SkPDFName("CIDFontType0c"))->unref(); + } + fDescriptor->insert("FontFile3", + new SkPDFObjRef(fontStream.get()))->unref(); + break; + } + default: + SkASSERT(false); + } + + const uint16_t emSize = fFontInfo->fEmSize; + fResources.push(fDescriptor.get()); + fDescriptor->ref(); + insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref(); + + fDescriptor->insert("FontName", new SkPDFName( + fFontInfo->fFontName))->unref(); + fDescriptor->insert("Flags", new SkPDFInt(fFontInfo->fStyle))->unref(); + fDescriptor->insert("Ascent", new SkPDFScalar( + scaleFromFontUnits(fFontInfo->fAscent, emSize)))->unref(); + fDescriptor->insert("Descent", new SkPDFScalar( + scaleFromFontUnits(fFontInfo->fDescent, emSize)))->unref(); + fDescriptor->insert("StemV", new SkPDFScalar( + scaleFromFontUnits(fFontInfo->fStemV, emSize)))->unref(); + fDescriptor->insert("CapHeight", new SkPDFScalar( + scaleFromFontUnits(fFontInfo->fCapHeight, emSize)))->unref(); + fDescriptor->insert("ItalicAngle", new SkPDFInt( + fFontInfo->fItalicAngle))->unref(); + fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox, + fFontInfo->fEmSize))->unref(); + + if (defaultWidth > 0) { + fDescriptor->insert("MissingWidth", new SkPDFScalar( + scaleFromFontUnits(defaultWidth, emSize)))->unref(); + } + return true; +} +void SkPDFFont::addWidthInfoFromRange( + int16_t defaultWidth, + const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) { + SkRefPtr<SkPDFArray> widthArray = new SkPDFArray(); + widthArray->unref(); // SkRefPtr and new both took a ref. + int firstChar = 0; + if (widthRangeEntry) { + const uint16_t emSize = fFontInfo->fEmSize; + int startIndex = fFirstGlyphID - widthRangeEntry->fStartId; + int endIndex = startIndex + fLastGlyphID - fFirstGlyphID + 1; + if (startIndex < 0) + startIndex = 0; + if (endIndex > widthRangeEntry->fAdvance.count()) + endIndex = widthRangeEntry->fAdvance.count(); + if (widthRangeEntry->fStartId == 0) { + appendWidth(widthRangeEntry->fAdvance[0], emSize, widthArray.get()); + } else { + firstChar = startIndex + widthRangeEntry->fStartId; + } + for (int i = startIndex; i < endIndex; i++) + appendWidth(widthRangeEntry->fAdvance[i], emSize, widthArray.get()); + } else { + appendWidth(defaultWidth, 1000, widthArray.get()); + } + insert("FirstChar", new SkPDFInt(firstChar))->unref(); + insert("LastChar", + new SkPDFInt(firstChar + widthArray->size() - 1))->unref(); + insert("Widths", widthArray.get()); +} + +void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(int16_t glyphID) { + // Single byte glyph encoding supports a max of 255 glyphs. + fFirstGlyphID = glyphID - (glyphID - 1) % 255; + if (fLastGlyphID > fFirstGlyphID + 255 - 1) { + fLastGlyphID = fFirstGlyphID + 255 - 1; + } +} + + +bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const { + if (fFontID != b.fFontID) + return false; + if (fFont != NULL && b.fFont != NULL) { + return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID && + fFont->fLastGlyphID == b.fFont->fLastGlyphID; + } + if (fGlyphID == 0 || b.fGlyphID == 0) + return true; + + if (fFont != NULL) { + return fFont->fFirstGlyphID <= b.fGlyphID && + b.fGlyphID <= fFont->fLastGlyphID; + } else if (b.fFont != NULL) { + return b.fFont->fFirstGlyphID <= fGlyphID && + fGlyphID <= b.fFont->fLastGlyphID; + } + return fGlyphID == b.fGlyphID; +} + +SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID) + : fFont(font), + fFontID(fontID), + fGlyphID(glyphID) { +} diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp new file mode 100644 index 0000000000..40a5564847 --- /dev/null +++ b/src/pdf/SkPDFFormXObject.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFFormXObject.h" + +#include "SkMatrix.h" +#include "SkPDFCatalog.h" +#include "SkPDFDevice.h" +#include "SkPDFUtils.h" +#include "SkStream.h" +#include "SkTypes.h" + +SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) { + // We don't want to keep around device because we'd have two copies + // of content, so reference or copy everything we need (content and + // resources). + device->getResources(&fResources); + + SkRefPtr<SkStream> content = device->content(); + content->unref(); // SkRefPtr and content() both took a reference. + fStream = new SkPDFStream(content.get()); + fStream->unref(); // SkRefPtr and new both took a reference. + + insert("Type", new SkPDFName("XObject"))->unref(); + insert("Subtype", new SkPDFName("Form"))->unref(); + insert("BBox", device->getMediaBox().get()); + insert("Resources", device->getResourceDict().get()); + + // We invert the initial transform and apply that to the xobject so that + // it doesn't get applied twice. We can't just undo it because it's + // embedded in things like shaders and images. + if (!device->initialTransform().isIdentity()) { + SkMatrix inverse; + inverse.reset(); + device->initialTransform().invert(&inverse); + insert("Matrix", SkPDFUtils::MatrixToArray(inverse))->unref(); + } + + // Right now SkPDFFormXObject is only used for saveLayer, which implies + // isolated blending. Do this conditionally if that changes. + SkRefPtr<SkPDFDict> group = new SkPDFDict("Group"); + group->unref(); // SkRefPtr and new both took a reference. + group->insert("S", new SkPDFName("Transparency"))->unref(); + group->insert("I", new SkPDFBool(true))->unref(); // Isolated. + insert("Group", group.get()); +} + +SkPDFFormXObject::~SkPDFFormXObject() { + fResources.unrefAll(); +} + +void SkPDFFormXObject::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + fStream->emitObject(stream, catalog, indirect); +} + +size_t SkPDFFormXObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + + return fStream->getOutputSize(catalog, indirect); +} + +void SkPDFFormXObject::getResources(SkTDArray<SkPDFObject*>* resourceList) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + } +} + +SkPDFObject* SkPDFFormXObject::insert(SkPDFName* key, SkPDFObject* value) { + return fStream->insert(key, value); +} + +SkPDFObject* SkPDFFormXObject::insert(const char key[], SkPDFObject* value) { + return fStream->insert(key, value); +} diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp new file mode 100644 index 0000000000..b08bf24c26 --- /dev/null +++ b/src/pdf/SkPDFGraphicState.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFFormXObject.h" +#include "SkPDFGraphicState.h" +#include "SkPDFUtils.h" +#include "SkStream.h" +#include "SkTypes.h" + +static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) { + switch (mode) { + case SkXfermode::kSrcOver_Mode: return "Normal"; + case SkXfermode::kMultiply_Mode: return "Multiply"; + case SkXfermode::kScreen_Mode: return "Screen"; + case SkXfermode::kOverlay_Mode: return "Overlay"; + case SkXfermode::kDarken_Mode: return "Darken"; + case SkXfermode::kLighten_Mode: return "Lighten"; + case SkXfermode::kColorDodge_Mode: return "ColorDodge"; + case SkXfermode::kColorBurn_Mode: return "ColorBurn"; + case SkXfermode::kHardLight_Mode: return "HardLight"; + case SkXfermode::kSoftLight_Mode: return "SoftLight"; + case SkXfermode::kDifference_Mode: return "Difference"; + case SkXfermode::kExclusion_Mode: return "Exclusion"; + + // These are handled in SkPDFDevice::setUpContentEntry. + case SkXfermode::kClear_Mode: + case SkXfermode::kSrc_Mode: + case SkXfermode::kDst_Mode: + case SkXfermode::kDstOver_Mode: + case SkXfermode::kSrcIn_Mode: + case SkXfermode::kDstIn_Mode: + case SkXfermode::kSrcOut_Mode: + case SkXfermode::kDstOut_Mode: + return "Normal"; + + // TODO(vandebo) Figure out if we can support more of these modes. + case SkXfermode::kSrcATop_Mode: + case SkXfermode::kDstATop_Mode: + case SkXfermode::kXor_Mode: + case SkXfermode::kPlus_Mode: + return NULL; + } + return NULL; +} + +SkPDFGraphicState::~SkPDFGraphicState() { + SkAutoMutexAcquire lock(canonicalPaintsMutex()); + if (!fSMask) { + int index = find(fPaint); + SkASSERT(index >= 0); + canonicalPaints().removeShuffle(index); + } + fResources.unrefAll(); +} + +void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + fResources[i]->getResources(resourceList); + } +} + +void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + populateDict(); + SkPDFDict::emitObject(stream, catalog, indirect); +} + +// static +size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + populateDict(); + return SkPDFDict::getOutputSize(catalog, indirect); +} + +// static +SkTDArray<SkPDFGraphicState::GSCanonicalEntry>& +SkPDFGraphicState::canonicalPaints() { + // This initialization is only thread safe with gcc. + static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints; + return gCanonicalPaints; +} + +// static +SkMutex& SkPDFGraphicState::canonicalPaintsMutex() { + // This initialization is only thread safe with gcc. + static SkMutex gCanonicalPaintsMutex; + return gCanonicalPaintsMutex; +} + +// static +SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint( + const SkPaint& paint) { + SkAutoMutexAcquire lock(canonicalPaintsMutex()); + int index = find(paint); + if (index >= 0) { + canonicalPaints()[index].fGraphicState->ref(); + return canonicalPaints()[index].fGraphicState; + } + GSCanonicalEntry newEntry(new SkPDFGraphicState(paint)); + canonicalPaints().push(newEntry); + return newEntry.fGraphicState; +} + +// static +SkPDFObject* SkPDFGraphicState::GetInvertFunction() { + // This assumes that canonicalPaintsMutex is held. + static SkPDFStream* invertFunction = NULL; + if (!invertFunction) { + // Acrobat crashes if we use a type 0 function, kpdf crashes if we use + // a type 2 function, so we use a type 4 function. + SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray; + domainAndRange->unref(); // SkRefPtr and new both took a reference. + domainAndRange->reserve(2); + domainAndRange->append(new SkPDFInt(0))->unref(); + domainAndRange->append(new SkPDFInt(1))->unref(); + + static const char psInvert[] = "{1 exch sub}"; + SkRefPtr<SkMemoryStream> psInvertStream = + new SkMemoryStream(&psInvert, strlen(psInvert), true); + psInvertStream->unref(); // SkRefPtr and new both took a reference. + + invertFunction = new SkPDFStream(psInvertStream.get()); + invertFunction->insert("FunctionType", new SkPDFInt(4))->unref(); + invertFunction->insert("Domain", domainAndRange.get()); + invertFunction->insert("Range", domainAndRange.get()); + } + return invertFunction; +} + +// static +SkPDFGraphicState* SkPDFGraphicState::getSMaskGraphicState( + SkPDFFormXObject* sMask, bool invert) { + // The practical chances of using the same mask more than once are unlikely + // enough that it's not worth canonicalizing. + SkAutoMutexAcquire lock(canonicalPaintsMutex()); + + SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask"); + sMaskDict->unref(); // SkRefPtr and new both took a reference. + sMaskDict->insert("S", new SkPDFName("Alpha"))->unref(); + sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref(); + + SkPDFGraphicState* result = new SkPDFGraphicState; + result->fPopulated = true; + result->fSMask = true; + result->insert("Type", new SkPDFName("ExtGState"))->unref(); + result->insert("SMask", sMaskDict.get()); + result->fResources.push(sMask); + sMask->ref(); + + if (invert) { + SkPDFObject* invertFunction = GetInvertFunction(); + result->fResources.push(invertFunction); + invertFunction->ref(); + sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref(); + } + + return result; +} + +// static +SkPDFGraphicState* SkPDFGraphicState::getNoSMaskGraphicState() { + SkAutoMutexAcquire lock(canonicalPaintsMutex()); + static SkPDFGraphicState* noSMaskGS = NULL; + if (!noSMaskGS) { + noSMaskGS = new SkPDFGraphicState; + noSMaskGS->fPopulated = true; + noSMaskGS->fSMask = true; + noSMaskGS->insert("Type", new SkPDFName("ExtGState"))->unref(); + noSMaskGS->insert("SMask", new SkPDFName("None"))->unref(); + } + noSMaskGS->ref(); + return noSMaskGS; +} + +// static +int SkPDFGraphicState::find(const SkPaint& paint) { + GSCanonicalEntry search(&paint); + return canonicalPaints().find(search); +} + +SkPDFGraphicState::SkPDFGraphicState() + : fPopulated(false), + fSMask(false) { +} + +SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint) + : fPaint(paint), + fPopulated(false), + fSMask(false) { +} + +// populateDict and operator== have to stay in sync with each other. +void SkPDFGraphicState::populateDict() { + if (!fPopulated) { + fPopulated = true; + insert("Type", new SkPDFName("ExtGState"))->unref(); + + SkRefPtr<SkPDFScalar> alpha = + new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF)); + alpha->unref(); // SkRefPtr and new both took a reference. + insert("CA", alpha.get()); + insert("ca", alpha.get()); + + SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch); + SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch); + SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch); + SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch); + SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2); + insert("LC", new SkPDFInt(fPaint.getStrokeCap()))->unref(); + + SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch); + SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch); + SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch); + SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch); + SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2); + insert("LJ", new SkPDFInt(fPaint.getStrokeJoin()))->unref(); + + insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref(); + insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref(); + insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment. + + SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode; + // If asMode fails, default to kSrcOver_Mode. + if (fPaint.getXfermode()) + fPaint.getXfermode()->asMode(&xfermode); + // If we don't support the mode, just use kSrcOver_Mode. + if (xfermode < 0 || xfermode > SkXfermode::kLastMode || + blend_mode_from_xfermode(xfermode) == NULL) { + xfermode = SkXfermode::kSrcOver_Mode; + NOT_IMPLEMENTED("unsupported xfermode", false); + } + insert("BM", + new SkPDFName(blend_mode_from_xfermode(xfermode)))->unref(); + } +} + +// We're only interested in some fields of the SkPaint, so we have a custom +// operator== function. +bool SkPDFGraphicState::GSCanonicalEntry::operator==( + const SkPDFGraphicState::GSCanonicalEntry& gs) const { + const SkPaint* a = fPaint; + const SkPaint* b = gs.fPaint; + SkASSERT(a != NULL); + SkASSERT(b != NULL); + + if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) || + a->getStrokeCap() != b->getStrokeCap() || + a->getStrokeJoin() != b->getStrokeJoin() || + a->getStrokeWidth() != b->getStrokeWidth() || + a->getStrokeMiter() != b->getStrokeMiter()) { + return false; + } + + SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode; + SkXfermode* aXfermode = a->getXfermode(); + if (aXfermode) { + aXfermode->asMode(&aXfermodeName); + } + if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode || + blend_mode_from_xfermode(aXfermodeName) == NULL) { + aXfermodeName = SkXfermode::kSrcOver_Mode; + } + const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName); + SkASSERT(aXfermodeString != NULL); + + SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode; + SkXfermode* bXfermode = b->getXfermode(); + if (bXfermode) { + bXfermode->asMode(&bXfermodeName); + } + if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode || + blend_mode_from_xfermode(bXfermodeName) == NULL) { + bXfermodeName = SkXfermode::kSrcOver_Mode; + } + const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName); + SkASSERT(bXfermodeString != NULL); + + return strcmp(aXfermodeString, bXfermodeString) == 0; +} diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp new file mode 100644 index 0000000000..be69f7f757 --- /dev/null +++ b/src/pdf/SkPDFImage.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFImage.h" + +#include "SkBitmap.h" +#include "SkColor.h" +#include "SkColorPriv.h" +#include "SkPaint.h" +#include "SkPackBits.h" +#include "SkPDFCatalog.h" +#include "SkRect.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkUnPreMultiply.h" + +namespace { + +void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, + SkStream** imageData, SkStream** alphaData) { + SkMemoryStream* image = NULL; + SkMemoryStream* alpha = NULL; + bool hasAlpha = false; + bool isTransparent = false; + + bitmap.lockPixels(); + switch (bitmap.getConfig()) { + case SkBitmap::kIndex8_Config: { + const int rowBytes = srcRect.width(); + image = new SkMemoryStream(rowBytes * srcRect.height()); + uint8_t* dst = (uint8_t*)image->getMemoryBase(); + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); + dst += rowBytes; + } + break; + } + case SkBitmap::kRLE_Index8_Config: { + const int rowBytes = srcRect.width(); + image = new SkMemoryStream(rowBytes * srcRect.height()); + uint8_t* dst = (uint8_t*)image->getMemoryBase(); + const SkBitmap::RLEPixels* rle = + (const SkBitmap::RLEPixels*)bitmap.getPixels(); + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + SkPackBits::Unpack8(dst, srcRect.fLeft, rowBytes, + rle->packedAtY(y)); + dst += rowBytes; + } + break; + } + case SkBitmap::kARGB_4444_Config: { + isTransparent = true; + const int rowBytes = (srcRect.width() * 3 + 1) / 2; + const int alphaRowBytes = (srcRect.width() + 1) / 2; + image = new SkMemoryStream(rowBytes * srcRect.height()); + alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); + uint8_t* dst = (uint8_t*)image->getMemoryBase(); + uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint16_t* src = bitmap.getAddr16(0, y); + int x; + for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { + dst[0] = (SkGetPackedR4444(src[x]) << 4) | + SkGetPackedG4444(src[x]); + dst[1] = (SkGetPackedB4444(src[x]) << 4) | + SkGetPackedR4444(src[x + 1]); + dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | + SkGetPackedB4444(src[x + 1]); + dst += 3; + alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | + SkGetPackedA4444(src[x + 1]); + if (alphaDst[0] != 0xFF) + hasAlpha = true; + if (alphaDst[0]) + isTransparent = false; + alphaDst++; + } + if (srcRect.width() & 1) { + dst[0] = (SkGetPackedR4444(src[x]) << 4) | + SkGetPackedG4444(src[x]); + dst[1] = (SkGetPackedB4444(src[x]) << 4); + dst += 2; + alphaDst[0] = (SkGetPackedA4444(src[x]) << 4); + if (alphaDst[0] != 0xF0) + hasAlpha = true; + if (alphaDst[0] & 0xF0) + isTransparent = false; + alphaDst++; + } + } + break; + } + case SkBitmap::kRGB_565_Config: { + const int rowBytes = srcRect.width() * 3; + image = new SkMemoryStream(rowBytes * srcRect.height()); + uint8_t* dst = (uint8_t*)image->getMemoryBase(); + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint16_t* src = bitmap.getAddr16(0, y); + for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { + dst[0] = SkGetPackedR16(src[x]); + dst[1] = SkGetPackedG16(src[x]); + dst[2] = SkGetPackedB16(src[x]); + dst += 3; + } + } + break; + } + case SkBitmap::kARGB_8888_Config: { + isTransparent = true; + const int rowBytes = srcRect.width() * 3; + image = new SkMemoryStream(rowBytes * srcRect.height()); + alpha = new SkMemoryStream(srcRect.width() * srcRect.height()); + uint8_t* dst = (uint8_t*)image->getMemoryBase(); + uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint32_t* src = bitmap.getAddr32(0, y); + for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { + dst[0] = SkGetPackedR32(src[x]); + dst[1] = SkGetPackedG32(src[x]); + dst[2] = SkGetPackedB32(src[x]); + dst += 3; + alphaDst[0] = SkGetPackedA32(src[x]); + if (alphaDst[0] != 0xFF) + hasAlpha = true; + if (alphaDst[0]) + isTransparent = false; + alphaDst++; + } + } + break; + } + case SkBitmap::kA1_Config: { + isTransparent = true; + image = new SkMemoryStream(1); + ((uint8_t*)image->getMemoryBase())[0] = 0; + + const int alphaRowBytes = (srcRect.width() + 7) / 8; + alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); + uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); + int offset1 = srcRect.fLeft % 8; + int offset2 = 8 - offset1; + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint8_t* src = bitmap.getAddr1(0, y); + // This may read up to one byte after src, but the potentially + // invalid bits are never used for computation. + for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { + if (offset1) { + alphaDst[0] = src[x / 8] << offset1 | + src[x / 8 + 1] >> offset2; + } else { + alphaDst[0] = src[x / 8]; + } + if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) + hasAlpha = true; + if (x + 7 < srcRect.fRight && alphaDst[0]) + isTransparent = false; + alphaDst++; + } + // Calculate the mask of bits we're interested in within the + // last byte of alphaDst. + // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE + uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); + if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) + hasAlpha = true; + if (srcRect.width() % 8 && (alphaDst[-1] & mask)) + isTransparent = false; + } + break; + } + case SkBitmap::kA8_Config: { + isTransparent = true; + image = new SkMemoryStream(1); + ((uint8_t*)image->getMemoryBase())[0] = 0; + + const int alphaRowBytes = srcRect.width(); + alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); + uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint8_t* src = bitmap.getAddr8(0, y); + for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { + alphaDst[0] = src[x]; + if (alphaDst[0] != 0xFF) + hasAlpha = true; + if (alphaDst[0]) + isTransparent = false; + alphaDst++; + } + } + break; + } + default: + SkASSERT(false); + } + bitmap.unlockPixels(); + + if (isTransparent) { + SkSafeUnref(image); + } else { + *imageData = image; + } + + if (isTransparent || !hasAlpha) { + SkSafeUnref(alpha); + } else { + *alphaData = alpha; + } +} + +SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { + SkPDFArray* result = new SkPDFArray(); + result->reserve(4); + result->append(new SkPDFName("Indexed"))->unref(); + result->append(new SkPDFName("DeviceRGB"))->unref();; + result->append(new SkPDFInt(table->count() - 1))->unref(); + + // Potentially, this could be represented in fewer bytes with a stream. + // Max size as a string is 1.5k. + SkString index; + for (int i = 0; i < table->count(); i++) { + char buf[3]; + SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); + buf[0] = SkGetPackedR32(color); + buf[1] = SkGetPackedG32(color); + buf[2] = SkGetPackedB32(color); + index.append(buf, 3); + } + result->append(new SkPDFString(index))->unref(); + return result; +} + +}; // namespace + +// static +SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, + const SkIRect& srcRect, + const SkPaint& paint) { + if (bitmap.getConfig() == SkBitmap::kNo_Config) + return NULL; + + SkStream* imageData = NULL; + SkStream* alphaData = NULL; + extractImageData(bitmap, srcRect, &imageData, &alphaData); + SkAutoUnref unrefImageData(imageData); + SkAutoUnref unrefAlphaData(alphaData); + if (!imageData) { + SkASSERT(!alphaData); + return NULL; + } + + SkPDFImage* image = + new SkPDFImage(imageData, bitmap, srcRect, false, paint); + + if (alphaData != NULL) { + image->addSMask(new SkPDFImage(alphaData, bitmap, srcRect, true, + paint))->unref(); + } + return image; +} + +SkPDFImage::~SkPDFImage() { + fResources.unrefAll(); +} + +SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { + fResources.push(mask); + mask->ref(); + insert("SMask", new SkPDFObjRef(mask))->unref(); + return mask; +} + +void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + fStream->emitObject(stream, catalog, indirect); +} + +size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + + return fStream->getOutputSize(catalog, indirect); +} + +void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) { + if (fResources.count()) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + fResources[i]->getResources(resourceList); + } + } +} + +SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, + const SkIRect& srcRect, bool doingAlpha, + const SkPaint& paint) { + fStream = new SkPDFStream(imageData); + fStream->unref(); // SkRefPtr and new both took a reference. + + SkBitmap::Config config = bitmap.getConfig(); + bool alphaOnly = (config == SkBitmap::kA1_Config || + config == SkBitmap::kA8_Config); + + insert("Type", new SkPDFName("XObject"))->unref(); + insert("Subtype", new SkPDFName("Image"))->unref(); + + if (!doingAlpha && alphaOnly) { + // For alpha only images, we stretch a single pixel of black for + // the color/shape part. + SkRefPtr<SkPDFInt> one = new SkPDFInt(1); + one->unref(); // SkRefPtr and new both took a reference. + insert("Width", one.get()); + insert("Height", one.get()); + } else { + insert("Width", new SkPDFInt(srcRect.width()))->unref(); + insert("Height", new SkPDFInt(srcRect.height()))->unref(); + } + + // if (!image mask) { + if (doingAlpha || alphaOnly) { + insert("ColorSpace", new SkPDFName("DeviceGray"))->unref(); + } else if (config == SkBitmap::kIndex8_Config || + config == SkBitmap::kRLE_Index8_Config) { + insert("ColorSpace", + makeIndexedColorSpace(bitmap.getColorTable()))->unref(); + } else { + insert("ColorSpace", new SkPDFName("DeviceRGB"))->unref(); + } + // } + + int bitsPerComp = 8; + if (config == SkBitmap::kARGB_4444_Config) + bitsPerComp = 4; + else if (doingAlpha && config == SkBitmap::kA1_Config) + bitsPerComp = 1; + insert("BitsPerComponent", new SkPDFInt(bitsPerComp))->unref(); + + if (config == SkBitmap::kRGB_565_Config) { + SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0); + zeroVal->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFScalar> scale5Val = + new SkPDFScalar(8.2258f); // 255/2^5-1 + scale5Val->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFScalar> scale6Val = + new SkPDFScalar(4.0476f); // 255/2^6-1 + scale6Val->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray(); + decodeValue->unref(); // SkRefPtr and new both took a reference. + decodeValue->reserve(6); + decodeValue->append(zeroVal.get()); + decodeValue->append(scale5Val.get()); + decodeValue->append(zeroVal.get()); + decodeValue->append(scale6Val.get()); + decodeValue->append(zeroVal.get()); + decodeValue->append(scale5Val.get()); + insert("Decode", decodeValue.get()); + } +} + +SkPDFObject* SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) { + return fStream->insert(key, value); +} + +SkPDFObject* SkPDFImage::insert(const char key[], SkPDFObject* value) { + return fStream->insert(key, value); +} diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp new file mode 100644 index 0000000000..2a8183d425 --- /dev/null +++ b/src/pdf/SkPDFPage.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFCatalog.h" +#include "SkPDFDevice.h" +#include "SkPDFPage.h" +#include "SkStream.h" + +SkPDFPage::SkPDFPage(const SkRefPtr<SkPDFDevice>& content) + : SkPDFDict("Page"), + fDevice(content) { +} + +SkPDFPage::~SkPDFPage() {} + +void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage, + SkTDArray<SkPDFObject*>* resourceObjects) { + if (fContentStream.get() == NULL) { + insert("Resources", fDevice->getResourceDict().get()); + insert("MediaBox", fDevice->getMediaBox().get()); + + SkRefPtr<SkStream> content = fDevice->content(); + content->unref(); // SkRefPtr and content() both took a reference. + fContentStream = new SkPDFStream(content.get()); + fContentStream->unref(); // SkRefPtr and new both took a reference. + insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref(); + } + catalog->addObject(fContentStream.get(), firstPage); + fDevice->getResources(resourceObjects); +} + +off_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) { + SkASSERT(fContentStream.get() != NULL); + catalog->setFileOffset(fContentStream.get(), fileOffset); + return fContentStream->getOutputSize(catalog, true); +} + +void SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) { + SkASSERT(fContentStream.get() != NULL); + fContentStream->emitObject(stream, catalog, true); +} + +// static +void SkPDFPage::generatePageTree(const SkTDArray<SkPDFPage*>& pages, + SkPDFCatalog* catalog, + SkTDArray<SkPDFDict*>* pageTree, + SkPDFDict** rootNode) { + // PDF wants a tree describing all the pages in the document. We arbitrary + // choose 8 (kNodeSize) as the number of allowed children. The internal + // nodes have type "Pages" with an array of children, a parent pointer, and + // the number of leaves below the node as "Count." The leaves are passed + // into the method, have type "Page" and need a parent pointer. This method + // builds the tree bottom up, skipping internal nodes that would have only + // one child. + static const int kNodeSize = 8; + + SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids"); + kidsName->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFName> countName = new SkPDFName("Count"); + countName->unref(); // SkRefPtr and new both took a reference. + SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent"); + parentName->unref(); // SkRefPtr and new both took a reference. + + // curNodes takes a reference to its items, which it passes to pageTree. + SkTDArray<SkPDFDict*> curNodes; + curNodes.setReserve(pages.count()); + for (int i = 0; i < pages.count(); i++) { + SkSafeRef(pages[i]); + curNodes.push(pages[i]); + } + + // nextRoundNodes passes its references to nodes on to curNodes. + SkTDArray<SkPDFDict*> nextRoundNodes; + nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); + + int treeCapacity = kNodeSize; + do { + for (int i = 0; i < curNodes.count(); ) { + if (i > 0 && i + 1 == curNodes.count()) { + nextRoundNodes.push(curNodes[i]); + break; + } + + SkPDFDict* newNode = new SkPDFDict("Pages"); + SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode); + newNodeRef->unref(); // SkRefPtr and new both took a reference. + + SkRefPtr<SkPDFArray> kids = new SkPDFArray; + kids->unref(); // SkRefPtr and new both took a reference. + kids->reserve(kNodeSize); + + int count = 0; + for (; i < curNodes.count() && count < kNodeSize; i++, count++) { + curNodes[i]->insert(parentName.get(), newNodeRef.get()); + kids->append(new SkPDFObjRef(curNodes[i]))->unref(); + + // TODO(vandebo) put the objects in strict access order. + // Probably doesn't matter because they are so small. + if (curNodes[i] != pages[0]) { + pageTree->push(curNodes[i]); // Transfer reference. + catalog->addObject(curNodes[i], false); + } else { + SkSafeUnref(curNodes[i]); + } + } + + newNode->insert(kidsName.get(), kids.get()); + int pageCount = treeCapacity; + if (count < kNodeSize) { + pageCount = pages.count() % treeCapacity; + } + newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); + nextRoundNodes.push(newNode); // Transfer reference. + } + + curNodes = nextRoundNodes; + nextRoundNodes.rewind(); + treeCapacity *= kNodeSize; + } while(curNodes.count() > 1); + + pageTree->push(curNodes[0]); // Transfer reference. + catalog->addObject(curNodes[0], false); + if (rootNode) + *rootNode = curNodes[0]; +} + +const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const { + return fDevice->getFontResources(); +} diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp new file mode 100644 index 0000000000..6845e09833 --- /dev/null +++ b/src/pdf/SkPDFShader.cpp @@ -0,0 +1,778 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFShader.h" + +#include "SkCanvas.h" +#include "SkPDFCatalog.h" +#include "SkPDFDevice.h" +#include "SkPDFTypes.h" +#include "SkPDFUtils.h" +#include "SkScalar.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkThread.h" +#include "SkTypes.h" + +static void transformBBox(const SkMatrix& matrix, SkRect* bbox) { + SkMatrix inverse; + inverse.reset(); + matrix.invert(&inverse); + inverse.mapRect(bbox); +} + +static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) { + SkVector vec = pts[1] - pts[0]; + SkScalar mag = vec.length(); + SkScalar inv = mag ? SkScalarInvert(mag) : 0; + + vec.scale(inv); + matrix->setSinCos(vec.fY, vec.fX); + matrix->preTranslate(pts[0].fX, pts[0].fY); + matrix->preScale(mag, mag); +} + +/* Assumes t + startOffset is on the stack and does a linear interpolation on t + between startOffset and endOffset from prevColor to curColor (for each color + component), leaving the result in component order on the stack. + @param range endOffset - startOffset + @param curColor[components] The current color components. + @param prevColor[components] The previous color components. + @param result The result ps function. + */ +static void interpolateColorCode(SkScalar range, SkScalar* curColor, + SkScalar* prevColor, int components, + SkString* result) { + // Figure out how to scale each color component. + SkAutoSTMalloc<4, SkScalar> multiplierAlloc(components); + SkScalar *multiplier = multiplierAlloc.get(); + for (int i = 0; i < components; i++) { + multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range); + } + + // Calculate when we no longer need to keep a copy of the input parameter t. + // If the last component to use t is i, then dupInput[0..i - 1] = true + // and dupInput[i .. components] = false. + SkAutoSTMalloc<4, bool> dupInputAlloc(components); + bool *dupInput = dupInputAlloc.get(); + dupInput[components - 1] = false; + for (int i = components - 2; i >= 0; i--) { + dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0; + } + + if (!dupInput[0] && multiplier[0] == 0) { + result->append("pop "); + } + + for (int i = 0; i < components; i++) { + // If the next components needs t, make a copy. + if (dupInput[i]) { + result->append("dup "); + } + + if (multiplier[i] == 0) { + result->appendScalar(prevColor[i]); + result->append(" "); + } else { + if (multiplier[i] != 1) { + result->appendScalar(multiplier[i]); + result->append(" mul "); + } + if (prevColor[i] != 0) { + result->appendScalar(prevColor[i]); + result->append(" add "); + } + } + + if (dupInput[i]) { + result->append("exch\n"); + } + } +} + +/* Generate Type 4 function code to map t=[0,1) to the passed gradient, + clamping at the edges of the range. The generated code will be of the form: + if (t < 0) { + return colorData[0][r,g,b]; + } else { + if (t < info.fColorOffsets[1]) { + return linearinterpolation(colorData[0][r,g,b], + colorData[1][r,g,b]); + } else { + if (t < info.fColorOffsets[2]) { + return linearinterpolation(colorData[1][r,g,b], + colorData[2][r,g,b]); + } else { + + ... } else { + return colorData[info.fColorCount - 1][r,g,b]; + } + ... + } + } + */ +static void gradientFunctionCode(const SkShader::GradientInfo& info, + SkString* result) { + /* We want to linearly interpolate from the previous color to the next. + Scale the colors from 0..255 to 0..1 and determine the multipliers + for interpolation. + C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. + */ + static const int kColorComponents = 3; + typedef SkScalar ColorTuple[kColorComponents]; + SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); + ColorTuple *colorData = colorDataAlloc.get(); + const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); + for (int i = 0; i < info.fColorCount; i++) { + colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); + colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); + colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); + } + + // Clamp the initial color. + result->append("dup 0 le {pop "); + result->appendScalar(colorData[0][0]); + result->append(" "); + result->appendScalar(colorData[0][1]); + result->append(" "); + result->appendScalar(colorData[0][2]); + result->append(" }\n"); + + // The gradient colors. + for (int i = 1 ; i < info.fColorCount; i++) { + result->append("{dup "); + result->appendScalar(info.fColorOffsets[i]); + result->append(" le {"); + if (info.fColorOffsets[i - 1] != 0) { + result->appendScalar(info.fColorOffsets[i - 1]); + result->append(" sub\n"); + } + + interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1], + colorData[i], colorData[i - 1], kColorComponents, + result); + result->append("}\n"); + } + + // Clamp the final color. + result->append("{pop "); + result->appendScalar(colorData[info.fColorCount - 1][0]); + result->append(" "); + result->appendScalar(colorData[info.fColorCount - 1][1]); + result->append(" "); + result->appendScalar(colorData[info.fColorCount - 1][2]); + + for (int i = 0 ; i < info.fColorCount; i++) { + result->append("} ifelse\n"); + } +} + +/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ +static void tileModeCode(SkShader::TileMode mode, SkString* result) { + if (mode == SkShader::kRepeat_TileMode) { + result->append("dup truncate sub\n"); // Get the fractional part. + result->append("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1) + return; + } + + if (mode == SkShader::kMirror_TileMode) { + // Map t mod 2 into [0, 1, 1, 0]. + // Code Stack + result->append("abs " // Map negative to positive. + "dup " // t.s t.s + "truncate " // t.s t + "dup " // t.s t t + "cvi " // t.s t T + "2 mod " // t.s t (i mod 2) + "1 eq " // t.s t true|false + "3 1 roll " // true|false t.s t + "sub " // true|false 0.s + "exch " // 0.s true|false + "{1 exch sub} if\n"); // 1 - 0.s|0.s + } +} + +static SkString linearCode(const SkShader::GradientInfo& info) { + SkString function("{pop\n"); // Just ditch the y value. + tileModeCode(info.fTileMode, &function); + gradientFunctionCode(info, &function); + function.append("}"); + return function; +} + +static SkString radialCode(const SkShader::GradientInfo& info) { + SkString function("{"); + // Find the distance from the origin. + function.append("dup " // x y y + "mul " // x y^2 + "exch " // y^2 x + "dup " // y^2 x x + "mul " // y^2 x^2 + "add " // y^2+x^2 + "sqrt\n"); // sqrt(y^2+x^2) + + tileModeCode(info.fTileMode, &function); + gradientFunctionCode(info, &function); + function.append("}"); + return function; +} + +/* The math here is all based on the description in Two_Point_Radial_Gradient, + with one simplification, the coordinate space has been scaled so that + Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. + */ +static SkString twoPointRadialCode(const SkShader::GradientInfo& info) { + SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; + SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; + SkScalar sr = info.fRadius[0]; + SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; + bool posRoot = info.fRadius[1] > info.fRadius[0]; + + // We start with a stack of (x y), copy it and then consume one copy in + // order to calculate b and the other to calculate c. + SkString function("{"); + function.append("2 copy "); + + // Calculate -b and b^2. + function.appendScalar(dy); + function.append(" mul exch "); + function.appendScalar(dx); + function.append(" mul add "); + function.appendScalar(sr); + function.append(" sub 2 mul neg dup dup mul\n"); + + // Calculate c + function.append("4 2 roll dup mul exch dup mul add "); + function.appendScalar(SkScalarMul(sr, sr)); + function.append(" sub\n"); + + // Calculate the determinate + function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); + function.append(" mul sub abs sqrt\n"); + + // And then the final value of t. + if (posRoot) { + function.append("sub "); + } else { + function.append("add "); + } + function.appendScalar(SkScalarMul(SkIntToScalar(2), a)); + function.append(" div\n"); + + tileModeCode(info.fTileMode, &function); + gradientFunctionCode(info, &function); + function.append("}"); + return function; +} + +static SkString sweepCode(const SkShader::GradientInfo& info) { + SkString function("{exch atan 360 div\n"); + tileModeCode(info.fTileMode, &function); + gradientFunctionCode(info, &function); + function.append("}"); + return function; +} + +SkPDFShader::~SkPDFShader() { + SkAutoMutexAcquire lock(canonicalShadersMutex()); + ShaderCanonicalEntry entry(this, fState.get()); + int index = canonicalShaders().find(entry); + SkASSERT(index >= 0); + canonicalShaders().removeShuffle(index); + fResources.unrefAll(); +} + +void SkPDFShader::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + fContent->emitObject(stream, catalog, indirect); +} + +size_t SkPDFShader::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + + return fContent->getOutputSize(catalog, indirect); +} + +void SkPDFShader::getResources(SkTDArray<SkPDFObject*>* resourceList) { + resourceList->setReserve(resourceList->count() + fResources.count()); + for (int i = 0; i < fResources.count(); i++) { + resourceList->push(fResources[i]); + fResources[i]->ref(); + } +} + +// static +SkPDFShader* SkPDFShader::getPDFShader(const SkShader& shader, + const SkMatrix& matrix, + const SkIRect& surfaceBBox) { + SkRefPtr<SkPDFShader> pdfShader; + SkAutoMutexAcquire lock(canonicalShadersMutex()); + SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); + + ShaderCanonicalEntry entry(NULL, shaderState.get()); + int index = canonicalShaders().find(entry); + if (index >= 0) { + SkPDFShader* result = canonicalShaders()[index].fPDFShader; + result->ref(); + return result; + } + // The PDFShader takes ownership of the shaderSate. + pdfShader = new SkPDFShader(shaderState.detach()); + // Check for a valid shader. + if (pdfShader->fContent.get() == NULL) { + pdfShader->unref(); + return NULL; + } + entry.fPDFShader = pdfShader.get(); + canonicalShaders().push(entry); + return pdfShader.get(); // return the reference that came from new. +} + +// static +SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::canonicalShaders() { + // This initialization is only thread safe with gcc. + static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders; + return gCanonicalShaders; +} + +// static +SkMutex& SkPDFShader::canonicalShadersMutex() { + // This initialization is only thread safe with gcc. + static SkMutex gCanonicalShadersMutex; + return gCanonicalShadersMutex; +} + +// static +SkPDFObject* SkPDFShader::rangeObject() { + // This initialization is only thread safe with gcc. + static SkPDFArray* range = NULL; + // This method is only used with canonicalShadersMutex, so it's safe to + // populate domain. + if (range == NULL) { + range = new SkPDFArray; + range->reserve(6); + range->append(new SkPDFInt(0))->unref(); + range->append(new SkPDFInt(1))->unref(); + range->append(new SkPDFInt(0))->unref(); + range->append(new SkPDFInt(1))->unref(); + range->append(new SkPDFInt(0))->unref(); + range->append(new SkPDFInt(1))->unref(); + } + return range; +} + +SkPDFShader::SkPDFShader(State* state) : fState(state) { + if (fState.get()->fType == SkShader::kNone_GradientType) { + doImageShader(); + } else { + doFunctionShader(); + } +} + +void SkPDFShader::doFunctionShader() { + SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; + SkPoint transformPoints[2]; + + // Depending on the type of the gradient, we want to transform the + // coordinate space in different ways. + const SkShader::GradientInfo* info = &fState.get()->fInfo; + transformPoints[0] = info->fPoint[0]; + transformPoints[1] = info->fPoint[1]; + switch (fState.get()->fType) { + case SkShader::kLinear_GradientType: + codeFunction = &linearCode; + break; + case SkShader::kRadial_GradientType: + transformPoints[1] = transformPoints[0]; + transformPoints[1].fX += info->fRadius[0]; + codeFunction = &radialCode; + break; + case SkShader::kRadial2_GradientType: { + // Bail out if the radii are the same. Not setting fContent will + // cause the higher level code to detect the resulting object + // as invalid. + if (info->fRadius[0] == info->fRadius[1]) { + return; + } + transformPoints[1] = transformPoints[0]; + SkScalar dr = info->fRadius[1] - info->fRadius[0]; + transformPoints[1].fX += dr; + codeFunction = &twoPointRadialCode; + break; + } + case SkShader::kSweep_GradientType: + transformPoints[1] = transformPoints[0]; + transformPoints[1].fX += 1; + codeFunction = &sweepCode; + break; + case SkShader::kColor_GradientType: + case SkShader::kNone_GradientType: + SkASSERT(false); + return; + } + + // Move any scaling (assuming a unit gradient) or translation + // (and rotation for linear gradient), of the final gradient from + // info->fPoints to the matrix (updating bbox appropriately). Now + // the gradient can be drawn on on the unit segment. + SkMatrix mapperMatrix; + unitToPointsMatrix(transformPoints, &mapperMatrix); + SkMatrix finalMatrix = fState.get()->fCanvasTransform; + finalMatrix.preConcat(mapperMatrix); + finalMatrix.preConcat(fState.get()->fShaderTransform); + SkRect bbox; + bbox.set(fState.get()->fBBox); + transformBBox(finalMatrix, &bbox); + + SkRefPtr<SkPDFArray> domain = new SkPDFArray; + domain->unref(); // SkRefPtr and new both took a reference. + domain->reserve(4); + domain->append(new SkPDFScalar(bbox.fLeft))->unref(); + domain->append(new SkPDFScalar(bbox.fRight))->unref(); + domain->append(new SkPDFScalar(bbox.fTop))->unref(); + domain->append(new SkPDFScalar(bbox.fBottom))->unref(); + + SkString functionCode; + // The two point radial gradient further references fState.get()->fInfo + // in translating from x, y coordinates to the t parameter. So, we have + // to transform the points and radii according to the calculated matrix. + if (fState.get()->fType == SkShader::kRadial2_GradientType) { + SkShader::GradientInfo twoPointRadialInfo = *info; + SkMatrix inverseMapperMatrix; + mapperMatrix.invert(&inverseMapperMatrix); + inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); + twoPointRadialInfo.fRadius[0] = + inverseMapperMatrix.mapRadius(info->fRadius[0]); + twoPointRadialInfo.fRadius[1] = + inverseMapperMatrix.mapRadius(info->fRadius[1]); + functionCode = codeFunction(twoPointRadialInfo); + } else { + functionCode = codeFunction(*info); + } + + SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get()); + // Pass one reference to fResources, SkRefPtr and new both took a reference. + fResources.push(function.get()); + + SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict; + pdfShader->unref(); // SkRefPtr and new both took a reference. + pdfShader->insert("ShadingType", new SkPDFInt(1))->unref(); + pdfShader->insert("ColorSpace", new SkPDFName("DeviceRGB"))->unref(); + pdfShader->insert("Domain", domain.get()); + pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref(); + + fContent = new SkPDFDict("Pattern"); + fContent->unref(); // SkRefPtr and new both took a reference. + fContent->insert("PatternType", new SkPDFInt(2))->unref(); + fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); + fContent->insert("Shading", pdfShader.get()); +} + +// SkShader* shader, SkMatrix matrix, const SkRect& surfaceBBox +void SkPDFShader::doImageShader() { + fState.get()->fImage.lockPixels(); + + SkMatrix finalMatrix = fState.get()->fCanvasTransform; + finalMatrix.preConcat(fState.get()->fShaderTransform); + SkRect surfaceBBox; + surfaceBBox.set(fState.get()->fBBox); + transformBBox(finalMatrix, &surfaceBBox); + + SkMatrix unflip; + unflip.setTranslate(0, SkScalarRound(surfaceBBox.height())); + unflip.preScale(1, -1); + SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), + SkScalarRound(surfaceBBox.height())); + SkPDFDevice pattern(size, size, unflip); + SkCanvas canvas(&pattern); + canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); + finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); + + const SkBitmap* image = &fState.get()->fImage; + int width = image->width(); + int height = image->height(); + SkShader::TileMode tileModes[2]; + tileModes[0] = fState.get()->fImageTileModes[0]; + tileModes[1] = fState.get()->fImageTileModes[1]; + + canvas.drawBitmap(*image, 0, 0); + SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, + width, height); + + // Tiling is implied. First we handle mirroring. + if (tileModes[0] == SkShader::kMirror_TileMode) { + SkMatrix xMirror; + xMirror.setScale(-1, 1); + xMirror.postTranslate(2 * width, 0); + canvas.drawBitmapMatrix(*image, xMirror); + patternBBox.fRight += width; + } + if (tileModes[1] == SkShader::kMirror_TileMode) { + SkMatrix yMirror; + yMirror.setScale(1, -1); + yMirror.postTranslate(0, 2 * height); + canvas.drawBitmapMatrix(*image, yMirror); + patternBBox.fBottom += height; + } + if (tileModes[0] == SkShader::kMirror_TileMode && + tileModes[1] == SkShader::kMirror_TileMode) { + SkMatrix mirror; + mirror.setScale(-1, -1); + mirror.postTranslate(2 * width, 2 * height); + canvas.drawBitmapMatrix(*image, mirror); + } + + // Then handle Clamping, which requires expanding the pattern canvas to + // cover the entire surfaceBBox. + + // If both x and y are in clamp mode, we start by filling in the corners. + // (Which are just a rectangles of the corner colors.) + if (tileModes[0] == SkShader::kClamp_TileMode && + tileModes[1] == SkShader::kClamp_TileMode) { + SkPaint paint; + SkRect rect; + rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); + if (!rect.isEmpty()) { + paint.setColor(image->getColor(0, 0)); + canvas.drawRect(rect, paint); + } + + rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); + if (!rect.isEmpty()) { + paint.setColor(image->getColor(width - 1, 0)); + canvas.drawRect(rect, paint); + } + + rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, + surfaceBBox.fBottom); + if (!rect.isEmpty()) { + paint.setColor(image->getColor(width - 1, height - 1)); + canvas.drawRect(rect, paint); + } + + rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, + surfaceBBox.fBottom); + if (!rect.isEmpty()) { + paint.setColor(image->getColor(0, height - 1)); + canvas.drawRect(rect, paint); + } + } + + // Then expand the left, right, top, then bottom. + if (tileModes[0] == SkShader::kClamp_TileMode) { + SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height); + if (surfaceBBox.fLeft < 0) { + SkBitmap left; + SkAssertResult(image->extractSubset(&left, subset)); + + SkMatrix leftMatrix; + leftMatrix.setScale(-surfaceBBox.fLeft, 1); + leftMatrix.postTranslate(surfaceBBox.fLeft, 0); + canvas.drawBitmapMatrix(left, leftMatrix); + + if (tileModes[1] == SkShader::kMirror_TileMode) { + leftMatrix.postScale(1, -1); + leftMatrix.postTranslate(0, 2 * height); + canvas.drawBitmapMatrix(left, leftMatrix); + } + patternBBox.fLeft = 0; + } + + if (surfaceBBox.fRight > width) { + SkBitmap right; + subset.offset(width - 1, 0); + SkAssertResult(image->extractSubset(&right, subset)); + + SkMatrix rightMatrix; + rightMatrix.setScale(surfaceBBox.fRight - width, 1); + rightMatrix.postTranslate(width, 0); + canvas.drawBitmapMatrix(right, rightMatrix); + + if (tileModes[1] == SkShader::kMirror_TileMode) { + rightMatrix.postScale(1, -1); + rightMatrix.postTranslate(0, 2 * height); + canvas.drawBitmapMatrix(right, rightMatrix); + } + patternBBox.fRight = surfaceBBox.width(); + } + } + + if (tileModes[1] == SkShader::kClamp_TileMode) { + SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1); + if (surfaceBBox.fTop < 0) { + SkBitmap top; + SkAssertResult(image->extractSubset(&top, subset)); + + SkMatrix topMatrix; + topMatrix.setScale(1, -surfaceBBox.fTop); + topMatrix.postTranslate(0, surfaceBBox.fTop); + canvas.drawBitmapMatrix(top, topMatrix); + + if (tileModes[0] == SkShader::kMirror_TileMode) { + topMatrix.postScale(-1, 1); + topMatrix.postTranslate(2 * width, 0); + canvas.drawBitmapMatrix(top, topMatrix); + } + patternBBox.fTop = 0; + } + + if (surfaceBBox.fBottom > height) { + SkBitmap bottom; + subset.offset(0, height - 1); + SkAssertResult(image->extractSubset(&bottom, subset)); + + SkMatrix bottomMatrix; + bottomMatrix.setScale(1, surfaceBBox.fBottom - height); + bottomMatrix.postTranslate(0, height); + canvas.drawBitmapMatrix(bottom, bottomMatrix); + + if (tileModes[0] == SkShader::kMirror_TileMode) { + bottomMatrix.postScale(-1, 1); + bottomMatrix.postTranslate(2 * width, 0); + canvas.drawBitmapMatrix(bottom, bottomMatrix); + } + patternBBox.fBottom = surfaceBBox.height(); + } + } + + SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray; + patternBBoxArray->unref(); // SkRefPtr and new both took a reference. + patternBBoxArray->reserve(4); + patternBBoxArray->append(new SkPDFScalar(patternBBox.fLeft))->unref(); + patternBBoxArray->append(new SkPDFScalar(patternBBox.fTop))->unref(); + patternBBoxArray->append(new SkPDFScalar(patternBBox.fRight))->unref(); + patternBBoxArray->append(new SkPDFScalar(patternBBox.fBottom))->unref(); + + // Put the canvas into the pattern stream (fContent). + SkRefPtr<SkStream> content = pattern.content(); + content->unref(); // SkRefPtr and content() both took a reference. + pattern.getResources(&fResources); + + fContent = new SkPDFStream(content.get()); + fContent->unref(); // SkRefPtr and new both took a reference. + fContent->insert("Type", new SkPDFName("Pattern"))->unref(); + fContent->insert("PatternType", new SkPDFInt(1))->unref(); + fContent->insert("PaintType", new SkPDFInt(1))->unref(); + fContent->insert("TilingType", new SkPDFInt(1))->unref(); + fContent->insert("BBox", patternBBoxArray.get()); + fContent->insert("XStep", new SkPDFScalar(patternBBox.width()))->unref(); + fContent->insert("YStep", new SkPDFScalar(patternBBox.height()))->unref(); + fContent->insert("Resources", pattern.getResourceDict().get()); + fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); + + fState.get()->fImage.unlockPixels(); +} + +SkPDFStream* SkPDFShader::makePSFunction(const SkString& psCode, + SkPDFArray* domain) { + SkRefPtr<SkMemoryStream> funcStream = + new SkMemoryStream(psCode.c_str(), psCode.size(), true); + funcStream->unref(); // SkRefPtr and new both took a reference. + + SkPDFStream* result = new SkPDFStream(funcStream.get()); + result->insert("FunctionType", new SkPDFInt(4))->unref(); + result->insert("Domain", domain); + result->insert("Range", rangeObject()); + return result; +} + +bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { + if (fType != b.fType || + fCanvasTransform != b.fCanvasTransform || + fShaderTransform != b.fShaderTransform || + fBBox != b.fBBox) { + return false; + } + + if (fType == SkShader::kNone_GradientType) { + if (fPixelGeneration != b.fPixelGeneration || + fPixelGeneration == 0 || + fImageTileModes[0] != b.fImageTileModes[0] || + fImageTileModes[1] != b.fImageTileModes[1]) { + return false; + } + } else { + if (fInfo.fColorCount != b.fInfo.fColorCount || + memcmp(fInfo.fColors, b.fInfo.fColors, + sizeof(SkColor) * fInfo.fColorCount) != 0 || + memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets, + sizeof(SkScalar) * fInfo.fColorCount) != 0 || + fInfo.fPoint[0] != b.fInfo.fPoint[0] || + fInfo.fTileMode != b.fInfo.fTileMode) { + return false; + } + + switch (fType) { + case SkShader::kLinear_GradientType: + if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) { + return false; + } + break; + case SkShader::kRadial_GradientType: + if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) { + return false; + } + break; + case SkShader::kRadial2_GradientType: + if (fInfo.fPoint[1] != b.fInfo.fPoint[1] || + fInfo.fRadius[0] != b.fInfo.fRadius[0] || + fInfo.fRadius[1] != b.fInfo.fRadius[1]) { + return false; + } + break; + case SkShader::kSweep_GradientType: + case SkShader::kNone_GradientType: + case SkShader::kColor_GradientType: + break; + } + } + return true; +} + +SkPDFShader::State::State(const SkShader& shader, + const SkMatrix& canvasTransform, const SkIRect& bbox) + : fCanvasTransform(canvasTransform), + fBBox(bbox) { + + fInfo.fColorCount = 0; + fInfo.fColors = NULL; + fInfo.fColorOffsets = NULL; + shader.getLocalMatrix(&fShaderTransform); + + fType = shader.asAGradient(&fInfo); + + if (fType == SkShader::kNone_GradientType) { + SkShader::BitmapType bitmapType; + SkMatrix matrix; + bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes, NULL); + if (bitmapType != SkShader::kDefault_BitmapType) { + fImage.reset(); + return; + } + SkASSERT(matrix.isIdentity()); + fPixelGeneration = fImage.getGenerationID(); + } else { + fColorData.set(sk_malloc_throw( + fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); + fInfo.fColors = (SkColor*)fColorData.get(); + fInfo.fColorOffsets = (SkScalar*)(fInfo.fColors + fInfo.fColorCount); + shader.asAGradient(&fInfo); + } +} diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp new file mode 100644 index 0000000000..b1bd5ff05d --- /dev/null +++ b/src/pdf/SkPDFStream.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkFlate.h" +#include "SkPDFCatalog.h" +#include "SkPDFStream.h" +#include "SkStream.h" + +SkPDFStream::SkPDFStream(SkStream* stream) { + if (SkFlate::HaveFlate()) + SkAssertResult(SkFlate::Deflate(stream, &fCompressedData)); + + if (SkFlate::HaveFlate() && + fCompressedData.getOffset() < stream->getLength()) { + fLength = fCompressedData.getOffset(); + insert("Filter", new SkPDFName("FlateDecode"))->unref(); + } else { + fCompressedData.reset(); + fPlainData = stream; + fLength = fPlainData->getLength(); + } + insert("Length", new SkPDFInt(fLength))->unref(); +} + +SkPDFStream::~SkPDFStream() { +} + +void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + this->INHERITED::emitObject(stream, catalog, false); + stream->writeText(" stream\n"); + if (fPlainData.get()) + stream->write(fPlainData->getMemoryBase(), fLength); + else + stream->write(fCompressedData.getStream(), fLength); + stream->writeText("\nendstream"); +} + +size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + + return this->INHERITED::getOutputSize(catalog, false) + + strlen(" stream\n\nendstream") + fLength; +} diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp new file mode 100644 index 0000000000..b9420eba83 --- /dev/null +++ b/src/pdf/SkPDFTypes.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPDFCatalog.h" +#include "SkPDFTypes.h" +#include "SkStream.h" + +#ifdef SK_BUILD_FOR_WIN + #define SNPRINTF _snprintf +#else + #define SNPRINTF snprintf +#endif + +SkPDFObject::SkPDFObject() {} +SkPDFObject::~SkPDFObject() {} + +size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + SkDynamicMemoryWStream buffer; + emitObject(&buffer, catalog, indirect); + return buffer.getOffset(); +} + +void SkPDFObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {} + +void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) { + catalog->emitObjectNumber(stream, this); + stream->writeText(" obj\n"); + emitObject(stream, catalog, false); + stream->writeText("\nendobj\n"); +} + +SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {} +SkPDFObjRef::~SkPDFObjRef() {} + +size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) { + return catalog->getObjectNumberSize(this) + strlen(" obj\n") + + this->getOutputSize(catalog, false) + strlen("\nendobj\n"); +} + +void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + SkASSERT(!indirect); + catalog->emitObjectNumber(stream, fObj.get()); + stream->writeText(" R"); +} + +size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + SkASSERT(!indirect); + return catalog->getObjectNumberSize(fObj.get()) + strlen(" R"); +} + +SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {} +SkPDFInt::~SkPDFInt() {} + +void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + stream->writeDecAsText(fValue); +} + +SkPDFBool::SkPDFBool(bool value) : fValue(value) {} +SkPDFBool::~SkPDFBool() {} + +void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + SkASSERT(!indirect); + if (fValue) { + stream->writeText("true"); + } else { + stream->writeText("false"); + } +} + +size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + SkASSERT(!indirect); + if (fValue) + return strlen("true"); + return strlen("false"); +} + +SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {} +SkPDFScalar::~SkPDFScalar() {} + +void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + Append(fValue, stream); +} + +// static +void SkPDFScalar::Append(SkScalar value, SkWStream* stream) { + // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and + // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31). + // When using floats that are outside the whole value range, we can use + // integers instead. + + +#if defined(SK_SCALAR_IS_FIXED) + stream->writeScalarAsText(value); + return; +#endif // SK_SCALAR_IS_FIXED + +#if !defined(SK_ALLOW_LARGE_PDF_SCALARS) + if (value > 32767 || value < -32767) { + stream->writeDecAsText(SkScalarRound(value)); + return; + } + + char buffer[SkStrAppendScalar_MaxSize]; + char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value)); + stream->write(buffer, end - buffer); + return; +#endif // !SK_ALLOW_LARGE_PDF_SCALARS + +#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS) + // Floats have 24bits of significance, so anything outside that range is + // no more precise than an int. (Plus PDF doesn't support scientific + // notation, so this clamps to SK_Max/MinS32). + if (value > (1 << 24) || value < -(1 << 24)) { + stream->writeDecAsText(value); + return; + } + // Continue to enforce the PDF limits for small floats. + if (value < 1.0f/65536 && value > -1.0f/65536) { + stream->writeDecAsText(0); + return; + } + // SkStrAppendFloat might still use scientific notation, so use snprintf + // directly.. + static const int kFloat_MaxSize = 19; + char buffer[kFloat_MaxSize]; + int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value); + // %f always prints trailing 0s, so strip them. + for (; buffer[len - 1] == '0' && len > 0; len--) { + buffer[len - 1] = '\0'; + } + if (buffer[len - 1] == '.') { + buffer[len - 1] = '\0'; + } + stream->writeText(buffer); + return; +#endif // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS +} + +SkPDFString::SkPDFString(const char value[]) + : fValue(formatString(value, strlen(value))) { +} + +SkPDFString::SkPDFString(const SkString& value) + : fValue(formatString(value.c_str(), value.size())) { +} + +SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars) + : fValue(formatString(value, len, wideChars)) { +} + +SkPDFString::~SkPDFString() {} + +void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + stream->write(fValue.c_str(), fValue.size()); +} + +size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + return fValue.size(); +} + +// static +SkString SkPDFString::formatString(const char* input, size_t len) { + return doFormatString(input, len, false, false); +} + +SkString SkPDFString::formatString(const uint16_t* input, size_t len, + bool wideChars) { + return doFormatString(input, len, true, wideChars); +} + +// static +SkString SkPDFString::doFormatString(const void* input, size_t len, + bool wideInput, bool wideOutput) { + SkASSERT(len <= kMaxLen); + const uint16_t* win = (const uint16_t*) input; + const char* cin = (const char*) input; + + if (wideOutput) { + SkASSERT(wideInput); + SkString result; + result.append("<"); + for (size_t i = 0; i < len; i++) + result.appendHex(win[i], 4); + result.append(">"); + return result; + } + + // 7-bit clean is a heuristic to decide what string format to use; + // a 7-bit clean string should require little escaping. + bool sevenBitClean = true; + for (size_t i = 0; i < len; i++) { + SkASSERT(!wideInput || !(win[i] & ~0xFF)); + char val = wideInput ? win[i] : cin[i]; + if (val > '~' || val < ' ') { + sevenBitClean = false; + break; + } + } + + SkString result; + if (sevenBitClean) { + result.append("("); + for (size_t i = 0; i < len; i++) { + SkASSERT(!wideInput || !(win[i] & ~0xFF)); + char val = wideInput ? win[i] : cin[i]; + if (val == '\\' || val == '(' || val == ')') + result.append("\\"); + result.append(&val, 1); + } + result.append(")"); + } else { + result.append("<"); + for (size_t i = 0; i < len; i++) { + SkASSERT(!wideInput || !(win[i] & ~0xFF)); + unsigned char val = wideInput ? win[i] : cin[i]; + result.appendHex(val, 2); + } + result.append(">"); + } + + return result; +} + +SkPDFName::SkPDFName(const char name[]) : fValue(formatName(SkString(name))) {} +SkPDFName::SkPDFName(const SkString& name) : fValue(formatName(name)) {} +SkPDFName::~SkPDFName() {} + +void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + SkASSERT(!indirect); + stream->write(fValue.c_str(), fValue.size()); +} + +size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + SkASSERT(!indirect); + return fValue.size(); +} + +// static +SkString SkPDFName::formatName(const SkString& input) { + SkASSERT(input.size() <= kMaxLen); + + SkString result("/"); + for (size_t i = 0; i < input.size(); i++) { + if (input[i] & 0x80 || input[i] < '!' || input[i] == '#') { + result.append("#"); + result.appendHex(input[i], 2); + } else { + result.append(input.c_str() + i, 1); + } + } + + return result; +} + +SkPDFArray::SkPDFArray() {} +SkPDFArray::~SkPDFArray() { + fValue.safeUnrefAll(); +} + +void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + stream->writeText("["); + for (int i = 0; i < fValue.count(); i++) { + fValue[i]->emitObject(stream, catalog, false); + if (i + 1 < fValue.count()) + stream->writeText(" "); + } + stream->writeText("]"); +} + +size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + + size_t result = strlen("[]"); + if (fValue.count()) + result += fValue.count() - 1; + for (int i = 0; i < fValue.count(); i++) + result += fValue[i]->getOutputSize(catalog, false); + return result; +} + +void SkPDFArray::reserve(int length) { + SkASSERT(length <= kMaxLen); + fValue.setReserve(length); +} + +SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) { + SkASSERT(offset < fValue.count()); + SkSafeUnref(fValue[offset]); + fValue[offset] = value; + SkSafeRef(fValue[offset]); + return value; +} + +SkPDFObject* SkPDFArray::append(SkPDFObject* value) { + SkASSERT(fValue.count() < kMaxLen); + SkSafeRef(value); + fValue.push(value); + return value; +} + +SkPDFDict::SkPDFDict() {} + +SkPDFDict::SkPDFDict(const char type[]) { + insert("Type", new SkPDFName(type))->unref(); +} + +SkPDFDict::~SkPDFDict() { + clear(); +} + +void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog, + bool indirect) { + if (indirect) + return emitIndirectObject(stream, catalog); + + stream->writeText("<<"); + for (int i = 0; i < fValue.count(); i++) { + fValue[i].key->emitObject(stream, catalog, false); + stream->writeText(" "); + fValue[i].value->emitObject(stream, catalog, false); + stream->writeText("\n"); + } + stream->writeText(">>"); +} + +size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) { + if (indirect) + return getIndirectOutputSize(catalog); + + size_t result = strlen("<<>>") + (fValue.count() * 2); + for (int i = 0; i < fValue.count(); i++) { + result += fValue[i].key->getOutputSize(catalog, false); + result += fValue[i].value->getOutputSize(catalog, false); + } + return result; +} + +SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) { + struct Rec* newEntry = fValue.append(); + newEntry->key = key; + SkSafeRef(newEntry->key); + newEntry->value = value; + SkSafeRef(newEntry->value); + return value; +} + +SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) { + SkRefPtr<SkPDFName> keyName = new SkPDFName(key); + keyName->unref(); // SkRefPtr and new both took a reference. + return insert(keyName.get(), value); +} + +void SkPDFDict::clear() { + for (int i = 0; i < fValue.count(); i++) { + SkSafeUnref(fValue[i].key); + SkSafeUnref(fValue[i].value); + } + fValue.reset(); +} diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp new file mode 100644 index 0000000000..a838427c73 --- /dev/null +++ b/src/pdf/SkPDFUtils.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPDFUtils.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkPDFTypes.h" + +// static +SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) { + SkScalar values[6]; + SkAssertResult(matrix.pdfTransform(values)); + + SkPDFArray* result = new SkPDFArray; + result->reserve(6); + for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) { + result->append(new SkPDFScalar(values[i]))->unref(); + } + return result; +} + +// static +void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) { + SkScalar values[6]; + SkAssertResult(matrix.pdfTransform(values)); + for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) { + SkPDFScalar::Append(values[i], content); + content->writeText(" "); + } + content->writeText("cm\n"); +} + +// static +void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) { + SkPDFScalar::Append(x, content); + content->writeText(" "); + SkPDFScalar::Append(y, content); + content->writeText(" m\n"); +} + +// static +void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) { + SkPDFScalar::Append(x, content); + content->writeText(" "); + SkPDFScalar::Append(y, content); + content->writeText(" l\n"); +} + +// static +void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y, + SkScalar ctl2X, SkScalar ctl2Y, + SkScalar dstX, SkScalar dstY, SkWStream* content) { + SkString cmd("y\n"); + SkPDFScalar::Append(ctl1X, content); + content->writeText(" "); + SkPDFScalar::Append(ctl1Y, content); + content->writeText(" "); + if (ctl2X != dstX || ctl2Y != dstY) { + cmd.set("c\n"); + SkPDFScalar::Append(ctl2X, content); + content->writeText(" "); + SkPDFScalar::Append(ctl2Y, content); + content->writeText(" "); + } + SkPDFScalar::Append(dstX, content); + content->writeText(" "); + SkPDFScalar::Append(dstY, content); + content->writeText(" "); + content->writeText(cmd.c_str()); +} + +// static +void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) { + // Skia has 0,0 at top left, pdf at bottom left. Do the right thing. + SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop); + + SkPDFScalar::Append(rect.fLeft, content); + content->writeText(" "); + SkPDFScalar::Append(bottom, content); + content->writeText(" "); + SkPDFScalar::Append(rect.width(), content); + content->writeText(" "); + SkPDFScalar::Append(rect.height(), content); + content->writeText(" re\n"); +} + +// static +void SkPDFUtils::EmitPath(const SkPath& path, SkWStream* content) { + SkPoint args[4]; + SkPath::Iter iter(path, false); + for (SkPath::Verb verb = iter.next(args); + verb != SkPath::kDone_Verb; + verb = iter.next(args)) { + // args gets all the points, even the implicit first point. + switch (verb) { + case SkPath::kMove_Verb: + MoveTo(args[0].fX, args[0].fY, content); + break; + case SkPath::kLine_Verb: + AppendLine(args[1].fX, args[1].fY, content); + break; + case SkPath::kQuad_Verb: { + // Convert quad to cubic (degree elevation). http://goo.gl/vS4i + const SkScalar three = SkIntToScalar(3); + args[1].scale(SkIntToScalar(2)); + SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three); + SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three); + SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three); + SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three); + AppendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY, + content); + break; + } + case SkPath::kCubic_Verb: + AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, + args[3].fX, args[3].fY, content); + break; + case SkPath::kClose_Verb: + ClosePath(content); + break; + case SkPath::kDone_Verb: + break; + default: + SkASSERT(false); + break; + } + } +} + +// static +void SkPDFUtils::ClosePath(SkWStream* content) { + content->writeText("h\n"); +} + +// static +void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill, + SkWStream* content) { + if (style == SkPaint::kFill_Style) + content->writeText("f"); + else if (style == SkPaint::kStrokeAndFill_Style) + content->writeText("B"); + else if (style == SkPaint::kStroke_Style) + content->writeText("S"); + + if (style != SkPaint::kStroke_Style) { + NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false); + NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false); + if (fill == SkPath::kEvenOdd_FillType) + content->writeText("*"); + } + content->writeText("\n"); +} + +// static +void SkPDFUtils::StrokePath(SkWStream* content) { + SkPDFUtils::PaintPath( + SkPaint::kStroke_Style, SkPath::kWinding_FillType, content); +} + +// static +void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) { + content->writeText("/X"); + content->writeDecAsText(objectIndex); + content->writeText(" Do\n"); +} + +// static +void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) { + content->writeText("/G"); + content->writeDecAsText(objectIndex); + content->writeText(" gs\n"); +} diff --git a/src/pdf/pdf_files.mk b/src/pdf/pdf_files.mk new file mode 100644 index 0000000000..c9b4fb41d3 --- /dev/null +++ b/src/pdf/pdf_files.mk @@ -0,0 +1,13 @@ +SOURCE := \ + SkPDFCatalog.cpp \ + SkPDFDevice.cpp \ + SkPDFDocument.cpp \ + SkPDFFont.cpp \ + SkPDFFormXObject.cpp \ + SkPDFGraphicState.cpp \ + SkPDFImage.cpp \ + SkPDFPage.cpp \ + SkPDFShader.cpp \ + SkPDFStream.cpp \ + SkPDFTypes.cpp \ + SkPDFUtils.cpp diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h new file mode 100644 index 0000000000..fb1553693d --- /dev/null +++ b/src/pipe/SkGPipePriv.h @@ -0,0 +1,213 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#ifndef SkGPipePriv_DEFINED +#define SkGPipePriv_DEFINED + +#include "SkTypes.h" + +#define UNIMPLEMENTED + +// these must be contiguous, 0...N-1 +enum PaintFlats { + kColorFilter_PaintFlat, + kDrawLooper_PaintFlat, + kMaskFilter_PaintFlat, + kPathEffect_PaintFlat, + kRasterizer_PaintFlat, + kShader_PaintFlat, + kXfermode_PaintFlat, + + kLast_PaintFlat = kXfermode_PaintFlat +}; +#define kCount_PaintFlats (kLast_PaintFlat + 1) + +enum DrawOps { + kSkip_DrawOp, // skip an addition N bytes (N == data) + + // these match Canvas apis + kClipPath_DrawOp, + kClipRegion_DrawOp, + kClipRect_DrawOp, + kConcat_DrawOp, + kDrawBitmap_DrawOp, + kDrawBitmapMatrix_DrawOp, + kDrawBitmapRect_DrawOp, + kDrawClear_DrawOp, + kDrawData_DrawOp, + kDrawPaint_DrawOp, + kDrawPath_DrawOp, + kDrawPicture_DrawOp, + kDrawPoints_DrawOp, + kDrawPosText_DrawOp, + kDrawPosTextH_DrawOp, + kDrawRect_DrawOp, + kDrawShape_DrawOp, + kDrawSprite_DrawOp, + kDrawText_DrawOp, + kDrawTextOnPath_DrawOp, + kDrawVertices_DrawOp, + kRestore_DrawOp, + kRotate_DrawOp, + kSave_DrawOp, + kSaveLayer_DrawOp, + kScale_DrawOp, + kSetMatrix_DrawOp, + kSkew_DrawOp, + kTranslate_DrawOp, + + kPaintOp_DrawOp, + + kDef_Typeface_DrawOp, + kDef_Flattenable_DrawOp, + + kName_Flattenable_DrawOp, // index <--> name + + // these are signals to playback, not drawing verbs + kDone_DrawOp, +}; + +/** + * DrawOp packs into a 32bit int as follows + * + * DrawOp:8 - Flags:4 - Data:20 + * + * Flags and Data are called out separately, so we can reuse Data between + * different Ops that might have different Flags. e.g. Data might be a Paint + * index for both drawRect (no flags) and saveLayer (does have flags). + * + * All Ops that take a SkPaint use their Data field to store the index to + * the paint (previously defined with kPaintOp_DrawOp). + */ + +#define DRAWOPS_OP_BITS 8 +#define DRAWOPS_FLAG_BITS 4 +#define DRAWOPS_DATA_BITS 20 + +#define DRAWOPS_OP_MASK ((1 << DRAWOPS_OP_BITS) - 1) +#define DRAWOPS_FLAG_MASK ((1 << DRAWOPS_FLAG_BITS) - 1) +#define DRAWOPS_DATA_MASK ((1 << DRAWOPS_DATA_BITS) - 1) + +static unsigned DrawOp_unpackOp(uint32_t op32) { + return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS)); +} + +static unsigned DrawOp_unpackFlags(uint32_t op32) { + return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK; +} + +static unsigned DrawOp_unpackData(uint32_t op32) { + return op32 & DRAWOPS_DATA_MASK; +} + +static uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) { + SkASSERT(0 == (op & ~DRAWOPS_OP_MASK)); + SkASSERT(0 == (flags & ~DRAWOPS_FLAG_MASK)); + SkASSERT(0 == (data & ~DRAWOPS_DATA_MASK)); + + return (op << DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS) | + (flags << DRAWOPS_DATA_BITS) | + data; +} + +/** DrawOp specific flag bits + */ + +enum { + kSaveLayer_HasBounds_DrawOpFlag = 1 << 0, + kSaveLayer_HasPaint_DrawOpFlag = 1 << 1, +}; +enum { + kClear_HasColor_DrawOpFlag = 1 << 0 +}; +enum { + kDrawTextOnPath_HasMatrix_DrawOpFlag = 1 << 0 +}; +enum { + kDrawVertices_HasTexs_DrawOpFlag = 1 << 0, + kDrawVertices_HasColors_DrawOpFlag = 1 << 1, + kDrawVertices_HasIndices_DrawOpFlag = 1 << 2, +}; + +/////////////////////////////////////////////////////////////////////////////// + +enum PaintOps { + kReset_PaintOp, // no arg + + kFlags_PaintOp, // arg inline + kColor_PaintOp, // arg 32 + kStyle_PaintOp, // arg inline + kJoin_PaintOp, // arg inline + kCap_PaintOp, // arg inline + kWidth_PaintOp, // arg scalar + kMiter_PaintOp,// arg scalar + + kEncoding_PaintOp, // arg inline - text + kHinting_PaintOp, // arg inline - text + kAlign_PaintOp, // arg inline - text + kTextSize_PaintOp, // arg scalar - text + kTextScaleX_PaintOp,// arg scalar - text + kTextSkewX_PaintOp, // arg scalar - text + kTypeface_PaintOp, // arg inline (index) - text + + kFlatIndex_PaintOp, // flags=paintflat, data=index +}; + +#define PAINTOPS_OP_BITS 8 +#define PAINTOPS_FLAG_BITS 4 +#define PAINTOPS_DATA_BITS 20 + +#define PAINTOPS_OP_MASK ((1 << PAINTOPS_OP_BITS) - 1) +#define PAINTOPS_FLAG_MASK ((1 << PAINTOPS_FLAG_BITS) - 1) +#define PAINTOPS_DATA_MASK ((1 << PAINTOPS_DATA_BITS) - 1) + +static unsigned PaintOp_unpackOp(uint32_t op32) { + return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS)); +} + +static unsigned PaintOp_unpackFlags(uint32_t op32) { + return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK; +} + +static unsigned PaintOp_unpackData(uint32_t op32) { + return op32 & PAINTOPS_DATA_MASK; +} + +static uint32_t PaintOp_packOp(PaintOps op) { + SkASSERT(0 == (op & ~PAINTOPS_OP_MASK)); + + return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS); +} + +static uint32_t PaintOp_packOpData(PaintOps op, unsigned data) { + SkASSERT(0 == (op & ~PAINTOPS_OP_MASK)); + SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK)); + + return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) | data; +} + +static uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) { + SkASSERT(0 == (op & ~PAINTOPS_OP_MASK)); + SkASSERT(0 == (flags & ~PAINTOPS_FLAG_MASK)); + SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK)); + + return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) | + (flags << PAINTOPS_DATA_BITS) | + data; +} + +#endif diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp new file mode 100644 index 0000000000..ecb19fd059 --- /dev/null +++ b/src/pipe/SkGPipeRead.cpp @@ -0,0 +1,570 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkGPipe.h" +#include "SkGPipePriv.h" +#include "SkReader32.h" +#include "SkStream.h" + +#include "SkColorFilter.h" +#include "SkDrawLooper.h" +#include "SkMaskFilter.h" +#include "SkPathEffect.h" +#include "SkRasterizer.h" +#include "SkShader.h" +#include "SkTypeface.h" +#include "SkXfermode.h" + +static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat) { + SkASSERT(paintFlat < kCount_PaintFlats); + switch (paintFlat) { + case kColorFilter_PaintFlat: + paint->setColorFilter((SkColorFilter*)obj); + break; + case kDrawLooper_PaintFlat: + paint->setLooper((SkDrawLooper*)obj); + break; + case kMaskFilter_PaintFlat: + paint->setMaskFilter((SkMaskFilter*)obj); + break; + case kPathEffect_PaintFlat: + paint->setPathEffect((SkPathEffect*)obj); + break; + case kRasterizer_PaintFlat: + paint->setRasterizer((SkRasterizer*)obj); + break; + case kShader_PaintFlat: + paint->setShader((SkShader*)obj); + break; + case kXfermode_PaintFlat: + paint->setXfermode((SkXfermode*)obj); + break; + default: + SkASSERT(!"never gets here"); + } +} + +template <typename T> class SkRefCntTDArray : public SkTDArray<T> { +public: + ~SkRefCntTDArray() { this->unrefAll(); } +}; + +class SkGPipeState { +public: + SkGPipeState(); + ~SkGPipeState(); + + void setReader(SkFlattenableReadBuffer* reader) { + fReader = reader; + fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count()); + } + + const SkPaint& paint() const { return fPaint; } + SkPaint* editPaint() { return &fPaint; } + + SkFlattenable* getFlat(unsigned index) const { + if (0 == index) { + return NULL; + } + return fFlatArray[index - 1]; + } + + void defFlattenable(PaintFlats pf, unsigned index) { + SkASSERT(index == fFlatArray.count() + 1); + SkFlattenable* obj = fReader->readFlattenable(); + *fFlatArray.append() = obj; + } + + void nameFlattenable(PaintFlats pf, unsigned index) { + SkASSERT(index == fFactoryArray.count() + 1); + const char* name = fReader->readString(); + SkFlattenable::Factory fact = SkFlattenable::NameToFactory(name); + *fFactoryArray.append() = fact; + + // update this each time we grow the array + fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count()); + } + + void addTypeface() { + size_t size = fReader->readU32(); + const void* data = fReader->skip(SkAlign4(size)); + SkMemoryStream stream(data, size, false); + *fTypefaces.append() = SkTypeface::Deserialize(&stream); + } + void setTypeface(SkPaint* paint, unsigned id) { + paint->setTypeface(id ? fTypefaces[id - 1] : NULL); + } + + SkFlattenableReadBuffer* fReader; + +private: + SkPaint fPaint; + SkTDArray<SkFlattenable*> fFlatArray; + SkTDArray<SkTypeface*> fTypefaces; + SkTDArray<SkFlattenable::Factory> fFactoryArray; +}; + +/////////////////////////////////////////////////////////////////////////////// + +template <typename T> const T* skip(SkReader32* reader, int count = 1) { + SkASSERT(count >= 0); + size_t size = sizeof(T) * count; + SkASSERT(SkAlign4(size) == size); + return reinterpret_cast<const T*>(reader->skip(size)); +} + +template <typename T> const T* skipAlign(SkReader32* reader, int count = 1) { + SkASSERT(count >= 0); + size_t size = SkAlign4(sizeof(T) * count); + return reinterpret_cast<const T*>(reader->skip(size)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void clipPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkPath path; + path.unflatten(*reader); + canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32)); +} + +static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkRegion rgn; + SkReadRegion(reader, &rgn); + canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32)); +} + +static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + canvas->clipRect(*skip<SkRect>(reader), (SkRegion::Op)DrawOp_unpackData(op32)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkMatrix matrix; + SkReadMatrix(reader, &matrix); + canvas->setMatrix(matrix); +} + +static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkMatrix matrix; + SkReadMatrix(reader, &matrix); + canvas->concat(matrix); +} + +static void scale_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + const SkScalar* param = skip<SkScalar>(reader, 2); + canvas->scale(param[0], param[1]); +} + +static void skew_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + const SkScalar* param = skip<SkScalar>(reader, 2); + canvas->skew(param[0], param[1]); +} + +static void rotate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + canvas->rotate(reader->readScalar()); +} + +static void translate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + const SkScalar* param = skip<SkScalar>(reader, 2); + canvas->translate(param[0], param[1]); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void save_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + canvas->save((SkCanvas::SaveFlags)DrawOp_unpackData(op32)); +} + +static void saveLayer_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + unsigned flags = DrawOp_unpackFlags(op32); + SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags)DrawOp_unpackData(op32); + + const SkRect* bounds = NULL; + if (flags & kSaveLayer_HasBounds_DrawOpFlag) { + bounds = skip<SkRect>(reader); + } + const SkPaint* paint = NULL; + if (flags & kSaveLayer_HasPaint_DrawOpFlag) { + paint = &state->paint(); + } + canvas->saveLayer(bounds, paint, saveFlags); +} + +static void restore_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + canvas->restore(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void drawClear_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkColor color = 0; + if (DrawOp_unpackFlags(op32) & kClear_HasColor_DrawOpFlag) { + color = reader->readU32(); + } + canvas->clear(color); +} + +static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + canvas->drawPaint(state->paint()); +} + +static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkCanvas::PointMode mode = (SkCanvas::PointMode)DrawOp_unpackFlags(op32); + size_t count = reader->readU32(); + const SkPoint* pts = skip<SkPoint>(reader, count); + canvas->drawPoints(mode, count, pts, state->paint()); +} + +static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + canvas->drawRect(*skip<SkRect>(reader), state->paint()); +} + +static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + SkPath path; + path.unflatten(*reader); + canvas->drawPath(path, state->paint()); +} + +static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + unsigned flags = DrawOp_unpackFlags(op32); + + SkCanvas::VertexMode mode = (SkCanvas::VertexMode)reader->readU32(); + int vertexCount = reader->readU32(); + const SkPoint* verts = skip<SkPoint>(reader, vertexCount); + + const SkPoint* texs = NULL; + if (flags & kDrawVertices_HasTexs_DrawOpFlag) { + texs = skip<SkPoint>(reader, vertexCount); + } + + const SkColor* colors = NULL; + if (flags & kDrawVertices_HasColors_DrawOpFlag) { + colors = skip<SkColor>(reader, vertexCount); + } + + // TODO: flatten/unflatten xfermodes + SkXfermode* xfer = NULL; + + int indexCount = 0; + const uint16_t* indices = NULL; + if (flags & kDrawVertices_HasIndices_DrawOpFlag) { + indexCount = reader->readU32(); + indices = skipAlign<uint16_t>(reader, indexCount); + } + + canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer, + indices, indexCount, state->paint()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void drawText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + size_t len = reader->readU32(); + const void* text = reader->skip(SkAlign4(len)); + const SkScalar* xy = skip<SkScalar>(reader, 2); + canvas->drawText(text, len, xy[0], xy[1], state->paint()); +} + +static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + size_t len = reader->readU32(); + const void* text = reader->skip(SkAlign4(len)); + size_t posCount = reader->readU32(); // compute by our writer + const SkPoint* pos = skip<SkPoint>(reader, posCount); + canvas->drawPosText(text, len, pos, state->paint()); +} + +static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + size_t len = reader->readU32(); + const void* text = reader->skip(SkAlign4(len)); + size_t posCount = reader->readU32(); // compute by our writer + const SkScalar* xpos = skip<SkScalar>(reader, posCount); + SkScalar constY = reader->readScalar(); + canvas->drawPosTextH(text, len, xpos, constY, state->paint()); +} + +static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + size_t len = reader->readU32(); + const void* text = reader->skip(SkAlign4(len)); + + SkPath path; + path.unflatten(*reader); + + SkMatrix matrixStorage; + const SkMatrix* matrix = NULL; + if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) { + SkReadMatrix(reader, &matrixStorage); + matrix = &matrixStorage; + } + + canvas->drawTextOnPath(text, len, path, matrix, state->paint()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + UNIMPLEMENTED +} + +static void drawBitmapMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + UNIMPLEMENTED +} + +static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + UNIMPLEMENTED +} + +static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + UNIMPLEMENTED +} + +/////////////////////////////////////////////////////////////////////////////// + +static void drawData_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + // since we don't have a paint, we can use data for our (small) sizes + size_t size = DrawOp_unpackData(op32); + if (0 == size) { + size = reader->readU32(); + } + const void* data = reader->skip(SkAlign4(size)); + canvas->drawData(data, size); +} + +static void drawShape_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + UNIMPLEMENTED +} + +static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + UNIMPLEMENTED +} + +/////////////////////////////////////////////////////////////////////////////// + +static void paintOp_rp(SkCanvas*, SkReader32* reader, uint32_t op32, + SkGPipeState* state) { + size_t offset = reader->offset(); + size_t stop = offset + PaintOp_unpackData(op32); + SkPaint* p = state->editPaint(); + + do { + uint32_t p32 = reader->readU32(); + unsigned op = PaintOp_unpackOp(p32); + unsigned data = PaintOp_unpackData(p32); + +// SkDebugf(" read %08X op=%d flags=%d data=%d\n", p32, op, done, data); + + switch (op) { + case kReset_PaintOp: p->reset(); break; + case kFlags_PaintOp: p->setFlags(data); break; + case kColor_PaintOp: p->setColor(reader->readU32()); break; + case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break; + case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break; + case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break; + case kWidth_PaintOp: p->setStrokeWidth(reader->readScalar()); break; + case kMiter_PaintOp: p->setStrokeMiter(reader->readScalar()); break; + case kEncoding_PaintOp: + p->setTextEncoding((SkPaint::TextEncoding)data); + break; + case kHinting_PaintOp: p->setHinting((SkPaint::Hinting)data); break; + case kAlign_PaintOp: p->setTextAlign((SkPaint::Align)data); break; + case kTextSize_PaintOp: p->setTextSize(reader->readScalar()); break; + case kTextScaleX_PaintOp: p->setTextScaleX(reader->readScalar()); break; + case kTextSkewX_PaintOp: p->setTextSkewX(reader->readScalar()); break; + + case kFlatIndex_PaintOp: { + PaintFlats pf = (PaintFlats)PaintOp_unpackFlags(p32); + unsigned index = data; + set_paintflat(p, state->getFlat(index), pf); + break; + } + + case kTypeface_PaintOp: state->setTypeface(p, data); break; + default: SkASSERT(!"bad paintop"); return; + } + SkASSERT(reader->offset() <= stop); + } while (reader->offset() < stop); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void def_Typeface_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState* state) { + state->addTypeface(); +} + +static void def_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32, + SkGPipeState* state) { + PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32); + unsigned index = DrawOp_unpackData(op32); + state->defFlattenable(pf, index); +} + +static void name_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32, + SkGPipeState* state) { + PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32); + unsigned index = DrawOp_unpackData(op32); + state->nameFlattenable(pf, index); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) { + size_t bytes = DrawOp_unpackData(op32); + (void)reader->skip(bytes); +} + +static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {} + +typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*); + +static const ReadProc gReadTable[] = { + skip_rp, + clipPath_rp, + clipRegion_rp, + clipRect_rp, + concat_rp, + drawBitmap_rp, + drawBitmapMatrix_rp, + drawBitmapRect_rp, + drawClear_rp, + drawData_rp, + drawPaint_rp, + drawPath_rp, + drawPicture_rp, + drawPoints_rp, + drawPosText_rp, + drawPosTextH_rp, + drawRect_rp, + drawShape_rp, + drawSprite_rp, + drawText_rp, + drawTextOnPath_rp, + drawVertices_rp, + restore_rp, + rotate_rp, + save_rp, + saveLayer_rp, + scale_rp, + setMatrix_rp, + skew_rp, + translate_rp, + + paintOp_rp, + def_Typeface_rp, + def_PaintFlat_rp, + name_PaintFlat_rp, + + done_rp +}; + +/////////////////////////////////////////////////////////////////////////////// + +SkGPipeState::SkGPipeState() {} + +SkGPipeState::~SkGPipeState() { + fTypefaces.unrefAll(); + fFlatArray.unrefAll(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkGPipe.h" + +SkGPipeReader::SkGPipeReader(SkCanvas* target) { + SkSafeRef(target); + fCanvas = target; + fState = NULL; +} + +SkGPipeReader::~SkGPipeReader() { + SkSafeUnref(fCanvas); + delete fState; +} + +SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length, + size_t* bytesRead) { + if (NULL == fCanvas) { + return kError_Status; + } + + if (NULL == fState) { + fState = new SkGPipeState; + } + + SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1)); + + const ReadProc* table = gReadTable; + SkFlattenableReadBuffer reader(data, length); + SkCanvas* canvas = fCanvas; + Status status = kEOF_Status; + + fState->setReader(&reader); + while (!reader.eof()) { + uint32_t op32 = reader.readU32(); + unsigned op = DrawOp_unpackOp(op32); + SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;) + + if (op >= SK_ARRAY_COUNT(gReadTable)) { + SkDebugf("---- bad op during GPipeState::playback\n"); + status = kError_Status; + break; + } + if (kDone_DrawOp == op) { + status = kDone_Status; + break; + } + table[op](canvas, &reader, op32, fState); + } + + if (bytesRead) { + *bytesRead = reader.offset(); + } + return status; +} + + diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp new file mode 100644 index 0000000000..428f864fe9 --- /dev/null +++ b/src/pipe/SkGPipeWrite.cpp @@ -0,0 +1,813 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkGPipe.h" +#include "SkGPipePriv.h" +#include "SkStream.h" +#include "SkTSearch.h" +#include "SkTypeface.h" +#include "SkWriter32.h" +#include "SkColorFilter.h" +#include "SkDrawLooper.h" +#include "SkMaskFilter.h" +#include "SkRasterizer.h" +#include "SkShader.h" + +static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) { + SkASSERT(paintFlat < kCount_PaintFlats); + switch (paintFlat) { + case kColorFilter_PaintFlat: return paint.getColorFilter(); + case kDrawLooper_PaintFlat: return paint.getLooper(); + case kMaskFilter_PaintFlat: return paint.getMaskFilter(); + case kPathEffect_PaintFlat: return paint.getPathEffect(); + case kRasterizer_PaintFlat: return paint.getRasterizer(); + case kShader_PaintFlat: return paint.getShader(); + case kXfermode_PaintFlat: return paint.getXfermode(); + } + SkASSERT(!"never gets here"); + return NULL; +} + +static size_t estimateFlattenSize(const SkPath& path) { + int n = path.countPoints(); + size_t bytes = 3 * sizeof(int32_t); + bytes += n * sizeof(SkPoint); + bytes += SkAlign4(n + 2); // verbs: add 2 for move/close extras + +#ifdef SK_DEBUG + { + SkWriter32 writer(1024); + path.flatten(writer); + SkASSERT(writer.size() <= bytes); + } +#endif + return bytes; +} + +static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) { + SkASSERT(typeface); + SkDynamicMemoryWStream stream; + typeface->serialize(&stream); + size_t size = stream.getOffset(); + if (writer) { + writer->write32(size); + writer->write(stream.getStream(), size); + } + return 4 + size; +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkGPipeCanvas : public SkCanvas { +public: + SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*); + virtual ~SkGPipeCanvas(); + + void finish() { + if (!fDone) { + this->writeOp(kDone_DrawOp); + this->doNotify(); + fDone = true; + } + } + + // overrides from SkCanvas + virtual int save(SaveFlags); + virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags); + virtual void restore(); + virtual bool translate(SkScalar dx, SkScalar dy); + virtual bool scale(SkScalar sx, SkScalar sy); + virtual bool rotate(SkScalar degrees); + virtual bool skew(SkScalar sx, SkScalar sy); + virtual bool concat(const SkMatrix& matrix); + virtual void setMatrix(const SkMatrix& matrix); + virtual bool clipRect(const SkRect& rect, SkRegion::Op op); + virtual bool clipPath(const SkPath& path, SkRegion::Op op); + virtual bool clipRegion(const SkRegion& region, SkRegion::Op op); + virtual void clear(SkColor); + virtual void drawPaint(const SkPaint& paint); + virtual void drawPoints(PointMode, size_t count, const SkPoint pts[], + const SkPaint&); + virtual void drawRect(const SkRect& rect, const SkPaint&); + virtual void drawPath(const SkPath& path, const SkPaint&); + virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top, + const SkPaint*); + virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src, + const SkRect& dst, const SkPaint*); + virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&, + const SkPaint*); + virtual void drawSprite(const SkBitmap&, int left, int top, + const SkPaint*); + virtual void drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint&); + virtual void drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint&); + virtual void drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, const SkPaint&); + virtual void drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint&); + virtual void drawPicture(SkPicture& picture); + virtual void drawShape(SkShape*); + virtual void drawVertices(VertexMode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode*, + const uint16_t indices[], int indexCount, + const SkPaint&); + virtual void drawData(const void*, size_t); + +private: + SkFactorySet* fFactorySet; // optional, only used if cross-process + SkGPipeController* fController; + SkWriter32& fWriter; + size_t fBlockSize; // amount allocated for writer + size_t fBytesNotified; + bool fDone; + + SkRefCntSet fTypefaceSet; + + uint32_t getTypefaceID(SkTypeface*); + + inline void writeOp(DrawOps op, unsigned flags, unsigned data) { + fWriter.write32(DrawOp_packOpFlagData(op, flags, data)); + } + + inline void writeOp(DrawOps op) { + fWriter.write32(DrawOp_packOpFlagData(op, 0, 0)); + } + + bool needOpBytes(size_t size = 0); + + inline void doNotify() { + if (!fDone) { + size_t bytes = fWriter.size() - fBytesNotified; + fController->notifyWritten(bytes); + fBytesNotified += bytes; + } + } + + struct FlatData { + uint32_t fIndex; // always > 0 + uint32_t fSize; + + void* data() { return (char*)this + sizeof(*this); } + + static int Compare(const FlatData* a, const FlatData* b) { + return memcmp(&a->fSize, &b->fSize, a->fSize + sizeof(a->fSize)); + } + }; + SkTDArray<FlatData*> fFlatArray; + int fCurrFlatIndex[kCount_PaintFlats]; + int flattenToIndex(SkFlattenable* obj, PaintFlats); + + SkPaint fPaint; + void writePaint(const SkPaint&); + + class AutoPipeNotify { + public: + AutoPipeNotify(SkGPipeCanvas* canvas) : fCanvas(canvas) {} + ~AutoPipeNotify() { fCanvas->doNotify(); } + private: + SkGPipeCanvas* fCanvas; + }; + friend class AutoPipeNotify; + + typedef SkCanvas INHERITED; +}; + +// return 0 for NULL (or unflattenable obj), or index-base-1 +int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) { + if (NULL == obj) { + return 0; + } + + SkFlattenable::Factory fact = obj->getFactory(); + if (NULL == fact) { + return 0; + } + + if (fFactorySet) { + uint32_t id = fFactorySet->find((void*)fact); + if (0 == id) { + const char* name = SkFlattenable::FactoryToName(fact); + if (NULL == name) { + return 0; + } + size_t len = strlen(name); + size_t size = SkWriter32::WriteStringSize(name, len); + if (!this->needOpBytes(size)) { + return 0; + } + unsigned id = fFactorySet->add(fact); + this->writeOp(kName_Flattenable_DrawOp, paintflat, id); + fWriter.writeString(name, len); + } + } + + SkFlattenableWriteBuffer tmpWriter(1024); + tmpWriter.setFactoryRecorder(fFactorySet); + + tmpWriter.writeFlattenable(obj); + size_t len = tmpWriter.size(); + size_t allocSize = len + sizeof(FlatData); + + SkAutoSMalloc<1024> storage(allocSize); + FlatData* flat = (FlatData*)storage.get(); + flat->fSize = len; + tmpWriter.flatten(flat->data()); + + int index = SkTSearch<FlatData>((const FlatData**)fFlatArray.begin(), + fFlatArray.count(), flat, sizeof(flat), + &FlatData::Compare); + if (index < 0) { + index = ~index; + FlatData* copy = (FlatData*)sk_malloc_throw(allocSize); + memcpy(copy, flat, allocSize); + *fFlatArray.insert(index) = copy; + // call this after the insert, so that count() will have been grown + copy->fIndex = fFlatArray.count(); +// SkDebugf("--- add flattenable[%d] size=%d index=%d\n", paintflat, len, copy->fIndex); + + if (this->needOpBytes(len)) { + this->writeOp(kDef_Flattenable_DrawOp, paintflat, copy->fIndex); + fWriter.write(copy->data(), len); + } + } + return fFlatArray[index]->fIndex; +} + +/////////////////////////////////////////////////////////////////////////////// + +#define MIN_BLOCK_SIZE (16 * 1024) + +SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller, + SkWriter32* writer, SkFactorySet* fset) + : fWriter(*writer), fFactorySet(fset) { + fController = controller; + fDone = false; + fBlockSize = 0; // need first block from controller + sk_bzero(fCurrFlatIndex, sizeof(fCurrFlatIndex)); + + // we need a device to limit our clip + // should the caller give us the bounds? + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32767, 32767); + SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false)); + this->setDevice(device)->unref(); +} + +SkGPipeCanvas::~SkGPipeCanvas() { + this->finish(); + + fFlatArray.freeAll(); +} + +bool SkGPipeCanvas::needOpBytes(size_t needed) { + if (fDone) { + return false; + } + + needed += 4; // size of DrawOp atom + if (fWriter.size() + needed > fBlockSize) { + void* block = fController->requestBlock(MIN_BLOCK_SIZE, &fBlockSize); + if (NULL == block) { + fDone = true; + return false; + } + fWriter.reset(block, fBlockSize); + fBytesNotified = 0; + } + return true; +} + +uint32_t SkGPipeCanvas::getTypefaceID(SkTypeface* face) { + uint32_t id = 0; // 0 means default/null typeface + if (face) { + id = fTypefaceSet.find(face); + if (0 == id) { + id = fTypefaceSet.add(face); + size_t size = writeTypeface(NULL, face); + if (this->needOpBytes(size)) { + this->writeOp(kDef_Typeface_DrawOp); + writeTypeface(&fWriter, face); + } + } + } + return id; +} + +/////////////////////////////////////////////////////////////////////////////// + +#define NOTIFY_SETUP(canvas) \ + AutoPipeNotify apn(canvas) + +int SkGPipeCanvas::save(SaveFlags flags) { + NOTIFY_SETUP(this); + if (this->needOpBytes()) { + this->writeOp(kSave_DrawOp, 0, flags); + } + return this->INHERITED::save(flags); +} + +int SkGPipeCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags saveFlags) { + NOTIFY_SETUP(this); + size_t size = 0; + unsigned opFlags = 0; + + if (bounds) { + opFlags |= kSaveLayer_HasBounds_DrawOpFlag; + size += sizeof(SkRect); + } + if (paint) { + opFlags |= kSaveLayer_HasPaint_DrawOpFlag; + this->writePaint(*paint); + } + + if (this->needOpBytes(size)) { + this->writeOp(kSaveLayer_DrawOp, opFlags, saveFlags); + if (bounds) { + fWriter.writeRect(*bounds); + } + } + + // we just pass on the save, so we don't create a layer + return this->INHERITED::save(saveFlags); +} + +void SkGPipeCanvas::restore() { + NOTIFY_SETUP(this); + if (this->needOpBytes()) { + this->writeOp(kRestore_DrawOp); + } + this->INHERITED::restore(); +} + +bool SkGPipeCanvas::translate(SkScalar dx, SkScalar dy) { + if (dx || dy) { + NOTIFY_SETUP(this); + if (this->needOpBytes(2 * sizeof(SkScalar))) { + this->writeOp(kTranslate_DrawOp); + fWriter.writeScalar(dx); + fWriter.writeScalar(dy); + } + } + return this->INHERITED::translate(dx, dy); +} + +bool SkGPipeCanvas::scale(SkScalar sx, SkScalar sy) { + if (sx || sy) { + NOTIFY_SETUP(this); + if (this->needOpBytes(2 * sizeof(SkScalar))) { + this->writeOp(kScale_DrawOp); + fWriter.writeScalar(sx); + fWriter.writeScalar(sy); + } + } + return this->INHERITED::scale(sx, sy); +} + +bool SkGPipeCanvas::rotate(SkScalar degrees) { + if (degrees) { + NOTIFY_SETUP(this); + if (this->needOpBytes(sizeof(SkScalar))) { + this->writeOp(kRotate_DrawOp); + fWriter.writeScalar(degrees); + } + } + return this->INHERITED::rotate(degrees); +} + +bool SkGPipeCanvas::skew(SkScalar sx, SkScalar sy) { + if (sx || sy) { + NOTIFY_SETUP(this); + if (this->needOpBytes(2 * sizeof(SkScalar))) { + this->writeOp(kSkew_DrawOp); + fWriter.writeScalar(sx); + fWriter.writeScalar(sy); + } + } + return this->INHERITED::skew(sx, sy); +} + +bool SkGPipeCanvas::concat(const SkMatrix& matrix) { + if (!matrix.isIdentity()) { + NOTIFY_SETUP(this); + if (this->needOpBytes(matrix.flatten(NULL))) { + this->writeOp(kConcat_DrawOp); + SkWriteMatrix(&fWriter, matrix); + } + } + return this->INHERITED::concat(matrix); +} + +void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) { + NOTIFY_SETUP(this); + if (this->needOpBytes(matrix.flatten(NULL))) { + this->writeOp(kSetMatrix_DrawOp); + SkWriteMatrix(&fWriter, matrix); + } + this->INHERITED::setMatrix(matrix); +} + +bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp) { + NOTIFY_SETUP(this); + if (this->needOpBytes(sizeof(SkRect))) { + this->writeOp(kClipRect_DrawOp, 0, rgnOp); + fWriter.writeRect(rect); + } + return this->INHERITED::clipRect(rect, rgnOp); +} + +bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp) { + NOTIFY_SETUP(this); + if (this->needOpBytes(estimateFlattenSize(path))) { + this->writeOp(kClipPath_DrawOp, 0, rgnOp); + path.flatten(fWriter); + } + // we just pass on the bounds of the path + return this->INHERITED::clipRect(path.getBounds(), rgnOp); +} + +bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) { + NOTIFY_SETUP(this); + if (this->needOpBytes(region.flatten(NULL))) { + this->writeOp(kClipRegion_DrawOp, 0, rgnOp); + SkWriteRegion(&fWriter, region); + } + return this->INHERITED::clipRegion(region, rgnOp); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGPipeCanvas::clear(SkColor color) { + NOTIFY_SETUP(this); + unsigned flags = 0; + if (color) { + flags |= kClear_HasColor_DrawOpFlag; + } + if (this->needOpBytes(sizeof(SkColor))) { + this->writeOp(kDrawClear_DrawOp, flags, 0); + if (color) { + fWriter.write32(color); + } + } +} + +void SkGPipeCanvas::drawPaint(const SkPaint& paint) { + NOTIFY_SETUP(this); + this->writePaint(paint); + if (this->needOpBytes()) { + this->writeOp(kDrawPaint_DrawOp); + } +} + +void SkGPipeCanvas::drawPoints(PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint) { + if (count) { + NOTIFY_SETUP(this); + this->writePaint(paint); + if (this->needOpBytes(4 + count * sizeof(SkPoint))) { + this->writeOp(kDrawPoints_DrawOp, mode, 0); + fWriter.write32(count); + fWriter.write(pts, count * sizeof(SkPoint)); + } + } +} + +void SkGPipeCanvas::drawRect(const SkRect& rect, const SkPaint& paint) { + NOTIFY_SETUP(this); + this->writePaint(paint); + if (this->needOpBytes(sizeof(SkRect))) { + this->writeOp(kDrawRect_DrawOp); + fWriter.writeRect(rect); + } +} + +void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) { + NOTIFY_SETUP(this); + this->writePaint(paint); + if (this->needOpBytes(estimateFlattenSize(path))) { + this->writeOp(kDrawPath_DrawOp); + path.flatten(fWriter); + } +} + +void SkGPipeCanvas::drawBitmap(const SkBitmap&, SkScalar left, SkScalar top, + const SkPaint*) { + UNIMPLEMENTED +} + +void SkGPipeCanvas::drawBitmapRect(const SkBitmap&, const SkIRect* src, + const SkRect& dst, const SkPaint*) { + UNIMPLEMENTED +} + +void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap&, const SkMatrix&, + const SkPaint*) { + UNIMPLEMENTED +} + +void SkGPipeCanvas::drawSprite(const SkBitmap&, int left, int top, + const SkPaint*) { + UNIMPLEMENTED +} + +void SkGPipeCanvas::drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint) { + if (byteLength) { + NOTIFY_SETUP(this); + this->writePaint(paint); + if (this->needOpBytes(4 + SkAlign4(byteLength) + 2 * sizeof(SkScalar))) { + this->writeOp(kDrawText_DrawOp); + fWriter.write32(byteLength); + fWriter.writePad(text, byteLength); + fWriter.writeScalar(x); + fWriter.writeScalar(y); + } + } +} + +void SkGPipeCanvas::drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) { + if (byteLength) { + NOTIFY_SETUP(this); + this->writePaint(paint); + int count = paint.textToGlyphs(text, byteLength, NULL); + if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkPoint))) { + this->writeOp(kDrawPosText_DrawOp); + fWriter.write32(byteLength); + fWriter.writePad(text, byteLength); + fWriter.write32(count); + fWriter.write(pos, count * sizeof(SkPoint)); + } + } +} + +void SkGPipeCanvas::drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint) { + if (byteLength) { + NOTIFY_SETUP(this); + this->writePaint(paint); + int count = paint.textToGlyphs(text, byteLength, NULL); + if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkScalar) + 4)) { + this->writeOp(kDrawPosTextH_DrawOp); + fWriter.write32(byteLength); + fWriter.writePad(text, byteLength); + fWriter.write32(count); + fWriter.write(xpos, count * sizeof(SkScalar)); + fWriter.writeScalar(constY); + } + } +} + +void SkGPipeCanvas::drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) { + if (byteLength) { + NOTIFY_SETUP(this); + unsigned flags = 0; + size_t size = 4 + SkAlign4(byteLength) + estimateFlattenSize(path); + if (matrix) { + flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag; + size += matrix->flatten(NULL); + } + this->writePaint(paint); + if (this->needOpBytes(size)) { + this->writeOp(kDrawTextOnPath_DrawOp, flags, 0); + + fWriter.write32(byteLength); + fWriter.writePad(text, byteLength); + + path.flatten(fWriter); + if (matrix) { + SkWriteMatrix(&fWriter, *matrix); + } + } + } +} + +void SkGPipeCanvas::drawPicture(SkPicture& picture) { + // we want to playback the picture into individual draw calls + this->INHERITED::drawPicture(picture); +} + +void SkGPipeCanvas::drawShape(SkShape* shape) { + UNIMPLEMENTED +} + +void SkGPipeCanvas::drawVertices(VertexMode mode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode*, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + if (0 == vertexCount) { + return; + } + + NOTIFY_SETUP(this); + size_t size = 4 + vertexCount * sizeof(SkPoint); + this->writePaint(paint); + unsigned flags = 0; + if (texs) { + flags |= kDrawVertices_HasTexs_DrawOpFlag; + size += vertexCount * sizeof(SkPoint); + } + if (colors) { + flags |= kDrawVertices_HasColors_DrawOpFlag; + size += vertexCount * sizeof(SkColor); + } + if (indices && indexCount > 0) { + flags |= kDrawVertices_HasIndices_DrawOpFlag; + size += 4 + SkAlign4(indexCount * sizeof(uint16_t)); + } + + if (this->needOpBytes(size)) { + this->writeOp(kDrawVertices_DrawOp, flags, 0); + fWriter.write32(mode); + fWriter.write32(vertexCount); + fWriter.write(vertices, vertexCount * sizeof(SkPoint)); + if (texs) { + fWriter.write(texs, vertexCount * sizeof(SkPoint)); + } + if (colors) { + fWriter.write(colors, vertexCount * sizeof(SkColor)); + } + + // TODO: flatten xfermode + + if (indices && indexCount > 0) { + fWriter.write32(indexCount); + fWriter.writePad(indices, indexCount * sizeof(uint16_t)); + } + } +} + +void SkGPipeCanvas::drawData(const void* ptr, size_t size) { + if (size && ptr) { + NOTIFY_SETUP(this); + unsigned data = 0; + if (size < (1 << DRAWOPS_DATA_BITS)) { + data = (unsigned)size; + } + if (this->needOpBytes(4 + SkAlign4(size))) { + this->writeOp(kDrawData_DrawOp, 0, data); + if (0 == data) { + fWriter.write32(size); + } + fWriter.writePad(ptr, size); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +template <typename T> uint32_t castToU32(T value) { + union { + T fSrc; + uint32_t fDst; + } data; + data.fSrc = value; + return data.fDst; +} + +void SkGPipeCanvas::writePaint(const SkPaint& paint) { + SkPaint& base = fPaint; + uint32_t storage[32]; + uint32_t* ptr = storage; + + if (base.getFlags() != paint.getFlags()) { + *ptr++ = PaintOp_packOpData(kFlags_PaintOp, paint.getFlags()); + base.setFlags(paint.getFlags()); + } + if (base.getColor() != paint.getColor()) { + *ptr++ = PaintOp_packOp(kColor_PaintOp); + *ptr++ = paint.getColor(); + base.setColor(paint.getColor()); + } + if (base.getStyle() != paint.getStyle()) { + *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle()); + base.setStyle(paint.getStyle()); + } + if (base.getStrokeJoin() != paint.getStrokeJoin()) { + *ptr++ = PaintOp_packOpData(kJoin_PaintOp, paint.getStrokeJoin()); + base.setStrokeJoin(paint.getStrokeJoin()); + } + if (base.getStrokeCap() != paint.getStrokeCap()) { + *ptr++ = PaintOp_packOpData(kCap_PaintOp, paint.getStrokeCap()); + base.setStrokeCap(paint.getStrokeCap()); + } + if (base.getStrokeWidth() != paint.getStrokeWidth()) { + *ptr++ = PaintOp_packOp(kWidth_PaintOp); + *ptr++ = castToU32(paint.getStrokeWidth()); + base.setStrokeWidth(paint.getStrokeWidth()); + } + if (base.getStrokeMiter() != paint.getStrokeMiter()) { + *ptr++ = PaintOp_packOp(kMiter_PaintOp); + *ptr++ = castToU32(paint.getStrokeMiter()); + base.setStrokeMiter(paint.getStrokeMiter()); + } + if (base.getTextEncoding() != paint.getTextEncoding()) { + *ptr++ = PaintOp_packOpData(kEncoding_PaintOp, paint.getTextEncoding()); + base.setTextEncoding(paint.getTextEncoding()); + } + if (base.getHinting() != paint.getHinting()) { + *ptr++ = PaintOp_packOpData(kHinting_PaintOp, paint.getHinting()); + base.setHinting(paint.getHinting()); + } + if (base.getTextAlign() != paint.getTextAlign()) { + *ptr++ = PaintOp_packOpData(kAlign_PaintOp, paint.getTextAlign()); + base.setTextAlign(paint.getTextAlign()); + } + if (base.getTextSize() != paint.getTextSize()) { + *ptr++ = PaintOp_packOp(kTextSize_PaintOp); + *ptr++ = castToU32(paint.getTextSize()); + base.setTextSize(paint.getTextSize()); + } + if (base.getTextScaleX() != paint.getTextScaleX()) { + *ptr++ = PaintOp_packOp(kTextScaleX_PaintOp); + *ptr++ = castToU32(paint.getTextScaleX()); + base.setTextScaleX(paint.getTextScaleX()); + } + if (base.getTextSkewX() != paint.getTextSkewX()) { + *ptr++ = PaintOp_packOp(kTextSkewX_PaintOp); + *ptr++ = castToU32(paint.getTextSkewX()); + base.setTextSkewX(paint.getTextSkewX()); + } + + if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) { + uint32_t id = this->getTypefaceID(paint.getTypeface()); + *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id); + base.setTypeface(paint.getTypeface()); + } + + for (int i = 0; i < kCount_PaintFlats; i++) { + int index = this->flattenToIndex(get_paintflat(paint, i), (PaintFlats)i); + SkASSERT(index >= 0 && index <= fFlatArray.count()); + if (index != fCurrFlatIndex[i]) { + *ptr++ = PaintOp_packOpFlagData(kFlatIndex_PaintOp, i, index); + fCurrFlatIndex[i] = index; + } + } + + size_t size = (char*)ptr - (char*)storage; + if (size && this->needOpBytes(size)) { + this->writeOp(kPaintOp_DrawOp, 0, size); + fWriter.write(storage, size); + for (size_t i = 0; i < size/4; i++) { +// SkDebugf("[%d] %08X\n", i, storage[i]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkGPipe.h" + +SkGPipeWriter::SkGPipeWriter() : fWriter(0) { + fCanvas = NULL; +} + +SkGPipeWriter::~SkGPipeWriter() { + this->endRecording(); + SkSafeUnref(fCanvas); +} + +SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, + uint32_t flags) { + if (NULL == fCanvas) { + fWriter.reset(NULL, 0); + fFactorySet.reset(); + fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter, + (flags & kCrossProcess_Flag) ? + &fFactorySet : NULL)); + } + return fCanvas; +} + +void SkGPipeWriter::endRecording() { + if (fCanvas) { + fCanvas->finish(); + fCanvas->unref(); + fCanvas = NULL; + } +} + diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index b3cc7832e5..de1ce737d4 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -72,6 +72,14 @@ using namespace skia_advanced_typeface_metrics_utils; +// SK_FREETYPE_LCD_LERP should be 0...256 +// 0 means no color reduction (e.g. just as returned from FreeType) +// 256 means 100% color reduction (e.g. gray) +// +#ifndef SK_FREETYPE_LCD_LERP + #define SK_FREETYPE_LCD_LERP 96 +#endif + ////////////////////////////////////////////////////////////////////////// struct SkFaceRec; @@ -906,7 +914,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } switch ( fFace->glyph->format ) { - case FT_GLYPH_FORMAT_OUTLINE: + case FT_GLYPH_FORMAT_OUTLINE: { FT_BBox bbox; if (fRec.fFlags & kEmbolden_Flag) { @@ -934,6 +942,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { glyph->fTop = -SkToS16(bbox.yMax >> 6); glyph->fLeft = SkToS16(bbox.xMin >> 6); break; + } case FT_GLYPH_FORMAT_BITMAP: if (fRec.fFlags & kEmbolden_Flag) { @@ -979,6 +988,22 @@ extern void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bi using namespace skia_freetype_support; #endif +static int lerp(int start, int end) { + SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256); + return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8); +} + +static uint16_t packTriple(unsigned r, unsigned g, unsigned b) { + if (SK_FREETYPE_LCD_LERP) { + // want (a+b+c)/3, but we approx to avoid the divide + unsigned ave = (5 * (r + g + b) + b) >> 4; + r = lerp(r, ave); + g = lerp(g, ave); + b = lerp(b, ave); + } + return SkPackRGB16(r >> 3, g >> 2, b >> 3); +} + static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap) { SkASSERT(glyph.fWidth * 3 == bitmap.width - 6); SkASSERT(glyph.fHeight == bitmap.rows); @@ -991,7 +1016,7 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap) { for (int y = 0; y < glyph.fHeight; y++) { const uint8_t* triple = src; for (int x = 0; x < width; x++) { - dst[x] = SkPackRGB16(triple[0] >> 3, triple[1] >> 2, triple[2] >> 3); + dst[x] = packTriple(triple[0], triple[1], triple[2]); triple += 3; } src += bitmap.pitch; diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp index 9aabce7db9..37c2c35eb2 100644 --- a/src/ports/SkFontHost_linux.cpp +++ b/src/ports/SkFontHost_linux.cpp @@ -391,7 +391,7 @@ static SkTypeface* gDefaultNormal; static void load_system_fonts() { // check if we've already be called if (NULL != gDefaultNormal) { - printf("---- default font %p\n", gDefaultNormal); +// printf("---- default font %p\n", gDefaultNormal); return; } diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp index e6378777ce..ad57e16f91 100644 --- a/src/ports/SkFontHost_mac_coretext.cpp +++ b/src/ports/SkFontHost_mac_coretext.cpp @@ -18,13 +18,17 @@ #include "SkFontHost.h" #include "SkDescriptor.h" +#include "SkEndian.h" #include "SkFloatingPoint.h" #include "SkPaint.h" #include "SkString.h" +#include "SkStream.h" #include "SkTypeface_mac.h" #include "SkUtils.h" #include "SkTypefaceCache.h" +using namespace skia_advanced_typeface_metrics_utils; + static const size_t FONT_CACHE_MEMORY_BUDGET = 1024 * 1024; static const char FONT_DEFAULT_NAME[] = "Lucida Sans"; @@ -400,6 +404,28 @@ static void bytes_to_bits(uint8_t dst[], const uint8_t src[], int count) { } } +#if 1 +static inline int r32_to_16(int x) { return SkR32ToR16(x); } +static inline int g32_to_16(int x) { return SkG32ToG16(x); } +static inline int b32_to_16(int x) { return SkB32ToB16(x); } +#else +static inline int round8to5(int x) { + return (x + 3 - (x >> 5) + (x >> 7)) >> 3; +} +static inline int round8to6(int x) { + int xx = (x + 1 - (x >> 6) + (x >> 7)) >> 2; + SkASSERT((unsigned)xx <= 63); + + int ix = x >> 2; + SkASSERT(SkAbs32(xx - ix) <= 1); + return xx; +} + +static inline int r32_to_16(int x) { return round8to5(x); } +static inline int g32_to_16(int x) { return round8to6(x); } +static inline int b32_to_16(int x) { return round8to5(x); } +#endif + static inline uint16_t rgb_to_lcd16(uint32_t rgb) { int r = (rgb >> 16) & 0xFF; int g = (rgb >> 8) & 0xFF; @@ -411,7 +437,7 @@ static inline uint16_t rgb_to_lcd16(uint32_t rgb) { g = 255 - g; b = 255 - b; - return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b)); + return SkPackRGB16(r32_to_16(r), g32_to_16(g), b32_to_16(b)); } #define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) @@ -604,12 +630,141 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) return SkFontHost::CreateTypeface(NULL, NULL, NULL, NULL, SkTypeface::kNormal); } +// Construct Glyph to Unicode table. +// Unicode code points that require conjugate pairs in utf16 are not +// supported. +static void populate_glyph_to_unicode(CTFontRef ctFont, + const unsigned glyphCount, SkTDArray<SkUnichar>* glyphToUnicode) { + CFCharacterSetRef charSet = CTFontCopyCharacterSet(ctFont); + CFDataRef bitmap = CFCharacterSetCreateBitmapRepresentation( + kCFAllocatorDefault, charSet); + if (!bitmap) { + return; + } + CFIndex length = CFDataGetLength(bitmap); + if (!length) { + CFSafeRelease(bitmap); + return; + } + if (length > 8192) { + // TODO: Add support for Unicode above 0xFFFF + // Consider only the BMP portion of the Unicode character points. + // The bitmap may contain other planes, up to plane 16. + // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html + length = 8192; + } + const UInt8* bits = CFDataGetBytePtr(bitmap); + glyphToUnicode->setCount(glyphCount); + SkUnichar* out = glyphToUnicode->begin(); + sk_bzero(out, glyphCount * sizeof(SkUnichar)); + for (int i = 0; i < length; i++) { + int mask = bits[i]; + if (!mask) { + continue; + } + for (int j = 0; j < 8; j++) { + CGGlyph glyph; + UniChar unichar = static_cast<UniChar>((i << 3) + j); + if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, + &unichar, &glyph, 1)) { + out[glyph] = unichar; + } + } + } + CFSafeRelease(bitmap); +} + +static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) { + CGSize advance; + advance.width = 0; + CGGlyph glyph = gId; + CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, + &advance, 1); + *data = advance.width; + return true; +} + // static SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( uint32_t fontID, SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) { - SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); - return NULL; + CTFontRef ctFont = GetFontRefFromFontID(fontID); + SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics; + CFStringRef fontName = CTFontCopyPostScriptName(ctFont); + int length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(fontName), + kCFStringEncodingUTF8); + info->fFontName.resize(length); + CFStringGetCString(fontName, info->fFontName.writable_str(), length, + kCFStringEncodingUTF8); + info->fMultiMaster = false; + CFIndex glyphCount = CTFontGetGlyphCount(ctFont); + info->fLastGlyphID = SkToU16(glyphCount - 1); + info->fEmSize = CTFontGetUnitsPerEm(ctFont); + + if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) { + populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode); + } + + // TODO: get font type, ala: + // CFTypeRef attr = CTFontCopyAttribute(ctFont, kCTFontFormatAttribute); + info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; + info->fStyle = 0; + CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont); + if (symbolicTraits & kCTFontMonoSpaceTrait) { + info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; + } + if (symbolicTraits & kCTFontItalicTrait) { + info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; + } + CTFontStylisticClass stylisticClass = symbolicTraits & + kCTFontClassMaskTrait; + if (stylisticClass & kCTFontSymbolicClass) { + info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style; + } + if (stylisticClass >= kCTFontOldStyleSerifsClass + && stylisticClass <= kCTFontSlabSerifsClass) { + info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; + } else if (stylisticClass & kCTFontScriptsClass) { + info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; + } + info->fItalicAngle = CTFontGetSlantAngle(ctFont); + info->fAscent = CTFontGetAscent(ctFont); + info->fDescent = CTFontGetDescent(ctFont); + info->fCapHeight = CTFontGetCapHeight(ctFont); + CGRect bbox = CTFontGetBoundingBox(ctFont); + info->fBBox = SkIRect::MakeXYWH(bbox.origin.x, bbox.origin.y, + bbox.size.width, bbox.size.height); + + // Figure out a good guess for StemV - Min width of i, I, !, 1. + // This probably isn't very good with an italic font. + int16_t min_width = SHRT_MAX; + info->fStemV = 0; + static const UniChar stem_chars[] = {'i', 'I', '!', '1'}; + const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]); + CGGlyph glyphs[count]; + CGRect boundingRects[count]; + if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) { + CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation, + glyphs, boundingRects, count); + for (size_t i = 0; i < count; i++) { + int16_t width = boundingRects[i].size.width; + if (width > 0 && width < min_width) { + min_width = width; + info->fStemV = min_width; + } + } + } + + if (false) { // TODO: haven't figured out how to know if font is embeddable + // (information is in the OS/2 table) + info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; + } else if (perGlyphInfo & + SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) { + info->fGlyphWidths.reset( + getAdvanceData(ctFont, glyphCount, &getWidthAdvance)); + } + + return info; } /////////////////////////////////////////////////////////////////////////////// @@ -618,9 +773,88 @@ bool SkFontHost::ValidFontID(SkFontID fontID) { return SkTypefaceCache::FindByID(fontID) != NULL; } +struct FontHeader { + SkFixed fVersion; + uint16_t fNumTables; + uint16_t fSearchRange; + uint16_t fEntrySelector; + uint16_t fRangeShift; +}; + +struct TableEntry { + uint32_t fTag; + uint32_t fCheckSum; + uint32_t fOffset; + uint32_t fLength; +}; + +static uint32 CalcTableCheckSum(uint32 *table, uint32 numberOfBytesInTable) { + uint32 sum = 0; + uint32 nLongs = (numberOfBytesInTable + 3) / 4; + + while (nLongs-- > 0) { + sum += SkEndian_SwapBE32(*table++); + } + return sum; +} + SkStream* SkFontHost::OpenStream(SkFontID uniqueID) { - SkASSERT(!"SkFontHost::OpenStream unimplemented"); - return(NULL); + // get table tags + int tableCount = CountTables(uniqueID); + SkTDArray<SkFontTableTag> tableTags; + tableTags.setCount(tableCount); + GetTableTags(uniqueID, tableTags.begin()); + + // calc total size for font, save sizes + SkTDArray<size_t> tableSizes; + size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount; + for (int index = 0; index < tableCount; ++index) { + size_t tableSize = GetTableSize(uniqueID, tableTags[index]); + totalSize += (tableSize + 3) & ~3; + *tableSizes.append() = tableSize; + } + + // reserve memory for stream, and zero it (tables must be zero padded) + SkMemoryStream* stream = new SkMemoryStream(totalSize); + char* dataStart = (char*)stream->getMemoryBase(); + sk_bzero(dataStart, totalSize); + char* dataPtr = dataStart; + + // compute font header entries + uint16_t entrySelector = 0; + uint16_t searchRange = 1; + while (searchRange < tableCount >> 1) { + entrySelector++; + searchRange <<= 1; + } + searchRange <<= 4; + uint16_t rangeShift = (tableCount << 4) - searchRange; + + // write font header (also called sfnt header, offset subtable) + FontHeader* offsetTable = (FontHeader*)dataPtr; + offsetTable->fVersion = SkEndian_SwapBE32(SK_Fixed1); + offsetTable->fNumTables = SkEndian_SwapBE16(tableCount); + offsetTable->fSearchRange = SkEndian_SwapBE16(searchRange); + offsetTable->fEntrySelector = SkEndian_SwapBE16(entrySelector); + offsetTable->fRangeShift = SkEndian_SwapBE16(rangeShift); + dataPtr += sizeof(FontHeader); + + // write tables + TableEntry* entry = (TableEntry*)dataPtr; + dataPtr += sizeof(TableEntry) * tableCount; + for (int index = 0; index < tableCount; ++index) { + size_t tableSize = tableSizes[index]; + GetTableData(uniqueID, tableTags[index], 0, tableSize, dataPtr); + entry->fTag = SkEndian_SwapBE32(tableTags[index]); + entry->fCheckSum = SkEndian_SwapBE32(CalcTableCheckSum( + (uint32*)dataPtr, tableSize)); + entry->fOffset = SkEndian_SwapBE32(dataPtr - dataStart); + entry->fLength = SkEndian_SwapBE32(tableSize); + dataPtr += (tableSize + 3) & ~3; + ++entry; + } + + return stream; } size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index d9404f3943..f5d126ea6f 100755 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -83,7 +83,8 @@ static inline FIXED SkScalarToFIXED(SkScalar x) { static unsigned calculateGlyphCount(HDC hdc) { // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes. - const DWORD maxpTag = *(DWORD*) "maxp"; + const DWORD maxpTag = + SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p')); uint16_t glyphs; if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) { return SkEndian_SwapBE16(glyphs); @@ -271,6 +272,10 @@ private: HFONT fFont; SCRIPT_CACHE fSC; int fGlyphCount; + + HFONT fHiResFont; + MAT2 fMat22Identity; + SkMatrix fHiResMatrix; }; static float mul2float(SkScalar a, SkScalar b) { @@ -283,6 +288,16 @@ static FIXED float2FIXED(float x) { static SkMutex gFTMutex; +#define HIRES_TEXTSIZE 2048 +#define HIRES_SHIFT 11 +static inline SkFixed HiResToFixed(int value) { + return value << (16 - HIRES_SHIFT); +} + +static bool needHiResMetrics(const SkScalar mat[2][2]) { + return mat[1][0] || mat[0][1]; +} + SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0) , fGlyphCount(-1) { @@ -303,6 +318,7 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) fMat22.eM22 = float2FIXED(-fXform.eM22); fDDC = ::CreateCompatibleDC(NULL); + SetGraphicsMode(fDDC, GM_ADVANCED); SetBkMode(fDDC, TRANSPARENT); // Scaling by the DPI is inconsistent with how Skia draws elsewhere @@ -311,6 +327,21 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) GetLogFontByID(fRec.fFontID, &lf); lf.lfHeight = -gCanonicalTextSize; fFont = CreateFontIndirect(&lf); + + // if we're rotated, or want fractional widths, create a hires font + fHiResFont = 0; + if (needHiResMetrics(fRec.fPost2x2) || (fRec.fFlags & kSubpixelPositioning_Flag)) { + lf.lfHeight = -HIRES_TEXTSIZE; + fHiResFont = CreateFontIndirect(&lf); + + fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1); + fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0); + + // construct a matrix to go from HIRES logical units to our device units + fRec.getSingleMatrix(&fHiResMatrix); + SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE)); + fHiResMatrix.preScale(scale, scale); + } fSavefont = (HFONT)SelectObject(fDDC, fFont); } @@ -322,6 +353,9 @@ SkScalerContext_Windows::~SkScalerContext_Windows() { if (fFont) { ::DeleteObject(fFont); } + if (fHiResFont) { + ::DeleteObject(fHiResFont); + } if (fSC) { ::ScriptFreeCache(&fSC); } @@ -368,7 +402,7 @@ void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) { SkASSERT(fDDC); GLYPHMETRICS gm; - memset(&gm, 0, sizeof(gm)); + sk_bzero(&gm, sizeof(gm)); glyph->fRsbDelta = 0; glyph->fLsbDelta = 0; @@ -399,6 +433,19 @@ void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) { glyph->fTop -= 1; glyph->fLeft -= 1; } + + if (fHiResFont) { + SelectObject(fDDC, fHiResFont); + sk_bzero(&gm, sizeof(gm)); + ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity); + if (GDI_ERROR != ret) { + SkPoint advance; + fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance); + glyph->fAdvanceX = SkScalarToFixed(advance.fX); + glyph->fAdvanceY = SkScalarToFixed(advance.fY); + } + SelectObject(fDDC, fFont); + } } else { glyph->fWidth = 0; } @@ -453,34 +500,53 @@ static inline uint16_t rgb_to_lcd16(uint32_t rgb) { return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b)); } +static int alignTo32(int n) { + return (n + 31) & ~31; +} + +struct MyBitmapInfo : public BITMAPINFO { + RGBQUAD fMoreSpaceForColors[1]; +}; + void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) { SkAutoMutexAcquire ac(gFTMutex); SkASSERT(fDDC); - if (SkMask::kLCD16_Format == fRec.fMaskFormat) { + const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; + if ((SkMask::kLCD16_Format == fRec.fMaskFormat) || isBW) { HDC dc = CreateCompatibleDC(0); void* bits = 0; - BITMAPINFO info; + int biWidth = isBW ? alignTo32(glyph.fWidth) : glyph.fWidth; + MyBitmapInfo info; sk_bzero(&info, sizeof(info)); + if (isBW) { + RGBQUAD blackQuad = { 0, 0, 0, 0 }; + RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 }; + info.bmiColors[0] = blackQuad; + info.bmiColors[1] = whiteQuad; + } info.bmiHeader.biSize = sizeof(info.bmiHeader); - info.bmiHeader.biWidth = glyph.fWidth; + info.bmiHeader.biWidth = biWidth; info.bmiHeader.biHeight = glyph.fHeight; info.bmiHeader.biPlanes = 1; - info.bmiHeader.biBitCount = 32; + info.bmiHeader.biBitCount = isBW ? 1 : 32; info.bmiHeader.biCompression = BI_RGB; + if (isBW) { + info.bmiHeader.biClrUsed = 2; + } HBITMAP bm = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &bits, 0, 0); SelectObject(dc, bm); // erase to white - size_t srcRB = glyph.fWidth << 2; + size_t srcRB = isBW ? (biWidth >> 3) : (glyph.fWidth << 2); size_t size = glyph.fHeight * srcRB; - memset(bits, 0xFF, size); + memset(bits, isBW ? 0 : 0xFF, size); + SetGraphicsMode(dc, GM_ADVANCED); SetBkMode(dc, TRANSPARENT); SetTextAlign(dc, TA_LEFT | TA_BASELINE); - SetGraphicsMode(dc, GM_ADVANCED); XFORM xform = fXform; xform.eDx = (float)-glyph.fLeft; @@ -488,7 +554,7 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) { SetWorldTransform(dc, &xform); HGDIOBJ prevFont = SelectObject(dc, fFont); - COLORREF color = SetTextColor(dc, 0); // black + COLORREF color = SetTextColor(dc, isBW ? 0xFFFFFF : 0); SkASSERT(color != CLR_INVALID); uint16_t glyphID = glyph.getGlyphID(); #if defined(UNICODE) @@ -501,15 +567,26 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) { // downsample from rgba to rgb565 int width = glyph.fWidth; size_t dstRB = glyph.rowBytes(); - const uint32_t* src = (const uint32_t*)bits; - // gdi's bitmap is upside-down, so we reverse dst walking in Y - uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB); - for (int y = 0; y < glyph.fHeight; y++) { - for (int i = 0; i < width; i++) { - dst[i] = rgb_to_lcd16(src[i]); + if (isBW) { + const uint8_t* src = (const uint8_t*)bits; + // gdi's bitmap is upside-down, so we reverse dst walking in Y + uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB); + for (int y = 0; y < glyph.fHeight; y++) { + memcpy(dst, src, dstRB); + src += srcRB; + dst -= dstRB; + } + } else { // LCD16 + const uint32_t* src = (const uint32_t*)bits; + // gdi's bitmap is upside-down, so we reverse dst walking in Y + uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB); + for (int y = 0; y < glyph.fHeight; y++) { + for (int i = 0; i < width; i++) { + dst[i] = rgb_to_lcd16(src[i]); + } + src = (const uint32_t*)((const char*)src + srcRB); + dst = (uint16_t*)((char*)dst - dstRB); } - src = (const uint32_t*)((const char*)src + srcRB); - dst = (uint16_t*)((char*)dst - dstRB); } DeleteDC(dc); @@ -790,7 +867,8 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { } SkStream* SkFontHost::OpenStream(SkFontID uniqueID) { - const DWORD kTTCTag = *(DWORD*) "ttcf"; + const DWORD kTTCTag = + SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f')); LOGFONT lf; GetLogFontByID(uniqueID, &lf); @@ -896,10 +974,6 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { if (SkMask::kLCD16_Format == rec->fMaskFormat) { return; } - // we never like BW format - if (SkMask::kBW_Format == rec->fMaskFormat) { - rec->fMaskFormat = SkMask::kA8_Format; - } if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) { rec->fMaskFormat = SkMask::kA8_Format; diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp index df861780b2..f4ad1987f9 100644 --- a/src/svg/SkSVGParser.cpp +++ b/src/svg/SkSVGParser.cpp @@ -76,8 +76,8 @@ void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) { int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue, size_t len, bool isPaint) { const SkSVGAttribute* attributes; - int count = element->getAttributes(&attributes); - int result = 0; + size_t count = element->getAttributes(&attributes); + size_t result = 0; while (result < count) { if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) { SkASSERT(result == (attributes->fOffset - @@ -200,7 +200,7 @@ bool SkSVGParser::onStartElementLen(const char name[], size_t len) { } else if (fInSVG == false) return false; const char* nextColon = strchr(name, ':'); - if (nextColon && nextColon - name < len) + if (nextColon && (size_t)(nextColon - name) < len) return false; SkSVGTypes type = GetType(name, len); // SkASSERT(type >= 0); diff --git a/src/utils/SkEGLContext_none.cpp b/src/utils/SkEGLContext_none.cpp index 1c55c95e36..cb08f400ee 100644 --- a/src/utils/SkEGLContext_none.cpp +++ b/src/utils/SkEGLContext_none.cpp @@ -1,6 +1,6 @@ #include "SkEGLContext.h" -SkEGLContext::SkEGLContext() : fContext(NULL) { +SkEGLContext::SkEGLContext() { } SkEGLContext::~SkEGLContext() { diff --git a/src/utils/mac/SkBitmap_Mac.cpp b/src/utils/mac/SkBitmap_Mac.cpp new file mode 100644 index 0000000000..06c2b2701a --- /dev/null +++ b/src/utils/mac/SkBitmap_Mac.cpp @@ -0,0 +1,142 @@ +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkMath.h" + +#if defined(SK_BUILD_FOR_MAC) && !defined(SK_USE_WXWIDGETS) + +#include <ApplicationServices/ApplicationServices.h> + +#ifndef __ppc__ + #define SWAP_16BIT +#endif + +static void convertGL32_to_Mac32(uint32_t dst[], const SkBitmap& bm) { + memcpy(dst, bm.getPixels(), bm.getSize()); + return; + + uint32_t* stop = dst + (bm.getSize() >> 2); + const uint8_t* src = (const uint8_t*)bm.getPixels(); + while (dst < stop) { + *dst++ = src[2] << 24 | src[1] << 16 | src[0] << 8 | src[3] << 0; + src += sizeof(uint32_t); + } +} + +static void convert565_to_32(uint32_t dst[], const SkBitmap& bm) { + for (int y = 0; y < bm.height(); y++) { + const uint16_t* src = bm.getAddr16(0, y); + const uint16_t* stop = src + bm.width(); + while (src < stop) { + unsigned c = *src++; + unsigned r = SkPacked16ToR32(c); + unsigned g = SkPacked16ToG32(c); + unsigned b = SkPacked16ToB32(c); + + *dst++ = (b << 24) | (g << 16) | (r << 8) | 0xFF; + } + } +} + +static void convert4444_to_555(uint16_t dst[], const uint16_t src[], int count) +{ + const uint16_t* stop = src + count; + + while (src < stop) + { + unsigned c = *src++; + + unsigned r = SkGetPackedR4444(c); + unsigned g = SkGetPackedG4444(c); + unsigned b = SkGetPackedB4444(c); + // convert to 5 bits + r = (r << 1) | (r >> 3); + g = (g << 1) | (g >> 3); + b = (b << 1) | (b >> 3); + // build the 555 + c = (r << 10) | (g << 5) | b; + +#ifdef SWAP_16BIT + c = (c >> 8) | (c << 8); +#endif + *dst++ = c; + } +} + +#include "SkTemplates.h" + +static CGImageRef bitmap2imageref(const SkBitmap& bm) { + size_t bitsPerComp; + size_t bitsPerPixel; + CGBitmapInfo info; + CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGDataProviderRef data = CGDataProviderCreateWithData(NULL, + bm.getPixels(), + bm.getSize(), + NULL); + SkAutoTCallVProc<CGDataProvider, CGDataProviderRelease> acp(data); + SkAutoTCallVProc<CGColorSpace, CGColorSpaceRelease> acp2(cs); + + switch (bm.config()) { + case SkBitmap::kARGB_8888_Config: + bitsPerComp = 8; + bitsPerPixel = 32; + info = kCGImageAlphaPremultipliedLast; + break; + case SkBitmap::kARGB_4444_Config: + bitsPerComp = 4; + bitsPerPixel = 16; + info = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder16Little; + break; +#if 0 // not supported by quartz !!! + case SkBitmap::kRGB_565_Config: + bitsPerComp = 5; + bitsPerPixel = 16; + info = kCGImageAlphaNone | kCGBitmapByteOrder16Little; + break; +#endif + default: + return NULL; + } + + return CGImageCreate(bm.width(), bm.height(), bitsPerComp, bitsPerPixel, + bm.rowBytes(), cs, info, data, + NULL, false, kCGRenderingIntentDefault); +} + +void SkBitmap::drawToPort(WindowRef wind, CGContextRef cg) const { + if (fPixels == NULL || fWidth == 0 || fHeight == 0) { + return; + } + + bool useQD = false; + if (NULL == cg) { + SetPortWindowPort(wind); + QDBeginCGContext(GetWindowPort(wind), &cg); + useQD = true; + } + + SkBitmap bm; + if (this->config() == kRGB_565_Config) { + this->copyTo(&bm, kARGB_8888_Config); + } else { + bm = *this; + } + bm.lockPixels(); + + CGImageRef image = bitmap2imageref(bm); + if (image) { + CGRect rect; + rect.origin.x = rect.origin.y = 0; + rect.size.width = bm.width(); + rect.size.height = bm.height(); + + CGContextDrawImage(cg, rect, image); + CGImageRelease(image); + } + + if (useQD) { + QDEndCGContext(GetWindowPort(wind), &cg); + } +} + +#endif diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp new file mode 100644 index 0000000000..f4bda45c9a --- /dev/null +++ b/src/utils/mac/SkCreateCGImageRef.cpp @@ -0,0 +1,129 @@ +#include "SkCGUtils.h" +#include "SkBitmap.h" +#include "SkColorPriv.h" + +static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) { + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info); + delete bitmap; +} + +#define HAS_ARGB_SHIFTS(a, r, g, b) \ + (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \ + && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b)) + +static SkBitmap* prepareForImageRef(const SkBitmap& bm, + size_t* bitsPerComponent, + CGBitmapInfo* info) { + bool upscaleTo32 = false; + + switch (bm.config()) { + case SkBitmap::kRGB_565_Config: + upscaleTo32 = true; + // fall through + case SkBitmap::kARGB_8888_Config: + *bitsPerComponent = 8; +#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \ + || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8) + *info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast; +#elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \ + || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) + // Matches the CGBitmapInfo that Apple recommends for best + // performance, used by google chrome. + *info = kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst; +#else +// ...add more formats as required... +#warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \ + This will probably not work. + // Legacy behavior. Perhaps turn this into an error at some + // point. + *info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast; +#endif + break; +#if 0 + case SkBitmap::kRGB_565_Config: + // doesn't see quite right. Are they thinking 1555? + *bitsPerComponent = 5; + *info = kCGBitmapByteOrder16Little; + break; +#endif + case SkBitmap::kARGB_4444_Config: + *bitsPerComponent = 4; + *info = kCGBitmapByteOrder16Little | kCGImageAlphaPremultipliedLast; + break; + default: + return NULL; + } + + SkBitmap* copy; + if (upscaleTo32) { + copy = new SkBitmap; + // here we make a ceep copy of the pixels, since CG won't take our + // 565 directly + bm.copyTo(copy, SkBitmap::kARGB_8888_Config); + } else { + copy = new SkBitmap(bm); + } + return copy; +} + +#undef HAS_ARGB_SHIFTS + +CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, + CGColorSpaceRef colorSpace) { + size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING; + CGBitmapInfo info SK_INIT_TO_AVOID_WARNING; + + SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info); + if (NULL == bitmap) { + return NULL; + } + + const int w = bitmap->width(); + const int h = bitmap->height(); + const size_t s = bitmap->getSize(); + + // our provider "owns" the bitmap*, and will take care of deleting it + // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release + // proc, which will in turn unlock the pixels + bitmap->lockPixels(); + CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s, + SkBitmap_ReleaseInfo); + + bool releaseColorSpace = false; + if (NULL == colorSpace) { + colorSpace = CGColorSpaceCreateDeviceRGB(); + releaseColorSpace = true; + } + + CGImageRef ref = CGImageCreate(w, h, bitsPerComponent, + bitmap->bytesPerPixel() * 8, + bitmap->rowBytes(), colorSpace, info, dataRef, + NULL, false, kCGRenderingIntentDefault); + + if (releaseColorSpace) { + CGColorSpaceRelease(colorSpace); + } + CGDataProviderRelease(dataRef); + return ref; +} + +void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { + CGImageRef img = SkCreateCGImageRef(bm); + + if (img) { + CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); + + CGContextSaveGState(cg); + CGContextTranslateCTM(cg, x, r.size.height + y); + CGContextScaleCTM(cg, 1, -1); + + CGContextDrawImage(cg, r, img); + + CGContextRestoreGState(cg); + + CGImageRelease(img); + } +} + + + diff --git a/src/utils/mac/SkEGLContext_mac.cpp b/src/utils/mac/SkEGLContext_mac.cpp new file mode 100644 index 0000000000..e601f35617 --- /dev/null +++ b/src/utils/mac/SkEGLContext_mac.cpp @@ -0,0 +1,68 @@ +#include "SkEGLContext.h" +//#include "SkTypes.h" +#include <AGL/agl.h> + +SkEGLContext::SkEGLContext() : context(NULL) { +} + +SkEGLContext::~SkEGLContext() { + if (this->context) { + aglDestroyContext(this->context); + } +} + +bool SkEGLContext::init(int width, int height) { + GLint major, minor; + AGLContext ctx; + + aglGetVersion(&major, &minor); + //SkDebugf("---- agl version %d %d\n", major, minor); + + const GLint pixelAttrs[] = { + AGL_RGBA, + AGL_STENCIL_SIZE, 8, +/* + AGL_SAMPLE_BUFFERS_ARB, 1, + AGL_MULTISAMPLE, + AGL_SAMPLES_ARB, 2, +*/ + AGL_ACCELERATED, + AGL_NONE + }; + AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs); + //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs); + //SkDebugf("----- agl format %p\n", format); + ctx = aglCreateContext(format, NULL); + //SkDebugf("----- agl context %p\n", ctx); + aglDestroyPixelFormat(format); + +/* + static const GLint interval = 1; + aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval); +*/ + + aglSetCurrentContext(ctx); + this->context = ctx; + + // Now create our FBO render target + + GLuint fboID; + GLuint cbID; + GLuint dsID; + glGenFramebuffersEXT(1, &fboID); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); + glGenRenderbuffers(1, &cbID); + glBindRenderbuffer(GL_RENDERBUFFER, cbID); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID); + glGenRenderbuffers(1, &dsID); + glBindRenderbuffer(GL_RENDERBUFFER, dsID); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID); + glViewport(0, 0, width, height); + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + return GL_FRAMEBUFFER_COMPLETE == status; +} diff --git a/src/utils/mac/SkOSWindow_Mac.cpp b/src/utils/mac/SkOSWindow_Mac.cpp new file mode 100644 index 0000000000..f1a2926560 --- /dev/null +++ b/src/utils/mac/SkOSWindow_Mac.cpp @@ -0,0 +1,534 @@ +#include "SkTypes.h" + +#if defined(SK_BUILD_FOR_MAC) && !defined(SK_USE_WXWIDGETS) + +#include <AGL/agl.h> + +#include <Carbon/Carbon.h> +#include "SkCGUtils.h" + +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkOSMenu.h" +#include "SkTime.h" + +#include "SkGraphics.h" +#include <new.h> + +static void (*gPrevNewHandler)(); + +extern "C" { + static void sk_new_handler() + { + if (SkGraphics::SetFontCacheUsed(0)) + return; + if (gPrevNewHandler) + gPrevNewHandler(); + else + sk_throw(); + } +} + +static SkOSWindow* gCurrOSWin; +static EventTargetRef gEventTarget; +static EventQueueRef gCurrEventQ; + +static OSStatus MyDrawEventHandler(EventHandlerCallRef myHandler, + EventRef event, void *userData) { + // NOTE: GState is save/restored by the HIView system doing the callback, + // so the draw handler doesn't need to do it + + OSStatus status = noErr; + CGContextRef context; + HIRect bounds; + + // Get the CGContextRef + status = GetEventParameter (event, kEventParamCGContextRef, + typeCGContextRef, NULL, + sizeof (CGContextRef), + NULL, + &context); + + if (status != noErr) { + SkDebugf("Got error %d getting the context!\n", status); + return status; + } + + // Get the bounding rectangle + HIViewGetBounds ((HIViewRef) userData, &bounds); + + gCurrOSWin->doPaint(context); + return status; +} + +#define SK_MacEventClass FOUR_CHAR_CODE('SKec') +#define SK_MacEventKind FOUR_CHAR_CODE('SKek') +#define SK_MacEventParamName FOUR_CHAR_CODE('SKev') +#define SK_MacEventSinkIDParamName FOUR_CHAR_CODE('SKes') + +static void set_bindingside(HISideBinding* side, HIViewRef parent, HIBindingKind kind) { + side->toView = parent; + side->kind = kind; + side->offset = 0; +} + +static void set_axisscale(HIAxisScale* axis, HIViewRef parent) { + axis->toView = parent; + axis->kind = kHILayoutScaleAbsolute; + axis->ratio = 1; +} + +static void set_axisposition(HIAxisPosition* pos, HIViewRef parent, HIPositionKind kind) { + pos->toView = parent; + pos->kind = kind; + pos->offset = 0; +} + +SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd), fAGLCtx(NULL) +{ + OSStatus result; + WindowRef wr = (WindowRef)hWnd; + + HIViewRef imageView, parent; + HIViewRef rootView = HIViewGetRoot(wr); + HIViewFindByID(rootView, kHIViewWindowContentID, &parent); + result = HIImageViewCreate(NULL, &imageView); + SkASSERT(result == noErr); + + result = HIViewAddSubview(parent, imageView); + SkASSERT(result == noErr); + + fHVIEW = imageView; + + HIViewSetVisible(imageView, true); + HIViewPlaceInSuperviewAt(imageView, 0, 0); + + if (true) { + HILayoutInfo layout; + layout.version = kHILayoutInfoVersionZero; + set_bindingside(&layout.binding.left, parent, kHILayoutBindLeft); + set_bindingside(&layout.binding.top, parent, kHILayoutBindTop); + set_bindingside(&layout.binding.right, parent, kHILayoutBindRight); + set_bindingside(&layout.binding.bottom, parent, kHILayoutBindBottom); + set_axisscale(&layout.scale.x, parent); + set_axisscale(&layout.scale.y, parent); + set_axisposition(&layout.position.x, parent, kHILayoutPositionLeft); + set_axisposition(&layout.position.y, rootView, kHILayoutPositionTop); + HIViewSetLayoutInfo(imageView, &layout); + } + + HIImageViewSetOpaque(imageView, true); + HIImageViewSetScaleToFit(imageView, false); + + static const EventTypeSpec gTypes[] = { + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, + { kEventClassWindow, kEventWindowBoundsChanged }, +// { kEventClassWindow, kEventWindowDrawContent }, + { SK_MacEventClass, SK_MacEventKind } + }; + + EventHandlerUPP handlerUPP = NewEventHandlerUPP(SkOSWindow::EventHandler); + int count = SK_ARRAY_COUNT(gTypes); + + result = InstallEventHandler(GetWindowEventTarget(wr), handlerUPP, + count, gTypes, this, nil); + SkASSERT(result == noErr); + + gCurrOSWin = this; + gCurrEventQ = GetCurrentEventQueue(); + gEventTarget = GetWindowEventTarget(wr); + + static bool gOnce = true; + if (gOnce) { + gOnce = false; + gPrevNewHandler = set_new_handler(sk_new_handler); + } +} + +void SkOSWindow::doPaint(void* ctx) +{ +#if 0 + this->update(NULL); + + const SkBitmap& bm = this->getBitmap(); + CGImageRef img = SkCreateCGImageRef(bm); + + if (img) { + CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); + + CGContextRef cg = reinterpret_cast<CGContextRef>(ctx); + + CGContextSaveGState(cg); + CGContextTranslateCTM(cg, 0, r.size.height); + CGContextScaleCTM(cg, 1, -1); + + CGContextDrawImage(cg, r, img); + + CGContextRestoreGState(cg); + + CGImageRelease(img); + } +#endif +} + +void SkOSWindow::updateSize() +{ + Rect r; + + GetWindowBounds((WindowRef)fHWND, kWindowContentRgn, &r); + this->resize(r.right - r.left, r.bottom - r.top); + +#if 0 + HIRect frame; + HIViewRef imageView = (HIViewRef)getHVIEW(); + HIViewRef parent = HIViewGetSuperview(imageView); + + HIViewGetBounds(imageView, &frame); + SkDebugf("------ %d bounds %g %g %g %g\n", r.right - r.left, + frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); +#endif +} + +void SkOSWindow::onHandleInval(const SkIRect& r) +{ + SkEvent* evt = new SkEvent("inval-imageview"); + evt->post(this->getSinkID()); +} + +bool SkOSWindow::onEvent(const SkEvent& evt) { + if (evt.isType("inval-imageview")) { + this->update(NULL); + + const SkBitmap& bm = this->getBitmap(); + + CGImageRef img = SkCreateCGImageRef(bm); + HIImageViewSetImage((HIViewRef)getHVIEW(), img); + CGImageRelease(img); + return true; + } + return INHERITED::onEvent(evt); +} + +void SkOSWindow::onSetTitle(const char title[]) +{ + CFStringRef str = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); + SetWindowTitleWithCFString((WindowRef)fHWND, str); + CFRelease(str); +} + +void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) +{ +} + +static void getparam(EventRef inEvent, OSType name, OSType type, UInt32 size, void* data) +{ + EventParamType actualType; + UInt32 actualSize; + OSStatus status; + + status = GetEventParameter(inEvent, name, type, &actualType, size, &actualSize, data); + SkASSERT(status == noErr); + SkASSERT(actualType == type); + SkASSERT(actualSize == size); +} + +enum { + SK_MacReturnKey = 36, + SK_MacDeleteKey = 51, + SK_MacEndKey = 119, + SK_MacLeftKey = 123, + SK_MacRightKey = 124, + SK_MacDownKey = 125, + SK_MacUpKey = 126, + + SK_Mac0Key = 0x52, + SK_Mac1Key = 0x53, + SK_Mac2Key = 0x54, + SK_Mac3Key = 0x55, + SK_Mac4Key = 0x56, + SK_Mac5Key = 0x57, + SK_Mac6Key = 0x58, + SK_Mac7Key = 0x59, + SK_Mac8Key = 0x5b, + SK_Mac9Key = 0x5c +}; + +static SkKey raw2key(UInt32 raw) +{ + static const struct { + UInt32 fRaw; + SkKey fKey; + } gKeys[] = { + { SK_MacUpKey, kUp_SkKey }, + { SK_MacDownKey, kDown_SkKey }, + { SK_MacLeftKey, kLeft_SkKey }, + { SK_MacRightKey, kRight_SkKey }, + { SK_MacReturnKey, kOK_SkKey }, + { SK_MacDeleteKey, kBack_SkKey }, + { SK_MacEndKey, kEnd_SkKey }, + { SK_Mac0Key, k0_SkKey }, + { SK_Mac1Key, k1_SkKey }, + { SK_Mac2Key, k2_SkKey }, + { SK_Mac3Key, k3_SkKey }, + { SK_Mac4Key, k4_SkKey }, + { SK_Mac5Key, k5_SkKey }, + { SK_Mac6Key, k6_SkKey }, + { SK_Mac7Key, k7_SkKey }, + { SK_Mac8Key, k8_SkKey }, + { SK_Mac9Key, k9_SkKey } + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) + if (gKeys[i].fRaw == raw) + return gKeys[i].fKey; + return kNONE_SkKey; +} + +static void post_skmacevent() +{ + EventRef ref; + OSStatus status = CreateEvent(nil, SK_MacEventClass, SK_MacEventKind, 0, 0, &ref); + SkASSERT(status == noErr); + +#if 0 + status = SetEventParameter(ref, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt); + SkASSERT(status == noErr); + status = SetEventParameter(ref, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID); + SkASSERT(status == noErr); +#endif + + EventTargetRef target = gEventTarget; + SetEventParameter(ref, kEventParamPostTarget, typeEventTargetRef, sizeof(target), &target); + SkASSERT(status == noErr); + + status = PostEventToQueue(gCurrEventQ, ref, kEventPriorityStandard); + SkASSERT(status == noErr); + + ReleaseEvent(ref); +} + +pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ) +{ + SkOSWindow* win = (SkOSWindow*)userData; + OSStatus result = eventNotHandledErr; + UInt32 wClass = GetEventClass(inEvent); + UInt32 wKind = GetEventKind(inEvent); + + gCurrOSWin = win; // will need to be in TLS. Set this so PostEvent will work + + switch (wClass) { + case kEventClassMouse: { + Point pt; + getparam(inEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pt), &pt); + SetPortWindowPort((WindowRef)win->getHWND()); + GlobalToLocal(&pt); + + switch (wKind) { + case kEventMouseDown: + if (win->handleClick(pt.h, pt.v, Click::kDown_State)) { + result = noErr; + } + break; + case kEventMouseMoved: + // fall through + case kEventMouseDragged: + (void)win->handleClick(pt.h, pt.v, Click::kMoved_State); + // result = noErr; + break; + case kEventMouseUp: + (void)win->handleClick(pt.h, pt.v, Click::kUp_State); + // result = noErr; + break; + default: + break; + } + break; + } + case kEventClassKeyboard: + if (wKind == kEventRawKeyDown) { + UInt32 raw; + getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw); + SkKey key = raw2key(raw); + if (key != kNONE_SkKey) + (void)win->handleKey(key); + } else if (wKind == kEventRawKeyUp) { + UInt32 raw; + getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw); + SkKey key = raw2key(raw); + if (key != kNONE_SkKey) + (void)win->handleKeyUp(key); + } + break; + case kEventClassTextInput: + if (wKind == kEventTextInputUnicodeForKeyEvent) { + UInt16 uni; + getparam(inEvent, kEventParamTextInputSendText, typeUnicodeText, sizeof(uni), &uni); + win->handleChar(uni); + } + break; + case kEventClassWindow: + switch (wKind) { + case kEventWindowBoundsChanged: + win->updateSize(); + break; + case kEventWindowDrawContent: { + CGContextRef cg; + result = GetEventParameter(inEvent, + kEventParamCGContextRef, + typeCGContextRef, + NULL, + sizeof (CGContextRef), + NULL, + &cg); + if (result != 0) { + cg = NULL; + } + win->doPaint(cg); + break; + } + default: + break; + } + break; + case SK_MacEventClass: { + SkASSERT(wKind == SK_MacEventKind); + if (SkEvent::ProcessEvent()) { + post_skmacevent(); + } + #if 0 + SkEvent* evt; + SkEventSinkID sinkID; + getparam(inEvent, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt); + getparam(inEvent, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID); + #endif + result = noErr; + break; + } + default: + break; + } + if (result == eventNotHandledErr) { + result = CallNextEventHandler(inHandler, inEvent); + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +void SkEvent::SignalNonEmptyQueue() +{ + post_skmacevent(); +// SkDebugf("signal nonempty\n"); +} + +static TMTask gTMTaskRec; +static TMTask* gTMTaskPtr; + +static void sk_timer_proc(TMTask* rec) +{ + SkEvent::ServiceQueueTimer(); +// SkDebugf("timer task fired\n"); +} + +void SkEvent::SignalQueueTimer(SkMSec delay) +{ + if (gTMTaskPtr) + { + RemoveTimeTask((QElem*)gTMTaskPtr); + DisposeTimerUPP(gTMTaskPtr->tmAddr); + gTMTaskPtr = nil; + } + if (delay) + { + gTMTaskPtr = &gTMTaskRec; + memset(gTMTaskPtr, 0, sizeof(gTMTaskRec)); + gTMTaskPtr->tmAddr = NewTimerUPP(sk_timer_proc); + OSErr err = InstallTimeTask((QElem*)gTMTaskPtr); +// SkDebugf("installtimetask of %d returned %d\n", delay, err); + PrimeTimeTask((QElem*)gTMTaskPtr, delay); + } +} + +#define USE_MSAA 0 + +AGLContext create_gl(WindowRef wref) +{ + GLint major, minor; + AGLContext ctx; + + aglGetVersion(&major, &minor); + SkDebugf("---- agl version %d %d\n", major, minor); + + const GLint pixelAttrs[] = { + AGL_RGBA, + AGL_STENCIL_SIZE, 8, +#if USE_MSAA + AGL_SAMPLE_BUFFERS_ARB, 1, + AGL_MULTISAMPLE, + AGL_SAMPLES_ARB, 8, +#endif + AGL_ACCELERATED, + AGL_DOUBLEBUFFER, + AGL_NONE + }; + AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs); + //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs); + SkDebugf("----- agl format %p\n", format); + ctx = aglCreateContext(format, NULL); + SkDebugf("----- agl context %p\n", ctx); + aglDestroyPixelFormat(format); + + static const GLint interval = 1; + aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval); + aglSetCurrentContext(ctx); + return ctx; +} + +bool SkOSWindow::attachGL() +{ + if (NULL == fAGLCtx) { + fAGLCtx = create_gl((WindowRef)fHWND); + if (NULL == fAGLCtx) { + return false; + } + } + + GLboolean success = true; + + int width, height; + + success = aglSetWindowRef((AGLContext)fAGLCtx, (WindowRef)fHWND); + width = this->width(); + height = this->height(); + + GLenum err = aglGetError(); + if (err) { + SkDebugf("---- aglSetWindowRef %d %d %s [%d %d]\n", success, err, + aglErrorString(err), width, height); + } + + if (success) { + glViewport(0, 0, width, height); + glClearColor(0, 0, 0, 0); + glClearStencil(0); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } + return success; +} + +void SkOSWindow::detachGL() { + aglSetWindowRef((AGLContext)fAGLCtx, NULL); +} + +void SkOSWindow::presentGL() { + aglSwapBuffers((AGLContext)fAGLCtx); +} + +#endif + diff --git a/src/utils/mac/skia_mac.cpp b/src/utils/mac/skia_mac.cpp new file mode 100644 index 0000000000..a1345cf62c --- /dev/null +++ b/src/utils/mac/skia_mac.cpp @@ -0,0 +1,43 @@ +#include <Carbon/Carbon.h>
+#include "SkApplication.h"
+#include "SkWindow.h"
+
+int main(int argc, char* argv[])
+{
+ WindowRef window;
+ OSStatus err = noErr;
+
+ Rect bounds = {100, 100, 500, 500};
+ WindowAttributes attrs = kWindowStandardHandlerAttribute |
+ kWindowLiveResizeAttribute |
+ kWindowInWindowMenuAttribute |
+ kWindowCompositingAttribute |
+ kWindowAsyncDragAttribute |
+ kWindowFullZoomAttribute |
+ kWindowFrameworkScaledAttribute;
+ //kWindowDoesNotCycleAttribute;
+ CreateNewWindow(kDocumentWindowClass, attrs, &bounds, &window);
+
+ MenuRef menu;
+ CreateNewMenu(0, 0, &menu);
+
+ // if we get here, we can start our normal Skia sequence
+ {
+ application_init();
+ (void)create_sk_window(window);
+ SizeWindow(window, 640, 480, false);
+ }
+
+ // The window was created hidden so show it.
+ ShowWindow( window );
+
+ // Call the event loop
+ RunApplicationEventLoop();
+
+ application_term();
+
+CantCreateWindow:
+CantGetNibRef:
+ return err;
+}
+
diff --git a/src/utils/mesa/SkEGLContext_Mesa.cpp b/src/utils/mesa/SkEGLContext_Mesa.cpp new file mode 100644 index 0000000000..ed1b7cd451 --- /dev/null +++ b/src/utils/mesa/SkEGLContext_Mesa.cpp @@ -0,0 +1,128 @@ +#include "SkEGLContext.h" +#include "SkTypes.h" + +#include "GL/osmesa.h" +#include "GL/glu.h" + +#define SK_GL_DECL_PROC(T, F) T F ## _func = NULL; +#define SK_GL_GET_PROC(T, F) F ## _func = (T)OSMesaGetProcAddress(#F); +#define SK_GL_GET_EXT_PROC(T, F) F ## _func = (T)OSMesaGetProcAddress(#F "EXT"); + +SkEGLContext::SkEGLContext() : context(NULL), image(NULL) { +} + +SkEGLContext::~SkEGLContext() { + if (this->image) + free(this->image); + + if (this->context) + OSMesaDestroyContext(this->context); +} + +#if SK_B32_SHIFT < SK_G32_SHIFT &&\ + SK_G32_SHIFT < SK_R32_SHIFT &&\ + SK_R32_SHIFT < SK_A32_SHIFT + #define SK_OSMESA_COLOR_ORDER OSMESA_BGRA +#elif SK_R32_SHIFT < SK_G32_SHIFT &&\ + SK_G32_SHIFT < SK_B32_SHIFT &&\ + SK_B32_SHIFT < SK_A32_SHIFT + #define SK_OSMESA_COLOR_ORDER OSMESA_RGBA +#elif SK_A32_SHIFT < SK_R32_SHIFT && \ + SK_R32_SHIFT < SK_G32_SHIFT && \ + SK_G32_SHIFT < SK_B32_SHIFT + #define SK_OSMESA_COLOR_ORDER OSMESA_ARGB +#else + //Color order (rgba) SK_R32_SHIFT SK_G32_SHIFT SK_B32_SHIFT SK_A32_SHIFT + #define SK_OSMESA_COLOR_ORDER OSMESA_RGBA +#endif + +bool SkEGLContext::init(const int width, const int height) { + /* Create an RGBA-mode context */ +#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 + /* specify Z, stencil, accum sizes */ + OSMesaContext ctx = OSMesaCreateContextExt(SK_OSMESA_COLOR_ORDER, 16, 0, 0, NULL); +#else + OSMesaContext ctx = OSMesaCreateContext(SK_OSMESA_COLOR_ORDER, NULL); +#endif + if (!ctx) { + SkDebugf("OSMesaCreateContext failed!\n"); + return false; + } + this->context = ctx; + + // Allocate the image buffer + GLfloat *buffer = (GLfloat *) malloc(width * height * 4 * sizeof(GLfloat)); + if (!buffer) { + SkDebugf("Alloc image buffer failed!\n"); + return false; + } + this->image = buffer; + + // Bind the buffer to the context and make it current + if (!OSMesaMakeCurrent(ctx, buffer, GL_FLOAT, width, height)) { + SkDebugf("OSMesaMakeCurrent failed!\n"); + return false; + } + + //Setup the framebuffers + SK_GL_DECL_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers) + SK_GL_DECL_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer) + SK_GL_DECL_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) + SK_GL_DECL_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) + SK_GL_DECL_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) + SK_GL_DECL_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) + SK_GL_DECL_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus) + + const GLubyte* glExts = glGetString(GL_EXTENSIONS); + if (gluCheckExtension( + reinterpret_cast<const GLubyte*>("GL_ARB_framebuffer_object") + , glExts)) + { + SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers) + SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer) + SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) + SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) + SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) + SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) + SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus) + + //osmesa on mac currently only supports EXT + } else if (gluCheckExtension( + reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object") + , glExts)) + { + SK_GL_GET_EXT_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers) + SK_GL_GET_EXT_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer) + SK_GL_GET_EXT_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers) + SK_GL_GET_EXT_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer) + SK_GL_GET_EXT_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage) + SK_GL_GET_EXT_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer) + SK_GL_GET_EXT_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus) + } else { + SkDebugf("GL_ARB_framebuffer_object not found.\n"); + return false; + } + + GLuint fboID; + GLuint cbID; + GLuint dsID; + glGenFramebuffers_func(1, &fboID); + glBindFramebuffer_func(GL_FRAMEBUFFER, fboID); + + glGenRenderbuffers_func(1, &cbID); + glBindRenderbuffer_func(GL_RENDERBUFFER, cbID); + glRenderbufferStorage_func(GL_RENDERBUFFER, OSMESA_RGBA, width, height); + glFramebufferRenderbuffer_func(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID); + + glGenRenderbuffers_func(1, &dsID); + glBindRenderbuffer_func(GL_RENDERBUFFER_EXT, dsID); + glRenderbufferStorage_func(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); + glFramebufferRenderbuffer_func(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID); + + glViewport(0, 0, width, height); + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + GLenum status = glCheckFramebufferStatus_func(GL_FRAMEBUFFER); + return GL_FRAMEBUFFER_COMPLETE == status; +} diff --git a/src/utils/unix/SkEGLContext_Unix.cpp b/src/utils/unix/SkEGLContext_Unix.cpp new file mode 100644 index 0000000000..7921b8a542 --- /dev/null +++ b/src/utils/unix/SkEGLContext_Unix.cpp @@ -0,0 +1,264 @@ +#include "SkEGLContext.h" +#include "SkTypes.h" + +#include <GL/gl.h> +#include <GL/glext.h> +#include <GL/glu.h> +#include <GL/glx.h> +#include <X11/Xlib.h> + +#define SK_GL_GET_PROC(T, F) T F = NULL; \ + F = (T) glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(#F)); + +static bool ctxErrorOccurred = false; +static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { + ctxErrorOccurred = true; + return 0; +} + +SkEGLContext::SkEGLContext() : context(NULL), display(NULL), pixmap(0), glxPixmap(0) { +} + +SkEGLContext::~SkEGLContext() { + if (this->display) { + glXMakeCurrent(this->display, 0, 0); + + if (this->context) + glXDestroyContext(this->display, this->context); + + if (this->glxPixmap) + glXDestroyGLXPixmap(this->display, this->glxPixmap); + + if (this->pixmap) + XFreePixmap(this->display, this->pixmap); + + XCloseDisplay(this->display); + } +} + +bool SkEGLContext::init(const int width, const int height) { + Display *display = XOpenDisplay(0); + this->display = display; + + if (!display) { + SkDebugf("Failed to open X display.\n"); + return false; + } + + // Get a matching FB config + static int visual_attribs[] = { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_PIXMAP_BIT, + GLX_RENDER_TYPE , GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, + GLX_RED_SIZE , 8, + GLX_GREEN_SIZE , 8, + GLX_BLUE_SIZE , 8, + GLX_ALPHA_SIZE , 8, + GLX_DEPTH_SIZE , 24, + GLX_STENCIL_SIZE , 8, + GLX_DOUBLEBUFFER , True, + //GLX_SAMPLE_BUFFERS , 1, + //GLX_SAMPLES , 4, + None + }; + + int glx_major, glx_minor; + + // FBConfigs were added in GLX version 1.3. + if (!glXQueryVersion( display, &glx_major, &glx_minor) || + ( (glx_major == 1) && (glx_minor < 3) ) || (glx_major < 1)) + { + SkDebugf("Invalid GLX version."); + return false; + } + + //SkDebugf("Getting matching framebuffer configs.\n"); + int fbcount; + GLXFBConfig *fbc = glXChooseFBConfig(display, DefaultScreen(display), + visual_attribs, &fbcount); + if (!fbc) { + SkDebugf("Failed to retrieve a framebuffer config.\n"); + return false; + } + //SkDebugf("Found %d matching FB configs.\n", fbcount); + + // Pick the FB config/visual with the most samples per pixel + //SkDebugf("Getting XVisualInfos.\n"); + int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; + + int i; + for (i = 0; i < fbcount; ++i) { + XVisualInfo *vi = glXGetVisualFromFBConfig(display, fbc[i]); + if (vi) { + int samp_buf, samples; + glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLES, &samples); + + //SkDebugf(" Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d," + // " SAMPLES = %d\n", + // i, (unsigned int)vi->visualid, samp_buf, samples); + + if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) + best_fbc = i, best_num_samp = samples; + if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp) + worst_fbc = i, worst_num_samp = samples; + } + XFree(vi); + } + + GLXFBConfig bestFbc = fbc[best_fbc]; + + // Be sure to free the FBConfig list allocated by glXChooseFBConfig() + XFree(fbc); + + // Get a visual + XVisualInfo *vi = glXGetVisualFromFBConfig(display, bestFbc); + //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid); + + Pixmap pixmap = XCreatePixmap( + display, RootWindow(display, vi->screen), width, height, vi->depth + ); + + this->pixmap = pixmap; + if (!pixmap) { + SkDebugf("Failed to create pixmap.\n"); + return false; + } + + GLXPixmap glxPixmap = glXCreateGLXPixmap(display, vi, pixmap); + this->glxPixmap = glxPixmap; + + // Done with the visual info data + XFree(vi); + + // Create the context + GLXContext ctx = 0; + + // Install an X error handler so the application won't exit if GL 3.0 + // context allocation fails. + // + // Note this error handler is global. + // All display connections in all threads of a process use the same + // error handler, so be sure to guard against other threads issuing + // X commands while this code is running. + ctxErrorOccurred = false; + int (*oldHandler)(Display*, XErrorEvent*) = + XSetErrorHandler(&ctxErrorHandler); + + // Get the default screen's GLX extension list + const char *glxExts = glXQueryExtensionsString( + display, DefaultScreen(display) + ); + // Check for the GLX_ARB_create_context extension string and the function. + // If either is not present, use GLX 1.3 context creation method. + if (!gluCheckExtension( + reinterpret_cast<const GLubyte*>("GLX_ARB_create_context") + , reinterpret_cast<const GLubyte*>(glxExts))) + { + //SkDebugf("GLX_ARB_create_context not found." + // " Using old-style GLX context.\n"); + ctx = glXCreateNewContext(display, bestFbc, GLX_RGBA_TYPE, 0, True); + + } else { + //SkDebugf("Creating context.\n"); + + SK_GL_GET_PROC(PFNGLXCREATECONTEXTATTRIBSARBPROC, glXCreateContextAttribsARB) + int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + //GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + None + }; + ctx = glXCreateContextAttribsARB( + display, bestFbc, 0, True, context_attribs + ); + + // Sync to ensure any errors generated are processed. + XSync(display, False); + if (!ctxErrorOccurred && ctx) { + //SkDebugf( "Created GL 3.0 context.\n" ); + } else { + // Couldn't create GL 3.0 context. + // Fall back to old-style 2.x context. + // When a context version below 3.0 is requested, + // implementations will return the newest context version compatible + // with OpenGL versions less than version 3.0. + + // GLX_CONTEXT_MAJOR_VERSION_ARB = 1 + context_attribs[1] = 1; + // GLX_CONTEXT_MINOR_VERSION_ARB = 0 + context_attribs[3] = 0; + + ctxErrorOccurred = false; + + //SkDebugf("Failed to create GL 3.0 context." + // " Using old-style GLX context.\n"); + ctx = glXCreateContextAttribsARB( + display, bestFbc, 0, True, context_attribs + ); + } + } + + // Sync to ensure any errors generated are processed. + XSync(display, False); + + // Restore the original error handler + XSetErrorHandler(oldHandler); + + if (ctxErrorOccurred || !ctx) { + SkDebugf("Failed to create an OpenGL context.\n"); + return false; + } + this->context = ctx; + + // Verify that context is a direct context + if (!glXIsDirect(display, ctx)) { + //SkDebugf("Indirect GLX rendering context obtained.\n"); + } else { + //SkDebugf("Direct GLX rendering context obtained.\n"); + } + + //SkDebugf("Making context current.\n"); + if (!glXMakeCurrent(display, glxPixmap, ctx)) { + SkDebugf("Could not set the context.\n"); + return false; + } + + //Setup the framebuffers + const GLubyte* glExts = glGetString(GL_EXTENSIONS); + if (!gluCheckExtension( + reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object") + , glExts)) + { + SkDebugf("GL_EXT_framebuffer_object not found.\n"); + return false; + } + SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT) + SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT) + SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffersEXT) + SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbufferEXT) + SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorageEXT) + SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbufferEXT) + SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT) + + GLuint fboID; + GLuint cbID; + GLuint dsID; + glGenFramebuffersEXT(1, &fboID); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); + glGenRenderbuffersEXT(1, &cbID); + glBindRenderbufferEXT(GL_RENDERBUFFER, cbID); + glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, width, height); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID); + glGenRenderbuffersEXT(1, &dsID); + glBindRenderbufferEXT(GL_RENDERBUFFER, dsID); + glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID); + glViewport(0, 0, width, height); + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); + return GL_FRAMEBUFFER_COMPLETE == status; +} diff --git a/src/utils/unix/SkOSWindow_Unix.cpp b/src/utils/unix/SkOSWindow_Unix.cpp index 4ec0c74306..ae881d5148 100644 --- a/src/utils/unix/SkOSWindow_Unix.cpp +++ b/src/utils/unix/SkOSWindow_Unix.cpp @@ -76,7 +76,7 @@ void SkOSWindow::post_linuxevent() long event_mask = NoEventMask; XClientMessageEvent event; event.type = ClientMessage; - Atom myAtom; + Atom myAtom(0); event.message_type = myAtom; event.format = 32; event.data.l[0] = 0; diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp new file mode 100644 index 0000000000..07da123284 --- /dev/null +++ b/src/views/SkBGViewArtist.cpp @@ -0,0 +1,24 @@ +#include "SkBGViewArtist.h" +#include "SkCanvas.h" +#include "SkParsePaint.h" + +SkBGViewArtist::SkBGViewArtist(SkColor c) +{ + fPaint.setColor(c); +} + +SkBGViewArtist::~SkBGViewArtist() +{ +} + +void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas) +{ + // only works for views that are clipped their bounds. + canvas->drawPaint(fPaint); +} + +void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkPaint_Inflate(&fPaint, dom, node); +} + diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp new file mode 100644 index 0000000000..74a24775f4 --- /dev/null +++ b/src/views/SkBorderView.cpp @@ -0,0 +1,89 @@ +#include "SkBorderView.h" +#include "SkAnimator.h" +#include "SkWidgetViews.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" +#include "SkStackViewLayout.h" + +SkBorderView::SkBorderView() : fLeft(SkIntToScalar(0)), + fRight(SkIntToScalar(0)), + fTop(SkIntToScalar(0)), + fBottom(SkIntToScalar(0)) +{ + fAnim.setHostEventSink(this); + init_skin_anim(kBorder_SkinEnum, &fAnim); +} + +SkBorderView::~SkBorderView() +{ + +} + +void SkBorderView::setSkin(const char skin[]) +{ + init_skin_anim(skin, &fAnim); +} + +/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); +} + +/*virtual*/ void SkBorderView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); +} + +/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas) +{ + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(NULL); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } +} + +/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + this->inval(NULL); + return true; + } + if (evt.isType("recommendDim")) + { + evt.findScalar("leftMargin", &fLeft); + evt.findScalar("rightMargin", &fRight); + evt.findScalar("topMargin", &fTop); + evt.findScalar("bottomMargin", &fBottom); + + //setup_views.cpp uses SkView::Layout instead of SkStackViewLayout + //but that gives me an error + SkStackViewLayout* layout; + fMargin.set(fLeft, fTop, fRight, fBottom); + if (this->getLayout()) + { + layout = (SkStackViewLayout*)this->getLayout(); + layout->setMargin(fMargin); + } + else + { + layout = new SkStackViewLayout; + layout->setMargin(fMargin); + this->setLayout(layout)->unref(); + } + this->invokeLayout(); + } + return this->INHERITED::onEvent(evt); +} diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp new file mode 100644 index 0000000000..ec4a7b47b7 --- /dev/null +++ b/src/views/SkEvent.cpp @@ -0,0 +1,580 @@ +/* libs/graphics/views/SkEvent.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEvent.h" + +void SkEvent::initialize(const char* type, size_t typeLen) { + fType = NULL; + setType(type, typeLen); + f32 = 0; +#ifdef SK_DEBUG + fTargetID = 0; + fTime = 0; + fNextEvent = NULL; +#endif + SkDEBUGCODE(fDebugTrace = false;) +} + +SkEvent::SkEvent() +{ + initialize("", 0); +} + +SkEvent::SkEvent(const SkEvent& src) +{ + *this = src; + if (((size_t) fType & 1) == 0) + setType(src.fType); +} + +SkEvent::SkEvent(const SkString& type) +{ + initialize(type.c_str(), type.size()); +} + +SkEvent::SkEvent(const char type[]) +{ + SkASSERT(type); + initialize(type, strlen(type)); +} + +SkEvent::~SkEvent() +{ + if (((size_t) fType & 1) == 0) + sk_free((void*) fType); +} + +static size_t makeCharArray(char* buffer, size_t compact) +{ + size_t bits = (size_t) compact >> 1; + memcpy(buffer, &bits, sizeof(compact)); + buffer[sizeof(compact)] = 0; + return strlen(buffer); +} + +#if 0 +const char* SkEvent::getType() const +{ + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + fType = (char*) sk_malloc_throw(len); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, chars, len); + } + return fType; +} +#endif + +void SkEvent::getType(SkString* str) const +{ + if (str) + { + if ((size_t) fType & 1) // not a pointer + { + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + str->set(chars, len); + } + else + str->set(fType); + } +} + +bool SkEvent::isType(const SkString& str) const +{ + return this->isType(str.c_str(), str.size()); +} + +bool SkEvent::isType(const char type[], size_t typeLen) const +{ + if (typeLen == 0) + typeLen = strlen(type); + if ((size_t) fType & 1) { // not a pointer + char chars[sizeof(size_t) + 1]; + size_t len = makeCharArray(chars, (size_t) fType); + return len == typeLen && strncmp(chars, type, typeLen) == 0; + } + return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0; +} + +void SkEvent::setType(const char type[], size_t typeLen) +{ + if (typeLen == 0) + typeLen = strlen(type); + if (typeLen <= sizeof(fType)) { + size_t slot = 0; + memcpy(&slot, type, typeLen); + if (slot << 1 >> 1 != slot) + goto useCharStar; + slot <<= 1; + slot |= 1; + fType = (char*) slot; + } else { +useCharStar: + fType = (char*) sk_malloc_throw(typeLen + 1); + SkASSERT(((size_t) fType & 1) == 0); + memcpy(fType, type, typeLen); + fType[typeLen] = 0; + } +} + +void SkEvent::setType(const SkString& type) +{ + setType(type.c_str()); +} + +//////////////////////////////////////////////////////////////////////////// + +#include "SkParse.h" + +void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + const char* name = dom.findAttr(node, "type"); + if (name) + this->setType(name); + + const char* value; + if ((value = dom.findAttr(node, "fast32")) != NULL) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setFast32(n); + } + + for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node)) + { + if (strcmp(dom.getName(node), "data")) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));) + continue; + } + + name = dom.findAttr(node, "name"); + if (name == NULL) + { + SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");) + continue; + } + + if ((value = dom.findAttr(node, "s32")) != NULL) + { + int32_t n; + if (SkParse::FindS32(value, &n)) + this->setS32(name, n); + } + else if ((value = dom.findAttr(node, "scalar")) != NULL) + { + SkScalar x; + if (SkParse::FindScalar(value, &x)) + this->setScalar(name, x); + } + else if ((value = dom.findAttr(node, "string")) != NULL) + this->setString(name, value); +#ifdef SK_DEBUG + else + { + SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name); + } +#endif + } +} + +#ifdef SK_DEBUG + + #ifndef SkScalarToFloat + #define SkScalarToFloat(x) ((x) / 65536.f) + #endif + + void SkEvent::dump(const char title[]) + { + if (title) + SkDebugf("%s ", title); + + SkString etype; + this->getType(&etype); + SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32()); + + const SkMetaData& md = this->getMetaData(); + SkMetaData::Iter iter(md); + SkMetaData::Type mtype; + int count; + const char* name; + + while ((name = iter.next(&mtype, &count)) != NULL) + { + SkASSERT(count > 0); + + SkDebugf(" <%s>=", name); + switch (mtype) { + case SkMetaData::kS32_Type: // vector version??? + { + int32_t value; + md.findS32(name, &value); + SkDebugf("%d ", value); + } + break; + case SkMetaData::kScalar_Type: + { + const SkScalar* values = md.findScalars(name, &count, NULL); + SkDebugf("%f", SkScalarToFloat(values[0])); + for (int i = 1; i < count; i++) + SkDebugf(", %f", SkScalarToFloat(values[i])); + SkDebugf(" "); + } + break; + case SkMetaData::kString_Type: + { + const char* value = md.findString(name); + SkASSERT(value); + SkDebugf("<%s> ", value); + } + break; + case SkMetaData::kPtr_Type: // vector version??? + { + void* value; + md.findPtr(name, &value); + SkDebugf("%p ", value); + } + break; + case SkMetaData::kBool_Type: // vector version??? + { + bool value; + md.findBool(name, &value); + SkDebugf("%s ", value ? "true" : "false"); + } + break; + default: + SkASSERT(!"unknown metadata type returned from iterator"); + break; + } + } + SkDebugf("\n"); + } +#endif + +/////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG +// #define SK_TRACE_EVENTSx +#endif + +#ifdef SK_TRACE_EVENTS + static void event_log(const char s[]) + { + SkDEBUGF(("%s\n", s)); + } + + #define EVENT_LOG(s) event_log(s) + #define EVENT_LOGN(s, n) do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0) +#else + #define EVENT_LOG(s) + #define EVENT_LOGN(s, n) +#endif + +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkTime.h" + +#define SK_Event_GlobalsTag SkSetFourByteTag('e', 'v', 'n', 't') + +class SkEvent_Globals : public SkGlobals::Rec { +public: + SkMutex fEventMutex; + SkEvent* fEventQHead, *fEventQTail; + SkEvent* fDelayQHead; + SkDEBUGCODE(int fEventCounter;) +}; + +static SkGlobals::Rec* create_globals() +{ + SkEvent_Globals* rec = new SkEvent_Globals; + rec->fEventQHead = NULL; + rec->fEventQTail = NULL; + rec->fDelayQHead = NULL; + SkDEBUGCODE(rec->fEventCounter = 0;) + return rec; +} + +bool SkEvent::Post(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) +{ + if (delay) + return SkEvent::PostTime(evt, sinkID, SkTime::GetMSecs() + delay); + + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + evt->fTargetID = sinkID; + +#ifdef SK_TRACE_EVENTS + { + SkString str("SkEvent::Post("); + str.append(evt->getType()); + str.append(", 0x"); + str.appendHex(sinkID); + str.append(", "); + str.appendS32(delay); + str.append(")"); + event_log(str.c_str()); + } +#endif + + globals.fEventMutex.acquire(); + bool wasEmpty = SkEvent::Enqueue(evt); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + return true; +} + +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS) +SkMSec gMaxDrawTime; +#endif + +bool SkEvent::PostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time) +{ +#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS) + gMaxDrawTime = time; +#endif + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + evt->fTargetID = sinkID; + +#ifdef SK_TRACE_EVENTS + { + SkString str("SkEvent::Post("); + str.append(evt->getType()); + str.append(", 0x"); + str.appendHex(sinkID); + str.append(", "); + str.appendS32(time); + str.append(")"); + event_log(str.c_str()); + } +#endif + + globals.fEventMutex.acquire(); + SkMSec queueDelay = SkEvent::EnqueueTime(evt, time); + globals.fEventMutex.release(); + + // call outside of us holding the mutex + if ((int32_t)queueDelay != ~0) + SkEvent::SignalQueueTimer(queueDelay); + return true; +} + +bool SkEvent::Enqueue(SkEvent* evt) +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + // gEventMutex acquired by caller + + SkASSERT(evt); + + bool wasEmpty = globals.fEventQHead == NULL; + + if (globals.fEventQTail) + globals.fEventQTail->fNextEvent = evt; + globals.fEventQTail = evt; + if (globals.fEventQHead == NULL) + globals.fEventQHead = evt; + evt->fNextEvent = NULL; + + SkDEBUGCODE(++globals.fEventCounter); +// SkDebugf("Enqueue: count=%d\n", gEventCounter); + + return wasEmpty; +} + +SkEvent* SkEvent::Dequeue(SkEventSinkID* sinkID) +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + globals.fEventMutex.acquire(); + + SkEvent* evt = globals.fEventQHead; + if (evt) + { + SkDEBUGCODE(--globals.fEventCounter); + + if (sinkID) + *sinkID = evt->fTargetID; + + globals.fEventQHead = evt->fNextEvent; + if (globals.fEventQHead == NULL) + globals.fEventQTail = NULL; + } + globals.fEventMutex.release(); + +// SkDebugf("Dequeue: count=%d\n", gEventCounter); + + return evt; +} + +bool SkEvent::QHasEvents() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + // this is not thread accurate, need a semaphore for that + return globals.fEventQHead != NULL; +} + +#ifdef SK_TRACE_EVENTS + static int gDelayDepth; +#endif + +SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time) +{ +#ifdef SK_TRACE_EVENTS + SkDebugf("enqueue-delay %s %d (%d)", evt->getType(), time, gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); + ++gDelayDepth; +#endif + + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + // gEventMutex acquired by caller + + SkEvent* curr = globals.fDelayQHead; + SkEvent* prev = NULL; + + while (curr) + { + if (SkMSec_LT(time, curr->fTime)) + break; + prev = curr; + curr = curr->fNextEvent; + } + + evt->fTime = time; + evt->fNextEvent = curr; + if (prev == NULL) + globals.fDelayQHead = evt; + else + prev->fNextEvent = evt; + + SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs(); + if ((int32_t)delay <= 0) + delay = 1; + return delay; +} + +////////////////////////////////////////////////////////////////////////////// + +#include "SkEventSink.h" + +bool SkEvent::ProcessEvent() +{ + SkEventSinkID sinkID; + SkEvent* evt = SkEvent::Dequeue(&sinkID); + SkAutoTDelete<SkEvent> autoDelete(evt); + bool again = false; + + EVENT_LOGN("ProcessEvent", (int32_t)evt); + + if (evt) + { + (void)SkEventSink::DoEvent(*evt, sinkID); + again = SkEvent::QHasEvents(); + } + return again; +} + +void SkEvent::ServiceQueueTimer() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + globals.fEventMutex.acquire(); + + bool wasEmpty = false; + SkMSec now = SkTime::GetMSecs(); + SkEvent* evt = globals.fDelayQHead; + + while (evt) + { + if (SkMSec_LT(now, evt->fTime)) + break; + +#ifdef SK_TRACE_EVENTS + --gDelayDepth; + SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth); + const char* idStr = evt->findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); +#endif + + SkEvent* next = evt->fNextEvent; + if (SkEvent::Enqueue(evt)) + wasEmpty = true; + evt = next; + } + globals.fDelayQHead = evt; + + SkMSec time = evt ? evt->fTime - now : 0; + + globals.fEventMutex.release(); + + if (wasEmpty) + SkEvent::SignalNonEmptyQueue(); + + SkEvent::SignalQueueTimer(time); +} + +int SkEvent::CountEventsOnQueue() { + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + globals.fEventMutex.acquire(); + + int count = 0; + const SkEvent* evt = globals.fEventQHead; + while (evt) { + count += 1; + evt = evt->fNextEvent; + } + globals.fEventMutex.release(); + + return count; +} + +//////////////////////////////////////////////////////////////// + +void SkEvent::Init() +{ +} + +void SkEvent::Term() +{ + SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals); + + SkEvent* evt = globals.fEventQHead; + while (evt) + { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } + + evt = globals.fDelayQHead; + while (evt) + { + SkEvent* next = evt->fNextEvent; + delete evt; + evt = next; + } +} + diff --git a/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp new file mode 100644 index 0000000000..c8fe35ca18 --- /dev/null +++ b/src/views/SkEventSink.cpp @@ -0,0 +1,345 @@ +/* libs/graphics/views/SkEventSink.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkEventSink.h" +#include "SkTagList.h" +#include "SkThread.h" + +#include "SkGlobals.h" +#include "SkThread.h" +#include "SkTime.h" + +#define SK_EventSink_GlobalsTag SkSetFourByteTag('e', 'v', 's', 'k') + +class SkEventSink_Globals : public SkGlobals::Rec { +public: + SkMutex fSinkMutex; + SkEventSinkID fNextSinkID; + SkEventSink* fSinkHead; +}; + +static SkGlobals::Rec* create_globals() +{ + SkEventSink_Globals* rec = new SkEventSink_Globals; + rec->fNextSinkID = 0; + rec->fSinkHead = NULL; + return rec; +} + +SkEventSink::SkEventSink() : fTagHead(NULL) +{ + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + + globals.fSinkMutex.acquire(); + + fID = ++globals.fNextSinkID; + fNextSink = globals.fSinkHead; + globals.fSinkHead = this; + + globals.fSinkMutex.release(); +} + +SkEventSink::~SkEventSink() +{ + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + + if (fTagHead) + SkTagList::DeleteAll(fTagHead); + + globals.fSinkMutex.acquire(); + + SkEventSink* sink = globals.fSinkHead; + SkEventSink* prev = NULL; + + for (;;) + { + SkEventSink* next = sink->fNextSink; + if (sink == this) + { + if (prev) + prev->fNextSink = next; + else + globals.fSinkHead = next; + break; + } + prev = sink; + sink = next; + } + globals.fSinkMutex.release(); +} + +bool SkEventSink::doEvent(const SkEvent& evt) +{ + return this->onEvent(evt); +} + +bool SkEventSink::doQuery(SkEvent* evt) +{ + SkASSERT(evt); + return this->onQuery(evt); +} + +bool SkEventSink::onEvent(const SkEvent&) +{ + return false; +} + +bool SkEventSink::onQuery(SkEvent*) +{ + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTagList* SkEventSink::findTagList(U8CPU tag) const +{ + return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL; +} + +void SkEventSink::addTagList(SkTagList* rec) +{ + SkASSERT(rec); + SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL); + + rec->fNext = fTagHead; + fTagHead = rec; +} + +void SkEventSink::removeTagList(U8CPU tag) +{ + if (fTagHead) + SkTagList::DeleteTag(&fTagHead, tag); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SkListenersTagList : SkTagList { + SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList) + { + fExtra16 = SkToU16(count); + fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID)); + } + virtual ~SkListenersTagList() + { + sk_free(fIDs); + } + + int countListners() const { return fExtra16; } + + int find(SkEventSinkID id) const + { + const SkEventSinkID* idptr = fIDs; + for (int i = fExtra16 - 1; i >= 0; --i) + if (idptr[i] == id) + return i; + return -1; + } + + SkEventSinkID* fIDs; +}; + +void SkEventSink::addListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + int count = 0; + + if (prev) + { + if (prev->find(id) >= 0) + return; + count = prev->countListners(); + } + + SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1)); + + if (prev) + { + memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID)); + this->removeTagList(kListeners_SkTagList); + } + next->fIDs[count] = id; + this->addTagList(next); +} + +void SkEventSink::copyListeners(const SkEventSink& sink) +{ + SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList); + if (sinkList == NULL) + return; + SkASSERT(sinkList->countListners() > 0); + const SkEventSinkID* iter = sinkList->fIDs; + const SkEventSinkID* stop = iter + sinkList->countListners(); + while (iter < stop) + addListenerID(*iter++); +} + +void SkEventSink::removeListenerID(SkEventSinkID id) +{ + if (id == 0) + return; + + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + + if (list == NULL) + return; + + int index = list->find(id); + if (index >= 0) + { + int count = list->countListners(); + SkASSERT(count > 0); + if (count == 1) + this->removeTagList(kListeners_SkTagList); + else + { + // overwrite without resize/reallocating our struct (for speed) + list->fIDs[index] = list->fIDs[count - 1]; + list->fExtra16 = SkToU16(count - 1); + } + } +} + +bool SkEventSink::hasListeners() const +{ + return this->findTagList(kListeners_SkTagList) != NULL; +} + +void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay) +{ + SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList); + if (list) + { + SkASSERT(list->countListners() > 0); + const SkEventSinkID* iter = list->fIDs; + const SkEventSinkID* stop = iter + list->countListners(); + while (iter < stop) + (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID) +{ + SkEventSink* sink = SkEventSink::FindSink(sinkID); + + if (sink) + { +#ifdef SK_DEBUG + if (evt.isDebugTrace()) + { + SkString etype; + evt.getType(&etype); + SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID); + const char* idStr = evt.findString("id"); + if (idStr) + SkDebugf(" (%s)", idStr); + SkDebugf("\n"); + } +#endif + return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult; + } + else + { +#ifdef SK_DEBUG + if (sinkID) + SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID); + else + SkDebugf("Event sent to 0 sinkID\n"); + + if (evt.isDebugTrace()) + { + SkString etype; + evt.getType(&etype); + SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID); + } +#endif + return kSinkNotFound_EventResult; + } +} + +SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID) +{ + if (sinkID == 0) + return 0; + + SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals); + SkAutoMutexAcquire ac(globals.fSinkMutex); + SkEventSink* sink = globals.fSinkHead; + + while (sink) + { + if (sink->getSinkID() == sinkID) + return sink; + sink = sink->fNextSink; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +#if 0 // experimental, not tested + +#include "SkThread.h" +#include "SkTDict.h" + +#define kMinStringBufferSize 128 +static SkMutex gNamedSinkMutex; +static SkTDict<SkEventSinkID> gNamedSinkIDs(kMinStringBufferSize); + +/** Register a name/id pair with the system. If the name already exists, + replace its ID with the new id. This pair will persist until UnregisterNamedSink() + is called. +*/ +void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id) +{ + if (id && name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + gNamedSinkIDs.set(name, id); + } +} + +/** Return the id that matches the specified name (from a previous call to + RegisterNamedSinkID(). If no match is found, return 0 +*/ +SkEventSinkID SkEventSink::FindNamedSinkID(const char name[]) +{ + SkEventSinkID id = 0; + + if (name && *name) + { + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.find(name, &id); + } + return id; +} + +/** Remove all name/id pairs from the system. This is call internally + on shutdown, to ensure no memory leaks. It should not be called + before shutdown. +*/ +void SkEventSink::RemoveAllNamedSinkIDs() +{ + SkAutoMutexAcquire ac(gNamedSinkMutex); + (void)gNamedSinkIDs.reset(); +} +#endif diff --git a/src/views/SkImageView.cpp b/src/views/SkImageView.cpp new file mode 100644 index 0000000000..9c358c7e26 --- /dev/null +++ b/src/views/SkImageView.cpp @@ -0,0 +1,296 @@ +#include "SkImageView.h" +#include "SkAnimator.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImageDecoder.h" +#include "SkMatrix.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +SkImageView::SkImageView() +{ + fMatrix = NULL; + fScaleType = kMatrix_ScaleType; + + fData.fAnim = NULL; // handles initializing the other union values + fDataIsAnim = true; + + fUriIsValid = false; // an empty string is not valid +} + +SkImageView::~SkImageView() +{ + if (fMatrix) + sk_free(fMatrix); + + this->freeData(); +} + +void SkImageView::getUri(SkString* uri) const +{ + if (uri) + *uri = fUri; +} + +void SkImageView::setUri(const char uri[]) +{ + if (!fUri.equals(uri)) + { + fUri.set(uri); + this->onUriChange(); + } +} + +void SkImageView::setUri(const SkString& uri) +{ + if (fUri != uri) + { + fUri = uri; + this->onUriChange(); + } +} + +void SkImageView::setScaleType(ScaleType st) +{ + SkASSERT((unsigned)st <= kFitEnd_ScaleType); + + if ((ScaleType)fScaleType != st) + { + fScaleType = SkToU8(st); + if (fUriIsValid) + this->inval(NULL); + } +} + +bool SkImageView::getImageMatrix(SkMatrix* matrix) const +{ + if (fMatrix) + { + SkASSERT(!fMatrix->isIdentity()); + if (matrix) + *matrix = *fMatrix; + return true; + } + else + { + if (matrix) + matrix->reset(); + return false; + } +} + +void SkImageView::setImageMatrix(const SkMatrix* matrix) +{ + bool changed = false; + + if (matrix && !matrix->isIdentity()) + { + if (fMatrix == NULL) + fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix)); + *fMatrix = *matrix; + changed = true; + } + else // set us to identity + { + if (fMatrix) + { + SkASSERT(!fMatrix->isIdentity()); + sk_free(fMatrix); + fMatrix = NULL; + changed = true; + } + } + + // only redraw if we changed our matrix and we're not in scaleToFit mode + if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid) + this->inval(NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool SkImageView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + if (fUriIsValid) + this->inval(NULL); + return true; + } + return this->INHERITED::onEvent(evt); +} + +static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st) +{ + SkASSERT(st != SkImageView::kMatrix_ScaleType); + SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType); + + SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit); + SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit); + SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit); + SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit); + + return (SkMatrix::ScaleToFit)(st - 1); +} + +void SkImageView::onDraw(SkCanvas* canvas) +{ + SkRect src; + if (!this->getDataBounds(&src)) + { + SkDEBUGCODE(canvas->drawColor(SK_ColorRED);) + return; // nothing to draw + } + + SkAutoCanvasRestore restore(canvas, true); + SkMatrix matrix; + + if (this->getScaleType() == kMatrix_ScaleType) + (void)this->getImageMatrix(&matrix); + else + { + SkRect dst; + dst.set(0, 0, this->width(), this->height()); + matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType())); + } + canvas->concat(matrix); + + SkPaint paint; + + paint.setAntiAlias(true); + + if (fDataIsAnim) + { + SkMSec now = SkTime::GetMSecs(); + + SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now); + +SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff)); + + if (diff == SkAnimator::kDifferent) + this->inval(NULL); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fData.fAnim->getInvalBounds(&bounds); + matrix.mapRect(&bounds); // get the bounds into view coordinates + this->inval(&bounds); + } + } + else + canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint); +} + +void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* src = dom.findAttr(node, "src"); + if (src) + this->setUri(src); + + int index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd"); + if (index >= 0) + this->setScaleType((ScaleType)index); + + // need inflate syntax/reader for matrix +} + +///////////////////////////////////////////////////////////////////////////////////// + +void SkImageView::onUriChange() +{ + if (this->freeData()) + this->inval(NULL); + fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri +} + +bool SkImageView::freeData() +{ + if (fData.fAnim) // test is valid for all union values + { + if (fDataIsAnim) + delete fData.fAnim; + else + delete fData.fBitmap; + + fData.fAnim = NULL; // valid for all union values + return true; + } + return false; +} + +bool SkImageView::getDataBounds(SkRect* bounds) +{ + SkASSERT(bounds); + + if (this->ensureUriIsLoaded()) + { + SkScalar width, height; + + if (fDataIsAnim) + { + if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) || + SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y"))) + { + // cons up fake bounds + width = this->width(); + height = this->height(); + } + } + else + { + width = SkIntToScalar(fData.fBitmap->width()); + height = SkIntToScalar(fData.fBitmap->height()); + } + bounds->set(0, 0, width, height); + return true; + } + return false; +} + +bool SkImageView::ensureUriIsLoaded() +{ + if (fData.fAnim) // test is valid for all union values + { + SkASSERT(fUriIsValid); + return true; + } + if (!fUriIsValid) + return false; + + // try to load the url + if (fUri.endsWith(".xml")) // assume it is screenplay + { + SkAnimator* anim = new SkAnimator; + + if (!anim->decodeURI(fUri.c_str())) + { + delete anim; + fUriIsValid = false; + return false; + } + anim->setHostEventSink(this); + + fData.fAnim = anim; + fDataIsAnim = true; + } + else // assume it is an image format + { + #if 0 + SkBitmap* bitmap = new SkBitmap; + + if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap)) + { + delete bitmap; + fUriIsValid = false; + return false; + } + fData.fBitmap = bitmap; + fDataIsAnim = false; + #else + return false; + #endif + } + return true; +} + diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp new file mode 100644 index 0000000000..ba4f02afc9 --- /dev/null +++ b/src/views/SkListView.cpp @@ -0,0 +1,895 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkEvent.h" +#include "SkKey.h" +#include "SkParsePaint.h" +#include "SkSystemEventTypes.h" + +#if 0 + +SkEvent* SkListSource::getEvent(int index) +{ + return NULL; +} + +#include "SkOSFile.h" + +class SkDirListSource : public SkListSource { +public: + SkDirListSource(const char path[], const char suffix[], const char target[]) + : fPath(path), fSuffix(suffix), fTarget(target) + { + fCount = -1; + } + virtual int countRows() + { + if (fCount < 0) + { + fCount = 0; + fIter.reset(fPath.c_str(), fSuffix.c_str()); + while (fIter.next(NULL)) + fCount += 1; + fIter.reset(fPath.c_str(), fSuffix.c_str()); + fIndex = 0; + } + return fCount; + } + virtual void getRow(int index, SkString* left, SkString* right) + { + (void)this->countRows(); + SkASSERT((unsigned)index < (unsigned)fCount); + + if (fIndex > index) + { + fIter.reset(fPath.c_str(), fSuffix.c_str()); + fIndex = 0; + } + + while (fIndex < index) + { + fIter.next(NULL); + fIndex += 1; + } + + if (fIter.next(left)) + { + if (left) + left->remove(left->size() - fSuffix.size(), fSuffix.size()); + } + else + { + if (left) + left->reset(); + } + if (right) // only set to ">" if we know we're on a sub-directory + right->reset(); + + fIndex += 1; + } + virtual SkEvent* getEvent(int index) + { + SkASSERT((unsigned)index < (unsigned)fCount); + + SkEvent* evt = new SkEvent(); + SkString label; + + this->getRow(index, &label, NULL); + evt->setString("name", label.c_str()); + + int c = fPath.c_str()[fPath.size() - 1]; + if (c != '/' && c != '\\') + label.prepend("/"); + label.prepend(fPath); + label.append(fSuffix); + evt->setString("path", label.c_str()); + evt->setS32("index", index); + evt->setS32("duration", 22); + evt->setType(fTarget); + return evt; + } + +private: + SkString fPath, fSuffix; + SkString fTarget; + SkOSFile::Iter fIter; + int fCount; + int fIndex; +}; + +SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[]) +{ + return new SkDirListSource(path, suffix, target); +} + +////////////////////////////////////////////////////////////////// + +class SkDOMListSource : public SkListSource { +public: + enum Type { + kUnknown_Type, + kDir_Type, + kToggle_Type + }; + struct ItemRec { + SkString fLabel; + SkString fTail, fAltTail; + SkString fTarget; + Type fType; + }; + + SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">") + { + const SkDOM::Node* child = dom.getFirstChild(node, "item"); + int count = 0; + + while (child) + { + count += 1; + child = dom.getNextSibling(child, "item"); + } + + fCount = count; + fList = NULL; + if (count) + { + ItemRec* rec = fList = new ItemRec[count]; + + child = dom.getFirstChild(node, "item"); + while (child) + { + rec->fLabel.set(dom.findAttr(child, "label")); + rec->fTail.set(dom.findAttr(child, "tail")); + rec->fAltTail.set(dom.findAttr(child, "alt-tail")); + rec->fTarget.set(dom.findAttr(child, "target")); + rec->fType = kUnknown_Type; + + int index = dom.findList(child, "type", "dir,toggle"); + if (index >= 0) + rec->fType = (Type)(index + 1); + + child = dom.getNextSibling(child, "item"); + rec += 1; + } + } + } + virtual ~SkDOMListSource() + { + delete[] fList; + } + virtual int countRows() + { + return fCount; + } + virtual void getRow(int index, SkString* left, SkString* right) + { + SkASSERT((unsigned)index < (unsigned)fCount); + + if (left) + *left = fList[index].fLabel; + if (right) + *right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail; + } + virtual SkEvent* getEvent(int index) + { + SkASSERT((unsigned)index < (unsigned)fCount); + + if (fList[index].fType == kDir_Type) + { + SkEvent* evt = new SkEvent(); + evt->setType(fList[index].fTarget); + evt->setFast32(index); + return evt; + } + if (fList[index].fType == kToggle_Type) + fList[index].fTail.swap(fList[index].fAltTail); + + return NULL; + } + +private: + int fCount; + ItemRec* fList; + SkString fDirTail; +}; + +SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node) +{ + return new SkDOMListSource(dom, node); +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +SkListView::SkListView(U32 flags) : SkWidgetView(flags) +{ + fSource = NULL; + fScrollIndex = 0; + fCurrIndex = -1; + fRowHeight = SkIntToScalar(16); + fVisibleRowCount = 0; + fStrCache = NULL; + + fPaint[kBG_Attr].setColor(0); + fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14)); + fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14)); + fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE); + fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE); +} + +SkListView::~SkListView() +{ + delete[] fStrCache; + fSource->safeUnref(); +} + +void SkListView::setRowHeight(SkScalar height) +{ + SkASSERT(height >= 0); + + if (fRowHeight != height) + { + fRowHeight = height; + this->inval(NULL); + this->onSizeChange(); + } +} + +void SkListView::setSelection(int index) +{ + if (fCurrIndex != index) + { + this->invalSelection(); + fCurrIndex = index; + this->invalSelection(); + this->ensureSelectionIsVisible(); + + { + SkEvent evt; + evt.setType("listview-selection"); + evt.setFast32(index); + this->sendEventToParents(evt); + } + } +} + +void SkListView::moveSelectionUp() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = fSource->countRows() - 1; + else + index = SkMax32(index - 1, 0); + this->setSelection(index); + } +} + +void SkListView::moveSelectionDown() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = 0; + else + index = SkMin32(index + 1, fSource->countRows() - 1); + this->setSelection(index); + } +} + +void SkListView::invalSelection() +{ + SkRect r; + if (this->getRowRect(fCurrIndex, &r)) + this->inval(&r); +} + +void SkListView::ensureSelectionIsVisible() +{ + if (fSource == NULL) + return; + + if ((unsigned)fCurrIndex < (unsigned)fSource->countRows()) + { + int index = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll + { + if (index < 0) // too high + fScrollIndex = fCurrIndex; + else + fScrollIndex = fCurrIndex - fVisibleRowCount + 1; + SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows()); + + this->dirtyStrCache(); + this->inval(NULL); + } + } +} + +bool SkListView::getRowRect(int index, SkRect* r) const +{ + SkASSERT(r); + index = this->logicalToVisualIndex(index); + if (index >= 0) + { + SkScalar top = index * fRowHeight; + + if (top < this->height()) + { + if (r) + r->set(0, top, this->width(), top + fRowHeight); + return true; + } + } + return false; +} + +SkPaint& SkListView::paint(Attr attr) +{ + SkASSERT((unsigned)attr < kAttrCount); + return fPaint[attr]; +} + +SkListSource* SkListView::setListSource(SkListSource* src) +{ + if (fSource != src) + { + SkRefCnt_SafeAssign(fSource, src); + this->dirtyStrCache(); + this->ensureSelectionIsVisible(); + this->inval(NULL); + } + return src; +} + +void SkListView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + canvas->drawPaint(fPaint[kBG_Attr]); + + int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex); + if (visibleCount == 0) + return; + + this->ensureStrCache(visibleCount); + int currIndex = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)currIndex < (unsigned)visibleCount) + { + SkAutoCanvasRestore restore(canvas, true); + SkRect r; + + canvas->translate(0, currIndex * fRowHeight); + (void)this->getRowRect(fScrollIndex, &r); + canvas->drawRect(r, fPaint[kHiliteCell_Attr]); + } + + SkPaint* p; + SkScalar y, x = SkIntToScalar(6); + SkScalar rite = this->width() - x; + + { + SkScalar ascent, descent; + fPaint[kNormalText_Attr].measureText(0, NULL, &ascent, &descent); + y = SkScalarHalf(fRowHeight - descent + ascent) - ascent; + } + + for (int i = 0; i < visibleCount; i++) + { + if (i == currIndex) + p = &fPaint[kHiliteText_Attr]; + else + p = &fPaint[kNormalText_Attr]; + + p->setTextAlign(SkPaint::kLeft_Align); + canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p); + p->setTextAlign(SkPaint::kRight_Align); + canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p); + canvas->translate(0, fRowHeight); + } +} + +void SkListView::onSizeChange() +{ + SkScalar count = SkScalarDiv(this->height(), fRowHeight); + int n = SkScalarFloor(count); + + // only want to show rows that are mostly visible + if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100) + n += 1; + + if (fVisibleRowCount != n) + { + fVisibleRowCount = n; + this->ensureSelectionIsVisible(); + this->dirtyStrCache(); + } +} + +void SkListView::dirtyStrCache() +{ + if (fStrCache) + { + delete[] fStrCache; + fStrCache = NULL; + } +} + +void SkListView::ensureStrCache(int count) +{ + if (fStrCache == NULL) + { + fStrCache = new SkString[count << 1]; + + if (fSource) + for (int i = 0; i < count; i++) + fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]); + } +} + +bool SkListView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key)) + { + switch (evt.getFast32()) { + case kUp_SkKey: + this->moveSelectionUp(); + return true; + case kDown_SkKey: + this->moveSelectionDown(); + return true; + case kRight_SkKey: + case kOK_SkKey: + if (fSource && fCurrIndex >= 0) + { + SkEvent* evt = fSource->getEvent(fCurrIndex); + if (evt) + { + SkView* view = this->sendEventToParents(*evt); + delete evt; + return view != NULL; + } + else // hack to make toggle work + { + this->dirtyStrCache(); + this->inval(NULL); + } + } + break; + } + } + return this->INHERITED::onEvent(evt); +} + +void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + SkScalar x; + const SkDOM::Node* child; + + if (dom.findScalar(node, "row-height", &x)) + this->setRowHeight(x); + + if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL) + SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child); + + // look for a listsource + { + SkListSource* src = NULL; + + if ((child = dom.getFirstChild(node, "file-listsource")) != NULL) + { + const char* path = dom.findAttr(child, "path"); + if (path) + src = SkListSource::CreateFromDir( path, + dom.findAttr(child, "filter"), + dom.findAttr(child, "target")); + } + else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL) + { + src = SkListSource::CreateFromDOM(dom, child); + } + + if (src) + { + this->setListSource(src)->unref(); + this->setSelection(0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkImageDecoder.h" +#include "SkShader.h" + +class SkScrollBarView : public SkView { +public: + SkScrollBarView(const char bg[], const char fg[]) + { + fBGRef = SkBitmapRef::Decode(bg, true); + fFGRef = SkBitmapRef::Decode(fg, true); + + if (fBGRef) + this->setWidth(SkIntToScalar(fBGRef->bitmap().width())); + } + ~SkScrollBarView() + { + delete fBGRef; + delete fFGRef; + } +protected: + virtual void onDraw(SkCanvas* canvas) + { + if (fBGRef == NULL) return; + + SkPaint paint; + + SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode); + paint.setShader(shader)->unref(); + + canvas->drawPaint(paint); + } +private: + SkBitmapRef* fBGRef, *fFGRef; +}; + +SkGridView::SkGridView(U32 flags) : SkWidgetView(flags) +{ + fSource = NULL; + fCurrIndex = -1; + fVisibleCount.set(0, 0); + + fPaint[kBG_Attr].setColor(SK_ColorWHITE); + fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW); + fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style); + fPaint[kHiliteCell_Attr].setAntiAliasOn(true); + fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3); + + fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg"); + this->attachChildToFront(fScrollBar)->unref(); + fScrollBar->setVisibleP(true); +} + +SkGridView::~SkGridView() +{ + fSource->safeUnref(); +} + +void SkGridView::getCellSize(SkPoint* size) const +{ + if (size) + *size = fCellSize; +} + +void SkGridView::setCellSize(SkScalar x, SkScalar y) +{ + SkASSERT(x >= 0 && y >= 0); + + if (!fCellSize.equals(x, y)) + { + fCellSize.set(x, y); + this->inval(NULL); + } +} + +void SkGridView::setSelection(int index) +{ + if (fCurrIndex != index) + { + this->invalSelection(); + fCurrIndex = index; + this->invalSelection(); + this->ensureSelectionIsVisible(); + + // this generates the click + { + SkEvent evt; + evt.setType("listview-selection"); + evt.setFast32(index); + this->sendEventToParents(evt); + } + } +} + +void SkGridView::moveSelectionUp() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = fSource->countRows() - 1; + else + index = SkMax32(index - 1, 0); + this->setSelection(index); + } +} + +void SkGridView::moveSelectionDown() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = 0; + else + index = SkMin32(index + 1, fSource->countRows() - 1); + this->setSelection(index); + } +} + +void SkGridView::invalSelection() +{ + SkRect r; + if (this->getCellRect(fCurrIndex, &r)) + { + SkScalar inset = 0; + if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style) + inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2; + if (fPaint[kHiliteCell_Attr].isAntiAliasOn()) + inset += SK_Scalar1; + r.inset(-inset, -inset); + this->inval(&r); + } +} + +void SkGridView::ensureSelectionIsVisible() +{ + if (fSource == NULL) + return; +#if 0 + if ((unsigned)fCurrIndex < (unsigned)fSource->countRows()) + { + int index = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll + { + if (index < 0) // too high + fScrollIndex = fCurrIndex; + else + fScrollIndex = fCurrIndex - fVisibleRowCount + 1; + SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows()); + + this->dirtyStrCache(); + this->inval(NULL); + } + } +#endif +} + +bool SkGridView::getCellRect(int index, SkRect* r) const +{ + if (fVisibleCount.fY == 0) + return false; + + index = this->logicalToVisualIndex(index); + if (index >= 0) + { + SkRect bounds; + int row = index / fVisibleCount.fY; + int col = index % fVisibleCount.fY; + + bounds.set(0, 0, fCellSize.fX, fCellSize.fY); + bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)), + row * (fCellSize.fY + SkIntToScalar(row > 0))); + + if (bounds.fTop < this->height()) + { + if (r) + *r = bounds; + return true; + } + } + return false; +} + +SkPaint& SkGridView::paint(Attr attr) +{ + SkASSERT((unsigned)attr < kAttrCount); + return fPaint[attr]; +} + +SkListSource* SkGridView::setListSource(SkListSource* src) +{ + if (fSource != src) + { + SkRefCnt_SafeAssign(fSource, src); + // this->dirtyStrCache(); + this->ensureSelectionIsVisible(); + this->inval(NULL); + } + return src; +} + +#include "SkShader.h" + +static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint) +{ + SkRect src; + SkMatrix matrix; + + src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())); + if (matrix.setRectToRect(src, dst)) + { + SkPaint p(paint); + SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode); + p.setShader(shader)->unref(); + + shader->setLocalMatrix(matrix); + canvas->drawRect(dst, p); + } +} + +#include "SkImageDecoder.h" + +void SkGridView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + canvas->drawPaint(fPaint[kBG_Attr]); + + if (fSource == NULL) + return; + +#if 0 + int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex); + if (visibleCount == 0) + return; + + this->ensureStrCache(visibleCount); + int currIndex = this->logicalToVisualIndex(fCurrIndex); +#endif + + SkPaint p; + for (int i = 0; i < fSource->countRows(); i++) + { + bool forced = false; + SkEvent* evt = fSource->getEvent(i); + SkASSERT(evt); + SkString path(evt->findString("path")); + delete evt; + + SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false); + if (bmr == NULL) + { + bmr = SkBitmapRef::Decode(path.c_str(), true); + if (bmr) + forced = true; + } + + if (bmr) + { + SkAutoTDelete<SkBitmapRef> autoRef(bmr); + SkRect r; + if (!this->getCellRect(i, &r)) + break; + copybits(canvas, bmr->bitmap(), r, p); + } + // only draw one forced bitmap at a time + if (forced) + { + this->inval(NULL); // could inval only the remaining visible cells... + break; + } + } + + // draw the hilite + { + SkRect r; + if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r)) + canvas->drawRect(r, fPaint[kHiliteCell_Attr]); + } +} + +static int check_count(int n, SkScalar s) +{ + // only want to show cells that are mostly visible + if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100) + n += 1; + return n; +} + +void SkGridView::onSizeChange() +{ + fScrollBar->setHeight(this->height()); + fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0); + + if (fCellSize.equals(0, 0)) + { + fVisibleCount.set(0, 0); + return; + } + + SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY); + SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX); + int y = SkScalarFloor(rows); + int x = SkScalarFloor(cols); + + y = check_count(y, rows); + x = check_count(x, cols); + + if (!fVisibleCount.equals(x, y)) + { + fVisibleCount.set(x, y); + this->ensureSelectionIsVisible(); + // this->dirtyStrCache(); + } +} + +bool SkGridView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key)) + { + switch (evt.getFast32()) { + case kUp_SkKey: + this->moveSelectionUp(); + return true; + case kDown_SkKey: + this->moveSelectionDown(); + return true; + case kRight_SkKey: + case kOK_SkKey: + if (fSource && fCurrIndex >= 0) + { + SkEvent* evt = fSource->getEvent(fCurrIndex); + if (evt) + { + // augment the event with our local rect + (void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, NULL)); + + SkView* view = this->sendEventToParents(*evt); + delete evt; + return view != NULL; + } + } + break; + } + } + return this->INHERITED::onEvent(evt); +} + +void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + SkScalar x[2]; + const SkDOM::Node* child; + + if (dom.findScalars(node, "cell-size", x, 2)) + this->setCellSize(x[0], x[1]); + + if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL) + SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child); + + // look for a listsource + { + SkListSource* src = NULL; + + if ((child = dom.getFirstChild(node, "file-listsource")) != NULL) + { + const char* path = dom.findAttr(child, "path"); + if (path) + src = SkListSource::CreateFromDir( path, + dom.findAttr(child, "filter"), + dom.findAttr(child, "target")); + } + else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL) + { + src = SkListSource::CreateFromDOM(dom, child); + } + + if (src) + { + this->setListSource(src)->unref(); + this->setSelection(0); + } + } + this->onSizeChange(); +} + +#endif diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp new file mode 100644 index 0000000000..89008e7ebe --- /dev/null +++ b/src/views/SkListWidget.cpp @@ -0,0 +1,623 @@ +#include "SkWidgetViews.h" + +#include "SkAnimator.h" +#include "SkScrollBarView.h" + +extern void init_skin_anim(const char name[], SkAnimator*); + +struct SkListView::BindingRec { + SkString fSlotName; + int fFieldIndex; +}; + +SkListView::SkListView() +{ + fSource = NULL; // our list-source + fScrollBar = NULL; + fAnims = NULL; // array of animators[fVisibleRowCount] + fBindings = NULL; // our fields->slot array + fBindingCount = 0; // number of entries in fSlots array + fScrollIndex = 0; // number of cells to skip before first visible cell + fCurrIndex = -1; // index of "selected" cell + fVisibleRowCount = 0; // number of cells that can fit in our bounds + fAnimContentDirty = true; // true if fAnims[] have their correct content + fAnimFocusDirty = true; + + fHeights[kNormal_Height] = SkIntToScalar(16); + fHeights[kSelected_Height] = SkIntToScalar(16); + + this->setFlags(this->getFlags() | kFocusable_Mask); +} + +SkListView::~SkListView() +{ + SkSafeUnref(fScrollBar); + SkSafeUnref(fSource); + delete[] fAnims; + delete[] fBindings; +} + +void SkListView::setHasScrollBar(bool hasSB) +{ + if (hasSB != this->hasScrollBar()) + { + if (hasSB) + { + SkASSERT(fScrollBar == NULL); + fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum); + fScrollBar->setVisibleP(true); + this->attachChildToFront(fScrollBar); + fScrollBar->setHeight(this->height()); // assume it auto-sets its width + // fScrollBar->setLoc(this->getContentWidth(), 0); + fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0); + } + else + { + SkASSERT(fScrollBar); + fScrollBar->detachFromParent(); + fScrollBar->unref(); + fScrollBar = NULL; + } + this->dirtyCache(kAnimContent_DirtyFlag); + } +} + +void SkListView::setSelection(int index) +{ + if (fCurrIndex != index) + { + fAnimFocusDirty = true; + this->inval(NULL); + + this->invalSelection(); + fCurrIndex = index; + this->invalSelection(); + this->ensureSelectionIsVisible(); + } +} + +bool SkListView::moveSelectionUp() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = fSource->countRecords() - 1; + else + index = SkMax32(index - 1, 0); + + if (fCurrIndex != index) + { + this->setSelection(index); + return true; + } + } + return false; +} + +bool SkListView::moveSelectionDown() +{ + if (fSource) + { + int index = fCurrIndex; + if (index < 0) // no selection + index = 0; + else + index = SkMin32(index + 1, fSource->countRecords() - 1); + + if (fCurrIndex != index) + { + this->setSelection(index); + return true; + } + } + return false; +} + +void SkListView::invalSelection() +{ + SkRect r; + if (this->getRowRect(fCurrIndex, &r)) + this->inval(&r); +} + +void SkListView::ensureSelectionIsVisible() +{ + if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords()) + { + int index = this->logicalToVisualIndex(fCurrIndex); + + if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll + { + int newIndex; + + if (index < 0) // too high + newIndex = fCurrIndex; + else + newIndex = fCurrIndex - fVisibleRowCount + 1; + SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords()); + this->inval(NULL); + + if (fScrollIndex != newIndex) + { + fScrollIndex = newIndex; + if (fScrollBar) + fScrollBar->setStart(newIndex); + this->dirtyCache(kAnimContent_DirtyFlag); + } + } + } +} + +SkScalar SkListView::getContentWidth() const +{ + SkScalar width = this->width(); + + if (fScrollBar) + { + width -= fScrollBar->width(); + if (width < 0) + width = 0; + } + return width; +} + +bool SkListView::getRowRect(int index, SkRect* r) const +{ + SkASSERT(r); + + index = this->logicalToVisualIndex(index); + if (index >= 0) + { + int selection = this->logicalToVisualIndex(fCurrIndex); + + SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height]; + SkScalar top = index * fHeights[kNormal_Height]; + + if (index > selection && selection >= 0) + top += fHeights[kSelected_Height] - fHeights[kNormal_Height]; + + if (top < this->height()) + { + if (r) + r->set(0, top, this->getContentWidth(), top + height); + return true; + } + } + return false; +} + +SkListSource* SkListView::setListSource(SkListSource* src) +{ + if (fSource != src) + { + SkRefCnt_SafeAssign(fSource, src); + this->ensureSelectionIsVisible(); + this->inval(NULL); + + if (fScrollBar) + fScrollBar->setTotal(fSource->countRecords()); + } + return src; +} + +void SkListView::dirtyCache(unsigned dirtyFlags) +{ + if (dirtyFlags & kAnimCount_DirtyFlag) + { + delete fAnims; + fAnims = NULL; + fAnimContentDirty = true; + fAnimFocusDirty = true; + } + if (dirtyFlags & kAnimContent_DirtyFlag) + { + if (!fAnimContentDirty) + { + this->inval(NULL); + fAnimContentDirty = true; + } + fAnimFocusDirty = true; + } +} + +bool SkListView::ensureCache() +{ + if (fSkinName.size() == 0) + return false; + + if (fAnims == NULL) + { + int n = SkMax32(1, fVisibleRowCount); + + SkASSERT(fAnimContentDirty); + fAnims = new SkAnimator[n]; + for (int i = 0; i < n; i++) + { + fAnims[i].setHostEventSink(this); + init_skin_anim(fSkinName.c_str(), &fAnims[i]); + } + + fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value"); + fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value"); + + fAnimFocusDirty = true; + } + + if (fAnimContentDirty && fSource) + { + fAnimContentDirty = false; + + SkString str; + SkEvent evt("user"); + evt.setString("id", "setFields"); + evt.setS32("rowCount", fVisibleRowCount); + + SkEvent dimEvt("user"); + dimEvt.setString("id", "setDim"); + dimEvt.setScalar("dimX", this->getContentWidth()); + dimEvt.setScalar("dimY", this->height()); + + for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++) + { + evt.setS32("relativeIndex", i - fScrollIndex); + for (int j = 0; j < fBindingCount; j++) + { + fSource->getRecord(i, fBindings[j].fFieldIndex, &str); +//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str())); + evt.setString(fBindings[j].fSlotName.c_str(), str.c_str()); + } + (void)fAnims[i % fVisibleRowCount].doUserEvent(evt); + (void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt); + } + fAnimFocusDirty = true; + } + + if (fAnimFocusDirty) + { +//SkDEBUGF(("service fAnimFocusDirty\n")); + fAnimFocusDirty = false; + + SkEvent focusEvt("user"); + focusEvt.setString("id", "setFocus"); + + for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++) + { + focusEvt.setS32("FOCUS", i == fCurrIndex); + (void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt); + } + } + + return true; +} + +void SkListView::ensureVisibleRowCount() +{ + SkScalar height = this->height(); + int n = 0; + + if (height > 0) + { + n = 1; + height -= fHeights[kSelected_Height]; + if (height > 0) + { + SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]); + n += SkScalarFloor(count); + if (count - SkIntToScalar(n) > SK_Scalar1*3/4) + n += 1; + + // SkDebugf("count %g, n %d\n", count/65536., n); + } + } + + if (fVisibleRowCount != n) + { + if (fScrollBar) + fScrollBar->setShown(n); + + fVisibleRowCount = n; + this->ensureSelectionIsVisible(); + this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +void SkListView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + + if (fScrollBar) + fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0); + + this->ensureVisibleRowCount(); +} + +void SkListView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + this->ensureVisibleRowCount(); + + int visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex); + if (visibleCount == 0 || !this->ensureCache()) + return; + +//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex); + + SkAutoCanvasRestore ar(canvas, true); + SkMSec now = SkTime::GetMSecs(); + SkRect bounds; + + bounds.fLeft = 0; + bounds.fRight = this->getContentWidth(); + bounds.fBottom = 0; + // assign bounds.fTop inside the loop + + // hack to reveal our bounds for debugging + if (this->hasFocus()) + canvas->drawARGB(0x11, 0, 0, 0xFF); + else + canvas->drawARGB(0x11, 0x88, 0x88, 0x88); + + for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++) + { + SkPaint paint; + SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height]; + + bounds.fTop = bounds.fBottom; + bounds.fBottom += height; + + canvas->save(); + if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent) + this->inval(&bounds); + canvas->restore(); + + canvas->translate(0, height); + } +} + +bool SkListView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key)) + { + switch (evt.getFast32()) { + case kUp_SkKey: + return this->moveSelectionUp(); + case kDown_SkKey: + return this->moveSelectionDown(); + case kRight_SkKey: + case kOK_SkKey: + this->postWidgetEvent(); + return true; + default: + break; + } + } + return this->INHERITED::onEvent(evt); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static const char gListViewEventSlot[] = "sk-listview-slot-name"; + +/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt) +{ + if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) && + fSource->prepareWidgetEvent(evt, fCurrIndex)) + { + evt->setS32(gListViewEventSlot, fCurrIndex); + return true; + } + return false; +} + +int SkListView::GetWidgetEventListIndex(const SkEvent& evt) +{ + int32_t index; + + return evt.findS32(gListViewEventSlot, &index) ? index : -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + { + bool hasScrollBar; + if (dom.findBool(node, "scrollBar", &hasScrollBar)) + this->setHasScrollBar(hasScrollBar); + } + + const SkDOM::Node* child; + + if ((child = dom.getFirstChild(node, "bindings")) != NULL) + { + delete[] fBindings; + fBindings = NULL; + fBindingCount = 0; + + SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields")); + SkASSERT(listSrc); + fSkinName.set(dom.findAttr(child, "skin-slots")); + SkASSERT(fSkinName.size()); + + this->setListSource(listSrc)->unref(); + + int count = dom.countChildren(child, "bind"); + if (count > 0) + { + fBindings = new BindingRec[count]; + count = 0; // reuse this to count up to the number of valid bindings + + child = dom.getFirstChild(child, "bind"); + SkASSERT(child); + do { + const char* fieldName = dom.findAttr(child, "field"); + const char* slotName = dom.findAttr(child, "slot"); + if (fieldName && slotName) + { + fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName); + if (fBindings[count].fFieldIndex >= 0) + fBindings[count++].fSlotName.set(slotName); + } + } while ((child = dom.getNextSibling(child, "bind")) != NULL); + + fBindingCount = SkToU16(count); + if (count == 0) + { + SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n")); + delete[] fBindings; + } + } + this->dirtyCache(kAnimCount_DirtyFlag); + this->setSelection(0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +class SkXMLListSource : public SkListSource { +public: + SkXMLListSource(const char doc[], size_t len); + virtual ~SkXMLListSource() + { + delete[] fFields; + delete[] fRecords; + } + + virtual int countFields() { return fFieldCount; } + virtual void getFieldName(int index, SkString* field) + { + SkASSERT((unsigned)index < (unsigned)fFieldCount); + if (field) + *field = fFields[index]; + } + virtual int findFieldIndex(const char field[]) + { + for (int i = 0; i < fFieldCount; i++) + if (fFields[i].equals(field)) + return i; + return -1; + } + + virtual int countRecords() { return fRecordCount; } + virtual void getRecord(int rowIndex, int fieldIndex, SkString* data) + { + SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount); + SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount); + if (data) + *data = fRecords[rowIndex * fFieldCount + fieldIndex]; + } + + virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex) + { + // hack, for testing right now. Need the xml to tell us what to jam in and where + SkString data; + + this->getRecord(rowIndex, 0, &data); + evt->setString("xml-listsource", data.c_str()); + return true; + } + +private: + SkString* fFields; // [fFieldCount] + SkString* fRecords; // [fRecordCount][fFieldCount] + int fFieldCount, fRecordCount; +}; + +#include "SkDOM.h" + +SkXMLListSource::SkXMLListSource(const char doc[], size_t len) +{ + fFieldCount = fRecordCount = 0; + fFields = fRecords = NULL; + + SkDOM dom; + + const SkDOM::Node* node = dom.build(doc, len); + SkASSERT(node); + const SkDOM::Node* child; + + child = dom.getFirstChild(node, "fields"); + if (child) + { + fFieldCount = dom.countChildren(child, "field"); + fFields = new SkString[fFieldCount]; + + int n = 0; + child = dom.getFirstChild(child, "field"); + while (child) + { + fFields[n].set(dom.findAttr(child, "name")); + child = dom.getNextSibling(child, "field"); + n += 1; + } + SkASSERT(n == fFieldCount); + } + + child = dom.getFirstChild(node, "records"); + if (child) + { + fRecordCount = dom.countChildren(child, "record"); + fRecords = new SkString[fRecordCount * fFieldCount]; + + int n = 0; + child = dom.getFirstChild(child, "record"); + while (child) + { + for (int i = 0; i < fFieldCount; i++) + fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str())); + child = dom.getNextSibling(child, "record"); + n += 1; + } + SkASSERT(n == fRecordCount); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +SkListSource* SkListSource::Factory(const char name[]) +{ + static const char gDoc[] = + "<db name='contacts.db'>" + "<fields>" + "<field name='name'/>" + "<field name='work-num'/>" + "<field name='home-num'/>" + "<field name='type'/>" + "</fields>" + "<records>" + "<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>" + "<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />" + "<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />" + "<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />" + "<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />" + "<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />" + "<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />" + "<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />" + "<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />" + "<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />" + "<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />" + "<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />" + "<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />" + "<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />" + "<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />" + "<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />" + "<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />" + "</records>" + "</db>"; + +//SkDebugf("doc size %d\n", sizeof(gDoc)-1); + return new SkXMLListSource(gDoc, sizeof(gDoc) - 1); +} + + + diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp new file mode 100644 index 0000000000..3760ddd5cb --- /dev/null +++ b/src/views/SkOSMenu.cpp @@ -0,0 +1,53 @@ +#include "SkOSMenu.h" + +static int gOSMenuCmd = 7000; + +SkOSMenu::SkOSMenu(const char title[]) +{ + fTitle = title; +} + +SkOSMenu::~SkOSMenu() +{ +} + +int SkOSMenu::countItems() const +{ + return fItems.count(); +} + +void SkOSMenu::appendItem(const char title[], const char eventType[], int32_t eventData) +{ + Item* item = fItems.append(); + + item->fTitle = title; + item->fEventType = eventType; + item->fEventData = eventData; + item->fOSCmd = ++gOSMenuCmd; +} + +SkEvent* SkOSMenu::createEvent(uint32_t os_cmd) +{ + const Item* iter = fItems.begin(); + const Item* stop = fItems.end(); + + while (iter < stop) + { + if (iter->fOSCmd == os_cmd) + { + SkEvent* evt = new SkEvent(iter->fEventType); + evt->setFast32(iter->fEventData); + return evt; + } + iter++; + } + return NULL; +} + +const char* SkOSMenu::getItem(int index, uint32_t* cmdID) const +{ + if (cmdID) + *cmdID = fItems[index].fOSCmd; + return fItems[index].fTitle; +} + diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp new file mode 100644 index 0000000000..a79f30c150 --- /dev/null +++ b/src/views/SkParsePaint.cpp @@ -0,0 +1,103 @@ +#include "SkParsePaint.h" +#include "SkTSearch.h" +#include "SkParse.h" +#include "SkImageDecoder.h" +#include "SkGradientShader.h" + +static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node) +{ + if ((node = dom.getFirstChild(node, "shader")) == NULL) + return NULL; + + const char* str; + + if (dom.hasAttr(node, "type", "linear-gradient")) + { + SkColor colors[2]; + SkPoint pts[2]; + + colors[0] = colors[1] = SK_ColorBLACK; // need to initialized the alpha to opaque, since FindColor doesn't set it + if ((str = dom.findAttr(node, "c0")) != NULL && + SkParse::FindColor(str, &colors[0]) && + (str = dom.findAttr(node, "c1")) != NULL && + SkParse::FindColor(str, &colors[1]) && + dom.findScalars(node, "p0", &pts[0].fX, 2) && + dom.findScalars(node, "p1", &pts[1].fX, 2)) + { + SkShader::TileMode mode = SkShader::kClamp_TileMode; + int index; + + if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0) + mode = (SkShader::TileMode)index; + return SkGradientShader::CreateLinear(pts, colors, NULL, 2, mode); + } + } + else if (dom.hasAttr(node, "type", "bitmap")) + { + if ((str = dom.findAttr(node, "src")) == NULL) + return NULL; + + SkBitmap bm; + + if (SkImageDecoder::DecodeFile(str, &bm)) + { + SkShader::TileMode mode = SkShader::kRepeat_TileMode; + int index; + + if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0) + mode = (SkShader::TileMode)index; + + return SkShader::CreateBitmapShader(bm, mode, mode); + } + } + return NULL; +} + +void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(paint); + SkASSERT(&dom); + SkASSERT(node); + + SkScalar x; + + if (dom.findScalar(node, "stroke-width", &x)) + paint->setStrokeWidth(x); + if (dom.findScalar(node, "text-size", &x)) + paint->setTextSize(x); + + bool b; + + SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b)); + + if (dom.findBool(node, "is-stroke", &b)) + paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style); + if (dom.findBool(node, "is-antialias", &b)) + paint->setAntiAlias(b); + if (dom.findBool(node, "is-lineartext", &b)) + paint->setLinearText(b); + + const char* str = dom.findAttr(node, "color"); + if (str) + { + SkColor c = paint->getColor(); + if (SkParse::FindColor(str, &c)) + paint->setColor(c); + } + + // do this AFTER parsing for the color + if (dom.findScalar(node, "opacity", &x)) + { + x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1)); + paint->setAlpha(SkScalarRound(x * 255)); + } + + int index = dom.findList(node, "text-anchor", "left,center,right"); + if (index >= 0) + paint->setTextAlign((SkPaint::Align)index); + + SkShader* shader = inflate_shader(dom, node); + if (shader) + paint->setShader(shader)->unref(); +} + diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp new file mode 100644 index 0000000000..a9fd516458 --- /dev/null +++ b/src/views/SkProgressBarView.cpp @@ -0,0 +1,102 @@ +#include "SkProgressBarView.h" +#include "SkAnimator.h" +#include "SkWidgetViews.h" +#include "SkTime.h" +#include "SkSystemEventTypes.h" + +SkProgressBarView::SkProgressBarView() +{ + init_skin_anim(kProgress_SkinEnum, &fAnim); + fAnim.setHostEventSink(this); + fProgress = 0; + fMax = 100; + +} + +void SkProgressBarView::changeProgress(int diff) +{ + int newProg = fProgress + diff; + if (newProg > 0 && newProg < fMax) + this->setProgress(newProg); + //otherwise i'll just leave it as it is + //this implies that if a new max and progress are set, max must be set first +} + +/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas) +{ + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(NULL); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } +} + +/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + this->inval(NULL); + return true; + } + if (evt.isType("recommendDim")) + { + SkScalar height; + + if (evt.findScalar("y", &height)) + this->setHeight(height); + return true; + } + return this->INHERITED::onEvent(evt); +} + +/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + int32_t temp; + if (dom.findS32(node, "max", &temp)) + this->setMax(temp); + if (dom.findS32(node, "progress", &temp)) + this->setProgress(temp); +} + +/*virtual*/ void SkProgressBarView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); +} + +void SkProgressBarView::reset() +{ + fProgress = 0; + SkEvent e("user"); + e.setString("id", "reset"); + fAnim.doUserEvent(e); +} + +void SkProgressBarView::setMax(int max) +{ + fMax = max; + SkEvent e("user"); + e.setString("id", "setMax"); + e.setS32("newMax", max); + fAnim.doUserEvent(e); +} + +void SkProgressBarView::setProgress(int progress) +{ + fProgress = progress; + SkEvent e("user"); + e.setString("id", "setProgress"); + e.setS32("newProgress", progress); + fAnim.doUserEvent(e); +} diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp new file mode 100644 index 0000000000..0c81bccacd --- /dev/null +++ b/src/views/SkProgressView.cpp @@ -0,0 +1,125 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkShader.h" +#include "SkInterpolator.h" +#include "SkTime.h" + +SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL) +{ + fValue = 0; + fMax = 0; + fInterp = NULL; + fDoInterp = false; +} + +SkProgressView::~SkProgressView() +{ + delete fInterp; + SkSafeUnref(fOnShader); + SkSafeUnref(fOffShader); +} + +void SkProgressView::setMax(U16CPU max) +{ + if (fMax != max) + { + fMax = SkToU16(max); + if (fValue > 0) + this->inval(NULL); + } +} + +void SkProgressView::setValue(U16CPU value) +{ + if (fValue != value) + { + if (fDoInterp) + { + if (fInterp) + delete fInterp; + fInterp = new SkInterpolator(1, 2); + SkScalar x = (SkScalar)(fValue << 8); + fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0); + x = (SkScalar)(value << 8); + fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x); + } + fValue = SkToU16(value); + this->inval(NULL); + } +} + +void SkProgressView::onDraw(SkCanvas* canvas) +{ + if (fMax == 0) + return; + + SkFixed percent; + + if (fInterp) + { + SkScalar x; + if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result) + { + delete fInterp; + fInterp = NULL; + } + percent = (SkFixed)x; // now its 16.8 + percent = SkMax32(0, SkMin32(percent, fMax << 8)); // now its pinned + percent = SkFixedDiv(percent, fMax << 8); // now its 0.16 + this->inval(NULL); + } + else + { + U16CPU value = SkMax32(0, SkMin32(fValue, fMax)); + percent = SkFixedDiv(value, fMax); + } + + + SkRect r; + SkPaint p; + + r.set(0, 0, this->width(), this->height()); + p.setAntiAlias(true); + + r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent)); + p.setStyle(SkPaint::kFill_Style); + + p.setColor(SK_ColorDKGRAY); + p.setShader(fOnShader); + canvas->drawRect(r, p); + + p.setColor(SK_ColorWHITE); + p.setShader(fOffShader); + r.fLeft = r.fRight; + r.fRight = this->width() - SK_Scalar1; + if (r.width() > 0) + canvas->drawRect(r, p); +} + +#include "SkImageDecoder.h" + +static SkShader* inflate_shader(const char file[]) +{ + SkBitmap bm; + + return SkImageDecoder::DecodeFile(file, &bm) ? + SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) : + NULL; +} + +void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* s; + + SkASSERT(fOnShader == NULL); + SkASSERT(fOffShader == NULL); + + if ((s = dom.findAttr(node, "src-on")) != NULL) + fOnShader = inflate_shader(s); + if ((s = dom.findAttr(node, "src-off")) != NULL) + fOffShader = inflate_shader(s); + (void)dom.findBool(node, "do-interp", &fDoInterp); +} + diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp new file mode 100644 index 0000000000..2e087bd1ae --- /dev/null +++ b/src/views/SkScrollBarView.cpp @@ -0,0 +1,139 @@ +#include "SkScrollBarView.h" +#include "SkAnimator.h" +#include "SkWidgetViews.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +//see SkProgressBarView.cpp +//#include "SkWidgetViews.cpp" + +SkScrollBarView::SkScrollBarView() +{ + fAnim.setHostEventSink(this); + init_skin_anim(kScroll_SkinEnum, &fAnim); + + fTotalLength = 0; + fStartPoint = 0; + fShownLength = 0; + + this->adjust(); +} + +void SkScrollBarView::setStart(unsigned start) +{ + if ((int)start < 0) + start = 0; + + if (fStartPoint != start) + { + fStartPoint = start; + this->adjust(); + } +} + +void SkScrollBarView::setShown(unsigned shown) +{ + if ((int)shown < 0) + shown = 0; + + if (fShownLength != shown) + { + fShownLength = shown; + this->adjust(); + } +} + +void SkScrollBarView::setTotal(unsigned total) +{ + if ((int)total < 0) + total = 0; + + if (fTotalLength != total) + { + fTotalLength = total; + this->adjust(); + } +} + +/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int32_t value; + if (dom.findS32(node, "total", &value)) + this->setTotal(value); + if (dom.findS32(node, "shown", &value)) + this->setShown(value); +} + +/*virtual*/ void SkScrollBarView::onSizeChange() +{ + this->INHERITED::onSizeChange(); + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); +} + +/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas) +{ + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(NULL); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } +} + +/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Inval)) + { + this->inval(NULL); + return true; + } + if (evt.isType("recommendDim")) + { + SkScalar width; + + if (evt.findScalar("x", &width)) + this->setWidth(width); + return true; + } + + return this->INHERITED::onEvent(evt); +} + +void SkScrollBarView::adjust() +{ + int total = fTotalLength; + int start = fStartPoint; + int shown = fShownLength; + int hideBar = 0; + + if (total <= 0 || shown <= 0 || shown >= total) // no bar to show + { + total = 1; // avoid divide-by-zero. should be done by skin/script + hideBar = 1; // signal we don't want a thumb + } + else + { + if (start + shown > total) + start = total - shown; + } + + SkEvent e("user"); + e.setString("id", "adjustScrollBar"); + e.setScalar("_totalLength", SkIntToScalar(total)); + e.setScalar("_startPoint", SkIntToScalar(start)); + e.setScalar("_shownLength", SkIntToScalar(shown)); +// e.setS32("hideBar", hideBar); + fAnim.doUserEvent(e); +} + diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp new file mode 100644 index 0000000000..ae2e9e81a5 --- /dev/null +++ b/src/views/SkStackViewLayout.cpp @@ -0,0 +1,262 @@ +#include "SkStackViewLayout.h" + +SkStackViewLayout::SkStackViewLayout() +{ + fMargin.set(0, 0, 0, 0); + fSpacer = 0; + fOrient = kHorizontal_Orient; + fPack = kStart_Pack; + fAlign = kStart_Align; + fRound = false; +} + +void SkStackViewLayout::setOrient(Orient ori) +{ + SkASSERT((unsigned)ori < kOrientCount); + fOrient = SkToU8(ori); +} + +void SkStackViewLayout::getMargin(SkRect* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStackViewLayout::setMargin(const SkRect& margin) +{ + fMargin = margin; +} + +void SkStackViewLayout::setSpacer(SkScalar spacer) +{ + fSpacer = spacer; +} + +void SkStackViewLayout::setPack(Pack pack) +{ + SkASSERT((unsigned)pack < kPackCount); + fPack = SkToU8(pack); +} + +void SkStackViewLayout::setAlign(Align align) +{ + SkASSERT((unsigned)align < kAlignCount); + fAlign = SkToU8(align); +} + +void SkStackViewLayout::setRound(bool r) +{ + fRound = SkToU8(r); +} + +//////////////////////////////////////////////////////////////////////////////// + +typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit); +typedef SkScalar (SkView::*GetSizeProc)() const; +typedef void (SkView::*SetLocProc)(SkScalar coord); +typedef void (SkView::*SetSizeProc)(SkScalar coord); + +static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } +static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); } +static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; } +static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; } + +/* Measure the main-dimension for all the children. If a child is marked flex in that direction + ignore its current value but increment the counter for flexChildren +*/ +static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count, + uint32_t flexMask, int* flexCount) +{ + SkView::B2FIter iter(parent); + SkView* child; + SkScalar limit = 0; + int n = 0, flex = 0; + + while ((child = iter.next()) != NULL) + { + n += 1; + if (child->getFlags() & flexMask) + flex += 1; + else + limit += (child->*sizeProc)(); + } + if (count) + *count = n; + if (flexCount) + *flexCount = flex; + return limit; +} + +void SkStackViewLayout::onLayoutChildren(SkView* parent) +{ + static AlignProc gAlignProcs[] = { + left_align_proc, + center_align_proc, + right_align_proc, + fill_align_proc + }; + + SkScalar startM, endM, crossStartM, crossLimit; + GetSizeProc mainGetSizeP, crossGetSizeP; + SetLocProc mainLocP, crossLocP; + SetSizeProc mainSetSizeP, crossSetSizeP; + SkView::Flag_Mask flexMask; + + if (fOrient == kHorizontal_Orient) + { + startM = fMargin.fLeft; + endM = fMargin.fRight; + crossStartM = fMargin.fTop; + crossLimit = -fMargin.fTop - fMargin.fBottom; + + mainGetSizeP = &SkView::width; + crossGetSizeP = &SkView::height; + mainLocP = &SkView::setLocX; + crossLocP = &SkView::setLocY; + + mainSetSizeP = &SkView::setWidth; + crossSetSizeP = &SkView::setHeight; + + flexMask = SkView::kFlexH_Mask; + } + else + { + startM = fMargin.fTop; + endM = fMargin.fBottom; + crossStartM = fMargin.fLeft; + crossLimit = -fMargin.fLeft - fMargin.fRight; + + mainGetSizeP = &SkView::height; + crossGetSizeP = &SkView::width; + mainLocP = &SkView::setLocY; + crossLocP = &SkView::setLocX; + + mainSetSizeP = &SkView::setHeight; + crossSetSizeP = &SkView::setWidth; + + flexMask = SkView::kFlexV_Mask; + } + crossLimit += (parent->*crossGetSizeP)(); + if (fAlign != kStretch_Align) + crossSetSizeP = NULL; + + int childCount, flexCount; + SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount); + + if (childCount == 0) + return; + + childLimit += (childCount - 1) * fSpacer; + + SkScalar parentLimit = (parent->*mainGetSizeP)() - startM - endM; + SkScalar pos = startM + gAlignProcs[fPack](childLimit, parentLimit); + SkScalar flexAmount = 0; + SkView::B2FIter iter(parent); + SkView* child; + + if (flexCount > 0 && parentLimit > childLimit) + flexAmount = (parentLimit - childLimit) / flexCount; + + while ((child = iter.next()) != NULL) + { + if (fRound) + pos = SkIntToScalar(SkScalarRound(pos)); + (child->*mainLocP)(pos); + SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit); + if (fRound) + crossLoc = SkIntToScalar(SkScalarRound(crossLoc)); + (child->*crossLocP)(crossLoc); + + if (crossSetSizeP) + (child->*crossSetSizeP)(crossLimit); + if (child->getFlags() & flexMask) + (child->*mainSetSizeP)(flexAmount); + pos += (child->*mainGetSizeP)() + fSpacer; + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif + +void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + int index; + SkScalar value[4]; + + if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0) + this->setOrient((Orient)index); + else + assert_no_attr(dom, node, "orient"); + + if (dom.findScalars(node, "margin", value, 4)) + { + SkRect margin; + margin.set(value[0], value[1], value[2], value[3]); + this->setMargin(margin); + } + else + assert_no_attr(dom, node, "margin"); + + if (dom.findScalar(node, "spacer", value)) + this->setSpacer(value[0]); + else + assert_no_attr(dom, node, "spacer"); + + if ((index = dom.findList(node, "pack", "start,center,end")) >= 0) + this->setPack((Pack)index); + else + assert_no_attr(dom, node, "pack"); + + if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0) + this->setAlign((Align)index); + else + assert_no_attr(dom, node, "align"); +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +SkFillViewLayout::SkFillViewLayout() +{ + fMargin.setEmpty(); +} + +void SkFillViewLayout::getMargin(SkRect* r) const +{ + if (r) + *r = fMargin; +} + +void SkFillViewLayout::setMargin(const SkRect& margin) +{ + fMargin = margin; +} + +void SkFillViewLayout::onLayoutChildren(SkView* parent) +{ + SkView::B2FIter iter(parent); + SkView* child; + + while ((child = iter.next()) != NULL) + { + child->setLoc(fMargin.fLeft, fMargin.fTop); + child->setSize( parent->width() - fMargin.fRight - fMargin.fLeft, + parent->height() - fMargin.fBottom - fMargin.fTop); + } +} + +void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4); +} + diff --git a/src/views/SkStaticTextView.cpp b/src/views/SkStaticTextView.cpp new file mode 100644 index 0000000000..8fb8bc12a5 --- /dev/null +++ b/src/views/SkStaticTextView.cpp @@ -0,0 +1,177 @@ +#include "SkWidgetViews.h" +#include "SkTextBox.h" + +#ifdef SK_DEBUG +static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) +{ + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); +} +#else + #define assert_no_attr(dom, node, attr) +#endif + +SkStaticTextView::SkStaticTextView() +{ + fMargin.set(0, 0); + fMode = kFixedSize_Mode; + fSpacingAlign = SkTextBox::kStart_SpacingAlign; + +// init_skin_paint(kStaticText_SkinEnum, &fPaint); +} + +SkStaticTextView::~SkStaticTextView() +{ +} + +void SkStaticTextView::computeSize() +{ + if (fMode == kAutoWidth_Mode) + { + SkScalar width = fPaint.measureText(fText.c_str(), fText.size()); + this->setWidth(width + fMargin.fX * 2); + } + else if (fMode == kAutoHeight_Mode) + { + SkScalar width = this->width() - fMargin.fX * 2; + int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; + + this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2); + } +} + +void SkStaticTextView::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + + if (fMode != mode) + { + fMode = SkToU8(mode); + this->computeSize(); + } +} + +void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) +{ + fSpacingAlign = SkToU8(align); + this->inval(NULL); +} + +void SkStaticTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) +{ + if (fMargin.fX != dx || fMargin.fY != dy) + { + fMargin.set(dx, dy); + this->computeSize(); + this->inval(NULL); + } +} + +size_t SkStaticTextView::getText(SkString* text) const +{ + if (text) + *text = fText; + return fText.size(); +} + +size_t SkStaticTextView::getText(char text[]) const +{ + if (text) + memcpy(text, fText.c_str(), fText.size()); + return fText.size(); +} + +void SkStaticTextView::setText(const SkString& text) +{ + this->setText(text.c_str(), text.size()); +} + +void SkStaticTextView::setText(const char text[]) +{ + if (text == NULL) + text = ""; + this->setText(text, strlen(text)); +} + +void SkStaticTextView::setText(const char text[], size_t len) +{ + if (!fText.equals(text, len)) + { + fText.set(text, len); + this->computeSize(); + this->inval(NULL); + } +} + +void SkStaticTextView::getPaint(SkPaint* paint) const +{ + if (paint) + *paint = fPaint; +} + +void SkStaticTextView::setPaint(const SkPaint& paint) +{ + if (fPaint != paint) + { + fPaint = paint; + this->computeSize(); + this->inval(NULL); + } +} + +void SkStaticTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.isEmpty()) + return; + + SkTextBox box; + + box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); + box.setSpacingAlign(this->getSpacingAlign()); + box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); + box.draw(canvas, fText.c_str(), fText.size(), fPaint); +} + +void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ +#if 0 + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) + this->setMode((Mode)index); + else + assert_no_attr(dom, node, "mode"); + + if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) + this->setSpacingAlign((SkTextBox::SpacingAlign)index); + else + assert_no_attr(dom, node, "spacing-align"); + + SkScalar s[2]; + if (dom.findScalars(node, "margin", s, 2)) + this->setMargin(s[0], s[1]); + else + assert_no_attr(dom, node, "margin"); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + if ((node = dom.getFirstChild(node, "paint")) != NULL && + (node = dom.getFirstChild(node, "screenplay")) != NULL) + { + inflate_paint(dom, node, &fPaint); + } +#endif +} + diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp new file mode 100644 index 0000000000..4576ce67c0 --- /dev/null +++ b/src/views/SkTagList.cpp @@ -0,0 +1,71 @@ +/* libs/graphics/views/SkTagList.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTagList.h" + +SkTagList::~SkTagList() +{ +} + +SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + while (rec != NULL) + { + if (rec->fTag == tag) + break; + rec = rec->fNext; + } + return rec; +} + +void SkTagList::DeleteTag(SkTagList** head, U8CPU tag) +{ + SkASSERT(tag < kSkTagListCount); + + SkTagList* rec = *head; + SkTagList* prev = NULL; + + while (rec != NULL) + { + SkTagList* next = rec->fNext; + + if (rec->fTag == tag) + { + if (prev) + prev->fNext = next; + else + *head = next; + delete rec; + break; + } + prev = rec; + rec = next; + } +} + +void SkTagList::DeleteAll(SkTagList* rec) +{ + while (rec) + { + SkTagList* next = rec->fNext; + delete rec; + rec = next; + } +} + diff --git a/src/views/SkTagList.h b/src/views/SkTagList.h new file mode 100644 index 0000000000..5f428e4022 --- /dev/null +++ b/src/views/SkTagList.h @@ -0,0 +1,51 @@ +/* libs/graphics/views/SkTagList.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkTagList_DEFINED +#define SkTagList_DEFINED + +#include "SkTypes.h" + +enum SkTagListEnum { + kListeners_SkTagList, + kViewLayout_SkTagList, + kViewArtist_SkTagList, + + kSkTagListCount +}; + +struct SkTagList { + SkTagList* fNext; + uint16_t fExtra16; + uint8_t fExtra8; + uint8_t fTag; + + SkTagList(U8CPU tag) : fTag(SkToU8(tag)) + { + SkASSERT(tag < kSkTagListCount); + fNext = NULL; + fExtra16 = 0; + fExtra8 = 0; + } + virtual ~SkTagList(); + + static SkTagList* Find(SkTagList* head, U8CPU tag); + static void DeleteTag(SkTagList** headptr, U8CPU tag); + static void DeleteAll(SkTagList* head); +}; + +#endif diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp new file mode 100644 index 0000000000..0e31ac68e4 --- /dev/null +++ b/src/views/SkTextBox.cpp @@ -0,0 +1,237 @@ +/* libs/graphics/views/SkTextBox.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "SkTextBox.h" +#include "../core/SkGlyphCache.h" +#include "SkUtils.h" +#include "SkAutoKern.h" + +static inline int is_ws(int c) +{ + return !((c - 1) >> 5); +} + +static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin) +{ + const char* start = text; + + SkAutoGlyphCache ac(paint, NULL); + SkGlyphCache* cache = ac.getCache(); + SkFixed w = 0; + SkFixed limit = SkScalarToFixed(margin); + SkAutoKern autokern; + + const char* word_start = text; + int prevWS = true; + + while (text < stop) + { + const char* prevText = text; + SkUnichar uni = SkUTF8_NextUnichar(&text); + int currWS = is_ws(uni); + const SkGlyph& glyph = cache->getUnicharMetrics(uni); + + if (!currWS && prevWS) + word_start = prevText; + prevWS = currWS; + + w += autokern.adjust(glyph) + glyph.fAdvanceX; + if (w > limit) + { + if (currWS) // eat the rest of the whitespace + { + while (text < stop && is_ws(SkUTF8_ToUnichar(text))) + text += SkUTF8_CountUTF8Bytes(text); + } + else // backup until a whitespace (or 1 char) + { + if (word_start == start) + { + if (prevText > start) + text = prevText; + } + else + text = word_start; + } + break; + } + } + return text - start; +} + +int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width) +{ + const char* stop = text + len; + int count = 0; + + if (width > 0) + { + do { + count += 1; + text += linebreak(text, stop, paint, width); + } while (text < stop); + } + return count; +} + +////////////////////////////////////////////////////////////////////////////// + +SkTextBox::SkTextBox() +{ + fBox.setEmpty(); + fSpacingMul = SK_Scalar1; + fSpacingAdd = 0; + fMode = kLineBreak_Mode; + fSpacingAlign = kStart_SpacingAlign; +} + +void SkTextBox::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + fMode = SkToU8(mode); +} + +void SkTextBox::setSpacingAlign(SpacingAlign align) +{ + SkASSERT((unsigned)align < kSpacingAlignCount); + fSpacingAlign = SkToU8(align); +} + +void SkTextBox::getBox(SkRect* box) const +{ + if (box) + *box = fBox; +} + +void SkTextBox::setBox(const SkRect& box) +{ + fBox = box; +} + +void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) +{ + fBox.set(left, top, right, bottom); +} + +void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const +{ + if (mul) + *mul = fSpacingMul; + if (add) + *add = fSpacingAdd; +} + +void SkTextBox::setSpacing(SkScalar mul, SkScalar add) +{ + fSpacingMul = mul; + fSpacingAdd = add; +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint) +{ + SkASSERT(canvas && &paint && (text || len == 0)); + + SkScalar marginWidth = fBox.width(); + + if (marginWidth <= 0 || len == 0) + return; + + const char* textStop = text + len; + + SkScalar x, y, scaledSpacing, height, fontHeight; + SkPaint::FontMetrics metrics; + + switch (paint.getTextAlign()) { + case SkPaint::kLeft_Align: + x = 0; + break; + case SkPaint::kCenter_Align: + x = SkScalarHalf(marginWidth); + break; + default: + x = marginWidth; + break; + } + x += fBox.fLeft; + + fontHeight = paint.getFontMetrics(&metrics); + scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd; + height = fBox.height(); + + // compute Y position for first line + { + SkScalar textHeight = fontHeight; + + if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign) + { + int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth); + SkASSERT(count > 0); + textHeight += scaledSpacing * (count - 1); + } + + switch (fSpacingAlign) { + case kStart_SpacingAlign: + y = 0; + break; + case kCenter_SpacingAlign: + y = SkScalarHalf(height - textHeight); + break; + default: + SkASSERT(fSpacingAlign == kEnd_SpacingAlign); + y = height - textHeight; + break; + } + y += fBox.fTop - metrics.fAscent; + } + + for (;;) + { + len = linebreak(text, textStop, paint, marginWidth); + if (y + metrics.fDescent + metrics.fLeading > 0) + canvas->drawText(text, len, x, y, paint); + text += len; + if (text >= textStop) + break; + y += scaledSpacing; + if (y + metrics.fAscent >= height) + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) { + fText = text; + fLen = len; + fPaint = &paint; +} + +void SkTextBox::draw(SkCanvas* canvas) { + this->draw(canvas, fText, fLen, *fPaint); +} + +int SkTextBox::countLines() const { + return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width()); +} + +SkScalar SkTextBox::getTextHeight() const { + SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd; + return this->countLines() * spacing; +} + diff --git a/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp new file mode 100644 index 0000000000..1732176983 --- /dev/null +++ b/src/views/SkTouchGesture.cpp @@ -0,0 +1,336 @@ +/* + Copyright 2010 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#include "SkTouchGesture.h" +#include "SkMatrix.h" +#include "SkTime.h" + +#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER true + +static const float MAX_FLING_SPEED = 1500; + +static float pin_max_fling(float speed) { + if (speed > MAX_FLING_SPEED) { + speed = MAX_FLING_SPEED; + } + return speed; +} + +static double getseconds() { + return SkTime::GetMSecs() * 0.001; +} + +// returns +1 or -1, depending on the sign of x +// returns +1 if x is zero +static SkScalar SkScalarSign(SkScalar x) { + SkScalar sign = SK_Scalar1; + if (x < 0) { + sign = -sign; + } + return sign; +} + +static void unit_axis_align(SkVector* unit) { + const SkScalar TOLERANCE = SkDoubleToScalar(0.15); + if (SkScalarAbs(unit->fX) < TOLERANCE) { + unit->fX = 0; + unit->fY = SkScalarSign(unit->fY); + } else if (SkScalarAbs(unit->fY) < TOLERANCE) { + unit->fX = SkScalarSign(unit->fX); + unit->fY = 0; + } +} + +void SkFlingState::reset(float sx, float sy) { + fActive = true; + fDirection.set(sx, sy); + fSpeed0 = SkPoint::Normalize(&fDirection); + fSpeed0 = pin_max_fling(fSpeed0); + fTime0 = getseconds(); + + unit_axis_align(&fDirection); +// printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY); +} + +bool SkFlingState::evaluateMatrix(SkMatrix* matrix) { + if (!fActive) { + return false; + } + + const float t = (float)(getseconds() - fTime0); + const float MIN_SPEED = 2; + const float K0 = 5; + const float K1 = 0.02f; + const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1); + if (speed <= MIN_SPEED) { + fActive = false; + return false; + } + float dist = (fSpeed0 - speed) / K0; + +// printf("---- time %g speed %g dist %g\n", t, speed, dist); + float tx = fDirection.fX * dist; + float ty = fDirection.fY * dist; + if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) { + tx = (float)sk_float_round2int(tx); + ty = (float)sk_float_round2int(ty); + } + matrix->setTranslate(tx, ty); +// printf("---- evaluate (%g %g)\n", tx, ty); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const SkMSec MAX_DBL_TAP_INTERVAL = 300; +static const float MAX_DBL_TAP_DISTANCE = 100; +static const float MAX_JITTER_RADIUS = 2; + +// if true, then ignore the touch-move, 'cause its probably just jitter +static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) { + return sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS && + sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTouchGesture::SkTouchGesture() { + this->reset(); +} + +SkTouchGesture::~SkTouchGesture() { +} + +void SkTouchGesture::reset() { + fTouches.reset(); + fState = kEmpty_State; + fLocalM.reset(); + fGlobalM.reset(); + + fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL; + fLastUpP.set(0, 0); +} + +void SkTouchGesture::flushLocalM() { + fGlobalM.postConcat(fLocalM); + fLocalM.reset(); +} + +const SkMatrix& SkTouchGesture::localM() { + if (fFlinger.isActive()) { + if (!fFlinger.evaluateMatrix(&fLocalM)) { + this->flushLocalM(); + } + } + return fLocalM; +} + +void SkTouchGesture::appendNewRec(void* owner, float x, float y) { + Rec* rec = fTouches.append(); + rec->fOwner = owner; + rec->fStartX = rec->fPrevX = rec->fLastX = x; + rec->fStartY = rec->fPrevY = rec->fLastY = y; + rec->fLastT = rec->fPrevT = SkTime::GetMSecs(); +} + +void SkTouchGesture::touchBegin(void* owner, float x, float y) { +// GrPrintf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y); + + int index = this->findRec(owner); + if (index >= 0) { + this->flushLocalM(); + fTouches.removeShuffle(index); + SkDebugf("---- already exists, removing\n"); + } + + if (fTouches.count() == 2) { + return; + } + + this->flushLocalM(); + fFlinger.stop(); + + this->appendNewRec(owner, x, y); + + switch (fTouches.count()) { + case 1: + fState = kTranslate_State; + break; + case 2: + fState = kZoom_State; + break; + default: + break; + } +} + +int SkTouchGesture::findRec(void* owner) const { + for (int i = 0; i < fTouches.count(); i++) { + if (owner == fTouches[i].fOwner) { + return i; + } + } + return -1; +} + +static float center(float pos0, float pos1) { + return (pos0 + pos1) * 0.5f; +} + +static const float MAX_ZOOM_SCALE = 4; +static const float MIN_ZOOM_SCALE = 0.25f; + +float SkTouchGesture::limitTotalZoom(float scale) const { + // this query works 'cause we know that we're square-scale w/ no skew/rotation + const float curr = fGlobalM[0]; + + if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) { + scale = MAX_ZOOM_SCALE / curr; + } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) { + scale = MIN_ZOOM_SCALE / curr; + } + return scale; +} + +void SkTouchGesture::touchMoved(void* owner, float x, float y) { +// GrPrintf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y); + + SkASSERT(kEmpty_State != fState); + + int index = this->findRec(owner); + if (index < 0) { + // not found, so I guess we should add it... + SkDebugf("---- add missing begin\n"); + this->appendNewRec(owner, x, y); + index = fTouches.count() - 1; + } + + Rec& rec = fTouches[index]; + + // not sure how valuable this is + if (fTouches.count() == 2) { + if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) { +// GrPrintf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y); + return; + } + } + + rec.fPrevX = rec.fLastX; rec.fLastX = x; + rec.fPrevY = rec.fLastY; rec.fLastY = y; + rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs(); + + switch (fTouches.count()) { + case 1: { + float dx = rec.fLastX - rec.fStartX; + float dy = rec.fLastY - rec.fStartY; + dx = (float)sk_float_round2int(dx); + dy = (float)sk_float_round2int(dy); + fLocalM.setTranslate(dx, dy); + } break; + case 2: { + SkASSERT(kZoom_State == fState); + const Rec& rec0 = fTouches[0]; + const Rec& rec1 = fTouches[1]; + + float scale = this->computePinch(rec0, rec1); + scale = this->limitTotalZoom(scale); + + fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX), + -center(rec0.fStartY, rec1.fStartY)); + fLocalM.postScale(scale, scale); + fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX), + center(rec0.fLastY, rec1.fLastY)); + } break; + default: + break; + } +} + +void SkTouchGesture::touchEnd(void* owner) { +// GrPrintf("--- %d touchEnd %p\n", fTouches.count(), owner); + + int index = this->findRec(owner); + if (index < 0) { + SkDebugf("--- not found\n"); + return; + } + + const Rec& rec = fTouches[index]; + if (this->handleDblTap(rec.fLastX, rec.fLastY)) { + return; + } + + // count() reflects the number before we removed the owner + switch (fTouches.count()) { + case 1: { + this->flushLocalM(); + float dx = rec.fLastX - rec.fPrevX; + float dy = rec.fLastY - rec.fPrevY; + float dur = (rec.fLastT - rec.fPrevT) * 0.001f; + if (dur > 0) { + fFlinger.reset(dx / dur, dy / dur); + } + fState = kEmpty_State; + } break; + case 2: + this->flushLocalM(); + SkASSERT(kZoom_State == fState); + fState = kEmpty_State; + break; + default: + SkASSERT(kZoom_State == fState); + break; + } + + fTouches.removeShuffle(index); +} + +float SkTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) { + double dx = rec0.fStartX - rec1.fStartX; + double dy = rec0.fStartY - rec1.fStartY; + double dist0 = sqrt(dx*dx + dy*dy); + + dx = rec0.fLastX - rec1.fLastX; + dy = rec0.fLastY - rec1.fLastY; + double dist1 = sqrt(dx*dx + dy*dy); + + double scale = dist1 / dist0; + return (float)scale; +} + +bool SkTouchGesture::handleDblTap(float x, float y) { + bool found = false; + SkMSec now = SkTime::GetMSecs(); + if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) { + if (SkPoint::Length(fLastUpP.fX - x, + fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) { + fFlinger.stop(); + fLocalM.reset(); + fGlobalM.reset(); + fTouches.reset(); + fState = kEmpty_State; + found = true; + } + } + + fLastUpT = now; + fLastUpP.set(x, y); + return found; +} + + diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp new file mode 100644 index 0000000000..1cd63394db --- /dev/null +++ b/src/views/SkView.cpp @@ -0,0 +1,793 @@ +#include "SkView.h" +#include "SkCanvas.h" + +//////////////////////////////////////////////////////////////////////// + +SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags)) +{ + fWidth = fHeight = 0; + fLoc.set(0, 0); + fParent = fFirstChild = fNextSibling = fPrevSibling = NULL; + + fContainsFocus = 0; +} + +SkView::~SkView() +{ + this->detachAllChildren(); +} + +void SkView::setFlags(uint32_t flags) +{ + SkASSERT((flags & ~kAllFlagMasks) == 0); + + uint32_t diff = fFlags ^ flags; + + if (diff & kVisible_Mask) + this->inval(NULL); + + fFlags = SkToU8(flags); + + if (diff & kVisible_Mask) + { + this->inval(NULL); + } +} + +void SkView::setVisibleP(bool pred) +{ + this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift)); +} + +void SkView::setEnabledP(bool pred) +{ + this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift)); +} + +void SkView::setFocusableP(bool pred) +{ + this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift)); +} + +void SkView::setClipToBounds(bool pred) { + this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift)); +} + +void SkView::setSize(SkScalar width, SkScalar height) +{ + width = SkMaxScalar(0, width); + height = SkMaxScalar(0, height); + + if (fWidth != width || fHeight != height) + { + this->inval(NULL); + fWidth = width; + fHeight = height; + this->inval(NULL); + this->onSizeChange(); + this->invokeLayout(); + } +} + +void SkView::setLoc(SkScalar x, SkScalar y) +{ + if (fLoc.fX != x || fLoc.fY != y) + { + this->inval(NULL); + fLoc.set(x, y); + this->inval(NULL); + } +} + +void SkView::offset(SkScalar dx, SkScalar dy) +{ + if (dx || dy) + this->setLoc(fLoc.fX + dx, fLoc.fY + dy); +} + +void SkView::draw(SkCanvas* canvas) +{ + if (fWidth && fHeight && this->isVisible()) + { + SkRect r; + r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight); + if (this->isClipToBounds() && + canvas->quickReject(r, SkCanvas::kBW_EdgeType)) { + return; + } + + SkAutoCanvasRestore as(canvas, true); + + if (this->isClipToBounds()) { + canvas->clipRect(r); + } + canvas->translate(fLoc.fX, fLoc.fY); + + if (fParent) { + fParent->beforeChild(this, canvas); + } + + int sc = canvas->save(); + this->onDraw(canvas); + canvas->restoreToCount(sc); + + if (fParent) { + fParent->afterChild(this, canvas); + } + + B2FIter iter(this); + SkView* child; + + SkCanvas* childCanvas = this->beforeChildren(canvas); + + while ((child = iter.next()) != NULL) + child->draw(childCanvas); + + this->afterChildren(canvas); + } +} + +void SkView::inval(SkRect* rect) { + SkView* view = this; + SkRect storage; + + for (;;) { + if (!view->isVisible()) { + return; + } + if (view->isClipToBounds()) { + SkRect bounds; + view->getLocalBounds(&bounds); + if (rect && !bounds.intersect(*rect)) { + return; + } + storage = bounds; + rect = &storage; + } + if (view->handleInval(rect)) { + return; + } + + SkView* parent = view->fParent; + if (parent == NULL) { + return; + } + + if (rect) { + rect->offset(view->fLoc.fX, view->fLoc.fY); + } + view = parent; + } +} + +//////////////////////////////////////////////////////////////////////////// + +bool SkView::setFocusView(SkView* fv) +{ + SkView* view = this; + + do { + if (view->onSetFocusView(fv)) + return true; + } while ((view = view->fParent) != NULL); + return false; +} + +SkView* SkView::getFocusView() const +{ + SkView* focus = NULL; + const SkView* view = this; + do { + if (view->onGetFocusView(&focus)) + break; + } while ((view = view->fParent) != NULL); + return focus; +} + +bool SkView::hasFocus() const +{ + return this == this->getFocusView(); +} + +bool SkView::acceptFocus() +{ + return this->isFocusable() && this->setFocusView(this); +} + +/* + Try to give focus to this view, or its children +*/ +SkView* SkView::acceptFocus(FocusDirection dir) +{ + if (dir == kNext_FocusDirection) + { + if (this->acceptFocus()) + return this; + + B2FIter iter(this); + SkView* child, *focus; + while ((child = iter.next()) != NULL) + if ((focus = child->acceptFocus(dir)) != NULL) + return focus; + } + else // prev + { + F2BIter iter(this); + SkView* child, *focus; + while ((child = iter.next()) != NULL) + if ((focus = child->acceptFocus(dir)) != NULL) + return focus; + + if (this->acceptFocus()) + return this; + } + + return NULL; +} + +SkView* SkView::moveFocus(FocusDirection dir) +{ + SkView* focus = this->getFocusView(); + + if (focus == NULL) + { // start with the root + focus = this; + while (focus->fParent) + focus = focus->fParent; + } + + SkView* child, *parent; + + if (dir == kNext_FocusDirection) + { + parent = focus; + child = focus->fFirstChild; + if (child) + goto FIRST_CHILD; + else + goto NEXT_SIB; + + do { + while (child != parent->fFirstChild) + { + FIRST_CHILD: + if ((focus = child->acceptFocus(dir)) != NULL) + return focus; + child = child->fNextSibling; + } + NEXT_SIB: + child = parent->fNextSibling; + parent = parent->fParent; + } while (parent != NULL); + } + else // prevfocus + { + parent = focus->fParent; + if (parent == NULL) // we're the root + return focus->acceptFocus(dir); + else + { + child = focus; + while (parent) + { + while (child != parent->fFirstChild) + { + child = child->fPrevSibling; + if ((focus = child->acceptFocus(dir)) != NULL) + return focus; + } + if (parent->acceptFocus()) + return parent; + + child = parent; + parent = parent->fParent; + } + } + } + return NULL; +} + +void SkView::onFocusChange(bool gainFocusP) +{ + this->inval(NULL); +} + +//////////////////////////////////////////////////////////////////////////// + +SkView::Click::Click(SkView* target) +{ + SkASSERT(target); + fTargetID = target->getSinkID(); + fType = NULL; + fWeOwnTheType = false; +} + +SkView::Click::~Click() +{ + this->resetType(); +} + +void SkView::Click::resetType() +{ + if (fWeOwnTheType) + { + sk_free(fType); + fWeOwnTheType = false; + } + fType = NULL; +} + +bool SkView::Click::isType(const char type[]) const +{ + const char* t = fType; + + if (type == t) + return true; + + if (type == NULL) + type = ""; + if (t == NULL) + t = ""; + return !strcmp(t, type); +} + +void SkView::Click::setType(const char type[]) +{ + this->resetType(); + fType = (char*)type; +} + +void SkView::Click::copyType(const char type[]) +{ + if (fType != type) + { + this->resetType(); + if (type) + { + size_t len = strlen(type) + 1; + fType = (char*)sk_malloc_throw(len); + memcpy(fType, type, len); + fWeOwnTheType = true; + } + } +} + +SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y) +{ + if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) { + return false; + } + + if (this->onSendClickToChildren(x, y)) { + F2BIter iter(this); + SkView* child; + + while ((child = iter.next()) != NULL) + { + Click* click = child->findClickHandler(x - child->fLoc.fX, + y - child->fLoc.fY); + if (click) { + return click; + } + } + } + return this->onFindClickHandler(x, y); +} + +void SkView::DoClickDown(Click* click, int x, int y) +{ + SkASSERT(click); + + SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); + if (target == NULL) + return; + + click->fIOrig.set(x, y); + click->fICurr = click->fIPrev = click->fIOrig; + + click->fOrig.iset(x, y); + target->globalToLocal(&click->fOrig); + click->fPrev = click->fCurr = click->fOrig; + + click->fState = Click::kDown_State; + target->onClick(click); +} + +void SkView::DoClickMoved(Click* click, int x, int y) +{ + SkASSERT(click); + + SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); + if (target == NULL) + return; + + click->fIPrev = click->fICurr; + click->fICurr.set(x, y); + + click->fPrev = click->fCurr; + click->fCurr.iset(x, y); + target->globalToLocal(&click->fCurr); + + click->fState = Click::kMoved_State; + target->onClick(click); +} + +void SkView::DoClickUp(Click* click, int x, int y) +{ + SkASSERT(click); + + SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID); + if (target == NULL) + return; + + click->fIPrev = click->fICurr; + click->fICurr.set(x, y); + + click->fPrev = click->fCurr; + click->fCurr.iset(x, y); + target->globalToLocal(&click->fCurr); + + click->fState = Click::kUp_State; + target->onClick(click); +} + +////////////////////////////////////////////////////////////////////// + +void SkView::invokeLayout() { + SkView::Layout* layout = this->getLayout(); + + if (layout) { + layout->layoutChildren(this); + } +} + +void SkView::onDraw(SkCanvas* canvas) { + Artist* artist = this->getArtist(); + + if (artist) { + artist->draw(this, canvas); + } +} + +void SkView::onSizeChange() {} + +bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) { + return true; +} + +SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) { + return NULL; +} + +bool SkView::onClick(Click*) { + return false; +} + +bool SkView::handleInval(const SkRect*) { + return false; +} + +////////////////////////////////////////////////////////////////////// + +void SkView::getLocalBounds(SkRect* bounds) const +{ + if (bounds) + bounds->set(0, 0, fWidth, fHeight); +} + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +void SkView::detachFromParent_NoLayout() +{ + if (fParent == NULL) + return; + + if (fContainsFocus) + (void)this->setFocusView(NULL); + + this->inval(NULL); + + SkView* next = NULL; + + if (fNextSibling != this) // do we have any siblings + { + fNextSibling->fPrevSibling = fPrevSibling; + fPrevSibling->fNextSibling = fNextSibling; + next = fNextSibling; + } + + if (fParent->fFirstChild == this) + fParent->fFirstChild = next; + + fParent = fNextSibling = fPrevSibling = NULL; + + this->unref(); +} + +void SkView::detachFromParent() +{ + SkView* parent = fParent; + + if (parent) + { + this->detachFromParent_NoLayout(); + parent->invokeLayout(); + } +} + +SkView* SkView::attachChildToBack(SkView* child) +{ + SkASSERT(child != this); + + if (child == NULL || fFirstChild == child) + goto DONE; + + child->ref(); + child->detachFromParent_NoLayout(); + + if (fFirstChild == NULL) + { + child->fNextSibling = child; + child->fPrevSibling = child; + } + else + { + child->fNextSibling = fFirstChild; + child->fPrevSibling = fFirstChild->fPrevSibling; + fFirstChild->fPrevSibling->fNextSibling = child; + fFirstChild->fPrevSibling = child; + } + + fFirstChild = child; + child->fParent = this; + child->inval(NULL); + + this->invokeLayout(); +DONE: + return child; +} + +SkView* SkView::attachChildToFront(SkView* child) +{ + SkASSERT(child != this); + + if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child)) + goto DONE; + + child->ref(); + child->detachFromParent_NoLayout(); + + if (fFirstChild == NULL) + { + fFirstChild = child; + child->fNextSibling = child; + child->fPrevSibling = child; + } + else + { + child->fNextSibling = fFirstChild; + child->fPrevSibling = fFirstChild->fPrevSibling; + fFirstChild->fPrevSibling->fNextSibling = child; + fFirstChild->fPrevSibling = child; + } + + child->fParent = this; + child->inval(NULL); + + this->invokeLayout(); +DONE: + return child; +} + +void SkView::detachAllChildren() +{ + while (fFirstChild) + fFirstChild->detachFromParent_NoLayout(); +} + +void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const +{ + SkASSERT(this); + + if (local) + { + const SkView* view = this; + while (view) + { + x -= view->fLoc.fX; + y -= view->fLoc.fY; + view = view->fParent; + } + local->set(x, y); + } +} + +////////////////////////////////////////////////////////////////// + +/* Even if the subclass overrides onInflate, they should always be + sure to call the inherited method, so that we get called. +*/ +void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkScalar x, y; + + x = this->locX(); + y = this->locY(); + (void)dom.findScalar(node, "x", &x); + (void)dom.findScalar(node, "y", &y); + this->setLoc(x, y); + + x = this->width(); + y = this->height(); + (void)dom.findScalar(node, "width", &x); + (void)dom.findScalar(node, "height", &y); + this->setSize(x, y); + + // inflate the flags + + static const char* gFlagNames[] = { + "visible", "enabled", "focusable", "flexH", "flexV" + }; + SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount); + + bool b; + uint32_t flags = this->getFlags(); + for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++) + if (dom.findBool(node, gFlagNames[i], &b)) + flags = SkSetClearShift(flags, b, i); + this->setFlags(flags); +} + +void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->onInflate(dom, node); +} + +void SkView::onPostInflate(const SkTDict<SkView*>&) +{ + // override in subclass as needed +} + +void SkView::postInflate(const SkTDict<SkView*>& dict) +{ + this->onPostInflate(dict); + + B2FIter iter(this); + SkView* child; + while ((child = iter.next()) != NULL) + child->postInflate(dict); +} + +////////////////////////////////////////////////////////////////// + +SkView* SkView::sendEventToParents(const SkEvent& evt) +{ + SkView* parent = fParent; + + while (parent) + { + if (parent->doEvent(evt)) + return parent; + parent = parent->fParent; + } + return NULL; +} + +SkView* SkView::sendQueryToParents(SkEvent* evt) { + SkView* parent = fParent; + + while (parent) { + if (parent->doQuery(evt)) { + return parent; + } + parent = parent->fParent; + } + return NULL; +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +SkView::F2BIter::F2BIter(const SkView* parent) +{ + fFirstChild = parent ? parent->fFirstChild : NULL; + fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL; +} + +SkView* SkView::F2BIter::next() +{ + SkView* curr = fChild; + + if (fChild) + { + if (fChild == fFirstChild) + fChild = NULL; + else + fChild = fChild->fPrevSibling; + } + return curr; +} + +SkView::B2FIter::B2FIter(const SkView* parent) +{ + fFirstChild = parent ? parent->fFirstChild : NULL; + fChild = fFirstChild; +} + +SkView* SkView::B2FIter::next() +{ + SkView* curr = fChild; + + if (fChild) + { + SkView* next = fChild->fNextSibling; + if (next == fFirstChild) + next = NULL; + fChild = next; + } + return curr; +} + +////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +static inline void show_if_nonzero(const char name[], SkScalar value) +{ + if (value) + SkDebugf("%s=\"%g\"", name, value/65536.); +} + +static void tab(int level) +{ + for (int i = 0; i < level; i++) + SkDebugf(" "); +} + +static void dumpview(const SkView* view, int level, bool recurse) +{ + tab(level); + + SkDebugf("<view"); + show_if_nonzero(" x", view->locX()); + show_if_nonzero(" y", view->locY()); + show_if_nonzero(" width", view->width()); + show_if_nonzero(" height", view->height()); + + if (recurse) + { + SkView::B2FIter iter(view); + SkView* child; + bool noChildren = true; + + while ((child = iter.next()) != NULL) + { + if (noChildren) + SkDebugf(">\n"); + noChildren = false; + dumpview(child, level + 1, true); + } + + if (!noChildren) + { + tab(level); + SkDebugf("</view>\n"); + } + else + goto ONELINER; + } + else + { + ONELINER: + SkDebugf(" />\n"); + } +} + +void SkView::dump(bool recurse) const +{ + dumpview(this, 0, recurse); +} + +#endif diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp new file mode 100644 index 0000000000..8f5489de54 --- /dev/null +++ b/src/views/SkViewInflate.cpp @@ -0,0 +1,139 @@ +#include "SkViewInflate.h" +#include "SkView.h" +#include <stdio.h> + +SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc) +{ +} + +SkViewInflate::~SkViewInflate() +{ +} + +void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent) +{ + const char* str = dom.findAttr(node, "id"); + if (str) + fIDs.set(str, parent); + + const SkDOM::Node* child = dom.getFirstChild(node); + while (child) + { + SkView* view = this->createView(dom, child); + if (view) + { + this->rInflate(dom, child, view); + parent->attachChildToFront(view)->unref(); + } + else + { + const char* name = dom.getName(child); + const char* target; + + if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL) + this->addIDStr(&fListenTo, parent, target); + + if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL) + this->addIDStr(&fBroadcastTo, parent, target); + } + child = dom.getNextSibling(child); + } + + parent->setVisibleP(true); + this->inflateView(parent, dom, node); +} + +void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node) +{ + // called after all of view's children have been instantiated. + // this may be overridden by a subclass, to load in layout or other helpers + // they should call through to us (INHERITED) before or after their patch + view->inflate(dom, node); +} + +SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root) +{ + fIDs.reset(); + + if (root == NULL) + { + root = this->createView(dom, node); + if (root == NULL) + { + printf("createView returned NULL on <%s>\n", dom.getName(node)); + return NULL; + } + } + this->rInflate(dom, node, root); + + // resolve listeners and broadcasters + { + SkView* target; + const IDStr* iter = fListenTo.begin(); + const IDStr* stop = fListenTo.end(); + for (; iter < stop; iter++) + { + if (fIDs.find(iter->fStr, &target)) + target->addListenerID(iter->fView->getSinkID()); + } + + iter = fBroadcastTo.begin(); + stop = fBroadcastTo.end(); + for (; iter < stop; iter++) + { + if (fIDs.find(iter->fStr, &target)) + iter->fView->addListenerID(target->getSinkID()); + } + } + + // now that the tree is built, give everyone a shot at the ID dict + root->postInflate(fIDs); + return root; +} + +SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root) +{ + SkDOM dom; + const SkDOM::Node* node = dom.build(xml, len); + + return node ? this->inflate(dom, node, root) : NULL; +} + +SkView* SkViewInflate::findViewByID(const char id[]) const +{ + SkASSERT(id); + SkView* view; + return fIDs.find(id, &view) ? view : NULL; +} + +SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node) +{ + if (!strcmp(dom.getName(node), "view")) + return new SkView; + return NULL; +} + +void SkViewInflate::addIDStr(SkTDArray<IDStr>* list, SkView* view, const char* str) +{ + size_t len = strlen(str) + 1; + IDStr* pair = list->append(); + pair->fView = view; + pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType); + memcpy(pair->fStr, str, len); +} + +#ifdef SK_DEBUG +void SkViewInflate::dump() const +{ + const IDStr* iter = fListenTo.begin(); + const IDStr* stop = fListenTo.end(); + for (; iter < stop; iter++) + SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr); + + iter = fBroadcastTo.begin(); + stop = fBroadcastTo.end(); + for (; iter < stop; iter++) + SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr); +} +#endif + diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp new file mode 100644 index 0000000000..b03ca8c101 --- /dev/null +++ b/src/views/SkViewPriv.cpp @@ -0,0 +1,97 @@ +#include "SkViewPriv.h" + +////////////////////////////////////////////////////////////////////// + +void SkView::Artist::draw(SkView* view, SkCanvas* canvas) +{ + SkASSERT(view && canvas); + this->onDraw(view, canvas); +} + +void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(&dom && node); + this->onInflate(dom, node); +} + +void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + // subclass should override this as needed +} + +SkView::Artist* SkView::getArtist() const +{ + Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList); + SkASSERT(rec == NULL || rec->fArtist != NULL); + + return rec ? rec->fArtist : NULL; +} + +SkView::Artist* SkView::setArtist(Artist* obj) +{ + if (obj == NULL) + { + this->removeTagList(kViewArtist_SkTagList); + } + else // add/replace + { + Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList); + + if (rec) + SkRefCnt_SafeAssign(rec->fArtist, obj); + else + this->addTagList(new Artist_SkTagList(obj)); + } + return obj; +} + +//////////////////////////////////////////////////////////////////////////////// + +void SkView::Layout::layoutChildren(SkView* parent) +{ + SkASSERT(parent); + if (parent->width() > 0 && parent->height() > 0) + this->onLayoutChildren(parent); +} + +void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node) +{ + SkASSERT(&dom && node); + this->onInflate(dom, node); +} + +void SkView::Layout::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + // subclass should override this as needed +} + +SkView::Layout* SkView::getLayout() const +{ + Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList); + SkASSERT(rec == NULL || rec->fLayout != NULL); + + return rec ? rec->fLayout : NULL; +} + +SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow) +{ + if (obj == NULL) + { + this->removeTagList(kViewLayout_SkTagList); + } + else // add/replace + { + Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList); + + if (rec) + SkRefCnt_SafeAssign(rec->fLayout, obj); + else + this->addTagList(new Layout_SkTagList(obj)); + } + + if (invokeLayoutNow) + this->invokeLayout(); + + return obj; +} + diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h new file mode 100644 index 0000000000..06ce59bec6 --- /dev/null +++ b/src/views/SkViewPriv.h @@ -0,0 +1,38 @@ +#ifndef SkViewPriv_DEFINED +#define SkViewPriv_DEFINED + +#include "SkView.h" +#include "SkTagList.h" + +struct Layout_SkTagList : SkTagList { + SkView::Layout* fLayout; + + Layout_SkTagList(SkView::Layout* layout) + : SkTagList(kViewLayout_SkTagList), fLayout(layout) + { + SkASSERT(layout); + layout->ref(); + } + virtual ~Layout_SkTagList() + { + fLayout->unref(); + } +}; + +struct Artist_SkTagList : SkTagList { + SkView::Artist* fArtist; + + Artist_SkTagList(SkView::Artist* artist) + : SkTagList(kViewArtist_SkTagList), fArtist(artist) + { + SkASSERT(artist); + artist->ref(); + } + virtual ~Artist_SkTagList() + { + fArtist->unref(); + } +}; + +#endif + diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp new file mode 100644 index 0000000000..4d055c43ec --- /dev/null +++ b/src/views/SkWidget.cpp @@ -0,0 +1,323 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkInterpolator.h" +#include "SkTime.h" +#include "SkParsePaint.h" + +#if 0 +SkWidgetView::SkWidgetView(U32 flags) : SkView(flags) +{ +} + +SkWidgetView::~SkWidgetView() +{ +} + +const char* SkWidgetView::GetEventType() +{ + return "SkWidgetView"; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class SkTextView::Interp { +public: + Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2) + { + SkScalar x = 0; + fInterp.setKeyFrame(0, now, &x, 0); + x = SK_Scalar1; + if (dir == kBackward_AnimDir) + x = -x; + fInterp.setKeyFrame(1, now + dur, &x); + } + bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint) + { + SkScalar scale; + + if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result) + { + canvas->drawText(newText.c_str(), newText.size(), x, y, paint); + return false; + } + else + { + U8 alpha = paint.getAlpha(); + SkScalar above, below; + (void)paint.measureText(NULL, 0, &above, &below); + SkScalar height = below - above; + SkScalar dy = SkScalarMul(height, scale); + if (scale < 0) + height = -height; + + // draw the old + paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale))); + canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint); + // draw the new + paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale))); + canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint); + // restore the paint + paint.setAlpha(alpha); + return true; + } + } + +private: + SkString fOldText; + SkInterpolator fInterp; +}; + +SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(NULL), fDoInterp(false) +{ + fMargin.set(0, 0); +} + +SkTextView::~SkTextView() +{ + delete fInterp; +} + +void SkTextView::getText(SkString* str) const +{ + if (str) + str->set(fText); +} + +void SkTextView::setText(const char text[], AnimaDir dir) +{ + if (!fText.equals(text)) + { + SkString tmp(text); + this->privSetText(tmp, dir); + } +} + +void SkTextView::setText(const char text[], size_t len, AnimaDir dir) +{ + if (!fText.equals(text)) + { + SkString tmp(text, len); + this->privSetText(tmp, dir); + } +} + +void SkTextView::setText(const SkString& src, AnimaDir dir) +{ + if (fText != src) + this->privSetText(src, dir); +} + +void SkTextView::privSetText(const SkString& src, AnimaDir dir) +{ + SkASSERT(fText != src); + + if (fDoInterp) + { + if (fInterp) + delete fInterp; + fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir); + } + fText = src; + this->inval(NULL); +} + +///////////////////////////////////////////////////////////////// + +void SkTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkTextView::setMargin(const SkPoint& margin) +{ + if (fMargin != margin) + { + fMargin = margin; + this->inval(NULL); + } +} + +void SkTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.size() == 0) + return; + + SkPaint::Align align = fPaint.getTextAlign(); + SkScalar x, y; + + switch (align) { + case SkPaint::kLeft_Align: + x = fMargin.fX; + break; + case SkPaint::kCenter_Align: + x = SkScalarHalf(this->width()); + break; + default: + SkASSERT(align == SkPaint::kRight_Align); + x = this->width() - fMargin.fX; + break; + } + + fPaint.measureText(NULL, 0, &y, NULL); + y = fMargin.fY - y; + + if (fInterp) + { + if (fInterp->draw(canvas, fText, x, y, fPaint)) + this->inval(NULL); + else + { + delete fInterp; + fInterp = NULL; + } + } + else + canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint); +} + +////////////////////////////////////////////////////////////////////////////////////// + +void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + SkPoint margin; + if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2)) + this->setMargin(margin); + (void)dom.findBool(node, "do-interp", &fDoInterp); + + SkPaint_Inflate(&fPaint, dom, node); +} + +////////////////////////////////////////////////////////////////////////////////////// + +SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags) +{ + fValue = 0; + fMax = 0; +} + +static U16 actual_value(U16CPU value, U16CPU max) +{ + return SkToU16(SkMax32(0, SkMin32(value, max))); +} + +void SkSliderView::setMax(U16CPU max) +{ + if (fMax != max) + { + fMax = SkToU16(max); + if (fValue > 0) + this->inval(NULL); + } +} + +void SkSliderView::setValue(U16CPU value) +{ + if (fValue != value) + { + U16 prev = actual_value(fValue, fMax); + U16 next = actual_value(value, fMax); + + fValue = SkToU16(value); + if (prev != next) + { + this->inval(NULL); + + if (this->hasListeners()) + { + SkEvent evt; + + evt.setType(SkWidgetView::GetEventType()); + evt.setFast32(this->getSinkID()); + evt.setS32("sliderValue", next); + this->postToListeners(evt); + } + } + } +} + +#include "SkGradientShader.h" + +static void setgrad(SkPaint* paint, const SkRect& r) +{ + SkPoint pts[2]; + SkColor colors[2]; + +#if 0 + pts[0].set(r.fLeft, r.fTop); + pts[1].set(r.fLeft + r.height(), r.fBottom); +#else + pts[0].set(r.fRight, r.fBottom); + pts[1].set(r.fRight - r.height(), r.fTop); +#endif + colors[0] = SK_ColorBLUE; + colors[1] = SK_ColorWHITE; + + paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kMirror_TileMode))->unref(); +} + +void SkSliderView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + U16CPU value = SkMax32(0, SkMin32(fValue, fMax)); + + SkRect r; + SkPaint p; + + r.set(0, 0, this->width(), this->height()); + + p.setAntiAliasOn(true); + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(SK_Scalar1); + r.inset(SK_Scalar1/2, SK_Scalar1/2); + canvas->drawRect(r, p); + + if (fMax) + { + SkFixed percent = SkFixedDiv(value, fMax); + + r.inset(SK_Scalar1/2, SK_Scalar1/2); + r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent)); + p.setStyle(SkPaint::kFill_Style); + setgrad(&p, r); + canvas->drawRect(r, p); + } + +#if 0 + r.set(0, 0, this->width(), this->height()); + r.inset(SK_Scalar1, SK_Scalar1); + r.inset(r.width()/2, 0); + p.setColor(SK_ColorBLACK); + canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p); +#endif +} + +SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y) +{ + return new Click(this); +} + +bool SkSliderView::onClick(Click* click) +{ + if (fMax) + { + SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2); + percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1)); + this->setValue(SkScalarRound(percent * fMax)); + return true; + } + return false; +} + +#endif + diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp new file mode 100644 index 0000000000..2705307472 --- /dev/null +++ b/src/views/SkWidgetViews.cpp @@ -0,0 +1,405 @@ +#include "SkWidgetViews.h" +#include "SkAnimator.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkStream.h" +#include "SkSystemEventTypes.h" + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif +/* +I have moved this to SkWidgetViews.h +enum SkinEnum { + kButton_SkinEnum, + kProgress_SkinEnum, + kScroll_SkinEnum, + kStaticText_SkinEnum, + + kSkinEnumCount +}; +*/ + +const char* get_skin_enum_path(SkinEnum se) +{ + SkASSERT((unsigned)se < kSkinEnumCount); + + static const char* gSkinPaths[] = { + "common/default/default/skins/border3.xml", + "common/default/default/skins/button.xml", + "common/default/default/skins/progressBar.xml", + "common/default/default/skins/scrollBar.xml", + "common/default/default/skins/statictextpaint.xml" + }; + + return gSkinPaths[se]; +} + +void init_skin_anim(const char path[], SkAnimator* anim) +{ + SkASSERT(path && anim); + + SkFILEStream stream(path); + + if (!stream.isValid()) + { + SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path)); + sk_throw(); + } + + if (!anim->decodeStream(&stream)) + { + SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path)); + sk_throw(); + } +} + +void init_skin_anim(SkinEnum se, SkAnimator* anim) +{ + init_skin_anim(get_skin_enum_path(se), anim); +} + +void init_skin_paint(SkinEnum se, SkPaint* paint) +{ + SkASSERT(paint); + + SkAnimator anim; + SkCanvas canvas; + + init_skin_anim(se, &anim); + anim.draw(&canvas, paint, 0); +} + +void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint) +{ + SkASSERT(paint); + + SkAnimator anim; + SkCanvas canvas; + + if (!anim.decodeDOM(dom, node)) + { + SkDEBUGF(("inflate_paint: decoding dom failed\n")); + SkDEBUGCODE(dom.dump(node);) + sk_throw(); + } + anim.draw(&canvas, paint, 0); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask) +{ +} + +const char* SkWidgetView::getLabel() const +{ + return fLabel.c_str(); +} + +void SkWidgetView::getLabel(SkString* label) const +{ + if (label) + *label = fLabel; +} + +void SkWidgetView::setLabel(const char label[]) +{ + this->setLabel(label, label ? strlen(label) : 0); +} + +void SkWidgetView::setLabel(const char label[], size_t len) +{ + if ((label == NULL && fLabel.size() != 0) || !fLabel.equals(label, len)) + { + SkString tmp(label, len); + + this->onLabelChange(fLabel.c_str(), tmp.c_str()); + fLabel.swap(tmp); + } +} + +void SkWidgetView::setLabel(const SkString& label) +{ + if (fLabel != label) + { + this->onLabelChange(fLabel.c_str(), label.c_str()); + fLabel = label; + } +} + +bool SkWidgetView::postWidgetEvent() +{ + if (!fEvent.isType("")) + { + SkEvent evt(fEvent); // make a copy since onPrepareWidgetEvent may edit the event + + if (this->onPrepareWidgetEvent(&evt)) + { + SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");) + + this->postToListeners(evt); // wonder if this should return true if there are > 0 listeners... + return true; + } + } + return false; +} + +/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* label = dom.findAttr(node, "label"); + if (label) + this->setLabel(label); + + if ((node = dom.getFirstChild(node, "event")) != NULL) + fEvent.inflate(dom, node); +} + +/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[]) +{ + this->inval(NULL); +} + +static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot"; + +/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt) +{ + evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID()); + return true; +} + +SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt) +{ + int32_t sinkID; + + return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey) + { + this->postWidgetEvent(); + return true; + } + return this->INHERITED::onEvent(evt); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState) +{ +} + +void SkCheckButtonView::setCheckState(CheckState state) +{ + SkASSERT((unsigned)state <= kUnknown_CheckState); + + if (fCheckState != state) + { + this->onCheckStateChange(this->getCheckState(), state); + fCheckState = SkToU8(state); + } +} + +/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState) +{ + this->inval(NULL); +} + +/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index = dom.findList(node, "check-state", "off,on,unknown"); + if (index >= 0) + this->setCheckState((CheckState)index); +} + +static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot"; + +/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt) +{ + // could check if we're "disabled", and return false... + + evt->setS32(gCheckStateSlotName, this->getCheckState()); + return true; +} + +bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state) +{ + int32_t state32; + + if (evt.findS32(gCheckStateSlotName, &state32)) + { + if (state) + *state = (CheckState)state32; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkTime.h" +#include <stdio.h> + +class SkAnimButtonView : public SkButtonView { +public: + SkAnimButtonView() + { + fAnim.setHostEventSink(this); + init_skin_anim(kButton_SkinEnum, &fAnim); + } + +protected: + virtual void onLabelChange(const char oldLabel[], const char newLabel[]) + { + this->INHERITED::onLabelChange(oldLabel, newLabel); + + SkEvent evt("user"); + evt.setString("id", "setLabel"); + evt.setString("LABEL", newLabel); + fAnim.doUserEvent(evt); + } + + virtual void onFocusChange(bool gainFocus) + { + this->INHERITED::onFocusChange(gainFocus); + + SkEvent evt("user"); + evt.setString("id", "setFocus"); + evt.setS32("FOCUS", gainFocus); + fAnim.doUserEvent(evt); + } + + virtual void onSizeChange() + { + this->INHERITED::onSizeChange(); + + SkEvent evt("user"); + evt.setString("id", "setDim"); + evt.setScalar("dimX", this->width()); + evt.setScalar("dimY", this->height()); + fAnim.doUserEvent(evt); + } + + virtual void onDraw(SkCanvas* canvas) + { + SkPaint paint; + SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs()); + + if (diff == SkAnimator::kDifferent) + this->inval(NULL); + else if (diff == SkAnimator::kPartiallyDifferent) + { + SkRect bounds; + fAnim.getInvalBounds(&bounds); + this->inval(&bounds); + } + } + + virtual bool onEvent(const SkEvent& evt) + { + if (evt.isType(SK_EventType_Inval)) + { + this->inval(NULL); + return true; + } + if (evt.isType("recommendDim")) + { + SkScalar height; + + if (evt.findScalar("y", &height)) + this->setHeight(height); + return true; + } + return this->INHERITED::onEvent(evt); + } + + virtual bool onPrepareWidgetEvent(SkEvent* evt) + { + if (this->INHERITED::onPrepareWidgetEvent(evt)) + { + SkEvent e("user"); + e.setString("id", "handlePress"); + (void)fAnim.doUserEvent(e); + return true; + } + return false; + } + +private: + SkAnimator fAnim; + + typedef SkButtonView INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// + +SkView* SkWidgetFactory(const char name[]) +{ + if (name == NULL) + return NULL; + + // must be in the same order as the SkSkinWidgetEnum is declared + static const char* gNames[] = { + "sk-border", + "sk-button", + "sk-image", + "sk-list", + "sk-progress", + "sk-scroll", + "sk-text" + + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) + if (!strcmp(gNames[i], name)) + return SkWidgetFactory((SkWidgetEnum)i); + + return NULL; +} + +#include "SkImageView.h" +#include "SkProgressBarView.h" +#include "SkScrollBarView.h" +#include "SkBorderView.h" + +SkView* SkWidgetFactory(SkWidgetEnum sw) +{ + switch (sw) { + case kBorder_WidgetEnum: + return new SkBorderView; + case kButton_WidgetEnum: + return new SkAnimButtonView; + case kImage_WidgetEnum: + return new SkImageView; + case kList_WidgetEnum: + return new SkListView; + case kProgress_WidgetEnum: + return new SkProgressBarView; + case kScroll_WidgetEnum: + return new SkScrollBarView; + case kText_WidgetEnum: + return new SkStaticTextView; + default: + SkASSERT(!"unknown enum passed to SkWidgetFactory"); + break; + } + return NULL; +} diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp new file mode 100644 index 0000000000..dba9ab354e --- /dev/null +++ b/src/views/SkWidgets.cpp @@ -0,0 +1,554 @@ +#include "SkWidget.h" +#include "SkCanvas.h" +#include "SkKey.h" +#include "SkParsePaint.h" +#include "SkSystemEventTypes.h" +#include "SkTextBox.h" + +#if 0 + +#ifdef SK_DEBUG + static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) + { + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); + } +#else + #define assert_no_attr(dom, node, attr) +#endif + +#include "SkAnimator.h" +#include "SkTime.h" + +/////////////////////////////////////////////////////////////////////////////// + +enum SkinType { + kPushButton_SkinType, + kStaticText_SkinType, + + kSkinTypeCount +}; + +struct SkinSuite { + SkinSuite(); + ~SkinSuite() + { + for (int i = 0; i < kSkinTypeCount; i++) + delete fAnimators[i]; + } + + SkAnimator* get(SkinType); + +private: + SkAnimator* fAnimators[kSkinTypeCount]; +}; + +SkinSuite::SkinSuite() +{ + static const char kSkinPath[] = "skins/"; + + static const char* gSkinNames[] = { + "pushbutton_skin.xml", + "statictext_skin.xml" + }; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++) + { + size_t len = strlen(gSkinNames[i]); + SkString path(sizeof(kSkinPath) - 1 + len); + + memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1); + memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len); + + fAnimators[i] = new SkAnimator; + if (!fAnimators[i]->decodeURI(path.c_str())) + { + delete fAnimators[i]; + fAnimators[i] = NULL; + } + } +} + +SkAnimator* SkinSuite::get(SkinType st) +{ + SkASSERT((unsigned)st < kSkinTypeCount); + return fAnimators[st]; +} + +static SkinSuite* gSkinSuite; + +static SkAnimator* get_skin_animator(SkinType st) +{ +#if 0 + if (gSkinSuite == NULL) + gSkinSuite = new SkinSuite; + return gSkinSuite->get(st); +#else + return NULL; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkWidget::Init() +{ +} + +void SkWidget::Term() +{ + delete gSkinSuite; +} + +void SkWidget::onEnabledChange() +{ + this->inval(NULL); +} + +void SkWidget::postWidgetEvent() +{ + if (!fEvent.isType("") && this->hasListeners()) + { + this->prepareWidgetEvent(&fEvent); + this->postToListeners(fEvent); + } +} + +void SkWidget::prepareWidgetEvent(SkEvent*) +{ + // override in subclass to add any additional fields before posting +} + +void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + if ((node = dom.getFirstChild(node, "event")) != NULL) + fEvent.inflate(dom, node); +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkHasLabelWidget::getLabel(SkString* str) const +{ + if (str) + *str = fLabel; + return fLabel.size(); +} + +size_t SkHasLabelWidget::getLabel(char buffer[]) const +{ + if (buffer) + memcpy(buffer, fLabel.c_str(), fLabel.size()); + return fLabel.size(); +} + +void SkHasLabelWidget::setLabel(const SkString& str) +{ + this->setLabel(str.c_str(), str.size()); +} + +void SkHasLabelWidget::setLabel(const char label[]) +{ + this->setLabel(label, strlen(label)); +} + +void SkHasLabelWidget::setLabel(const char label[], size_t len) +{ + if (!fLabel.equals(label, len)) + { + fLabel.set(label, len); + this->onLabelChange(); + } +} + +void SkHasLabelWidget::onLabelChange() +{ + // override in subclass +} + +void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* text = dom.findAttr(node, "label"); + if (text) + this->setLabel(text); +} + +///////////////////////////////////////////////////////////////////////////////////// + +void SkButtonWidget::setButtonState(State state) +{ + if (fState != state) + { + fState = state; + this->onButtonStateChange(); + } +} + +void SkButtonWidget::onButtonStateChange() +{ + this->inval(NULL); +} + +void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0) + this->setButtonState((State)index); +} + +///////////////////////////////////////////////////////////////////////////////////// + +bool SkPushButtonWidget::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey) + { + this->postWidgetEvent(); + return true; + } + return this->INHERITED::onEvent(evt); +} + +static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state) +{ + if (!enabled) + return "disabled"; + if (state == SkButtonWidget::kOn_State) + { + SkASSERT(focused); + return "enabled-pressed"; + } + if (focused) + return "enabled-focused"; + return "enabled"; +} + +#include "SkBlurMaskFilter.h" +#include "SkEmbossMaskFilter.h" + +static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed) +{ + SkEmbossMaskFilter::Light light; + + light.fDirection[0] = SK_Scalar1/2; + light.fDirection[1] = SK_Scalar1/2; + light.fDirection[2] = SK_Scalar1/3; + light.fAmbient = 0x48; + light.fSpecular = 0x80; + + if (pressed) + { + light.fDirection[0] = -light.fDirection[0]; + light.fDirection[1] = -light.fDirection[1]; + } + if (focus) + light.fDirection[2] += SK_Scalar1/4; + + paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref(); +} + +void SkPushButtonWidget::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + SkString label; + this->getLabel(&label); + + SkAnimator* anim = get_skin_animator(kPushButton_SkinType); + + if (anim) + { + SkEvent evt("user"); + + evt.setString("id", "prime"); + evt.setScalar("prime-width", this->width()); + evt.setScalar("prime-height", this->height()); + evt.setString("prime-text", label); + evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState())); + + (void)anim->doUserEvent(evt); + SkPaint paint; + anim->draw(canvas, &paint, SkTime::GetMSecs()); + } + else + { + SkRect r; + SkPaint p; + + r.set(0, 0, this->width(), this->height()); + p.setAntiAliasOn(true); + p.setColor(SK_ColorBLUE); + create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State); + canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p); + p.setMaskFilter(NULL); + + p.setTextAlign(SkPaint::kCenter_Align); + + SkTextBox box; + box.setMode(SkTextBox::kOneLine_Mode); + box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign); + box.setBox(0, 0, this->width(), this->height()); + +// if (this->getButtonState() == kOn_State) +// p.setColor(SK_ColorRED); +// else + p.setColor(SK_ColorWHITE); + + box.draw(canvas, label.c_str(), label.size(), p); + } +} + +SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y) +{ + this->acceptFocus(); + return new Click(this); +} + +bool SkPushButtonWidget::onClick(Click* click) +{ + SkRect r; + State state = kOff_State; + + this->getLocalBounds(&r); + if (r.contains(click->fCurr)) + { + if (click->fState == Click::kUp_State) + this->postWidgetEvent(); + else + state = kOn_State; + } + this->setButtonState(state); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags) +{ + fMargin.set(0, 0); + fMode = kFixedSize_Mode; + fSpacingAlign = SkTextBox::kStart_SpacingAlign; +} + +SkStaticTextView::~SkStaticTextView() +{ +} + +void SkStaticTextView::computeSize() +{ + if (fMode == kAutoWidth_Mode) + { + SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), NULL, NULL); + this->setWidth(width + fMargin.fX * 2); + } + else if (fMode == kAutoHeight_Mode) + { + SkScalar width = this->width() - fMargin.fX * 2; + int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; + + SkScalar before, after; + (void)fPaint.measureText(0, NULL, &before, &after); + + this->setHeight(lines * (after - before) + fMargin.fY * 2); + } +} + +void SkStaticTextView::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + + if (fMode != mode) + { + fMode = SkToU8(mode); + this->computeSize(); + } +} + +void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) +{ + fSpacingAlign = SkToU8(align); + this->inval(NULL); +} + +void SkStaticTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) +{ + if (fMargin.fX != dx || fMargin.fY != dy) + { + fMargin.set(dx, dy); + this->computeSize(); + this->inval(NULL); + } +} + +size_t SkStaticTextView::getText(SkString* text) const +{ + if (text) + *text = fText; + return fText.size(); +} + +size_t SkStaticTextView::getText(char text[]) const +{ + if (text) + memcpy(text, fText.c_str(), fText.size()); + return fText.size(); +} + +void SkStaticTextView::setText(const SkString& text) +{ + this->setText(text.c_str(), text.size()); +} + +void SkStaticTextView::setText(const char text[]) +{ + this->setText(text, strlen(text)); +} + +void SkStaticTextView::setText(const char text[], size_t len) +{ + if (!fText.equals(text, len)) + { + fText.set(text, len); + this->computeSize(); + this->inval(NULL); + } +} + +void SkStaticTextView::getPaint(SkPaint* paint) const +{ + if (paint) + *paint = fPaint; +} + +void SkStaticTextView::setPaint(const SkPaint& paint) +{ + if (fPaint != paint) + { + fPaint = paint; + this->computeSize(); + this->inval(NULL); + } +} + +void SkStaticTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.isEmpty()) + return; + + SkTextBox box; + + box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); + box.setSpacingAlign(this->getSpacingAlign()); + box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); + box.draw(canvas, fText.c_str(), fText.size(), fPaint); +} + +void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) + this->setMode((Mode)index); + else + assert_no_attr(dom, node, "mode"); + + if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) + this->setSpacingAlign((SkTextBox::SpacingAlign)index); + else + assert_no_attr(dom, node, "mode"); + + SkScalar s[2]; + if (dom.findScalars(node, "margin", s, 2)) + this->setMargin(s[0], s[1]); + else + assert_no_attr(dom, node, "margin"); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + if ((node = dom.getFirstChild(node, "paint")) != NULL) + SkPaint_Inflate(&fPaint, dom, node); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkImageDecoder.h" + +SkBitmapView::SkBitmapView(U32 flags) : SkView(flags) +{ +} + +SkBitmapView::~SkBitmapView() +{ +} + +bool SkBitmapView::getBitmap(SkBitmap* bitmap) const +{ + if (bitmap) + *bitmap = fBitmap; + return fBitmap.getConfig() != SkBitmap::kNo_Config; +} + +void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels) +{ + if (bitmap) + { + fBitmap = *bitmap; + fBitmap.setOwnsPixels(viewOwnsPixels); + } +} + +bool SkBitmapView::loadBitmapFromFile(const char path[]) +{ + SkBitmap bitmap; + + if (SkImageDecoder::DecodeFile(path, &bitmap)) + { + this->setBitmap(&bitmap, true); + bitmap.setOwnsPixels(false); + return true; + } + return false; +} + +void SkBitmapView::onDraw(SkCanvas* canvas) +{ + if (fBitmap.getConfig() != SkBitmap::kNo_Config && + fBitmap.width() && fBitmap.height()) + { + SkAutoCanvasRestore restore(canvas, true); + SkPaint p; + + p.setFilterType(SkPaint::kBilinear_FilterType); + canvas->scale( this->width() / fBitmap.width(), + this->height() / fBitmap.height(), + 0, 0); + canvas->drawBitmap(fBitmap, 0, 0, p); + } +} + +void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ + this->INHERITED::onInflate(dom, node); + + const char* src = dom.findAttr(node, "src"); + if (src) + (void)this->loadBitmapFromFile(src); +} + +#endif + diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp new file mode 100644 index 0000000000..db4faee11f --- /dev/null +++ b/src/views/SkWindow.cpp @@ -0,0 +1,412 @@ +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkOSMenu.h" +#include "SkSystemEventTypes.h" +#include "SkTime.h" + +#define SK_EventDelayInval "\xd" "n" "\xa" "l" + +#define TEST_BOUNDERx + +#include "SkBounder.h" +class test_bounder : public SkBounder { +public: + test_bounder(const SkBitmap& bm) : fCanvas(bm) {} +protected: + virtual bool onIRect(const SkIRect& r) + { + SkRect rr; + + rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop), + SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom)); + + SkPaint p; + + p.setStyle(SkPaint::kStroke_Style); + p.setColor(SK_ColorYELLOW); + +#if 0 + rr.inset(SK_ScalarHalf, SK_ScalarHalf); +#else + rr.inset(-SK_ScalarHalf, -SK_ScalarHalf); +#endif + + fCanvas.drawRect(rr, p); + return true; + } +private: + SkCanvas fCanvas; +}; + +SkWindow::SkWindow() : fFocusView(NULL) +{ + fClick = NULL; + fWaitingOnInval = false; + +#ifdef SK_BUILD_FOR_WINCE + fConfig = SkBitmap::kRGB_565_Config; +#else + fConfig = SkBitmap::kARGB_8888_Config; +#endif + + fMatrix.reset(); +} + +SkWindow::~SkWindow() +{ + delete fClick; + + fMenus.deleteAll(); +} + +void SkWindow::setMatrix(const SkMatrix& matrix) { + if (fMatrix != matrix) { + fMatrix = matrix; + this->inval(NULL); + } +} + +void SkWindow::preConcat(const SkMatrix& matrix) { + SkMatrix m; + m.setConcat(fMatrix, matrix); + this->setMatrix(m); +} + +void SkWindow::postConcat(const SkMatrix& matrix) { + SkMatrix m; + m.setConcat(matrix, fMatrix); + this->setMatrix(m); +} + +void SkWindow::setConfig(SkBitmap::Config config) +{ + this->resize(fBitmap.width(), fBitmap.height(), config); +} + +void SkWindow::resize(int width, int height, SkBitmap::Config config) +{ + if (config == SkBitmap::kNo_Config) + config = fConfig; + + if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig) + { + fConfig = config; + fBitmap.setConfig(config, width, height); + fBitmap.allocPixels(); + fBitmap.setIsOpaque(true); + + this->setSize(SkIntToScalar(width), SkIntToScalar(height)); + this->inval(NULL); + } +} + +void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +{ + fBitmap.eraseARGB(a, r, g, b); +} + +void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b) +{ + fBitmap.eraseRGB(r, g, b); +} + +bool SkWindow::handleInval(const SkRect* localR) +{ + SkIRect ir; + + if (localR) { + SkRect devR; + SkMatrix inverse; + if (!fMatrix.invert(&inverse)) { + return false; + } + fMatrix.mapRect(&devR, *localR); + devR.round(&ir); + } else { + ir.set(0, 0, + SkScalarRound(this->width()), + SkScalarRound(this->height())); + } + fDirtyRgn.op(ir, SkRegion::kUnion_Op); + + this->onHandleInval(ir); + return true; +} + +void SkWindow::forceInvalAll() { + fDirtyRgn.setRect(0, 0, + SkScalarCeil(this->width()), + SkScalarCeil(this->height())); +} + +#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) + #include <windows.h> + #include <gx.h> + extern GXDisplayProperties gDisplayProps; +#endif + +#ifdef SK_SIMULATE_FAILED_MALLOC +extern bool gEnableControlledThrow; +#endif + +bool SkWindow::update(SkIRect* updateArea, SkCanvas* canvas) +{ + if (!fDirtyRgn.isEmpty()) + { + SkBitmap bm = this->getBitmap(); + +#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) + char* buffer = (char*)GXBeginDraw(); + SkASSERT(buffer); + + RECT rect; + GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect); + buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch; + + bm.setPixels(buffer); +#endif + + SkCanvas rasterCanvas; + SkDevice* device; + + if (NULL == canvas) { + canvas = &rasterCanvas; + device = new SkDevice(canvas, bm, false); + canvas->setDevice(device)->unref(); + } else { + canvas->setBitmapDevice(bm); + } + + canvas->clipRegion(fDirtyRgn); + if (updateArea) + *updateArea = fDirtyRgn.getBounds(); + + SkAutoCanvasRestore acr(canvas, true); + canvas->concat(fMatrix); + + // empty this now, so we can correctly record any inval calls that + // might be made during the draw call. + fDirtyRgn.setEmpty(); + +#ifdef TEST_BOUNDER + test_bounder b(bm); + canvas->setBounder(&b); +#endif +#ifdef SK_SIMULATE_FAILED_MALLOC + gEnableControlledThrow = true; +#endif +#ifdef SK_BUILD_FOR_WIN32 + //try { + this->draw(canvas); + //} + //catch (...) { + //} +#else + this->draw(canvas); +#endif +#ifdef SK_SIMULATE_FAILED_MALLOC + gEnableControlledThrow = false; +#endif +#ifdef TEST_BOUNDER + canvas->setBounder(NULL); +#endif + +#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) + GXEndDraw(); +#endif + + return true; + } + return false; +} + +bool SkWindow::handleChar(SkUnichar uni) +{ + if (this->onHandleChar(uni)) + return true; + + SkView* focus = this->getFocusView(); + if (focus == NULL) + focus = this; + + SkEvent evt(SK_EventType_Unichar); + evt.setFast32(uni); + return focus->doEvent(evt); +} + +bool SkWindow::handleKey(SkKey key) +{ + if (key == kNONE_SkKey) + return false; + + if (this->onHandleKey(key)) + return true; + + // send an event to the focus-view + { + SkView* focus = this->getFocusView(); + if (focus == NULL) + focus = this; + + SkEvent evt(SK_EventType_Key); + evt.setFast32(key); + if (focus->doEvent(evt)) + return true; + } + + if (key == kUp_SkKey || key == kDown_SkKey) + { + if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL) + this->onSetFocusView(NULL); + return true; + } + return false; +} + +bool SkWindow::handleKeyUp(SkKey key) +{ + if (key == kNONE_SkKey) + return false; + + if (this->onHandleKeyUp(key)) + return true; + + //send an event to the focus-view + { + SkView* focus = this->getFocusView(); + if (focus == NULL) + focus = this; + + //should this one be the same? + SkEvent evt(SK_EventType_KeyUp); + evt.setFast32(key); + if (focus->doEvent(evt)) + return true; + } + return false; +} + +void SkWindow::addMenu(SkOSMenu* menu) +{ + *fMenus.append() = menu; + this->onAddMenu(menu); +} + +void SkWindow::setTitle(const char title[]) { + if (NULL == title) { + title = ""; + } + fTitle.set(title); + this->onSetTitle(title); +} + +bool SkWindow::handleMenu(uint32_t cmd) +{ + for (int i = 0; i < fMenus.count(); i++) + { + SkEvent* evt = fMenus[i]->createEvent(cmd); + if (evt) + { + evt->post(this->getSinkID()); + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////// + +bool SkWindow::onEvent(const SkEvent& evt) +{ + if (evt.isType(SK_EventDelayInval)) + { + SkRegion::Iterator iter(fDirtyRgn); + + for (; !iter.done(); iter.next()) + this->onHandleInval(iter.rect()); + fWaitingOnInval = false; + return true; + } + return this->INHERITED::onEvent(evt); +} + +bool SkWindow::onGetFocusView(SkView** focus) const +{ + if (focus) + *focus = fFocusView; + return true; +} + +bool SkWindow::onSetFocusView(SkView* focus) +{ + if (fFocusView != focus) + { + if (fFocusView) + fFocusView->onFocusChange(false); + fFocusView = focus; + if (focus) + focus->onFocusChange(true); + } + return true; +} + +////////////////////////////////////////////////////////////////////// + +void SkWindow::onHandleInval(const SkIRect&) +{ +} + +bool SkWindow::onHandleChar(SkUnichar) +{ + return false; +} + +bool SkWindow::onHandleKey(SkKey key) +{ + return false; +} + +bool SkWindow::onHandleKeyUp(SkKey key) +{ + return false; +} + +bool SkWindow::handleClick(int x, int y, Click::State state) { + return this->onDispatchClick(x, y, state); +} + +bool SkWindow::onDispatchClick(int x, int y, Click::State state) { + bool handled = false; + + switch (state) { + case Click::kDown_State: + if (fClick) + delete fClick; + fClick = this->findClickHandler(SkIntToScalar(x), SkIntToScalar(y)); + if (fClick) + { + SkView::DoClickDown(fClick, x, y); + handled = true; + } + break; + case Click::kMoved_State: + if (fClick) + { + SkView::DoClickMoved(fClick, x, y); + handled = true; + } + break; + case Click::kUp_State: + if (fClick) + { + SkView::DoClickUp(fClick, x, y); + delete fClick; + fClick = NULL; + handled = true; + } + break; + } + return handled; +} + diff --git a/src/views/views_files.mk b/src/views/views_files.mk new file mode 100644 index 0000000000..9c5e9e0fc6 --- /dev/null +++ b/src/views/views_files.mk @@ -0,0 +1,26 @@ +SOURCE := \ + SkEvent.cpp \ + SkEventSink.cpp \ + SkOSMenu.cpp \ + SkTagList.cpp \ + SkView.cpp \ + SkViewPriv.cpp \ + SkWindow.cpp \ + SkTouchGesture.cpp +# SkBGViewArtist.cpp \ + SkMetaData.cpp \ + SkListView.cpp \ + SkListWidget.cpp \ + SkParsePaint.cpp \ + SkProgressBarView.cpp \ + SkProgressView.cpp \ + SkScrollBarView.cpp \ + SkStackViewLayout.cpp \ + SkStaticTextView.cpp \ + SkTextBox.cpp \ + SkViewInflate.cpp \ + SkWidget.cpp \ + SkWidgetViews.cpp \ + SkWidgets.cpp \ +# SkBorderView.cpp \ +# SkImageView.cpp \ diff --git a/src/xml/xml_files.mk b/src/xml/xml_files.mk new file mode 100644 index 0000000000..d4342dd2ef --- /dev/null +++ b/src/xml/xml_files.mk @@ -0,0 +1,3 @@ +SOURCE := \ + SkDOM.cpp \ + SkXMLParser.cpp diff --git a/tests/Android.mk b/tests/Android.mk index 09c7739cef..5823b0e4a6 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -26,6 +26,7 @@ LOCAL_SRC_FILES:= \ ParsePathTest.cpp \ PathMeasureTest.cpp \ PathTest.cpp \ + Reader32Test.cpp \ RefDictTest.cpp \ RegionTest.cpp \ Sk64Test.cpp \ @@ -36,6 +37,7 @@ LOCAL_SRC_FILES:= \ Test.cpp \ TestSize.cpp \ UtilsTest.cpp \ + Writer32Test.cpp \ XfermodeTest.cpp # The name of the file with a main function must diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp index 4ef33ff35b..eafdd69052 100644 --- a/tests/ClipStackTest.cpp +++ b/tests/ClipStackTest.cpp @@ -107,7 +107,8 @@ static void TestClipStack(skiatest::Reporter* reporter) { // all of the above rects should have been intersected, leaving only 1 rect SkClipStack::B2FIter iter(stack); const SkClipStack::B2FIter::Clip* clip = iter.next(); - const SkRect answer = { 25, 25, 75, 75 }; + SkRect answer; + answer.iset(25, 25, 75, 75); REPORTER_ASSERT(reporter, clip); REPORTER_ASSERT(reporter, clip->fRect); diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp index 49a98c2c2c..4125f9f243 100644 --- a/tests/MatrixTest.cpp +++ b/tests/MatrixTest.cpp @@ -2,10 +2,12 @@ #include "SkMatrix.h" static bool nearly_equal_scalar(SkScalar a, SkScalar b) { + // Note that we get more compounded error for multiple operations when + // SK_SCALAR_IS_FIXED. #ifdef SK_SCALAR_IS_FLOAT - const float tolerance = 0.000005f; + const SkScalar tolerance = SK_Scalar1 / 200000; #else - const int32_t tolerance = 8; + const SkScalar tolerance = SK_Scalar1 / 1024; #endif return SkScalarAbs(a - b) <= tolerance; diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index 6feab51d6a..9a58fa6c1d 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -122,15 +122,14 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) { realHalf->unref(); // SkRefPtr and new both took a reference. CheckObjectOutput(reporter, realHalf.get(), "0.5", true); +#if defined(SK_SCALAR_IS_FLOAT) SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75); bigScalar->unref(); // SkRefPtr and new both took a reference. -#if defined(SK_SCALAR_IS_FIXED) || !defined(SK_ALLOW_LARGE_PDF_SCALARS) +#if !defined(SK_ALLOW_LARGE_PDF_SCALARS) CheckObjectOutput(reporter, bigScalar.get(), "111000", true); #else CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true); -#endif -#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS) SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1); biggerScalar->unref(); // SkRefPtr and new both took a reference. CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true); @@ -139,6 +138,7 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) { smallestScalar->unref(); // SkRefPtr and new both took a reference. CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true); #endif +#endif SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo"); stringSimple->unref(); // SkRefPtr and new both took a reference. diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 3884308c64..7e4e6bc6f0 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -13,15 +13,13 @@ static void test_convexity2(skiatest::Reporter* reporter) { SkPath pt; pt.moveTo(0, 0); pt.close(); -// check_convexity(reporter, pt, SkPath::kConvex_Convexity); - check_convexity(reporter, pt, SkPath::kUnknown_Convexity); + check_convexity(reporter, pt, SkPath::kConvex_Convexity); SkPath line; line.moveTo(12, 20); line.lineTo(-12, -20); line.close(); - // check_convexity(reporter, pt, SkPath::kConvex_Convexity); - check_convexity(reporter, pt, SkPath::kUnknown_Convexity); + check_convexity(reporter, pt, SkPath::kConvex_Convexity); SkPath triLeft; triLeft.moveTo(0, 0); @@ -79,21 +77,21 @@ static void test_convexity2(skiatest::Reporter* reporter) { SkPath spiral; spiral.moveTo(0, 0); - spiral.lineTo(1, 0); - spiral.lineTo(1, 1); - spiral.lineTo(0, 1); - spiral.lineTo(0,.5); - spiral.lineTo(.5,.5); - spiral.lineTo(.5,.75); + spiral.lineTo(100, 0); + spiral.lineTo(100, 100); + spiral.lineTo(0, 100); + spiral.lineTo(0, 50); + spiral.lineTo(50, 50); + spiral.lineTo(50, 75); spiral.close(); check_convexity(reporter, spiral, SkPath::kConcave_Convexity); SkPath dent; - dent.moveTo(0, 0); - dent.lineTo(1, 1); - dent.lineTo(0, 1); - dent.lineTo(-.5,2); - dent.lineTo(-2, 1); + dent.moveTo(SkIntToScalar(0), SkIntToScalar(0)); + dent.lineTo(SkIntToScalar(100), SkIntToScalar(100)); + dent.lineTo(SkIntToScalar(0), SkIntToScalar(100)); + dent.lineTo(SkIntToScalar(-50), SkIntToScalar(200)); + dent.lineTo(SkIntToScalar(-200), SkIntToScalar(100)); dent.close(); check_convexity(reporter, dent, SkPath::kConcave_Convexity); } @@ -133,13 +131,12 @@ static void setFromString(SkPath* path, const char str[]) { } static void test_convexity(skiatest::Reporter* reporter) { - static const SkPath::Convexity U = SkPath::kUnknown_Convexity; static const SkPath::Convexity C = SkPath::kConcave_Convexity; static const SkPath::Convexity V = SkPath::kConvex_Convexity; SkPath path; - REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path)); + REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); path.addCircle(0, 0, 10); REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); path.addCircle(0, 0, 10); // 2nd circle @@ -155,8 +152,9 @@ static void test_convexity(skiatest::Reporter* reporter) { const char* fPathStr; SkPath::Convexity fExpectedConvexity; } gRec[] = { - { "0 0", SkPath::kUnknown_Convexity }, - { "0 0 10 10", SkPath::kUnknown_Convexity }, + { "", SkPath::kConvex_Convexity }, + { "0 0", SkPath::kConvex_Convexity }, + { "0 0 10 10", SkPath::kConvex_Convexity }, { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity }, { "0 0 10 10 10 20", SkPath::kConvex_Convexity }, { "0 0 10 10 10 0", SkPath::kConvex_Convexity }, @@ -188,7 +186,7 @@ void TestPath(skiatest::Reporter* reporter) { SkRect bounds, bounds2; REPORTER_ASSERT(reporter, p.isEmpty()); - REPORTER_ASSERT(reporter, !p.isConvex()); + REPORTER_ASSERT(reporter, p.isConvex()); REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType); REPORTER_ASSERT(reporter, !p.isInverseFillType()); REPORTER_ASSERT(reporter, p == p2); @@ -198,17 +196,14 @@ void TestPath(skiatest::Reporter* reporter) { bounds.set(0, 0, SK_Scalar1, SK_Scalar1); - p.setIsConvex(false); p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1); check_convex_bounds(reporter, p, bounds); p.reset(); - p.setIsConvex(false); p.addOval(bounds); check_convex_bounds(reporter, p, bounds); p.reset(); - p.setIsConvex(false); p.addRect(bounds); check_convex_bounds(reporter, p, bounds); @@ -245,18 +240,6 @@ void TestPath(skiatest::Reporter* reporter) { p.getLastPt(&pt); REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1); - // check that reset and rewind clear the convex hint back to false - p.setIsConvex(false); - REPORTER_ASSERT(reporter, !p.isConvex()); - p.setIsConvex(true); - REPORTER_ASSERT(reporter, p.isConvex()); - p.reset(); - REPORTER_ASSERT(reporter, !p.isConvex()); - p.setIsConvex(true); - REPORTER_ASSERT(reporter, p.isConvex()); - p.rewind(); - REPORTER_ASSERT(reporter, !p.isConvex()); - test_convexity(reporter); test_convexity2(reporter); } diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp new file mode 100644 index 0000000000..c752b0f123 --- /dev/null +++ b/tests/Reader32Test.cpp @@ -0,0 +1,93 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#include "SkReader32.h" +#include "Test.h" + +static void assert_eof(skiatest::Reporter* reporter, const SkReader32& reader) { + REPORTER_ASSERT(reporter, reader.eof()); + REPORTER_ASSERT(reporter, reader.size() == reader.offset()); + REPORTER_ASSERT(reporter, (const char*)reader.peek() == + (const char*)reader.base() + reader.size()); +} + +static void assert_start(skiatest::Reporter* reporter, const SkReader32& reader) { + REPORTER_ASSERT(reporter, 0 == reader.offset()); + REPORTER_ASSERT(reporter, reader.size() == reader.available()); + REPORTER_ASSERT(reporter, reader.isAvailable(reader.size())); + REPORTER_ASSERT(reporter, !reader.isAvailable(reader.size() + 1)); + REPORTER_ASSERT(reporter, reader.peek() == reader.base()); +} + +static void assert_empty(skiatest::Reporter* reporter, const SkReader32& reader) { + REPORTER_ASSERT(reporter, 0 == reader.size()); + REPORTER_ASSERT(reporter, 0 == reader.offset()); + REPORTER_ASSERT(reporter, 0 == reader.available()); + REPORTER_ASSERT(reporter, !reader.isAvailable(1)); + assert_eof(reporter, reader); + assert_start(reporter, reader); +} + +static void Tests(skiatest::Reporter* reporter) { + SkReader32 reader; + assert_empty(reporter, reader); + REPORTER_ASSERT(reporter, NULL == reader.base()); + REPORTER_ASSERT(reporter, NULL == reader.peek()); + + size_t i; + + const int32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const SkScalar data2[] = { 0, SK_Scalar1, -SK_Scalar1, SK_Scalar1/2 }; + char buffer[SkMax32(sizeof(data), sizeof(data2))]; + + reader.setMemory(data, sizeof(data)); + for (i = 0; i < SK_ARRAY_COUNT(data); ++i) { + REPORTER_ASSERT(reporter, sizeof(data) == reader.size()); + REPORTER_ASSERT(reporter, i*4 == reader.offset()); + REPORTER_ASSERT(reporter, (const void*)data == reader.base()); + REPORTER_ASSERT(reporter, (const void*)&data[i] == reader.peek()); + REPORTER_ASSERT(reporter, data[i] == reader.readInt()); + } + assert_eof(reporter, reader); + reader.rewind(); + assert_start(reporter, reader); + reader.read(buffer, sizeof(data)); + REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(data))); + + reader.setMemory(data2, sizeof(data2)); + for (i = 0; i < SK_ARRAY_COUNT(data2); ++i) { + REPORTER_ASSERT(reporter, sizeof(data2) == reader.size()); + REPORTER_ASSERT(reporter, i*4 == reader.offset()); + REPORTER_ASSERT(reporter, (const void*)data2 == reader.base()); + REPORTER_ASSERT(reporter, (const void*)&data2[i] == reader.peek()); + REPORTER_ASSERT(reporter, data2[i] == reader.readScalar()); + } + assert_eof(reporter, reader); + reader.rewind(); + assert_start(reporter, reader); + reader.read(buffer, sizeof(data2)); + REPORTER_ASSERT(reporter, !memcmp(data2, buffer, sizeof(data2))); + + reader.setMemory(NULL, 0); + assert_empty(reporter, reader); + REPORTER_ASSERT(reporter, NULL == reader.base()); + REPORTER_ASSERT(reporter, NULL == reader.peek()); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Reader32", Reader32Class, Tests) + diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp index a051af23c7..4f36afe4a5 100644 --- a/tests/UtilsTest.cpp +++ b/tests/UtilsTest.cpp @@ -64,7 +64,7 @@ static void test_autounref(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, 1 == obj.getRefCnt()); } -////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// #define kSEARCH_COUNT 91 diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp new file mode 100644 index 0000000000..63b1209e98 --- /dev/null +++ b/tests/Writer32Test.cpp @@ -0,0 +1,90 @@ +/* + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +#include "SkReader32.h" +#include "SkWriter32.h" +#include "Test.h" + +static void test1(skiatest::Reporter* reporter, SkWriter32* writer) { + const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) { + REPORTER_ASSERT(reporter, i*4 == writer->size()); + writer->write32(data[i]); + uint32_t* addr = writer->peek32(i * 4); + REPORTER_ASSERT(reporter, data[i] == *addr); + } + + char buffer[sizeof(data)]; + REPORTER_ASSERT(reporter, sizeof(buffer) == writer->size()); + writer->flatten(buffer); + REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(buffer))); +} + +static void test2(skiatest::Reporter* reporter, SkWriter32* writer) { + static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz"; + size_t i; + + size_t len = 0; + for (i = 0; i <= 26; ++i) { + len += SkWriter32::WriteStringSize(gStr, i); + writer->writeString(gStr, i); + } + REPORTER_ASSERT(reporter, writer->size() == len); + + SkAutoMalloc storage(len); + writer->flatten(storage.get()); + + SkReader32 reader; + reader.setMemory(storage.get(), len); + for (i = 0; i <= 26; ++i) { + REPORTER_ASSERT(reporter, !reader.eof()); + const char* str = reader.readString(&len); + REPORTER_ASSERT(reporter, i == len); + REPORTER_ASSERT(reporter, strlen(str) == len); + REPORTER_ASSERT(reporter, !memcmp(str, gStr, len)); + } + REPORTER_ASSERT(reporter, reader.eof()); +} + +static void Tests(skiatest::Reporter* reporter) { + // dynamic allocator + { + SkWriter32 writer(256 * 4); + REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock()); + test1(reporter, &writer); + + writer.reset(); + test2(reporter, &writer); + } + + // single-block + { + SkWriter32 writer(0); + uint32_t storage[256]; + REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock()); + writer.reset(storage, sizeof(storage)); + REPORTER_ASSERT(reporter, (void*)storage == writer.getSingleBlock()); + test1(reporter, &writer); + + writer.reset(storage, sizeof(storage)); + test2(reporter, &writer); + } +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Writer32", Writer32Class, Tests) + diff --git a/tests/tests_files.mk b/tests/tests_files.mk index 28d5fe75f9..9b90179c7e 100644 --- a/tests/tests_files.mk +++ b/tests/tests_files.mk @@ -22,6 +22,7 @@ SOURCE := \ ParsePathTest.cpp \ PathMeasureTest.cpp \ PathTest.cpp \ + Reader32Test.cpp \ RefDictTest.cpp \ RegionTest.cpp \ Sk64Test.cpp \ @@ -33,4 +34,5 @@ SOURCE := \ Test.cpp \ TestSize.cpp \ UtilsTest.cpp \ + Writer32Test.cpp \ XfermodeTest.cpp |