diff options
author | Derek Sollenberger <djsollen@google.com> | 2011-06-06 17:02:24 -0400 |
---|---|---|
committer | Derek Sollenberger <djsollen@google.com> | 2011-06-07 14:00:04 -0400 |
commit | 0b15698a8c76bb8abc1b555c1d91892669b4118f (patch) | |
tree | 08732d5fbb7484ce7e10c65c96fb56294053073a | |
parent | 9770b0f3d2b5d512daac50c2c9561d2c073cd8d2 (diff) | |
download | external_skia-0b15698a8c76bb8abc1b555c1d91892669b4118f.zip external_skia-0b15698a8c76bb8abc1b555c1d91892669b4118f.tar.gz external_skia-0b15698a8c76bb8abc1b555c1d91892669b4118f.tar.bz2 |
Skia Merge (revision 1510)
This CL includes bug fixes and closely mirrors the version of
Skia used in Chrome M13, which is likely to be our baseline for
ICS.
The CL also adds source files for the SampleApp which will allow
us to execute basic skia tests. The SampleApp requires the
utils/views directory in order to run.
Finally, we have included the PDF backend for Skia in order to
experiment with using it to generate PDF files for certain
applications.
Note: The SampleApp and PDF code are not built as part of libskia.
Change-Id: I1895ccfbd8074e25f19148cc7bd1b4af571fb307
315 files changed, 40788 insertions, 3871 deletions
@@ -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 0000000..6d62c35 --- /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 0000000..e323246 --- /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 0000000..a7d8e54 --- /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 0000000..529047c --- /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 0000000..d71116b --- /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 0000000..72d3bc8 --- /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 0000000..b02c4d7 --- /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 b20aef9..71523af 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 0000000..d963bc7 --- /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 0000000..f6909b1 --- /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 4f6d81c..066573a 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 b3aeadf..acfb4a5 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 0487fe6..3903913 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 26fdc79..c934178 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 e867820..fec20b6 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 6267fd4..ea205e3 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 0000000..3dec7cc --- /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 ec56942..713847f 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 1cf562c..ea87823 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 5afde49..bba997f 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 324ce7e..5daea0a 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 b716407..891b95a 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 1fcbdd1..f7f74a7 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 f1f2437..9ee37c7 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 951c0e6..58c53ba 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 fae4e92..c79a191 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 93b381d..cd70d3e 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 77730d7..ab73ea4 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 29e56f3..6c2483b 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 591ab8c..150e8e4 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 e44956f..574a430 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 3035ca1..a7923f5 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 f958329..c23cfc4 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 e67ff69..0000000 --- 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 21cab6b..1ebad4f 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 dd47c53..373ea94 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 a26b67c..35cd61a 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 b014ee5..44a390d 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 accd114..e37e66b 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 08b10f0..9348375 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 683528b..565b460 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 2d1680c..c2613bb 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 8cb932b..dae1cd1 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 518b4ee..2848999 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 ef00df8..0000000 --- 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 0825a3f..5ecf8eb 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 5d2d8b3..ecb4753 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 1611ca2..473bcb6 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 9950afd..4fe7ccc 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 e8c7afb..ff2d406 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 da955cf..696b72f 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 e197a82..65229dc 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 077b6e2..487c09f 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 8965b06..50be67f 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 bb3024f..9392d1c 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 aa89d37..0000000 --- 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 317b7d3..58bb12e 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 115b0f6..8a72ba8 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 af05682..2cd00cb 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 a537e16..edf83fe 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 3993adb..0e3389c 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 0222042..2aacfa2 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 fef9784..bd6f061 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 320dd15..338f2cc 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 b9396fa..fb5b182 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 0000000..0350c30 --- /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 ba67065..3e9b975 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 53fb26a..428abb1 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 aadaffb..0000000 --- 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 72b4879..0000000 --- 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 7247828..0000000 --- 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 aa2e6cf..c56d8cf 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 ae0b974..6e8da76 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 f9e02a2..6fa9df3 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 0b198f6..ea37549 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 46bcf1a..d9a4fde 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 3e72904..efe378a 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 7c77902..480e077 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 18dcd11..7120d3f 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 57cc368..23ce02b 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 8d47d46..6ec73ce 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 03a34c7..654ebd5 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 550c5d1..19ee12a 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 ebe621b..5dbf684 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 55dfcef..3f818a3 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 8e133c2..c8ebb6a 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 4d46bb9..6ab9d6d 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 15def87..601da09 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 75099b2..65565c9 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 0000000..e02ffa1 --- /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 0000000..6e4d8db --- /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 0000000..0a76ea2 --- /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 0000000..93a72f1 --- /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 0000000..41719f0 --- /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 0000000..49809a4 --- /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 0000000..945ff9e --- /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 0000000..0e30028 --- /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 0000000..17f7f03 --- /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 0000000..a975ad6 --- /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 0000000..6b5146a --- /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 0000000..50da28c --- /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 0000000..897766f --- /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 4b17be1..ced31a5 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 0000000..7b0a035 --- /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 3d41a22..1852d99 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 0000000..4c4a4fb --- /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 0000000..1bca42f --- /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 0000000..94ccc1f --- /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 0000000..f6719d6 --- /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 0000000..27a6743 --- /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 0000000..57215c9 --- /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 0000000..3fd5114 --- /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 0000000..433a601 --- /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 0000000..38a4cf8 --- /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 0000000..232a202 --- /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 0000000..0ff24f3 --- /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 0000000..803ca13 --- /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 0000000..4b3e916 --- /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 0000000..c5dfc7c --- /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 0000000..6341fcb --- /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 0000000..b8a5209 --- /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 0000000..8000319 --- /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 0000000..8dfe8be --- /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 0000000..79d4e28 --- /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 0000000..d3633db --- /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 0000000..3ec65a6 --- /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 0000000..db85f01 --- /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 0000000..9b3a816 --- /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 0000000..fd4ce0a --- /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 0000000..c829b69 --- /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 0000000..2ae2119 --- /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 0000000..34a33b0 --- /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 0000000..abbf8f9 --- /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 0000000..a7b2a46 --- /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 0000000..99173fc --- /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 0000000..3efffe6 --- /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 0000000..8e3ad88 --- /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 0000000..868a67c --- /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 0000000..5ebb516 --- /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 0000000..002b2b9 --- /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 0000000..d2ea2b0 --- /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 0000000..d445df7 --- /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 0000000..2db3968 --- /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 0000000..2abc28d --- /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 0000000..88c1b91 --- /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 0000000..c42ee25 --- /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 0000000..0e1fb11 --- /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 0000000..672d055 --- /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 0000000..7b4eab6 --- /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 0000000..4cef07f --- /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 0000000..b192c5d --- /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 0000000..3e77a5d --- /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 0000000..0d62446 --- /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 0000000..deb1fb2 --- /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 0000000..30879f7 --- /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 0000000..a63c08d --- /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 0000000..8b3f194 --- /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 0000000..f4ea195 --- /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 0000000..860272d --- /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 0000000..393c5f7 --- /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 0000000..a9089fa --- /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 0000000..c1a16a8 --- /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 0000000..0b8187a --- /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 0000000..0b0d349 --- /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 0000000..5c41886 --- /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 0000000..ec5b22a --- /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 0000000..902b0bd --- /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 0000000..8368f5b --- /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 0000000..2944299 --- /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 0000000..8ef59ad --- /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 0000000..098958f --- /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 0000000..9bd00ae --- /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 0000000..6fc9c83 --- /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 0000000..ac6b013 --- /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 0000000..03dd30f --- /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 0000000..8078e03 --- /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 0000000..3d95156 --- /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 0000000..af34198 --- /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 0000000..e158287 --- /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 0000000..f625529 --- /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 0000000..d3ecff7 --- /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 0000000..7c5bf48 --- /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 0000000..ea365c7 --- /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 0000000..cd45ed9 --- /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 0000000..8139171 --- /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 0000000..75566b0 --- /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 0000000..845b7a8 --- /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 0000000..d7b6b22 --- /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 0000000..a2804b4 --- /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 0000000..aea0cb4 --- /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 0000000..822bd6f --- /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 0000000..9867074 --- /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 0000000..2748b55 --- /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 0000000..c1bb0fd --- /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 0000000..dc10f1a --- /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 0000000..11976e9 --- /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 0000000..3b7d05b --- /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 0000000..1a41440 --- /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 0000000..ae630ef --- /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 0000000..20c9e2c --- /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 0000000..bcb9e4f --- /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 0000000..4ce8640 --- /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 0000000..2676530 --- /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 0000000..ccfed68 --- /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 0000000..37a6be0 --- /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 0000000..f256b2e --- /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 0000000..96e8c9a --- /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 0000000..be000f9 --- /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 0000000..4752ed1 --- /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 0000000..0841474 --- /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 0000000..be9da8f --- /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 0000000..63f1d5a --- /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 0000000..b20aece --- /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 0000000..74e757f --- /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 0000000..bf4ef0d --- /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 0000000..0a3c4c7 --- /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 0000000..166e4e5 --- /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 0000000..4c660e5 --- /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 0000000..c124ad8 --- /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 b9e65f7..e456f26 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 a94b848..db6c838 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 eac956c..426a1e6 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 f81147d..96b73cd 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 d582d33..98db1fb 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 6d8c208..1284198 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 efd1872..edcc2af 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 7e8a030..05d5c28 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 4125b58..a5fc3c9 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 89e5481..669e5d2 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 9ae8b17..553bd89 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 15831b6..9a9e8c4 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 5ffa5b1..4ad4d41 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 4a76936..aee5a5e 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 983c8d2..06bca1e 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 52b0163..a607424 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 68eb35d..ca57237 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 5c5ea0b..105df3c 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 dae7b8a..697b399 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 5457b59..398f786 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 b84c576..52f2a32 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 141f751..9a60b8e 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 603deb7..1dfc24d 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 c6caba3..6e68b5e 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 fa8b6a7..6da8822 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 5b4f529..7166a07 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 f65cf1e..600c336 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 e58f035..eb260fb 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 0000000..afa9d9a --- /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 0000000..c6ddf39 --- /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 0000000..95370b4 --- /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 0000000..277ed12 --- /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 0000000..40a5564 --- /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 0000000..b08bf24 --- /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 0000000..be69f7f --- /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 0000000..2a8183d --- /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 0000000..6845e09 --- /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 0000000..b1bd5ff --- /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 0000000..b9420eb --- /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 0000000..a838427 --- /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 0000000..c9b4fb4 --- /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 0000000..fb15536 --- /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 0000000..ecb19fd --- /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 0000000..428f864 --- /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 b3cc783..de1ce73 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 9aabce7..37c2c35 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 e637877..ad57e16 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 d9404f3..f5d126e 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 df86178..f4ad198 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 1c55c95..cb08f40 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 0000000..06c2b27 --- /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 0000000..f4bda45 --- /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 0000000..e601f35 --- /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 0000000..f1a2926 --- /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 0000000..a1345cf --- /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 0000000..ed1b7cd --- /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 0000000..7921b8a --- /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 4ec0c74..ae881d5 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 0000000..07da123 --- /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 0000000..74a2477 --- /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 0000000..ec4a7b4 --- /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 0000000..c8fe35c --- /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 0000000..9c358c7 --- /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 0000000..ba4f02a --- /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 0000000..89008e7 --- /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 0000000..3760ddd --- /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 0000000..a79f30c --- /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 0000000..a9fd516 --- /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 0000000..0c81bcc --- /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 0000000..2e087bd --- /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 0000000..ae2e9e8 --- /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 0000000..8fb8bc1 --- /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 0000000..4576ce6 --- /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 0000000..5f428e4 --- /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 0000000..0e31ac6 --- /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 0000000..1732176 --- /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 0000000..1cd6339 --- /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 0000000..8f5489d --- /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 0000000..b03ca8c --- /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 0000000..06ce59b --- /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 0000000..4d055c4 --- /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 0000000..2705307 --- /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 0000000..dba9ab3 --- /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 0000000..db4faee --- /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 0000000..9c5e9e0 --- /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 0000000..d4342dd --- /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 09c7739..5823b0e 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 4ef33ff..eafdd69 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 49a98c2..4125f9f 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 6feab51..9a58fa6 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 3884308..7e4e6bc 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 0000000..c752b0f --- /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 a051af2..4f36afe 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 0000000..63b1209 --- /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 28d5fe7..9b90179 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 |