aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2011-06-08 10:09:47 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-06-08 10:09:47 -0700
commitd689f8cb9d3ad0d50a4b6e9c6c7b7f02939c4c72 (patch)
treef9a3b1867d52a0f2c44afd8540e8a0e45209315e
parentbc70b8e05fb595bd4aafd796517122c116a4675a (diff)
parent0b15698a8c76bb8abc1b555c1d91892669b4118f (diff)
downloadskia-d689f8cb9d3ad0d50a4b6e9c6c7b7f02939c4c72.tar.gz
Merge "Skia Merge (revision 1510)"
-rw-r--r--Android.mk3
-rw-r--r--android_sample/SampleApp/Android.mk71
-rw-r--r--android_sample/SampleApp/AndroidManifest.xml32
-rw-r--r--android_sample/SampleApp/README.txt25
-rw-r--r--android_sample/SampleApp/jni/sample-jni.cpp208
-rw-r--r--android_sample/SampleApp/res/layout/layout.xml26
-rw-r--r--android_sample/SampleApp/res/values/strings.xml18
-rw-r--r--android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java114
-rw-r--r--bench/Android.mk6
-rw-r--r--bench/MatrixBench.cpp229
-rw-r--r--bench/bench_compare.py140
-rw-r--r--bench/benchmain.cpp146
-rw-r--r--gm/Android.mk1
-rw-r--r--gm/bitmapfilters.cpp17
-rw-r--r--gm/blurs.cpp33
-rw-r--r--gm/gm_files.mk1
-rw-r--r--gm/gmmain.cpp6
-rwxr-xr-xgm/nocolorbleed.cpp75
-rw-r--r--gm/pathfill.cpp31
-rw-r--r--gm/shadertext.cpp6
-rw-r--r--gm/shadows.cpp75
-rw-r--r--gm/shapes.cpp8
-rw-r--r--gm/strokerects.cpp9
-rw-r--r--gpu/include/GrClipIterator.h2
-rw-r--r--gpu/include/GrConfig.h2
-rw-r--r--gpu/include/GrContext.h58
-rw-r--r--gpu/include/GrContext_impl.h14
-rw-r--r--gpu/include/GrDrawTarget.h92
-rw-r--r--gpu/include/GrFontScaler.h4
-rw-r--r--gpu/include/GrGLDefines.h8
-rw-r--r--gpu/include/GrGLInterface.h7
-rw-r--r--gpu/include/GrGpu.h47
-rw-r--r--gpu/include/GrPaint.h177
-rw-r--r--gpu/include/GrPath.h87
-rw-r--r--gpu/include/GrPathIter.h74
-rw-r--r--gpu/include/GrPathRenderer.h23
-rw-r--r--gpu/include/GrSamplerState.h15
-rw-r--r--gpu/include/GrScalar.h1
-rw-r--r--gpu/include/GrStencil.h2
-rw-r--r--gpu/include/GrTesselatedPathRenderer.h10
-rw-r--r--gpu/include/GrTypes.h10
-rw-r--r--gpu/src/GrBinHashKey.h80
-rw-r--r--gpu/src/GrClip.cpp2
-rw-r--r--gpu/src/GrContext.cpp206
-rw-r--r--gpu/src/GrDrawTarget.cpp66
-rw-r--r--gpu/src/GrGLEffect.h52
-rw-r--r--gpu/src/GrGLInterface.cpp9
-rw-r--r--gpu/src/GrGLProgram.cpp635
-rw-r--r--gpu/src/GrGLProgram.h168
-rw-r--r--gpu/src/GrGpu.cpp30
-rw-r--r--gpu/src/GrGpuGL.cpp120
-rw-r--r--gpu/src/GrGpuGL.h18
-rw-r--r--gpu/src/GrGpuGLFixed.cpp7
-rw-r--r--gpu/src/GrGpuGLFixed.h2
-rw-r--r--gpu/src/GrGpuGLShaders.cpp245
-rw-r--r--gpu/src/GrGpuGLShaders.h3
-rw-r--r--gpu/src/GrPath.cpp523
-rw-r--r--gpu/src/GrPathRenderer.cpp34
-rw-r--r--gpu/src/GrPathUtils.cpp20
-rw-r--r--gpu/src/GrPathUtils.h5
-rw-r--r--gpu/src/GrStencil.cpp9
-rw-r--r--gpu/src/GrTesselatedPathRenderer.cpp122
-rw-r--r--gpu/src/GrTextContext.cpp45
-rw-r--r--gpu/src/gr_files.mk2
-rw-r--r--gpu/src/gr_unittests.cpp141
-rw-r--r--gpu/src/mac/GrGLDefaultInterface_mac.cpp2
-rw-r--r--gpu/src/mesa/GrGLDefaultInterface_mesa.cpp184
-rw-r--r--gpu/src/unix/GrGLDefaultInterface_unix.cpp1
-rw-r--r--gpu/src/win/GrGLDefaultInterface_win.cpp1
-rw-r--r--gyp/common.gypi94
-rwxr-xr-xgyp/gyp_skia77
-rw-r--r--gyp/skia.gyp1589
-rw-r--r--include/config/SkUserConfig.h3
-rw-r--r--include/core/SkClipStack.h1
-rw-r--r--include/core/SkColorPriv.h20
-rw-r--r--include/core/SkComposeShader.h2
-rw-r--r--include/core/SkDevice.h27
-rw-r--r--include/core/SkMath.h2
-rw-r--r--include/core/SkMatrix.h21
-rw-r--r--include/core/SkPath.h38
-rw-r--r--include/core/SkPostConfig.h19
-rw-r--r--include/core/SkPreConfig.h10
-rw-r--r--include/core/SkReader32.h9
-rw-r--r--include/core/SkRect.h2
-rw-r--r--include/core/SkScalar.h39
-rw-r--r--include/core/SkScalerContext.h3
-rw-r--r--include/core/SkWriter32.h17
-rw-r--r--include/core/SkXfermode.h10
-rw-r--r--include/gpu/SkGpuDevice.h13
-rw-r--r--include/gpu/SkGr.h33
-rw-r--r--include/pdf/SkPDFCatalog.h102
-rw-r--r--include/pdf/SkPDFDevice.h227
-rw-r--r--include/pdf/SkPDFDocument.h84
-rw-r--r--include/pdf/SkPDFFont.h151
-rw-r--r--include/pdf/SkPDFFormXObject.h73
-rw-r--r--include/pdf/SkPDFGraphicState.h106
-rw-r--r--include/pdf/SkPDFImage.h95
-rw-r--r--include/pdf/SkPDFPage.h101
-rw-r--r--include/pdf/SkPDFShader.h110
-rw-r--r--include/pdf/SkPDFStream.h55
-rw-r--r--include/pdf/SkPDFTypes.h332
-rw-r--r--include/pdf/SkPDFUtils.h61
-rw-r--r--include/pipe/SkGPipe.h96
-rw-r--r--include/utils/SkEGLContext.h43
-rw-r--r--include/utils/android/AndroidKeyToSkKey.h40
-rw-r--r--include/utils/unix/XkeysToSkKeys.h8
-rw-r--r--include/views/SkApplication.h26
-rw-r--r--include/views/SkBGViewArtist.h41
-rw-r--r--include/views/SkBorderView.h48
-rw-r--r--include/views/SkEvent.h257
-rw-r--r--include/views/SkEventSink.h103
-rw-r--r--include/views/SkImageView.h75
-rw-r--r--include/views/SkKey.h63
-rw-r--r--include/views/SkOSMenu.h55
-rw-r--r--include/views/SkOSWindow_Android.h41
-rw-r--r--include/views/SkOSWindow_Mac.h62
-rw-r--r--include/views/SkOSWindow_SDL.h52
-rw-r--r--include/views/SkOSWindow_Unix.h79
-rw-r--r--include/views/SkOSWindow_Win.h79
-rw-r--r--include/views/SkOSWindow_wxwidgets.h52
-rw-r--r--include/views/SkProgressBarView.h57
-rw-r--r--include/views/SkScrollBarView.h52
-rw-r--r--include/views/SkStackViewLayout.h96
-rw-r--r--include/views/SkSystemEventTypes.h32
-rw-r--r--include/views/SkTouchGesture.h72
-rw-r--r--include/views/SkView.h370
-rw-r--r--include/views/SkViewInflate.h79
-rw-r--r--include/views/SkWidget.h476
-rw-r--r--include/views/SkWidgetViews.h312
-rw-r--r--include/views/SkWindow.h125
-rw-r--r--samplecode/ClockFaceView.cpp255
-rw-r--r--samplecode/OverView.cpp94
-rw-r--r--samplecode/SampleAARects.cpp191
-rw-r--r--samplecode/SampleAll.cpp715
-rw-r--r--samplecode/SampleAnimatedGradient.cpp90
-rw-r--r--samplecode/SampleAnimator.cpp159
-rw-r--r--samplecode/SampleApp.cpp1647
-rw-r--r--samplecode/SampleArc.cpp184
-rw-r--r--samplecode/SampleAvoid.cpp99
-rw-r--r--samplecode/SampleBigGradient.cpp43
-rw-r--r--samplecode/SampleBitmapRect.cpp92
-rw-r--r--samplecode/SampleBlur.cpp131
-rw-r--r--samplecode/SampleBox.cpp48
-rw-r--r--samplecode/SampleCamera.cpp99
-rw-r--r--samplecode/SampleCircle.cpp126
-rw-r--r--samplecode/SampleClamp.cpp61
-rw-r--r--samplecode/SampleCode.h81
-rw-r--r--samplecode/SampleColorFilter.cpp210
-rw-r--r--samplecode/SampleComplexClip.cpp147
-rw-r--r--samplecode/SampleCull.cpp189
-rw-r--r--samplecode/SampleDash.cpp88
-rw-r--r--samplecode/SampleDecode.cpp69
-rw-r--r--samplecode/SampleDither.cpp178
-rw-r--r--samplecode/SampleDitherBitmap.cpp141
-rw-r--r--samplecode/SampleDraw.cpp373
-rw-r--r--samplecode/SampleDrawLooper.cpp92
-rw-r--r--samplecode/SampleEffects.cpp130
-rw-r--r--samplecode/SampleEmboss.cpp66
-rw-r--r--samplecode/SampleEncode.cpp224
-rw-r--r--samplecode/SampleExtractAlpha.cpp90
-rw-r--r--samplecode/SampleFillType.cpp90
-rw-r--r--samplecode/SampleFilter.cpp138
-rw-r--r--samplecode/SampleFilter2.cpp116
-rw-r--r--samplecode/SampleFontCache.cpp135
-rw-r--r--samplecode/SampleFontScalerTest.cpp117
-rw-r--r--samplecode/SampleFuzz.cpp363
-rw-r--r--samplecode/SampleGM.cpp114
-rw-r--r--samplecode/SampleGradients.cpp176
-rw-r--r--samplecode/SampleHairline.cpp272
-rw-r--r--samplecode/SampleImage.cpp156
-rw-r--r--samplecode/SampleImageDir.cpp310
-rw-r--r--samplecode/SampleLCD.cpp61
-rw-r--r--samplecode/SampleLayerMask.cpp68
-rw-r--r--samplecode/SampleLayers.cpp271
-rw-r--r--samplecode/SampleLineClipper.cpp255
-rw-r--r--samplecode/SampleLines.cpp109
-rw-r--r--samplecode/SampleMeasure.cpp115
-rw-r--r--samplecode/SampleMipMap.cpp143
-rw-r--r--samplecode/SampleMovie.cpp61
-rw-r--r--samplecode/SampleNinePatch.cpp114
-rw-r--r--samplecode/SampleOvalTest.cpp110
-rw-r--r--samplecode/SampleOverflow.cpp100
-rw-r--r--samplecode/SamplePageFlip.cpp167
-rw-r--r--samplecode/SamplePatch.cpp342
-rw-r--r--samplecode/SamplePath.cpp200
-rw-r--r--samplecode/SamplePathClip.cpp84
-rw-r--r--samplecode/SamplePathEffects.cpp184
-rw-r--r--samplecode/SamplePathFill.cpp140
-rw-r--r--samplecode/SamplePicture.cpp254
-rw-r--r--samplecode/SamplePoints.cpp78
-rw-r--r--samplecode/SamplePolyToPoly.cpp161
-rw-r--r--samplecode/SampleRegion.cpp273
-rw-r--r--samplecode/SampleRepeatTile.cpp86
-rw-r--r--samplecode/SampleShaderText.cpp195
-rw-r--r--samplecode/SampleShaders.cpp134
-rw-r--r--samplecode/SampleShapes.cpp160
-rw-r--r--samplecode/SampleSkLayer.cpp239
-rw-r--r--samplecode/SampleSlides.cpp804
-rw-r--r--samplecode/SampleSpiral.cpp56
-rw-r--r--samplecode/SampleStrokePath.cpp217
-rw-r--r--samplecode/SampleStrokeRect.cpp69
-rw-r--r--samplecode/SampleStrokeText.cpp140
-rw-r--r--samplecode/SampleTests.cpp111
-rw-r--r--samplecode/SampleText.cpp396
-rw-r--r--samplecode/SampleTextAlpha.cpp114
-rw-r--r--samplecode/SampleTextBox.cpp91
-rw-r--r--samplecode/SampleTextEffects.cpp397
-rw-r--r--samplecode/SampleTextOnPath.cpp284
-rwxr-xr-xsamplecode/SampleTextureDomain.cpp80
-rw-r--r--samplecode/SampleTiling.cpp162
-rw-r--r--samplecode/SampleTinyBitmap.cpp76
-rw-r--r--samplecode/SampleTriangles.cpp118
-rw-r--r--samplecode/SampleTypeface.cpp128
-rw-r--r--samplecode/SampleUnitMapper.cpp157
-rw-r--r--samplecode/SampleVertices.cpp230
-rw-r--r--samplecode/SampleWarp.cpp467
-rw-r--r--samplecode/SampleXfermodes.cpp250
-rw-r--r--samplecode/SampleXfermodesBlur.cpp182
-rw-r--r--samplecode/samplecode_files.mk69
-rw-r--r--samplecode/vertexdump.cpp88
-rw-r--r--src/animator/SkDisplayApply.cpp10
-rw-r--r--src/animator/SkDisplayXMLParser.cpp4
-rw-r--r--src/animator/SkDump.cpp2
-rw-r--r--src/animator/SkScript.cpp6
-rw-r--r--src/animator/SkScriptDecompile.cpp10
-rw-r--r--src/animator/SkScriptRuntime.cpp2
-rw-r--r--src/animator/SkScriptTokenizer.cpp85
-rw-r--r--src/core/SkAdvancedTypefaceMetrics.cpp9
-rw-r--r--src/core/SkAlphaRuns.cpp24
-rw-r--r--src/core/SkAntiRun.h10
-rw-r--r--src/core/SkBitmapProcState_matrix.h16
-rw-r--r--src/core/SkBitmapProcState_shaderproc.h17
-rw-r--r--src/core/SkClipStack.cpp5
-rw-r--r--src/core/SkDevice.cpp12
-rw-r--r--src/core/SkMatrix.cpp33
-rw-r--r--src/core/SkPaint.cpp29
-rw-r--r--src/core/SkPath.cpp22
-rw-r--r--src/core/SkPictureFlat.cpp4
-rw-r--r--src/core/SkPictureFlat.h2
-rw-r--r--src/core/SkScan_AntiPath.cpp65
-rw-r--r--src/core/SkScan_Antihair.cpp6
-rw-r--r--src/core/SkWriter32.cpp69
-rw-r--r--src/effects/Sk2DPathEffect.cpp11
-rw-r--r--src/effects/SkTransparentShader.cpp10
-rw-r--r--src/gpu/GrPrintf_skia.cpp2
-rw-r--r--src/gpu/SkGpuDevice.cpp212
-rw-r--r--src/gpu/SkGr.cpp36
-rw-r--r--src/gpu/SkGrFontScaler.cpp4
-rw-r--r--src/pdf/SkPDFCatalog.cpp128
-rw-r--r--src/pdf/SkPDFDevice.cpp1488
-rw-r--r--src/pdf/SkPDFDocument.cpp192
-rwxr-xr-xsrc/pdf/SkPDFFont.cpp976
-rw-r--r--src/pdf/SkPDFFormXObject.cpp94
-rw-r--r--src/pdf/SkPDFGraphicState.cpp294
-rw-r--r--src/pdf/SkPDFImage.cpp382
-rw-r--r--src/pdf/SkPDFPage.cpp142
-rw-r--r--src/pdf/SkPDFShader.cpp778
-rw-r--r--src/pdf/SkPDFStream.cpp61
-rw-r--r--src/pdf/SkPDFTypes.cpp392
-rw-r--r--src/pdf/SkPDFUtils.cpp187
-rw-r--r--src/pdf/pdf_files.mk13
-rw-r--r--src/pipe/SkGPipePriv.h213
-rw-r--r--src/pipe/SkGPipeRead.cpp570
-rw-r--r--src/pipe/SkGPipeWrite.cpp813
-rw-r--r--src/ports/SkFontHost_FreeType.cpp29
-rw-r--r--src/ports/SkFontHost_linux.cpp2
-rw-r--r--src/ports/SkFontHost_mac_coretext.cpp244
-rwxr-xr-xsrc/ports/SkFontHost_win.cpp120
-rw-r--r--src/svg/SkSVGParser.cpp6
-rw-r--r--src/utils/SkEGLContext_none.cpp2
-rw-r--r--src/utils/mac/SkBitmap_Mac.cpp142
-rw-r--r--src/utils/mac/SkCreateCGImageRef.cpp129
-rw-r--r--src/utils/mac/SkEGLContext_mac.cpp68
-rw-r--r--src/utils/mac/SkOSWindow_Mac.cpp534
-rw-r--r--src/utils/mac/skia_mac.cpp43
-rw-r--r--src/utils/mesa/SkEGLContext_Mesa.cpp128
-rw-r--r--src/utils/unix/SkEGLContext_Unix.cpp264
-rw-r--r--src/utils/unix/SkOSWindow_Unix.cpp2
-rw-r--r--src/views/SkBGViewArtist.cpp24
-rw-r--r--src/views/SkBorderView.cpp89
-rw-r--r--src/views/SkEvent.cpp580
-rw-r--r--src/views/SkEventSink.cpp345
-rw-r--r--src/views/SkImageView.cpp296
-rw-r--r--src/views/SkListView.cpp895
-rw-r--r--src/views/SkListWidget.cpp623
-rw-r--r--src/views/SkOSMenu.cpp53
-rw-r--r--src/views/SkParsePaint.cpp103
-rw-r--r--src/views/SkProgressBarView.cpp102
-rw-r--r--src/views/SkProgressView.cpp125
-rw-r--r--src/views/SkScrollBarView.cpp139
-rw-r--r--src/views/SkStackViewLayout.cpp262
-rw-r--r--src/views/SkStaticTextView.cpp177
-rw-r--r--src/views/SkTagList.cpp71
-rw-r--r--src/views/SkTagList.h51
-rw-r--r--src/views/SkTextBox.cpp237
-rw-r--r--src/views/SkTouchGesture.cpp336
-rw-r--r--src/views/SkView.cpp793
-rw-r--r--src/views/SkViewInflate.cpp139
-rw-r--r--src/views/SkViewPriv.cpp97
-rw-r--r--src/views/SkViewPriv.h38
-rw-r--r--src/views/SkWidget.cpp323
-rw-r--r--src/views/SkWidgetViews.cpp405
-rw-r--r--src/views/SkWidgets.cpp554
-rw-r--r--src/views/SkWindow.cpp412
-rw-r--r--src/views/views_files.mk26
-rw-r--r--src/xml/xml_files.mk3
-rw-r--r--tests/Android.mk2
-rw-r--r--tests/ClipStackTest.cpp3
-rw-r--r--tests/MatrixTest.cpp6
-rw-r--r--tests/PDFPrimitivesTest.cpp6
-rw-r--r--tests/PathTest.cpp53
-rw-r--r--tests/Reader32Test.cpp93
-rw-r--r--tests/UtilsTest.cpp2
-rw-r--r--tests/Writer32Test.cpp90
-rw-r--r--tests/tests_files.mk2
315 files changed, 40788 insertions, 3871 deletions
diff --git a/Android.mk b/Android.mk
index 6c27066cec..41d17afaaa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -30,6 +30,7 @@ LOCAL_SRC_FILES:= \
src/core/SkCordic.cpp \
src/core/SkDebug.cpp \
src/core/SkFloatBits.cpp \
+ src/core/SkFontHost.cpp \
src/core/SkPoint.cpp \
src/core/SkRect.cpp \
src/core/SkRegion.cpp \
@@ -181,6 +182,7 @@ LOCAL_SRC_FILES:= \
src/utils/SkBoundaryPatch.cpp \
src/utils/SkCamera.cpp \
src/utils/SkDumpCanvas.cpp \
+ src/utils/SkEGLContext_none.cpp \
src/utils/SkInterpolator.cpp \
src/utils/SkLayer.cpp \
src/utils/SkOSFile.cpp \
@@ -290,7 +292,6 @@ LOCAL_SRC_FILES:= \
gpu/src/GrInOrderDrawBuffer.cpp \
gpu/src/GrMatrix.cpp \
gpu/src/GrMemory.cpp \
- gpu/src/GrPath.cpp \
gpu/src/GrPathUtils.cpp \
gpu/src/GrRectanizer_fifo.cpp \
gpu/src/GrResource.cpp \
diff --git a/android_sample/SampleApp/Android.mk b/android_sample/SampleApp/Android.mk
new file mode 100644
index 0000000000..6d62c35426
--- /dev/null
+++ b/android_sample/SampleApp/Android.mk
@@ -0,0 +1,71 @@
+######################################
+# Build the app.
+######################################
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SampleApp
+
+LOCAL_JNI_SHARED_LIBRARIES := libskia-sample
+
+include $(BUILD_PACKAGE)
+
+######################################
+# Build the shared library.
+######################################
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES += \
+ external/skia/include/core \
+ external/skia/include/config \
+ external/skia/include/effects \
+ external/skia/include/images \
+ external/skia/include/utils \
+ external/skia/include/utils/android \
+ external/skia/include/views \
+ external/skia/samplecode \
+ external/skia/include/xml \
+ external/skia/include/gpu \
+ external/skia/src/core \
+ external/skia/gpu/include \
+ frameworks/base/core/jni/android/graphics \
+ frameworks/base/native/include/android \
+ $(LOCAL_PATH)/jni
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libskia \
+ libandroid_runtime \
+ libGLESv2
+
+LOCAL_STATIC_LIBRARIES := \
+ libskiagpu
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE := libskia-sample
+
+LOCAL_SRC_FILES := \
+ ../../src/ports/SkXMLParser_empty.cpp \
+ jni/sample-jni.cpp
+
+include external/skia/src/views/views_files.mk
+LOCAL_SRC_FILES += $(addprefix ../../src/views/, $(SOURCE))
+
+include external/skia/src/xml/xml_files.mk
+LOCAL_SRC_FILES += $(addprefix ../../src/xml/, $(SOURCE))
+
+include external/skia/samplecode/samplecode_files.mk
+LOCAL_SRC_FILES += $(addprefix ../../samplecode/, $(SOURCE))
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android_sample/SampleApp/AndroidManifest.xml b/android_sample/SampleApp/AndroidManifest.xml
new file mode 100644
index 0000000000..e3232468f6
--- /dev/null
+++ b/android_sample/SampleApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Skia
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.skia.sampleapp"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="3" />
+ <application android:label="@string/app_name"
+ android:debuggable="true">
+ <activity android:name=".SampleApp"
+ android:theme="@android:style/Theme.Holo.NoActionBar"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/android_sample/SampleApp/README.txt b/android_sample/SampleApp/README.txt
new file mode 100644
index 0000000000..a7d8e54d2e
--- /dev/null
+++ b/android_sample/SampleApp/README.txt
@@ -0,0 +1,25 @@
+Building the sample app for Android using an Android tree:
+
+Copy this folder into an Android tree in packages/apps. In addition to jni,
+res, and src, there needs to be a fourth folder named "skia_extra". This
+will include the skia files which are not part of an Android checkout. It
+should have three folders: include, samplecode, and src.
+
+skia/trunk/include/views -> skia_extra/include/views
+skia/trunk/include/xml -> skia_extra/include/xml
+
+skia/trunk/samplecode -> skia_extra/samplecode
+
+skia/trunk/src/views -> skia_extra/src/views
+skia/trunk/src/ports/SkXMLParser_empty.cpp -> skia_extra/src/ports/
+skia/trunk/src/xml -> skia_extra/src/xml
+
+skia/trunk/include/utils/android/AndroidKeyToSkKey.h -> jni/
+
+From packages/apps/SampleApp, type "mm" to build, and install the
+resulting apk.
+
+(It may be necessary to remove samples that do not build from
+skia_extra/samplecode/samplecode_files.mk)
+
+TODO: Instructions for building from SDK/NDK
diff --git a/android_sample/SampleApp/jni/sample-jni.cpp b/android_sample/SampleApp/jni/sample-jni.cpp
new file mode 100644
index 0000000000..529047c318
--- /dev/null
+++ b/android_sample/SampleApp/jni/sample-jni.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <jni.h>
+
+#include "SkCanvas.h"
+#include "GraphicsJNI.h"
+#include "SkEvent.h"
+#include "SkWindow.h"
+#include "SkApplication.h"
+#include "utils/android/AndroidKeyToSkKey.h"
+
+///////////////////////////////////////////
+///////////////// Globals /////////////////
+///////////////////////////////////////////
+
+struct ActivityGlue {
+ JNIEnv* m_env;
+ jweak m_obj;
+ jmethodID m_setTitle;
+ ActivityGlue() {
+ m_env = NULL;
+ m_obj = NULL;
+ m_setTitle = NULL;
+ }
+} gActivityGlue;
+
+struct WindowGlue {
+ jweak m_obj;
+ jmethodID m_inval;
+ WindowGlue() {
+ m_obj = NULL;
+ m_inval = NULL;
+ }
+} gWindowGlue;
+
+SkOSWindow* gWindow;
+
+///////////////////////////////////////////
+///////////// SkOSWindow impl /////////////
+///////////////////////////////////////////
+
+void SkOSWindow::onSetTitle(const char title[])
+{
+ if (gActivityGlue.m_env) {
+ JNIEnv* env = gActivityGlue.m_env;
+ jstring string = env->NewStringUTF(title);
+ env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_setTitle,
+ string);
+ env->DeleteLocalRef(string);
+ }
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& rect)
+{
+ if (!gActivityGlue.m_env || !gWindowGlue.m_inval || !gWindowGlue.m_obj) {
+ return;
+ }
+ gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+}
+
+///////////////////////////////////////////
+/////////////// SkEvent impl //////////////
+///////////////////////////////////////////
+
+void SkEvent::SignalQueueTimer(SkMSec) {}
+
+void SkEvent::SignalNonEmptyQueue() {}
+
+///////////////////////////////////////////
+////////////////// JNI ////////////////////
+///////////////////////////////////////////
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[],
+ const char signature[])
+{
+ jmethodID m = env->GetMethodID(clazz, name, signature);
+ if (!m) SkDebugf("Could not find Java method %s\n", name);
+ return m;
+}
+
+extern "C" {
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas(
+ JNIEnv* env, jobject thiz, jobject jcanvas);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(
+ JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(
+ JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(
+ JNIEnv* env, jobject thiz, jint w, jint h);
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
+ JNIEnv* env, jobject thiz, jint keyCode, jint uni);
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(
+ JNIEnv* env, jobject thiz, jint keyCode);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(
+ JNIEnv* env, jobject thiz, jint x, jint y, jint state);
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow(
+ JNIEnv* env, jobject thiz, jobject jsampleView);
+};
+
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
+ JNIEnv* env, jobject thiz, jint keyCode, jint uni)
+{
+ bool handled = gWindow->handleKey(AndroidKeycodeToSkKey(keyCode));
+ handled |= gWindow->handleChar((SkUnichar) uni);
+ return handled;
+}
+
+JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(JNIEnv* env,
+ jobject thiz, jint keyCode)
+{
+ return gWindow->handleKeyUp(AndroidKeycodeToSkKey(keyCode));
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(JNIEnv* env,
+ jobject thiz, jint x, jint y, jint jstate)
+{
+ SkView::Click::State state;
+ switch(jstate) {
+ case 0: // MotionEvent.ACTION_DOWN
+ state = SkView::Click::kDown_State;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ case 3: // MotionEvent.ACTION_CANCEL
+ state = SkView::Click::kUp_State;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ state = SkView::Click::kMoved_State;
+ break;
+ default:
+ SkDebugf("motion event ignored\n");
+ return;
+ }
+ gWindow->handleClick(x, y, state);
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(JNIEnv* env,
+ jobject thiz, jint w, jint h)
+{
+ gWindow->resize(w, h);
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow(
+ JNIEnv* env, jobject thiz, jobject jsampleView)
+{
+ gWindow = create_sk_window(NULL);
+ // Only using a method on View.
+ jclass clazz = gActivityGlue.m_env->FindClass("android/view/View");
+ gWindowGlue.m_obj = gActivityGlue.m_env->NewWeakGlobalRef(jsampleView);
+ gWindowGlue.m_inval = GetJMethod(gActivityGlue.m_env, clazz, "invalidate",
+ "(IIII)V");
+ gActivityGlue.m_env->DeleteLocalRef(clazz);
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env,
+ jobject thiz)
+{
+ gActivityGlue.m_env = env;
+ // Only using a method on Activity.
+ jclass clazz = env->FindClass("android/app/Activity");
+ gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz);
+ gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle",
+ "(Ljava/lang/CharSequence;)V");
+ env->DeleteLocalRef(clazz);
+
+ application_init();
+}
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(JNIEnv* env,
+ jobject thiz)
+{
+ application_term();
+ if (gWindowGlue.m_obj) {
+ env->DeleteWeakGlobalRef(gWindowGlue.m_obj);
+ gWindowGlue.m_obj = NULL;
+ }
+ if (gActivityGlue.m_obj) {
+ env->DeleteWeakGlobalRef(gActivityGlue.m_obj);
+ gActivityGlue.m_obj = NULL;
+ }
+ delete gWindow;
+ gWindow = NULL;
+}
+
+
+JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas(
+ JNIEnv* env, jobject thiz, jobject jcanvas)
+{
+ if (!gWindow) return;
+ gWindow->update(NULL);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
+ canvas->drawBitmap(gWindow->getBitmap(), 0, 0);
+}
diff --git a/android_sample/SampleApp/res/layout/layout.xml b/android_sample/SampleApp/res/layout/layout.xml
new file mode 100644
index 0000000000..d71116bae1
--- /dev/null
+++ b/android_sample/SampleApp/res/layout/layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Skia
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/holder"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/title_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+</LinearLayout>
+
diff --git a/android_sample/SampleApp/res/values/strings.xml b/android_sample/SampleApp/res/values/strings.xml
new file mode 100644
index 0000000000..72d3bc8596
--- /dev/null
+++ b/android_sample/SampleApp/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 Skia
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="app_name">SampleApp</string>
+</resources>
diff --git a/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
new file mode 100644
index 0000000000..b02c4d7fe4
--- /dev/null
+++ b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.skia.sampleapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class SampleApp extends Activity
+{
+ private TextView mTitle;
+
+ public class SampleView extends View {
+ public SampleView(Context context) {
+ super(context);
+ createOSWindow(this);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawToCanvas(canvas);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ updateSize(w, h);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ final int action = event.getAction();
+ handleClick(x, y, action);
+ return true;
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ init();
+ setContentView(R.layout.layout);
+ mTitle = (TextView) findViewById(R.id.title_view);
+ LinearLayout holder = (LinearLayout) findViewById(R.id.holder);
+ View view = new SampleView(this);
+ holder.addView(view, new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ term();
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ int uni = event.getUnicodeChar(event.getMetaState());
+ return handleKeyDown(event.getKeyCode(), uni);
+ case KeyEvent.ACTION_UP:
+ return handleKeyUp(event.getKeyCode());
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitle.setText(title);
+ }
+
+ private native void drawToCanvas(Canvas canvas);
+ private native void init();
+ private native void term();
+ // Currently depends on init having already been called.
+ private native void createOSWindow(SampleView view);
+ private native void updateSize(int w, int h);
+ private native void handleClick(int x, int y, int state);
+ private native boolean handleKeyDown(int key, int uni);
+ private native boolean handleKeyUp(int key);
+
+ static {
+ System.loadLibrary("skia-sample");
+ }
+}
diff --git a/bench/Android.mk b/bench/Android.mk
index b20aef9729..71523af4e9 100644
--- a/bench/Android.mk
+++ b/bench/Android.mk
@@ -6,6 +6,9 @@ LOCAL_SRC_FILES := \
BitmapBench.cpp \
DecodeBench.cpp \
FPSBench.cpp \
+ GradientBench.cpp \
+ MatrixBench.cpp \
+ PathBench.cpp \
RectBench.cpp \
RepeatTileBench.cpp \
TextBench.cpp \
@@ -17,7 +20,8 @@ LOCAL_SRC_FILES += \
../src/utils/SkNWayCanvas.cpp \
../src/utils/SkParse.cpp
-LOCAL_SHARED_LIBRARIES := libcutils libskia
+LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2
+LOCAL_STATIC_LIBRARIES := libskiagpu
LOCAL_C_INCLUDES := \
external/skia/include/config \
external/skia/include/core \
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
new file mode 100644
index 0000000000..d963bc7126
--- /dev/null
+++ b/bench/MatrixBench.cpp
@@ -0,0 +1,229 @@
+#include "SkBenchmark.h"
+#include "SkMatrix.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+class MatrixBench : public SkBenchmark {
+ SkString fName;
+ enum { N = 100000 };
+public:
+ MatrixBench(void* param, const char name[]) : INHERITED(param) {
+ fName.printf("matrix_%s", name);
+ }
+
+ virtual void performTest() = 0;
+
+protected:
+ virtual int mulLoopCount() const { return 1; }
+
+ virtual const char* onGetName() {
+ return fName.c_str();
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ int n = N * this->mulLoopCount();
+ for (int i = 0; i < n; i++) {
+ this->performTest();
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+// we want to stop the compiler from eliminating code that it thinks is a no-op
+// so we have a non-static global we increment, hoping that will convince the
+// compiler to execute everything
+int gMatrixBench_NonStaticGlobal;
+
+#define always_do(pred) \
+ do { \
+ if (pred) { \
+ ++gMatrixBench_NonStaticGlobal; \
+ } \
+ } while (0)
+
+class EqualsMatrixBench : public MatrixBench {
+public:
+ EqualsMatrixBench(void* param) : INHERITED(param, "equals") {}
+protected:
+ virtual void performTest() {
+ SkMatrix m0, m1, m2;
+
+ m0.reset();
+ m1.reset();
+ m2.reset();
+ always_do(m0 == m1);
+ always_do(m1 == m2);
+ always_do(m2 == m0);
+ always_do(m0.getType());
+ always_do(m1.getType());
+ always_do(m2.getType());
+ }
+private:
+ typedef MatrixBench INHERITED;
+};
+
+class ScaleMatrixBench : public MatrixBench {
+public:
+ ScaleMatrixBench(void* param) : INHERITED(param, "scale") {
+
+ fM0.reset();
+ fM1.setScale(fSX, fSY);
+ fM2.setTranslate(fSX, fSY);
+ fSX = fSY = SkFloatToScalar(1.5f);
+ }
+protected:
+ virtual void performTest() {
+ SkMatrix m;
+ m = fM0; m.preScale(fSX, fSY);
+ m = fM1; m.preScale(fSX, fSY);
+ m = fM2; m.preScale(fSX, fSY);
+ }
+private:
+ SkMatrix fM0, fM1, fM2;
+ SkScalar fSX, fSY;
+ typedef MatrixBench INHERITED;
+};
+
+// having unknown values in our arrays can throw off the timing a lot, perhaps
+// handling NaN values is a lot slower. Anyway, this guy is just meant to put
+// reasonable values in our arrays.
+template <typename T> void init9(T array[9]) {
+ SkRandom rand;
+ for (int i = 0; i < 9; i++) {
+ array[i] = rand.nextSScalar1();
+ }
+}
+
+// Test the performance of setConcat() non-perspective case:
+// using floating point precision only.
+class FloatConcatMatrixBench : public MatrixBench {
+public:
+ FloatConcatMatrixBench(void* p) : INHERITED(p, "concat_floatfloat") {
+ init9(mya);
+ init9(myb);
+ init9(myr);
+ }
+protected:
+ virtual int mulLoopCount() const { return 4; }
+
+ static inline void muladdmul(float a, float b, float c, float d,
+ float* result) {
+ *result = a * b + c * d;
+ }
+ virtual void performTest() {
+ const float* a = mya;
+ const float* b = myb;
+ float* r = myr;
+ muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+ muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+ muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+ r[2] += a[2];
+ muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+ muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+ muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+ r[5] += a[5];
+ r[6] = r[7] = 0.0f;
+ r[8] = 1.0f;
+ }
+private:
+ float mya [9];
+ float myb [9];
+ float myr [9];
+ typedef MatrixBench INHERITED;
+};
+
+static inline float SkDoubleToFloat(double x) {
+ return static_cast<float>(x);
+}
+
+// Test the performance of setConcat() non-perspective case:
+// using floating point precision but casting up to float for
+// intermediate results during computations.
+class FloatDoubleConcatMatrixBench : public MatrixBench {
+public:
+ FloatDoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_floatdouble") {
+ init9(mya);
+ init9(myb);
+ init9(myr);
+ }
+protected:
+ virtual int mulLoopCount() const { return 4; }
+
+ static inline void muladdmul(float a, float b, float c, float d,
+ float* result) {
+ *result = SkDoubleToFloat((double)a * b + (double)c * d);
+ }
+ virtual void performTest() {
+ const float* a = mya;
+ const float* b = myb;
+ float* r = myr;
+ muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+ muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+ muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+ r[2] += a[2];
+ muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+ muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+ muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+ r[5] += a[5];
+ r[6] = r[7] = 0.0f;
+ r[8] = 1.0f;
+ }
+private:
+ float mya [9];
+ float myb [9];
+ float myr [9];
+ typedef MatrixBench INHERITED;
+};
+
+// Test the performance of setConcat() non-perspective case:
+// using double precision only.
+class DoubleConcatMatrixBench : public MatrixBench {
+public:
+ DoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_double") {
+ init9(mya);
+ init9(myb);
+ init9(myr);
+ }
+protected:
+ virtual int mulLoopCount() const { return 4; }
+
+ static inline void muladdmul(double a, double b, double c, double d,
+ double* result) {
+ *result = a * b + c * d;
+ }
+ virtual void performTest() {
+ const double* a = mya;
+ const double* b = myb;
+ double* r = myr;
+ muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+ muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+ muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+ r[2] += a[2];
+ muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+ muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+ muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+ r[5] += a[5];
+ r[6] = r[7] = 0.0;
+ r[8] = 1.0;
+ }
+private:
+ double mya [9];
+ double myb [9];
+ double myr [9];
+ typedef MatrixBench INHERITED;
+};
+
+
+static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); }
+static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); }
+static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); }
+static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); }
+static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); }
+
+static BenchRegistry gReg0(M0);
+static BenchRegistry gReg1(M1);
+static BenchRegistry gReg2(M2);
+static BenchRegistry gReg3(M3);
+static BenchRegistry gReg4(M4);
diff --git a/bench/bench_compare.py b/bench/bench_compare.py
new file mode 100644
index 0000000000..f6909b10b0
--- /dev/null
+++ b/bench/bench_compare.py
@@ -0,0 +1,140 @@
+'''
+Created on May 16, 2011
+
+@author: bungeman
+'''
+import sys
+import getopt
+import re
+
+def parse(lines):
+ """Takes iterable lines of bench output, returns {bench:{config:time}}."""
+
+ benches = {}
+ current_bench = None
+
+ for line in lines:
+ #see if this line starts a new bench
+ new_bench = re.search('running bench \[\d+ \d+\] (.{28})', line)
+ if new_bench:
+ current_bench = new_bench.group(1)
+
+ #add configs on this line to the current bench
+ if current_bench:
+ for new_config in re.finditer(' (.{4}): msecs = (\d+\.\d+)', line):
+ current_config = new_config.group(1)
+ current_time = float(new_config.group(2))
+ if current_bench in benches:
+ benches[current_bench][current_config] = current_time
+ else:
+ benches[current_bench] = {current_config : current_time}
+
+ return benches
+
+def usage():
+ """Prints simple usage information."""
+
+ print '-o <file> the old bench output file.'
+ print '-n <file> the new bench output file.'
+ print '-h causes headers to be output.'
+ print '-f <fieldSpec> which fields to output and in what order.'
+ print ' Not specifying is the same as -f "bcondp".'
+ print ' b: bench'
+ print ' c: config'
+ print ' o: old time'
+ print ' n: new time'
+ print ' d: diff'
+ print ' p: percent diff'
+
+
+def main():
+ """Parses command line and writes output."""
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "f:o:n:h")
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ column_formats = {
+ 'b' : '{bench: >28} ',
+ 'c' : '{config: <4} ',
+ 'o' : '{old_time: >10.2f} ',
+ 'n' : '{new_time: >10.2f} ',
+ 'd' : '{diff: >+10.2f} ',
+ 'p' : '{diffp: >+7.1%} ',
+ }
+ header_formats = {
+ 'b' : '{bench: >28} ',
+ 'c' : '{config: <4} ',
+ 'o' : '{old_time: >10} ',
+ 'n' : '{new_time: >10} ',
+ 'd' : '{diff: >10} ',
+ 'p' : '{diffp: >7} ',
+ }
+
+ old = None
+ new = None
+ column_format = ""
+ header_format = ""
+ columns = 'bcondp'
+ header = False
+
+ for option, value in opts:
+ if option == "-o":
+ old = value
+ elif option == "-n":
+ new = value
+ elif option == "-h":
+ header = True
+ elif option == "-f":
+ columns = value
+ else:
+ usage()
+ assert False, "unhandled option"
+
+ if old is None or new is None:
+ usage()
+ sys.exit(2)
+
+ for column_char in columns:
+ if column_formats[column_char]:
+ column_format += column_formats[column_char]
+ header_format += header_formats[column_char]
+ else:
+ usage()
+ sys.exit(2)
+
+ if header:
+ print header_format.format(
+ bench='bench'
+ , config='conf'
+ , old_time='old'
+ , new_time='new'
+ , diff='diff'
+ , diffp='diffP'
+ )
+
+ old_benches = parse(open(old, 'r'))
+ new_benches = parse(open(new, 'r'))
+
+ for old_bench, old_configs in old_benches.items():
+ if old_bench in new_benches:
+ new_configs = new_benches[old_bench]
+ for old_config, old_time in old_configs.items():
+ if old_config in new_configs:
+ new_time = new_configs[old_config]
+ old_time = old_configs[old_config]
+ print column_format.format(
+ bench=old_bench.strip()
+ , config=old_config.strip()
+ , old_time=old_time
+ , new_time=new_time
+ , diff=(old_time - new_time)
+ , diffp=((old_time-new_time)/old_time)
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 4f6d81ce96..066573aee9 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -6,6 +6,9 @@
#include "SkPicture.h"
#include "SkString.h"
#include "SkTime.h"
+#include "GrContext.h"
+#include "SkGpuDevice.h"
+#include "SkEGLContext.h"
#include "SkBenchmark.h"
@@ -30,6 +33,7 @@ static void erase(SkBitmap& bm) {
}
}
+#if 0
static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
if (bm1.width() != bm2.width() ||
bm1.height() != bm2.height() ||
@@ -43,9 +47,9 @@ static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
return false;
}
}
-
return true;
}
+#endif
class Iter {
public:
@@ -62,7 +66,7 @@ public:
}
return NULL;
}
-
+
private:
const BenchRegistry* fBench;
void* fParam;
@@ -102,7 +106,7 @@ static void saveFile(const char name[], const char config[], const char dir[],
*p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
}
}
-
+
SkString str;
make_filename(name, &str);
str.appendf("_%s.png", config);
@@ -118,7 +122,7 @@ static void performClip(SkCanvas* canvas, int w, int h) {
r.set(SkIntToScalar(10), SkIntToScalar(10),
SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
canvas->clipRect(r, SkRegion::kIntersect_Op);
-
+
r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
SkIntToScalar(w-10), SkIntToScalar(h-10));
canvas->clipRect(r, SkRegion::kXOR_Op);
@@ -143,21 +147,6 @@ static void performScale(SkCanvas* canvas, int w, int h) {
canvas->translate(-x, -y);
}
-static void compare_pict_to_bitmap(SkPicture* pict, const SkBitmap& bm) {
- SkBitmap bm2;
-
- bm2.setConfig(bm.config(), bm.width(), bm.height());
- bm2.allocPixels();
- erase(bm2);
-
- SkCanvas canvas(bm2);
- canvas.drawPicture(*pict);
-
- if (!equal(bm, bm2)) {
- SkDebugf("----- compare_pict_to_bitmap failed\n");
- }
-}
-
static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
if (argv < stop) {
*var = atoi(*argv) != 0;
@@ -166,16 +155,43 @@ static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
return false;
}
+enum Backend {
+ kRaster_Backend,
+ kGPU_Backend,
+ kPDF_Backend,
+};
+
+static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
+ Backend backend, GrContext* context) {
+ SkDevice* device = NULL;
+ SkBitmap bitmap;
+ bitmap.setConfig(config, size.fX, size.fY);
+
+ switch (backend) {
+ case kRaster_Backend:
+ bitmap.allocPixels();
+ erase(bitmap);
+ device = new SkDevice(NULL, bitmap, true);
+ break;
+ case kGPU_Backend:
+ device = new SkGpuDevice(context, bitmap, SkGpuDevice::Current3DApiRenderTarget());
+// device->clear(0xFFFFFFFF);
+ break;
+ case kPDF_Backend:
+ default:
+ SkASSERT(!"unsupported");
+ }
+ return device;
+}
+
static const struct {
SkBitmap::Config fConfig;
const char* fName;
+ Backend fBackend;
} gConfigs[] = {
- { SkBitmap::kARGB_8888_Config, "8888" },
- { SkBitmap::kRGB_565_Config, "565", },
-#if 0
- { SkBitmap::kARGB_4444_Config, "4444", },
- { SkBitmap::kA8_Config, "A8", }
-#endif
+ { SkBitmap::kARGB_8888_Config, "8888", kRaster_Backend },
+ { SkBitmap::kRGB_565_Config, "565", kRaster_Backend },
+ { SkBitmap::kARGB_8888_Config, "GPU", kGPU_Backend },
};
static int findConfig(const char config[]) {
@@ -189,7 +205,7 @@ static int findConfig(const char config[]) {
int main (int argc, char * const argv[]) {
SkAutoGraphics ag;
-
+
SkTDict<const char*> defineDict(1024);
int repeatDraw = 1;
int forceAlpha = 0xFF;
@@ -199,16 +215,16 @@ int main (int argc, char * const argv[]) {
bool doScale = false;
bool doRotate = false;
bool doClip = false;
- bool doPict = false;
const char* matchStr = NULL;
bool hasStrokeWidth = false;
float strokeWidth;
-
+
SkString outDir;
SkBitmap::Config outConfig = SkBitmap::kNo_Config;
const char* configName = "";
+ Backend backend = kRaster_Backend; // for warning
int configCount = SK_ARRAY_COUNT(gConfigs);
-
+
char* const* stop = argv + argc;
for (++argv; argv < stop; ++argv) {
if (strcmp(*argv, "-o") == 0) {
@@ -219,8 +235,6 @@ int main (int argc, char * const argv[]) {
outDir.append("/");
}
}
- } else if (strcmp(*argv, "-pict") == 0) {
- doPict = true;
} else if (strcmp(*argv, "-repeat") == 0) {
argv++;
if (argv < stop) {
@@ -290,6 +304,7 @@ int main (int argc, char * const argv[]) {
if (index >= 0) {
outConfig = gConfigs[index].fConfig;
configName = gConfigs[index].fName;
+ backend = gConfigs[index].fBackend;
configCount = 1;
} else {
SkString str;
@@ -316,7 +331,7 @@ int main (int argc, char * const argv[]) {
return -1;
}
}
-
+
// report our current settings
{
SkString str;
@@ -324,7 +339,13 @@ int main (int argc, char * const argv[]) {
forceAlpha, forceAA, forceFilter);
log_progress(str);
}
-
+
+ GrContext* context = NULL;
+ SkEGLContext eglContext;
+ if (eglContext.init(1024, 1024)) {
+ context = GrContext::CreateGLShaderContext();
+ }
+
Iter iter(&defineDict);
SkBenchmark* bench;
while ((bench = iter.next()) != NULL) {
@@ -340,32 +361,34 @@ int main (int argc, char * const argv[]) {
if (hasStrokeWidth) {
bench->setStrokeWidth(strokeWidth);
}
-
+
// only run benchmarks if their name contains matchStr
if (matchStr && strstr(bench->getName(), matchStr) == NULL) {
continue;
}
-
+
{
SkString str;
str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
bench->getName());
log_progress(str);
}
-
+
for (int configIndex = 0; configIndex < configCount; configIndex++) {
if (configCount > 1) {
outConfig = gConfigs[configIndex].fConfig;
configName = gConfigs[configIndex].fName;
+ backend = gConfigs[configIndex].fBackend;
}
- SkBitmap bm;
- bm.setConfig(outConfig, dim.fX, dim.fY);
- bm.allocPixels();
- erase(bm);
-
- SkCanvas canvas(bm);
-
+ if (kGPU_Backend == backend && NULL == context) {
+ continue;
+ }
+
+ SkDevice* device = make_device(outConfig, dim, backend, context);
+ SkCanvas canvas(device);
+ device->unref();
+
if (doClip) {
performClip(&canvas, dim.fX, dim.fY);
}
@@ -375,33 +398,27 @@ int main (int argc, char * const argv[]) {
if (doRotate) {
performRotate(&canvas, dim.fX, dim.fY);
}
-
+
+ //warm up caches if needed
if (repeatDraw > 1) {
SkAutoCanvasRestore acr(&canvas, true);
bench->draw(&canvas);
+ if (kGPU_Backend == backend && context) {
+ context->flush();
+ glFinish();
+ }
}
-
+
SkMSec now = SkTime::GetMSecs();
for (int i = 0; i < repeatDraw; i++) {
- SkCanvas* c = &canvas;
-
- SkNWayCanvas nway;
- SkPicture* pict = NULL;
- if (doPict) {
- pict = new SkPicture;
- nway.addCanvas(pict->beginRecording(bm.width(), bm.height()));
- nway.addCanvas(&canvas);
- c = &nway;
- }
-
- SkAutoCanvasRestore acr(c, true);
- bench->draw(c);
-
- if (pict) {
- compare_pict_to_bitmap(pict, bm);
- pict->unref();
- }
+ SkAutoCanvasRestore acr(&canvas, true);
+ bench->draw(&canvas);
+ }
+ if (kGPU_Backend == backend && context) {
+ context->flush();
+ glFinish();
}
+
if (repeatDraw > 1) {
double duration = SkTime::GetMSecs() - now;
SkString str;
@@ -409,7 +426,8 @@ int main (int argc, char * const argv[]) {
log_progress(str);
}
if (outDir.size() > 0) {
- saveFile(bench->getName(), configName, outDir.c_str(), bm);
+ saveFile(bench->getName(), configName, outDir.c_str(),
+ device->accessBitmap(false));
}
}
log_progress("\n");
diff --git a/gm/Android.mk b/gm/Android.mk
index b3aeadf1f8..acfb4a5b54 100644
--- a/gm/Android.mk
+++ b/gm/Android.mk
@@ -8,6 +8,7 @@ LOCAL_SRC_FILES := \
complexclip.cpp \
filltypes.cpp \
gradients.cpp \
+ nocolorbleed.cpp \
pathfill.cpp \
points.cpp \
poly2poly.cpp \
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
index 0487fe6958..39039130cd 100644
--- a/gm/bitmapfilters.cpp
+++ b/gm/bitmapfilters.cpp
@@ -3,15 +3,20 @@
namespace skiagm {
static void make_bm(SkBitmap* bm) {
- const SkColor colors[] = {
+ const SkColor colors[4] = {
SK_ColorRED, SK_ColorGREEN,
SK_ColorBLUE, SK_ColorWHITE
};
- SkColorTable* ctable = new SkColorTable(colors, 4);
+ SkPMColor colorsPM[4];
+ for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
+ colorsPM[i] = SkPreMultiplyColor(colors[i]);
+ }
+ SkColorTable* ctable = new SkColorTable(colorsPM, 4);
+
bm->setConfig(SkBitmap::kIndex8_Config, 2, 2);
bm->allocPixels(ctable);
ctable->unref();
-
+
*bm->getAddr8(0, 0) = 0;
*bm->getAddr8(1, 0) = 1;
*bm->getAddr8(0, 1) = 2;
@@ -57,7 +62,7 @@ static SkScalar draw_row(SkCanvas* canvas, const SkBitmap& bm) {
canvas->translate(SkIntToScalar(48), 0);
canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale));
-
+
x += draw_set(canvas, bm, 0, &paint);
paint.reset();
paint.setAlpha(0x80);
@@ -90,7 +95,7 @@ protected:
SkScalar x = SkIntToScalar(10);
SkScalar y = SkIntToScalar(10);
-
+
canvas->translate(x, y);
y = draw_row(canvas, fBM8);
canvas->translate(0, y);
@@ -100,7 +105,7 @@ protected:
canvas->translate(0, y);
draw_row(canvas, fBM32);
}
-
+
private:
typedef GM INHERITED;
};
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index 26fdc7995e..c934178e4a 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -5,14 +5,14 @@ namespace skiagm {
class BlursGM : public GM {
public:
- BlursGM() {}
+ BlursGM() {}
protected:
virtual SkString onShortName() {
return SkString("blurs");
}
- virtual SkISize onISize() {
+ virtual SkISize onISize() {
return make_isize(700, 500);
}
@@ -37,8 +37,8 @@ protected:
SkPaint paint;
paint.setAntiAlias(true);
- paint.setTextSize(25);
- canvas->translate(-40, 0);
+ paint.setTextSize(SkIntToScalar(25));
+ canvas->translate(SkIntToScalar(-40), SkIntToScalar(0));
SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag;
for (int j = 0; j < 2; j++) {
@@ -46,27 +46,32 @@ protected:
paint.setColor(SK_ColorBLUE);
for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
if (gRecs[i].fStyle != NONE) {
- SkMaskFilter* mf = SkBlurMaskFilter::Create(20,
- gRecs[i].fStyle,
- flags);
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(
+ SkIntToScalar(20), gRecs[i].fStyle, flags
+ );
paint.setMaskFilter(mf)->unref();
} else {
paint.setMaskFilter(NULL);
}
- canvas->drawCircle(200 + gRecs[i].fCx*100,
- 200 + gRecs[i].fCy*100, 50, paint);
+ canvas->drawCircle(SkIntToScalar(200 + gRecs[i].fCx*100)
+ , SkIntToScalar(200 + gRecs[i].fCy*100)
+ , SkIntToScalar(50)
+ , paint);
}
// draw text
{
- SkMaskFilter* mf = SkBlurMaskFilter::Create(4,
- SkBlurMaskFilter::kNormal_BlurStyle,
- flags);
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(
+ SkIntToScalar(4)
+ , SkBlurMaskFilter::kNormal_BlurStyle
+ , flags
+ );
paint.setMaskFilter(mf)->unref();
SkScalar x = SkIntToScalar(70);
SkScalar y = SkIntToScalar(400);
paint.setColor(SK_ColorBLACK);
canvas->drawText("Hamburgefons Style", 18, x, y, paint);
- canvas->drawText("Hamburgefons Style", 18, x, y + SkIntToScalar(50), paint);
+ canvas->drawText("Hamburgefons Style", 18
+ , x, y + SkIntToScalar(50), paint);
paint.setMaskFilter(NULL);
paint.setColor(SK_ColorWHITE);
x -= SkIntToScalar(2);
@@ -75,7 +80,7 @@ protected:
}
canvas->restore();
flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
- canvas->translate(350, 0);
+ canvas->translate(SkIntToScalar(350), SkIntToScalar(0));
}
}
diff --git a/gm/gm_files.mk b/gm/gm_files.mk
index e86782035b..fec20b6199 100644
--- a/gm/gm_files.mk
+++ b/gm/gm_files.mk
@@ -3,6 +3,7 @@ SOURCE := \
blurs.cpp \
filltypes.cpp \
gradients.cpp \
+ nocolorbleed.cpp \
pathfill.cpp \
points.cpp \
poly2poly.cpp \
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 6267fd4b85..ea205e34bd 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -441,7 +441,7 @@ int main(int argc, char * const argv[]) {
const char* readPath = NULL; // if non-null, were we read from to compare
const char* diffPath = NULL; // if non-null, where we write our diffs (from compare)
- bool doReplay = false;
+ bool doReplay = true;
bool doSerialize = false;
const char* const commandName = argv[0];
char* const* stop = argv + argc;
@@ -461,8 +461,8 @@ int main(int argc, char * const argv[]) {
if (argv < stop && **argv) {
diffPath = *argv;
}
- } else if (strcmp(*argv, "--replay") == 0) {
- doReplay = true;
+ } else if (strcmp(*argv, "--noreplay") == 0) {
+ doReplay = false;
} else if (strcmp(*argv, "--serialize") == 0) {
doSerialize = true;
} else {
diff --git a/gm/nocolorbleed.cpp b/gm/nocolorbleed.cpp
new file mode 100755
index 0000000000..3dec7ccc94
--- /dev/null
+++ b/gm/nocolorbleed.cpp
@@ -0,0 +1,75 @@
+#include "gm.h"
+
+namespace skiagm {
+
+class NoColorBleedGM : public GM {
+public:
+ NoColorBleedGM() {}
+
+protected:
+ virtual SkString onShortName() {
+ return SkString("nocolorbleed");
+ }
+
+ virtual SkISize onISize() {
+ return make_isize(200, 200);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ drawBG(canvas);
+
+ SkBitmap sprite;
+ sprite.setConfig(SkBitmap::kARGB_8888_Config, 4, 4, 4*sizeof(SkColor));
+ const SkColor spriteData[16] = {
+ SK_ColorBLACK, SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW,
+ SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorRED,
+ SK_ColorGREEN, SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLUE,
+ SK_ColorYELLOW, SK_ColorMAGENTA, SK_ColorCYAN, SK_ColorBLACK
+ };
+ sprite.allocPixels();
+ sprite.lockPixels();
+ SkPMColor* addr = sprite.getAddr32(0, 0);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(spriteData); ++i) {
+ addr[i] = SkPreMultiplyColor(spriteData[i]);
+ }
+ sprite.unlockPixels();
+
+ // We draw a magnified subrect of the sprite
+ // sample interpolation may cause color bleeding around edges
+ // the subrect is a pure white area
+ SkIRect srcRect;
+ SkRect dstRect;
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+ //First row : full texture with and without filtering
+ srcRect.setXYWH(0, 0, 4, 4);
+ dstRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0)
+ , SkIntToScalar(100), SkIntToScalar(100));
+ canvas->drawBitmapRect(sprite, &srcRect, dstRect, &paint);
+ dstRect.setXYWH(SkIntToScalar(100), SkIntToScalar(0)
+ , SkIntToScalar(100), SkIntToScalar(100));
+ canvas->drawBitmapRect(sprite, &srcRect, dstRect);
+ //Second row : sub rect of texture with and without filtering
+ srcRect.setXYWH(1, 1, 2, 2);
+ dstRect.setXYWH(SkIntToScalar(25), SkIntToScalar(125)
+ , SkIntToScalar(50), SkIntToScalar(50));
+ canvas->drawBitmapRect(sprite, &srcRect, dstRect, &paint);
+ dstRect.setXYWH(SkIntToScalar(125), SkIntToScalar(125)
+ , SkIntToScalar(50), SkIntToScalar(50));
+ canvas->drawBitmapRect(sprite, &srcRect, dstRect);
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new NoColorBleedGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/pathfill.cpp b/gm/pathfill.cpp
index ec56942a96..713847f514 100644
--- a/gm/pathfill.cpp
+++ b/gm/pathfill.cpp
@@ -6,14 +6,15 @@
typedef SkScalar (*MakePathProc)(SkPath*);
static SkScalar make_frame(SkPath* path) {
- SkRect r = { 10, 10, 630, 470 };
- path->addRoundRect(r, 15, 15);
+ SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+ SkIntToScalar(630), SkIntToScalar(470) };
+ path->addRoundRect(r, SkIntToScalar(15), SkIntToScalar(15));
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(5);
+ paint.setStrokeWidth(SkIntToScalar(5));
paint.getFillPath(*path, path);
- return 15;
+ return SkIntToScalar(15);
}
static SkScalar make_triangle(SkPath* path) {
@@ -24,21 +25,23 @@ static SkScalar make_triangle(SkPath* path) {
path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
path->close();
- path->offset(10, 0);
+ path->offset(SkIntToScalar(10), SkIntToScalar(0));
return SkIntToScalar(30);
}
static SkScalar make_rect(SkPath* path) {
- SkRect r = { 10, 10, 30, 30 };
+ SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+ SkIntToScalar(30), SkIntToScalar(30) };
path->addRect(r);
- path->offset(10, 0);
+ path->offset(SkIntToScalar(10), SkIntToScalar(0));
return SkIntToScalar(30);
}
static SkScalar make_oval(SkPath* path) {
- SkRect r = { 10, 10, 30, 30 };
+ SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+ SkIntToScalar(30), SkIntToScalar(30) };
path->addOval(r);
- path->offset(10, 0);
+ path->offset(SkIntToScalar(10), SkIntToScalar(0));
return SkIntToScalar(30);
}
@@ -56,8 +59,8 @@ static SkScalar make_sawtooth(SkPath* path) {
x += dx;
path->lineTo(x, y + dy);
}
- path->lineTo(x, y + 2 * dy);
- path->lineTo(x0, y + 2 * dy);
+ path->lineTo(x, y + (2 * dy));
+ path->lineTo(x0, y + (2 * dy));
path->close();
return SkIntToScalar(30);
}
@@ -100,7 +103,7 @@ class PathFillGM : public GM {
SkPath fPath[N];
SkScalar fDY[N];
public:
- PathFillGM() {
+ PathFillGM() {
for (size_t i = 0; i < N; i++) {
fDY[i] = gProcs[i](&fPath[i]);
}
@@ -111,7 +114,7 @@ protected:
return SkString("pathfill");
}
- virtual SkISize onISize() {
+ virtual SkISize onISize() {
return make_isize(640, 480);
}
@@ -127,7 +130,7 @@ protected:
for (size_t i = 0; i < N; i++) {
canvas->drawPath(fPath[i], paint);
- canvas->translate(0, fDY[i]);
+ canvas->translate(SkIntToScalar(0), fDY[i]);
}
}
diff --git a/gm/shadertext.cpp b/gm/shadertext.cpp
index 1cf562cc86..ea8782307d 100644
--- a/gm/shadertext.cpp
+++ b/gm/shadertext.cpp
@@ -11,7 +11,7 @@ static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
bm->eraseColor(0);
SkCanvas canvas(*bm);
- SkScalar s = w < h ? w : h;
+ SkScalar s = SkIntToScalar(SkMin32(w, h));
SkPoint pts[] = { { 0, 0 }, { s, s } };
SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
@@ -121,7 +121,7 @@ protected:
const char text[] = "Shaded Text";
const int textLen = SK_ARRAY_COUNT(text) - 1;
- static int pointSize = SkIntToScalar(48);
+ const int pointSize = 48;
int w = pointSize * textLen;
int h = pointSize;
@@ -194,5 +194,3 @@ static GM* MyFactory(void*) { return new ShaderTextGM; }
static GMRegistry reg(MyFactory);
}
-
-
diff --git a/gm/shadows.cpp b/gm/shadows.cpp
index 5afde495fe..bba997fb38 100644
--- a/gm/shadows.cpp
+++ b/gm/shadows.cpp
@@ -1,32 +1,29 @@
#include "gm.h"
-#include "SkPicture.h"
-#include "SkRectShape.h"
#include "SkBlurDrawLooper.h"
namespace skiagm {
///////////////////////////////////////////////////////////////////////////////
-class ShadowsGM : public GM {
+static void setup(SkPaint* paint, SkColor c, SkScalar strokeWidth) {
+ paint->setColor(c);
+ if (strokeWidth < 0) {
+ paint->setStyle(SkPaint::kFill_Style);
+ } else {
+ paint->setStyle(SkPaint::kStroke_Style);
+ paint->setStrokeWidth(strokeWidth);
+ }
+}
+class ShadowsGM : public GM {
public:
SkPath fCirclePath;
- SkPaint fPaint;
- SkRectShape fRectShape;
+ SkRect fRect;
+
ShadowsGM() {
fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) );
- fPaint.setStrokeWidth(SkIntToScalar(4));
- fPaint.setAntiAlias(true);
- fPaint.setColor(0xFF00FF00);
- fPaint.setStyle(SkPaint::kStroke_Style);
- SkRect rect;
- rect.set(SkIntToScalar(10), SkIntToScalar(10),
- SkIntToScalar(30), SkIntToScalar(30));
- fRectShape.setRect(rect);
- fRectShape.paint().setColor(SK_ColorRED);
- }
-
- virtual ~ShadowsGM() {
+ fRect.set(SkIntToScalar(10), SkIntToScalar(10),
+ SkIntToScalar(30), SkIntToScalar(30));
}
protected:
@@ -47,43 +44,61 @@ protected:
SkBlurDrawLooper* shadowLoopers[5];
shadowLoopers[0] =
- new SkBlurDrawLooper (10, 5, 10, 0xFF0000FF,
+ new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
+ SkIntToScalar(10), 0xFF0000FF,
SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
SkBlurDrawLooper::kOverrideColor_BlurFlag |
SkBlurDrawLooper::kHighQuality_BlurFlag );
SkAutoUnref aurL0(shadowLoopers[0]);
shadowLoopers[1] =
- new SkBlurDrawLooper (10, 5, 10, 0xFF0000FF,
+ new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
+ SkIntToScalar(10), 0xFF0000FF,
SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
SkBlurDrawLooper::kOverrideColor_BlurFlag );
SkAutoUnref aurL1(shadowLoopers[1]);
shadowLoopers[2] =
- new SkBlurDrawLooper (5, 5, 10, 0xFF000000,
+ new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(5),
+ SkIntToScalar(10), 0xFF000000,
SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
SkBlurDrawLooper::kHighQuality_BlurFlag );
SkAutoUnref aurL2(shadowLoopers[2]);
shadowLoopers[3] =
- new SkBlurDrawLooper (5, -5 ,-10, 0x7FFF0000,
+ new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(-5),
+ SkIntToScalar(-10), 0x7FFF0000,
SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
SkBlurDrawLooper::kOverrideColor_BlurFlag |
SkBlurDrawLooper::kHighQuality_BlurFlag );
SkAutoUnref aurL3(shadowLoopers[3]);
shadowLoopers[4] =
- new SkBlurDrawLooper (0, 5, 5, 0xFF000000,
+ new SkBlurDrawLooper (SkIntToScalar(0), SkIntToScalar(5),
+ SkIntToScalar(5), 0xFF000000,
SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
SkBlurDrawLooper::kOverrideColor_BlurFlag |
SkBlurDrawLooper::kHighQuality_BlurFlag );
SkAutoUnref aurL4(shadowLoopers[4]);
- for (int looper = 0; looper < 5; looper ++)
- {
- fRectShape.paint().setLooper(shadowLoopers[looper]);
- canvas->resetMatrix();
- canvas->translate(SkIntToScalar(looper*40), SkIntToScalar(0));
- canvas->drawShape(&fRectShape);
- fPaint.setLooper(shadowLoopers[looper]);
+ static const struct {
+ SkColor fColor;
+ SkScalar fStrokeWidth;
+ } gRec[] = {
+ { SK_ColorRED, -SK_Scalar1 },
+ { SK_ColorGREEN, SkIntToScalar(4) },
+ };
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(shadowLoopers); ++i) {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ paint.setLooper(shadowLoopers[i]);
+
+ canvas->translate(SkIntToScalar(i*40), SkIntToScalar(0));
+ setup(&paint, gRec[0].fColor, gRec[0].fStrokeWidth);
+ canvas->drawRect(fRect, paint);
+
canvas->translate(SkIntToScalar(0), SkIntToScalar(40));
- canvas->drawPath(fCirclePath, fPaint);
+ setup(&paint, gRec[1].fColor, gRec[1].fStrokeWidth);
+ canvas->drawPath(fCirclePath, paint);
}
}
diff --git a/gm/shapes.cpp b/gm/shapes.cpp
index 324ce7e871..5daea0aa4d 100644
--- a/gm/shapes.cpp
+++ b/gm/shapes.cpp
@@ -57,6 +57,8 @@ public:
for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
}
+ SkScalar c = SkIntToScalar(50);
+ fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
}
virtual ~ShapesGM() {
@@ -81,10 +83,6 @@ protected:
virtual void onDraw(SkCanvas* canvas) {
this->drawBG(canvas);
- SkMatrix saveM = *fMatrixRefs[3];
- SkScalar c = SkIntToScalar(50);
- fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
-
SkMatrix matrix;
SkGroupShape* gs = new SkGroupShape;
@@ -111,8 +109,6 @@ protected:
canvas->drawPicture(*pict);
pict->unref();
#endif
-
- *fMatrixRefs[3] = saveM;
}
private:
diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp
index b716407212..891b95a8c3 100644
--- a/gm/strokerects.cpp
+++ b/gm/strokerects.cpp
@@ -29,14 +29,14 @@ static const SkScalar SH = SkIntToScalar(H);
class StrokeRectGM : public GM {
public:
- StrokeRectGM() {}
+ StrokeRectGM() {}
protected:
virtual SkString onShortName() {
return SkString("strokerects");
}
- virtual SkISize onISize() {
+ virtual SkISize onISize() {
return make_isize(W*2, H*2);
}
@@ -63,7 +63,10 @@ protected:
SkAutoCanvasRestore acr(canvas, true);
canvas->translate(SW * x, SH * y);
- canvas->clipRect(SkRect::MakeLTRB(2, 2, SW - 2, SH - 2));
+ canvas->clipRect(SkRect::MakeLTRB(
+ SkIntToScalar(2), SkIntToScalar(2)
+ , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
+ ));
SkRandom rand;
for (int i = 0; i < N; i++) {
diff --git a/gpu/include/GrClipIterator.h b/gpu/include/GrClipIterator.h
index 1fcbdd178a..f7f74a76b1 100644
--- a/gpu/include/GrClipIterator.h
+++ b/gpu/include/GrClipIterator.h
@@ -47,7 +47,7 @@ public:
* Return the current path. It is an error to call this when isDone() is
* true or when getType() is kRect_Type.
*/
- virtual GrPathIter* getPathIter() = 0;
+ virtual const GrPath* getPath() = 0;
/**
* Return the fill rule for the path. It is an error to call this when
diff --git a/gpu/include/GrConfig.h b/gpu/include/GrConfig.h
index f1f24375ca..9ee37c73cc 100644
--- a/gpu/include/GrConfig.h
+++ b/gpu/include/GrConfig.h
@@ -189,7 +189,7 @@ typedef unsigned __int64 uint64_t;
// debug -vs- release
//
-extern void GrPrintf(const char format[], ...);
+extern GR_API void GrPrintf(const char format[], ...);
/**
* GR_STRING makes a string of X where X is expanded before conversion to a string
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 951c0e66e7..58c53ba27e 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -25,7 +25,6 @@
class GrFontCache;
class GrGpu;
struct GrGpuStats;
-class GrPathIter;
class GrVertexBufferAllocPool;
class GrIndexBufferAllocPool;
class GrInOrderDrawBuffer;
@@ -191,35 +190,16 @@ public:
* on failure.
*/
GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc);
-
/**
- * DEPRECATED, WILL BE REMOVED SOON. USE createPlatformSurface.
- *
- * Wraps an externally-created rendertarget in a GrRenderTarget.
- * @param platformRenderTarget 3D API-specific render target identifier
- * e.g. in GL platforamRenderTarget is an FBO
- * id.
- * @param stencilBits the number of stencil bits that the render
- * target has.
- * @param isMultisampled specify whether the render target is
- * multisampled.
- * @param width width of the render target.
- * @param height height of the render target.
- */
- GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width, int height);
-
- /**
- * DEPRECATED, WILL BE REMOVED SOON. USE createPlatformSurface.
- *
* Reads the current target object (e.g. FBO or IDirect3DSurface9*) and
* viewport state from the underlying 3D API and wraps it in a
* GrRenderTarget. The GrRenderTarget will not attempt to delete/destroy the
* underlying object in its destructor and it is up to caller to guarantee
* that it remains valid while the GrRenderTarget is used.
*
+ * Will not detect that the render target is also a texture. If you need
+ * to also use the render target as a GrTexture use createPlatformSurface.
+ *
* @return the newly created GrRenderTarget
*/
GrRenderTarget* createRenderTargetFrom3DApiState();
@@ -323,22 +303,14 @@ public:
* Draws a path.
*
* @param paint describes how to color pixels.
- * @param pathIter the path to draw
+ * @param path the path to draw
* @param fill the path filling rule to use.
* @param translate optional additional translation applied to the
* path.
*/
- void drawPath(const GrPaint& paint,
- GrPathIter* pathIter,
- GrPathFill fill,
- const GrPoint* translate = NULL);
- /**
- * Helper version of drawPath that takes a GrPath
- */
- void drawPath(const GrPaint& paint,
- const GrPath& path,
- GrPathFill fill,
+ void drawPath(const GrPaint& paint, const GrPath& path, GrPathFill fill,
const GrPoint* translate = NULL);
+
/**
* Draws vertices with a paint.
*
@@ -585,16 +557,9 @@ private:
void drawClipIntoStencil();
- GrPathRenderer* getPathRenderer(const GrDrawTarget* target,
- GrPathIter* path,
- GrPathFill fill);
+ GrPathRenderer* getPathRenderer(const GrDrawTarget*, const GrPath&, GrPathFill);
struct OffscreenRecord;
- // we currently only expose stage 0 through the paint so use stage 1. We
- // use stage 1 for the offscreen.
- enum {
- kOffscreenStage = 1,
- };
bool doOffscreenAA(GrDrawTarget* target,
const GrPaint& paint,
@@ -613,6 +578,15 @@ private:
const GrIRect& boundRect,
OffscreenRecord* record);
+ // computes vertex layout bits based on the paint. If paint expresses
+ // a texture for a stage, the stage coords will be bound to postitions
+ // unless hasTexCoords[s]==true in which case stage s's input coords
+ // are bound to tex coord index s. hasTexCoords == NULL is a shortcut
+ // for an array where all the values are false.
+ static int PaintStageVertexLayoutBits(
+ const GrPaint& paint,
+ const bool hasTexCoords[GrPaint::kTotalStages]);
+
};
/**
diff --git a/gpu/include/GrContext_impl.h b/gpu/include/GrContext_impl.h
index fae4e923f9..c79a191e2f 100644
--- a/gpu/include/GrContext_impl.h
+++ b/gpu/include/GrContext_impl.h
@@ -40,19 +40,15 @@ inline void GrContext::drawCustomVertices(const GrPaint& paint,
const COL_SRC* colorSrc,
const IDX_SRC* idxSrc) {
- GrVertexLayout layout = 0;
-
GrDrawTarget::AutoReleaseGeometry geo;
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
- if (NULL != paint.getTexture()) {
- if (NULL != texCoordSrc) {
- layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
- } else {
- layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
- }
- }
+ bool hasTexCoords[GrPaint::kTotalStages] = {
+ NULL != texCoordSrc, // texCoordSrc provides explicit stage 0 coords
+ 0 // remaining stages use positions
+ };
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
if (NULL != colorSrc) {
layout |= GrDrawTarget::kColor_VertexLayoutBit;
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index 93b381dd9f..cd70d3e7c5 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -32,7 +32,6 @@ class GrTexture;
class GrClipIterator;
class GrVertexBuffer;
class GrIndexBuffer;
-class GrEffect;
class GrDrawTarget : public GrRefCnt {
public:
@@ -51,10 +50,21 @@ public:
* or not.
*/
enum {
- kNumStages = 2,
+ kNumStages = 3,
kMaxTexCoords = kNumStages
};
+
+ /**
+ * The absolute maximum number of edges that may be specified for
+ * a single draw call when performing edge antialiasing. This is used for
+ * the size of several static buffers, so implementations of getMaxEdges()
+ * (below) should clamp to this value.
+ */
+ enum {
+ kMaxEdges = 32
+ };
+
/**
* Bitfield used to indicate which stages are in use.
*/
@@ -79,9 +89,6 @@ public:
kNoColorWrites_StateBit = 0x08, //<! If set it disables writing colors.
// Useful while performing stencil
// ops.
- kEdgeAA_StateBit = 0x10, //<! Perform edge anti-aliasing.
- // Requires the edges to be passed in
- // setEdgeAAData().
// subclass may use additional bits internally
kDummyStateBit,
@@ -129,6 +136,20 @@ public:
fCurrDrawState.fStencilSettings.setDisabled();
}
+ class Edge {
+ public:
+ Edge() {}
+ Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
+ GrPoint intersect(const Edge& other) {
+ return GrPoint::Make(
+ (fY * other.fZ - other.fY * fZ) /
+ (fX * other.fY - other.fX * fY),
+ (fX * other.fZ - other.fX * fZ) /
+ (other.fX * fY - fX * other.fY));
+ }
+ float fX, fY, fZ;
+ };
+
protected:
struct DrState {
@@ -137,19 +158,26 @@ protected:
// all DrState members should default to something
// valid by the memset
memset(this, 0, sizeof(DrState));
- // This is an exception to our memset, since it will
- // result in no change.
+
+ // memset exceptions
fColorFilterXfermode = SkXfermode::kDstIn_Mode;
+ fFirstCoverageStage = kNumStages;
+
+ // pedantic assertion that our ptrs will
+ // be NULL (0 ptr is mem addr 0)
GrAssert((intptr_t)(void*)NULL == 0LL);
+
+ // default stencil setting should be disabled
GrAssert(fStencilSettings.isDisabled());
+ fFirstCoverageStage = kNumStages;
}
uint32_t fFlagBits;
GrBlendCoeff fSrcBlend;
GrBlendCoeff fDstBlend;
GrColor fBlendConstant;
GrTexture* fTextures[kNumStages];
- GrEffect* fEffects[kNumStages];
GrSamplerState fSamplerStates[kNumStages];
+ int fFirstCoverageStage;
GrRenderTarget* fRenderTarget;
GrColor fColor;
DrawFace fDrawFace;
@@ -158,7 +186,8 @@ protected:
GrStencilSettings fStencilSettings;
GrMatrix fViewMatrix;
- float fEdgeAAEdges[18];
+ Edge fEdgeAAEdges[kMaxEdges];
+ int fEdgeAANumEdges;
bool operator ==(const DrState& s) const {
return 0 == memcmp(this, &s, sizeof(DrState));
}
@@ -245,6 +274,18 @@ public:
}
/**
+ * Shortcut for preConcatSamplerMatrix on all stages in mask with same
+ * matrix
+ */
+ void preConcatSamplerMatrices(int stageMask, const GrMatrix& matrix) {
+ for (int i = 0; i < kNumStages; ++i) {
+ if ((1 << i) & stageMask) {
+ this->preConcatSamplerMatrix(i, matrix);
+ }
+ }
+ }
+
+ /**
* Gets the matrix of a stage's sampler
*
* @param stage the stage to of sampler to get
@@ -343,6 +384,26 @@ public:
void setDrawFace(DrawFace face) { fCurrDrawState.fDrawFace = face; }
/**
+ * A common pattern is to compute a color with the initial stages and then
+ * modulate that color by a coverage value in later stage(s) (AA, mask-
+ * filters, glyph mask, etc). Color-filters, xfermodes, etc should be
+ * computed based on the pre-coverage-modulated color. The division of
+ * stages between color-computing and coverage-computing is specified by
+ * this method. Initially this is kNumStages (all stages are color-
+ * computing).
+ */
+ void setFirstCoverageStage(int firstCoverageStage) {
+ fCurrDrawState.fFirstCoverageStage = firstCoverageStage;
+ }
+
+ /**
+ * Gets the index of the first coverage-computing stage.
+ */
+ int getFirstCoverageStage() const {
+ return fCurrDrawState.fFirstCoverageStage;
+ }
+
+ /**
* Gets whether the target is drawing clockwise, counterclockwise,
* or both faces.
* @return the current draw face(s).
@@ -392,7 +453,7 @@ public:
* @param srcCoef coeffecient applied to the src color.
* @param dstCoef coeffecient applied to the dst color.
*/
- void setBlendFunc(GrBlendCoeff srcCoef, GrBlendCoeff dstCoef);
+ void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
/**
* Sets the blending function constant referenced by the following blending
@@ -498,7 +559,7 @@ public:
* @param edges 3 * 6 float values, representing the edge
* equations in Ax + By + C form
*/
- void setEdgeAAData(const float edges[18]);
+ void setEdgeAAData(const Edge* edges, int numEdges);
private:
static const int TEX_COORD_BIT_CNT = kNumStages*kMaxTexCoords;
@@ -766,6 +827,15 @@ public:
*/
virtual void clear(const GrIRect* rect, GrColor color) = 0;
+ /**
+ * Returns the maximum number of edges that may be specified in a single
+ * draw call when performing edge antialiasing. This is usually limited
+ * by the number of fragment uniforms which may be uploaded. Must be a
+ * minimum of six, since a triangle's vertices each belong to two boundary
+ * edges which may be distinct.
+ */
+ virtual int getMaxEdges() const { return 6; }
+
///////////////////////////////////////////////////////////////////////////
class AutoStateRestore : ::GrNoncopyable {
diff --git a/gpu/include/GrFontScaler.h b/gpu/include/GrFontScaler.h
index 77730d7eb9..ab73ea4f5c 100644
--- a/gpu/include/GrFontScaler.h
+++ b/gpu/include/GrFontScaler.h
@@ -21,7 +21,7 @@
#include "GrGlyph.h"
#include "GrKey.h"
-class GrPath;
+class SkPath;
/**
* This is a virtual base class which Gr's interface to the host platform's
@@ -37,7 +37,7 @@ public:
virtual bool getPackedGlyphBounds(GrGlyph::PackedID, GrIRect* bounds) = 0;
virtual bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height,
int rowBytes, void* image) = 0;
- virtual bool getGlyphPath(uint16_t glyphID, GrPath*) = 0;
+ virtual bool getGlyphPath(uint16_t glyphID, SkPath*) = 0;
};
#endif
diff --git a/gpu/include/GrGLDefines.h b/gpu/include/GrGLDefines.h
index 29e56f3f44..6c2483bacf 100644
--- a/gpu/include/GrGLDefines.h
+++ b/gpu/include/GrGLDefines.h
@@ -69,6 +69,12 @@
/* GL_DST_ALPHA */
/* GL_ONE_MINUS_DST_ALPHA */
+/* ExtendedBlendFactors */
+#define GR_GL_SRC1_COLOR 0x88F9
+#define GR_GL_ONE_MINUS_SRC1_COLOR 0x88FA
+/* GL_SRC1_ALPHA */
+#define GR_GL_ONE_MINUS_SRC1_ALPHA 0x88FB
+
/* BlendEquationSeparate */
#define GR_GL_FUNC_ADD 0x8006
#define GR_GL_BLEND_EQUATION 0x8009
@@ -318,6 +324,8 @@
#define GR_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
#define GR_GL_SHADING_LANGUAGE_VERSION 0x8B8C
#define GR_GL_CURRENT_PROGRAM 0x8B8D
+#define GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define GR_GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
/* StencilFunction */
#define GR_GL_NEVER 0x0200
diff --git a/gpu/include/GrGLInterface.h b/gpu/include/GrGLInterface.h
index 591ab8c2e5..150e8e469a 100644
--- a/gpu/include/GrGLInterface.h
+++ b/gpu/include/GrGLInterface.h
@@ -63,6 +63,7 @@ typedef unsigned int GrGLenum;
typedef unsigned char GrGLboolean;
typedef unsigned int GrGLbitfield;
typedef signed char GrGLbyte;
+typedef char GrGLchar;
typedef short GrGLshort;
typedef int GrGLint;
typedef int GrGLsizei;
@@ -199,6 +200,9 @@ extern "C" {
// Buffer mapping (extension in ES).
typedef GrGLvoid* (GR_GL_FUNCTION_TYPE *GrGLMapBufferProc)(GrGLenum target, GrGLenum access);
typedef GrGLboolean (GR_GL_FUNCTION_TYPE *GrGLUnmapBufferProc)(GrGLenum target);
+
+ // Dual source blending
+ typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name);
} // extern "C"
/*
@@ -333,6 +337,9 @@ struct GrGLInterface {
GrGLMapBufferProc fMapBuffer;
GrGLUnmapBufferProc fUnmapBuffer;
+ // Dual Source Blending
+ GrGLBindFragDataLocationIndexedProc fBindFragDataLocationIndexed;
+
// Code that initializes this struct using a static initializer should
// make this the last entry in the static initializer. It can help to guard
// against failing to initialize newly-added members of this struct.
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
index e44956f709..574a430be9 100644
--- a/gpu/include/GrGpu.h
+++ b/gpu/include/GrGpu.h
@@ -60,6 +60,21 @@ class GrGpu : public GrDrawTarget {
public:
/**
+ * Additional blend coeffecients for dual source blending, not exposed
+ * through GrPaint/GrContext.
+ */
+ enum ExtendedBlendCoeffs {
+ // source 2 refers to second output color when
+ // using dual source blending.
+ kS2C_BlendCoeff = kPublicBlendCoeffCount,
+ kIS2C_BlendCoeff,
+ kS2A_BlendCoeff,
+ kIS2A_BlendCoeff,
+
+ kTotalBlendCoeffCount
+ };
+
+ /**
* Create an instance of GrGpu that matches the specified Engine backend.
* If the requested engine is not supported (at compile-time or run-time)
* this returns NULL.
@@ -109,20 +124,6 @@ public:
*/
GrTexture* createTexture(const GrTextureDesc& desc,
const void* srcData, size_t rowBytes);
- /**
- * Wraps an externally-created rendertarget in a GrRenderTarget.
- * @param platformRenderTarget handle to the the render target in the
- * underlying 3D API. Interpretation depends on
- * GrGpu subclass in use.
- * @param stencilBits number of stencil bits the target has
- * @param isMultisampled specify whether the RT is multisampled
- * @param width width of the render target
- * @param height height of the render target
- */
- GrRenderTarget* createPlatformRenderTarget(intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width, int height);
GrResource* createPlatformSurface(const GrPlatformSurfaceDesc& desc);
@@ -204,6 +205,15 @@ public:
bool supports4x4DownsampleFilter() const { return f4X4DownsampleFilterSupport; }
/**
+ * Does this instance support dual-source blending? Required for proper
+ * blending with partial coverage with certain blend modes (dst coeff is
+ * not 1, ISA, or ISC)
+ */
+ bool supportsDualSourceBlending() const {
+ return fDualSourceBlendingSupport;
+ }
+
+ /**
* Gets the minimum width of a render target. If a texture/rt is created
* with a width less than this size the GrGpu object will clamp it to this
* value.
@@ -385,6 +395,7 @@ protected:
bool fAALineSupport;
bool fFSAASupport;
bool f4X4DownsampleFilterSupport; // supports GrSamplerState::k4x4Downsample_Filter
+ bool fDualSourceBlendingSupport;
// set by subclass to true if index and vertex buffers can be locked, false
// otherwise.
@@ -427,11 +438,6 @@ protected:
const void* srcData,
size_t rowBytes) = 0;
virtual GrResource* onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc) = 0;
- virtual GrRenderTarget* onCreatePlatformRenderTarget(
- intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width, int height) = 0;
virtual GrRenderTarget* onCreateRenderTargetFrom3DApiState() = 0;
virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
bool dynamic) = 0;
@@ -513,8 +519,7 @@ private:
void prepareIndexPool();
// determines the path renderer used to draw a clip path element.
- GrPathRenderer* getClipPathRenderer(GrPathIter* path,
- GrPathFill fill);
+ GrPathRenderer* getClipPathRenderer(const SkPath& path, GrPathFill fill);
void handleDirtyContext() {
if (fContextIsDirty) {
diff --git a/gpu/include/GrPaint.h b/gpu/include/GrPaint.h
index 3035ca1b67..a7923f5656 100644
--- a/gpu/include/GrPaint.h
+++ b/gpu/include/GrPaint.h
@@ -25,12 +25,17 @@
/**
* The paint describes how pixels are colored when the context draws to
- * them.
+ * them. TODO: Make this a "real" class with getters and setters, default
+ * values, and documentation.
*/
class GrPaint {
public:
+ enum {
+ kMaxTextures = 1,
+ kMaxMasks = 1,
+ };
- // All the paint fields are public except texture (it's ref-counted)
+ // All the paint fields are public except textures/samplers
GrBlendCoeff fSrcBlendCoeff;
GrBlendCoeff fDstBlendCoeff;
bool fAntiAlias;
@@ -38,22 +43,75 @@ public:
GrColor fColor;
- GrSamplerState fSampler;
-
GrColor fColorFilterColor;
SkXfermode::Mode fColorFilterXfermode;
- void setTexture(GrTexture* texture) {
+ void setTexture(int i, GrTexture* texture) {
+ GrAssert((unsigned)i < kMaxTextures);
GrSafeRef(texture);
- GrSafeUnref(fTexture);
- fTexture = texture;
+ GrSafeUnref(fTextures[i]);
+ fTextures[i] = texture;
+ }
+
+ GrTexture* getTexture(int i) const {
+ GrAssert((unsigned)i < kMaxTextures);
+ return fTextures[i];
+ }
+
+ GrSamplerState* getTextureSampler(int i) {
+ GrAssert((unsigned)i < kMaxTextures);
+ return fTextureSamplers + i;
+ }
+
+ const GrSamplerState* getTextureSampler(int i) const {
+ GrAssert((unsigned)i < kMaxTextures);
+ return fTextureSamplers + i;
+ }
+
+ // The mask can be alpha-only or per channel. It is applied
+ // after the colorfilter
+ void setMask(int i, GrTexture* mask) {
+ GrAssert((unsigned)i < kMaxMasks);
+ GrSafeRef(mask);
+ GrSafeUnref(fMaskTextures[i]);
+ fMaskTextures[i] = mask;
+ }
+
+ GrTexture* getMask(int i) const {
+ GrAssert((unsigned)i < kMaxMasks);
+ return fMaskTextures[i];
+ }
+
+ // mask's sampler matrix is always applied to the positions
+ // (i.e. no explicit texture coordinates)
+ GrSamplerState* getMaskSampler(int i) {
+ GrAssert((unsigned)i < kMaxMasks);
+ return fMaskSamplers + i;
+ }
+
+ const GrSamplerState* getMaskSampler(int i) const {
+ GrAssert((unsigned)i < kMaxMasks);
+ return fMaskSamplers + i;
}
- GrTexture* getTexture() const { return fTexture; }
+ // pre-concats sampler matrices for non-NULL textures and masks
+ void preConcatActiveSamplerMatrices(const GrMatrix& matrix) {
+ for (int i = 0; i < kMaxTextures; ++i) {
+ fTextureSamplers[i].preConcatMatrix(matrix);
+ }
+ for (int i = 0; i < kMaxMasks; ++i) {
+ fMaskSamplers[i].preConcatMatrix(matrix);
+ }
+ }
// uninitialized
GrPaint() {
- fTexture = NULL;
+ for (int i = 0; i < kMaxTextures; ++i) {
+ fTextures[i] = NULL;
+ }
+ for (int i = 0; i < kMaxMasks; ++i) {
+ fMaskTextures[i] = NULL;
+ }
}
GrPaint(const GrPaint& paint) {
@@ -67,22 +125,35 @@ public:
fColorFilterColor = paint.fColorFilterColor;
fColorFilterXfermode = paint.fColorFilterXfermode;
- fSampler = paint.fSampler;
- fTexture = paint.fTexture;
- GrSafeRef(fTexture);
+ for (int i = 0; i < kMaxTextures; ++i) {
+ fTextureSamplers[i] = paint.fTextureSamplers[i];
+ fTextures[i] = paint.fTextures[i];
+ GrSafeRef(fTextures[i]);
+ }
+ for (int i = 0; i < kMaxMasks; ++i) {
+ fMaskSamplers[i] = paint.fMaskSamplers[i];
+ fMaskTextures[i] = paint.fMaskTextures[i];
+ GrSafeRef(fMaskTextures[i]);
+ }
}
~GrPaint() {
- GrSafeUnref(fTexture);
+ for (int i = 0; i < kMaxTextures; ++i) {
+ GrSafeUnref(fTextures[i]);
+ }
+ for (int i = 0; i < kMaxMasks; ++i) {
+ GrSafeUnref(fMaskTextures[i]);
+ }
}
- // sets paint to src-over, solid white, no texture
+ // sets paint to src-over, solid white, no texture, no mask
void reset() {
- resetBlend();
- resetOptions();
- resetColor();
- resetTexture();
- resetColorFilter();
+ this->resetBlend();
+ this->resetOptions();
+ this->resetColor();
+ this->resetTextures();
+ this->resetColorFilter();
+ this->resetMasks();
}
void resetColorFilter() {
@@ -90,8 +161,60 @@ public:
fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
}
+ bool hasTexture() const {
+ return 0 != this->getActiveTextureStageMask();
+ }
+
+ bool hasMask() const {
+ return 0 != this->getActiveMaskStageMask();
+ }
+
+ bool hasTextureOrMask() const {
+ return this->hasTexture() || this->hasMask();
+ }
+
+ // helpers for GrContext, GrTextContext
+ int getActiveTextureStageMask() const {
+ int mask = 0;
+ for (int i = 0; i < kMaxTextures; ++i) {
+ if (NULL != fTextures[i]) {
+ mask |= 1 << (i + kFirstTextureStage);
+ }
+ }
+ return mask;
+ }
+
+ int getActiveMaskStageMask() const {
+ int mask = 0;
+ for (int i = 0; i < kMaxMasks; ++i) {
+ if (NULL != fMaskTextures[i]) {
+ mask |= 1 << (i + kFirstMaskStage);
+ }
+ }
+ return mask;
+ }
+
+ int getActiveStageMask() const {
+ return this->getActiveTextureStageMask() |
+ this->getActiveMaskStageMask();
+ }
+
+ // internal use
+ // GrPaint's textures and masks map to the first N stages
+ // of GrDrawTarget in that order (textures followed by masks)
+ enum {
+ kFirstTextureStage = 0,
+ kFirstMaskStage = kMaxTextures,
+ kTotalStages = kMaxTextures + kMaxMasks,
+ };
+
private:
- GrTexture* fTexture;
+
+ GrSamplerState fTextureSamplers[kMaxTextures];
+ GrSamplerState fMaskSamplers[kMaxMasks];
+
+ GrTexture* fTextures[kMaxTextures];
+ GrTexture* fMaskTextures[kMaxMasks];
void resetBlend() {
fSrcBlendCoeff = kOne_BlendCoeff;
@@ -107,11 +230,19 @@ private:
fColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
}
- void resetTexture() {
- setTexture(NULL);
- fSampler.setClampNoFilter();
+ void resetTextures() {
+ for (int i = 0; i < kMaxTextures; ++i) {
+ this->setTexture(i, NULL);
+ fTextureSamplers[i].setClampNoFilter();
+ }
}
+ void resetMasks() {
+ for (int i = 0; i < kMaxMasks; ++i) {
+ this->setMask(i, NULL);
+ fMaskSamplers[i].setClampNoFilter();
+ }
+ }
};
#endif
diff --git a/gpu/include/GrPath.h b/gpu/include/GrPath.h
index f958329d3f..c23cfc47c8 100644
--- a/gpu/include/GrPath.h
+++ b/gpu/include/GrPath.h
@@ -18,91 +18,10 @@
#ifndef GrPath_DEFINED
#define GrPath_DEFINED
-#include "GrPathSink.h"
-#include "GrPathIter.h"
-#include "GrTDArray.h"
-#include "GrPoint.h"
-#include "GrRect.h"
+#include "GrTypes.h"
+#include "SkPath.h"
-class GrPath : public GrPathSink {
-public:
- GrPath();
- GrPath(const GrPath&);
- explicit GrPath(GrPathIter&);
- virtual ~GrPath();
-
- GrConvexHint getConvexHint() const { return fConvexHint; }
- void setConvexHint(GrConvexHint hint) { fConvexHint = hint; }
-
- const GrRect& getConservativeBounds() const { return fConservativeBounds; }
-
- void resetFromIter(GrPathIter*);
-
- bool operator ==(const GrPath& path) const;
- bool operator !=(const GrPath& path) const { return !(*this == path); }
- // overrides from GrPathSink
-
- virtual void moveTo(GrScalar x, GrScalar y);
- virtual void lineTo(GrScalar x, GrScalar y);
- virtual void quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1);
- virtual void cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
- GrScalar x2, GrScalar y2);
- virtual void close();
-
- /**
- * Offset the path by (tx, ty), adding tx to the horizontal position
- * and adds ty to the vertical position of every point.
- */
- void offset(GrScalar tx, GrScalar ty);
-
- class Iter : public GrPathIter {
- public:
- /**
- * Creates an uninitialized iterator
- */
- Iter();
-
- Iter(const GrPath& path);
-
- // overrides from GrPathIter
- virtual GrPathCmd next(GrPoint points[]);
- virtual GrConvexHint convexHint() const;
- virtual GrPathCmd next();
- virtual void rewind();
- virtual bool getConservativeBounds(GrRect* rect) const;
-
- /**
- * Sets iterator to begining of path
- */
- void reset(const GrPath& path);
- private:
- const GrPath* fPath;
- GrPoint fLastPt;
- int fCmdIndex;
- int fPtIndex;
- };
-
- static void ConvexUnitTest();
-
-private:
-
- GrTDArray<GrPathCmd> fCmds;
- GrTDArray<GrPoint> fPts;
- GrConvexHint fConvexHint;
- GrRect fConservativeBounds;
-
- // this ensures we have a moveTo at the start of each contour
- inline void ensureMoveTo();
-
- bool wasLastVerb(GrPathCmd cmd) const {
- int count = fCmds.count();
- return count > 0 && cmd == fCmds[count - 1];
- }
-
- friend class Iter;
-
- typedef GrPathSink INHERITED;
-};
+typedef SkPath GrPath;
#endif
diff --git a/gpu/include/GrPathIter.h b/gpu/include/GrPathIter.h
deleted file mode 100644
index e67ff69856..0000000000
--- a/gpu/include/GrPathIter.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- Copyright 2010 Google Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-
-#ifndef GrPathIter_DEFINED
-#define GrPathIter_DEFINED
-
-#include "GrRect.h"
-
-/**
- 2D Path iterator. Porting layer creates a subclass of this. It allows Ganesh to
- parse the top-level API's 2D paths. Supports lines, quadratics, and cubic
- pieces and moves (multi-part paths).
- */
-class GrPathIter {
-public:
-
- virtual ~GrPathIter() {};
-
- /**
- * Iterates through the path. Should not be called after
- * kEnd_Command has been returned once. This version retrieves the
- * points for the command.
- * @param points The points relevant to returned commend. See Command
- * enum for number of points valid for each command.
- * @return The next command of the path.
- */
- virtual GrPathCmd next(GrPoint points[4]) = 0;
-
- /**
- * If the host API has knowledge of the convexity of the path
- * it can be communicated by this hint. Gr can analyze the path
- * as it is iterated. So it is not necessary to do additional work to
- * compute convexity status if it isn't already determined.
- *
- * @return a hint about the convexity of the path.
- */
- virtual GrConvexHint convexHint() const = 0;
-
- /**
- * Iterates through the path. Should not be called after
- * kEnd_Command has been returned once. This version does not retrieve the
- * points for the command.
- * @return The next command of the path.
- */
- virtual GrPathCmd next() = 0;
-
- /**
- * Returns conservative bounds on the path points. If returns false then
- * no bounds are available.
- */
- virtual bool getConservativeBounds(GrRect* rect) const = 0;
-
- /**
- Restarts iteration from the beginning.
- */
- virtual void rewind() = 0;
-
-};
-
-#endif
diff --git a/gpu/include/GrPathRenderer.h b/gpu/include/GrPathRenderer.h
index 21cab6bb1a..1ebad4f530 100644
--- a/gpu/include/GrPathRenderer.h
+++ b/gpu/include/GrPathRenderer.h
@@ -19,7 +19,7 @@
#include "GrDrawTarget.h"
-class GrPathIter;
+class SkPath;
struct GrPoint;
/**
@@ -37,8 +37,7 @@ public:
*
* @return true if the path can be drawn by this object, false otherwise.
*/
- virtual bool canDrawPath(const GrDrawTarget* target,
- GrPathIter* path,
+ virtual bool canDrawPath(const GrDrawTarget* target, const SkPath& path,
GrPathFill fill) const = 0;
/**
@@ -57,7 +56,7 @@ public:
*/
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill,
const GrPoint* translate) = 0;
@@ -80,7 +79,7 @@ public:
* clips.
*/
virtual bool requiresStencilPass(const GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill) const { return false; }
/**
@@ -102,7 +101,7 @@ public:
* the path. NULL means (0,0).
*/
virtual void drawPathToStencil(GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill,
const GrPoint* translate) {
GrCrash("Unexpected call to drawPathToStencil.");
@@ -113,7 +112,7 @@ public:
* having FSAA enabled for a render target)
*/
virtual bool supportsAA(GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill) { return false; }
/**
@@ -138,26 +137,26 @@ public:
bool stencilWrapOpsSupport);
virtual bool canDrawPath(const GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill) const { return true; }
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual bool requiresStencilPass(const GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill) const;
virtual void drawPathToStencil(GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill,
const GrPoint* translate);
private:
void onDrawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill,
const GrPoint* translate,
bool stencilOnly);
diff --git a/gpu/include/GrSamplerState.h b/gpu/include/GrSamplerState.h
index dd47c53726..373ea94cb3 100644
--- a/gpu/include/GrSamplerState.h
+++ b/gpu/include/GrSamplerState.h
@@ -94,6 +94,7 @@ public:
fSampleMode = kNormal_SampleMode;
fFilter = filter;
fMatrix.setIdentity();
+ fTextureDomain.setEmpty();
}
GrSamplerState(WrapMode wx, WrapMode wy, Filter filter) {
@@ -102,6 +103,7 @@ public:
fSampleMode = kNormal_SampleMode;
fFilter = filter;
fMatrix.setIdentity();
+ fTextureDomain.setEmpty();
}
GrSamplerState(WrapMode wx, WrapMode wy,
@@ -111,6 +113,7 @@ public:
fSampleMode = kNormal_SampleMode;
fFilter = filter;
fMatrix = matrix;
+ fTextureDomain.setEmpty();
}
GrSamplerState(WrapMode wx, WrapMode wy, SampleMode sample,
@@ -120,12 +123,15 @@ public:
fSampleMode = sample;
fMatrix = matrix;
fFilter = filter;
+ fTextureDomain.setEmpty();
}
WrapMode getWrapX() const { return fWrapX; }
WrapMode getWrapY() const { return fWrapY; }
SampleMode getSampleMode() const { return fSampleMode; }
const GrMatrix& getMatrix() const { return fMatrix; }
+ const GrRect& getTextureDomain() const { return fTextureDomain; }
+ bool hasTextureDomain() const {return SkIntToScalar(0) != fTextureDomain.right();}
Filter getFilter() const { return fFilter; }
bool isGradient() const {
@@ -146,6 +152,13 @@ public:
void setMatrix(const GrMatrix& matrix) { fMatrix = matrix; }
/**
+ * Sets the sampler's texture coordinate domain to a
+ * custom rectangle, rather than the default (0,1).
+ * This option is currently only supported with kClamp_WrapMode
+ */
+ void setTextureDomain(const GrRect& textureDomain) { fTextureDomain = textureDomain; }
+
+ /**
* Multiplies the current sampler matrix a matrix
*
* After this call M' = M*m where M is the old matrix, m is the parameter
@@ -169,6 +182,7 @@ public:
fSampleMode = kNormal_SampleMode;
fFilter = kNearest_Filter;
fMatrix.setIdentity();
+ fTextureDomain.setEmpty();
}
GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; }
@@ -198,6 +212,7 @@ private:
SampleMode fSampleMode;
Filter fFilter;
GrMatrix fMatrix;
+ GrRect fTextureDomain;
// these are undefined unless fSampleMode == kRadial2_SampleMode
GrScalar fRadial2CenterX1;
diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h
index a26b67c3bc..35cd61a22c 100644
--- a/gpu/include/GrScalar.h
+++ b/gpu/include/GrScalar.h
@@ -40,6 +40,7 @@
#define GrScalarHalf(a) SkScalarHalf(a)
#define GrScalarAve(a,b) SkScalarAve(a,b)
#define GrMul(a,b) SkScalarMul(a,b)
+#define GrScalarDiv(a,b) SkScalarDiv(a, b)
#define GrScalarToFloat(a) SkScalarToFloat(a)
#define GrFloatToScalar(a) SkScalarToFloat(a)
#define GrIntToScalar(a) SkIntToScalar(a)
diff --git a/gpu/include/GrStencil.h b/gpu/include/GrStencil.h
index b014ee5586..44a390d3fc 100644
--- a/gpu/include/GrStencil.h
+++ b/gpu/include/GrStencil.h
@@ -159,7 +159,7 @@ struct GrStencilSettings {
return kKeep_StencilOp == fFrontPassOp &&
kKeep_StencilOp == fBackPassOp &&
kKeep_StencilOp == fFrontFailOp &&
- kKeep_StencilOp == fFrontFailOp &&
+ kKeep_StencilOp == fBackFailOp &&
kAlways_StencilFunc == fFrontFunc &&
kAlways_StencilFunc == fBackFunc;
}
diff --git a/gpu/include/GrTesselatedPathRenderer.h b/gpu/include/GrTesselatedPathRenderer.h
index accd114e4c..e37e66bdf8 100644
--- a/gpu/include/GrTesselatedPathRenderer.h
+++ b/gpu/include/GrTesselatedPathRenderer.h
@@ -25,22 +25,22 @@ public:
virtual void drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual bool canDrawPath(const GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill) const;
virtual bool requiresStencilPass(const GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill) const { return false; }
virtual void drawPathToStencil(GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate);
virtual bool supportsAA(GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill);
};
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
index 08b10f0033..9348375987 100644
--- a/gpu/include/GrTypes.h
+++ b/gpu/include/GrTypes.h
@@ -18,11 +18,9 @@
#ifndef GrTypes_DEFINED
#define GrTypes_DEFINED
+#include "SkTypes.h"
#include "GrConfig.h"
-#include <memory.h>
-#include <string.h>
-
////////////////////////////////////////////////////////////////////////////////
/**
@@ -53,7 +51,7 @@
* Macro to round n up to the next multiple of 4, or return it unchanged if
* n is already a multiple of 4
*/
-#define GrALIGN4(n) (((n) + 3) >> 2 << 2)
+#define GrALIGN4(n) SkAlign4(n)
#define GrIsALIGN4(n) (((n) & 3) == 0)
template <typename T> const T& GrMin(const T& a, const T& b) {
@@ -111,7 +109,7 @@ static inline uint32_t GrSizeAlignDown(size_t x, uint32_t alignment) {
/**
* Count elements in an array
*/
-#define GR_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+#define GR_ARRAY_COUNT(array) SK_ARRAY_COUNT(array)
//!< allocate a block of memory, will never return NULL
extern void* GrMalloc(size_t bytes);
@@ -232,7 +230,7 @@ enum GrBlendCoeff {
kConstA_BlendCoeff, //<! constant color alpha
kIConstA_BlendCoeff, //<! one minus constant color alpha
- kBlendCoeffCount
+ kPublicBlendCoeffCount
};
/**
diff --git a/gpu/src/GrBinHashKey.h b/gpu/src/GrBinHashKey.h
index 683528b61e..565b4605a6 100644
--- a/gpu/src/GrBinHashKey.h
+++ b/gpu/src/GrBinHashKey.h
@@ -28,19 +28,20 @@ class GrBinHashKeyBuilder {
public:
GrBinHashKeyBuilder() {}
virtual ~GrBinHashKeyBuilder() {}
- virtual void keyData(const uint8_t *dataToAdd, size_t len) = 0;
+ virtual void keyData(const uint32_t *dataToAdd, size_t len) = 0;
};
/**
* Hash function class than can take a data stream of indeterminate length.
- * It also has the ability to recieve data in several chunks (steamed). The
- * hash function used is Adler-32.
+ * It also has the ability to recieve data in several chunks (steamed). The
+ * hash function used is the One-at-a-Time Hash
+ * (http://burtleburtle.net/bob/hash/doobs.html).
*
- * Keys are built in two passes the first pass builds the key until the
+ * Keys are built in two passes the first pass builds the key until the
* allocated storage for the key runs out, raw data accumulation stops, but
* the calculation of the 32-bit hash value and total key length continue.
* The second pass is only necessary if storage ran-out during the first pass.
- * If that is the case, the heap storage portion of the key will be
+ * If that is the case, the heap storage portion of the key will be
* re-allocated so that the entire key can be stored in the second pass.
*
* Code for building a key:
@@ -57,9 +58,8 @@ public:
template<typename Entry, size_t StackSize>
class GrBinHashKey : public GrBinHashKeyBuilder {
public:
- GrBinHashKey()
- : fA(1)
- , fB(0)
+ GrBinHashKey()
+ : fA(0)
, fLength(0)
, fHeapData(NULL)
, fPhysicalSize(StackSize)
@@ -77,15 +77,15 @@ private:
// errors with template classes, which are hard to trace back to the use
// of assignment.
GrBinHashKey(const GrBinHashKey<Entry, StackSize>&) {}
- GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry,
+ GrBinHashKey<Entry, StackSize>& operator=(const GrBinHashKey<Entry,
StackSize>&) {
return this;
}
-public:
+public:
void copyAndTakeOwnership(GrBinHashKey<Entry, StackSize>& key) {
- memcpy(this, &key, sizeof(*this));
GrAssert(key.fIsValid);
+ copyFields(key);
if (fUseHeap) {
key.fHeapData = NULL; // ownership transfer
}
@@ -98,7 +98,7 @@ public:
void deepCopyFrom(const GrBinHashKey<Entry, StackSize>& key) {
GrAssert(key.fIsValid);
- memcpy(this, &key, sizeof(key));
+ copyFields(key);
if (fUseHeap) {
fHeapData = reinterpret_cast<uint8_t*>(
GrMalloc(sizeof(uint8_t) * fPhysicalSize));
@@ -121,7 +121,7 @@ public:
if (1 == fPass) {
bool passNeeded = false;
if (fLength > fPhysicalSize) {
- // If the first pass ran out of space the we need to
+ // If the first pass ran out of space the we need to
// re-allocate and perform a second pass
GrFree(fHeapData);
fHeapData = reinterpret_cast<uint8_t*>(
@@ -132,14 +132,15 @@ public:
fLength = 0;
}
fPass++;
- return passNeeded;
+ return passNeeded;
}
return false;
}
- void keyData(const uint8_t *dataToAdd, size_t len) {
+ void keyData(const uint32_t *dataToAdd, size_t len) {
GrAssert(fIsValid);
GrAssert(fPass);
+ GrAssert(GrIsALIGN4(len));
if (fUseHeap) {
GrAssert(fHeapData);
GrAssert(fLength + len <= fPhysicalSize);
@@ -155,13 +156,18 @@ public:
fLength += len;
if (1 == fPass) {
- // update the 32-bit hash
- while (len) {
- fA = (fA + *dataToAdd) % kBigPrime;
- fB = (fB + fA) % kBigPrime;
- dataToAdd++;
- len--;
+ uint32_t a = fA;
+ while (len >= 4) {
+ a += *dataToAdd++;
+ a += (a << 10);
+ a ^= (a >> 6);
+ len -= 4;
}
+ a += (a << 3);
+ a ^= (a >> 11);
+ a += (a << 15);
+
+ fA = a;
}
}
@@ -175,34 +181,44 @@ public:
return memcmp(fStackData, key.fStackData, fLength);
}
}
-
+
return (fLength - key.fLength);
}
- static bool
+ static bool
EQ(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) {
GrAssert(key.fIsValid);
return 0 == entry.compare(key);
}
-
- static bool
+
+ static bool
LT(const Entry& entry, const GrBinHashKey<Entry, StackSize>& key) {
GrAssert(key.fIsValid);
- return entry.compare(key) < 0;
+ return entry.compare(key) < 0;
}
uint32_t getHash() const {
GrAssert(fIsValid);
- return (fB << 16) | fA;
+ return fA;
}
private:
- // For computing the Adler-32 hash
- enum Constants {
- kBigPrime = 65521 // largest prime smaller than 2^16
- };
+ void copyFields(const GrBinHashKey<Entry, StackSize>& src) {
+ if (fUseHeap) {
+ GrFree(fHeapData);
+ }
+ // We do a field-by-field copy because this is a non-POD
+ // class, and therefore memcpy would be bad
+ fA = src.fA;
+ fLength = src.fLength;
+ memcpy(fStackData, src.fStackData, StackSize);
+ fHeapData = src.fHeapData;
+ fPhysicalSize = src.fPhysicalSize;
+ fUseHeap = src.fUseHeap;
+ fPass = src.fPass;
+ }
+
uint32_t fA;
- uint32_t fB;
// For accumulating the variable length binary key
size_t fLength; // length of data accumulated so far
diff --git a/gpu/src/GrClip.cpp b/gpu/src/GrClip.cpp
index 2d1680cff8..c2613bbe25 100644
--- a/gpu/src/GrClip.cpp
+++ b/gpu/src/GrClip.cpp
@@ -132,7 +132,7 @@ void GrClip::setFromIterator(GrClipIterator* iter, GrScalar tx, GrScalar ty,
}
break;
case kPath_ClipType:
- e.fPath.resetFromIter(iter->getPathIter());
+ e.fPath = *iter->getPath();
if (tx || ty) {
e.fPath.offset(tx, ty);
}
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 8cb932b2c4..dae1cd14ab 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -19,14 +19,16 @@
#include "GrTextureCache.h"
#include "GrTextStrike.h"
#include "GrMemory.h"
-#include "GrPathIter.h"
#include "GrClipIterator.h"
#include "GrIndexBuffer.h"
#include "GrInOrderDrawBuffer.h"
#include "GrBufferAllocPool.h"
#include "GrPathRenderer.h"
-#define ENABLE_OFFSCREEN_AA 0
+// larger than this, and we don't AA. set to 0 for no AA
+#ifndef GR_MAX_OFFSCREEN_AA_DIM
+ #define GR_MAX_OFFSCREEN_AA_DIM 0
+#endif
#define DEFER_TEXT_RENDERING 1
@@ -107,6 +109,26 @@ void GrContext::freeGpuResources() {
////////////////////////////////////////////////////////////////////////////////
+int GrContext::PaintStageVertexLayoutBits(
+ const GrPaint& paint,
+ const bool hasTexCoords[GrPaint::kTotalStages]) {
+ int stageMask = paint.getActiveStageMask();
+ int layout = 0;
+ for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+ if ((1 << i) & stageMask) {
+ if (NULL != hasTexCoords && hasTexCoords[i]) {
+ layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(i, i);
+ } else {
+ layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+ }
+ }
+ }
+ return layout;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
enum {
kNPOTBit = 0x1,
kFilterBit = 0x2,
@@ -360,22 +382,7 @@ GrResource* GrContext::createPlatformSurface(const GrPlatformSurfaceDesc& desc)
return fGpu->createPlatformSurface(desc);
}
-GrRenderTarget* GrContext::createPlatformRenderTarget(intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width, int height) {
-#if GR_DEBUG
- GrPrintf("Using deprecated createPlatformRenderTarget API.");
-#endif
- return fGpu->createPlatformRenderTarget(platformRenderTarget,
- stencilBits, isMultisampled,
- width, height);
-}
-
GrRenderTarget* GrContext::createRenderTargetFrom3DApiState() {
-#if GR_DEBUG
- GrPrintf("Using deprecated createRenderTargetFrom3DApiState API.");
-#endif
return fGpu->createRenderTargetFrom3DApiState();
}
@@ -447,7 +454,7 @@ void GrContext::drawPaint(const GrPaint& paint) {
bool GrContext::doOffscreenAA(GrDrawTarget* target,
const GrPaint& paint,
bool isLines) const {
-#if !ENABLE_OFFSCREEN_AA
+#if GR_MAX_OFFSCREEN_AA_DIM==0
return false;
#else
if (!paint.fAntiAlias) {
@@ -477,7 +484,7 @@ bool GrContext::setupOffscreenAAPass1(GrDrawTarget* target,
bool requireStencil,
const GrIRect& boundRect,
OffscreenRecord* record) {
- GrAssert(ENABLE_OFFSCREEN_AA);
+ GrAssert(GR_MAX_OFFSCREEN_AA_DIM > 0);
GrAssert(NULL == record->fEntry0);
GrAssert(NULL == record->fEntry1);
@@ -577,6 +584,10 @@ void GrContext::offscreenAAPass2(GrDrawTarget* target,
GrTexture* src = record->fEntry0->texture();
int scale;
+ enum {
+ kOffscreenStage = GrPaint::kTotalStages,
+ };
+
if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
GrAssert(NULL != record->fEntry1);
scale = 2;
@@ -606,11 +617,14 @@ void GrContext::offscreenAAPass2(GrDrawTarget* target,
}
// setup for draw back to main RT
+ int stageMask = paint.getActiveStageMask();
+
target->restoreDrawState(record->fSavedState);
- if (NULL != paint.getTexture()) {
+
+ if (stageMask) {
GrMatrix invVM;
if (target->getViewInverse(&invVM)) {
- target->preConcatSamplerMatrix(0, invVM);
+ target->preConcatSamplerMatrices(stageMask, invVM);
}
}
target->setViewMatrix(GrMatrix::I());
@@ -624,7 +638,7 @@ void GrContext::offscreenAAPass2(GrDrawTarget* target,
target->setSamplerState(kOffscreenStage, sampler);
GrRect dstRect;
- int stages = (1 << kOffscreenStage) | (NULL == paint.getTexture() ? 0 : 1);
+ int stages = (1 << kOffscreenStage) | stageMask;
dstRect.set(boundRect);
target->drawSimpleRect(dstRect, NULL, stages);
@@ -662,11 +676,14 @@ static void setStrokeRectStrip(GrPoint verts[10], GrRect rect,
}
static GrColor getColorForMesh(const GrPaint& paint) {
- if (NULL == paint.getTexture()) {
- return paint.fColor;
- } else {
+ // FIXME: This was copied from SkGpuDevice, seems like
+ // we should have already smeared a in caller if that
+ // is what is desired.
+ if (paint.hasTexture()) {
unsigned a = GrColorUnpackA(paint.fColor);
return GrColorPackRGBA(a, a, a, a);
+ } else {
+ return paint.fColor;
}
}
@@ -742,10 +759,8 @@ void GrContext::fillAARect(GrDrawTarget* target,
const GrPaint& paint,
const GrRect& devRect) {
- GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
- if (NULL != paint.getTexture()) {
- layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
- }
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
+ GrDrawTarget::kColor_VertexLayoutBit;
size_t vsize = GrDrawTarget::VertexSize(layout);
@@ -783,11 +798,8 @@ void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint,
const GrScalar rx = GrMul(dx, GR_ScalarHalf);
const GrScalar ry = GrMul(dy, GR_ScalarHalf);
- GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
-
- if (NULL != paint.getTexture()) {
- layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
- }
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
+ GrDrawTarget::kColor_VertexLayoutBit;
GrScalar spare;
{
@@ -903,9 +915,9 @@ void GrContext::drawRect(const GrPaint& paint,
GrScalar width,
const GrMatrix* matrix) {
- bool textured = NULL != paint.getTexture();
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+ int stageMask = paint.getActiveStageMask();
GrRect devRect = rect;
GrMatrix combinedMatrix;
@@ -914,10 +926,10 @@ void GrContext::drawRect(const GrPaint& paint,
if (doAA) {
GrDrawTarget::AutoViewMatrixRestore avm(target);
- if (textured) {
+ if (stageMask) {
GrMatrix inv;
if (combinedMatrix.invert(&inv)) {
- target->preConcatSamplerMatrix(0, inv);
+ target->preConcatSamplerMatrices(stageMask, inv);
}
}
target->setViewMatrix(GrMatrix::I());
@@ -941,9 +953,8 @@ void GrContext::drawRect(const GrPaint& paint,
// TODO: consider making static vertex buffers for these cases.
// Hairline could be done by just adding closing vertex to
// unitSquareVertexBuffer()
- GrVertexLayout layout = textured ?
- GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
- 0;
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
+
static const int worstCaseVertCount = 10;
GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
@@ -974,17 +985,14 @@ void GrContext::drawRect(const GrPaint& paint,
if (NULL != matrix) {
avmr.set(target);
target->preConcatViewMatrix(*matrix);
- if (textured) {
- target->preConcatSamplerMatrix(0, *matrix);
- }
+ target->preConcatSamplerMatrices(stageMask, *matrix);
}
target->drawNonIndexed(primType, 0, vertCount);
} else {
#if GR_STATIC_RECT_VB
- GrVertexLayout layout = (textured) ?
- GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
- 0;
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
+
target->setVertexSourceToBuffer(layout,
fGpu->getUnitSquareVertexBuffer());
GrDrawTarget::AutoViewMatrixRestore avmr(target);
@@ -998,13 +1006,11 @@ void GrContext::drawRect(const GrPaint& paint,
}
target->preConcatViewMatrix(m);
-
- if (textured) {
- target->preConcatSamplerMatrix(0, m);
- }
+ target->preConcatSamplerMatrices(stageMask, m);
+
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
#else
- target->drawSimpleRect(rect, matrix, textured ? 1 : 0);
+ target->drawSimpleRect(rect, matrix, stageMask);
#endif
}
}
@@ -1015,7 +1021,8 @@ void GrContext::drawRectToRect(const GrPaint& paint,
const GrMatrix* dstMatrix,
const GrMatrix* srcMatrix) {
- if (NULL == paint.getTexture()) {
+ // srcRect refers to paint's first texture
+ if (NULL == paint.getTexture(0)) {
drawRect(paint, dstRect, -1, dstMatrix);
return;
}
@@ -1024,8 +1031,8 @@ void GrContext::drawRectToRect(const GrPaint& paint,
#if GR_STATIC_RECT_VB
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-
- GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
GrDrawTarget::AutoViewMatrixRestore avmr(target);
GrMatrix m;
@@ -1038,13 +1045,20 @@ void GrContext::drawRectToRect(const GrPaint& paint,
}
target->preConcatViewMatrix(m);
+ // srcRect refers to first stage
+ int otherStageMask = paint.getActiveStageMask() &
+ (~(1 << GrPaint::kFirstTextureStage));
+ if (otherStageMask) {
+ target->preConcatSamplerMatrices(otherStageMask, m);
+ }
+
m.setAll(srcRect.width(), 0, srcRect.fLeft,
0, srcRect.height(), srcRect.fTop,
0, 0, GrMatrix::I()[8]);
if (NULL != srcMatrix) {
m.postConcat(*srcMatrix);
}
- target->preConcatSamplerMatrix(0, m);
+ target->preConcatSamplerMatrix(GrPaint::kFirstTextureStage, m);
target->setVertexSourceToBuffer(layout, fGpu->getUnitSquareVertexBuffer());
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
@@ -1074,26 +1088,22 @@ void GrContext::drawVertices(const GrPaint& paint,
const GrColor colors[],
const uint16_t indices[],
int indexCount) {
- GrVertexLayout layout = 0;
- int vertexSize = sizeof(GrPoint);
GrDrawTarget::AutoReleaseGeometry geo;
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
- if (NULL != paint.getTexture()) {
- if (NULL == texCoords) {
- layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
- } else {
- layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
- vertexSize += sizeof(GrPoint);
- }
- }
+ bool hasTexCoords[GrPaint::kTotalStages] = {
+ NULL != texCoords, // texCoordSrc provides explicit stage 0 coords
+ 0 // remaining stages use positions
+ };
+
+ GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
if (NULL != colors) {
layout |= GrDrawTarget::kColor_VertexLayoutBit;
- vertexSize += sizeof(GrColor);
}
+ int vertexSize = GrDrawTarget::VertexSize(layout);
bool doAA = false;
OffscreenRecord record;
@@ -1106,9 +1116,9 @@ void GrContext::drawVertices(const GrPaint& paint,
}
int texOffsets[GrDrawTarget::kMaxTexCoords];
int colorOffset;
- int vsize = GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
- texOffsets,
- &colorOffset);
+ GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
+ texOffsets,
+ &colorOffset);
void* curVertex = geo.vertices();
for (int i = 0; i < vertexCount; ++i) {
@@ -1120,7 +1130,7 @@ void GrContext::drawVertices(const GrPaint& paint,
if (colorOffset > 0) {
*(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
}
- curVertex = (void*)((intptr_t)curVertex + vsize);
+ curVertex = (void*)((intptr_t)curVertex + vertexSize);
}
} else {
// we don't do offscreen AA when we have per-vertex tex coords or colors
@@ -1155,10 +1165,8 @@ void GrContext::drawVertices(const GrPaint& paint,
///////////////////////////////////////////////////////////////////////////////
-void GrContext::drawPath(const GrPaint& paint,
- GrPathIter* path,
- GrPathFill fill,
- const GrPoint* translate) {
+void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
+ GrPathFill fill, const GrPoint* translate) {
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
@@ -1180,9 +1188,10 @@ void GrContext::drawPath(const GrPaint& paint,
return;
}
}
- GrRect pathBounds;
- if (path->getConservativeBounds(&pathBounds)) {
- GrIRect pathIBounds;
+
+ GrRect pathBounds = path.getBounds();
+ GrIRect pathIBounds;
+ if (!pathBounds.isEmpty()) {
target->getViewMatrix().mapRect(&pathBounds, pathBounds);
pathBounds.roundOut(&pathIBounds);
if (!bound.intersect(pathIBounds)) {
@@ -1190,29 +1199,27 @@ void GrContext::drawPath(const GrPaint& paint,
}
}
+ // for now, abort antialiasing if our bounds are too big, so we don't
+ // hit the FBO size limit
+ if (pathIBounds.width() > GR_MAX_OFFSCREEN_AA_DIM ||
+ pathIBounds.height() > GR_MAX_OFFSCREEN_AA_DIM) {
+ goto NO_AA;
+ }
+
if (this->setupOffscreenAAPass1(target, needsStencil, bound, &record)) {
pr->drawPath(target, 0, path, fill, translate);
this->offscreenAAPass2(target, paint, bound, &record);
return;
}
- }
- GrDrawTarget::StageBitfield enabledStages = 0;
- if (NULL != paint.getTexture()) {
- enabledStages |= 1;
}
- pr->drawPath(target, enabledStages, path, fill, translate);
-}
+// we can fall out of the AA section for some reasons, and land here
+NO_AA:
+ GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask();
-void GrContext::drawPath(const GrPaint& paint,
- const GrPath& path,
- GrPathFill fill,
- const GrPoint* translate) {
- GrPath::Iter iter(path);
- this->drawPath(paint, &iter, fill, translate);
+ pr->drawPath(target, enabledStages, path, fill, translate);
}
-
////////////////////////////////////////////////////////////////////////////////
void GrContext::flush(int flagsBitfield) {
@@ -1320,8 +1327,21 @@ void GrContext::writePixels(int left, int top, int width, int height,
////////////////////////////////////////////////////////////////////////////////
void GrContext::SetPaint(const GrPaint& paint, GrDrawTarget* target) {
- target->setTexture(0, paint.getTexture());
- target->setSamplerState(0, paint.fSampler);
+
+ for (int i = 0; i < GrPaint::kMaxTextures; ++i) {
+ int s = i + GrPaint::kFirstTextureStage;
+ target->setTexture(s, paint.getTexture(i));
+ target->setSamplerState(s, *paint.getTextureSampler(i));
+ }
+
+ target->setFirstCoverageStage(GrPaint::kFirstMaskStage);
+
+ for (int i = 0; i < GrPaint::kMaxMasks; ++i) {
+ int s = i + GrPaint::kFirstMaskStage;
+ target->setTexture(s, paint.getMask(i));
+ target->setSamplerState(s, *paint.getMaskSampler(i));
+ }
+
target->setColor(paint.fColor);
if (paint.fDither) {
@@ -1483,7 +1503,7 @@ const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
}
GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill) {
if (NULL != fCustomPathRenderer &&
fCustomPathRenderer->canDrawPath(target, path, fill)) {
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 518b4eed6d..2848999fe4 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -19,6 +19,8 @@
#include "GrGpuVertex.h"
#include "GrTexture.h"
+namespace {
+
// recursive helper for creating mask with all the tex coord bits set for
// one stage
template <int N>
@@ -26,16 +28,16 @@ int stage_mask_recur(int stage) {
return GrDrawTarget::StageTexCoordVertexLayoutBit(stage, N) |
stage_mask_recur<N+1>(stage);
}
-template<> // linux build doesn't like static on specializations
+template<>
int stage_mask_recur<GrDrawTarget::kNumStages>(int) { return 0; }
// mask of all tex coord indices for one stage
-static int stage_tex_coord_mask(int stage) {
+int stage_tex_coord_mask(int stage) {
return stage_mask_recur<0>(stage);
}
// mask of all bits relevant to one stage
-static int stage_mask(int stage) {
+int stage_mask(int stage) {
return stage_tex_coord_mask(stage) |
GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(stage);
}
@@ -47,11 +49,11 @@ int tex_coord_mask_recur(int texCoordIdx) {
return GrDrawTarget::StageTexCoordVertexLayoutBit(N, texCoordIdx) |
tex_coord_mask_recur<N+1>(texCoordIdx);
}
-template<> // linux build doesn't like static on specializations
+template<>
int tex_coord_mask_recur<GrDrawTarget::kMaxTexCoords>(int) { return 0; }
// mask of all bits relevant to one texture coordinate index
-static int tex_coord_idx_mask(int texCoordIdx) {
+int tex_coord_idx_mask(int texCoordIdx) {
return tex_coord_mask_recur<0>(texCoordIdx);
}
@@ -66,6 +68,8 @@ bool check_layout(GrVertexLayout layout) {
return true;
}
+} //unnamed namespace
+
size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
@@ -370,10 +374,34 @@ void GrDrawTarget::disableState(uint32_t bits) {
fCurrDrawState.fFlagBits &= ~(bits);
}
-void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoef,
- GrBlendCoeff dstCoef) {
- fCurrDrawState.fSrcBlend = srcCoef;
- fCurrDrawState.fDstBlend = dstCoef;
+void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoeff,
+ GrBlendCoeff dstCoeff) {
+ fCurrDrawState.fSrcBlend = srcCoeff;
+ fCurrDrawState.fDstBlend = dstCoeff;
+#if GR_DEBUG
+ switch (dstCoeff) {
+ case kDC_BlendCoeff:
+ case kIDC_BlendCoeff:
+ case kDA_BlendCoeff:
+ case kIDA_BlendCoeff:
+ GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
+ "coverage stages.\n");
+ break;
+ default:
+ break;
+ }
+ switch (srcCoeff) {
+ case kSC_BlendCoeff:
+ case kISC_BlendCoeff:
+ case kSA_BlendCoeff:
+ case kISA_BlendCoeff:
+ GrPrintf("Unexpected src blend coeff. Won't work correctly with"
+ "coverage stages.\n");
+ break;
+ default:
+ break;
+ }
+#endif
}
void GrDrawTarget::setColor(GrColor c) {
@@ -478,10 +506,16 @@ void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
///////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::canDisableBlend() const {
- // If we're using edge antialiasing, we can't force blend off.
- if (fCurrDrawState.fFlagBits & kEdgeAA_StateBit) {
+ // If we compute a coverage value (using edge AA or a coverage stage) then
+ // we can't force blending off.
+ if (fCurrDrawState.fEdgeAANumEdges > 0) {
return false;
}
+ for (int s = fCurrDrawState.fFirstCoverageStage; s < kNumStages; ++s) {
+ if (this->isStageEnabled(s)) {
+ return false;
+ }
+ }
if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
(kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
@@ -506,8 +540,8 @@ bool GrDrawTarget::canDisableBlend() const {
return false;
}
- // ...and there isn't a texture with an alpha channel...
- for (int s = 0; s < kNumStages; ++s) {
+ // ...and there isn't a texture stage with an alpha channel...
+ for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
if (this->isStageEnabled(s)) {
GrAssert(NULL != fCurrDrawState.fTextures[s]);
@@ -531,8 +565,10 @@ bool GrDrawTarget::canDisableBlend() const {
}
///////////////////////////////////////////////////////////////////////////////
-void GrDrawTarget::setEdgeAAData(const float edges[18]) {
- memcpy(fCurrDrawState.fEdgeAAEdges, edges, sizeof(fCurrDrawState.fEdgeAAEdges));
+void GrDrawTarget::setEdgeAAData(const Edge* edges, int numEdges) {
+ GrAssert(numEdges <= kMaxEdges);
+ memcpy(fCurrDrawState.fEdgeAAEdges, edges, numEdges * sizeof(Edge));
+ fCurrDrawState.fEdgeAANumEdges = numEdges;
}
diff --git a/gpu/src/GrGLEffect.h b/gpu/src/GrGLEffect.h
deleted file mode 100644
index ef00df8f4b..0000000000
--- a/gpu/src/GrGLEffect.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- Copyright 2011 Google Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-#ifndef GrGLEffect_DEFINED
-#define GrGLEffect_DEFINED
-
-#include "GrGLInterface.h"
-#include "GrStringBuilder.h"
-
-class GrEffect;
-
-struct ShaderCodeSegments {
- GrStringBuilder fVSUnis;
- GrStringBuilder fVSAttrs;
- GrStringBuilder fVaryings;
- GrStringBuilder fFSUnis;
- GrStringBuilder fVSCode;
- GrStringBuilder fFSCode;
-};
-
-/**
- * This class is currently a stub. This will be a base class for "effects",
- * which extend the data model of GrPaint and extend the capability of
- * GrGLProgram in a modular fashion.
- */
-class GrGLEffect {
-protected:
- GrGLEffect(GrEffect* effect) {}
-public:
- virtual ~GrGLEffect() {}
- static GrGLEffect* Create(GrEffect* effect) { return NULL; }
- void genShaderCode(ShaderCodeSegments* segments) {}
- bool doGLSetup(GrPrimitiveType type, GrGLint program) { return true; }
- bool doGLPost() { return true; }
- void buildKey(GrBinHashKeyBuilder& key) const {}
- GrGLEffect* nextEffect() { return NULL; }
-};
-
-#endif
diff --git a/gpu/src/GrGLInterface.cpp b/gpu/src/GrGLInterface.cpp
index 0825a3f925..5ecf8eb591 100644
--- a/gpu/src/GrGLInterface.cpp
+++ b/gpu/src/GrGLInterface.cpp
@@ -333,6 +333,15 @@ bool GrGLInterface::validate(GrEngine engine) const {
}
}
+ // Dual source blending
+ if (kDesktop_GrGLBinding == fBindingsExported &&
+ (has_gl_extension_from_string("GL_ARB_blend_func_extended", ext) ||
+ (3 < major) || (3 == major && 3 <= minor))) {
+ if (NULL == fBindFragDataLocationIndexed) {
+ return false;
+ }
+ }
+
return true;
}
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index 5d2d8b342d..ecb4753fea 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -18,7 +18,6 @@
#include "GrBinHashKey.h"
#include "GrGLConfig.h"
-#include "GrGLEffect.h"
#include "GrMemory.h"
#include "SkXfermode.h"
@@ -86,6 +85,23 @@ static inline const char* vector_all_coords(int count) {
return ALL[count];
}
+static inline const char* all_ones_vec(int count) {
+ static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
+ "vec3(1,1,1)", "vec4(1,1,1,1)"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
+ return ONESVEC[count];
+}
+
+static inline const char* all_zeros_vec(int count) {
+ static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
+ "vec3(0,0,0)", "vec4(0,0,0,0)"};
+ GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
+ return ZEROSVEC[count];
+}
+
+static inline const char* declared_color_output_name() { return "fsColorOut"; }
+static inline const char* dual_source_output_name() { return "dualSourceOut"; }
+
static void tex_matrix_name(int stage, GrStringBuilder* s) {
#if GR_GL_ATTRIBUTE_MATRICES
*s = "aTexM";
@@ -120,113 +136,194 @@ static void radial2_varying_name(int stage, GrStringBuilder* s) {
s->appendS32(stage);
}
+static void tex_domain_name(int stage, GrStringBuilder* s) {
+ *s = "uTexDom";
+ s->appendS32(stage);
+}
+
GrGLProgram::GrGLProgram() {
- for(int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
- fStageEffects[stage] = NULL;
- }
}
GrGLProgram::~GrGLProgram() {
}
+void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
+ GrBlendCoeff* dstCoeff) const {
+ switch (fProgramDesc.fDualSrcOutput) {
+ case ProgramDesc::kNone_DualSrcOutput:
+ break;
+ // the prog will write a coverage value to the secondary
+ // output and the dst is blended by one minus that value.
+ case ProgramDesc::kCoverage_DualSrcOutput:
+ case ProgramDesc::kCoverageISA_DualSrcOutput:
+ case ProgramDesc::kCoverageISC_DualSrcOutput:
+ *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
+ break;
+ default:
+ GrCrash("Unexpected dual source blend output");
+ break;
+ }
+}
+
void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const {
// Add stage configuration to the key
- key.keyData(reinterpret_cast<const uint8_t*>(&fProgramDesc), sizeof(ProgramDesc));
-
- for(int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
- // First pass: count effects and write the count to the key.
- // This may seem like we are adding redundant data to the
- // key, but in ensures the one key cannot be a prefix of
- // another key, or identical to the key of a different program.
- GrGLEffect* currentEffect = fStageEffects[stage];
- uint8_t effectCount = 0;
- while (currentEffect) {
- GrAssert(effectCount < 255); // overflow detection
- ++effectCount;
- currentEffect = currentEffect->nextEffect();
- }
- key.keyData(reinterpret_cast<const uint8_t*>(&effectCount), sizeof(uint8_t));
+ key.keyData(reinterpret_cast<const uint32_t*>(&fProgramDesc), sizeof(ProgramDesc));
+}
- // Second pass: continue building key using the effects
- currentEffect = fStageEffects[stage];
- while (currentEffect) {
- fStageEffects[stage]->buildKey(key);
- }
+// assigns modulation of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all ones
+static inline void modulate_helper(const char* outputVar,
+ const char* var0,
+ const char* var1,
+ GrStringBuilder* code) {
+ GrAssert(NULL != outputVar);
+ GrAssert(NULL != var0);
+ GrAssert(NULL != var1);
+ GrAssert(NULL != code);
+
+ bool has0 = '\0' != *var0;
+ bool has1 = '\0' != *var1;
+
+ if (!has0 && !has1) {
+ code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
+ } else if (!has0) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+ } else if (!has1) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+ } else {
+ code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
}
}
-bool GrGLProgram::doGLSetup(GrPrimitiveType type,
- GrGLProgram::CachedData* programData) const {
- for (int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
- GrGLEffect* effect = fStageEffects[stage];
- if (effect) {
- if (!effect->doGLSetup(type, programData->fProgramID)) {
- return false;
- }
- }
+// assigns addition of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all zeros
+static inline void add_helper(const char* outputVar,
+ const char* var0,
+ const char* var1,
+ GrStringBuilder* code) {
+ GrAssert(NULL != outputVar);
+ GrAssert(NULL != var0);
+ GrAssert(NULL != var1);
+ GrAssert(NULL != code);
+
+ bool has0 = '\0' != *var0;
+ bool has1 = '\0' != *var1;
+
+ if (!has0 && !has1) {
+ code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
+ } else if (!has0) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+ } else if (!has1) {
+ code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+ } else {
+ code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
}
-
- return true;
}
-void GrGLProgram::doGLPost() const {
- for (int stage = 0; stage < GrDrawTarget::kNumStages; ++stage) {
- GrGLEffect* effect = fStageEffects[stage];
- if (effect) {
- effect->doGLPost();
- }
+// given two blend coeffecients determine whether the src
+// and/or dst computation can be omitted.
+static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
+ SkXfermode::Coeff dstCoeff,
+ bool* needSrcValue,
+ bool* needDstValue) {
+ if (SkXfermode::kZero_Coeff == srcCoeff) {
+ switch (dstCoeff) {
+ // these all read the src
+ case SkXfermode::kSC_Coeff:
+ case SkXfermode::kISC_Coeff:
+ case SkXfermode::kSA_Coeff:
+ case SkXfermode::kISA_Coeff:
+ *needSrcValue = true;
+ break;
+ default:
+ *needSrcValue = false;
+ break;
+ }
+ } else {
+ *needSrcValue = true;
+ }
+ if (SkXfermode::kZero_Coeff == dstCoeff) {
+ switch (srcCoeff) {
+ // these all read the dst
+ case SkXfermode::kDC_Coeff:
+ case SkXfermode::kIDC_Coeff:
+ case SkXfermode::kDA_Coeff:
+ case SkXfermode::kIDA_Coeff:
+ *needDstValue = true;
+ break;
+ default:
+ *needDstValue = false;
+ break;
+ }
+ } else {
+ *needDstValue = true;
}
}
/**
- * Create a text coefficient to be used in fragment shader code.
+ * Create a blend_coeff * value string to be used in shader code. Sets empty
+ * string if result is trivially zero.
*/
-static void coefficientString(GrStringBuilder* str, SkXfermode::Coeff coeff,
- const char* src, const char* dst) {
+static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
+ const char* src, const char* dst,
+ const char* value) {
switch (coeff) {
case SkXfermode::kZero_Coeff: /** 0 */
- *str = "0.0";
+ *str = "";
break;
case SkXfermode::kOne_Coeff: /** 1 */
- *str = "1.0";
+ *str = value;
+ break;
+ case SkXfermode::kSC_Coeff:
+ str->printf("(%s * %s)", src, value);
+ break;
+ case SkXfermode::kISC_Coeff:
+ str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
+ break;
+ case SkXfermode::kDC_Coeff:
+ str->printf("(%s * %s)", dst, value);
+ break;
+ case SkXfermode::kIDC_Coeff:
+ str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
break;
case SkXfermode::kSA_Coeff: /** src alpha */
- str->appendf("%s.a", src);
+ str->printf("(%s.a * %s)", src, value);
break;
case SkXfermode::kISA_Coeff: /** inverse src alpha (i.e. 1 - sa) */
- str->appendf("(1.0 - %s.a)", src);
+ str->printf("((1.0 - %s.a) * %s)", src, value);
break;
case SkXfermode::kDA_Coeff: /** dst alpha */
- str->appendf("%s.a", dst);
+ str->printf("(%s.a * %s)", dst, value);
break;
case SkXfermode::kIDA_Coeff: /** inverse dst alpha (i.e. 1 - da) */
- str->appendf("(1.0 - %s.a)", dst);
- break;
- case SkXfermode::kSC_Coeff:
- str->append(src);
+ str->printf("((1.0 - %s.a) * %s)", dst, value);
break;
default:
+ GrCrash("Unexpected xfer coeff.");
break;
}
}
-
/**
* Adds a line to the fragment shader code which modifies the color by
* the specified color filter.
*/
-static void addColorFilter(GrStringBuilder* FSCode, const char * outputVar,
- SkXfermode::Mode colorFilterXfermode, const char* dstColor) {
- SkXfermode::Coeff srcCoeff, dstCoeff;
- SkDEBUGCODE(bool success =)
- SkXfermode::ModeAsCoeff(colorFilterXfermode, &srcCoeff, &dstCoeff);
- // We currently do not handle modes that cannot be represented as
- // coefficients.
- GrAssert(success);
- GrStringBuilder srcCoeffStr, dstCoeffStr;
- coefficientString(&srcCoeffStr, srcCoeff, COL_FILTER_UNI_NAME, dstColor);
- coefficientString(&dstCoeffStr, dstCoeff, COL_FILTER_UNI_NAME, dstColor);
- FSCode->appendf("\t%s = %s*%s + %s*%s;\n", outputVar, srcCoeffStr.c_str(),
- COL_FILTER_UNI_NAME, dstCoeffStr.c_str(), dstColor);
+static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
+ SkXfermode::Coeff uniformCoeff,
+ SkXfermode::Coeff colorCoeff,
+ const char* inColor) {
+ GrStringBuilder colorStr, constStr;
+ blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
+ inColor, inColor);
+ blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
+ inColor, COL_FILTER_UNI_NAME);
+
+ add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
}
bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
@@ -236,6 +333,39 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
programData->fUniLocations.reset();
+ SkXfermode::Coeff colorCoeff, uniformCoeff;
+ // The rest of transfer mode color filters have not been implemented
+ if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+ GR_DEBUGCODE(bool success =)
+ SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
+ (fProgramDesc.fColorFilterXfermode),
+ &uniformCoeff, &colorCoeff);
+ GR_DEBUGASSERT(success);
+ } else {
+ colorCoeff = SkXfermode::kOne_Coeff;
+ uniformCoeff = SkXfermode::kZero_Coeff;
+ }
+
+ bool needColorFilterUniform;
+ bool needComputedColor;
+ needBlendInputs(uniformCoeff, colorCoeff,
+ &needColorFilterUniform, &needComputedColor);
+
+ // the dual source output has no canonical var name, have to
+ // declare an output, which is incompatible with gl_FragColor/gl_FragData.
+ const char* fsColorOutput;
+ bool dualSourceOutputWritten = false;
+ bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput !=
+ fProgramDesc.fDualSrcOutput;
+ if (usingDeclaredOutputs) {
+ GrAssert(0 == segments.fHeader.size());
+ segments.fHeader.printf("#version 150\n");
+ fsColorOutput = declared_color_output_name();
+ segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput);
+ } else {
+ fsColorOutput = "gl_FragColor";
+ }
+
#if GR_GL_ATTRIBUTE_MATRICES
segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
@@ -253,25 +383,23 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
// incoming color to current stage being processed.
GrStringBuilder inColor;
- switch (fProgramDesc.fColorType) {
- case ProgramDesc::kAttribute_ColorType:
- segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
- segments.fVaryings.append("varying vec4 vColor;\n");
- segments.fVSCode.append( "\tvColor = " COL_ATTR_NAME ";\n");
- inColor = "vColor";
- break;
- case ProgramDesc::kUniform_ColorType:
- segments.fFSUnis.append( "uniform vec4 " COL_UNI_NAME ";\n");
- programData->fUniLocations.fColorUni = kUseUniform;
- inColor = COL_UNI_NAME;
- break;
- case ProgramDesc::kNone_ColorType:
- inColor = "";
- break;
- }
-
- if (fProgramDesc.fUsesEdgeAA) {
- segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[6];\n");
+ if (needComputedColor) {
+ switch (fProgramDesc.fColorType) {
+ case ProgramDesc::kAttribute_ColorType:
+ segments.fVSAttrs.append( "attribute vec4 " COL_ATTR_NAME ";\n");
+ segments.fVaryings.append("varying vec4 vColor;\n");
+ segments.fVSCode.append( "\tvColor = " COL_ATTR_NAME ";\n");
+ inColor = "vColor";
+ break;
+ case ProgramDesc::kUniform_ColorType:
+ segments.fFSUnis.append( "uniform vec4 " COL_UNI_NAME ";\n");
+ programData->fUniLocations.fColorUni = kUseUniform;
+ inColor = COL_UNI_NAME;
+ break;
+ default:
+ GrAssert(ProgramDesc::kNone_ColorType == fProgramDesc.fColorType);
+ break;
+ }
}
if (fProgramDesc.fEmitsPointSize){
@@ -289,109 +417,204 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
}
}
- bool useColorFilter =
- // The rest of transfer mode color filters have not been implemented
- fProgramDesc.fColorFilterXfermode <= SkXfermode::kMultiply_Mode
- // This mode has no effect.
- && fProgramDesc.fColorFilterXfermode != SkXfermode::kDst_Mode;
- bool onlyUseColorFilter = useColorFilter
- && (fProgramDesc.fColorFilterXfermode == SkXfermode::kClear_Mode
- || fProgramDesc.fColorFilterXfermode == SkXfermode::kSrc_Mode);
- if (useColorFilter) {
- // Set up a uniform for the color
- segments.fFSUnis.append( "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
- programData->fUniLocations.fColorFilterUni = kUseUniform;
- }
-
- // for each enabled stage figure out what the input coordinates are
- // and count the number of stages in use.
- const char* stageInCoords[GrDrawTarget::kNumStages];
- int numActiveStages = 0;
+ ///////////////////////////////////////////////////////////////////////////
+ // compute the final color
- if (!onlyUseColorFilter) {
- for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
- if (fProgramDesc.fStages[s].fEnabled) {
- if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
- stageInCoords[s] = POS_ATTR_NAME;
+ // if we have color stages string them together, feeding the output color
+ // of each to the next and generating code for each stage.
+ if (needComputedColor) {
+ GrStringBuilder outColor;
+ for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
+ if (fProgramDesc.fStages[s].isEnabled()) {
+ // create var to hold stage result
+ outColor = "color";
+ outColor.appendS32(s);
+ segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+
+ const char* inCoords;
+ // figure out what our input coords are
+ if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+ layout) {
+ inCoords = POS_ATTR_NAME;
} else {
int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
// we better have input tex coordinates if stage is enabled.
GrAssert(tcIdx >= 0);
GrAssert(texCoordAttrs[tcIdx].size());
- stageInCoords[s] = texCoordAttrs[tcIdx].c_str();
+ inCoords = texCoordAttrs[tcIdx].c_str();
}
- ++numActiveStages;
+
+ genStageCode(s,
+ fProgramDesc.fStages[s],
+ inColor.size() ? inColor.c_str() : NULL,
+ outColor.c_str(),
+ inCoords,
+ &segments,
+ &programData->fUniLocations.fStages[s]);
+ inColor = outColor;
}
}
}
- // if we have active stages string them together, feeding the output color
- // of each to the next and generating code for each stage.
- if (numActiveStages) {
- int currActiveStage = 0;
- GrStringBuilder outColor;
- for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
- if (fProgramDesc.fStages[s].fEnabled) {
- if (currActiveStage < (numActiveStages - 1) || useColorFilter) {
- outColor = "color";
- outColor.appendS32(currActiveStage);
- segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+ // if have all ones for the "dst" input to the color filter then we can make
+ // additional optimizations.
+ if (needColorFilterUniform && !inColor.size() &&
+ (SkXfermode::kIDC_Coeff == uniformCoeff ||
+ SkXfermode::kIDA_Coeff == uniformCoeff)) {
+ uniformCoeff = SkXfermode::kZero_Coeff;
+ bool bogus;
+ needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
+ &needColorFilterUniform, &bogus);
+ }
+ if (needColorFilterUniform) {
+ segments.fFSUnis.append( "uniform vec4 " COL_FILTER_UNI_NAME ";\n");
+ programData->fUniLocations.fColorFilterUni = kUseUniform;
+ }
+
+ bool wroteFragColorZero = false;
+ if (SkXfermode::kZero_Coeff == uniformCoeff &&
+ SkXfermode::kZero_Coeff == colorCoeff) {
+ segments.fFSCode.appendf("\t%s = %s;\n",
+ fsColorOutput,
+ all_zeros_vec(4));
+ wroteFragColorZero = true;
+ } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
+ segments.fFSCode.appendf("\tvec4 filteredColor;\n");
+ const char* color = inColor.size() ? inColor.c_str() : all_ones_vec(4);
+ addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
+ colorCoeff, color);
+ inColor = "filteredColor";
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // compute the partial coverage (coverage stages and edge aa)
+
+ GrStringBuilder inCoverage;
+
+ // we don't need to compute coverage at all if we know the final shader
+ // output will be zero and we don't have a dual src blend output.
+ if (!wroteFragColorZero ||
+ ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+ if (fProgramDesc.fEdgeAANumEdges > 0) {
+ segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[");
+ segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges);
+ segments.fFSUnis.append("];\n");
+ programData->fUniLocations.fEdgesUni = kUseUniform;
+ int count = fProgramDesc.fEdgeAANumEdges;
+ segments.fFSCode.append(
+ "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n");
+ for (int i = 0; i < count; i++) {
+ segments.fFSCode.append("\tfloat a");
+ segments.fFSCode.appendS32(i);
+ segments.fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "[");
+ segments.fFSCode.appendS32(i);
+ segments.fFSCode.append("], pos), 0.0, 1.0);\n");
+ }
+ segments.fFSCode.append("\tfloat edgeAlpha = ");
+ for (int i = 0; i < count - 1; i++) {
+ segments.fFSCode.append("min(a");
+ segments.fFSCode.appendS32(i);
+ segments.fFSCode.append(" * a");
+ segments.fFSCode.appendS32(i + 1);
+ segments.fFSCode.append(", ");
+ }
+ segments.fFSCode.append("a");
+ segments.fFSCode.appendS32(count - 1);
+ segments.fFSCode.append(" * a0");
+ for (int i = 0; i < count - 1; i++) {
+ segments.fFSCode.append(")");
+ }
+ segments.fFSCode.append(";\n");
+ inCoverage = "edgeAlpha";
+ }
+
+ GrStringBuilder outCoverage;
+ const int& startStage = fProgramDesc.fFirstCoverageStage;
+ for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) {
+ if (fProgramDesc.fStages[s].isEnabled()) {
+ // create var to hold stage output
+ outCoverage = "coverage";
+ outCoverage.appendS32(s);
+ segments.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
+
+ const char* inCoords;
+ // figure out what our input coords are
+ if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
+ inCoords = POS_ATTR_NAME;
} else {
- outColor = "gl_FragColor";
+ int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+ // we better have input tex coordinates if stage is enabled.
+ GrAssert(tcIdx >= 0);
+ GrAssert(texCoordAttrs[tcIdx].size());
+ inCoords = texCoordAttrs[tcIdx].c_str();
}
genStageCode(s,
fProgramDesc.fStages[s],
- inColor.size() ? inColor.c_str() : NULL,
- outColor.c_str(),
- stageInCoords[s],
+ inCoverage.size() ? inCoverage.c_str() : NULL,
+ outCoverage.c_str(),
+ inCoords,
&segments,
&programData->fUniLocations.fStages[s]);
- ++currActiveStage;
- inColor = outColor;
+ inCoverage = outCoverage;
}
}
- if (useColorFilter) {
- addColorFilter(&segments.fFSCode, "gl_FragColor",
- fProgramDesc.fColorFilterXfermode, outColor.c_str());
- }
-
- } else {
- if (fProgramDesc.fUsesEdgeAA) {
- // FIXME: put the a's in a loop
- segments.fFSCode.append(
- "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n"
- "\tfloat a0 = clamp(dot(uEdges[0], pos), 0.0, 1.0);\n"
- "\tfloat a1 = clamp(dot(uEdges[1], pos), 0.0, 1.0);\n"
- "\tfloat a2 = clamp(dot(uEdges[2], pos), 0.0, 1.0);\n"
- "\tfloat a3 = clamp(dot(uEdges[3], pos), 0.0, 1.0);\n"
- "\tfloat a4 = clamp(dot(uEdges[4], pos), 0.0, 1.0);\n"
- "\tfloat a5 = clamp(dot(uEdges[5], pos), 0.0, 1.0);\n"
- "\tfloat edgeAlpha = min(min(a0 * a1, a2 * a3), a4 * a5);\n");
- if (inColor.size()) {
- inColor.append(" * edgeAlpha");
+ if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+ segments.fFSOutputs.appendf("out vec4 %s;\n",
+ dual_source_output_name());
+ bool outputIsZero = false;
+ GrStringBuilder coeff;
+ if (ProgramDesc::kCoverage_DualSrcOutput !=
+ fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+ if (!inColor.size()) {
+ outputIsZero = true;
+ } else {
+ if (fProgramDesc.fDualSrcOutput ==
+ ProgramDesc::kCoverageISA_DualSrcOutput) {
+ coeff.printf("(1 - %s.a)", inColor.c_str());
+ } else {
+ coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
+ }
+ }
+ }
+ if (outputIsZero) {
+ segments.fFSCode.appendf("\t%s = %s;\n",
+ dual_source_output_name(),
+ all_zeros_vec(4));
} else {
- inColor = "vec4(edgeAlpha)";
+ modulate_helper(dual_source_output_name(),
+ coeff.c_str(),
+ inCoverage.c_str(),
+ &segments.fFSCode);
}
- }
- // we may not have any incoming color
- const char * incomingColor = (inColor.size() ? inColor.c_str()
- : "vec4(1,1,1,1)");
- if (useColorFilter) {
- addColorFilter(&segments.fFSCode, "gl_FragColor",
- fProgramDesc.fColorFilterXfermode, incomingColor);
- } else {
- segments.fFSCode.appendf("\tgl_FragColor = %s;\n", incomingColor);
+ dualSourceOutputWritten = true;
}
}
+
+ ///////////////////////////////////////////////////////////////////////////
+ // combine color and coverage as frag color
+
+ if (!wroteFragColorZero) {
+ modulate_helper(fsColorOutput,
+ inColor.c_str(),
+ inCoverage.c_str(),
+ &segments.fFSCode);
+ }
+
segments.fVSCode.append("}\n");
segments.fFSCode.append("}\n");
+ ///////////////////////////////////////////////////////////////////////////
+ // compile and setup attribs and unis
+
if (!CompileFSAndVS(segments, programData)) {
return false;
}
- if (!this->bindAttribsAndLinkProgram(texCoordAttrs, programData)) {
+ if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
+ usingDeclaredOutputs,
+ dualSourceOutputWritten,
+ programData)) {
return false;
}
@@ -403,10 +626,16 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
CachedData* programData) {
- const char* strings[4];
- int lengths[4];
+ static const int MAX_STRINGS = 6;
+ const char* strings[MAX_STRINGS];
+ int lengths[MAX_STRINGS];
int stringCnt = 0;
+ if (segments.fHeader.size()) {
+ strings[stringCnt] = segments.fHeader.c_str();
+ lengths[stringCnt] = segments.fHeader.size();
+ ++stringCnt;
+ }
if (segments.fVSUnis.size()) {
strings[stringCnt] = segments.fVSUnis.c_str();
lengths[stringCnt] = segments.fVSUnis.size();
@@ -429,12 +658,14 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
++stringCnt;
#if PRINT_SHADERS
+ GrPrintf(segments.fHeader.c_str());
GrPrintf(segments.fVSUnis.c_str());
GrPrintf(segments.fVSAttrs.c_str());
GrPrintf(segments.fVaryings.c_str());
GrPrintf(segments.fVSCode.c_str());
GrPrintf("\n");
#endif
+ GrAssert(stringCnt <= MAX_STRINGS);
programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
stringCnt,
strings,
@@ -446,6 +677,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
stringCnt = 0;
+ if (segments.fHeader.size()) {
+ strings[stringCnt] = segments.fHeader.c_str();
+ lengths[stringCnt] = segments.fHeader.size();
+ ++stringCnt;
+ }
if (strlen(GrShaderPrecision()) > 1) {
strings[stringCnt] = GrShaderPrecision();
lengths[stringCnt] = strlen(GrShaderPrecision());
@@ -461,6 +697,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
lengths[stringCnt] = segments.fVaryings.size();
++stringCnt;
}
+ if (segments.fFSOutputs.size()) {
+ strings[stringCnt] = segments.fFSOutputs.c_str();
+ lengths[stringCnt] = segments.fFSOutputs.size();
+ ++stringCnt;
+ }
GrAssert(segments.fFSCode.size());
strings[stringCnt] = segments.fFSCode.c_str();
@@ -468,12 +709,15 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
++stringCnt;
#if PRINT_SHADERS
+ GrPrintf(segments.fHeader.c_str());
GrPrintf(GrShaderPrecision());
GrPrintf(segments.fFSUnis.c_str());
GrPrintf(segments.fVaryings.c_str());
+ GrPrintf(segments.fFSOutputs.c_str());
GrPrintf(segments.fFSCode.c_str());
GrPrintf("\n");
#endif
+ GrAssert(stringCnt <= MAX_STRINGS);
programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
stringCnt,
strings,
@@ -482,6 +726,7 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
if (!programData->fFShaderID) {
return false;
}
+
return true;
}
@@ -521,8 +766,11 @@ GrGLuint GrGLProgram::CompileShader(GrGLenum type,
return shader;
}
-bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
- CachedData* programData) const {
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
+ GrStringBuilder texCoordAttrNames[],
+ bool bindColorOut,
+ bool bindDualSrcOut,
+ CachedData* programData) const {
programData->fProgramID = GR_GL(CreateProgram());
if (!programData->fProgramID) {
return false;
@@ -532,6 +780,15 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
GR_GL(AttachShader(progID, programData->fVShaderID));
GR_GL(AttachShader(progID, programData->fFShaderID));
+ if (bindColorOut) {
+ GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
+ 0, 0, declared_color_output_name()));
+ }
+ if (bindDualSrcOut) {
+ GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
+ 0, 1, dual_source_output_name()));
+ }
+
// Bind the attrib locations to same values for all shaders
GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
@@ -542,7 +799,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
}
}
-
if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
GR_GL(BindAttribLocation(progID,
ViewMatrixAttributeIdx(),
@@ -560,7 +816,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
}
}
-
GR_GL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
GR_GL(LinkProgram(progID));
@@ -595,18 +850,18 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const
GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
}
if (kUseUniform == programData->fUniLocations.fColorUni) {
- programData->fUniLocations.fColorUni =
+ programData->fUniLocations.fColorUni =
GR_GL(GetUniformLocation(progID, COL_UNI_NAME));
GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
}
if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
- programData->fUniLocations.fColorFilterUni =
+ programData->fUniLocations.fColorFilterUni =
GR_GL(GetUniformLocation(progID, COL_FILTER_UNI_NAME));
GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
}
- if (fProgramDesc.fUsesEdgeAA) {
- programData->fUniLocations.fEdgesUni =
+ if (kUseUniform == programData->fUniLocations.fEdgesUni) {
+ programData->fUniLocations.fEdgesUni =
GR_GL(GetUniformLocation(progID, EDGES_UNI_NAME));
GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
} else {
@@ -615,7 +870,7 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
StageUniLocations& locations = programData->fUniLocations.fStages[s];
- if (fProgramDesc.fStages[s].fEnabled) {
+ if (fProgramDesc.fStages[s].isEnabled()) {
if (kUseUniform == locations.fTextureMatrixUni) {
GrStringBuilder texMName;
tex_matrix_name(s, &texMName);
@@ -637,7 +892,7 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const
if (kUseUniform == locations.fNormalizedTexelSizeUni) {
GrStringBuilder texelSizeName;
normalized_texel_size_name(s, &texelSizeName);
- locations.fNormalizedTexelSizeUni =
+ locations.fNormalizedTexelSizeUni =
GR_GL(GetUniformLocation(progID, texelSizeName.c_str()));
GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
}
@@ -650,6 +905,15 @@ void GrGLProgram::getUniformLocationsAndInitCache(CachedData* programData) const
radial2ParamName.c_str()));
GrAssert(kUnusedUniform != locations.fRadial2Uni);
}
+
+ if (kUseUniform == locations.fTexDomUni) {
+ GrStringBuilder texDomName;
+ tex_domain_name(s, &texDomName);
+ locations.fTexDomUni = GR_GL(GetUniformLocation(
+ progID,
+ texDomName.c_str()));
+ GrAssert(kUnusedUniform != locations.fTexDomUni);
+ }
}
}
GR_GL(UseProgram(progID));
@@ -726,7 +990,7 @@ void GrGLProgram::genStageCode(int stageNum,
segments->fFSUnis.appendf("uniform vec2 %s;\n", texelSizeName.c_str());
}
- segments->fVaryings.appendf("varying %s %s;\n",
+ segments->fVaryings.appendf("varying %s %s;\n",
float_vector_type(varyingDims), varyingName.c_str());
if (desc.fOptFlags & ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit) {
@@ -748,9 +1012,9 @@ void GrGLProgram::genStageCode(int stageNum,
if (ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping == desc.fCoordMapping) {
- segments->fVSUnis.appendf("uniform %s float %s[6];\n",
+ segments->fVSUnis.appendf("uniform %s float %s[6];\n",
GrPrecision(), radial2ParamsName.c_str());
- segments->fFSUnis.appendf("uniform float %s[6];\n",
+ segments->fFSUnis.appendf("uniform float %s[6];\n",
radial2ParamsName.c_str());
locations->fRadial2Uni = kUseUniform;
@@ -869,6 +1133,24 @@ void GrGLProgram::genStageCode(int stageNum,
modulate.printf(" * %s", fsInColor);
}
+ if (desc.fOptFlags &
+ ProgramDesc::StageDesc::kCustomTextureDomain_OptFlagBit) {
+ GrStringBuilder texDomainName;
+ tex_domain_name(stageNum, &texDomainName);
+ segments->fFSUnis.appendf("uniform %s %s;\n",
+ float_vector_type(4),
+ texDomainName.c_str());
+ GrStringBuilder coordVar("clampCoord");
+ segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
+ float_vector_type(coordDims),
+ coordVar.c_str(),
+ sampleCoords.c_str(),
+ texDomainName.c_str(),
+ texDomainName.c_str());
+ sampleCoords = coordVar;
+ locations->fTexDomUni = kUseUniform;
+ }
+
if (ProgramDesc::StageDesc::k2x2_FetchMode == desc.fFetchMode) {
locations->fNormalizedTexelSizeUni = kUseUniform;
if (complexCoord) {
@@ -889,11 +1171,6 @@ void GrGLProgram::genStageCode(int stageNum,
segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
} else {
- segments->fFSCode.appendf("\t%s = %s(%s, %s)%s %s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
- }
-
- if(fStageEffects[stageNum]) {
- fStageEffects[stageNum]->genShaderCode(segments);
+ segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
}
}
-
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 1611ca21c4..473bcb65a0 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -19,13 +19,22 @@
#include "GrGLInterface.h"
#include "GrStringBuilder.h"
-#include "GrDrawTarget.h"
+#include "GrGpu.h"
#include "SkXfermode.h"
class GrBinHashKeyBuilder;
-class GrGLEffect;
-struct ShaderCodeSegments;
+
+struct ShaderCodeSegments {
+ GrStringBuilder fHeader; // VS+FS, GLSL version, etc
+ GrStringBuilder fVSUnis;
+ GrStringBuilder fVSAttrs;
+ GrStringBuilder fVaryings;
+ GrStringBuilder fFSUnis;
+ GrStringBuilder fFSOutputs;
+ GrStringBuilder fVSCode;
+ GrStringBuilder fFSCode;
+};
/**
* This class manages a GPU program and records per-program information.
@@ -58,39 +67,29 @@ public:
*/
bool genProgram(CachedData* programData) const;
- /**
- * Routine that is called before rendering. Sets-up all the state and
- * other initializations required for the Gpu Program to run.
- */
- bool doGLSetup(GrPrimitiveType type, CachedData* programData) const;
-
- /**
- * Routine that is called after rendering. Performs state restoration.
- * May perform secondary render passes.
- */
- void doGLPost() const;
+ /**
+ * The shader may modify the blend coeffecients. Params are in/out
+ */
+ void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
/**
- * Configures the GrGLProgram based on the state of a GrDrawTarget
- * object. This is the fast and light initialization. Retrieves all the
- * state that is required for performing the heavy init (i.e. genProgram),
- * or for retrieving heavy init results from cache.
+ * Attribute indices
*/
- void buildFromTarget(const GrDrawTarget* target);
-
static int PositionAttributeIdx() { return 0; }
static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
- static int ViewMatrixAttributeIdx() {
- return 2 + GrDrawTarget::kMaxTexCoords;
+ static int ViewMatrixAttributeIdx() {
+ return 2 + GrDrawTarget::kMaxTexCoords;
}
- static int TextureMatrixAttributeIdx(int stage) {
- return 5 + GrDrawTarget::kMaxTexCoords + 3 * stage;
+ static int TextureMatrixAttributeIdx(int stage) {
+ return 5 + GrDrawTarget::kMaxTexCoords + 3 * stage;
}
private:
- //Parameters that affect code generation
+ // Parameters that affect code generation
+ // These structs should be kept compact; they are the input to an
+ // expensive hash key generator.
struct ProgramDesc {
ProgramDesc() {
// since we use this as part of a key we can't have any unitialized
@@ -98,46 +97,76 @@ private:
memset(this, 0, sizeof(ProgramDesc));
}
- // stripped of bits that don't affect prog generation
- GrVertexLayout fVertexLayout;
-
- enum {
- kNone_ColorType = 0,
- kAttribute_ColorType = 1,
- kUniform_ColorType = 2,
- } fColorType;
-
- bool fEmitsPointSize;
- bool fUsesEdgeAA;
-
- SkXfermode::Mode fColorFilterXfermode;
-
struct StageDesc {
enum OptFlagBits {
- kNoPerspective_OptFlagBit = 0x1,
- kIdentityMatrix_OptFlagBit = 0x2
+ kNoPerspective_OptFlagBit = 1 << 0,
+ kIdentityMatrix_OptFlagBit = 1 << 1,
+ kCustomTextureDomain_OptFlagBit = 1 << 2,
+ kIsEnabled_OptFlagBit = 1 << 7
};
-
- unsigned fOptFlags;
- bool fEnabled;
-
enum Modulation {
kColor_Modulation,
kAlpha_Modulation
- } fModulation;
-
+ };
enum FetchMode {
kSingle_FetchMode,
k2x2_FetchMode
- } fFetchMode;
-
+ };
enum CoordMapping {
kIdentity_CoordMapping,
kRadialGradient_CoordMapping,
kSweepGradient_CoordMapping,
kRadial2Gradient_CoordMapping
- } fCoordMapping;
- } fStages[GrDrawTarget::kNumStages];
+ };
+
+ uint8_t fOptFlags;
+ uint8_t fModulation; // casts to enum Modulation
+ uint8_t fFetchMode; // casts to enum FetchMode
+ uint8_t fCoordMapping; // casts to enum CoordMapping
+
+ inline bool isEnabled() const {
+ return fOptFlags & kIsEnabled_OptFlagBit;
+ }
+ inline void setEnabled(bool newValue) {
+ if (newValue) {
+ fOptFlags |= kIsEnabled_OptFlagBit;
+ } else {
+ fOptFlags &= ~kIsEnabled_OptFlagBit;
+ }
+ }
+ };
+
+ enum ColorType {
+ kNone_ColorType = 0,
+ kAttribute_ColorType = 1,
+ kUniform_ColorType = 2,
+ };
+ // Dual-src blending makes use of a secondary output color that can be
+ // used as a per-pixel blend coeffecient. This controls whether a
+ // secondary source is output and what value it holds.
+ enum DualSrcOutput {
+ kNone_DualSrcOutput,
+ kCoverage_DualSrcOutput,
+ kCoverageISA_DualSrcOutput,
+ kCoverageISC_DualSrcOutput,
+ kDualSrcOutputCnt
+ };
+
+ // stripped of bits that don't affect prog generation
+ GrVertexLayout fVertexLayout;
+
+ StageDesc fStages[GrDrawTarget::kNumStages];
+
+ uint8_t fColorType; // casts to enum ColorType
+ uint8_t fDualSrcOutput; // casts to enum DualSrcOutput
+ int8_t fFirstCoverageStage;
+ SkBool8 fEmitsPointSize;
+
+ int8_t fEdgeAANumEdges;
+ uint8_t fColorFilterXfermode; // casts to enum SkXfermode::Mode
+
+ uint8_t fPadTo32bLengthMultiple [2];
+
} fProgramDesc;
const ProgramDesc& getDesc() { return fProgramDesc; }
@@ -153,11 +182,13 @@ public:
GrGLint fNormalizedTexelSizeUni;
GrGLint fSamplerUni;
GrGLint fRadial2Uni;
+ GrGLint fTexDomUni;
void reset() {
fTextureMatrixUni = kUnusedUniform;
fNormalizedTexelSizeUni = kUnusedUniform;
fSamplerUni = kUnusedUniform;
fRadial2Uni = kUnusedUniform;
+ fTexDomUni = kUnusedUniform;
}
};
@@ -181,35 +212,13 @@ public:
class CachedData : public ::GrNoncopyable {
public:
CachedData() {
- GR_DEBUGCODE(fEffectUniCount = 0;)
- fEffectUniLocationsExtended = NULL;
}
~CachedData() {
- GrFree(fEffectUniLocationsExtended);
}
void copyAndTakeOwnership(CachedData& other) {
memcpy(this, &other, sizeof(*this));
- other.fEffectUniLocationsExtended = NULL; // ownership transfer
- GR_DEBUGCODE(other.fEffectUniCount = 0;)
- }
-
- void setEffectUniformCount(size_t effectUniforms) {
- GR_DEBUGCODE(fEffectUniCount = effectUniforms;)
- GrFree(fEffectUniLocationsExtended);
- if (effectUniforms > kUniLocationPreAllocSize) {
- fEffectUniLocationsExtended = (GrGLint*)GrMalloc(sizeof(GrGLint)*(effectUniforms-kUniLocationPreAllocSize));
- } else {
- fEffectUniLocationsExtended = NULL;
- }
- }
-
- GrGLint& effectUniLocation(size_t index) {
- GrAssert(index < fEffectUniCount);
- return (index < kUniLocationPreAllocSize) ?
- fEffectUniLocations[index] :
- fEffectUniLocationsExtended[index - kUniLocationPreAllocSize];
}
public:
@@ -234,19 +243,15 @@ public:
GrScalar fRadial2CenterX1[GrDrawTarget::kNumStages];
GrScalar fRadial2Radius0[GrDrawTarget::kNumStages];
bool fRadial2PosRoot[GrDrawTarget::kNumStages];
+ GrRect fTextureDomain[GrDrawTarget::kNumStages];
private:
enum Constants {
kUniLocationPreAllocSize = 8
};
- GrGLint fEffectUniLocations[kUniLocationPreAllocSize];
- GrGLint* fEffectUniLocationsExtended;
- GR_DEBUGCODE(size_t fEffectUniCount;)
}; // CachedData
- GrGLEffect* fStageEffects[GrDrawTarget::kNumStages];
-
private:
enum {
kUseUniform = 2000
@@ -273,8 +278,11 @@ private:
// Creates a GL program ID, binds shader attributes to GL vertex attrs, and
// links the program
- bool bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
- CachedData* programData) const;
+ bool bindOutputsAttribsAndLinkProgram(
+ GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
+ bool bindColorOut,
+ bool bindDualSrcOut,
+ CachedData* programData) const;
// Gets locations for all uniforms set to kUseUniform and initializes cache
// to invalid values.
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 9950afd27b..4fe7ccc30d 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -142,17 +142,6 @@ GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
return this->onCreateTexture(desc, srcData, rowBytes);
}
-GrRenderTarget* GrGpu::createPlatformRenderTarget(intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width, int height) {
- this->handleDirtyContext();
- return this->onCreatePlatformRenderTarget(platformRenderTarget,
- stencilBits,
- isMultisampled,
- width, height);
-}
-
GrRenderTarget* GrGpu::createRenderTargetFrom3DApiState() {
this->handleDirtyContext();
return this->onCreateRenderTargetFrom3DApiState();
@@ -467,17 +456,16 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
// resolve in/out status.
GrPathRenderer* pr = NULL;
- GrPath::Iter pathIter;
+ const GrPath* clipPath = NULL;
if (kRect_ClipType == clip.getElementType(c)) {
canRenderDirectToStencil = true;
fill = kEvenOdd_PathFill;
} else {
fill = clip.getPathFill(c);
- const GrPath& path = clip.getPath(c);
- pathIter.reset(path);
- pr = this->getClipPathRenderer(&pathIter, NonInvertedFill(fill));
+ clipPath = &clip.getPath(c);
+ pr = this->getClipPathRenderer(*clipPath, NonInvertedFill(fill));
canRenderDirectToStencil =
- !pr->requiresStencilPass(this, &pathIter,
+ !pr->requiresStencilPass(this, *clipPath,
NonInvertedFill(fill));
}
@@ -513,12 +501,10 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
} else {
if (canRenderDirectToStencil) {
this->setStencil(gDrawToStencil);
- pr->drawPath(this, 0,
- &pathIter,
- NonInvertedFill(fill),
+ pr->drawPath(this, 0, *clipPath, NonInvertedFill(fill),
NULL);
} else {
- pr->drawPathToStencil(this, &pathIter,
+ pr->drawPathToStencil(this, *clipPath,
NonInvertedFill(fill),
NULL);
}
@@ -537,7 +523,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
} else {
SET_RANDOM_COLOR
GrAssert(!IsFillInverted(fill));
- pr->drawPath(this, 0, &pathIter, fill, NULL);
+ pr->drawPath(this, 0, *clipPath, fill, NULL);
}
} else {
SET_RANDOM_COLOR
@@ -561,7 +547,7 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
return true;
}
-GrPathRenderer* GrGpu::getClipPathRenderer(GrPathIter* path,
+GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
GrPathFill fill) {
if (NULL != fClientPathRenderer &&
fClientPathRenderer->canDrawPath(this, path, fill)) {
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index e8c7afb0ed..ff2d406e3d 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -16,6 +16,7 @@
#include "GrGpuGL.h"
#include "GrMemory.h"
+#include "GrTypes.h"
static const GrGLuint GR_MAX_GLUINT = ~0;
static const GrGLint GR_INVAL_GLINT = ~0;
@@ -41,9 +42,15 @@ static const GrGLenum gXfermodeCoeff2Blend[] = {
GR_GL_ONE_MINUS_CONSTANT_COLOR,
GR_GL_CONSTANT_ALPHA,
GR_GL_ONE_MINUS_CONSTANT_ALPHA,
+
+ // extended blend coeffs
+ GR_GL_SRC1_COLOR,
+ GR_GL_ONE_MINUS_SRC1_COLOR,
+ GR_GL_SRC1_ALPHA,
+ GR_GL_ONE_MINUS_SRC1_ALPHA,
};
-bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
+bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
static const bool gCoeffReferencesBlendConst[] = {
false,
false,
@@ -59,28 +66,40 @@ bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
true,
true,
true,
+
+ // extended blend coeffs
+ false,
+ false,
+ false,
+ false,
};
return gCoeffReferencesBlendConst[coeff];
- GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+ GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+
+ GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
+ GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
+ GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
+ GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
+ GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
+ GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
+ GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
+ GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
+ GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
+ GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
+ GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
+ GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
+ GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
+ GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
+
+ GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
+ GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
+ GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
+ GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
+
+ // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
+ GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
}
-GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
-GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
-GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
-GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
-GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
-GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
-GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
-GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
-GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
-GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
-GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
-GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
-GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
-GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
-
-GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
-
///////////////////////////////////////////////////////////////////////////////
void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
@@ -201,6 +220,16 @@ GrGpuGL::GrGpuGL() {
GR_GL_GetIntegerv(GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
GrAssert(maxTextureUnits > kNumStages);
}
+ if (GR_GL_SUPPORT_ES2) {
+ GR_GL_GetIntegerv(GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+ &fMaxFragmentUniformVectors);
+ } else if (GR_GL_SUPPORT_DESKTOP) {
+ GrGLint max;
+ GR_GL_GetIntegerv(GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
+ fMaxFragmentUniformVectors = max / 4;
+ } else {
+ fMaxFragmentUniformVectors = 16;
+ }
////////////////////////////////////////////////////////////////////////////
// Check for supported features.
@@ -604,32 +633,6 @@ GrResource* GrGpuGL::onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc)
}
}
-GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget(
- intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width,
- int height) {
- GrGLRenderTarget::GLRenderTargetIDs rtIDs;
- rtIDs.fStencilRenderbufferID = 0;
- rtIDs.fMSColorRenderbufferID = 0;
- rtIDs.fTexFBOID = 0;
- rtIDs.fOwnIDs = false;
- GrGLIRect viewport;
-
- // viewport is in GL coords (top >= bottom)
- viewport.fLeft = 0;
- viewport.fBottom = 0;
- viewport.fWidth = width;
- viewport.fHeight = height;
-
- rtIDs.fRTFBOID = (GrGLuint)platformRenderTarget;
- rtIDs.fTexFBOID = (GrGLuint)platformRenderTarget;
-
- return new GrGLRenderTarget(this, rtIDs, NULL, stencilBits,
- isMultisampled, viewport, NULL);
-}
-
GrRenderTarget* GrGpuGL::onCreateRenderTargetFrom3DApiState() {
GrGLRenderTarget::GLRenderTargetIDs rtIDs;
@@ -1667,7 +1670,9 @@ void GrGpuGL::flushAAState(GrPrimitiveType type) {
}
}
-void GrGpuGL::flushBlend(GrPrimitiveType type) {
+void GrGpuGL::flushBlend(GrPrimitiveType type,
+ GrBlendCoeff srcCoeff,
+ GrBlendCoeff dstCoeff) {
if (GrIsPrimTypeLines(type) && useSmoothLines()) {
if (fHWBlendDisabled) {
GR_GL(Enable(GR_GL_BLEND));
@@ -1691,15 +1696,15 @@ void GrGpuGL::flushBlend(GrPrimitiveType type) {
fHWBlendDisabled = blendOff;
}
if (!blendOff) {
- if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend ||
- fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) {
- GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend],
- gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend]));
- fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend;
- fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend;
+ if (fHWDrawState.fSrcBlend != srcCoeff ||
+ fHWDrawState.fDstBlend != dstCoeff) {
+ GR_GL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+ gXfermodeCoeff2Blend[dstCoeff]));
+ fHWDrawState.fSrcBlend = srcCoeff;
+ fHWDrawState.fDstBlend = dstCoeff;
}
- if ((BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
- BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) &&
+ if ((BlendCoeffReferencesConstant(srcCoeff) ||
+ BlendCoeffReferencesConstant(dstCoeff)) &&
fHWDrawState.fBlendConstant != fCurrDrawState.fBlendConstant) {
float c[] = {
@@ -1802,7 +1807,6 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
}
this->flushRenderTarget(rect);
this->flushAAState(type);
- this->flushBlend(type);
if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
(fHWDrawState.fFlagBits & kDither_StateBit)) {
@@ -2064,3 +2068,9 @@ void GrGpuGL::setBuffers(bool indexed,
}
}
}
+
+int GrGpuGL::getMaxEdges() const {
+ // FIXME: This is a pessimistic estimate based on how many other things
+ // want to add uniforms. This should be centralized somewhere.
+ return GR_CT_MIN(fMaxFragmentUniformVectors - 8, kMaxEdges);
+}
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index da955cfc56..696b72f290 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -81,11 +81,6 @@ protected:
virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
bool dynamic);
virtual GrResource* onCreatePlatformSurface(const GrPlatformSurfaceDesc& desc);
- virtual GrRenderTarget* onCreatePlatformRenderTarget(
- intptr_t platformRenderTarget,
- int stencilBits,
- bool isMultisampled,
- int width, int height);
virtual GrRenderTarget* onCreateRenderTargetFrom3DApiState();
virtual void onClear(const GrIRect* rect, GrColor color);
@@ -107,6 +102,7 @@ protected:
virtual void flushScissor(const GrIRect* rect);
void clearStencil(uint32_t value, uint32_t mask);
virtual void clearStencilClip(const GrIRect& rect);
+ virtual int getMaxEdges() const;
// binds texture unit in GL
void setTextureUnit(int unitIdx);
@@ -120,13 +116,17 @@ protected:
// flushes state that is common to fixed and programmable GL
// dither
// line smoothing
- // blend func
// texture binding
// sampler state (filtering, tiling)
// FBO binding
// line width
bool flushGLStateCommon(GrPrimitiveType type);
+ // subclass should call this to flush the blend state
+ void flushBlend(GrPrimitiveType type,
+ GrBlendCoeff srcCoeff,
+ GrBlendCoeff dstCoeff);
+
// adjusts texture matrix to account for orientation, size, and npotness
static void AdjustTextureMatrix(const GrGLTexture* texture,
GrSamplerState::SampleMode mode,
@@ -138,7 +138,7 @@ protected:
static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
const GrSamplerState& sampler);
- static bool BlendCoefReferencesConstant(GrBlendCoeff coeff);
+ static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
private:
@@ -160,7 +160,6 @@ private:
void flushRenderTarget(const GrIRect* bound);
void flushStencil();
void flushAAState(GrPrimitiveType type);
- void flushBlend(GrPrimitiveType type);
void resolveRenderTarget(GrGLRenderTarget* texture);
@@ -189,6 +188,9 @@ private:
// Do we have stencil wrap ops.
bool fHasStencilWrap;
+ // The maximum number of fragment uniform vectors (GLES has min. 16).
+ int fMaxFragmentUniformVectors;
+
// ES requires an extension to support RGBA8 in RenderBufferStorage
bool fRGBA8Renderbuffer;
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index e197a82533..65229dc0a1 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -57,6 +57,7 @@ static const GrGLenum gMatrixMode2Enum[] = {
GrGpuGLFixed::GrGpuGLFixed() {
f4X4DownsampleFilterSupport = false;
+ fDualSourceBlendingSupport = false;
}
GrGpuGLFixed::~GrGpuGLFixed() {
@@ -136,8 +137,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {
}
if (GR_GL_SUPPORT_ES1) {
- if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
- BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) {
+ if (BlendCoeffReferencesConstant(fCurrDrawState.fSrcBlend) ||
+ BlendCoeffReferencesConstant(fCurrDrawState.fDstBlend)) {
unimpl("ES1 doesn't support blend constant");
return false;
}
@@ -147,6 +148,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {
return false;
}
+ this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend);
+
if (fDirtyFlags.fRenderTargetChanged) {
flushProjectionMatrix();
}
diff --git a/gpu/src/GrGpuGLFixed.h b/gpu/src/GrGpuGLFixed.h
index 077b6e2a38..487c09f087 100644
--- a/gpu/src/GrGpuGLFixed.h
+++ b/gpu/src/GrGpuGLFixed.h
@@ -41,7 +41,7 @@ private:
const GrMatrix& getHWSamplerMatrix(int stage) const {
return fHWDrawState.fSamplerStates[stage].getMatrix();
}
- const void recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
+ void recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
fHWDrawState.fSamplerStates[stage].setMatrix(matrix);
}
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 8965b06520..50be67f0be 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -15,7 +15,6 @@
*/
#include "GrBinHashKey.h"
-#include "GrGLEffect.h"
#include "GrGLProgram.h"
#include "GrGpuGLShaders.h"
#include "GrGpuVertex.h"
@@ -169,6 +168,14 @@ void GrGpuGLShaders::ProgramUnitTest() {
GrRandom random;
for (int t = 0; t < NUM_TESTS; ++t) {
+#if 0
+ GrPrintf("\nTest Program %d\n-------------\n", t);
+ static const int stop = -1;
+ if (t == stop) {
+ int breakpointhere = 9;
+ }
+#endif
+
pdesc.fVertexLayout = 0;
pdesc.fEmitsPointSize = random.nextF() > .5f;
float colorType = random.nextF();
@@ -179,6 +186,24 @@ void GrGpuGLShaders::ProgramUnitTest() {
} else {
pdesc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
}
+
+ int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt));
+ pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx;
+
+ idx = (int)(random.nextF() * (kNumStages+1));
+ pdesc.fFirstCoverageStage = idx;
+
+ pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));
+
+ if (fDualSourceBlendingSupport) {
+ pdesc.fDualSrcOutput =
+ (GrGLProgram::ProgramDesc::DualSrcOutput)
+ (int)(random.nextF() * GrGLProgram::ProgramDesc::kDualSrcOutputCnt);
+ } else {
+ pdesc.fDualSrcOutput =
+ GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
+ }
+
for (int s = 0; s < kNumStages; ++s) {
// enable the stage?
if (random.nextF() > .5f) {
@@ -194,19 +219,15 @@ void GrGpuGLShaders::ProgramUnitTest() {
if (random.nextF() > .5f) {
pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
}
- }
-
- for (int s = 0; s < kNumStages; ++s) {
- int x;
- pdesc.fStages[s].fEnabled = VertexUsesStage(s, pdesc.fVertexLayout);
- x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
- pdesc.fStages[s].fOptFlags = STAGE_OPTS[x];
- x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
- pdesc.fStages[s].fModulation = STAGE_MODULATES[x];
- x = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
- pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[x];
- x = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
- pdesc.fStages[s].fFetchMode = FETCH_MODES[x];
+ idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
+ pdesc.fStages[s].fOptFlags = STAGE_OPTS[idx];
+ idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
+ pdesc.fStages[s].fModulation = STAGE_MODULATES[idx];
+ idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
+ pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[idx];
+ idx = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
+ pdesc.fStages[s].fFetchMode = FETCH_MODES[idx];
+ pdesc.fStages[s].setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
}
GrGLProgram::CachedData cachedData;
program.genProgram(&cachedData);
@@ -219,11 +240,20 @@ void GrGpuGLShaders::ProgramUnitTest() {
}
}
-
GrGpuGLShaders::GrGpuGLShaders() {
resetContext();
+ int major, minor;
+ gl_version(&major, &minor);
+
f4X4DownsampleFilterSupport = true;
+ if (GR_GL_SUPPORT_DESKTOP) {
+ fDualSourceBlendingSupport =
+ major > 3 ||(3 == major && 3 <= minor) ||
+ has_gl_extension("GL_ARB_blend_func_extended");
+ } else {
+ fDualSourceBlendingSupport = false;
+ }
fProgramData = NULL;
fProgramCache = new ProgramCache();
@@ -283,16 +313,16 @@ void GrGpuGLShaders::flushViewMatrix() {
// ES doesn't allow you to pass true to the transpose param,
// so do our own transpose
- GrScalar mt[] = {
- m[GrMatrix::kMScaleX],
- m[GrMatrix::kMSkewY],
- m[GrMatrix::kMPersp0],
- m[GrMatrix::kMSkewX],
- m[GrMatrix::kMScaleY],
- m[GrMatrix::kMPersp1],
- m[GrMatrix::kMTransX],
- m[GrMatrix::kMTransY],
- m[GrMatrix::kMPersp2]
+ GrGLfloat mt[] = {
+ GrScalarToFloat(m[GrMatrix::kMScaleX]),
+ GrScalarToFloat(m[GrMatrix::kMSkewY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp0]),
+ GrScalarToFloat(m[GrMatrix::kMSkewX]),
+ GrScalarToFloat(m[GrMatrix::kMScaleY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp1]),
+ GrScalarToFloat(m[GrMatrix::kMTransX]),
+ GrScalarToFloat(m[GrMatrix::kMTransY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp2])
};
if (GrGLProgram::kSetAsAttribute ==
@@ -309,8 +339,48 @@ void GrGpuGLShaders::flushViewMatrix() {
}
}
+void GrGpuGLShaders::flushTextureDomain(int s) {
+ const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni;
+ if (GrGLProgram::kUnusedUniform != uni) {
+ const GrRect &texDom =
+ fCurrDrawState.fSamplerStates[s].getTextureDomain();
+
+ if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
+ fProgramData->fTextureDomain[s] != texDom) {
+
+ fProgramData->fTextureDomain[s] = texDom;
+
+ float values[4] = {
+ GrScalarToFloat(texDom.left()),
+ GrScalarToFloat(texDom.top()),
+ GrScalarToFloat(texDom.right()),
+ GrScalarToFloat(texDom.bottom())
+ };
+
+ GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
+ GrGLTexture::Orientation orientation = texture->orientation();
+
+ // vertical flip if necessary
+ if (GrGLTexture::kBottomUp_Orientation == orientation) {
+ values[1] = 1.0f - values[1];
+ values[3] = 1.0f - values[3];
+ // The top and bottom were just flipped, so correct the ordering
+ // of elements so that values = (l, t, r, b).
+ SkTSwap(values[1], values[3]);
+ }
+
+ values[0] *= SkScalarToFloat(texture->contentScaleX());
+ values[2] *= SkScalarToFloat(texture->contentScaleX());
+ values[1] *= SkScalarToFloat(texture->contentScaleY());
+ values[3] *= SkScalarToFloat(texture->contentScaleY());
+
+ GR_GL(Uniform4fv(uni, 1, values));
+ }
+ }
+}
+
void GrGpuGLShaders::flushTextureMatrix(int s) {
- const int& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
+ const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
if (NULL != texture) {
if (GrGLProgram::kUnusedUniform != uni &&
@@ -328,17 +398,18 @@ void GrGpuGLShaders::flushTextureMatrix(int s) {
// ES doesn't allow you to pass true to the transpose param,
// so do our own transpose
- GrScalar mt[] = {
- m[GrMatrix::kMScaleX],
- m[GrMatrix::kMSkewY],
- m[GrMatrix::kMPersp0],
- m[GrMatrix::kMSkewX],
- m[GrMatrix::kMScaleY],
- m[GrMatrix::kMPersp1],
- m[GrMatrix::kMTransX],
- m[GrMatrix::kMTransY],
- m[GrMatrix::kMPersp2]
+ GrGLfloat mt[] = {
+ GrScalarToFloat(m[GrMatrix::kMScaleX]),
+ GrScalarToFloat(m[GrMatrix::kMSkewY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp0]),
+ GrScalarToFloat(m[GrMatrix::kMSkewX]),
+ GrScalarToFloat(m[GrMatrix::kMScaleY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp1]),
+ GrScalarToFloat(m[GrMatrix::kMTransX]),
+ GrScalarToFloat(m[GrMatrix::kMTransY]),
+ GrScalarToFloat(m[GrMatrix::kMPersp2])
};
+
if (GrGLProgram::kSetAsAttribute ==
fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) {
int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s);
@@ -399,16 +470,17 @@ void GrGpuGLShaders::flushTexelSize(int s) {
void GrGpuGLShaders::flushEdgeAAData() {
const int& uni = fProgramData->fUniLocations.fEdgesUni;
if (GrGLProgram::kUnusedUniform != uni) {
- float edges[18];
- memcpy(edges, fCurrDrawState.fEdgeAAEdges, sizeof(edges));
+ int count = fCurrDrawState.fEdgeAANumEdges;
+ Edge edges[kMaxEdges];
// Flip the edges in Y
float height = fCurrDrawState.fRenderTarget->height();
- for (int i = 0; i < 6; ++i) {
- float b = edges[i * 3 + 1];
- edges[i * 3 + 1] = -b;
- edges[i * 3 + 2] += b * height;
+ for (int i = 0; i < count; ++i) {
+ edges[i] = fCurrDrawState.fEdgeAAEdges[i];
+ float b = edges[i].fY;
+ edges[i].fY = -b;
+ edges[i].fZ += b * height;
}
- GR_GL(Uniform3fv(uni, 6, edges));
+ GR_GL(Uniform3fv(uni, count, &edges[0].fX));
}
}
@@ -489,10 +561,11 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
GR_GL(UseProgram(fProgramData->fProgramID));
fHWProgramID = fProgramData->fProgramID;
}
-
- if (!fCurrentProgram.doGLSetup(type, fProgramData)) {
- return false;
- }
+ GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend;
+ GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend;
+
+ fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+ this->flushBlend(type, srcCoeff, dstCoeff);
this->flushColor();
@@ -515,6 +588,8 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
this->flushRadial2(s);
this->flushTexelSize(s);
+
+ this->flushTextureDomain(s);
}
this->flushEdgeAAData();
resetDirtyFlags();
@@ -522,7 +597,6 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
}
void GrGpuGLShaders::postDraw() {
- fCurrentProgram.doGLPost();
}
void GrGpuGLShaders::setupGeometry(int* startVertex,
@@ -634,6 +708,8 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
// existing program in the cache.
desc.fVertexLayout &= ~(kColor_VertexLayoutBit);
+ desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
+
#if GR_AGGRESSIVE_SHADER_OPTS
if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
@@ -649,24 +725,26 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
desc.fColorType = GrGLProgram::ProgramDesc::kAttribute_ColorType;
}
- desc.fUsesEdgeAA = fCurrDrawState.fFlagBits & kEdgeAA_StateBit;
+ desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
+
+ int lastEnabledStage = -1;
for (int s = 0; s < kNumStages; ++s) {
GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s];
- stage.fEnabled = this->isStageEnabled(s);
+ stage.fOptFlags = 0;
+ stage.setEnabled(this->isStageEnabled(s));
- if (stage.fEnabled) {
+ if (stage.isEnabled()) {
+ lastEnabledStage = s;
GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
GrAssert(NULL != texture);
// we matrix to invert when orientation is TopDown, so make sure
// we aren't in that case before flagging as identity.
if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) {
- stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
+ stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
} else if (!getSamplerMatrix(s).hasPerspective()) {
- stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
- } else {
- stage.fOptFlags = 0;
+ stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
}
switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
case GrSamplerState::kNormal_SampleMode:
@@ -701,27 +779,62 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
break;
}
+ if (fCurrDrawState.fSamplerStates[s].hasTextureDomain()) {
+ GrAssert(GrSamplerState::kClamp_WrapMode ==
+ fCurrDrawState.fSamplerStates[s].getWrapX() &&
+ GrSamplerState::kClamp_WrapMode ==
+ fCurrDrawState.fSamplerStates[s].getWrapY());
+ stage.fOptFlags |=
+ GrGLProgram::ProgramDesc::StageDesc::
+ kCustomTextureDomain_OptFlagBit;
+ }
+
if (GrPixelConfigIsAlphaOnly(texture->config())) {
stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation;
} else {
stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation;
}
-
- if (fCurrDrawState.fEffects[s]) {
- fCurrentProgram.fStageEffects[s] = GrGLEffect::Create(fCurrDrawState.fEffects[s]);
- } else {
- delete fCurrentProgram.fStageEffects[s];
- fCurrentProgram.fStageEffects[s] = NULL;
- }
} else {
stage.fOptFlags = 0;
stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0;
stage.fModulation = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
- fCurrentProgram.fStageEffects[s] = NULL;
}
}
- desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
-}
-
+ desc.fDualSrcOutput = GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
+ // use canonical value when coverage/color distinction won't affect
+ // generated code to prevent duplicate programs.
+ desc.fFirstCoverageStage = kNumStages;
+ if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) {
+ // color filter is applied between color/coverage computation
+ if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
+ desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+ }
+ // We could consider cases where the final color is solid (0xff alpha)
+ // and the dst coeff can correctly be set to a non-dualsrc gl value.
+ // (e.g. solid draw, and dst coeff is kZero. It's correct to make
+ // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
+ // kOne).
+ if (fDualSourceBlendingSupport) {
+ if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
+ // write the coverage value to second color
+ desc.fDualSrcOutput =
+ GrGLProgram::ProgramDesc::kCoverage_DualSrcOutput;
+ desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+ } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
+ // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+ // cover
+ desc.fDualSrcOutput =
+ GrGLProgram::ProgramDesc::kCoverageISA_DualSrcOutput;
+ desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+ } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
+ // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+ // cover
+ desc.fDualSrcOutput =
+ GrGLProgram::ProgramDesc::kCoverageISC_DualSrcOutput;
+ desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+ }
+ }
+ }
+}
diff --git a/gpu/src/GrGpuGLShaders.h b/gpu/src/GrGpuGLShaders.h
index bb3024faf9..9392d1c371 100644
--- a/gpu/src/GrGpuGLShaders.h
+++ b/gpu/src/GrGpuGLShaders.h
@@ -51,6 +51,9 @@ private:
// sets the texture matrix uniform for currently bound program
void flushTextureMatrix(int stage);
+ // sets the texture domain uniform for currently bound program
+ void flushTextureDomain(int stage);
+
// sets the color specified by GrDrawTarget::setColor()
void flushColor();
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
deleted file mode 100644
index aa89d37382..0000000000
--- a/gpu/src/GrPath.cpp
+++ /dev/null
@@ -1,523 +0,0 @@
-#include "GrPath.h"
-
-GrPath::GrPath() {
- fConvexHint = kNone_ConvexHint;
- fConservativeBounds.setLargestInverted();
-}
-
-GrPath::GrPath(const GrPath& src) : INHERITED() {
- GrPath::Iter iter(src);
- this->resetFromIter(&iter);
-}
-
-GrPath::GrPath(GrPathIter& iter) {
- this->resetFromIter(&iter);
-}
-
-GrPath::~GrPath() {
-}
-
-bool GrPath::operator ==(const GrPath& path) const {
- if (fCmds.count() != path.fCmds.count() ||
- fPts.count() != path.fPts.count()) {
- return false;
- }
-
- for (int v = 0; v < fCmds.count(); ++v) {
- if (fCmds[v] != path.fCmds[v]) {
- return false;
- }
- }
-
- for (int p = 0; p < fPts.count(); ++p) {
- if (fPts[p] != path.fPts[p]) {
- return false;
- }
- }
- return true;
-}
-
-void GrPath::ensureMoveTo() {
- if (fCmds.isEmpty() || this->wasLastVerb(kClose_PathCmd)) {
- *fCmds.append() = kMove_PathCmd;
- fPts.append()->set(0, 0);
- fConservativeBounds.growToInclude(0,0);
- }
-}
-
-void GrPath::moveTo(GrScalar x, GrScalar y) {
- if (this->wasLastVerb(kMove_PathCmd)) {
- // overwrite prev kMove value
- fPts[fPts.count() - 1].set(x, y);
- } else {
- *fCmds.append() = kMove_PathCmd;
- fPts.append()->set(x, y);
- }
- fConservativeBounds.growToInclude(x,y);
-}
-
-void GrPath::lineTo(GrScalar x, GrScalar y) {
- this->ensureMoveTo();
- *fCmds.append() = kLine_PathCmd;
- fPts.append()->set(x, y);
- fConservativeBounds.growToInclude(x,y);
-}
-
-void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) {
- this->ensureMoveTo();
- *fCmds.append() = kQuadratic_PathCmd;
- fPts.append()->set(x0, y0);
- fPts.append()->set(x1, y1);
- fConservativeBounds.growToInclude(x0,y0);
- fConservativeBounds.growToInclude(x1,y1);
-}
-
-void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
- GrScalar x2, GrScalar y2) {
- this->ensureMoveTo();
- *fCmds.append() = kCubic_PathCmd;
- fPts.append()->set(x0, y0);
- fPts.append()->set(x1, y1);
- fPts.append()->set(x2, y2);
- fConservativeBounds.growToInclude(x0,y0);
- fConservativeBounds.growToInclude(x1,y1);
- fConservativeBounds.growToInclude(x2,y2);
-}
-
-void GrPath::close() {
- if (!fCmds.isEmpty() && !this->wasLastVerb(kClose_PathCmd)) {
- // should we allow kMove followed by kClose?
- *fCmds.append() = kClose_PathCmd;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void GrPath::offset(GrScalar tx, GrScalar ty) {
- if (!tx && !ty) {
- return; // nothing to do
- }
-
- GrPoint* iter = fPts.begin();
- GrPoint* stop = fPts.end();
- while (iter < stop) {
- iter->offset(tx, ty);
- ++iter;
- }
- fConservativeBounds.offset(tx, ty);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static bool check_two_vecs(const GrVec& prevVec,
- const GrVec& currVec,
- GrScalar turnDir,
- int* xDir,
- int* yDir,
- int* flipX,
- int* flipY) {
- if (currVec.fX * *xDir < 0) {
- ++*flipX;
- if (*flipX > 2) {
- return false;
- }
- *xDir = -*xDir;
- }
- if (currVec.fY * *yDir < 0) {
- ++*flipY;
- if (*flipY > 2) {
- return false;
- }
- *yDir = -*yDir;
- }
- GrScalar d = prevVec.cross(currVec);
- return (d * turnDir) >= 0;
-}
-
-static void init_from_two_vecs(const GrVec& firstVec,
- const GrVec& secondVec,
- GrScalar* turnDir,
- int* xDir, int* yDir) {
- *turnDir = firstVec.cross(secondVec);
- if (firstVec.fX > 0) {
- *xDir = 1;
- } else if (firstVec.fX < 0) {
- *xDir = -1;
- } else {
- *xDir = 0;
- }
- if (firstVec.fY > 0) {
- *yDir = 1;
- } else if (firstVec.fY < 0) {
- *yDir = -1;
- } else {
- *yDir = 0;
- }
-}
-
-void GrPath::resetFromIter(GrPathIter* iter) {
- fPts.reset();
- fCmds.reset();
- fConservativeBounds.setLargestInverted();
-
- fConvexHint = iter->convexHint();
-
- // first point of the subpath
- GrPoint firstPt = { 0, 0 };
- // first edge of the subpath
- GrVec firstVec = { 0, 0 };
- // vec of most recently processed edge, that wasn't degenerate
- GrVec previousVec = { 0, 0 };
- // most recently processed point
- GrPoint previousPt = { 0, 0 };
-
- // sign indicates whether we're bending left or right
- GrScalar turnDir = 0;
- // number of times the direction has flipped in x or y
-
- // we track which direction we are moving in x/y and the
- // number of times it changes.
- int xDir = 0;
- int yDir = 0;
- int flipX = 0;
- int flipY = 0;
-
- // counts number of sub path pts that didn't add a degenerate edge.
- int subPathPts = 0;
- bool subPathClosed = false;
-
- int numSubPaths = 0;
- iter->rewind();
- GrPathCmd cmd;
- GrPoint pts[4];
- do {
- cmd = iter->next(pts);
- // If the convexity test is ever updated to handle multiple subpaths
- // the loop has to be adjusted to handle moving to a new subpath without
- // closing the previous one. Currently the implicit closing vectors for a
- // filled path would never be examined.
- switch (cmd) {
- case kMove_PathCmd:
- this->moveTo(pts[0].fX, pts[0].fY);
- subPathPts = 0;
- subPathClosed = false;
- break;
- case kLine_PathCmd:
- this->lineTo(pts[1].fX, pts[1].fY);
- break;
- case kQuadratic_PathCmd:
- this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
- break;
- case kCubic_PathCmd:
- this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
- pts[3].fX, pts[3].fY);
- break;
- case kClose_PathCmd:
- this->close();
- subPathClosed = true;
- break;
- case kEnd_PathCmd:
- break;
- }
- int n = NumPathCmdPoints(cmd);
- for (int i = 0; i < n; ++i) {
- fConservativeBounds.growToInclude(pts[i].fX, pts[i].fY);
- }
- if (0 == subPathPts && n > 0) {
- previousPt = pts[0];
- firstPt = previousPt;
- flipX = 0;
- flipY = 0;
- turnDir = 0;
- subPathPts = 1;
- ++numSubPaths;
- }
- // either we skip the first pt because it is redundant with
- // last point of the previous subpath cmd or we just ate it
- // in the above if.
- int consumed = 1;
- if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) {
- while (consumed < n) {
- GrAssert(pts[consumed-1] == previousPt);
- GrVec vec = pts[consumed] - previousPt;
-// vec.setBetween(previousPt, pts[consumed]);
- if (vec.fX || vec.fY) {
- if (subPathPts >= 2) {
- if (0 == turnDir) {
- firstVec = previousVec;
- init_from_two_vecs(firstVec, vec,
- &turnDir, &xDir, &yDir);
- // here we aren't checking whether the x/y dirs
- // change between the first and second edge. It
- // gets covered when the path is closed.
- } else {
- if (!check_two_vecs(previousVec, vec, turnDir,
- &xDir, &yDir,
- &flipX, &flipY)) {
- fConvexHint = kConcave_ConvexHint;
- break;
- }
- }
- }
- previousVec = vec;
- previousPt = pts[consumed];
- ++subPathPts;
- }
- ++consumed;
- }
- if (subPathPts > 2 && (kClose_PathCmd == cmd ||
- (!subPathClosed && kEnd_PathCmd == cmd ))) {
- // if an additional vector is needed to close the loop check
- // that it validates against the previous vector.
- GrVec vec = firstPt - previousPt;
-// vec.setBetween(previousPt, firstPt);
- if (vec.fX || vec.fY) {
- if (!check_two_vecs(previousVec, vec, turnDir,
- &xDir, &yDir, &flipX, &flipY)) {
- fConvexHint = kConcave_ConvexHint;
- break;
- }
- previousVec = vec;
- }
- // check that closing vector validates against the first vector.
- if (!check_two_vecs(previousVec, firstVec, turnDir,
- &xDir, &yDir, &flipX, &flipY)) {
- fConvexHint = kConcave_ConvexHint;
- break;
- }
- }
- }
- } while (cmd != kEnd_PathCmd);
- if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) {
- fConvexHint = kConvex_ConvexHint;
- } else {
- bool recurse = false;
- if (recurse) {
- this->resetFromIter(iter);
- }
- }
-}
-
-void GrPath::ConvexUnitTest() {
- GrPath testPath;
- GrPath::Iter testIter;
-
- GrPath pt;
- pt.moveTo(0, 0);
- pt.close();
-
- testIter.reset(pt);
- testPath.resetFromIter(&testIter);
- GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
- GrPath line;
- line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
- line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
- line.close();
-
- testIter.reset(line);
- testPath.resetFromIter(&testIter);
- GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
- GrPath triLeft;
- triLeft.moveTo(0, 0);
- triLeft.lineTo(1, 0);
- triLeft.lineTo(1, 1);
- triLeft.close();
-
- testIter.reset(triLeft);
- testPath.resetFromIter(&testIter);
- GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
- GrPath triRight;
- triRight.moveTo(0, 0);
- triRight.lineTo(-1, 0);
- triRight.lineTo(1, 1);
- triRight.close();
-
- testIter.reset(triRight);
- testPath.resetFromIter(&testIter);
- GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
- GrPath square;
- square.moveTo(0, 0);
- square.lineTo(1, 0);
- square.lineTo(1, 1);
- square.lineTo(0, 1);
- square.close();
-
- testIter.reset(square);
- testPath.resetFromIter(&testIter);
- GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
- GrPath redundantSquare;
- square.moveTo(0, 0);
- square.lineTo(0, 0);
- square.lineTo(0, 0);
- square.lineTo(1, 0);
- square.lineTo(1, 0);
- square.lineTo(1, 0);
- square.lineTo(1, 1);
- square.lineTo(1, 1);
- square.lineTo(1, 1);
- square.lineTo(0, 1);
- square.lineTo(0, 1);
- square.lineTo(0, 1);
- square.close();
-
- testIter.reset(redundantSquare);
- testPath.resetFromIter(&testIter);
- GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-
- GrPath bowTie;
- bowTie.moveTo(0, 0);
- bowTie.lineTo(0, 0);
- bowTie.lineTo(0, 0);
- bowTie.lineTo(1, 1);
- bowTie.lineTo(1, 1);
- bowTie.lineTo(1, 1);
- bowTie.lineTo(1, 0);
- bowTie.lineTo(1, 0);
- bowTie.lineTo(1, 0);
- bowTie.lineTo(0, 1);
- bowTie.lineTo(0, 1);
- bowTie.lineTo(0, 1);
- bowTie.close();
-
- testIter.reset(bowTie);
- testPath.resetFromIter(&testIter);
- GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-
- GrPath spiral;
- spiral.moveTo(0, 0);
- spiral.lineTo(1, 0);
- spiral.lineTo(1, 1);
- spiral.lineTo(0, 1);
- spiral.lineTo(0,.5);
- spiral.lineTo(.5,.5);
- spiral.lineTo(.5,.75);
- spiral.close();
-
- testIter.reset(spiral);
- testPath.resetFromIter(&testIter);
- GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-
- GrPath dent;
- dent.moveTo(0, 0);
- dent.lineTo(1, 1);
- dent.lineTo(0, 1);
- dent.lineTo(-.5,2);
- dent.lineTo(-2, 1);
- dent.close();
-
- testIter.reset(dent);
- testPath.resetFromIter(&testIter);
- GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-}
-///////////////////////////////////////////////////////////////////////////////
-
-GrPath::Iter::Iter() : fPath(NULL) {
-}
-
-GrPath::Iter::Iter(const GrPath& path) : fPath(&path) {
- this->rewind();
-}
-
-#ifdef SK_DEBUG
-static bool containsInclusive(const GrRect& rect, const GrPoint& point) {
- return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
- point.fY >= rect.fTop && point.fY <= rect.fBottom;
-}
-#endif
-
-GrPathCmd GrPath::Iter::next(GrPoint points[]) {
- if (fCmdIndex == fPath->fCmds.count()) {
- GrAssert(fPtIndex == fPath->fPts.count());
- return kEnd_PathCmd;
- } else {
- GrAssert(fCmdIndex < fPath->fCmds.count());
- }
-
- GrPathCmd cmd = fPath->fCmds[fCmdIndex++];
- const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex;
-
- switch (cmd) {
- case kMove_PathCmd:
- if (points) {
- points[0] = srcPts[0];
- }
- fLastPt = srcPts[0];
- GrAssert(fPtIndex <= fPath->fPts.count() + 1);
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
- fPtIndex += 1;
- break;
- case kLine_PathCmd:
- if (points) {
- points[0] = fLastPt;
- points[1] = srcPts[0];
- }
- fLastPt = srcPts[0];
- GrAssert(fPtIndex <= fPath->fPts.count() + 1);
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
- fPtIndex += 1;
- break;
- case kQuadratic_PathCmd:
- if (points) {
- points[0] = fLastPt;
- points[1] = srcPts[0];
- points[2] = srcPts[1];
- }
- fLastPt = srcPts[1];
- GrAssert(fPtIndex <= fPath->fPts.count() + 2);
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[1]));
- fPtIndex += 2;
- break;
- case kCubic_PathCmd:
- if (points) {
- points[0] = fLastPt;
- points[1] = srcPts[0];
- points[2] = srcPts[1];
- points[3] = srcPts[2];
- }
- fLastPt = srcPts[2];
- GrAssert(fPtIndex <= fPath->fPts.count() + 3);
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[0]));
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[1]));
- GrAssert(containsInclusive(fPath->getConservativeBounds(), srcPts[2]));
- fPtIndex += 3;
- break;
- case kClose_PathCmd:
- break;
- default:
- GrAssert(!"unknown grpath cmd");
- break;
- }
- return cmd;
-}
-
-GrConvexHint GrPath::Iter::convexHint() const {
- return fPath->getConvexHint();
-}
-
-GrPathCmd GrPath::Iter::next() {
- return this->next(NULL);
-}
-
-void GrPath::Iter::rewind() {
- this->reset(*fPath);
-}
-
-void GrPath::Iter::reset(const GrPath& path) {
- fPath = &path;
- fCmdIndex = fPtIndex = 0;
-}
-
-bool GrPath::Iter::getConservativeBounds(GrRect* rect) const {
- if (!fPath->getConservativeBounds().isEmpty()) {
- *rect = fPath->getConservativeBounds();
- return true;
- }
- return false;
-}
-
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 317b7d3571..58bb12e01d 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -2,7 +2,6 @@
#include "GrPoint.h"
#include "GrDrawTarget.h"
-#include "GrPathIter.h"
#include "GrPathUtils.h"
#include "GrMemory.h"
#include "GrTexture.h"
@@ -147,20 +146,24 @@ static const GrStencilSettings gDirectToStencil = {
////////////////////////////////////////////////////////////////////////////////
// Helpers for drawPath
+static GrConvexHint getConvexHint(const SkPath& path) {
+ return path.isConvex() ? kConvex_ConvexHint : kConcave_ConvexHint;
+}
+
#define STENCIL_OFF 0 // Always disable stencil (even when needed)
static inline bool single_pass_path(const GrDrawTarget& target,
- const GrPathIter& path,
+ const GrPath& path,
GrPathFill fill) {
#if STENCIL_OFF
return true;
#else
if (kEvenOdd_PathFill == fill) {
- GrConvexHint hint = path.convexHint();
+ GrConvexHint hint = getConvexHint(path);
return hint == kConvex_ConvexHint ||
hint == kNonOverlappingConvexPieces_ConvexHint;
} else if (kWinding_PathFill == fill) {
- GrConvexHint hint = path.convexHint();
+ GrConvexHint hint = getConvexHint(path);
return hint == kConvex_ConvexHint ||
hint == kNonOverlappingConvexPieces_ConvexHint ||
(hint == kSameWindingConvexPieces_ConvexHint &&
@@ -172,14 +175,14 @@ static inline bool single_pass_path(const GrDrawTarget& target,
}
bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill) const {
- return !single_pass_path(*target, *path, fill);
+ return !single_pass_path(*target, path, fill);
}
void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate,
bool stencilOnly) {
@@ -200,13 +203,10 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
// TODO: deal with perspective in some better way.
tol /= 10;
} else {
- GrScalar sinv = GR_Scalar1 / stretch;
- tol = GrMul(tol, sinv);
+ tol = GrScalarDiv(tol, stretch);
}
GrScalar tolSqd = GrMul(tol, tol);
- path->rewind();
-
int subpathCnt;
int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
@@ -226,8 +226,6 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
- path->rewind();
-
// TODO: use primitve restart if available rather than multiple draws
GrPrimitiveType type;
int passCount = 0;
@@ -248,7 +246,7 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
drawFace[0] = GrDrawTarget::kBoth_DrawFace;
} else {
type = kTriangleFan_PrimitiveType;
- if (single_pass_path(*target, *path, fill)) {
+ if (single_pass_path(*target, path, fill)) {
passCount = 1;
if (stencilOnly) {
passes[0] = &gDirectToStencil;
@@ -329,8 +327,10 @@ void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
bool first = true;
int subpath = 0;
+ SkPath::Iter iter(path, false);
+
for (;;) {
- GrPathCmd cmd = path->next(pts);
+ GrPathCmd cmd = (GrPathCmd)iter.next(pts);
switch (cmd) {
case kMove_PathCmd:
if (!first) {
@@ -431,14 +431,14 @@ FINISHED:
void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate) {
this->onDrawPath(target, stages, path, fill, translate, false);
}
void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate) {
GrAssert(kInverseEvenOdd_PathFill != fill);
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 115b0f6f2f..8a72ba87a7 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -15,8 +15,6 @@
*/
#include "GrPathUtils.h"
-
-#include "GrPathIter.h"
#include "GrPoint.h"
const GrScalar GrPathUtils::gTolerance = GR_Scalar1;
@@ -33,8 +31,8 @@ uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
// subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
// points.
// 2^(log4(x)) = sqrt(x);
- d = ceilf(sqrtf(d/tol));
- return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
+ int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
+ return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE);
}
}
@@ -67,12 +65,12 @@ uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
GrScalar tol) {
GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
- d = sqrtf(d);
+ d = SkScalarSqrt(d);
if (d < tol) {
return 1;
} else {
- d = ceilf(sqrtf(d/tol));
- return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
+ int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
+ return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE);
}
}
@@ -106,18 +104,18 @@ uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0,
return a + b;
}
-int GrPathUtils::worstCasePointCount(GrPathIter* path,
- int* subpaths,
- GrScalar tol) {
+int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
+ GrScalar tol) {
int pointCount = 0;
*subpaths = 1;
bool first = true;
+ SkPath::Iter iter(path, true);
GrPathCmd cmd;
GrPoint pts[4];
- while ((cmd = path->next(pts)) != kEnd_PathCmd) {
+ while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
switch (cmd) {
case kLine_PathCmd:
diff --git a/gpu/src/GrPathUtils.h b/gpu/src/GrPathUtils.h
index af05682a4d..2cd00cbb84 100644
--- a/gpu/src/GrPathUtils.h
+++ b/gpu/src/GrPathUtils.h
@@ -19,15 +19,14 @@
#include "GrNoncopyable.h"
#include "GrPoint.h"
-
-class GrPathIter;
+#include "GrPath.h"
/**
* Utilities for evaluating paths.
*/
class GrPathUtils : public GrNoncopyable {
public:
- static int worstCasePointCount(GrPathIter* path,
+ static int worstCasePointCount(const GrPath&,
int* subpaths,
GrScalar tol);
static uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp
index a537e169fd..edf83fe94c 100644
--- a/gpu/src/GrStencil.cpp
+++ b/gpu/src/GrStencil.cpp
@@ -16,7 +16,14 @@
#include "GrStencil.h"
-const GrStencilSettings GrStencilSettings::gDisabled = {};
+const GrStencilSettings GrStencilSettings::gDisabled = {
+ kKeep_StencilOp, kKeep_StencilOp,
+ kKeep_StencilOp, kKeep_StencilOp,
+ kAlways_StencilFunc, kAlways_StencilFunc,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0
+};
GR_STATIC_ASSERT(0 == kKeep_StencilOp);
GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 3993adb5d9..0e3389c25d 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -18,8 +18,10 @@
#include "GrMemory.h"
#include "GrPathUtils.h"
+#include "GrPoint.h"
+#include "GrTDArray.h"
-#include <internal_glu.h>
+#include <sk_glu.h>
struct PolygonData {
PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices)
@@ -83,24 +85,12 @@ static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
}
-class Edge {
- public:
- Edge() {}
- Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
- GrPoint intersect(const Edge& other) {
- return GrPoint::Make(
- (fY * other.fZ - other.fY * fZ) / (fX * other.fY - other.fX * fY),
- (fX * other.fZ - other.fX * fZ) / (other.fX * fY - fX * other.fY));
- }
- float fX, fY, fZ;
-};
-
-typedef GrTDArray<Edge> EdgeArray;
+typedef GrTDArray<GrDrawTarget::Edge> EdgeArray;
-bool isCCW(const GrPoint* v)
+bool isCCW(const GrPoint* pts)
{
- GrVec v1 = v[1] - v[0];
- GrVec v2 = v[2] - v[1];
+ GrVec v1 = pts[1] - pts[0];
+ GrVec v2 = pts[2] - pts[1];
return v1.cross(v2) < 0;
}
@@ -110,25 +100,24 @@ static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
size_t numVertices,
EdgeArray* edges)
{
+ matrix.mapPoints(vertices, numVertices);
GrPoint p = vertices[numVertices - 1];
- matrix.mapPoints(&p, 1);
float sign = isCCW(vertices) ? -1.0f : 1.0f;
for (size_t i = 0; i < numVertices; ++i) {
GrPoint q = vertices[i];
- matrix.mapPoints(&q, 1);
if (p == q) continue;
GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
float scale = sign / tangent.length();
float cross2 = p.fX * q.fY - q.fX * p.fY;
- Edge edge(tangent.fX * scale,
+ GrDrawTarget::Edge edge(tangent.fX * scale,
tangent.fY * scale,
cross2 * scale + 0.5f);
*edges->append() = edge;
p = q;
}
- Edge prev_edge = *edges->back();
- for (size_t i = 0; i < edges->count(); ++i) {
- Edge edge = edges->at(i);
+ GrDrawTarget::Edge prev_edge = *edges->back();
+ for (int i = 0; i < edges->count(); ++i) {
+ GrDrawTarget::Edge edge = edges->at(i);
vertices[i] = prev_edge.intersect(edge);
inverse.mapPoints(&vertices[i], 1);
prev_edge = edge;
@@ -138,11 +127,10 @@ static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
GrDrawTarget::StageBitfield stages,
- GrPathIter* path,
+ const GrPath& path,
GrPathFill fill,
const GrPoint* translate) {
GrDrawTarget::AutoStateRestore asr(target);
- bool colorWritesWereDisabled = target->isColorWriteDisabled();
// face culling doesn't make sense here
GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
@@ -157,13 +145,10 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
// TODO: deal with perspective in some better way.
tol /= 10;
} else {
- GrScalar sinv = GR_Scalar1 / stretch;
- tol = GrMul(tol, sinv);
+ tol = GrScalarDiv(tol, stretch);
}
GrScalar tolSqd = GrMul(tol, tol);
- path->rewind();
-
int subpathCnt;
int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
@@ -185,16 +170,14 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
- path->rewind();
-
GrPoint pts[4];
+ SkPath::Iter iter(path, true);
bool first = true;
int subpath = 0;
for (;;) {
- GrPathCmd cmd = path->next(pts);
- switch (cmd) {
+ switch (iter.next(pts)) {
case kMove_PathCmd:
if (!first) {
subpathVertCount[subpath] = vert-subpathBase;
@@ -263,31 +246,32 @@ FINISHED:
return;
}
- if (subpathCnt == 1 && !inverted && path->convexHint() == kConvex_ConvexHint) {
+ if (subpathCnt == 1 && !inverted && path.isConvex()) {
if (target->isAntialiasState()) {
- target->enableState(GrDrawTarget::kEdgeAA_StateBit);
EdgeArray edges;
GrMatrix inverse, matrix = target->getViewMatrix();
target->getViewInverse(&inverse);
count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
- GrPoint triangle[3];
- triangle[0] = base[0];
- Edge triangleEdges[6];
- triangleEdges[0] = *edges.back();
- triangleEdges[1] = edges[0];
- for (size_t i = 1; i < count - 1; i++) {
- triangle[1] = base[i];
- triangle[2] = base[i + 1];
- triangleEdges[2] = edges[i - 1];
- triangleEdges[3] = edges[i];
- triangleEdges[4] = edges[i];
- triangleEdges[5] = edges[i + 1];
- target->setVertexSourceToArray(layout, triangle, 3);
- target->setEdgeAAData(&triangleEdges[0].fX);
- target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
+ size_t maxEdges = target->getMaxEdges();
+ if (count <= maxEdges) {
+ // All edges fit; upload all edges and draw all verts as a fan
+ target->setVertexSourceToArray(layout, base, count);
+ target->setEdgeAAData(&edges[0], count);
+ target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
+ } else {
+ // Upload "maxEdges" edges and verts at a time, and draw as
+ // separate fans
+ for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
+ edges[i] = edges[0];
+ base[i] = base[0];
+ int size = GR_CT_MIN(count - i, maxEdges);
+ target->setVertexSourceToArray(layout, &base[i], size);
+ target->setEdgeAAData(&edges[i], size);
+ target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
+ }
}
- target->disableState(GrDrawTarget::kEdgeAA_StateBit);
+ target->setEdgeAAData(NULL, 0);
} else {
target->setVertexSourceToArray(layout, base, count);
target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
@@ -305,34 +289,34 @@ FINISHED:
inVertices[i * 3 + 2] = 1.0;
}
- GLUtesselator* tess = internal_gluNewTess();
+ GLUtesselator* tess = Sk_gluNewTess();
unsigned windingRule = fill_type_to_glu_winding_rule(fill);
- internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
- internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
- internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
- internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
- internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
- internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
+ Sk_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
+ Sk_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
+ Sk_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
+ Sk_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
+ Sk_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
+ Sk_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
GrTDArray<short> indices;
GrTDArray<GrPoint> vertices;
PolygonData data(&vertices, &indices);
- internal_gluTessBeginPolygon(tess, &data);
+ Sk_gluTessBeginPolygon(tess, &data);
size_t i = 0;
for (int sp = 0; sp < subpathCnt; ++sp) {
- internal_gluTessBeginContour(tess);
+ Sk_gluTessBeginContour(tess);
int start = i;
- int end = start + subpathVertCount[sp];
+ size_t end = start + subpathVertCount[sp];
for (; i < end; ++i) {
double* inVertex = &inVertices[i * 3];
*vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
- internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
+ Sk_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
}
- internal_gluTessEndContour(tess);
+ Sk_gluTessEndContour(tess);
}
- internal_gluTessEndPolygon(tess);
- internal_gluDeleteTess(tess);
+ Sk_gluTessEndPolygon(tess);
+ Sk_gluDeleteTess(tess);
if (indices.count() > 0) {
target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
@@ -348,25 +332,25 @@ FINISHED:
}
bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill) const {
return kHairLine_PathFill != fill;
}
void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill,
const GrPoint* translate) {
GrAlwaysAssert(!"multipass stencil should not be needed");
}
bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
- GrPathIter* path,
+ const SkPath& path,
GrPathFill fill) {
int subpathCnt = 0;
int tol = GrPathUtils::gTolerance;
GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
return (subpathCnt == 1 &&
!IsFillInverted(fill) &&
- path->convexHint() == kConvex_ConvexHint);
+ path.isConvex());
}
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
index 0222042b37..2aacfa25ad 100644
--- a/gpu/src/GrTextContext.cpp
+++ b/gpu/src/GrTextContext.cpp
@@ -25,11 +25,9 @@
#include "GrGpuVertex.h"
#include "GrDrawTarget.h"
-static const int TEXT_STAGE = 1;
-
-static const GrVertexLayout BASE_VLAYOUT =
- GrDrawTarget::kTextFormat_VertexLayoutBit |
- GrDrawTarget::StageTexCoordVertexLayoutBit(TEXT_STAGE,0);
+enum {
+ kGlyphMaskStage = GrPaint::kTotalStages,
+};
void GrTextContext::flushGlyphs() {
if (fCurrVertex > 0) {
@@ -45,17 +43,17 @@ void GrTextContext::flushGlyphs() {
GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
GrSamplerState::kRepeat_WrapMode,
filter);
- fDrawTarget->setSamplerState(TEXT_STAGE, sampler);
+ fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
GrAssert(GrIsALIGN4(fCurrVertex));
int nIndices = fCurrVertex + (fCurrVertex >> 1);
GrAssert(fCurrTexture);
- fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture);
+ fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
- NULL != fPaint.getTexture()) {
+ fPaint.hasTexture()) {
GrPrintf("LCD Text will not draw correctly.\n");
}
// setup blend so that we get mask * paintColor + (1-mask)*dstColor
@@ -117,18 +115,31 @@ GrTextContext::GrTextContext(GrContext* context,
fOrigViewMatrix = fContext->getMatrix();
fContext->setMatrix(fExtMatrix);
- fVertexLayout = BASE_VLAYOUT;
- if (NULL != paint.getTexture()) {
- fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+ fDrawTarget = fContext->getTextTarget(fPaint);
+
+ fVertices = NULL;
+ fMaxVertices = 0;
+
+ fVertexLayout =
+ GrDrawTarget::kTextFormat_VertexLayoutBit |
+ GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
+
+ int stageMask = paint.getActiveStageMask();
+ if (stageMask) {
GrMatrix inverseViewMatrix;
if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
- fPaint.fSampler.preConcatMatrix(inverseViewMatrix);
+ fDrawTarget->preConcatSamplerMatrices(stageMask,
+ inverseViewMatrix);
+ }
+ for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+ if ((1 << i) & stageMask) {
+ fVertexLayout |=
+ GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+ GrAssert(i != kGlyphMaskStage);
+ }
}
}
- fVertices = NULL;
- fMaxVertices = 0;
- fDrawTarget = fContext->getTextTarget(fPaint);
}
GrTextContext::~GrTextContext() {
@@ -201,11 +212,11 @@ void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
}
glyph->fPath = path;
}
- GrPath::Iter iter(*glyph->fPath);
+
GrPoint translate;
translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
- fContext->drawPath(fPaint, &iter, kWinding_PathFill,
+ fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
&translate);
return;
}
diff --git a/gpu/src/gr_files.mk b/gpu/src/gr_files.mk
index fef97844c8..bd6f06148e 100644
--- a/gpu/src/gr_files.mk
+++ b/gpu/src/gr_files.mk
@@ -19,10 +19,10 @@ SOURCE := \
GrInOrderDrawBuffer.cpp \
GrMatrix.cpp \
GrMemory.cpp \
- GrPath.cpp \
GrPathUtils.cpp \
GrRectanizer_fifo.cpp \
GrResource.cpp \
+ GrTesselatedPathRenderer.cpp \
GrTexture.cpp \
GrTextureCache.cpp \
GrTextContext.cpp \
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
index 320dd15bd7..338f2cc35e 100644
--- a/gpu/src/gr_unittests.cpp
+++ b/gpu/src/gr_unittests.cpp
@@ -68,7 +68,7 @@ static void test_bsearch() {
for (size_t n = 0; n < GR_ARRAY_COUNT(array); n++) {
for (size_t i = 0; i < n; i++) {
int index = GrTBSearch<int, int>(array, n, array[i]);
- GrAssert(index == i);
+ GrAssert(index == (int) i);
index = GrTBSearch<int, int>(array, n, -array[i]);
GrAssert(index < 0);
}
@@ -80,10 +80,10 @@ class BogusEntry {};
static void test_binHashKey()
{
- const char* testStringA = "abcdA";
- const char* testStringB = "abcdB";
+ const char* testStringA = "abcdABCD";
+ const char* testStringB = "abcdBBCD";
enum {
- kDataLenUsedForKey = 5
+ kDataLenUsedForKey = 8
};
typedef GrBinHashKey<BogusEntry, kDataLenUsedForKey> KeyType;
@@ -92,7 +92,7 @@ static void test_binHashKey()
int passCnt = 0;
while (keyA.doPass()) {
++passCnt;
- keyA.keyData(reinterpret_cast<const uint8_t*>(testStringA), kDataLenUsedForKey);
+ keyA.keyData(reinterpret_cast<const uint32_t*>(testStringA), kDataLenUsedForKey);
}
GrAssert(passCnt == 1); //We expect the static allocation to suffice
GrBinHashKey<BogusEntry, kDataLenUsedForKey-1> keyBust;
@@ -100,7 +100,7 @@ static void test_binHashKey()
while (keyBust.doPass()) {
++passCnt;
// Exceed static storage by 1
- keyBust.keyData(reinterpret_cast<const uint8_t*>(testStringA), kDataLenUsedForKey);
+ keyBust.keyData(reinterpret_cast<const uint32_t*>(testStringA), kDataLenUsedForKey);
}
GrAssert(passCnt == 2); //We expect dynamic allocation to be necessary
GrAssert(keyA.getHash() == keyBust.getHash());
@@ -109,14 +109,14 @@ static void test_binHashKey()
// the same hash as with one chunk
KeyType keyA2;
while (keyA2.doPass()) {
- keyA2.keyData(reinterpret_cast<const uint8_t*>(testStringA), 2);
- keyA2.keyData(&reinterpret_cast<const uint8_t*>(testStringA)[2], kDataLenUsedForKey-2);
+ keyA2.keyData(reinterpret_cast<const uint32_t*>(testStringA), 4);
+ keyA2.keyData(&reinterpret_cast<const uint32_t*>(testStringA)[4], kDataLenUsedForKey-4);
}
GrAssert(keyA.getHash() == keyA2.getHash());
KeyType keyB;
while (keyB.doPass()){
- keyB.keyData(reinterpret_cast<const uint8_t*>(testStringB), kDataLenUsedForKey);
+ keyB.keyData(reinterpret_cast<const uint32_t*>(testStringB), kDataLenUsedForKey);
}
GrAssert(keyA.compare(keyB) < 0);
GrAssert(keyA.compare(keyA2) == 0);
@@ -148,13 +148,130 @@ static void test_binHashKey()
GrAssert(keyBust3.compare(keyBust2) == 0);
}
+static void test_convex() {
+#if 0
+ GrPath testPath;
+ GrPath::Iter testIter;
+
+ GrPath pt;
+ pt.moveTo(0, 0);
+ pt.close();
+
+ testIter.reset(pt);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath line;
+ line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
+ line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
+ line.close();
+
+ testIter.reset(line);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath triLeft;
+ triLeft.moveTo(0, 0);
+ triLeft.lineTo(1, 0);
+ triLeft.lineTo(1, 1);
+ triLeft.close();
+
+ testIter.reset(triLeft);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath triRight;
+ triRight.moveTo(0, 0);
+ triRight.lineTo(-1, 0);
+ triRight.lineTo(1, 1);
+ triRight.close();
+
+ testIter.reset(triRight);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath square;
+ square.moveTo(0, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 1);
+ square.lineTo(0, 1);
+ square.close();
+
+ testIter.reset(square);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath redundantSquare;
+ square.moveTo(0, 0);
+ square.lineTo(0, 0);
+ square.lineTo(0, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 0);
+ square.lineTo(1, 1);
+ square.lineTo(1, 1);
+ square.lineTo(1, 1);
+ square.lineTo(0, 1);
+ square.lineTo(0, 1);
+ square.lineTo(0, 1);
+ square.close();
+
+ testIter.reset(redundantSquare);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+ GrPath bowTie;
+ bowTie.moveTo(0, 0);
+ bowTie.lineTo(0, 0);
+ bowTie.lineTo(0, 0);
+ bowTie.lineTo(1, 1);
+ bowTie.lineTo(1, 1);
+ bowTie.lineTo(1, 1);
+ bowTie.lineTo(1, 0);
+ bowTie.lineTo(1, 0);
+ bowTie.lineTo(1, 0);
+ bowTie.lineTo(0, 1);
+ bowTie.lineTo(0, 1);
+ bowTie.lineTo(0, 1);
+ bowTie.close();
+
+ testIter.reset(bowTie);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+
+ GrPath spiral;
+ spiral.moveTo(0, 0);
+ spiral.lineTo(1, 0);
+ spiral.lineTo(1, 1);
+ spiral.lineTo(0, 1);
+ spiral.lineTo(0,.5);
+ spiral.lineTo(.5,.5);
+ spiral.lineTo(.5,.75);
+ spiral.close();
+
+ testIter.reset(spiral);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+
+ GrPath dent;
+ dent.moveTo(0, 0);
+ dent.lineTo(1, 1);
+ dent.lineTo(0, 1);
+ dent.lineTo(-.5,2);
+ dent.lineTo(-2, 1);
+ dent.close();
+
+ testIter.reset(dent);
+ testPath.resetFromIter(&testIter);
+ GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+#endif
+}
+
void gr_run_unittests() {
test_tdarray();
test_bsearch();
test_binHashKey();
+ test_convex();
GrRedBlackTree<int>::UnitTest();
- GrPath::ConvexUnitTest();
GrDrawTarget::VertexLayoutUnitTest();
}
-
-
diff --git a/gpu/src/mac/GrGLDefaultInterface_mac.cpp b/gpu/src/mac/GrGLDefaultInterface_mac.cpp
index b9396faace..fb5b182609 100644
--- a/gpu/src/mac/GrGLDefaultInterface_mac.cpp
+++ b/gpu/src/mac/GrGLDefaultInterface_mac.cpp
@@ -155,6 +155,8 @@ void GrGLSetDefaultGLInterface() {
gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT;
#endif
#endif
+ gDefaultInterface.fBindFragDataLocationIndexed = NULL;
+
gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
gDefaultInterfaceInit = true;
diff --git a/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
new file mode 100644
index 0000000000..0350c30dbf
--- /dev/null
+++ b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
@@ -0,0 +1,184 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include "GrGLInterface.h"
+
+#include "GL/osmesa.h"
+#include <GL/glext.h>
+#include <GL/glu.h>
+
+#define GR_GL_GET_PROC(F) gDefaultInterface.f ## F = (GrGL ## F ## Proc) \
+ OSMesaGetProcAddress("gl" #F);
+#define GR_GL_GET_PROC_SUFFIX(F, S) gDefaultInterface.f ## F = (GrGL ## F ## Proc) \
+ OSMesaGetProcAddress("gl" #F #S);
+
+void GrGLSetDefaultGLInterface() {
+ static GrGLInterface gDefaultInterface;
+ static bool gDefaultInterfaceInit;
+ if (!gDefaultInterfaceInit && NULL != OSMesaGetCurrentContext()) {
+ int major, minor;
+ const char* versionString = (const char*) glGetString(GL_VERSION);
+ const char* extString = (const char*) glGetString(GL_EXTENSIONS);
+ gl_version_from_string(&major, &minor, versionString);
+
+ if (major == 1 && minor < 5) {
+ // We must have array and element_array buffer objects.
+ return;
+ }
+
+ gDefaultInterface.fActiveTexture = glActiveTexture;
+ GR_GL_GET_PROC(AttachShader);
+ GR_GL_GET_PROC(BindAttribLocation);
+ GR_GL_GET_PROC(BindBuffer);
+ gDefaultInterface.fBindTexture = glBindTexture;
+ gDefaultInterface.fBlendColor = glBlendColor;
+ gDefaultInterface.fBlendFunc = glBlendFunc;
+ GR_GL_GET_PROC(BufferData);
+ GR_GL_GET_PROC(BufferSubData);
+ gDefaultInterface.fClear = glClear;
+ gDefaultInterface.fClearColor = glClearColor;
+ gDefaultInterface.fClearStencil = glClearStencil;
+ gDefaultInterface.fClientActiveTexture = glClientActiveTexture;
+ gDefaultInterface.fColorMask = glColorMask;
+ gDefaultInterface.fColorPointer = glColorPointer;
+ gDefaultInterface.fColor4ub = glColor4ub;
+ GR_GL_GET_PROC(CompileShader);
+ gDefaultInterface.fCompressedTexImage2D = glCompressedTexImage2D;
+ GR_GL_GET_PROC(CreateProgram);
+ GR_GL_GET_PROC(CreateShader);
+ gDefaultInterface.fCullFace = glCullFace;
+ GR_GL_GET_PROC(DeleteBuffers);
+ GR_GL_GET_PROC(DeleteProgram);
+ GR_GL_GET_PROC(DeleteShader);
+ gDefaultInterface.fDeleteTextures = glDeleteTextures;
+ gDefaultInterface.fDepthMask = glDepthMask;
+ gDefaultInterface.fDisable = glDisable;
+ gDefaultInterface.fDisableClientState = glDisableClientState;
+ GR_GL_GET_PROC(DisableVertexAttribArray);
+ gDefaultInterface.fDrawArrays = glDrawArrays;
+ gDefaultInterface.fDrawElements = glDrawElements;
+ gDefaultInterface.fEnable = glEnable;
+ gDefaultInterface.fEnableClientState = glEnableClientState;
+ GR_GL_GET_PROC(EnableVertexAttribArray);
+ gDefaultInterface.fFrontFace = glFrontFace;
+ GR_GL_GET_PROC(GenBuffers);
+ GR_GL_GET_PROC(GetBufferParameteriv);
+ gDefaultInterface.fGetError = glGetError;
+ gDefaultInterface.fGetIntegerv = glGetIntegerv;
+ GR_GL_GET_PROC(GetProgramInfoLog);
+ GR_GL_GET_PROC(GetProgramiv);
+ GR_GL_GET_PROC(GetShaderInfoLog);
+ GR_GL_GET_PROC(GetShaderiv);
+ gDefaultInterface.fGetString = glGetString;
+ gDefaultInterface.fGenTextures = glGenTextures;
+ GR_GL_GET_PROC(GetUniformLocation);
+ gDefaultInterface.fLineWidth = glLineWidth;
+ GR_GL_GET_PROC(LinkProgram);
+ gDefaultInterface.fLoadMatrixf = glLoadMatrixf;
+ GR_GL_GET_PROC(MapBuffer);
+ gDefaultInterface.fMatrixMode = glMatrixMode;
+ gDefaultInterface.fPointSize = glPointSize;
+ gDefaultInterface.fPixelStorei = glPixelStorei;
+ gDefaultInterface.fReadPixels = glReadPixels;
+ gDefaultInterface.fScissor = glScissor;
+ gDefaultInterface.fShadeModel = glShadeModel;
+ GR_GL_GET_PROC(ShaderSource);
+ gDefaultInterface.fStencilFunc = glStencilFunc;
+ GR_GL_GET_PROC(StencilFuncSeparate);
+ gDefaultInterface.fStencilMask = glStencilMask;
+ GR_GL_GET_PROC(StencilMaskSeparate);
+ gDefaultInterface.fStencilOp = glStencilOp;
+ GR_GL_GET_PROC(StencilOpSeparate);
+ gDefaultInterface.fTexCoordPointer = glTexCoordPointer;
+ gDefaultInterface.fTexEnvi = glTexEnvi;
+ //OSMesa on Mac's glTexImage2D takes a GLenum for internalFormat rather than a GLint.
+ gDefaultInterface.fTexImage2D = reinterpret_cast<GrGLTexImage2DProc>(glTexImage2D);
+ gDefaultInterface.fTexParameteri = glTexParameteri;
+ gDefaultInterface.fTexSubImage2D = glTexSubImage2D;
+ GR_GL_GET_PROC(Uniform1f);
+ GR_GL_GET_PROC(Uniform1i);
+ GR_GL_GET_PROC(Uniform1fv);
+ GR_GL_GET_PROC(Uniform1iv);
+ GR_GL_GET_PROC(Uniform2f);
+ GR_GL_GET_PROC(Uniform2i);
+ GR_GL_GET_PROC(Uniform2fv);
+ GR_GL_GET_PROC(Uniform2iv);
+ GR_GL_GET_PROC(Uniform3f);
+ GR_GL_GET_PROC(Uniform3i);
+ GR_GL_GET_PROC(Uniform3fv);
+ GR_GL_GET_PROC(Uniform3iv);
+ GR_GL_GET_PROC(Uniform4f);
+ GR_GL_GET_PROC(Uniform4i);
+ GR_GL_GET_PROC(Uniform4fv);
+ GR_GL_GET_PROC(Uniform4iv);
+ GR_GL_GET_PROC(UniformMatrix2fv);
+ GR_GL_GET_PROC(UniformMatrix3fv);
+ GR_GL_GET_PROC(UniformMatrix4fv);
+ GR_GL_GET_PROC(UnmapBuffer);
+ GR_GL_GET_PROC(UseProgram);
+ GR_GL_GET_PROC(VertexAttrib4fv);
+ GR_GL_GET_PROC(VertexAttribPointer);
+ gDefaultInterface.fVertexPointer = glVertexPointer;
+ gDefaultInterface.fViewport = glViewport;
+
+ // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
+ // GL_ARB_framebuffer_object doesn't use ARB suffix.)
+ if (major >= 3 || has_gl_extension_from_string(
+ "GL_ARB_framebuffer_object", extString)) {
+ GR_GL_GET_PROC(GenFramebuffers);
+ GR_GL_GET_PROC(BindFramebuffer);
+ GR_GL_GET_PROC(FramebufferTexture2D);
+ GR_GL_GET_PROC(CheckFramebufferStatus);
+ GR_GL_GET_PROC(DeleteFramebuffers);
+ GR_GL_GET_PROC(RenderbufferStorage);
+ GR_GL_GET_PROC(GenRenderbuffers);
+ GR_GL_GET_PROC(DeleteRenderbuffers);
+ GR_GL_GET_PROC(FramebufferRenderbuffer);
+ GR_GL_GET_PROC(BindRenderbuffer);
+ GR_GL_GET_PROC(RenderbufferStorageMultisample);
+ GR_GL_GET_PROC(BlitFramebuffer);
+ } else if (has_gl_extension_from_string("GL_EXT_framebuffer_object",
+ extString)) {
+ GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
+ GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
+ GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
+ GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
+ GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
+ GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
+ GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
+ GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
+ GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
+ GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
+ if (has_gl_extension_from_string("GL_EXT_framebuffer_multisample",
+ extString)) {
+ GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
+ }
+ if (has_gl_extension_from_string("GL_EXT_framebuffer_blit",
+ extString)) {
+ GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
+ }
+ } else {
+ // we must have FBOs
+ return;
+ }
+ GR_GL_GET_PROC(BindFragDataLocationIndexed);
+ gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
+
+ gDefaultInterfaceInit = true;
+ }
+ if (gDefaultInterfaceInit)
+ GrGLSetGLInterface(&gDefaultInterface);
+}
diff --git a/gpu/src/unix/GrGLDefaultInterface_unix.cpp b/gpu/src/unix/GrGLDefaultInterface_unix.cpp
index ba670657c8..3e9b9754d6 100644
--- a/gpu/src/unix/GrGLDefaultInterface_unix.cpp
+++ b/gpu/src/unix/GrGLDefaultInterface_unix.cpp
@@ -133,6 +133,7 @@ void GrGLSetDefaultGLInterface() {
GR_GL_GET_PROC(VertexAttribPointer);
gDefaultInterface.fVertexPointer = glVertexPointer;
gDefaultInterface.fViewport = glViewport;
+ GR_GL_GET_PROC(BindFragDataLocationIndexed);
// First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
// GL_ARB_framebuffer_object doesn't use ARB suffix.)
diff --git a/gpu/src/win/GrGLDefaultInterface_win.cpp b/gpu/src/win/GrGLDefaultInterface_win.cpp
index 53fb26a17a..428abb1c2e 100644
--- a/gpu/src/win/GrGLDefaultInterface_win.cpp
+++ b/gpu/src/win/GrGLDefaultInterface_win.cpp
@@ -139,6 +139,7 @@ void GrGLSetDefaultGLInterface() {
GR_GL_GET_PROC(UseProgram);
GR_GL_GET_PROC(VertexAttrib4fv);
GR_GL_GET_PROC(VertexAttribPointer);
+ GR_GL_GET_PROC(BindFragDataLocationIndexed);
// First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
// GL_ARB_framebuffer_object doesn't use ARB suffix.)
diff --git a/gyp/common.gypi b/gyp/common.gypi
deleted file mode 100644
index aadaffb543..0000000000
--- a/gyp/common.gypi
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-{
- 'conditions' : [
- ['OS == "win"',
- {
- 'target_defaults': {
- 'msvs_cygwin_shell': 0,
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'WarningLevel': '1',
- 'WarnAsError': 'false',
- 'DebugInformationFormat': '3',
- 'AdditionalOptions': '/MP',
- },
- },
- 'configurations': {
- 'Debug': {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '0', # 0 = /Od
- 'PreprocessorDefinitions': ['_DEBUG'],
- 'RuntimeLibrary': '3', # 3 = /MDd (debug DLL)
- },
- 'VCLinkerTool': {
- 'GenerateDebugInformation': 'true',
- },
- },
- },
- 'Release': {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '2', # 2 = /Os
- 'PreprocessorDefinitions': ['NDEBUG'],
- 'RuntimeLibrary': '2', # 2 = /MD (nondebug DLL)
- },
- 'VCLinkerTool': {
- 'GenerateDebugInformation': 'false',
- },
- },
- },
- },
- },
- },
- ],
- ['OS == "linux"',
- {
- 'target_defaults': {
- 'configurations': {
- 'Debug': {
- 'cflags': ['-g']
- },
- 'Release': {
- 'cflags': ['-O2']
- },
- },
- },
- },
- ],
- ['OS == "mac"',
- {
- 'target_defaults': {
- 'configurations': {
- 'Debug': {
- 'cflags': ['-g']
- },
- 'Release': {
- 'cflags': ['-O2']
- },
- },
- },
- 'xcode_settings': {
- 'SYMROOT': '<(DEPTH)/xcodebuild',
- },
- },
- ],
- ],
-}
-# Local Variables:
-# tab-width:2
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/gyp_skia b/gyp/gyp_skia
deleted file mode 100755
index 72b4879457..0000000000
--- a/gyp/gyp_skia
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script is a wrapper which invokes gyp with the correct --depth argument,
-# and supports the automatic regeneration of build files if skia.gyp is
-# changed (Linux-only).
-
-import glob
-import os
-import shlex
-import sys
-
-script_dir = os.path.dirname(__file__)
-
-gyp_dir = os.path.normpath(os.path.join(script_dir, os.pardir, 'third_party'))
-
-sys.path.append(os.path.join(gyp_dir, 'gyp', 'pylib'))
-import gyp
-
-def additional_include_files(args=[]):
- # Determine the include files specified on the command line.
- # This doesn't cover all the different option formats you can use,
- # but it's mainly intended to avoid duplicating flags on the automatic
- # makefile regeneration which only uses this format.
- specified_includes = set()
- for arg in args:
- if arg.startswith('-I') and len(arg) > 2:
- specified_includes.add(os.path.realpath(arg[2:]))
-
- result = []
- def AddInclude(path):
- if os.path.realpath(path) not in specified_includes:
- result.append(path)
-
- # Always include common.gypi
- AddInclude(os.path.join(script_dir, 'common.gypi'))
-
- return result
-
-if __name__ == '__main__':
- args = sys.argv[1:]
-
- # This could give false positives since it doesn't actually do real option
- # parsing. Oh well.
- gyp_file_specified = False
- for arg in args:
- if arg.endswith('.gyp'):
- gyp_file_specified = True
- break
-
- # If we didn't get a file, then fall back to assuming 'skia.gyp' from the
- # same directory as the script.
- if not gyp_file_specified:
- args.append(os.path.join(script_dir, 'skia.gyp'))
-
- args.extend(['-I' + i for i in additional_include_files(args)])
-
- args.extend(['--depth'])
- args.extend([os.path.abspath(script_dir)])
- print 'Updating projects from gyp files...'
- sys.stdout.flush()
-
- # Off we go...
- sys.exit(gyp.main(args))
diff --git a/gyp/skia.gyp b/gyp/skia.gyp
deleted file mode 100644
index 7247828bb1..0000000000
--- a/gyp/skia.gyp
+++ /dev/null
@@ -1,1589 +0,0 @@
-{
- 'target_defaults': {
- 'configurations': {
- 'Debug': {
- 'defines': [
- 'SK_DEBUG',
- 'GR_DEBUG=1',
- ],
- },
- 'Release': {
- 'defines': [
- 'SK_RELEASE',
- 'GR_RELEASE=1',
- ],
- },
- },
- 'conditions': [
- [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
- 'include_dirs' : [
- '/usr/include/freetype2',
- ],
- }],
- [ 'OS == "mac"', {
- 'defines': [
- 'SK_BUILD_FOR_MAC',
- ],
- }],
- [ 'OS == "win"', {
- 'defines': [
- 'SK_BUILD_FOR_WIN32',
- 'SK_IGNORE_STDINT_DOT_H',
- ],
- }],
- [ 'OS == "linux"', {
- 'defines': [
- 'SK_SAMPLES_FOR_X',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'conditions': [
- [ 'OS == "mac"', {
- 'defines': [
- 'SK_BUILD_FOR_MAC',
- ],
- }],
- [ 'OS == "win"', {
- 'defines': [
- 'SK_BUILD_FOR_WIN32',
- ],
- }],
- ],
- },
- },
- 'targets': [
- {
- 'target_name': 'skia',
- 'type': 'static_library',
- 'msvs_guid': 'B7760B5E-BFA8-486B-ACFD-49E3A6DE8E76',
- 'sources': [
- '../src/core/ARGB32_Clamp_Bilinear_BitmapShader.h',
- '../src/core/Sk64.cpp',
- '../src/core/SkAdvancedTypefaceMetrics.cpp',
- '../src/core/SkAlphaRuns.cpp',
- '../src/core/SkAntiRun.h',
- '../src/core/SkBitmap.cpp',
- '../src/core/SkBitmapProcShader.cpp',
- '../src/core/SkBitmapProcShader.h',
- '../src/core/SkBitmapProcState.cpp',
- '../src/core/SkBitmapProcState.h',
- '../src/core/SkBitmapProcState_matrix.h',
- '../src/core/SkBitmapProcState_matrixProcs.cpp',
- '../src/core/SkBitmapProcState_sample.h',
- '../src/core/SkBitmapSampler.cpp',
- '../src/core/SkBitmapSampler.h',
- '../src/core/SkBitmapSamplerTemplate.h',
- '../src/core/SkBitmapShader16BilerpTemplate.h',
- '../src/core/SkBitmapShaderTemplate.h',
- '../src/core/SkBitmap_scroll.cpp',
- '../src/core/SkBlitBWMaskTemplate.h',
- '../src/core/SkBlitRow_D16.cpp',
- '../src/core/SkBlitRow_D32.cpp',
- '../src/core/SkBlitRow_D4444.cpp',
- '../src/core/SkBlitter.cpp',
- '../src/core/SkBlitter_4444.cpp',
- '../src/core/SkBlitter_A1.cpp',
- '../src/core/SkBlitter_A8.cpp',
- '../src/core/SkBlitter_ARGB32.cpp',
- '../src/core/SkBlitter_RGB16.cpp',
- '../src/core/SkBlitter_Sprite.cpp',
- '../src/core/SkBuffer.cpp',
- '../src/core/SkCanvas.cpp',
- '../src/core/SkChunkAlloc.cpp',
- '../src/core/SkClampRange.cpp',
- '../src/core/SkClipStack.cpp',
- '../src/core/SkColor.cpp',
- '../src/core/SkColorFilter.cpp',
- '../src/core/SkColorTable.cpp',
- '../src/core/SkComposeShader.cpp',
- '../src/core/SkConcaveToTriangles.cpp',
- '../src/core/SkConcaveToTriangles.h',
- '../src/core/SkCordic.cpp',
- '../src/core/SkCordic.h',
- '../src/core/SkCoreBlitters.h',
- '../src/core/SkCubicClipper.cpp',
- '../src/core/SkCubicClipper.h',
- '../src/core/SkDebug.cpp',
- '../src/core/SkDeque.cpp',
- '../src/core/SkDevice.cpp',
- '../src/core/SkDither.cpp',
- '../src/core/SkDraw.cpp',
- '../src/core/SkDrawProcs.h',
- '../src/core/SkEdgeBuilder.cpp',
- '../src/core/SkEdgeClipper.cpp',
- '../src/core/SkEdge.cpp',
- '../src/core/SkEdge.h',
- '../src/core/SkFP.h',
- '../src/core/SkFilterProc.cpp',
- '../src/core/SkFilterProc.h',
- '../src/core/SkFlattenable.cpp',
- '../src/core/SkFloat.cpp',
- '../src/core/SkFloat.h',
- '../src/core/SkFloatBits.cpp',
- '../src/core/SkGeometry.cpp',
- '../src/core/SkGlobals.cpp',
- '../src/core/SkGlyphCache.cpp',
- '../src/core/SkGlyphCache.h',
- '../src/core/SkGraphics.cpp',
- '../src/core/SkLineClipper.cpp',
- '../src/core/SkMallocPixelRef.cpp',
- '../src/core/SkMask.cpp',
- '../src/core/SkMaskFilter.cpp',
- '../src/core/SkMath.cpp',
- '../src/core/SkMatrix.cpp',
- '../src/core/SkMetaData.cpp',
- '../src/core/SkPackBits.cpp',
- '../src/core/SkPaint.cpp',
- '../src/core/SkPath.cpp',
- '../src/core/SkPathEffect.cpp',
- '../src/core/SkPathHeap.cpp',
- '../src/core/SkPathHeap.h',
- '../src/core/SkPathMeasure.cpp',
- '../src/core/SkPicture.cpp',
- '../src/core/SkPictureFlat.cpp',
- '../src/core/SkPictureFlat.h',
- '../src/core/SkPicturePlayback.cpp',
- '../src/core/SkPicturePlayback.h',
- '../src/core/SkPictureRecord.cpp',
- '../src/core/SkPictureRecord.h',
- '../src/core/SkPixelRef.cpp',
- '../src/core/SkPoint.cpp',
- '../src/core/SkProcSpriteBlitter.cpp',
- '../src/core/SkPtrRecorder.cpp',
- '../src/core/SkQuadClipper.cpp',
- '../src/core/SkQuadClipper.h',
- '../src/core/SkRasterizer.cpp',
- '../src/core/SkRect.cpp',
- '../src/core/SkRefDict.cpp',
- '../src/core/SkRegion.cpp',
- '../src/core/SkRegionPriv.h',
- '../src/core/SkRegion_path.cpp',
- '../src/core/SkScalar.cpp',
- '../src/core/SkScalerContext.cpp',
- '../src/core/SkScan.cpp',
- '../src/core/SkScanPriv.h',
- '../src/core/SkScan_AntiPath.cpp',
- '../src/core/SkScan_Antihair.cpp',
- '../src/core/SkScan_Hairline.cpp',
- '../src/core/SkScan_Path.cpp',
- '../src/core/SkShader.cpp',
- '../src/core/SkShape.cpp',
- '../src/core/SkSpriteBlitter_ARGB32.cpp',
- '../src/core/SkSpriteBlitter_RGB16.cpp',
- '../src/core/SkSinTable.h',
- '../src/core/SkSpriteBlitter.h',
- '../src/core/SkSpriteBlitterTemplate.h',
- '../src/core/SkStream.cpp',
- '../src/core/SkString.cpp',
- '../src/core/SkStroke.cpp',
- '../src/core/SkStrokerPriv.cpp',
- '../src/core/SkStrokerPriv.h',
- '../src/core/SkTextFormatParams.h',
- '../src/core/SkTSearch.cpp',
- '../src/core/SkTSort.h',
- '../src/core/SkTemplatesPriv.h',
- '../src/core/SkTypeface.cpp',
- '../src/core/SkTypefaceCache.cpp',
- '../src/core/SkTypefaceCache.h',
- '../src/core/SkUnPreMultiply.cpp',
- '../src/core/SkUtils.cpp',
- '../src/core/SkWriter32.cpp',
- '../src/core/SkXfermode.cpp',
-
- '../src/opts/opts_check_SSE2.cpp',
-
- '../src/ports/SkDebug_stdio.cpp',
- '../src/ports/SkDebug_win.cpp',
-
- '../src/ports/SkFontHost_tables.cpp',
- '../src/ports/SkGlobals_global.cpp',
- '../src/ports/SkMemory_malloc.cpp',
- '../src/ports/SkOSFile_stdio.cpp',
- '../src/ports/SkTime_Unix.cpp',
- '../src/ports/SkXMLParser_empty.cpp',
- '../src/ports/sk_predefined_gamma.h',
-
- '../include/core/Sk64.h',
- '../include/core/SkAdvancedTypefaceMetrics.h',
- '../include/core/SkAutoKern.h',
- '../include/core/SkBitmap.h',
- '../include/core/SkBlitRow.h',
- '../include/core/SkBlitter.h',
- '../include/core/SkBounder.h',
- '../include/core/SkBuffer.h',
- '../include/core/SkCanvas.h',
- '../include/core/SkChunkAlloc.h',
- '../include/core/SkClampRange.h',
- '../include/core/SkClipStack.h',
- '../include/core/SkColor.h',
- '../include/core/SkColorFilter.h',
- '../include/core/SkColorPriv.h',
- '../include/core/SkColorShader.h',
- '../include/core/SkComposeShader.h',
- '../include/core/SkDeque.h',
- '../include/core/SkDescriptor.h',
- '../include/core/SkDevice.h',
- '../include/core/SkDither.h',
- '../include/core/SkDraw.h',
- '../include/core/SkDrawFilter.h',
- '../include/core/SkDrawLooper.h',
- '../include/core/SkEndian.h',
- '../include/core/SkFDot6.h',
- '../include/core/SkFixed.h',
- '../include/core/SkFlattenable.h',
- '../include/core/SkFloatBits.h',
- '../include/core/SkFloatingPoint.h',
- '../include/core/SkFontHost.h',
- '../include/core/SkGeometry.h',
- '../include/core/SkGlobals.h',
- '../include/core/SkGraphics.h',
- '../include/core/SkMallocPixelRef.h',
- '../include/core/SkMask.h',
- '../include/core/SkMaskFilter.h',
- '../include/core/SkMath.h',
- '../include/core/SkMatrix.h',
- '../include/core/SkMetaData.h',
- '../include/core/SkOSFile.h',
- '../include/core/SkPackBits.h',
- '../include/core/SkPaint.h',
- '../include/core/SkPath.h',
- '../include/core/SkPathEffect.h',
- '../include/core/SkPathMeasure.h',
- '../include/core/SkPerspIter.h',
- '../include/core/SkPicture.h',
- '../include/core/SkPixelRef.h',
- '../include/core/SkPoint.h',
- '../include/core/SkPtrRecorder.h',
- '../include/core/SkRandom.h',
- '../include/core/SkRasterizer.h',
- '../include/core/SkReader32.h',
- '../include/core/SkRect.h',
- '../include/core/SkRefCnt.h',
- '../include/core/SkRefDict.h',
- '../include/core/SkRegion.h',
- '../include/core/SkScalar.h',
- '../include/core/SkScalarCompare.h',
- '../include/core/SkScalerContext.h',
- '../include/core/SkScan.h',
- '../include/core/SkShader.h',
- '../include/core/SkStream.h',
- '../include/core/SkString.h',
- '../include/core/SkStroke.h',
- '../include/core/SkTDArray.h',
- '../include/core/SkTDStack.h',
- '../include/core/SkTDict.h',
- '../include/core/SkTRegistry.h',
- '../include/core/SkTScopedPtr.h',
- '../include/core/SkTSearch.h',
- '../include/core/SkTemplates.h',
- '../include/core/SkThread.h',
- '../include/core/SkThread_platform.h',
- '../include/core/SkTime.h',
- '../include/core/SkTypeface.h',
- '../include/core/SkTypes.h',
- '../include/core/SkUnPreMultiply.h',
- '../include/core/SkUnitMapper.h',
- '../include/core/SkUtils.h',
- '../include/core/SkWriter32.h',
- '../include/core/SkXfermode.h',
- ],
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/ports',
- '../include/xml',
- '../src/core',
- ],
- 'msvs_disabled_warnings': [4244, 4267,4345, 4390, 4554, 4800],
- 'conditions': [
- [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
- 'cflags': [
- '-Wno-unused',
- '-Wno-unused-function',
- ],
- 'sources': [
- '../include/core/SkMMapStream.h',
- '../src/core/SkMMapStream.cpp',
- '../src/core/SkBlitter_ARGB32_Subpixel.cpp',
- '../src/core/SkFontHost.cpp',
- '../src/ports/SkThread_pthread.cpp',
- '../src/ports/SkTime_Unix.cpp',
- '../src/ports/SkFontHost_FreeType_Subpixel.cpp',
- '../src/ports/SkFontHost_FreeType.cpp',
- '../src/ports/SkFontHost_gamma_none.cpp',
- '../src/ports/SkFontHost_linux.cpp',
- ],
- 'link_settings': {
- 'libraries': [
- '-lfreetype',
- '-lpthread',
- ],
- },
- }],
- [ 'OS == "mac"', {
- 'include_dirs': [
- '../include/utils/mac',
- ],
- 'sources': [
- '../include/core/SkMMapStream.h',
- '../include/utils/mac/SkCGUtils.h',
-
- '../src/core/SkMMapStream.cpp',
- '../src/ports/SkFontHost_mac_coretext.cpp',
-
- '../src/ports/SkThread_pthread.cpp',
- '../src/ports/SkTime_Unix.cpp',
-
- '../src/utils/mac/SkCreateCGImageRef.cpp',
- ],
- }],
- [ 'OS == "win"', {
- 'include_dirs': [
- 'config/win',
- ],
- 'sources': [
- '../src/ports/SkFontHost_win.cpp',
- '../src/ports/SkThread_win.cpp',
- ],
- 'sources!': [
- '../src/ports/SkDebug_stdio.cpp',
- ],
- }],
- [ 'OS != "win"', {
- 'sources!': [
- '../src/ports/SkDebug_win.cpp',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- 'config',
- '../include/config',
- '../include/core',
- 'ext',
- ],
- },
- 'dependencies': [
- 'skia_opts'
- ],
- },
-
- # Due to an unfortunate intersection of lameness between gcc and gyp,
- # we have to build the *_SSE2.cpp files in a separate target. The
- # gcc lameness is that, in order to compile SSE2 intrinsics code, it
- # must be passed the -msse2 flag. However, with this flag, it may
- # emit SSE2 instructions even for scalar code, such as the CPUID
- # test used to test for the presence of SSE2. So that, and all other
- # code must be compiled *without* -msse2. The gyp lameness is that it
- # does not allow file-specific CFLAGS, so we must create this extra
- # target for those files to be compiled with -msse2.
- #
- # This is actually only a problem on 32-bit Linux (all Intel Macs have
- # SSE2, Linux x86_64 has SSE2 by definition, and MSC will happily emit
- # SSE2 from instrinsics, while generating plain ol' 386 for everything
- # else). However, to keep the .gyp file simple and avoid platform-specific
- # build breakage, we do this on all platforms.
-
- # For about the same reason, we need to compile the ARM opts files
- # separately as well.
- {
- 'target_name': 'skia_opts',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../src/core',
- ],
- 'conditions': [
- [ '(OS == "linux" or OS == "freebsd" or OS == "openbsd")', {
- 'cflags': [
- '-msse2',
- ],
- }],
- ],
- 'sources': [
- '../src/opts/SkBitmapProcState_opts_SSE2.cpp',
- '../src/opts/SkBlitRow_opts_SSE2.cpp',
- '../src/opts/SkUtils_opts_SSE2.cpp',
- ],
- },
- {
- 'target_name': 'effects',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/effects',
- ],
- 'sources': [
- '../include/effects/Sk1DPathEffect.h',
- '../include/effects/Sk2DPathEffect.h',
- '../include/effects/SkAvoidXfermode.h',
- '../include/effects/SkBlurDrawLooper.h',
- '../include/effects/SkBlurMaskFilter.h',
- '../include/effects/SkColorMatrix.h',
- '../include/effects/SkColorMatrixFilter.h',
- '../include/effects/SkCornerPathEffect.h',
- '../include/effects/SkDashPathEffect.h',
- '../include/effects/SkDiscretePathEffect.h',
- '../include/effects/SkDrawExtraPathEffect.h',
- '../include/effects/SkEmbossMaskFilter.h',
- '../include/effects/SkGradientShader.h',
- '../include/effects/SkGroupShape.h',
- '../include/effects/SkKernel33MaskFilter.h',
- '../include/effects/SkLayerDrawLooper.h',
- '../include/effects/SkLayerRasterizer.h',
- '../include/effects/SkPaintFlagsDrawFilter.h',
- '../include/effects/SkPixelXorXfermode.h',
- '../include/effects/SkPorterDuff.h',
- '../include/effects/SkRectShape.h',
- '../include/effects/SkTableMaskFilter.h',
- '../include/effects/SkTransparentShader.h',
-
- '../src/effects/Sk1DPathEffect.cpp',
- '../src/effects/Sk2DPathEffect.cpp',
- '../src/effects/SkAvoidXfermode.cpp',
- '../src/effects/SkBitmapCache.cpp',
- '../src/effects/SkBitmapCache.h',
- '../src/effects/SkBlurDrawLooper.cpp',
- '../src/effects/SkBlurMask.cpp',
- '../src/effects/SkBlurMask.h',
- '../src/effects/SkBlurMaskFilter.cpp',
- '../src/effects/SkColorFilters.cpp',
- '../src/effects/SkColorMatrixFilter.cpp',
- '../src/effects/SkCornerPathEffect.cpp',
- '../src/effects/SkDashPathEffect.cpp',
- '../src/effects/SkDiscretePathEffect.cpp',
- '../src/effects/SkEmbossMask.cpp',
- '../src/effects/SkEmbossMask.h',
- '../src/effects/SkEmbossMask_Table.h',
- '../src/effects/SkEmbossMaskFilter.cpp',
- '../src/effects/SkGradientShader.cpp',
- '../src/effects/SkGroupShape.cpp',
- '../src/effects/SkKernel33MaskFilter.cpp',
- '../src/effects/SkLayerDrawLooper.cpp',
- '../src/effects/SkLayerRasterizer.cpp',
- '../src/effects/SkPaintFlagsDrawFilter.cpp',
- '../src/effects/SkPixelXorXfermode.cpp',
- '../src/effects/SkPorterDuff.cpp',
- '../src/effects/SkRadialGradient_Table.h',
- '../src/effects/SkRectShape.cpp',
- '../src/effects/SkTableMaskFilter.cpp',
- '../src/effects/SkTransparentShader.cpp',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/effects',
- ],
- },
- },
- {
- 'target_name': 'images',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/images',
- ],
- 'sources': [
- '../include/images/SkFlipPixelRef.h',
- '../include/images/SkImageDecoder.h',
- '../include/images/SkImageEncoder.h',
- '../include/images/SkImageRef.h',
- '../include/images/SkImageRef_GlobalPool.h',
- '../include/images/SkJpegUtility.h',
- '../include/images/SkMovie.h',
- '../include/images/SkPageFlipper.h',
-
- '../src/images/bmpdecoderhelper.cpp',
- '../src/images/bmpdecoderhelper.h',
- '../src/images/SkBitmap_RLEPixels.h',
- '../src/images/SkCreateRLEPixelRef.cpp',
- '../src/images/SkFDStream.cpp',
- '../src/images/SkFlipPixelRef.cpp',
- '../src/images/SkImageDecoder.cpp',
- '../src/images/SkImageDecoder_Factory.cpp',
- '../src/images/SkImageDecoder_libbmp.cpp',
- '../src/images/SkImageDecoder_libgif.cpp',
- '../src/images/SkImageDecoder_libico.cpp',
- '../src/images/SkImageDecoder_libjpeg.cpp',
- '../src/images/SkImageDecoder_libpng.cpp',
- '../src/images/SkImageDecoder_libpvjpeg.c',
- '../src/images/SkImageDecoder_wbmp.cpp',
- '../src/images/SkImageEncoder.cpp',
- '../src/images/SkImageEncoder_Factory.cpp',
- '../src/images/SkImageRef.cpp',
- '../src/images/SkImageRefPool.cpp',
- '../src/images/SkImageRefPool.h',
- '../src/images/SkImageRef_GlobalPool.cpp',
- '../src/images/SkJpegUtility.cpp',
- '../src/images/SkMovie.cpp',
- '../src/images/SkMovie_gif.cpp',
- '../src/images/SkPageFlipper.cpp',
- '../src/images/SkScaledBitmapSampler.cpp',
- '../src/images/SkScaledBitmapSampler.h',
- ],
- 'conditions': [
- [ 'OS == "win"', {
- 'sources!': [
- '../include/images/SkJpegUtility.h',
-
- '../src/images/SkFDStream.cpp',
- '../src/images/SkImageDecoder_libgif.cpp',
- '../src/images/SkImageDecoder_libjpeg.cpp',
- '../src/images/SkImageDecoder_libpng.cpp',
- '../src/images/SkImageDecoder_libpvjpeg.c',
- '../src/images/SkJpegUtility.cpp',
- '../src/images/SkMovie_gif.cpp',
- ],
- }],
- [ 'OS == "mac"', {
- 'sources!': [
- '../include/images/SkJpegUtility.h',
-
- '../src/images/SkImageDecoder_libgif.cpp',
- '../src/images/SkImageDecoder_libjpeg.cpp',
- '../src/images/SkImageDecoder_libpng.cpp',
- '../src/images/SkImageDecoder_libpvjpeg.c',
- '../src/images/SkJpegUtility.cpp',
- '../src/images/SkMovie_gif.cpp',
- ],
- }],
- [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
- 'sources!': [
- '../include/images/SkJpegUtility.h',
-
- '../src/images/SkImageDecoder_libjpeg.cpp',
- '../src/images/SkImageDecoder_libgif.cpp',
- '../src/images/SkImageDecoder_libpvjpeg.c',
- '../src/images/SkJpegUtility.cpp',
- '../src/images/SkMovie_gif.cpp',
- ],
- }],
-
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/images',
- ],
- },
- },
- {
- 'target_name': 'xml',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/xml',
- '../include/utils',
- ],
- 'sources': [
- '../include/xml/SkBML_WXMLParser.h',
- '../include/xml/SkBML_XMLParser.h',
- '../include/xml/SkDOM.h',
- '../include/xml/SkJS.h',
- '../include/xml/SkXMLParser.h',
- '../include/xml/SkXMLWriter.h',
-
- '../src/xml/SkBML_Verbs.h',
- '../src/xml/SkBML_XMLParser.cpp',
- '../src/xml/SkDOM.cpp',
- '../src/xml/SkJS.cpp',
- '../src/xml/SkJSDisplayable.cpp',
- '../src/xml/SkXMLParser.cpp',
- '../src/xml/SkXMLPullParser.cpp',
- '../src/xml/SkXMLWriter.cpp',
- ],
- 'sources!': [
- '../src/xml/SkXMLPullParser.cpp', #if 0 around class decl in header
- ],
- 'conditions': [
- [ 'OS == "win" or OS == "mac" or OS == "linux" or OS == "openbsd" or OS == "solaris"', {
- 'sources!': [
- # no jsapi.h by default on system
- '../include/xml/SkJS.h',
- '../src/xml/SkJS.cpp',
- '../src/xml/SkJSDisplayable.cpp',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/xml',
- ],
- },
- },
- {
- 'target_name': 'pdf',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/pdf',
- '../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h
- ],
- 'sources': [
- '../include/pdf/SkPDFCatalog.h',
- '../include/pdf/SkPDFDevice.h',
- '../include/pdf/SkPDFDocument.h',
- '../include/pdf/SkPDFFont.h',
- '../include/pdf/SkPDFFormXObject.h',
- '../include/pdf/SkPDFGraphicState.h',
- '../include/pdf/SkPDFImage.h',
- '../include/pdf/SkPDFPage.h',
- '../include/pdf/SkPDFShader.h',
- '../include/pdf/SkPDFStream.h',
- '../include/pdf/SkPDFTypes.h',
- '../include/pdf/SkPDFUtils.h',
-
- '../src/pdf/SkPDFCatalog.cpp',
- '../src/pdf/SkPDFDevice.cpp',
- '../src/pdf/SkPDFDocument.cpp',
- '../src/pdf/SkPDFFont.cpp',
- '../src/pdf/SkPDFFormXObject.cpp',
- '../src/pdf/SkPDFGraphicState.cpp',
- '../src/pdf/SkPDFImage.cpp',
- '../src/pdf/SkPDFPage.cpp',
- '../src/pdf/SkPDFShader.cpp',
- '../src/pdf/SkPDFStream.cpp',
- '../src/pdf/SkPDFTypes.cpp',
- '../src/pdf/SkPDFUtils.cpp',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/pdf',
- ],
- },
- },
- {
- 'target_name': 'utils',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/utils',
- '../include/views',
- '../include/effects',
- '../include/xml',
- ],
- 'sources': [
- '../include/utils/SkBoundaryPatch.h',
- '../include/utils/SkCamera.h',
- '../include/utils/SkCubicInterval.h',
- '../include/utils/SkCullPoints.h',
- '../include/utils/SkDumpCanvas.h',
- '../include/utils/SkEGLContext.h',
- '../include/utils/SkGLCanvas.h',
- '../include/utils/SkInterpolator.h',
- '../include/utils/SkLayer.h',
- '../include/utils/SkMeshUtils.h',
- '../include/utils/SkNinePatch.h',
- '../include/utils/SkNWayCanvas.h',
- '../include/utils/SkParse.h',
- '../include/utils/SkParsePaint.h',
- '../include/utils/SkParsePath.h',
- '../include/utils/SkProxyCanvas.h',
- '../include/utils/SkSfntUtils.h',
- '../include/utils/SkTextBox.h',
- '../include/utils/SkUnitMappers.h',
-
- '../src/utils/SkBoundaryPatch.cpp',
- '../src/utils/SkCamera.cpp',
- '../src/utils/SkColorMatrix.cpp',
- '../src/utils/SkCubicInterval.cpp',
- '../src/utils/SkCullPoints.cpp',
- '../src/utils/SkDumpCanvas.cpp',
- '../src/utils/SkEGLContext_none.cpp',
- '../src/utils/SkInterpolator.cpp',
- '../src/utils/SkLayer.cpp',
- '../src/utils/SkMeshUtils.cpp',
- '../src/utils/SkNinePatch.cpp',
- '../src/utils/SkNWayCanvas.cpp',
- '../src/utils/SkOSFile.cpp',
- '../src/utils/SkParse.cpp',
- '../src/utils/SkParseColor.cpp',
- '../src/utils/SkParsePath.cpp',
- '../src/utils/SkProxyCanvas.cpp',
- '../src/utils/SkSfntUtils.cpp',
- '../src/utils/SkUnitMappers.cpp',
- ],
- 'conditions': [
- [ 'OS == "mac"', {
- 'sources': [
- '../include/utils/SkCGUtils.h',
- '../src/utils/mac/SkCreateCGImageRef.cpp',
- '../src/utils/mac/SkEGLContext_mac.cpp',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/utils',
- ],
- },
- },
- {
- 'target_name': 'views',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/views',
- '../include/xml',
- '../include/utils',
- '../include/images',
- '../include/animator',
- '../include/effects',
- ],
- 'sources': [
- '../include/views/SkApplication.h',
- '../include/views/SkBGViewArtist.h',
- '../include/views/SkBorderView.h',
- '../include/views/SkEvent.h',
- '../include/views/SkEventSink.h',
- '../include/views/SkImageView.h',
- '../include/views/SkKey.h',
- '../include/views/SkOSMenu.h',
- '../include/views/SkOSWindow_Mac.h',
- '../include/views/SkOSWindow_SDL.h',
- '../include/views/SkOSWindow_Unix.h',
- '../include/views/SkOSWindow_Win.h',
- #'../include/views/SkOSWindow_wxwidgets.h',
- '../include/views/SkProgressBarView.h',
- '../include/views/SkScrollBarView.h',
- '../include/views/SkStackViewLayout.h',
- '../include/views/SkSystemEventTypes.h',
- '../include/views/SkTouchGesture.h',
- '../include/views/SkView.h',
- '../include/views/SkViewInflate.h',
- '../include/views/SkWidget.h',
- '../include/views/SkWidgetViews.h',
- '../include/views/SkWindow.h',
-
- '../src/views/SkBGViewArtist.cpp',
- '../src/views/SkBorderView.cpp',
- '../src/views/SkEvent.cpp',
- '../src/views/SkEventSink.cpp',
- '../src/views/SkImageView.cpp',
- '../src/views/SkListView.cpp',
- '../src/views/SkListWidget.cpp',
- '../src/views/SkOSMenu.cpp',
- '../src/views/SkParsePaint.cpp',
- '../src/views/SkProgressBarView.cpp',
- '../src/views/SkProgressView.cpp',
- '../src/views/SkScrollBarView.cpp',
- '../src/views/SkStackViewLayout.cpp',
- '../src/views/SkStaticTextView.cpp',
- '../src/views/SkTagList.cpp',
- '../src/views/SkTagList.h',
- '../src/views/SkTextBox.cpp',
- '../src/views/SkTouchGesture.cpp',
- '../src/views/SkView.cpp',
- '../src/views/SkViewInflate.cpp',
- '../src/views/SkViewPriv.cpp',
- '../src/views/SkViewPriv.h',
- '../src/views/SkWidget.cpp',
- '../src/views/SkWidgets.cpp',
- '../src/views/SkWidgetViews.cpp',
- '../src/views/SkWindow.cpp',
- ],
- 'sources!' : [
- '../src/views/SkListView.cpp', #depends on missing SkListSource implementation
- '../src/views/SkListWidget.cpp', #depends on missing SkListSource implementation
- ],
- 'conditions': [
- [ 'OS == "win"', {
- 'sources': [
- '../src/utils/win/SkOSWindow_Win.cpp',
- '../src/utils/win/skia_win.cpp',
- ],
- }],
- [ 'OS == "mac"', {
- 'sources': [
- '../include/utils/SkCGUtils.h',
- #'../src/utils/mac/SkBitmap_Mac.cpp',
- '../src/utils/mac/SkCreateCGImageRef.cpp',
- '../src/utils/mac/SkEGLContext_mac.cpp',
- '../src/utils/mac/skia_mac.cpp',
- '../src/utils/mac/SkOSWindow_Mac.cpp',
- ],
- 'link_settings': {
- 'libraries': [
- '$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
- '$(SDKROOT)/System/Library/Frameworks/AGL.framework',
- ],
- },
- }],
- [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
- 'include_dirs' : [
- '../include/utils/unix',
- ],
- 'sources': [
- '../src/utils/unix/keysym2ucs.c',
- '../src/utils/unix/SkOSWindow_Unix.cpp',
- '../unix_test_app/main.cpp',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/views',
- ],
- },
- },
- {
- 'target_name': 'skgr',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../src/core',
- '../include/gpu',
- '../gpu/include',
- ],
- 'sources': [
- '../include/gpu/SkGpuCanvas.h',
- '../include/gpu/SkGpuDevice.h',
- '../include/gpu/SkGpuDeviceFactory.h',
- '../include/gpu/SkGr.h',
- '../include/gpu/SkGrTexturePixelRef.h',
-
- '../src/gpu/GrPrintf_skia.cpp',
- '../src/gpu/SkGpuCanvas.cpp',
- '../src/gpu/SkGpuDevice.cpp',
- '../src/gpu/SkGr.cpp',
- '../src/gpu/SkGrFontScaler.cpp',
- '../src/gpu/SkGrTexturePixelRef.cpp',
- ],
- 'conditions': [
- [ 'OS == "linux"', {
- 'defines': [
- 'GR_LINUX_BUILD=1',
- ],
- }],
- [ 'OS == "mac"', {
- 'defines': [
- 'GR_MAC_BUILD=1',
- ],
- }],
- [ 'OS == "win"', {
- 'defines': [
- 'GR_WIN32_BUILD=1',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'conditions': [
- [ 'OS == "linux"', {
- 'defines': [
- 'GR_LINUX_BUILD=1',
- ],
- }],
- [ 'OS == "mac"', {
- 'defines': [
- 'GR_MAC_BUILD=1',
- ],
- }],
- [ 'OS == "win"', {
- 'defines': [
- 'GR_WIN32_BUILD=1',
- ],
- }],
- ],
- 'include_dirs': [
- '../include/gpu',
- ],
- },
- },
- {
- 'target_name': 'gr',
- 'type': 'static_library',
- 'include_dirs': [
- '../gpu/include',
- '../include/core',
- '../include/config',
- ],
- 'dependencies': [
- 'libtess',
- ],
- 'sources': [
- '../gpu/include/GrAllocator.h',
- '../gpu/include/GrAllocPool.h',
- '../gpu/include/GrAtlas.h',
- '../gpu/include/GrClip.h',
- '../gpu/include/GrClipIterator.h',
- '../gpu/include/GrColor.h',
- '../gpu/include/GrConfig.h',
- '../gpu/include/GrContext.h',
- '../gpu/include/GrContext_impl.h',
- '../gpu/include/GrDrawTarget.h',
- '../gpu/include/GrFontScaler.h',
- '../gpu/include/GrGeometryBuffer.h',
- '../gpu/include/GrGLConfig.h',
- '../gpu/include/GrGLConfig_chrome.h',
- '../gpu/include/GrGLIndexBuffer.h',
- '../gpu/include/GrGLInterface.h',
- '../gpu/include/GrGLIRect.h',
- '../gpu/include/GrGLTexture.h',
- '../gpu/include/GrGLVertexBuffer.h',
- '../gpu/include/GrGlyph.h',
- '../gpu/include/GrGpu.h',
- '../gpu/include/GrGpuVertex.h',
- '../gpu/include/GrIndexBuffer.h',
- '../gpu/include/GrInOrderDrawBuffer.h',
- '../gpu/include/GrInstanceCounter.h',
- '../gpu/include/GrIPoint.h',
- '../gpu/include/GrKey.h',
- '../gpu/include/GrMatrix.h',
- '../gpu/include/GrMemory.h',
- '../gpu/include/GrMesh.h',
- '../gpu/include/GrNoncopyable.h',
- '../gpu/include/GrPaint.h',
- '../gpu/include/GrPath.h',
- '../gpu/include/GrPathIter.h',
- '../gpu/include/GrPathRenderer.h',
- '../gpu/include/GrPathSink.h',
- '../gpu/include/GrPlotMgr.h',
- '../gpu/include/GrPoint.h',
- '../gpu/include/GrRandom.h',
- '../gpu/include/GrRect.h',
- '../gpu/include/GrRectanizer.h',
- '../gpu/include/GrRefCnt.h',
- '../gpu/include/GrResource.h',
- '../gpu/include/GrSamplerState.h',
- '../gpu/include/GrScalar.h',
- '../gpu/include/GrStencil.h',
- '../gpu/include/GrStopwatch.h',
- '../gpu/include/GrStringBuilder.h',
- '../gpu/include/GrTArray.h',
- '../gpu/include/GrTBSearch.h',
- '../gpu/include/GrTDArray.h',
- '../gpu/include/GrTesselatedPathRenderer.h',
- '../gpu/include/GrTextContext.h',
- '../gpu/include/GrTextStrike.h',
- '../gpu/include/GrTexture.h',
- '../gpu/include/GrTextureCache.h',
- '../gpu/include/GrTHashCache.h',
- '../gpu/include/GrTLList.h',
- '../gpu/include/GrTouchGesture.h',
- '../gpu/include/GrTypes.h',
- '../gpu/include/GrUserConfig.h',
- '../gpu/include/GrVertexBuffer.h',
-
- '../gpu/src/GrAllocPool.cpp',
- '../gpu/src/GrAtlas.cpp',
- '../gpu/src/GrBinHashKey.h',
- '../gpu/src/GrBufferAllocPool.cpp',
- '../gpu/src/GrBufferAllocPool.h',
- '../gpu/src/GrClip.cpp',
- '../gpu/src/GrContext.cpp',
- '../gpu/src/GrCreatePathRenderer_none.cpp',
- '../gpu/src/GrDrawTarget.cpp',
- '../gpu/src/GrGLEffect.h',
- '../gpu/src/GrGLDefaultInterface_none.cpp',
- '../gpu/src/GrGLIndexBuffer.cpp',
- '../gpu/src/GrGLInterface.cpp',
- '../gpu/src/GrGLProgram.cpp',
- '../gpu/src/GrGLProgram.h',
- '../gpu/src/GrGLTexture.cpp',
- '../gpu/src/GrGLUtil.cpp',
- '../gpu/src/GrGLVertexBuffer.cpp',
- '../gpu/src/GrGpu.cpp',
- '../gpu/src/GrGpuFactory.cpp',
- '../gpu/src/GrGpuGL.cpp',
- '../gpu/src/GrGpuGL.h',
- '../gpu/src/GrGpuGLFixed.cpp',
- '../gpu/src/GrGpuGLFixed.h',
- '../gpu/src/GrGpuGLShaders.cpp',
- '../gpu/src/GrGpuGLShaders.h',
- '../gpu/src/GrInOrderDrawBuffer.cpp',
- '../gpu/src/GrMatrix.cpp',
- '../gpu/src/GrMemory.cpp',
- '../gpu/src/GrPath.cpp',
- '../gpu/src/GrPathRenderer.cpp',
- '../gpu/src/GrPathUtils.cpp',
- '../gpu/src/GrPathUtils.h',
- '../gpu/src/GrRectanizer.cpp',
- '../gpu/src/GrRedBlackTree.h',
- '../gpu/src/GrResource.cpp',
- '../gpu/src/GrStencil.cpp',
- '../gpu/src/GrTesselatedPathRenderer.cpp',
- '../gpu/src/GrTextContext.cpp',
- '../gpu/src/GrTextStrike.cpp',
- '../gpu/src/GrTextStrike_impl.h',
- '../gpu/src/GrTexture.cpp',
- '../gpu/src/GrTextureCache.cpp',
- '../gpu/src/gr_unittests.cpp',
-
- '../gpu/src/mac/GrGLDefaultInterface_mac.cpp',
-
- '../gpu/src/win/GrGLDefaultInterface_win.cpp',
-
- '../gpu/src/unix/GrGLDefaultInterface_unix.cpp',
- ],
- 'defines': [
- 'GR_IMPLEMENTATION=1',
- ],
- 'conditions': [
- [ 'OS == "linux"', {
- 'defines': [
- 'GR_LINUX_BUILD=1',
- ],
- 'sources!': [
- '../gpu/src/GrGLDefaultInterface_none.cpp',
- ],
- 'link_settings': {
- 'libraries': [
- '-lGL',
- '-lX11',
- ],
- },
- }],
- [ 'OS == "mac"', {
- 'defines': [
- 'GR_MAC_BUILD=1',
- ],
- 'link_settings': {
- 'libraries': [
- '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
- ],
- },
- 'sources!': [
- '../gpu/src/GrGLDefaultInterface_none.cpp',
- ],
- }],
- [ 'OS == "win"', {
- 'defines': [
- 'GR_WIN32_BUILD=1',
- 'GR_GL_FUNCTION_TYPE=__stdcall',
- ],
- 'sources!': [
- '../gpu/src/GrGLDefaultInterface_none.cpp',
- ],
- }],
- [ 'OS != "win"', {
- 'sources!': [
- '../gpu/src/win/GrGLDefaultInterface_win.cpp',
- ],
- }],
- [ 'OS != "mac"', {
- 'sources!': [
- '../gpu/src/mac/GrGLDefaultInterface_mac.cpp',
- ],
- }],
- [ 'OS != "linux"', {
- 'sources!': [
- '../gpu/src/unix/GrGLDefaultInterface_unix.cpp',
- ],
- }],
- ],
- 'direct_dependent_settings': {
- 'conditions': [
- [ 'OS == "linux"', {
- 'defines': [
- 'GR_LINUX_BUILD=1',
- ],
- }],
- [ 'OS == "mac"', {
- 'defines': [
- 'GR_MAC_BUILD=1',
- ],
- }],
- [ 'OS == "win"', {
- 'defines': [
- 'GR_WIN32_BUILD=1',
- 'GR_GL_FUNCTION_TYPE=__stdcall',
- ],
- }],
- ],
- 'include_dirs': [
- '../gpu/include',
- ],
- },
- },
- {
- 'target_name': 'animator',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/effects',
- '../include/animator',
- '../include/views',
- '../include/xml',
- '../include/utils',
- '../include/images',
- ],
- 'sources': [
- '../include/animator/SkAnimator.h',
- '../include/animator/SkAnimatorView.h',
-
- '../src/animator/SkAnimate.h',
- '../src/animator/SkAnimateActive.cpp',
- '../src/animator/SkAnimateActive.h',
- '../src/animator/SkAnimateBase.cpp',
- '../src/animator/SkAnimateBase.h',
- '../src/animator/SkAnimateField.cpp',
- '../src/animator/SkAnimateMaker.cpp',
- '../src/animator/SkAnimateMaker.h',
- '../src/animator/SkAnimateProperties.h',
- '../src/animator/SkAnimateSet.cpp',
- '../src/animator/SkAnimateSet.h',
- '../src/animator/SkAnimator.cpp',
- '../src/animator/SkAnimatorScript.cpp',
- '../src/animator/SkAnimatorScript.h',
- #'../src/animator/SkAnimatorScript2.cpp', fails on windows
- #'../src/animator/SkAnimatorScript2.h',
- '../src/animator/SkBase64.cpp',
- '../src/animator/SkBase64.h',
- '../src/animator/SkBoundable.cpp',
- '../src/animator/SkBoundable.h',
- '../src/animator/SkBuildCondensedInfo.cpp',
- #'../src/animator/SkCondensedDebug.cpp', fails on windows
- #'../src/animator/SkCondensedRelease.cpp',
- '../src/animator/SkDisplayable.cpp',
- '../src/animator/SkDisplayable.h',
- '../src/animator/SkDisplayAdd.cpp',
- '../src/animator/SkDisplayAdd.h',
- '../src/animator/SkDisplayApply.cpp',
- '../src/animator/SkDisplayApply.h',
- '../src/animator/SkDisplayBounds.cpp',
- '../src/animator/SkDisplayBounds.h',
- '../src/animator/SkDisplayEvent.cpp',
- '../src/animator/SkDisplayEvent.h',
- '../src/animator/SkDisplayEvents.cpp',
- '../src/animator/SkDisplayEvents.h',
- '../src/animator/SkDisplayInclude.cpp',
- '../src/animator/SkDisplayInclude.h',
- '../src/animator/SkDisplayInput.cpp',
- '../src/animator/SkDisplayInput.h',
- '../src/animator/SkDisplayList.cpp',
- '../src/animator/SkDisplayList.h',
- '../src/animator/SkDisplayMath.cpp',
- '../src/animator/SkDisplayMath.h',
- '../src/animator/SkDisplayMovie.cpp',
- '../src/animator/SkDisplayMovie.h',
- '../src/animator/SkDisplayNumber.cpp',
- '../src/animator/SkDisplayNumber.h',
- '../src/animator/SkDisplayPost.cpp',
- '../src/animator/SkDisplayPost.h',
- '../src/animator/SkDisplayRandom.cpp',
- '../src/animator/SkDisplayRandom.h',
- '../src/animator/SkDisplayScreenplay.cpp',
- '../src/animator/SkDisplayScreenplay.h',
- '../src/animator/SkDisplayType.cpp',
- '../src/animator/SkDisplayType.h',
- '../src/animator/SkDisplayTypes.cpp',
- '../src/animator/SkDisplayTypes.h',
- '../src/animator/SkDisplayXMLParser.cpp',
- '../src/animator/SkDisplayXMLParser.h',
- '../src/animator/SkDraw3D.cpp',
- '../src/animator/SkDraw3D.h',
- '../src/animator/SkDrawable.cpp',
- '../src/animator/SkDrawable.h',
- '../src/animator/SkDrawBitmap.cpp',
- '../src/animator/SkDrawBitmap.h',
- '../src/animator/SkDrawBlur.cpp',
- '../src/animator/SkDrawBlur.h',
- '../src/animator/SkDrawClip.cpp',
- '../src/animator/SkDrawClip.h',
- '../src/animator/SkDrawColor.cpp',
- '../src/animator/SkDrawColor.h',
- '../src/animator/SkDrawDash.cpp',
- '../src/animator/SkDrawDash.h',
- '../src/animator/SkDrawDiscrete.cpp',
- '../src/animator/SkDrawDiscrete.h',
- '../src/animator/SkDrawEmboss.cpp',
- '../src/animator/SkDrawEmboss.h',
- '../src/animator/SkDrawExtraPathEffect.cpp',
- '../src/animator/SkDrawFull.cpp',
- '../src/animator/SkDrawFull.h',
- '../src/animator/SkDrawGradient.cpp',
- '../src/animator/SkDrawGradient.h',
- '../src/animator/SkDrawGroup.cpp',
- '../src/animator/SkDrawGroup.h',
- '../src/animator/SkDrawLine.cpp',
- '../src/animator/SkDrawLine.h',
- '../src/animator/SkDrawMatrix.cpp',
- '../src/animator/SkDrawMatrix.h',
- '../src/animator/SkDrawOval.cpp',
- '../src/animator/SkDrawOval.h',
- '../src/animator/SkDrawPaint.cpp',
- '../src/animator/SkDrawPaint.h',
- '../src/animator/SkDrawPath.cpp',
- '../src/animator/SkDrawPath.h',
- '../src/animator/SkDrawPoint.cpp',
- '../src/animator/SkDrawPoint.h',
- '../src/animator/SkDrawRectangle.cpp',
- '../src/animator/SkDrawRectangle.h',
- '../src/animator/SkDrawSaveLayer.cpp',
- '../src/animator/SkDrawSaveLayer.h',
- '../src/animator/SkDrawShader.cpp',
- '../src/animator/SkDrawShader.h',
- '../src/animator/SkDrawText.cpp',
- '../src/animator/SkDrawText.h',
- '../src/animator/SkDrawTextBox.cpp',
- '../src/animator/SkDrawTextBox.h',
- '../src/animator/SkDrawTo.cpp',
- '../src/animator/SkDrawTo.h',
- '../src/animator/SkDrawTransparentShader.cpp',
- '../src/animator/SkDrawTransparentShader.h',
- '../src/animator/SkDump.cpp',
- '../src/animator/SkDump.h',
- '../src/animator/SkExtras.h',
- '../src/animator/SkGetCondensedInfo.cpp',
- '../src/animator/SkHitClear.cpp',
- '../src/animator/SkHitClear.h',
- '../src/animator/SkHitTest.cpp',
- '../src/animator/SkHitTest.h',
- '../src/animator/SkIntArray.h',
- '../src/animator/SkMatrixParts.cpp',
- '../src/animator/SkMatrixParts.h',
- '../src/animator/SkMemberInfo.cpp',
- '../src/animator/SkMemberInfo.h',
- '../src/animator/SkOpArray.cpp',
- '../src/animator/SkOpArray.h',
- '../src/animator/SkOperand.h',
- '../src/animator/SkOperand2.h',
- '../src/animator/SkOperandInterpolator.h',
- '../src/animator/SkOperandIterpolator.cpp',
- '../src/animator/SkPaintParts.cpp',
- '../src/animator/SkPaintParts.h',
- '../src/animator/SkParseSVGPath.cpp',
- '../src/animator/SkPathParts.cpp',
- '../src/animator/SkPathParts.h',
- '../src/animator/SkPostParts.cpp',
- '../src/animator/SkPostParts.h',
- '../src/animator/SkScript.cpp',
- '../src/animator/SkScript.h',
- '../src/animator/SkScript2.h',
- '../src/animator/SkScriptCallBack.h',
- '../src/animator/SkScriptDecompile.cpp',
- '../src/animator/SkScriptRuntime.cpp',
- '../src/animator/SkScriptRuntime.h',
- '../src/animator/SkScriptTokenizer.cpp',
- '../src/animator/SkSnapshot.cpp',
- '../src/animator/SkSnapshot.h',
- '../src/animator/SkTDArray_Experimental.h',
- '../src/animator/SkTextOnPath.cpp',
- '../src/animator/SkTextOnPath.h',
- '../src/animator/SkTextToPath.cpp',
- '../src/animator/SkTextToPath.h',
- '../src/animator/SkTime.cpp',
- '../src/animator/SkTypedArray.cpp',
- '../src/animator/SkTypedArray.h',
- '../src/animator/SkXMLAnimatorWriter.cpp',
- '../src/animator/SkXMLAnimatorWriter.h',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/animator',
- ],
- },
- },
-
- {
- 'target_name': 'svg',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/xml',
- '../include/utils',
- '../include/svg',
- ],
- 'sources': [
- '../include/svg/SkSVGAttribute.h',
- '../include/svg/SkSVGBase.h',
- '../include/svg/SkSVGPaintState.h',
- '../include/svg/SkSVGParser.h',
- '../include/svg/SkSVGTypes.h',
-
- '../src/svg/SkSVGCircle.cpp',
- '../src/svg/SkSVGCircle.h',
- '../src/svg/SkSVGClipPath.cpp',
- '../src/svg/SkSVGClipPath.h',
- '../src/svg/SkSVGDefs.cpp',
- '../src/svg/SkSVGDefs.h',
- '../src/svg/SkSVGElements.cpp',
- '../src/svg/SkSVGElements.h',
- '../src/svg/SkSVGEllipse.cpp',
- '../src/svg/SkSVGEllipse.h',
- '../src/svg/SkSVGFeColorMatrix.cpp',
- '../src/svg/SkSVGFeColorMatrix.h',
- '../src/svg/SkSVGFilter.cpp',
- '../src/svg/SkSVGFilter.h',
- '../src/svg/SkSVGG.cpp',
- '../src/svg/SkSVGG.h',
- '../src/svg/SkSVGGradient.cpp',
- '../src/svg/SkSVGGradient.h',
- '../src/svg/SkSVGGroup.cpp',
- '../src/svg/SkSVGGroup.h',
- '../src/svg/SkSVGImage.cpp',
- '../src/svg/SkSVGImage.h',
- '../src/svg/SkSVGLine.cpp',
- '../src/svg/SkSVGLine.h',
- '../src/svg/SkSVGLinearGradient.cpp',
- '../src/svg/SkSVGLinearGradient.h',
- '../src/svg/SkSVGMask.cpp',
- '../src/svg/SkSVGMask.h',
- '../src/svg/SkSVGMetadata.cpp',
- '../src/svg/SkSVGMetadata.h',
- '../src/svg/SkSVGPaintState.cpp',
- '../src/svg/SkSVGParser.cpp',
- '../src/svg/SkSVGPath.cpp',
- '../src/svg/SkSVGPath.h',
- '../src/svg/SkSVGPolygon.cpp',
- '../src/svg/SkSVGPolygon.h',
- '../src/svg/SkSVGPolyline.cpp',
- '../src/svg/SkSVGPolyline.h',
- '../src/svg/SkSVGRadialGradient.cpp',
- '../src/svg/SkSVGRadialGradient.h',
- '../src/svg/SkSVGRect.cpp',
- '../src/svg/SkSVGRect.h',
- '../src/svg/SkSVGStop.cpp',
- '../src/svg/SkSVGStop.h',
- '../src/svg/SkSVGSVG.cpp',
- '../src/svg/SkSVGSVG.h',
- '../src/svg/SkSVGSymbol.cpp',
- '../src/svg/SkSVGSymbol.h',
- '../src/svg/SkSVGText.cpp',
- '../src/svg/SkSVGText.h',
- '../src/svg/SkSVGUse.cpp',
- ],
- 'sources!' : [
- '../src/svg/SkSVG.cpp', # doesn't compile, maybe this is test code?
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/svg',
- ],
- },
- },
-
- {
- 'target_name': 'experimental',
- 'type': 'static_library',
- 'include_dirs': [
- '../include/config',
- '../include/core',
- ],
- 'sources': [
- '../experimental/SkMatrix44.cpp',
- '../experimental/SkMatrix44.h',
- '../experimental/SkSetPoly3To3.cpp',
- '../experimental/SkSetPoly3To3_A.cpp',
- '../experimental/SkSetPoly3To3_D.cpp',
- ],
- 'sources!': [
- '../experimental/SkMatrix44.cpp', #doesn't compile
- '../experimental/SkMatrix44.h',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../experimental',
- ],
- },
- },
-
- {
- 'target_name': 'SampleApp',
- 'type': 'executable',
- 'mac_bundle' : 1,
- 'include_dirs' : [
- '../src/core', # needed to get SkConcaveToTriangle, maybe this should be moved to include dir?
- '../gm', # SampleGM.cpp pulls gm.h
- ],
- 'sources': [
- # gm files needed for SampleGM.cpp
- '../gm/bitmapfilters.cpp',
- '../gm/blurs.cpp',
- '../gm/complexclip.cpp',
- '../gm/filltypes.cpp',
- '../gm/gm.h',
- '../gm/gradients.cpp',
- '../gm/points.cpp',
- '../gm/poly2poly.cpp',
- '../gm/shadertext.cpp',
- '../gm/shadows.cpp',
- '../gm/shapes.cpp',
- '../gm/tilemodes.cpp',
- '../gm/xfermodes.cpp',
-
- '../samplecode/ClockFaceView.cpp',
- '../samplecode/OverView.cpp',
- '../samplecode/SampleAll.cpp',
- '../samplecode/SampleAnimator.cpp',
- '../samplecode/SampleApp.cpp',
- '../samplecode/SampleArc.cpp',
- '../samplecode/SampleAvoid.cpp',
- '../samplecode/SampleBigGradient.cpp',
- '../samplecode/SampleBitmapRect.cpp',
- '../samplecode/SampleBlur.cpp',
- '../samplecode/SampleCamera.cpp',
- '../samplecode/SampleCircle.cpp',
- '../samplecode/SampleCode.h',
- '../samplecode/SampleColorFilter.cpp',
- '../samplecode/SampleComplexClip.cpp',
- '../samplecode/SampleCull.cpp',
- '../samplecode/SampleDecode.cpp',
- '../samplecode/SampleDither.cpp',
- '../samplecode/SampleDitherBitmap.cpp',
- '../samplecode/SampleDrawLooper.cpp',
- '../samplecode/SampleEffects.cpp',
- '../samplecode/SampleEmboss.cpp',
- '../samplecode/SampleEncode.cpp',
- '../samplecode/SampleExtractAlpha.cpp',
- '../samplecode/SampleFillType.cpp',
- '../samplecode/SampleFilter.cpp',
- '../samplecode/SampleFilter2.cpp',
- '../samplecode/SampleFontCache.cpp',
- '../samplecode/SampleFontScalerTest.cpp',
- '../samplecode/SampleFuzz.cpp',
- '../samplecode/SampleGM.cpp',
- '../samplecode/SampleGradients.cpp',
- '../samplecode/SampleHairline.cpp',
- '../samplecode/SampleImage.cpp',
- '../samplecode/SampleImageDir.cpp',
- '../samplecode/SampleLayerMask.cpp',
- '../samplecode/SampleLayers.cpp',
- '../samplecode/SampleLCD.cpp',
- '../samplecode/SampleLineClipper.cpp',
- '../samplecode/SampleLines.cpp',
- '../samplecode/SampleMeasure.cpp',
- '../samplecode/SampleMipMap.cpp',
- '../samplecode/SampleMovie.cpp',
- '../samplecode/SampleNinePatch.cpp',
- '../samplecode/SampleOvalTest.cpp',
- '../samplecode/SampleOverflow.cpp',
- '../samplecode/SamplePageFlip.cpp',
- '../samplecode/SamplePatch.cpp',
- '../samplecode/SamplePath.cpp',
- '../samplecode/SamplePathClip.cpp',
- '../samplecode/SamplePathEffects.cpp',
- '../samplecode/SamplePicture.cpp',
- '../samplecode/SamplePoints.cpp',
- '../samplecode/SamplePolyToPoly.cpp',
- '../samplecode/SampleAARects.cpp',
- '../samplecode/SampleRegion.cpp',
- '../samplecode/SampleRepeatTile.cpp',
- '../samplecode/SampleShaders.cpp',
- '../samplecode/SampleShaderText.cpp',
- '../samplecode/SampleShapes.cpp',
- '../samplecode/SampleSkLayer.cpp',
- '../samplecode/SampleSlides.cpp',
- '../samplecode/SampleStrokePath.cpp',
- '../samplecode/SampleStrokeText.cpp',
- '../samplecode/SampleSVG.cpp',
- '../samplecode/SampleTests.cpp',
- '../samplecode/SampleText.cpp',
- '../samplecode/SampleTextAlpha.cpp',
- '../samplecode/SampleTextBox.cpp',
- '../samplecode/SampleTextEffects.cpp',
- '../samplecode/SampleTextOnPath.cpp',
- '../samplecode/SampleTiling.cpp',
- '../samplecode/SampleTinyBitmap.cpp',
- '../samplecode/SampleTriangles.cpp',
- '../samplecode/SampleTypeface.cpp',
- '../samplecode/SampleUnitMapper.cpp',
- '../samplecode/SampleVertices.cpp',
- '../samplecode/SampleXfermodes.cpp',
- ],
- 'sources!': [
- '../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile
- '../samplecode/SampleTests.cpp', #includes unknown file SkShaderExtras.h
- '../samplecode/SampleWarp.cpp',
- '../samplecode/SampleFontCache.cpp',
- ],
- 'dependencies': [
- 'skia',
- 'effects',
- 'images',
- 'views',
- 'utils',
- 'animator',
- 'xml',
- 'svg',
- 'experimental',
- 'gr',
- 'skgr',
- ],
- 'conditions' : [
- [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
- 'sources!': [
- '../samplecode/SampleDecode.cpp',
- ],
- }],
- [ 'OS == "win"', {
- 'sources!': [
- # require UNIX functions
- '../samplecode/SampleEncode.cpp',
- '../samplecode/SamplePageFlip.cpp',
- ],
- }],
- [ 'OS == "mac"', {
- 'sources!': [
- '../samplecode/SampleDecode.cpp',
- ],
- }],
-
- ],
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'SubSystem': '2',
- 'AdditionalDependencies': [
- 'OpenGL32.lib',
- 'usp10.lib',
- 'd3d9.lib',
- ],
- },
- },
- },
- {
- 'target_name': 'libtess',
- 'type': 'static_library',
- 'include_dirs': [
- '../third_party/glu',
- ],
- 'sources': [
- '../third_party/glu/internal_glu.h',
- '../third_party/glu/gluos.h',
- '../third_party/glu/libtess/dict-list.h',
- '../third_party/glu/libtess/dict.c',
- '../third_party/glu/libtess/dict.h',
- '../third_party/glu/libtess/geom.c',
- '../third_party/glu/libtess/geom.h',
- '../third_party/glu/libtess/memalloc.c',
- '../third_party/glu/libtess/memalloc.h',
- '../third_party/glu/libtess/mesh.c',
- '../third_party/glu/libtess/mesh.h',
- '../third_party/glu/libtess/normal.c',
- '../third_party/glu/libtess/normal.h',
- '../third_party/glu/libtess/priorityq-heap.h',
- '../third_party/glu/libtess/priorityq-sort.h',
- '../third_party/glu/libtess/priorityq.c',
- '../third_party/glu/libtess/priorityq.h',
- '../third_party/glu/libtess/render.c',
- '../third_party/glu/libtess/render.h',
- '../third_party/glu/libtess/sweep.c',
- '../third_party/glu/libtess/sweep.h',
- '../third_party/glu/libtess/tess.c',
- '../third_party/glu/libtess/tess.h',
- '../third_party/glu/libtess/tessmono.c',
- '../third_party/glu/libtess/tessmono.h',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../third_party/glu',
- ],
- },
- },
- ],
-}
-
-# Local Variables:
-# tab-width:2
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h
index aa2e6cf40b..c56d8cfa78 100644
--- a/include/config/SkUserConfig.h
+++ b/include/config/SkUserConfig.h
@@ -54,7 +54,7 @@
/* Somewhat independent of how SkScalar is implemented, Skia also wants to know
if it can use floats at all. Naturally, if SK_SCALAR_IS_FLOAT is defined,
- then so muse SK_CAN_USE_FLOAT, but if scalars are fixed, SK_CAN_USE_FLOAT
+ SK_CAN_USE_FLOAT must be too; but if scalars are fixed, SK_CAN_USE_FLOAT
can go either way.
*/
//#define SK_CAN_USE_FLOAT
@@ -151,4 +151,3 @@
#endif
#endif
-
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index ae0b974d6e..6e8da76d29 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -43,6 +43,7 @@ public:
struct Clip {
friend bool operator==(const Clip& a, const Clip& b);
+ friend bool operator!=(const Clip& a, const Clip& b);
const SkRect* fRect; // if non-null, this is a rect clip
const SkPath* fPath; // if non-null, this is a path clip
SkRegion::Op fOp;
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
index f9e02a29c7..6fa9df365d 100644
--- a/include/core/SkColorPriv.h
+++ b/include/core/SkColorPriv.h
@@ -155,31 +155,13 @@ static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[],
#define SkRGB16Add(a, b) ((a) + (b))
#endif
-/////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
#define SK_A32_BITS 8
#define SK_R32_BITS 8
#define SK_G32_BITS 8
#define SK_B32_BITS 8
-/* we check to see if the SHIFT value has already been defined (SkUserConfig.h)
- if not, we define it ourself to some default values. We default to OpenGL
- order (in memory: r,g,b,a)
-*/
-#ifndef SK_A32_SHIFT
- #ifdef SK_CPU_BENDIAN
- #define SK_R32_SHIFT 24
- #define SK_G32_SHIFT 16
- #define SK_B32_SHIFT 8
- #define SK_A32_SHIFT 0
- #else
- #define SK_R32_SHIFT 0
- #define SK_G32_SHIFT 8
- #define SK_B32_SHIFT 16
- #define SK_A32_SHIFT 24
- #endif
-#endif
-
#define SK_A32_MASK ((1 << SK_A32_BITS) - 1)
#define SK_R32_MASK ((1 << SK_R32_BITS) - 1)
#define SK_G32_MASK ((1 << SK_G32_BITS) - 1)
diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h
index 0b198f61da..ea37549652 100644
--- a/include/core/SkComposeShader.h
+++ b/include/core/SkComposeShader.h
@@ -27,7 +27,7 @@ class SkXfermode;
This subclass of shader returns the coposition of two other shaders, combined by
a xfermode.
*/
-class SkComposeShader : public SkShader {
+class SK_API SkComposeShader : public SkShader {
public:
/** Create a new compose shader, given shaders A, B, and a combining xfermode mode.
When the xfermode is called, it will be given the result from shader A as its
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 46bcf1a105..d9a4fde4c2 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -155,31 +155,6 @@ public:
virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
const SkClipStack&);
- /**
- * Observer interface for listening to the calls to
- * SkDevice::setMatrixClip(...). Users of SkDevice instances should
- * implement matrixClipChanged(...) to receive notifications.
- */
- class SkMatrixClipObserver : public SkRefCnt {
- public:
- virtual void matrixClipChanged(const SkMatrix&, const SkRegion&,
- const SkClipStack&) = 0;
- };
-
- /** Assign the clip observer. Note that an extra reference is added to the
- * observer, and removed at SkDevice construction, or re-assignment of a
- * different observer.
- */
- void setMatrixClipObserver(SkMatrixClipObserver* observer);
-
- /** Return the device's associated SkMatrixClipObserver, or NULL.
- * If non-null is returned, the reference count of the object is not
- * modified.
- */
- SkMatrixClipObserver* getMatrixClipObserver() const {
- return fMatrixClipObserver;
- }
-
/** Called when this device gains focus (i.e becomes the current device
for drawing).
*/
@@ -306,8 +281,6 @@ private:
SkIPoint fOrigin;
SkMetaData* fMetaData;
- SkMatrixClipObserver* fMatrixClipObserver;
-
SkDeviceFactory* fCachedDeviceFactory;
};
diff --git a/include/core/SkMath.h b/include/core/SkMath.h
index 3e729042e3..efe378a733 100644
--- a/include/core/SkMath.h
+++ b/include/core/SkMath.h
@@ -115,7 +115,7 @@ static inline unsigned SkClampUMax(unsigned value, unsigned max) {
///////////////////////////////////////////////////////////////////////////////
-#if defined(__arm__) && !defined(__thumb__)
+#if defined(__arm__)
#define SkCLZ(x) __builtin_clz(x)
#endif
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 7c779027be..480e0777ac 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -438,6 +438,7 @@ public:
*/
bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
+#ifdef SK_SCALAR_IS_FIXED
friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0;
}
@@ -445,6 +446,12 @@ public:
friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
}
+#else
+ friend bool operator==(const SkMatrix& a, const SkMatrix& b);
+ friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
+ return !(a == b);
+ }
+#endif
enum {
// flatten/unflatten will never return a value larger than this
@@ -488,7 +495,12 @@ private:
kRectStaysRect_Mask = 0x10,
kUnknown_Mask = 0x80,
-
+
+ kORableMasks = kTranslate_Mask |
+ kScale_Mask |
+ kAffine_Mask |
+ kPerspective_Mask,
+
kAllMasks = kTranslate_Mask |
kScale_Mask |
kAffine_Mask |
@@ -506,7 +518,12 @@ private:
SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask);
fTypeMask = SkToU8(mask);
}
-
+
+ void orTypeMask(int mask) {
+ SkASSERT((mask & kORableMasks) == mask);
+ fTypeMask = SkToU8(fTypeMask | mask);
+ }
+
void clearTypeMask(int mask) {
// only allow a valid mask
SkASSERT((mask & kAllMasks) == mask);
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 18dcd11070..7120d3fa95 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -88,9 +88,10 @@ public:
/** Returns true if the filltype is one of the Inverse variants */
bool isInverseFillType() const { return (fFillType & 2) != 0; }
- /** Toggle between inverse and normal filltypes. This reverse the return
- value of isInverseFillType()
- */
+ /**
+ * Toggle between inverse and normal filltypes. This reverse the return
+ * value of isInverseFillType()
+ */
void toggleInverseFillType() {
fFillType ^= 2;
GEN_ID_INC;
@@ -103,14 +104,33 @@ public:
};
/**
- * Return the path's convexity, as stored in the path.
+ * Return the path's convexity, as stored in the path. If it is currently
+ * unknown, and the computeIfUnknown bool is true, then this will first
+ * call ComputeConvexity() and then return that (cached) value.
+ */
+ Convexity getConvexity() const {
+ if (kUnknown_Convexity == fConvexity) {
+ fConvexity = (uint8_t)ComputeConvexity(*this);
+ }
+ return (Convexity)fConvexity;
+ }
+
+ /**
+ * Return the currently cached value for convexity, even if that is set to
+ * kUnknown_Convexity. Note: getConvexity() will automatically call
+ * ComputeConvexity and cache its return value if the current setting is
+ * kUnknown.
*/
- Convexity getConvexity() const { return (Convexity)fConvexity; }
+ Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
/**
* Store a convexity setting in the path. There is no automatic check to
* see if this value actually agress with the return value from
* ComputeConvexity().
+ *
+ * Note: even if this is set to a "known" value, if the path is later
+ * changed (e.g. lineTo(), addRect(), etc.) then the cached value will be
+ * reset to kUnknown_Convexity.
*/
void setConvexity(Convexity);
@@ -118,9 +138,11 @@ public:
* Compute the convexity of the specified path. This does not look at the
* value stored in the path, but computes it directly from the path's data.
*
+ * This never returns kUnknown_Convexity.
+ *
* If there is more than one contour, this returns kConcave_Convexity.
- * If the contour is degenerate (i.e. all segements are colinear,
- * then this returns kUnknown_Convexity.
+ * If the contour is degenerate (e.g. there are fewer than 3 non-degenerate
+ * segments), then this returns kConvex_Convexity.
* The contour is treated as if it were closed, even if there is no kClose
* verb.
*/
@@ -635,7 +657,7 @@ private:
mutable SkRect fBounds;
mutable uint8_t fBoundsIsDirty;
uint8_t fFillType;
- uint8_t fConvexity;
+ mutable uint8_t fConvexity;
#ifdef ANDROID
uint32_t fGenerationID;
#endif
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index 57cc3682f5..23ce02beb3 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -132,6 +132,25 @@
#endif
#endif
+/*
+ * We check to see if the SHIFT value has already been defined.
+ * if not, we define it ourself to some default values. We default to OpenGL
+ * order (in memory: r,g,b,a)
+ */
+#ifndef SK_A32_SHIFT
+ #ifdef SK_CPU_BENDIAN
+ #define SK_R32_SHIFT 24
+ #define SK_G32_SHIFT 16
+ #define SK_B32_SHIFT 8
+ #define SK_A32_SHIFT 0
+ #else
+ #define SK_R32_SHIFT 0
+ #define SK_G32_SHIFT 8
+ #define SK_B32_SHIFT 16
+ #define SK_A32_SHIFT 24
+ #endif
+#endif
+
// stdlib macros
#if 0
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
index 8d47d461ff..6ec73cec38 100644
--- a/include/core/SkPreConfig.h
+++ b/include/core/SkPreConfig.h
@@ -41,14 +41,16 @@
#define SK_BUILD_FOR_UNIX
#elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#define SK_BUILD_FOR_IOS
- #elif defined(ANDROID_NDK)
- #define SK_BUILD_FOR_ANDROID_NDK
- #elif defined(ANROID)
- #define SK_BUILD_FOR_ANDROID
#else
#define SK_BUILD_FOR_MAC
#endif
+ #if defined(ANDROID)
+ #define SK_BUILD_FOR_ANDROID
+ #endif
+ #if defined(ANDROID_NDK)
+ #define SK_BUILD_FOR_ANDROID_NDK
+ #endif
#endif
//////////////////////////////////////////////////////////////////////
diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h
index 03a34c7ca6..654ebd5f19 100644
--- a/include/core/SkReader32.h
+++ b/include/core/SkReader32.h
@@ -101,7 +101,14 @@ public:
uint16_t readU16() { return (uint16_t)this->readInt(); }
int32_t readS32() { return this->readInt(); }
uint32_t readU32() { return this->readInt(); }
-
+
+ /**
+ * Read the length of a string written by SkWriter32::writeString()
+ * (if len is not NULL) and return the null-ternimated address of the
+ * string.
+ */
+ const char* readString(size_t* len = NULL);
+
private:
// these are always 4-byte aligned
const char* fCurr; // current position within buffer
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 550c5d1d46..19ee12a596 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -290,7 +290,7 @@ struct SK_API SkIRect {
void sort();
static const SkIRect& EmptyIRect() {
- static const SkIRect gEmpty = {0,0,0,0};
+ static const SkIRect gEmpty = { 0, 0, 0, 0 };
return gEmpty;
}
};
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
index ebe621be4f..5dbf6841a0 100644
--- a/include/core/SkScalar.h
+++ b/include/core/SkScalar.h
@@ -66,9 +66,47 @@
int exponent = bits << 1 >> 24;
return exponent != 0xFF;
}
+#ifdef SK_DEBUG
+ /** SkIntToScalar(n) returns its integer argument as an SkScalar
+ *
+ * If we're compiling in DEBUG mode, and can thus afford some extra runtime
+ * cycles, check to make sure that the parameter passed in has not already
+ * been converted to SkScalar. (A double conversion like this is harmless
+ * for SK_SCALAR_IS_FLOAT, but for SK_SCALAR_IS_FIXED this causes trouble.)
+ *
+ * Note that we need all of these method signatures to properly handle the
+ * various types that we pass into SkIntToScalar() to date:
+ * int, size_t, U8CPU, etc., even though what we really mean is "anything
+ * but a float".
+ */
+ static inline float SkIntToScalar(signed int param) {
+ return (float)param;
+ }
+ static inline float SkIntToScalar(unsigned int param) {
+ return (float)param;
+ }
+ static inline float SkIntToScalar(signed long param) {
+ return (float)param;
+ }
+ static inline float SkIntToScalar(unsigned long param) {
+ return (float)param;
+ }
+ static inline float SkIntToScalar(float param) {
+ /* If the parameter passed into SkIntToScalar is a float,
+ * one of two things has happened:
+ * 1. the parameter was an SkScalar (which is typedef'd to float)
+ * 2. the parameter was a float instead of an int
+ *
+ * Either way, it's not good.
+ */
+ SkASSERT(!"looks like you passed an SkScalar into SkIntToScalar");
+ return (float)0;
+ }
+#else // not SK_DEBUG
/** SkIntToScalar(n) returns its integer argument as an SkScalar
*/
#define SkIntToScalar(n) ((float)(n))
+#endif // not SK_DEBUG
/** SkFixedToScalar(n) returns its SkFixed argument as an SkScalar
*/
#define SkFixedToScalar(x) SkFixedToFloat(x)
@@ -282,4 +320,3 @@ SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[],
const SkScalar values[], int length);
#endif
-
diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h
index 55dfcef049..3f818a3112 100644
--- a/include/core/SkScalerContext.h
+++ b/include/core/SkScalerContext.h
@@ -179,6 +179,9 @@ public:
kEmbolden_Flag = 0x80,
kSubpixelPositioning_Flag = 0x100,
kAutohinting_Flag = 0x200,
+ // these should only ever be set if fMaskFormat is LCD
+ kLCD_Vertical_Flag = 0x400, // else Horizontal
+ kLCD_BGROrder_Flag = 0x800, // else RGB order
};
private:
enum {
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index 8e133c2202..c8ebb6a6ef 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -100,7 +100,22 @@ public:
}
void writePad(const void* src, size_t size);
-
+
+ /**
+ * Writes a string to the writer, which can be retrieved with
+ * SkReader32::readString().
+ * The length can be specified, or if -1 is passed, it will be computed by
+ * calling strlen(). The length must be < 0xFFFF
+ */
+ void writeString(const char* str, size_t len = (size_t)-1);
+
+ /**
+ * Computes the size (aligned to multiple of 4) need to write the string
+ * in a call to writeString(). If the length is not specified, it will be
+ * computed by calling strlen().
+ */
+ static size_t WriteStringSize(const char* str, size_t len = (size_t)-1);
+
// return the current offset (will always be a multiple of 4)
uint32_t size() const { return fSize; }
void reset();
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
index 4d46bb919a..6ab9d6d562 100644
--- a/include/core/SkXfermode.h
+++ b/include/core/SkXfermode.h
@@ -103,11 +103,15 @@ public:
kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
- // these modes are defined in the SVG Compositing standard
+ // all remaining modes are defined in the SVG Compositing standard
// http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
kPlus_Mode,
- kMultiply_Mode,
- kScreen_Mode,
+ kMultiply_Mode,
+
+ // all above modes can be expressed as pair of src/dst Coeffs
+ kCoeffModesCnt,
+
+ kScreen_Mode = kCoeffModesCnt,
kOverlay_Mode,
kDarken_Mode,
kLighten_Mode,
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 15def87942..601da09427 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -163,17 +163,26 @@ private:
// doesn't set the texture/sampler/matrix state
// caller needs to null out GrPaint's texture if
// non-textured drawing is desired.
+ // Set constantColor to true if a constant color
+ // will be used. This is an optimization, and can
+ // always be set to false. constantColor should
+ // never be true if justAlpha is true.
bool skPaint2GrPaintNoShader(const SkPaint& skPaint,
bool justAlpha,
- GrPaint* grPaint);
+ GrPaint* grPaint,
+ bool constantColor);
// uses the SkShader to setup paint, act used to
// hold lock on cached texture and free it when
// destroyed.
+ // If there is no shader, constantColor will
+ // be passed to skPaint2GrPaintNoShader. Otherwise
+ // it is ignored.
bool skPaint2GrPaintShader(const SkPaint& skPaint,
SkAutoCachedTexture* act,
const SkMatrix& ctm,
- GrPaint* grPaint);
+ GrPaint* grPaint,
+ bool constantColor);
SkDrawProcs* initDrawForText(GrTextContext*);
bool bindDeviceAsTexture(GrPaint* paint);
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 75099b2df6..65565c9b68 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -24,8 +24,8 @@
#include "GrConfig.h"
#include "GrContext.h"
#include "GrFontScaler.h"
-#include "GrPathIter.h"
#include "GrClipIterator.h"
+#include "GrPath.h"
// skia headers
#include "SkBitmap.h"
@@ -130,29 +130,6 @@ public:
////////////////////////////////////////////////////////////////////////////////
// Classes
-class SkGrPathIter : public GrPathIter {
-public:
- SkGrPathIter() { fPath = NULL; }
- SkGrPathIter(const SkPath& path) { reset(path); }
- virtual GrPathCmd next(GrPoint pts[]);
- virtual GrPathCmd next();
- virtual void rewind();
- virtual GrConvexHint convexHint() const;
- virtual bool getConservativeBounds(GrRect* rect) const;
-
- void reset(const SkPath& path) {
- fPath = &path;
- fIter.setPath(path, false);
- }
-private:
-
-#if !SK_SCALAR_IS_GR_SCALAR
- SkPoint fPoints[4];
-#endif
- SkPath::Iter fIter;
- const SkPath* fPath;
-};
-
class SkGrClipIterator : public GrClipIterator {
public:
SkGrClipIterator() { fClipStack = NULL; fCurr = NULL; }
@@ -176,9 +153,8 @@ public:
}
}
- virtual GrPathIter* getPathIter() {
- fPathIter.reset(*fCurr->fPath);
- return &fPathIter;
+ virtual const GrPath* getPath() {
+ return fCurr->fPath;
}
virtual GrPathFill getPathFill() const;
@@ -186,7 +162,6 @@ public:
private:
const SkClipStack* fClipStack;
SkClipStack::B2FIter fIter;
- SkGrPathIter fPathIter;
// SkClipStack's auto advances on each get
// so we store the current pos here.
const SkClipStack::B2FIter::Clip* fCurr;
@@ -218,7 +193,7 @@ public:
rect->fBottom = GrIntToScalar(r.fBottom);
}
- virtual GrPathIter* getPathIter() {
+ virtual const GrPath* getPath() {
SkASSERT(0);
return NULL;
}
diff --git a/include/pdf/SkPDFCatalog.h b/include/pdf/SkPDFCatalog.h
new file mode 100644
index 0000000000..e02ffa1a47
--- /dev/null
+++ b/include/pdf/SkPDFCatalog.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFCatalog_DEFINED
+#define SkPDFCatalog_DEFINED
+
+#include <sys/types.h>
+
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+/** \class SkPDFCatalog
+
+ The PDF catalog manages object numbers and file offsets. It is used
+ to create the PDF cross reference table.
+*/
+class SK_API SkPDFCatalog {
+public:
+ /** Create a PDF catalog.
+ */
+ SkPDFCatalog();
+ ~SkPDFCatalog();
+
+ /** Add the passed object to the catalog. Refs obj.
+ * @param obj The object to add.
+ * @param onFirstPage Is the object on the first page.
+ * @return The obj argument is returned.
+ */
+ SkPDFObject* addObject(SkPDFObject* obj, bool onFirstPage);
+
+ /** Inform the catalog of the object's position in the final stream.
+ * The object should already have been added to the catalog. Returns
+ * the object's size.
+ * @param obj The object to add.
+ * @param offset The byte offset in the output stream of this object.
+ */
+ size_t setFileOffset(SkPDFObject* obj, size_t offset);
+
+ /** Output the object number for the passed object.
+ * @param obj The object of interest.
+ * @param stream The writable output stream to send the output to.
+ */
+ void emitObjectNumber(SkWStream* stream, SkPDFObject* obj);
+
+ /** Return the number of bytes that would be emitted for the passed
+ * object's object number.
+ * @param obj The object of interest
+ */
+ size_t getObjectNumberSize(SkPDFObject* obj);
+
+ /** Output the cross reference table for objects in the catalog.
+ * Returns the total number of objects.
+ * @param stream The writable output stream to send the output to.
+ * @param firstPage If true, include first page objects only, otherwise
+ * include all objects not on the first page.
+ */
+ int32_t emitXrefTable(SkWStream* stream, bool firstPage);
+
+private:
+ struct Rec {
+ Rec(SkPDFObject* object, bool onFirstPage)
+ : fObject(object),
+ fFileOffset(0),
+ fObjNumAssigned(false),
+ fOnFirstPage(onFirstPage) {
+ }
+ SkPDFObject* fObject;
+ off_t fFileOffset;
+ bool fObjNumAssigned;
+ bool fOnFirstPage;
+ };
+
+ // TODO(vandebo) Make this a hash if it's a performance problem.
+ SkTDArray<struct Rec> fCatalog;
+
+ // Number of objects on the first page.
+ uint32_t fFirstPageCount;
+ // Next object number to assign (on page > 1).
+ uint32_t fNextObjNum;
+ // Next object number to assign on the first page.
+ uint32_t fNextFirstPageObjNum;
+
+ int findObjectIndex(SkPDFObject* obj) const;
+
+ int assignObjNum(SkPDFObject* obj);
+};
+
+#endif
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
new file mode 100644
index 0000000000..6e4d8db725
--- /dev/null
+++ b/include/pdf/SkPDFDevice.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFDevice_DEFINED
+#define SkPDFDevice_DEFINED
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTScopedPtr.h"
+
+class SkPDFArray;
+class SkPDFDevice;
+class SkPDFDict;
+class SkPDFFont;
+class SkPDFFormXObject;
+class SkPDFGraphicState;
+class SkPDFObject;
+class SkPDFShader;
+class SkPDFStream;
+
+// Private classes.
+struct ContentEntry;
+struct GraphicStateEntry;
+
+class SkPDFDeviceFactory : public SkDeviceFactory {
+public:
+ virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width,
+ int height, bool isOpaque, bool isForLayer);
+};
+
+/** \class SkPDFDevice
+
+ The drawing context for the PDF backend.
+*/
+class SkPDFDevice : public SkDevice {
+public:
+ /** Create a PDF drawing context with the given width and height.
+ * 72 points/in means letter paper is 612x792.
+ * @param pageSize Page size in points.
+ * @param contentSize The content size of the page in points. This will be
+ * combined with the initial transform to determine the drawing area
+ * (as reported by the width and height methods). Anything outside
+ * of the drawing area will be clipped.
+ * @param initialTransform The initial transform to apply to the page.
+ * This may be useful to, for example, move the origin in and
+ * over a bit to account for a margin, scale the canvas,
+ * or apply a rotation. Note1: the SkPDFDevice also applies
+ * a scale+translate transform to move the origin from the
+ * bottom left (PDF default) to the top left. Note2: drawDevice
+ * (used by layer restore) draws the device after this initial
+ * transform is applied, so the PDF device factory does an
+ * inverse scale+translate to accommodate the one that SkPDFDevice
+ * always does.
+ */
+ // TODO(vandebo) The sizes should be SkSize and not SkISize.
+ SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
+ const SkMatrix& initialTransform);
+ SK_API virtual ~SkPDFDevice();
+
+ virtual uint32_t getDeviceCapabilities() { return kVector_Capability; }
+
+ virtual void clear(SkColor color);
+
+ virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
+ return false;
+ }
+
+ /** These are called inside the per-device-layer loop for each draw call.
+ When these are called, we have already applied any saveLayer operations,
+ and are handling any looping from the paint, and any effects from the
+ DrawFilter.
+ */
+ virtual void drawPaint(const SkDraw&, const SkPaint& paint);
+ virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
+ size_t count, const SkPoint[],
+ const SkPaint& paint);
+ virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint);
+ virtual void drawPath(const SkDraw&, const SkPath& origpath,
+ const SkPaint& paint, const SkMatrix* prePathMatrix,
+ bool pathIsMutable);
+ virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+ const SkIRect* srcRectOrNull,
+ const SkMatrix& matrix, const SkPaint& paint);
+ virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint);
+ virtual void drawText(const SkDraw&, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint);
+ virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint);
+ virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint);
+ virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
+ int vertexCount, const SkPoint verts[],
+ const SkPoint texs[], const SkColor colors[],
+ SkXfermode* xmode, const uint16_t indices[],
+ int indexCount, const SkPaint& paint);
+ virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+ const SkPaint&);
+
+ // PDF specific methods.
+
+ /** Returns a reference to the resource dictionary for this device.
+ */
+ SK_API const SkRefPtr<SkPDFDict>& getResourceDict();
+
+ /** Get the list of resources (PDF objects) used on this page.
+ * @param resourceList A list to append the resources to.
+ */
+ SK_API void getResources(SkTDArray<SkPDFObject*>* resourceList) const;
+
+ /** Get the fonts used on this device.
+ */
+ SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
+
+ /** Returns the media box for this device.
+ */
+ SK_API SkRefPtr<SkPDFArray> getMediaBox() const;
+
+ /** Returns a SkStream with the page contents. The caller is responsible
+ for a reference to the returned value.
+ */
+ SK_API SkStream* content() const;
+
+ SK_API const SkMatrix& initialTransform() const {
+ return fInitialTransform;
+ }
+
+protected:
+ // override
+ virtual SkDeviceFactory* onNewDeviceFactory();
+
+private:
+ friend class SkPDFDeviceFactory;
+ // TODO(vandebo) push most of SkPDFDevice's state into a core object in
+ // order to get the right access levels without using friend.
+ friend class ScopedContentEntry;
+
+ SkISize fPageSize;
+ SkISize fContentSize;
+ SkMatrix fInitialTransform;
+ SkClipStack fExistingClipStack;
+ SkRegion fExistingClipRegion;
+ SkRefPtr<SkPDFDict> fResourceDict;
+
+ SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
+ SkTDArray<SkPDFObject*> fXObjectResources;
+ SkTDArray<SkPDFFont*> fFontResources;
+ SkTDArray<SkPDFShader*> fShaderResources;
+
+ SkTScopedPtr<ContentEntry> fContentEntries;
+ ContentEntry* fLastContentEntry;
+
+ // For use by the DeviceFactory.
+ SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
+ const SkRegion& existingClipRegion);
+
+ void init();
+ void cleanUp();
+ void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
+
+ // Clear the passed clip from all existing content entries.
+ void clearClipFromContent(const SkClipStack* clipStack,
+ const SkRegion& clipRegion);
+ void drawFormXObjectWithClip(SkPDFFormXObject* form,
+ const SkClipStack* clipStack,
+ const SkRegion& clipRegion,
+ bool invertClip);
+
+ // If the paint or clip is such that we shouldn't draw anything, this
+ // returns NULL and does not create a content entry.
+ // setUpContentEntry and finishContentEntry can be used directly, but
+ // the preferred method is to use the ScopedContentEntry helper class.
+ ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
+ const SkRegion& clipRegion,
+ const SkMatrix& matrix,
+ const SkPaint& paint,
+ bool hasText,
+ SkRefPtr<SkPDFFormXObject>* dst);
+ void finishContentEntry(SkXfermode::Mode xfermode,
+ SkPDFFormXObject* dst);
+ bool isContentEmpty();
+
+ void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
+ const SkClipStack& clipStack,
+ const SkRegion& clipRegion,
+ const SkPaint& paint,
+ bool hasText,
+ GraphicStateEntry* entry);
+ int addGraphicStateResource(SkPDFGraphicState* gs);
+
+ void updateFont(const SkPaint& paint, uint16_t glyphID,
+ ContentEntry* contentEntry);
+ int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
+
+ void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
+ void internalDrawBitmap(const SkMatrix& matrix,
+ const SkClipStack* clipStack,
+ const SkRegion& clipRegion,
+ const SkBitmap& bitmap,
+ const SkIRect* srcRect,
+ const SkPaint& paint);
+
+ // Disable the default copy and assign implementation.
+ SkPDFDevice(const SkPDFDevice&);
+ void operator=(const SkPDFDevice&);
+};
+
+#endif
diff --git a/include/pdf/SkPDFDocument.h b/include/pdf/SkPDFDocument.h
new file mode 100644
index 0000000000..0a76ea26aa
--- /dev/null
+++ b/include/pdf/SkPDFDocument.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFDocument_DEFINED
+#define SkPDFDocument_DEFINED
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+class SkPDFDevice;
+class SkPDFPage;
+class SkWSteam;
+
+/** \class SkPDFDocument
+
+ A SkPDFDocument assembles pages together and generates the final PDF file.
+*/
+class SkPDFDocument {
+public:
+ /** Create a PDF document.
+ */
+ SK_API SkPDFDocument();
+ SK_API ~SkPDFDocument();
+
+ /** Output the PDF to the passed stream.
+ * @param stream The writable output stream to send the PDF to.
+ */
+ SK_API bool emitPDF(SkWStream* stream);
+
+ /** Append the passed pdf device to the document as a new page. Returns
+ * true if successful. Will fail if the document has already been emitted.
+ *
+ * @param pdfDevice The page to add to this document.
+ */
+ SK_API bool appendPage(const SkRefPtr<SkPDFDevice>& pdfDevice);
+
+ /** Get the list of pages in this document.
+ */
+ SK_API const SkTDArray<SkPDFPage*>& getPages();
+
+private:
+ SkPDFCatalog fCatalog;
+ int64_t fXRefFileOffset;
+
+ SkTDArray<SkPDFPage*> fPages;
+ SkTDArray<SkPDFDict*> fPageTree;
+ SkRefPtr<SkPDFDict> fDocCatalog;
+ SkTDArray<SkPDFObject*> fPageResources;
+ int fSecondPageFirstResourceIndex;
+
+ SkRefPtr<SkPDFDict> fTrailerDict;
+
+ /** Output the PDF header to the passed stream.
+ * @param stream The writable output stream to send the header to.
+ */
+ void emitHeader(SkWStream* stream);
+
+ /** Get the size of the header.
+ */
+ size_t headerSize();
+
+ /** Output the PDF footer to the passed stream.
+ * @param stream The writable output stream to send the footer to.
+ * @param objCount The number of objects in the PDF.
+ */
+ void emitFooter(SkWStream* stream, int64_t objCount);
+};
+
+#endif
diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h
new file mode 100644
index 0000000000..93a72f173b
--- /dev/null
+++ b/include/pdf/SkPDFFont.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFFont_DEFINED
+#define SkPDFFont_DEFINED
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkPDFTypes.h"
+#include "SkTDArray.h"
+#include "SkThread.h"
+
+class SkPaint;
+
+/** \class SkPDFFont
+ A PDF Object class representing a font. The font may have resources
+ attached to it in order to embed the font. SkPDFFonts are canonicalized
+ so that resource deduplication will only include one copy of a font.
+ This class uses the same pattern as SkPDFGraphicState, a static weak
+ reference to each instantiated class.
+*/
+class SkPDFFont : public SkPDFDict {
+public:
+ SK_API virtual ~SkPDFFont();
+
+ SK_API virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+ /** Returns the typeface represented by this class. Returns NULL for the
+ * default typeface.
+ */
+ SK_API SkTypeface* typeface();
+
+ /** Returns the font type represented in this font. For Type0 fonts,
+ * returns the type of the decendant font.
+ */
+ SK_API SkAdvancedTypefaceMetrics::FontType getType();
+
+ /** Return true if this font has an encoding for the passed glyph id.
+ */
+ SK_API bool hasGlyph(uint16_t glyphID);
+
+ /** Returns true if this font encoding supports glyph IDs above 255.
+ */
+ SK_API bool multiByteGlyphs();
+
+ /** Convert (in place) the input glyph IDs into the font encoding. If the
+ * font has more glyphs than can be encoded (like a type 1 font with more
+ * than 255 glyphs) this method only converts up to the first out of range
+ * glyph ID.
+ * @param glyphIDs The input text as glyph IDs.
+ * @param numGlyphs The number of input glyphs.
+ * @return Returns the number of glyphs consumed.
+ */
+ SK_API size_t glyphsToPDFFontEncoding(uint16_t* glyphIDs, size_t numGlyphs);
+
+ /** Get the font resource for the passed typeface and glyphID. The
+ * reference count of the object is incremented and it is the caller's
+ * responsibility to unreference it when done. This is needed to
+ * accommodate the weak reference pattern used when the returned object
+ * is new and has no other references.
+ * @param typeface The typeface to find.
+ * @param glyphID Specify which section of a large font is of interest.
+ */
+ SK_API static SkPDFFont* getFontResource(SkTypeface* typeface,
+ uint16_t glyphID);
+
+private:
+ SkRefPtr<SkTypeface> fTypeface;
+ SkAdvancedTypefaceMetrics::FontType fType;
+#ifdef SK_DEBUG
+ bool fDescendant;
+#endif
+ bool fMultiByteGlyphs;
+
+ // The glyph IDs accessible with this font. For Type1 (non CID) fonts,
+ // this will be a subset if the font has more than 255 glyphs.
+ uint16_t fFirstGlyphID;
+ uint16_t fLastGlyphID;
+ // The font info is only kept around after construction for large
+ // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs.
+ SkRefPtr<SkAdvancedTypefaceMetrics> fFontInfo;
+ SkTDArray<SkPDFObject*> fResources;
+ SkRefPtr<SkPDFDict> fDescriptor;
+
+ class FontRec {
+ public:
+ SkPDFFont* fFont;
+ uint32_t fFontID;
+ uint16_t fGlyphID;
+
+ // A fGlyphID of 0 with no fFont always matches.
+ bool operator==(const FontRec& b) const;
+ FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID);
+ };
+
+ // This should be made a hash table if performance is a problem.
+ static SkTDArray<FontRec>& canonicalFonts();
+ static SkMutex& canonicalFontsMutex();
+
+ /** Construct a new font dictionary and support objects.
+ * @param fontInfo Information about the to create.
+ * @param typeface The typeface for the font.
+ * @param glyphID The glyph ID the caller is interested in. This
+ * is important only for Type1 fonts, which have
+ * more than 255 glyphs.
+ * @param descendantFont If this is the descendant (true) or root
+ * (Type 0 font - false) font dictionary. Only True
+ * Type and CID encoded fonts will use a true value.
+ * @param fontDescriptor If the font descriptor has already have generated
+ * for this font, pass it in here, otherwise pass
+ * NULL.
+ */
+ SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface,
+ uint16_t glyphID, bool descendantFont, SkPDFDict* fontDescriptor);
+
+ void populateType0Font();
+ void populateCIDFont();
+ bool populateType1Font(int16_t glyphID);
+
+ /** Populate the PDF font dictionary as Type3 font which includes glyph
+ * descriptions with instructions for painting the glyphs. This function
+ * doesn't use any fields from SkAdvancedTypefaceMetrics (fFontInfo). Font
+ * information including glyph paths are queried from the platform
+ * dependent SkGlyphCache.
+ */
+ void populateType3Font(int16_t glyphID);
+ bool addFontDescriptor(int16_t defaultWidth);
+ void populateToUnicodeTable();
+ void addWidthInfoFromRange(int16_t defaultWidth,
+ const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry);
+ /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
+ * including the passed glyphID.
+ */
+ void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID);
+
+ static bool find(uint32_t fontID, uint16_t glyphID, int* index);
+};
+
+#endif
diff --git a/include/pdf/SkPDFFormXObject.h b/include/pdf/SkPDFFormXObject.h
new file mode 100644
index 0000000000..41719f0820
--- /dev/null
+++ b/include/pdf/SkPDFFormXObject.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFFormXObject_DEFINED
+#define SkPDFFormXObject_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+class SkMatrix;
+class SkPDFDevice;
+class SkPDFCatalog;
+
+/** \class SkPDFFormXObject
+
+ A form XObject; a self contained description of graphics objects. A form
+ XObject is basically a page object with slightly different syntax, that
+ can be drawn onto a page.
+*/
+
+// The caller could keep track of the form XObjects it creates and
+// canonicalize them, but the Skia API doesn't provide enough context to
+// automatically do it (trivially).
+class SkPDFFormXObject : public SkPDFObject {
+public:
+ /** Create a PDF form XObject. Entries for the dictionary entries are
+ * automatically added.
+ * @param device The set of graphical elements on this form.
+ */
+ explicit SkPDFFormXObject(SkPDFDevice* device);
+ virtual ~SkPDFFormXObject();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+ virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+ /** Add the value to the stream dictionary with the given key. Refs value.
+ * @param key The key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* insert(SkPDFName* key, SkPDFObject* value);
+
+ /** Add the value to the stream dictionary with the given key. Refs value.
+ * @param key The text of the key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* insert(const char key[], SkPDFObject* value);
+
+private:
+ SkRefPtr<SkPDFStream> fStream;
+ SkTDArray<SkPDFObject*> fResources;
+};
+
+#endif
diff --git a/include/pdf/SkPDFGraphicState.h b/include/pdf/SkPDFGraphicState.h
new file mode 100644
index 0000000000..49809a4c03
--- /dev/null
+++ b/include/pdf/SkPDFGraphicState.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFGraphicState_DEFINED
+#define SkPDFGraphicState_DEFINED
+
+#include "SkPaint.h"
+#include "SkPDFTypes.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+class SkPDFFormXObject;
+
+/** \class SkPDFGraphicState
+ SkPaint objects roughly correspond to graphic state dictionaries that can
+ be installed. So that a given dictionary is only output to the pdf file
+ once, we want to canonicalize them. Static methods in this class manage
+ a weakly referenced set of SkPDFGraphicState objects: when the last
+ reference to a SkPDFGraphicState is removed, it removes itself from the
+ static set of objects.
+
+*/
+class SkPDFGraphicState : public SkPDFDict {
+public:
+ virtual ~SkPDFGraphicState();
+
+ virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+ // Override emitObject and getOutputSize so that we can populate
+ // the dictionary on demand.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ /** Get the graphic state for the passed SkPaint. The reference count of
+ * the object is incremented and it is the caller's responsibility to
+ * unreference it when done. This is needed to accommodate the weak
+ * reference pattern used when the returned object is new and has no
+ * other references.
+ * @param paint The SkPaint to emulate.
+ */
+ static SkPDFGraphicState* getGraphicStateForPaint(const SkPaint& paint);
+
+ /** Make a graphic state that only sets the passed soft mask. The
+ * reference count of the object is incremented and it is the caller's
+ * responsibility to unreference it when done.
+ * @param sMask The form xobject to use as a soft mask.
+ * @param invert Indicates if the alpha of the sMask should be inverted.
+ */
+ static SkPDFGraphicState* getSMaskGraphicState(SkPDFFormXObject* sMask,
+ bool invert);
+
+ /** Get a graphic state that only unsets the soft mask. The reference
+ * count of the object is incremented and it is the caller's responsibility
+ * to unreference it when done. This is needed to accommodate the weak
+ * reference pattern used when the returned object is new and has no
+ * other references.
+ */
+ static SkPDFGraphicState* getNoSMaskGraphicState();
+
+private:
+ const SkPaint fPaint;
+ SkTDArray<SkPDFObject*> fResources;
+ bool fPopulated;
+ bool fSMask;
+
+ class GSCanonicalEntry {
+ public:
+ SkPDFGraphicState* fGraphicState;
+ const SkPaint* fPaint;
+
+ bool operator==(const GSCanonicalEntry& b) const;
+ explicit GSCanonicalEntry(SkPDFGraphicState* gs)
+ : fGraphicState(gs),
+ fPaint(&gs->fPaint) {}
+ explicit GSCanonicalEntry(const SkPaint* paint) : fPaint(paint) {}
+ };
+
+ // This should be made a hash table if performance is a problem.
+ static SkTDArray<GSCanonicalEntry>& canonicalPaints();
+ static SkMutex& canonicalPaintsMutex();
+
+ SkPDFGraphicState();
+ explicit SkPDFGraphicState(const SkPaint& paint);
+
+ void populateDict();
+
+ static SkPDFObject* GetInvertFunction();
+
+ static int find(const SkPaint& paint);
+};
+
+#endif
diff --git a/include/pdf/SkPDFImage.h b/include/pdf/SkPDFImage.h
new file mode 100644
index 0000000000..945ff9e541
--- /dev/null
+++ b/include/pdf/SkPDFImage.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFImage_DEFINED
+#define SkPDFImage_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPDFCatalog;
+struct SkIRect;
+
+/** \class SkPDFImage
+
+ An image XObject.
+*/
+
+// We could play the same trick here as is done in SkPDFGraphicState, storing
+// a copy of the Bitmap object (not the pixels), the pixel generation number,
+// and settings used from the paint to canonicalize image objects.
+class SkPDFImage : public SkPDFObject {
+public:
+ /** Create a new Image XObject to represent the passed bitmap.
+ * @param bitmap The image to encode.
+ * @param srcRect The rectangle to cut out of bitmap.
+ * @param paint Used to calculate alpha, masks, etc.
+ * @return The image XObject or NUll if there is nothing to draw for
+ * the given parameters.
+ */
+ static SkPDFImage* CreateImage(const SkBitmap& bitmap,
+ const SkIRect& srcRect,
+ const SkPaint& paint);
+
+ virtual ~SkPDFImage();
+
+ /** Add a Soft Mask (alpha or shape channel) to the image. Refs mask.
+ * @param mask A gray scale image representing the mask.
+ * @return The mask argument is returned.
+ */
+ SkPDFImage* addSMask(SkPDFImage* mask);
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+ virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+private:
+ SkRefPtr<SkPDFStream> fStream;
+ SkTDArray<SkPDFObject*> fResources;
+
+ /** Create a PDF image XObject. Entries for the image properties are
+ * automatically added to the stream dictionary.
+ * @param imageData The final raw bits representing the image.
+ * @param bitmap The image parameters to use (Config, etc).
+ * @param srcRect The clipping applied to bitmap before generating
+ * imageData.
+ * @param alpha Is this the alpha channel of the bitmap.
+ * @param paint Used to calculate alpha, masks, etc.
+ */
+ SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
+ const SkIRect& srcRect, bool alpha, const SkPaint& paint);
+
+ /** Add the value to the stream dictionary with the given key. Refs value.
+ * @param key The key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* insert(SkPDFName* key, SkPDFObject* value);
+
+ /** Add the value to the stream dictionary with the given key. Refs value.
+ * @param key The text of the key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* insert(const char key[], SkPDFObject* value);
+};
+
+#endif
diff --git a/include/pdf/SkPDFPage.h b/include/pdf/SkPDFPage.h
new file mode 100644
index 0000000000..0e30028ae7
--- /dev/null
+++ b/include/pdf/SkPDFPage.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFPage_DEFINED
+#define SkPDFPage_DEFINED
+
+#include "SkPDFTypes.h"
+#include "SkPDFStream.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+
+class SkPDFCatalog;
+class SkPDFDevice;
+class SkWStream;
+
+/** \class SkPDFPage
+
+ A SkPDFPage contains meta information about a page, is used in the page
+ tree and points to the content of the page.
+*/
+class SkPDFPage : public SkPDFDict {
+public:
+ /** Create a PDF page with the passed PDF device. The device need not
+ * have content on it yet.
+ * @param content The page content.
+ */
+ explicit SkPDFPage(const SkRefPtr<SkPDFDevice>& content);
+ ~SkPDFPage();
+
+ /** Before a page and its contents can be sized and emitted, it must
+ * be finalized. No changes to the PDFDevice will be honored after
+ * finalizePage has been called. This function adds the page content
+ * to the passed catalog, so it must be called for each document
+ * that the page is part of.
+ * @param catalog The catalog to add page content objects to.
+ * @param firstPage Indicate if this is the first page of a document.
+ * @param resourceObjects The resource objects used on the page are added
+ * to this array. This gives the caller a chance
+ * to deduplicate resources across pages.
+ */
+ void finalizePage(SkPDFCatalog* catalog, bool firstPage,
+ SkTDArray<SkPDFObject*>* resourceObjects);
+
+ /** Determine the size of the page content and store to the catalog
+ * the offsets of all nonresource-indirect objects that make up the page
+ * content. This must be called before emitPage(), but after finalizePage.
+ * @param catalog The catalog to add the object offsets to.
+ * @param fileOffset The file offset where the page content will be
+ * emitted.
+ */
+ off_t getPageSize(SkPDFCatalog* catalog, off_t fileOffset);
+
+ /** Output the page content to the passed stream.
+ * @param stream The writable output stream to send the content to.
+ * @param catalog The active object catalog.
+ */
+ void emitPage(SkWStream* stream, SkPDFCatalog* catalog);
+
+ /** Generate a page tree for the passed vector of pages. New objects are
+ * added to the catalog. The pageTree vector is populated with all of
+ * the 'Pages' dictionaries as well as the 'Page' objects. Page trees
+ * have both parent and children links, creating reference cycles, so
+ * it must be torn down explicitly. The first page is not added to
+ * the pageTree dictionary array so the caller can handle it specially.
+ * @param pages The ordered vector of page objects.
+ * @param catalog The catalog to add new objects into.
+ * @param pageTree An output vector with all of the internal and leaf
+ * nodes of the pageTree.
+ * @param rootNode An output parameter set to the root node.
+ */
+ static void generatePageTree(const SkTDArray<SkPDFPage*>& pages,
+ SkPDFCatalog* catalog,
+ SkTDArray<SkPDFDict*>* pageTree,
+ SkPDFDict** rootNode);
+
+ /** Get the fonts used on this page.
+ */
+ SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
+
+private:
+ // Multiple pages may reference the content.
+ SkRefPtr<SkPDFDevice> fDevice;
+
+ // Once the content is finalized, put it into a stream for output.
+ SkRefPtr<SkPDFStream> fContentStream;
+};
+
+#endif
diff --git a/include/pdf/SkPDFShader.h b/include/pdf/SkPDFShader.h
new file mode 100644
index 0000000000..17f7f03316
--- /dev/null
+++ b/include/pdf/SkPDFShader.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFShader_DEFINED
+#define SkPDFShader_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkMatrix.h"
+#include "SkRefCnt.h"
+#include "SkShader.h"
+
+class SkObjRef;
+class SkPDFCatalog;
+
+/** \class SkPDFShader
+
+ In PDF parlance, this is a pattern, used in place of a color when the
+ pattern color space is selected.
+*/
+
+class SkPDFShader : public SkPDFObject {
+public:
+ virtual ~SkPDFShader();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+ virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+ /** Get the PDF shader for the passed SkShader. If the SkShader is
+ * invalid in some way, returns NULL. The reference count of
+ * the object is incremented and it is the caller's responsibility to
+ * unreference it when done. This is needed to accommodate the weak
+ * reference pattern used when the returned object is new and has no
+ * other references.
+ * @param shader The SkShader to emulate.
+ * @param matrix The current transform. (PDF shaders are absolutely
+ * positioned, relative to where the page is drawn.)
+ * @param surfceBBox The bounding box of the drawing surface (with matrix
+ * already applied).
+ */
+ static SkPDFShader* getPDFShader(const SkShader& shader,
+ const SkMatrix& matrix,
+ const SkIRect& surfaceBBox);
+
+private:
+ class State {
+ public:
+ SkShader::GradientType fType;
+ SkShader::GradientInfo fInfo;
+ SkAutoFree fColorData;
+ SkMatrix fCanvasTransform;
+ SkMatrix fShaderTransform;
+ SkIRect fBBox;
+
+ SkBitmap fImage;
+ uint32_t fPixelGeneration;
+ SkShader::TileMode fImageTileModes[2];
+
+ explicit State(const SkShader& shader, const SkMatrix& canvasTransform,
+ const SkIRect& bbox);
+ bool operator==(const State& b) const;
+ };
+
+ SkRefPtr<SkPDFDict> fContent;
+ SkTDArray<SkPDFObject*> fResources;
+ SkAutoTDelete<const State> fState;
+
+ class ShaderCanonicalEntry {
+ public:
+ SkPDFShader* fPDFShader;
+ const State* fState;
+
+ bool operator==(const ShaderCanonicalEntry& b) const {
+ return fPDFShader == b.fPDFShader || *fState == *b.fState;
+ }
+ ShaderCanonicalEntry(SkPDFShader* pdfShader, const State* state)
+ : fPDFShader(pdfShader),
+ fState(state) {
+ }
+ };
+ // This should be made a hash table if performance is a problem.
+ static SkTDArray<ShaderCanonicalEntry>& canonicalShaders();
+ static SkMutex& canonicalShadersMutex();
+
+ static SkPDFObject* rangeObject();
+
+ SkPDFShader(State* state);
+
+ void doFunctionShader();
+ void doImageShader();
+ SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain);
+};
+
+#endif
diff --git a/include/pdf/SkPDFStream.h b/include/pdf/SkPDFStream.h
new file mode 100644
index 0000000000..a975ad6efc
--- /dev/null
+++ b/include/pdf/SkPDFStream.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFStream_DEFINED
+#define SkPDFStream_DEFINED
+
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+class SkPDFCatalog;
+
+/** \class SkPDFStream
+
+ A stream object in a PDF. Note, all streams must be indirect objects (via
+ SkObjRef).
+*/
+class SkPDFStream : public SkPDFDict {
+public:
+ /** Create a PDF stream. A Length entry is automatically added to the
+ * stream dictionary.
+ * @param stream The data part of the stream.
+ */
+ explicit SkPDFStream(SkStream* stream);
+ virtual ~SkPDFStream();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+ size_t fLength;
+ // Only one of the two streams will be valid.
+ SkRefPtr<SkStream> fPlainData;
+ SkDynamicMemoryWStream fCompressedData;
+
+ typedef SkPDFDict INHERITED;
+};
+
+#endif
diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h
new file mode 100644
index 0000000000..6b5146a2b7
--- /dev/null
+++ b/include/pdf/SkPDFTypes.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFTypes_DEFINED
+#define SkPDFTypes_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+
+class SkPDFCatalog;
+class SkWStream;
+
+/** \class SkPDFObject
+
+ A PDF Object is the base class for primitive elements in a PDF file. A
+ common subtype is used to ease the use of indirect object references,
+ which are common in the PDF format.
+*/
+class SkPDFObject : public SkRefCnt {
+public:
+ /** Create a PDF object.
+ */
+ SkPDFObject();
+ virtual ~SkPDFObject();
+
+ /** Subclasses must implement this method to print the object to the
+ * PDF file.
+ * @param catalog The object catalog to use.
+ * @param indirect If true, output an object identifier with the object.
+ * @param stream The writable output stream to send the output to.
+ */
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) = 0;
+
+ /** Return the size (number of bytes) of this object in the final output
+ * file. Compound objects or objects that are computationally intensive
+ * to output should override this method.
+ * @param catalog The object catalog to use.
+ * @param indirect If true, output an object identifier with the object.
+ */
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ /** If this object explicitly depends on other objects, add them to the
+ * end of the list. This only applies to higher level object, where
+ * the depenency is explicit and introduced by the class. i.e. an
+ * SkPDFImage added to an SkPDFDevice, but not an SkPDFObjRef added to
+ * an SkPDFArray.
+ * @param resourceList The list to append dependant resources to.
+ */
+ virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+ /** Helper function to output an indirect object.
+ * @param catalog The object catalog to use.
+ * @param stream The writable output stream to send the output to.
+ */
+ void emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog);
+
+ /** Helper function to find the size of an indirect object.
+ * @param catalog The object catalog to use.
+ */
+ size_t getIndirectOutputSize(SkPDFCatalog* catalog);
+};
+
+/** \class SkPDFObjRef
+
+ An indirect reference to a PDF object.
+*/
+class SkPDFObjRef : public SkPDFObject {
+public:
+ /** Create a reference to an existing SkPDFObject.
+ * @param obj The object to reference.
+ */
+ explicit SkPDFObjRef(SkPDFObject* obj);
+ virtual ~SkPDFObjRef();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+ SkRefPtr<SkPDFObject> fObj;
+};
+
+/** \class SkPDFInt
+
+ An integer object in a PDF.
+*/
+class SkPDFInt : public SkPDFObject {
+public:
+ /** Create a PDF integer (usually for indirect reference purposes).
+ * @param value An integer value between 2^31 - 1 and -2^31.
+ */
+ explicit SkPDFInt(int32_t value);
+ virtual ~SkPDFInt();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+
+private:
+ int32_t fValue;
+};
+
+/** \class SkPDFBool
+
+ An boolean value in a PDF.
+*/
+class SkPDFBool : public SkPDFObject {
+public:
+ /** Create a PDF boolean.
+ * @param value true or false.
+ */
+ explicit SkPDFBool(bool value);
+ virtual ~SkPDFBool();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+ bool fValue;
+};
+
+/** \class SkPDFScalar
+
+ A real number object in a PDF.
+*/
+class SkPDFScalar : public SkPDFObject {
+public:
+ /** Create a PDF real number.
+ * @param value A real value.
+ */
+ explicit SkPDFScalar(SkScalar value);
+ virtual ~SkPDFScalar();
+
+ static void Append(SkScalar value, SkWStream* stream);
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+
+private:
+ SkScalar fValue;
+};
+
+/** \class SkPDFString
+
+ A string object in a PDF.
+*/
+class SkPDFString : public SkPDFObject {
+public:
+ /** Create a PDF string. Maximum length (in bytes) is 65,535.
+ * @param value A string value.
+ */
+ explicit SkPDFString(const char value[]);
+ explicit SkPDFString(const SkString& value);
+
+ /** Create a PDF string. Maximum length (in bytes) is 65,535.
+ * @param value A string value.
+ * @param len The length of value.
+ * @param wideChars Indicates if the top byte in value is significant and
+ * should be encoded (true) or not (false).
+ */
+ SkPDFString(const uint16_t* value, size_t len, bool wideChars);
+ virtual ~SkPDFString();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ static SkString formatString(const char* input, size_t len);
+ static SkString formatString(const uint16_t* input, size_t len,
+ bool wideChars);
+private:
+ static const size_t kMaxLen = 65535;
+
+ const SkString fValue;
+
+ static SkString doFormatString(const void* input, size_t len,
+ bool wideInput, bool wideOutput);
+};
+
+/** \class SkPDFName
+
+ A name object in a PDF.
+*/
+class SkPDFName : public SkPDFObject {
+public:
+ /** Create a PDF name object. Maximum length is 127 bytes.
+ * @param value The name.
+ */
+ explicit SkPDFName(const char name[]);
+ explicit SkPDFName(const SkString& name);
+ virtual ~SkPDFName();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+ static const size_t kMaxLen = 127;
+
+ const SkString fValue;
+
+ static SkString formatName(const SkString& input);
+};
+
+/** \class SkPDFArray
+
+ An array object in a PDF.
+*/
+class SkPDFArray : public SkPDFObject {
+public:
+ /** Create a PDF array. Maximum length is 8191.
+ */
+ SkPDFArray();
+ virtual ~SkPDFArray();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ /** The size of the array.
+ */
+ int size() { return fValue.count(); }
+
+ /** Preallocate space for the given number of entries.
+ * @param length The number of array slots to preallocate.
+ */
+ void reserve(int length);
+
+ /** Returns the object at the given offset in the array.
+ * @param index The index into the array to retrieve.
+ */
+ SkPDFObject* getAt(int index) { return fValue[index]; }
+
+ /** Set the object at the given offset in the array. Ref's value.
+ * @param index The index into the array to set.
+ * @param value The value to add to the array.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* setAt(int index, SkPDFObject* value);
+
+ /** Append the object to the end of the array and increments its ref count.
+ * @param value The value to add to the array.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* append(SkPDFObject* value);
+
+private:
+ static const int kMaxLen = 8191;
+ SkTDArray<SkPDFObject*> fValue;
+};
+
+/** \class SkPDFDict
+
+ A dictionary object in a PDF.
+*/
+class SkPDFDict : public SkPDFObject {
+public:
+ /** Create a PDF dictionary. Maximum number of entries is 4095.
+ */
+ SkPDFDict();
+
+ /** Create a PDF dictionary with a Type entry.
+ * @param type The value of the Type entry.
+ */
+ explicit SkPDFDict(const char type[]);
+
+ virtual ~SkPDFDict();
+
+ // The SkPDFObject interface.
+ virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect);
+ virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+ /** The size of the dictionary.
+ */
+ int size() { return fValue.count(); }
+
+ /** Add the value to the dictionary with the given key. Refs value.
+ * @param key The key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* insert(SkPDFName* key, SkPDFObject* value);
+
+ /** Add the value to the dictionary with the given key. Refs value. The
+ * method will create the SkPDFName object.
+ * @param key The text of the key for this dictionary entry.
+ * @param value The value for this dictionary entry.
+ * @return The value argument is returned.
+ */
+ SkPDFObject* insert(const char key[], SkPDFObject* value);
+
+ /** Remove all entries from the dictionary.
+ */
+ void clear();
+
+private:
+ static const int kMaxLen = 4095;
+
+ struct Rec {
+ SkPDFName* key;
+ SkPDFObject* value;
+ };
+
+ SkTDArray<struct Rec> fValue;
+};
+
+#endif
diff --git a/include/pdf/SkPDFUtils.h b/include/pdf/SkPDFUtils.h
new file mode 100644
index 0000000000..50da28cff0
--- /dev/null
+++ b/include/pdf/SkPDFUtils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkPDFUtils_DEFINED
+#define SkPDFUtils_DEFINED
+
+#include "SkPath.h"
+
+class SkMatrix;
+class SkPath;
+class SkPDFArray;
+struct SkRect;
+
+#if 0
+#define PRINT_NOT_IMPL(str) fprintf(stderr, str)
+#else
+#define PRINT_NOT_IMPL(str)
+#endif
+
+#define NOT_IMPLEMENTED(condition, assert) \
+ do { \
+ if (condition) { \
+ PRINT_NOT_IMPL("NOT_IMPLEMENTED: " #condition "\n"); \
+ SkDEBUGCODE(SkASSERT(!assert);) \
+ } \
+ } while(0)
+
+class SkPDFUtils {
+public:
+ static SkPDFArray* MatrixToArray(const SkMatrix& matrix);
+ static void AppendTransform(const SkMatrix& matrix, SkWStream* content);
+
+ static void MoveTo(SkScalar x, SkScalar y, SkWStream* content);
+ static void AppendLine(SkScalar x, SkScalar y, SkWStream* content);
+ static void AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+ SkScalar ctl2X, SkScalar ctl2Y,
+ SkScalar dstX, SkScalar dstY, SkWStream* content);
+ static void AppendRectangle(const SkRect& rect, SkWStream* content);
+ static void EmitPath(const SkPath& path, SkWStream* content);
+ static void ClosePath(SkWStream* content);
+ static void PaintPath(SkPaint::Style style, SkPath::FillType fill,
+ SkWStream* content);
+ static void StrokePath(SkWStream* content);
+ static void DrawFormXObject(int objectIndex, SkWStream* content);
+ static void ApplyGraphicState(int objectIndex, SkWStream* content);
+};
+
+#endif
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
new file mode 100644
index 0000000000..897766f08e
--- /dev/null
+++ b/include/pipe/SkGPipe.h
@@ -0,0 +1,96 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#ifndef SkGPipe_DEFINED
+#define SkGPipe_DEFINED
+
+#include "SkWriter32.h"
+#include "SkFlattenable.h"
+
+class SkCanvas;
+
+class SkGPipeReader {
+public:
+ SkGPipeReader(SkCanvas* target);
+ ~SkGPipeReader();
+
+ enum Status {
+ kDone_Status, //!< no more data expected from reader
+ kEOF_Status, //!< need more data from reader
+ kError_Status //!< encountered error
+ };
+
+ // data must be 4-byte aligned
+ // length must be a multiple of 4
+ Status playback(const void* data, size_t length, size_t* bytesRead = NULL);
+
+private:
+ SkCanvas* fCanvas;
+ class SkGPipeState* fState;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeController {
+public:
+ /**
+ * Called periodically by the writer, to get a working buffer of RAM to
+ * write into. The actual size of the block is also returned, and must be
+ * actual >= minRequest. If NULL is returned, then actual is ignored and
+ * writing will stop.
+ *
+ * The returned block must be 4-byte aligned, and actual must be a
+ * multiple of 4.
+ * minRequest will always be a multiple of 4.
+ */
+ virtual void* requestBlock(size_t minRequest, size_t* actual) = 0;
+
+ /**
+ * This is called each time some atomic portion of the data has been
+ * written to the block (most recently returned by requestBlock()).
+ * If bytes == 0, then the writer has finished.
+ *
+ * bytes will always be a multiple of 4.
+ */
+ virtual void notifyWritten(size_t bytes) = 0;
+};
+
+class SkGPipeWriter {
+public:
+ SkGPipeWriter();
+ ~SkGPipeWriter();
+
+ bool isRecording() const { return NULL != fCanvas; }
+
+ enum Flags {
+ kCrossProcess_Flag = 1 << 0,
+ };
+
+ SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0);
+
+ // called in destructor, but can be called sooner once you know there
+ // should be no more drawing calls made into the recording canvas.
+ void endRecording();
+
+private:
+ class SkGPipeCanvas* fCanvas;
+ SkGPipeController* fController;
+ SkFactorySet fFactorySet;
+ SkWriter32 fWriter;
+};
+
+#endif
diff --git a/include/utils/SkEGLContext.h b/include/utils/SkEGLContext.h
index 4b17be1d4a..ced31a5b85 100644
--- a/include/utils/SkEGLContext.h
+++ b/include/utils/SkEGLContext.h
@@ -1,20 +1,53 @@
#ifndef SkEGLContext_DEFINED
#define SkEGLContext_DEFINED
-#include "SkTypes.h"
+#if defined(SK_MESA)
+ #include "GL/osmesa.h"
+#elif defined(SK_BUILD_FOR_MAC)
+ #include <AGL/agl.h>
+#elif defined(SK_BUILD_FOR_ANDROID)
+ #include "GLES2/gl2.h"
+ #include "EGL/egl.h"
+#elif defined(SK_BUILD_FOR_UNIX)
+ #include <X11/Xlib.h>
+ #include <GL/glx.h>
+#elif defined(SK_BUILD_FOR_WIN32)
+ #include <Windows.h>
+ #include <GL/GL.h>
+#else
+
+#endif
/**
* Create an offscreen opengl context
*/
class SkEGLContext {
public:
- SkEGLContext();
- ~SkEGLContext();
+ SkEGLContext();
+ ~SkEGLContext();
- bool init(int width, int height);
+ bool init(const int width, const int height);
private:
- void* fContext;
+#if defined(SK_MESA)
+ OSMesaContext context;
+ GLfloat *image;
+#elif defined(SK_BUILD_FOR_MAC)
+ AGLContext context;
+#elif defined(SK_BUILD_FOR_ANDROID)
+
+#elif defined(SK_BUILD_FOR_UNIX)
+ GLXContext context;
+ Display *display;
+ Pixmap pixmap;
+ GLXPixmap glxPixmap;
+#elif defined(SK_BUILD_FOR_WIN32)
+ HWND fWindow;
+ HDC fDeviceContext;
+ HGLRC fGlRenderContext;
+#else
+
+#endif
};
#endif
diff --git a/include/utils/android/AndroidKeyToSkKey.h b/include/utils/android/AndroidKeyToSkKey.h
new file mode 100644
index 0000000000..7b0a03570e
--- /dev/null
+++ b/include/utils/android/AndroidKeyToSkKey.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_TO_SKIA_KEYCODES_H
+#define _ANDROID_TO_SKIA_KEYCODES_H
+
+#include "keycodes.h"
+#include "SkKey.h"
+
+// Convert an Android keycode to an SkKey. This is an incomplete list, only
+// including keys used by the sample app.
+SkKey AndroidKeycodeToSkKey(int keycode) {
+ switch (keycode) {
+ case AKEYCODE_DPAD_LEFT:
+ return kLeft_SkKey;
+ case AKEYCODE_DPAD_RIGHT:
+ return kRight_SkKey;
+ case AKEYCODE_DPAD_UP:
+ return kUp_SkKey;
+ case AKEYCODE_DPAD_DOWN:
+ return kDown_SkKey;
+ default:
+ return kNONE_SkKey;
+ }
+}
+
+#endif
diff --git a/include/utils/unix/XkeysToSkKeys.h b/include/utils/unix/XkeysToSkKeys.h
index 3d41a22e5c..1852d99346 100644
--- a/include/utils/unix/XkeysToSkKeys.h
+++ b/include/utils/unix/XkeysToSkKeys.h
@@ -8,6 +8,14 @@
SkKey XKeyToSkKey(KeySym keysym) {
switch (keysym) {
+ case XK_BackSpace:
+ return kBack_SkKey;
+ case XK_Return:
+ return kOK_SkKey;
+ case XK_Home:
+ return kHome_SkKey;
+ case XK_End:
+ return kEnd_SkKey;
case XK_Right:
return kRight_SkKey;
case XK_Left:
diff --git a/include/views/SkApplication.h b/include/views/SkApplication.h
new file mode 100644
index 0000000000..4c4a4fb0d1
--- /dev/null
+++ b/include/views/SkApplication.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkApplication_DEFINED
+#define SkApplication_DEFINED
+
+class SkOSWindow;
+
+extern SkOSWindow* create_sk_window(void* hwnd);
+extern void application_init();
+extern void application_term();
+
+#endif // SkApplication_DEFINED
diff --git a/include/views/SkBGViewArtist.h b/include/views/SkBGViewArtist.h
new file mode 100644
index 0000000000..1bca42fa52
--- /dev/null
+++ b/include/views/SkBGViewArtist.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkBGViewArtist_DEFINED
+#define SkBGViewArtist_DEFINED
+
+#include "SkView.h"
+#include "SkPaint.h"
+
+class SkBGViewArtist : public SkView::Artist {
+public:
+ SkBGViewArtist(SkColor c = SK_ColorWHITE);
+ virtual ~SkBGViewArtist();
+
+ const SkPaint& paint() const { return fPaint; }
+ SkPaint& paint() { return fPaint; }
+
+protected:
+ // overrides
+ virtual void onDraw(SkView*, SkCanvas*);
+ virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+ SkPaint fPaint;
+};
+
+#endif
+
diff --git a/include/views/SkBorderView.h b/include/views/SkBorderView.h
new file mode 100644
index 0000000000..94ccc1f0a5
--- /dev/null
+++ b/include/views/SkBorderView.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkBorderView_DEFINED
+#define SkBorderView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkBorderView : public SkWidgetView {
+public:
+ SkBorderView();
+ ~SkBorderView();
+ void setSkin(const char skin[]);
+ SkScalar getLeft() const { return fLeft; }
+ SkScalar getRight() const { return fRight; }
+ SkScalar getTop() const { return fTop; }
+ SkScalar getBottom() const { return fBottom; }
+protected:
+ //overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+ virtual void onSizeChange();
+ virtual void onDraw(SkCanvas* canvas);
+ virtual bool onEvent(const SkEvent& evt);
+private:
+ SkAnimator fAnim;
+ SkScalar fLeft, fRight, fTop, fBottom; //margin on each side
+ SkRect fMargin;
+
+ typedef SkWidgetView INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkEvent.h b/include/views/SkEvent.h
new file mode 100644
index 0000000000..f6719d66da
--- /dev/null
+++ b/include/views/SkEvent.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkEvent_DEFINED
+#define SkEvent_DEFINED
+
+#include "SkDOM.h"
+#include "SkMetaData.h"
+#include "SkString.h"
+
+/** Unique 32bit id used to identify an instance of SkEventSink. When events are
+ posted, they are posted to a specific sinkID. When it is time to dispatch the
+ event, the sinkID is used to find the specific SkEventSink object. If it is found,
+ its doEvent() method is called with the event.
+*/
+typedef uint32_t SkEventSinkID;
+
+/** \class SkEvent
+
+ SkEvents are used to communicate type-safe information to SkEventSinks.
+ SkEventSinks (including SkViews) each have a unique ID, which is stored
+ in an event. This ID is used to target the event once it has been "posted".
+*/
+class SkEvent {
+public:
+ /** Default construct, creating an empty event.
+ */
+ SkEvent();
+ /** Construct a new event with the specified type.
+ */
+ explicit SkEvent(const SkString& type);
+ /** Construct a new event with the specified type.
+ */
+ explicit SkEvent(const char type[]);
+ /** Construct a new event by copying the fields from the src event.
+ */
+ SkEvent(const SkEvent& src);
+ ~SkEvent();
+
+// /** Return the event's type (will never be null) */
+// const char* getType() const;
+ /** Copy the event's type into the specified SkString parameter */
+ void getType(SkString* str) const;
+ /** Returns true if the event's type matches exactly the specified type (case sensitive) */
+ bool isType(const SkString& str) const;
+ /** Returns true if the event's type matches exactly the specified type (case sensitive) */
+ bool isType(const char type[], size_t len = 0) const;
+ /** Set the event's type to the specified string.
+ In XML, use the "type" attribute.
+ */
+ void setType(const SkString&);
+ /** Set the event's type to the specified string.
+ In XML, use the "type" attribute.
+ */
+ void setType(const char type[], size_t len = 0);
+
+ /** Return the event's unnamed 32bit field. Default value is 0 */
+ uint32_t getFast32() const { return f32; }
+ /** Set the event's unnamed 32bit field. In XML, use
+ the subelement <data fast32=... />
+ */
+ void setFast32(uint32_t x) { f32 = x; }
+
+ /** Return true if the event contains the named 32bit field, and return the field
+ in value (if value is non-null). If there is no matching named field, return false
+ and ignore the value parameter.
+ */
+ bool findS32(const char name[], int32_t* value = NULL) const { return fMeta.findS32(name, value); }
+ /** Return true if the event contains the named SkScalar field, and return the field
+ in value (if value is non-null). If there is no matching named field, return false
+ and ignore the value parameter.
+ */
+ bool findScalar(const char name[], SkScalar* value = NULL) const { return fMeta.findScalar(name, value); }
+ /** Return true if the event contains the named SkScalar field, and return the fields
+ in value[] (if value is non-null), and return the number of SkScalars in count (if count is non-null).
+ If there is no matching named field, return false and ignore the value and count parameters.
+ */
+ const SkScalar* findScalars(const char name[], int* count, SkScalar values[] = NULL) const { return fMeta.findScalars(name, count, values); }
+ /** Return the value of the named string field, or if no matching named field exists, return null.
+ */
+ const char* findString(const char name[]) const { return fMeta.findString(name); }
+ /** Return true if the event contains the named pointer field, and return the field
+ in value (if value is non-null). If there is no matching named field, return false
+ and ignore the value parameter.
+ */
+ bool findPtr(const char name[], void** value) const { return fMeta.findPtr(name, value); }
+ bool findBool(const char name[], bool* value) const { return fMeta.findBool(name, value); }
+ const void* findData(const char name[], size_t* byteCount = NULL) const {
+ return fMeta.findData(name, byteCount);
+ }
+
+ /** Returns true if ethe event contains the named 32bit field, and if it equals the specified value */
+ bool hasS32(const char name[], int32_t value) const { return fMeta.hasS32(name, value); }
+ /** Returns true if ethe event contains the named SkScalar field, and if it equals the specified value */
+ bool hasScalar(const char name[], SkScalar value) const { return fMeta.hasScalar(name, value); }
+ /** Returns true if ethe event contains the named string field, and if it equals (using strcmp) the specified value */
+ bool hasString(const char name[], const char value[]) const { return fMeta.hasString(name, value); }
+ /** Returns true if ethe event contains the named pointer field, and if it equals the specified value */
+ bool hasPtr(const char name[], void* value) const { return fMeta.hasPtr(name, value); }
+ bool hasBool(const char name[], bool value) const { return fMeta.hasBool(name, value); }
+ bool hasData(const char name[], const void* data, size_t byteCount) const {
+ return fMeta.hasData(name, data, byteCount);
+ }
+
+ /** Add/replace the named 32bit field to the event. In XML use the subelement <data name=... s32=... /> */
+ void setS32(const char name[], int32_t value) { fMeta.setS32(name, value); }
+ /** Add/replace the named SkScalar field to the event. In XML use the subelement <data name=... scalar=... /> */
+ void setScalar(const char name[], SkScalar value) { fMeta.setScalar(name, value); }
+ /** Add/replace the named SkScalar[] field to the event. */
+ SkScalar* setScalars(const char name[], int count, const SkScalar values[] = NULL) { return fMeta.setScalars(name, count, values); }
+ /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */
+ void setString(const char name[], const SkString& value) { fMeta.setString(name, value.c_str()); }
+ /** Add/replace the named string field to the event. In XML use the subelement <data name=... string=... */
+ void setString(const char name[], const char value[]) { fMeta.setString(name, value); }
+ /** Add/replace the named pointer field to the event. There is no XML equivalent for this call */
+ void setPtr(const char name[], void* value) { fMeta.setPtr(name, value); }
+ void setBool(const char name[], bool value) { fMeta.setBool(name, value); }
+ void setData(const char name[], const void* data, size_t byteCount) {
+ fMeta.setData(name, data, byteCount);
+ }
+
+ /** Return the underlying metadata object */
+ SkMetaData& getMetaData() { return fMeta; }
+ /** Return the underlying metadata object */
+ const SkMetaData& getMetaData() const { return fMeta; }
+
+ void tron() { SkDEBUGCODE(fDebugTrace = true;) }
+ void troff() { SkDEBUGCODE(fDebugTrace = false;) }
+ bool isDebugTrace() const
+ {
+#ifdef SK_DEBUG
+ return fDebugTrace;
+#else
+ return false;
+#endif
+ }
+
+ /** Call this to initialize the event from the specified XML node */
+ void inflate(const SkDOM&, const SkDOM::Node*);
+
+ SkDEBUGCODE(void dump(const char title[] = NULL);)
+
+ /** Post the specified event to the event queue, targeting the specified eventsink, with an optional
+ delay. The event must be dynamically allocated for this. It cannot be a global or on the stack.
+ After this call, ownership is transfered to the system, so the caller must not retain
+ the event's ptr. Returns false if the event could not be posted (which means it will have been deleted).
+ */
+ static bool Post(SkEvent* evt, SkEventSinkID targetID, SkMSec delay = 0);
+ /** Post the specified event to the event queue, targeting the specified eventsink, to be delivered on/after the
+ specified millisecond time. The event must be dynamically allocated for this. It cannot be a global or on the stack.
+ After this call, ownership is transfered to the system, so the caller must not retain
+ the event's ptr. Returns false if the event could not be posted (which means it will have been deleted).
+ */
+ static bool PostTime(SkEvent* evt, SkEventSinkID targetID, SkMSec time);
+
+ /** Helper method for calling SkEvent::PostTime(this, ...), where the caller specifies a delay.
+ The real "time" will be computed automatically by sampling the clock and adding its value
+ to delay.
+ */
+ bool post(SkEventSinkID sinkID, SkMSec delay = 0)
+ {
+ return SkEvent::Post(this, sinkID, delay);
+ }
+
+ void postTime(SkEventSinkID sinkID, SkMSec time)
+ {
+ SkEvent::PostTime(this, sinkID, time);
+ }
+
+ ///////////////////////////////////////////////
+ /** Porting layer must call these functions **/
+ ///////////////////////////////////////////////
+
+ /** Global initialization function for the SkEvent system. Should be called exactly
+ once before any other event method is called, and should be called after the
+ call to SkGraphics::Init().
+ */
+ static void Init();
+ /** Global cleanup function for the SkEvent system. Should be called exactly once after
+ all event methods have been called, and should be called before calling SkGraphics::Term().
+ */
+ static void Term();
+
+ /** Call this to process one event from the queue. If it returns true, there are more events
+ to process.
+ */
+ static bool ProcessEvent();
+ /** Call this whenever the requested timer has expired (requested by a call to SetQueueTimer).
+ It will post any delayed events whose time as "expired" onto the event queue.
+ It may also call SignalQueueTimer() and SignalNonEmptyQueue().
+ */
+ static void ServiceQueueTimer();
+
+ /** Return the number of queued events. note that this value may be obsolete
+ upon return, since another thread may have called ProcessEvent() or
+ Post() after the count was made.
+ */
+ static int CountEventsOnQueue();
+
+ ////////////////////////////////////////////////////
+ /** Porting layer must implement these functions **/
+ ////////////////////////////////////////////////////
+
+ /** Called whenever an SkEvent is posted to an empty queue, so that the OS
+ can be told to later call Dequeue().
+ */
+ static void SignalNonEmptyQueue();
+ /** Called whenever the delay until the next delayed event changes. If zero is
+ passed, then there are no more queued delay events.
+ */
+ static void SignalQueueTimer(SkMSec delay);
+
+#ifndef SK_USE_WXWIDGETS
+#ifdef SK_BUILD_FOR_WIN
+ static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+#elif defined(SK_BUILD_FOR_UNIXx)
+ static uint32_t HandleTimer(uint32_t, void*);
+ static bool WndProc(Display*, Window, XEvent&);
+#endif
+#else
+ // Don't know yet what this will be
+ //static bool CustomEvent();
+#endif
+
+private:
+ SkMetaData fMeta;
+ mutable char* fType; // may be characters with low bit set to know that it is not a pointer
+ uint32_t f32;
+ SkDEBUGCODE(bool fDebugTrace;)
+
+ // these are for our implementation of the event queue
+ SkEventSinkID fTargetID;
+ SkMSec fTime;
+ SkEvent* fNextEvent; // either in the delay or normal event queue
+ void initialize(const char* type, size_t typeLen);
+
+ static bool Enqueue(SkEvent* evt);
+ static SkMSec EnqueueTime(SkEvent* evt, SkMSec time);
+ static SkEvent* Dequeue(SkEventSinkID* targetID);
+ static bool QHasEvents();
+};
+
+#endif
+
diff --git a/include/views/SkEventSink.h b/include/views/SkEventSink.h
new file mode 100644
index 0000000000..27a67437c3
--- /dev/null
+++ b/include/views/SkEventSink.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkEventSink_DEFINED
+#define SkEventSink_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkEvent.h"
+
+struct SkTagList;
+
+/** \class SkEventSink
+
+ SkEventSink is the base class for all objects that receive SkEvents.
+*/
+class SkEventSink : public SkRefCnt {
+public:
+ SkEventSink();
+ virtual ~SkEventSink();
+
+ /** Returns this eventsink's unique ID. Use this to post SkEvents to
+ this eventsink.
+ */
+ SkEventSinkID getSinkID() const { return fID; }
+
+ /** Call this to pass an event to this object for processing. Returns true if the
+ event was handled.
+ */
+ bool doEvent(const SkEvent&);
+ /** Returns true if the sink (or one of its subclasses) understands the event as a query.
+ If so, the sink may modify the event to communicate its "answer".
+ */
+ bool doQuery(SkEvent* query);
+
+ /** Add sinkID to the list of listeners, to receive events from calls to sendToListeners()
+ and postToListeners(). If sinkID already exists in the listener list, no change is made.
+ */
+ void addListenerID(SkEventSinkID sinkID);
+ /** Copy listeners from one event sink to another, typically from parent to child.
+ @param from the event sink to copy the listeners from
+ */
+ void copyListeners(const SkEventSink& from);
+ /** Remove sinkID from the list of listeners. If sinkID does not appear in the list,
+ no change is made.
+ */
+ void removeListenerID(SkEventSinkID);
+ /** Returns true if there are 1 or more listeners attached to this eventsink
+ */
+ bool hasListeners() const;
+ /** Posts a copy of evt to each of the eventsinks in the lisener list.
+ */
+ void postToListeners(const SkEvent& evt, SkMSec delay = 0);
+
+ enum EventResult {
+ kHandled_EventResult, //!< the eventsink returned true from its doEvent method
+ kNotHandled_EventResult, //!< the eventsink returned false from its doEvent method
+ kSinkNotFound_EventResult //!< no matching eventsink was found for the event's getSink().
+ };
+ /** DoEvent handles searching for an eventsink object that matches the targetID.
+ If one is found, it calls the sink's doEvent method, returning
+ either kHandled_EventResult or kNotHandled_EventResult. If no matching
+ eventsink is found, kSinkNotFound_EventResult is returned.
+ */
+ static EventResult DoEvent(const SkEvent&, SkEventSinkID targetID);
+
+ /** Returns the matching eventsink, or null if not found
+ */
+ static SkEventSink* FindSink(SkEventSinkID);
+
+protected:
+ /** Override this to handle events in your subclass. Be sure to call the inherited version
+ for events that you don't handle.
+ */
+ virtual bool onEvent(const SkEvent&);
+ virtual bool onQuery(SkEvent*);
+
+ SkTagList* findTagList(U8CPU tag) const;
+ void addTagList(SkTagList*);
+ void removeTagList(U8CPU tag);
+
+private:
+ SkEventSinkID fID;
+ SkTagList* fTagHead;
+
+ // for our private link-list
+ SkEventSink* fNextSink;
+};
+
+#endif
+
diff --git a/include/views/SkImageView.h b/include/views/SkImageView.h
new file mode 100644
index 0000000000..57215c9dce
--- /dev/null
+++ b/include/views/SkImageView.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkImageView_DEFINED
+#define SkImageView_DEFINED
+
+#include "SkView.h"
+#include "SkString.h"
+
+class SkAnimator;
+class SkBitmap;
+class SkMatrix;
+
+class SkImageView : public SkView {
+public:
+ SkImageView();
+ virtual ~SkImageView();
+
+ void getUri(SkString*) const;
+ void setUri(const char []);
+ void setUri(const SkString&);
+
+
+ enum ScaleType {
+ kMatrix_ScaleType,
+ kFitXY_ScaleType,
+ kFitStart_ScaleType,
+ kFitCenter_ScaleType,
+ kFitEnd_ScaleType
+ };
+ ScaleType getScaleType() const { return (ScaleType)fScaleType; }
+ void setScaleType(ScaleType);
+
+ bool getImageMatrix(SkMatrix*) const;
+ void setImageMatrix(const SkMatrix*);
+
+protected:
+ // overrides
+ virtual bool onEvent(const SkEvent&);
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM&, const SkDOMNode*);
+
+private:
+ SkString fUri;
+ SkMatrix* fMatrix; // null or copy of caller's matrix ,,,,,
+ union {
+ SkAnimator* fAnim;
+ SkBitmap* fBitmap;
+ } fData;
+ uint8_t fScaleType;
+ SkBool8 fDataIsAnim; // as opposed to bitmap
+ SkBool8 fUriIsValid;
+
+ void onUriChange();
+ bool getDataBounds(SkRect* bounds);
+ bool freeData();
+ bool ensureUriIsLoaded();
+
+ typedef SkView INHERITED;
+};
+
+#endif
diff --git a/include/views/SkKey.h b/include/views/SkKey.h
new file mode 100644
index 0000000000..3fd5114346
--- /dev/null
+++ b/include/views/SkKey.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkKey_DEFINED
+#define SkKey_DEFINED
+
+#include "SkTypes.h"
+
+enum SkKey {
+ //reordering these to match android.app.KeyEvent
+ kNONE_SkKey, //corresponds to android's UNKNOWN
+
+ kLeftSoftKey_SkKey,
+ kRightSoftKey_SkKey,
+
+ kHome_SkKey, //!< the home key - added to match android
+ kBack_SkKey, //!< (CLR)
+ kSend_SkKey, //!< the green (talk) key
+ kEnd_SkKey, //!< the red key
+
+ k0_SkKey,
+ k1_SkKey,
+ k2_SkKey,
+ k3_SkKey,
+ k4_SkKey,
+ k5_SkKey,
+ k6_SkKey,
+ k7_SkKey,
+ k8_SkKey,
+ k9_SkKey,
+ kStar_SkKey, //!< the * key
+ kHash_SkKey, //!< the # key
+
+ kUp_SkKey,
+ kDown_SkKey,
+ kLeft_SkKey,
+ kRight_SkKey,
+
+ kOK_SkKey, //!< the center key
+
+ kVolUp_SkKey, //!< volume up - match android
+ kVolDown_SkKey, //!< volume down - same
+ kPower_SkKey, //!< power button - same
+ kCamera_SkKey, //!< camera - same
+
+ kSkKeyCount
+};
+
+#endif
+
diff --git a/include/views/SkOSMenu.h b/include/views/SkOSMenu.h
new file mode 100644
index 0000000000..433a60155c
--- /dev/null
+++ b/include/views/SkOSMenu.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSMenu_DEFINED
+#define SkOSMenu_DEFINED
+
+#include "SkEvent.h"
+#include "SkTDArray.h"
+
+class SkOSMenu {
+public:
+ explicit SkOSMenu(const char title[]);
+ ~SkOSMenu();
+
+ const char* getTitle() const { return fTitle; }
+
+ void appendItem(const char title[], const char eventType[], int32_t eventData);
+
+ // called by SkOSWindow when it receives an OS menu event
+ int countItems() const;
+ const char* getItem(int index, uint32_t* cmdID) const;
+
+ SkEvent* createEvent(uint32_t os_cmd);
+
+private:
+ const char* fTitle;
+
+ struct Item {
+ const char* fTitle;
+ const char* fEventType;
+ uint32_t fEventData;
+ uint32_t fOSCmd; // internal
+ };
+ SkTDArray<Item> fItems;
+
+ // illegal
+ SkOSMenu(const SkOSMenu&);
+ SkOSMenu& operator=(const SkOSMenu&);
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h
new file mode 100644
index 0000000000..38a4cf8f5e
--- /dev/null
+++ b/include/views/SkOSWindow_Android.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 Skia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSWindow_Android_DEFINED
+#define SkOSWindow_Android_DEFINED
+
+#include "SkWindow.h"
+#include "SkEvent.h"
+
+class SkOSWindow : public SkWindow {
+public:
+ SkOSWindow(void*) {}
+ ~SkOSWindow() {}
+ bool attachGL() { return false; }
+ void detachGL() {}
+ void presentGL() {}
+
+protected:
+ // overrides from SkWindow
+ virtual void onHandleInval(const SkIRect&);
+ virtual void onSetTitle(const char title[]);
+
+private:
+ typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h
new file mode 100644
index 0000000000..232a202205
--- /dev/null
+++ b/include/views/SkOSWindow_Mac.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSWindow_Mac_DEFINED
+#define SkOSWindow_Mac_DEFINED
+
+#include <Carbon/Carbon.h>
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+ SkOSWindow(void* hwnd);
+
+ void* getHWND() const { return fHWND; }
+ void* getHVIEW() const { return fHVIEW; }
+ void updateSize();
+
+ static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+ static OSStatus EventHandler(EventHandlerCallRef inHandler,
+ EventRef inEvent, void* userData);
+
+ void doPaint(void* ctx);
+
+
+ bool attachGL();
+ void detachGL();
+ void presentGL();
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onEvent(const SkEvent& evt);
+ // overrides from SkWindow
+ virtual void onHandleInval(const SkIRect&);
+ // overrides from SkView
+ virtual void onAddMenu(const SkOSMenu*);
+ virtual void onSetTitle(const char[]);
+
+
+private:
+ void* fHWND;
+ void* fHVIEW;
+ void* fAGLCtx;
+
+ typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h
new file mode 100644
index 0000000000..0ff24f31ff
--- /dev/null
+++ b/include/views/SkOSWindow_SDL.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSWindow_SDL_DEFINED
+#define SkOSWindow_SDL_DEFINED
+
+#include "SDL.h"
+#include "SkWindow.h"
+
+class SkGLCanvas;
+
+class SkOSWindow : public SkWindow {
+public:
+ SkOSWindow(void* screen);
+ virtual ~SkOSWindow();
+
+ static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+ void handleSDLEvent(const SDL_Event& event);
+
+protected:
+ // overrides from SkWindow
+ virtual void onHandleInval(const SkIRect&);
+ // overrides from SkView
+ virtual void onAddMenu(const SkOSMenu*);
+ virtual void onSetTitle(const char[]);
+
+private:
+ SDL_Surface* fScreen;
+ SDL_Surface* fSurface;
+ SkGLCanvas* fGLCanvas;
+
+ void doDraw();
+
+ typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h
new file mode 100644
index 0000000000..803ca1303d
--- /dev/null
+++ b/include/views/SkOSWindow_Unix.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSWindow_Unix_DEFINED
+#define SkOSWindow_Unix_DEFINED
+
+#include "SkWindow.h"
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+class SkBitmap;
+class SkEvent;
+
+struct SkUnixWindow {
+ Display* fDisplay;
+ Window fWin;
+ size_t fOSWin;
+ GC fGc;
+ GLXContext fGLContext;
+ bool fGLCreated;
+};
+
+class SkOSWindow : public SkWindow {
+public:
+ SkOSWindow(void*);
+ ~SkOSWindow();
+
+ void* getHWND() const { return (void*)fUnixWindow.fWin; }
+ void* getDisplay() const { return (void*)fUnixWindow.fDisplay; }
+ void* getUnixWindow() const { return (void*)&fUnixWindow; }
+ void loop();
+ void post_linuxevent();
+ bool attachGL();
+ void detachGL();
+ void presentGL();
+
+ //static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+ //static bool WndProc(SkUnixWindow* w, XEvent &e);
+
+protected:
+ // overrides from SkWindow
+ virtual bool onEvent(const SkEvent&);
+ virtual void onHandleInval(const SkIRect&);
+ virtual bool onHandleChar(SkUnichar);
+ virtual bool onHandleKey(SkKey);
+ virtual bool onHandleKeyUp(SkKey);
+ virtual void onSetTitle(const char title[]);
+
+private:
+ SkUnixWindow fUnixWindow;
+ bool fGLAttached;
+ bool fRestart;
+
+ // Needed for GL
+ XVisualInfo* fVi;
+
+ void doPaint();
+ void restartLoop();
+ void mapWindowAndWait();
+
+ typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h
new file mode 100644
index 0000000000..4b3e9164c3
--- /dev/null
+++ b/include/views/SkOSWindow_Win.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkOSWindow_Win_DEFINED
+#define SkOSWindow_Win_DEFINED
+
+#include "SkWindow.h"
+
+class SkOSWindow : public SkWindow {
+public:
+ SkOSWindow(void* hwnd);
+ virtual ~SkOSWindow();
+
+ void* getHWND() const { return fHWND; }
+ void setSize(int width, int height);
+ void updateSize();
+
+ static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
+
+ bool attachGL();
+ void detachGL();
+ void presentGL();
+
+ bool attachD3D9();
+ void detachD3D9();
+ void presentD3D9();
+
+ void* d3d9Device() { return fD3D9Device; }
+
+ bool wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+ static bool QuitOnDeactivate(HWND hWnd);
+
+ enum {
+ SK_WM_SkEvent = WM_APP + 1000,
+ SK_WM_SkTimerID = 0xFFFF // just need a non-zero value
+ };
+
+protected:
+ virtual bool quitOnDeactivate() { return true; }
+
+ // overrides from SkWindow
+ virtual void onHandleInval(const SkIRect&);
+ // overrides from SkView
+ virtual void onAddMenu(const SkOSMenu*);
+
+ virtual void onSetTitle(const char title[]);
+
+private:
+ void* fHWND;
+
+ void doPaint(void* ctx);
+
+ void* fHGLRC;
+
+ bool fGLAttached;
+
+ void* fD3D9Device;
+ bool fD3D9Attached;
+
+ HMENU fMBar;
+
+ typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkOSWindow_wxwidgets.h b/include/views/SkOSWindow_wxwidgets.h
new file mode 100644
index 0000000000..c5dfc7c45a
--- /dev/null
+++ b/include/views/SkOSWindow_wxwidgets.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * SkOSWindow_wxwidgets.h
+ * wxwidgets
+ *
+ * Copyright 2005 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef SkOSWindow_wxwidgets_DEFINED
+#define SkOSWindow_wxwidgets_DEFINED
+
+#include "SkWindow.h"
+#include "wx/frame.h"
+
+class SkOSWindow: public SkWindow
+{
+public:
+ SkOSWindow();
+ SkOSWindow(const wxString& title, int x, int y, int width, int height);
+ ~SkOSWindow();
+
+ wxFrame* getWXFrame() const { return fFrame; }
+
+ void updateSize();
+
+protected:
+ virtual void onHandleInval(const SkIRect&);
+ virtual void onAddMenu(const SkOSMenu*);
+
+private:
+ wxFrame* fFrame;
+ typedef SkWindow INHERITED;
+
+};
+
+#endifpedef SkWindow INHERITED;
diff --git a/include/views/SkProgressBarView.h b/include/views/SkProgressBarView.h
new file mode 100644
index 0000000000..6341fcb051
--- /dev/null
+++ b/include/views/SkProgressBarView.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkProgressBarView_DEFINED
+#define SkProgressBarView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkProgressBarView : public SkWidgetView {
+ public:
+ SkProgressBarView();
+ //SkProgressBarView(int max);
+
+ //inflate: "sk-progress"
+
+ void reset(); //reset progress to zero
+ void setProgress(int progress);
+ void changeProgress(int diff);
+ void setMax(int max);
+
+ int getProgress() const { return fProgress; }
+ int getMax() const { return fMax; }
+
+ protected:
+ //overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+ virtual void onSizeChange();
+ virtual void onDraw(SkCanvas* canvas);
+ virtual bool onEvent(const SkEvent& evt);
+
+ private:
+ SkAnimator fAnim;
+ int fProgress;
+ int fMax;
+
+ typedef SkWidgetView INHERITED;
+};
+
+
+
+
+#endif
diff --git a/include/views/SkScrollBarView.h b/include/views/SkScrollBarView.h
new file mode 100644
index 0000000000..b8a5209ddb
--- /dev/null
+++ b/include/views/SkScrollBarView.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkScrollBarView_DEFINED
+#define SkScrollBarView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkScrollBarView : public SkWidgetView {
+public:
+ SkScrollBarView();
+
+ unsigned getStart() const { return fStartPoint; }
+ unsigned getShown() const { return fShownLength; }
+ unsigned getTotal() const { return fTotalLength; }
+
+ void setStart(unsigned start);
+ void setShown(unsigned shown);
+ void setTotal(unsigned total);
+
+protected:
+ //overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+ virtual void onSizeChange();
+ virtual void onDraw(SkCanvas* canvas);
+ virtual bool onEvent(const SkEvent& evt);
+
+private:
+ SkAnimator fAnim;
+ unsigned fTotalLength, fStartPoint, fShownLength;
+
+ void adjust();
+
+ typedef SkWidgetView INHERITED;
+};
+#endif
+
diff --git a/include/views/SkStackViewLayout.h b/include/views/SkStackViewLayout.h
new file mode 100644
index 0000000000..8000319fcb
--- /dev/null
+++ b/include/views/SkStackViewLayout.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkStackViewLayout_DEFINED
+#define SkStackViewLayout_DEFINED
+
+#include "SkView.h"
+
+class SkStackViewLayout : public SkView::Layout {
+public:
+ SkStackViewLayout();
+
+ enum Orient {
+ kHorizontal_Orient,
+ kVertical_Orient,
+
+ kOrientCount
+ };
+ Orient getOrient() const { return (Orient)fOrient; }
+ void setOrient(Orient);
+
+ void getMargin(SkRect*) const;
+ void setMargin(const SkRect&);
+
+ SkScalar getSpacer() const { return fSpacer; }
+ void setSpacer(SkScalar);
+
+ /** Controls the posititioning in the same direction as the orientation
+ */
+ enum Pack {
+ kStart_Pack,
+ kCenter_Pack,
+ kEnd_Pack,
+
+ kPackCount
+ };
+ Pack getPack() const { return (Pack)fPack; }
+ void setPack(Pack);
+
+ /** Controls the posititioning at right angles to the orientation
+ */
+ enum Align {
+ kStart_Align,
+ kCenter_Align,
+ kEnd_Align,
+ kStretch_Align,
+
+ kAlignCount
+ };
+ Align getAlign() const { return (Align)fAlign; }
+ void setAlign(Align);
+
+ bool getRound() const { return SkToBool(fRound); }
+ void setRound(bool);
+
+protected:
+ virtual void onLayoutChildren(SkView* parent);
+ virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+ SkRect fMargin;
+ SkScalar fSpacer;
+ uint8_t fOrient, fPack, fAlign, fRound;
+};
+
+class SkFillViewLayout : public SkView::Layout {
+public:
+ SkFillViewLayout();
+ void getMargin(SkRect*) const;
+ void setMargin(const SkRect&);
+
+protected:
+ // overrides;
+ virtual void onLayoutChildren(SkView* parent);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ SkRect fMargin;
+ typedef SkView::Layout INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkSystemEventTypes.h b/include/views/SkSystemEventTypes.h
new file mode 100644
index 0000000000..8dfe8bec07
--- /dev/null
+++ b/include/views/SkSystemEventTypes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkSystemEventTypes_DEFINED
+#define SkSystemEventTypes_DEFINED
+
+/*
+ The goal of these strings is two-fold:
+ 1) make funny strings (containing at least one char < 32) to avoid colliding with "user" strings
+ 2) keep them <= 4 bytes, so we can avoid an allocation in SkEvent::setType()
+*/
+#define SK_EventType_Delay "\xd" "lay"
+#define SK_EventType_Inval "nv" "\xa" "l"
+#define SK_EventType_Key "key" "\x1"
+#define SK_EventType_OnEnd "on" "\xe" "n"
+#define SK_EventType_Unichar "\xc" "har"
+#define SK_EventType_KeyUp "key" "\xf"
+
+#endif
diff --git a/include/views/SkTouchGesture.h b/include/views/SkTouchGesture.h
new file mode 100644
index 0000000000..79d4e283df
--- /dev/null
+++ b/include/views/SkTouchGesture.h
@@ -0,0 +1,72 @@
+#ifndef SkTouchGesture_DEFINED
+#define SkTouchGesture_DEFINED
+
+#include "SkTDArray.h"
+#include "SkMatrix.h"
+
+struct SkFlingState {
+ SkFlingState() : fActive(false) {}
+
+ bool isActive() const { return fActive; }
+ void stop() { fActive = false; }
+
+ void reset(float sx, float sy);
+ bool evaluateMatrix(SkMatrix* matrix);
+
+private:
+ SkPoint fDirection;
+ SkScalar fSpeed0;
+ double fTime0;
+ bool fActive;
+};
+
+class SkTouchGesture {
+public:
+ SkTouchGesture();
+ ~SkTouchGesture();
+
+ void touchBegin(void* owner, float x, float y);
+ void touchMoved(void* owner, float x, float y);
+ void touchEnd(void* owner);
+ void reset();
+
+ bool isActive() { return fFlinger.isActive(); }
+ void stop() { fFlinger.stop(); }
+
+ const SkMatrix& localM();
+ const SkMatrix& globalM() const { return fGlobalM; }
+
+private:
+ enum State {
+ kEmpty_State,
+ kTranslate_State,
+ kZoom_State,
+ };
+
+ struct Rec {
+ void* fOwner;
+ float fStartX, fStartY;
+ float fPrevX, fPrevY;
+ float fLastX, fLastY;
+ SkMSec fPrevT, fLastT;
+ };
+ SkTDArray<Rec> fTouches;
+
+ State fState;
+ SkMatrix fLocalM, fGlobalM;
+ SkFlingState fFlinger;
+ SkMSec fLastUpT;
+ SkPoint fLastUpP;
+
+
+ void flushLocalM();
+ int findRec(void* owner) const;
+ void appendNewRec(void* owner, float x, float y);
+ float computePinch(const Rec&, const Rec&);
+ float limitTotalZoom(float scale) const;
+ bool handleDblTap(float, float);
+};
+
+#endif
+
+
diff --git a/include/views/SkView.h b/include/views/SkView.h
new file mode 100644
index 0000000000..d3633db160
--- /dev/null
+++ b/include/views/SkView.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkView_DEFINED
+#define SkView_DEFINED
+
+#include "SkEventSink.h"
+#include "SkRect.h"
+#include "SkDOM.h"
+#include "SkTDict.h"
+
+class SkCanvas;
+class SkLayerView;
+
+/** \class SkView
+
+ SkView is the base class for screen management. All widgets and controls inherit
+ from SkView.
+*/
+class SkView : public SkEventSink {
+public:
+ enum Flag_Shift {
+ kVisible_Shift,
+ kEnabled_Shift,
+ kFocusable_Shift,
+ kFlexH_Shift,
+ kFlexV_Shift,
+ kNoClip_Shift,
+
+ kFlagShiftCount
+ };
+ enum Flag_Mask {
+ kVisible_Mask = 1 << kVisible_Shift, //!< set if the view is visible
+ kEnabled_Mask = 1 << kEnabled_Shift, //!< set if the view is enabled
+ kFocusable_Mask = 1 << kFocusable_Shift, //!< set if the view can receive focus
+ kFlexH_Mask = 1 << kFlexH_Shift, //!< set if the view's width is stretchable
+ kFlexV_Mask = 1 << kFlexV_Shift, //!< set if the view's height is stretchable
+ kNoClip_Mask = 1 << kNoClip_Shift, //!< set if the view is not clipped to its bounds
+
+ kAllFlagMasks = (uint32_t)(0 - 1) >> (32 - kFlagShiftCount)
+ };
+
+ SkView(uint32_t flags = 0);
+ virtual ~SkView();
+
+ /** Return the flags associated with the view
+ */
+ uint32_t getFlags() const { return fFlags; }
+ /** Set the flags associated with the view
+ */
+ void setFlags(uint32_t flags);
+
+ /** Helper that returns non-zero if the kVisible_Mask bit is set in the view's flags
+ */
+ int isVisible() const { return fFlags & kVisible_Mask; }
+ int isEnabled() const { return fFlags & kEnabled_Mask; }
+ int isFocusable() const { return fFlags & kFocusable_Mask; }
+ int isClipToBounds() const { return !(fFlags & kNoClip_Mask); }
+ /** Helper to set/clear the view's kVisible_Mask flag */
+ void setVisibleP(bool);
+ void setEnabledP(bool);
+ void setFocusableP(bool);
+ void setClipToBounds(bool);
+
+ /** Return the view's width */
+ SkScalar width() const { return fWidth; }
+ /** Return the view's height */
+ SkScalar height() const { return fHeight; }
+ /** Set the view's width and height. These must both be >= 0. This does not affect the view's loc */
+ void setSize(SkScalar width, SkScalar height);
+ void setSize(const SkPoint& size) { this->setSize(size.fX, size.fY); }
+ void setWidth(SkScalar width) { this->setSize(width, fHeight); }
+ void setHeight(SkScalar height) { this->setSize(fWidth, height); }
+ /** Return a rectangle set to [0, 0, width, height] */
+ void getLocalBounds(SkRect* bounds) const;
+
+ /** Return the view's left edge */
+ SkScalar locX() const { return fLoc.fX; }
+ /** Return the view's top edge */
+ SkScalar locY() const { return fLoc.fY; }
+ /** Set the view's left and top edge. This does not affect the view's size */
+ void setLoc(SkScalar x, SkScalar y);
+ void setLoc(const SkPoint& loc) { this->setLoc(loc.fX, loc.fY); }
+ void setLocX(SkScalar x) { this->setLoc(x, fLoc.fY); }
+ void setLocY(SkScalar y) { this->setLoc(fLoc.fX, y); }
+ /** Offset (move) the view by the specified dx and dy. This does not affect the view's size */
+ void offset(SkScalar dx, SkScalar dy);
+
+ /** Call this to have the view draw into the specified canvas. */
+ virtual void draw(SkCanvas* canvas);
+
+ /** Call this to invalidate part of all of a view, requesting that the view's
+ draw method be called. The rectangle parameter specifies the part of the view
+ that should be redrawn. If it is null, it specifies the entire view bounds.
+ */
+ void inval(SkRect* rectOrNull);
+
+ // Focus management
+
+ SkView* getFocusView() const;
+ bool hasFocus() const;
+
+ enum FocusDirection {
+ kNext_FocusDirection,
+ kPrev_FocusDirection,
+
+ kFocusDirectionCount
+ };
+ bool acceptFocus();
+ SkView* moveFocus(FocusDirection);
+
+ // Click handling
+
+ class Click {
+ public:
+ Click(SkView* target);
+ virtual ~Click();
+
+ const char* getType() const { return fType; }
+ bool isType(const char type[]) const;
+ void setType(const char type[]); // does NOT make a copy of the string
+ void copyType(const char type[]); // makes a copy of the string
+
+ enum State {
+ kDown_State,
+ kMoved_State,
+ kUp_State
+ };
+ SkPoint fOrig, fPrev, fCurr;
+ SkIPoint fIOrig, fIPrev, fICurr;
+ State fState;
+ private:
+ SkEventSinkID fTargetID;
+ char* fType;
+ bool fWeOwnTheType;
+
+ void resetType();
+
+ friend class SkView;
+ };
+ Click* findClickHandler(SkScalar x, SkScalar y);
+
+ static void DoClickDown(Click*, int x, int y);
+ static void DoClickMoved(Click*, int x, int y);
+ static void DoClickUp(Click*, int x, int y);
+
+ /** Send the event to the view's parent, and its parent etc. until one of them
+ returns true from its onEvent call. This view is returned. If no parent handles
+ the event, null is returned.
+ */
+ SkView* sendEventToParents(const SkEvent&);
+ /** Send the query to the view's parent, and its parent etc. until one of them
+ returns true from its onQuery call. This view is returned. If no parent handles
+ the query, null is returned.
+ */
+ SkView* sendQueryToParents(SkEvent*);
+
+ /** Depricated helper function. Just call event->post(sinkID, delay);
+ */
+ bool postEvent(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay) { return evt->post(sinkID, delay); }
+
+ // View hierarchy management
+
+ /** Return the view's parent, or null if it has none. This does not affect the parent's reference count. */
+ SkView* getParent() const { return fParent; }
+ SkView* attachChildToFront(SkView* child);
+ /** Attach the child view to this view, and increment the child's reference count. The child view is added
+ such that it will be drawn before all other child views.
+ The child view parameter is returned.
+ */
+ SkView* attachChildToBack(SkView* child);
+ /** If the view has a parent, detach the view from its parent and decrement the view's reference count.
+ If the parent was the only owner of the view, this will cause the view to be deleted.
+ */
+ void detachFromParent();
+ /** Attach the child view to this view, and increment the child's reference count. The child view is added
+ such that it will be drawn after all other child views.
+ The child view parameter is returned.
+ */
+ /** Detach all child views from this view. */
+ void detachAllChildren();
+
+ /** Convert the specified point from global coordinates into view-local coordinates
+ */
+ void globalToLocal(SkPoint* pt) const { if (pt) this->globalToLocal(pt->fX, pt->fY, pt); }
+ /** Convert the specified x,y from global coordinates into view-local coordinates, returning
+ the answer in the local parameter.
+ */
+ void globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const;
+
+ /** \class F2BIter
+
+ Iterator that will return each of this view's children, in
+ front-to-back order (the order used for clicking). The first
+ call to next() returns the front-most child view. When
+ next() returns null, there are no more child views.
+ */
+ class F2BIter {
+ public:
+ F2BIter(const SkView* parent);
+ SkView* next();
+ private:
+ SkView* fFirstChild, *fChild;
+ };
+
+ /** \class B2FIter
+
+ Iterator that will return each of this view's children, in
+ back-to-front order (the order they are drawn). The first
+ call to next() returns the back-most child view. When
+ next() returns null, there are no more child views.
+ */
+ class B2FIter {
+ public:
+ B2FIter(const SkView* parent);
+ SkView* next();
+ private:
+ SkView* fFirstChild, *fChild;
+ };
+
+ /** \class Artist
+
+ Install a subclass of this in a view (calling setArtist()), and then the
+ default implementation of that view's onDraw() will invoke this object
+ automatically.
+ */
+ class Artist : public SkRefCnt {
+ public:
+ void draw(SkView*, SkCanvas*);
+ void inflate(const SkDOM&, const SkDOM::Node*);
+ protected:
+ virtual void onDraw(SkView*, SkCanvas*) = 0;
+ virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+ };
+ /** Return the artist attached to this view (or null). The artist's reference
+ count is not affected.
+ */
+ Artist* getArtist() const;
+ /** Attach the specified artist (or null) to the view, replacing any existing
+ artist. If the new artist is not null, its reference count is incremented.
+ The artist parameter is returned.
+ */
+ Artist* setArtist(Artist* artist);
+
+ /** \class Layout
+
+ Install a subclass of this in a view (calling setLayout()), and then the
+ default implementation of that view's onLayoutChildren() will invoke
+ this object automatically.
+ */
+ class Layout : public SkRefCnt {
+ public:
+ void layoutChildren(SkView* parent);
+ void inflate(const SkDOM&, const SkDOM::Node*);
+ protected:
+ virtual void onLayoutChildren(SkView* parent) = 0;
+ virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+ };
+
+ /** Return the layout attached to this view (or null). The layout's reference
+ count is not affected.
+ */
+ Layout* getLayout() const;
+ /** Attach the specified layout (or null) to the view, replacing any existing
+ layout. If the new layout is not null, its reference count is incremented.
+ The layout parameter is returned.
+ */
+ Layout* setLayout(Layout*, bool invokeLayoutNow = true);
+ /** If a layout is attached to this view, call its layoutChildren() method
+ */
+ void invokeLayout();
+
+ /** Call this to initialize this view based on the specified XML node
+ */
+ void inflate(const SkDOM& dom, const SkDOM::Node* node);
+ /** After a view hierarchy is inflated, this may be called with a dictionary
+ containing pairs of <name, view*>, where the name string was the view's
+ "id" attribute when it was inflated.
+
+ This will call the virtual onPostInflate for this view, and the recursively
+ call postInflate on all of the view's children.
+ */
+ void postInflate(const SkTDict<SkView*>& ids);
+
+ SkDEBUGCODE(void dump(bool recurse) const;)
+
+protected:
+ /** Override this to draw inside the view. Be sure to call the inherited version too */
+ virtual void onDraw(SkCanvas*);
+ /** Override this to be notified when the view's size changes. Be sure to call the inherited version too */
+ virtual void onSizeChange();
+ /** Override this if you want to handle an inval request from this view or one of its children.
+ Tyically this is only overridden by the by the "window". If your subclass does handle the
+ request, return true so the request will not continue to propogate to the parent.
+ */
+ virtual bool handleInval(const SkRect*);
+ //! called once before all of the children are drawn (or clipped/translated)
+ virtual SkCanvas* beforeChildren(SkCanvas* c) { return c; }
+ //! called once after all of the children are drawn (or clipped/translated)
+ virtual void afterChildren(SkCanvas* orig) {}
+
+ //! called right before this child's onDraw is called
+ virtual void beforeChild(SkView* child, SkCanvas* canvas) {}
+ //! called right after this child's onDraw is called
+ virtual void afterChild(SkView* child, SkCanvas* canvas) {}
+
+ /** Override this if you might handle the click
+ */
+ virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+ /** Override this to decide if your children are targets for a click.
+ The default returns true, in which case your children views will be
+ candidates for onFindClickHandler. Returning false wil skip the children
+ and just call your onFindClickHandler.
+ */
+ virtual bool onSendClickToChildren(SkScalar x, SkScalar y);
+ /** Override this to track clicks, returning true as long as you want to track
+ the pen/mouse.
+ */
+ virtual bool onClick(Click*);
+ /** Override this to initialize your subclass from the XML node. Be sure to call the inherited version too */
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+ /** Override this if you want to perform post initialization work based on the ID dictionary built
+ during XML parsing. Be sure to call the inherited version too.
+ */
+ virtual void onPostInflate(const SkTDict<SkView*>&);
+
+public:
+ // default action is to inval the view
+ virtual void onFocusChange(bool gainFocusP);
+protected:
+
+ // override these if you're acting as a layer/host
+ virtual bool onGetFocusView(SkView**) const { return false; }
+ virtual bool onSetFocusView(SkView*) { return false; }
+
+private:
+ SkScalar fWidth, fHeight;
+ SkPoint fLoc;
+ SkView* fParent;
+ SkView* fFirstChild;
+ SkView* fNextSibling;
+ SkView* fPrevSibling;
+ uint8_t fFlags;
+ uint8_t fContainsFocus;
+
+ friend class B2FIter;
+ friend class F2BIter;
+
+ friend class SkLayerView;
+
+ bool setFocusView(SkView* fvOrNull);
+ SkView* acceptFocus(FocusDirection);
+ void detachFromParent_NoLayout();
+};
+
+#endif
+
diff --git a/include/views/SkViewInflate.h b/include/views/SkViewInflate.h
new file mode 100644
index 0000000000..3ec65a6f77
--- /dev/null
+++ b/include/views/SkViewInflate.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkViewInflate_DEFINED
+#define SkViewInflate_DEFINED
+
+#include "SkDOM.h"
+#include "SkTDict.h"
+#include "SkEvent.h"
+
+class SkView;
+
+class SkViewInflate {
+public:
+ SkViewInflate();
+ virtual ~SkViewInflate();
+
+ /** Return the tree of inflated views. If root is null, create the root element
+ as a view, otherwise assume root is that view, and just "inflate" it.
+
+ Returns null if the tree cannot be built.
+ */
+ SkView* inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root = NULL);
+ SkView* inflate(const char xml[], size_t len, SkView* root = NULL);
+
+ /** Given an id attribute value, return the corresponding view, or null
+ if no match is found.
+ */
+ SkView* findViewByID(const char id[]) const;
+
+ SkDEBUGCODE(void dump() const;)
+
+protected:
+ /* Override this in your subclass to handle instantiating views
+ Call the inherited version for nodes you don't recognize.
+
+ Do not call "inflate" on the view, just return it. This will
+ get called automatically after createView returns.
+ */
+ virtual SkView* createView(const SkDOM& dom, const SkDOM::Node* node);
+ /** Base implementation calls view->inflate(dom, node). Subclasses may override this
+ to perform additional initializations to view, either before or after calling
+ the inherited version.
+ */
+ virtual void inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ enum {
+ kMinIDStrAlloc = 64
+ };
+ SkTDict<SkView*> fIDs;
+
+ struct IDStr {
+ SkView* fView;
+ char* fStr;
+ };
+ SkTDArray<IDStr> fListenTo, fBroadcastTo;
+ SkChunkAlloc fStrings;
+
+ void addIDStr(SkTDArray<IDStr>* list, SkView*, const char* str);
+
+ void rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent);
+};
+
+#endif
+
diff --git a/include/views/SkWidget.h b/include/views/SkWidget.h
new file mode 100644
index 0000000000..db85f01ff4
--- /dev/null
+++ b/include/views/SkWidget.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkWidget_DEFINED
+#define SkWidget_DEFINED
+
+#include "SkView.h"
+#include "SkBitmap.h"
+#include "SkDOM.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkWidget : public SkView {
+public:
+ SkWidget(uint32_t flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {}
+
+ /** Call this to post the widget's event to its listeners */
+ void postWidgetEvent();
+
+ static void Init();
+ static void Term();
+protected:
+ // override to add slots to an event before posting
+ virtual void prepareWidgetEvent(SkEvent*);
+ virtual void onEnabledChange();
+
+ // <event ...> to initialize the event from XML
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ SkEvent fEvent;
+ typedef SkView INHERITED;
+};
+
+class SkHasLabelWidget : public SkWidget {
+public:
+ SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {}
+
+ size_t getLabel(SkString* label = NULL) const;
+ size_t getLabel(char lable[] = NULL) const;
+ void setLabel(const SkString&);
+ void setLabel(const char label[]);
+ void setLabel(const char label[], size_t len);
+
+protected:
+ // called when the label changes
+ virtual void onLabelChange();
+
+ // overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ SkString fLabel;
+ typedef SkWidget INHERITED;
+};
+
+class SkButtonWidget : public SkHasLabelWidget {
+public:
+ SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {}
+
+ enum State {
+ kOff_State, //!< XML: buttonState="off"
+ kOn_State, //!< XML: buttonState="on"
+ kUnknown_State //!< XML: buttonState="unknown"
+ };
+ State getButtonState() const { return fState; }
+ void setButtonState(State);
+
+protected:
+ /** called when the label changes. default behavior is to inval the widget */
+ virtual void onButtonStateChange();
+
+ // overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ State fState;
+ typedef SkHasLabelWidget INHERITED;
+};
+
+class SkPushButtonWidget : public SkButtonWidget {
+public:
+ SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {}
+
+protected:
+ virtual bool onEvent(const SkEvent&);
+ virtual void onDraw(SkCanvas*);
+ virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+ virtual bool onClick(Click* click);
+
+private:
+ typedef SkButtonWidget INHERITED;
+};
+
+class SkCheckBoxWidget : public SkButtonWidget {
+public:
+ SkCheckBoxWidget(uint32_t flags = 0);
+
+protected:
+ virtual bool onEvent(const SkEvent&);
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ typedef SkButtonWidget INHERITED;
+};
+
+#include "SkTextBox.h"
+
+class SkStaticTextView : public SkView {
+public:
+ SkStaticTextView(uint32_t flags = 0);
+ virtual ~SkStaticTextView();
+
+ enum Mode {
+ kFixedSize_Mode,
+ kAutoWidth_Mode,
+ kAutoHeight_Mode,
+
+ kModeCount
+ };
+ Mode getMode() const { return (Mode)fMode; }
+ void setMode(Mode);
+
+ SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; }
+ void setSpacingAlign(SkTextBox::SpacingAlign);
+
+ void getMargin(SkPoint* margin) const;
+ void setMargin(SkScalar dx, SkScalar dy);
+
+ size_t getText(SkString* text = NULL) const;
+ size_t getText(char text[] = NULL) const;
+ void setText(const SkString&);
+ void setText(const char text[]);
+ void setText(const char text[], size_t len);
+
+ void getPaint(SkPaint*) const;
+ void setPaint(const SkPaint&);
+
+protected:
+ // overrides
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ SkPoint fMargin;
+ SkString fText;
+ SkPaint fPaint;
+ uint8_t fMode;
+ uint8_t fSpacingAlign;
+
+ void computeSize();
+
+ typedef SkView INHERITED;
+};
+
+class SkBitmapView : public SkView {
+public:
+ SkBitmapView(uint32_t flags = 0);
+ virtual ~SkBitmapView();
+
+ bool getBitmap(SkBitmap*) const;
+ void setBitmap(const SkBitmap*, bool viewOwnsPixels);
+ bool loadBitmapFromFile(const char path[]);
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+
+private:
+ SkBitmap fBitmap;
+ typedef SkView INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class SkShader;
+class SkInterpolator;
+
+class SkWidgetView : public SkView {
+public:
+ SkWidgetView(uint32_t flags = 0);
+ virtual ~SkWidgetView();
+
+ static const char* GetEventType();
+};
+
+class SkSliderView : public SkWidgetView {
+public:
+ SkSliderView(uint32_t flags = 0);
+
+ uint16_t getValue() const { return fValue; }
+ uint16_t getMax() const { return fMax; }
+
+ void setMax(U16CPU max);
+ void setValue(U16CPU value);
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+ virtual bool onClick(Click*);
+
+private:
+ uint16_t fValue, fMax;
+
+ typedef SkWidgetView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SkHasLabelView : public SkView {
+public:
+ void getLabel(SkString*) const;
+ void setLabel(const SkString&);
+ void setLabel(const char label[]);
+
+protected:
+ SkString fLabel;
+
+ // called when the label changes
+ virtual void onLabelChange();
+
+ // overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+};
+
+class SkPushButtonView : public SkHasLabelView {
+public:
+ SkPushButtonView(uint32_t flags = 0);
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+};
+
+class SkCheckBoxView : public SkHasLabelView {
+public:
+ SkCheckBoxView(uint32_t flags = 0);
+
+ enum State {
+ kOff_State,
+ kOn_State,
+ kMaybe_State
+ };
+ State getState() const { return fState; }
+ void setState(State);
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ State fState;
+};
+
+class SkProgressView : public SkView {
+public:
+ SkProgressView(uint32_t flags = 0);
+ virtual ~SkProgressView();
+
+ uint16_t getValue() const { return fValue; }
+ uint16_t getMax() const { return fMax; }
+
+ void setMax(U16CPU max);
+ void setValue(U16CPU value);
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ uint16_t fValue, fMax;
+ SkShader* fOnShader, *fOffShader;
+ SkInterpolator* fInterp;
+ bool fDoInterp;
+
+ typedef SkView INHERITED;
+};
+
+class SkTextView : public SkView {
+public:
+ SkTextView(uint32_t flags = 0);
+ virtual ~SkTextView();
+
+ enum AnimaDir {
+ kNeutral_AnimDir,
+ kForward_AnimDir,
+ kBackward_AnimDir,
+ kAnimDirCount
+ };
+
+ void getText(SkString*) const;
+ void setText(const SkString&, AnimaDir dir = kNeutral_AnimDir);
+ void setText(const char text[], AnimaDir dir = kNeutral_AnimDir);
+ void setText(const char text[], size_t len, AnimaDir dir = kNeutral_AnimDir);
+
+ void getMargin(SkPoint* margin) const;
+ void setMargin(const SkPoint&);
+
+ SkPaint& paint() { return fPaint; }
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ SkString fText;
+ SkPaint fPaint;
+ SkPoint fMargin;
+
+ class Interp;
+ Interp* fInterp;
+ bool fDoInterp;
+ // called by the other setText methods. This guy does not check for !=
+ // before doing the assign, so the caller must check for us
+ void privSetText(const SkString&, AnimaDir dir);
+
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////
+
+class SkEvent;
+
+class SkListSource : public SkEventSink {
+public:
+ virtual int countRows() = 0;
+ virtual void getRow(int index, SkString* left, SkString* right) = 0;
+ virtual SkEvent* getEvent(int index);
+
+ static SkListSource* CreateFromDir(const char path[], const char suffix[],
+ const char targetPrefix[]);
+ static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node);
+};
+
+class SkListView : public SkWidgetView {
+public:
+ SkListView(uint32_t flags = 0);
+ virtual ~SkListView();
+
+ SkScalar getRowHeight() const { return fRowHeight; }
+ void setRowHeight(SkScalar);
+
+ /** Return the index of the selected row, or -1 if none
+ */
+ int getSelection() const { return fCurrIndex; }
+ /** Set the index of the selected row, or -1 for none
+ */
+ void setSelection(int);
+
+ void moveSelectionUp();
+ void moveSelectionDown();
+
+ enum Attr {
+ kBG_Attr,
+ kNormalText_Attr,
+ kHiliteText_Attr,
+ kHiliteCell_Attr,
+ kAttrCount
+ };
+ SkPaint& paint(Attr);
+
+ SkListSource* getListSource() const { return fSource; }
+ SkListSource* setListSource(SkListSource*);
+
+#if 0
+ enum Action {
+ kSelectionChange_Action,
+ kSelectionPicked_Action,
+ kActionCount
+ };
+ /** If event is not null, it is retained by the view, and a copy
+ of the event will be posted to its listeners when the specified
+ action occurs. If event is null, then no event will be posted for
+ the specified action.
+ */
+ void setActionEvent(Action, SkEvent* event);
+#endif
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onSizeChange();
+ virtual bool onEvent(const SkEvent&);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ SkPaint fPaint[kAttrCount];
+ SkListSource* fSource;
+ SkScalar fRowHeight;
+ int fCurrIndex; // logical index
+ int fScrollIndex; // logical index of top-most visible row
+ int fVisibleRowCount;
+ SkString* fStrCache;
+
+ void dirtyStrCache();
+ void ensureStrCache(int visibleCount);
+
+ int logicalToVisualIndex(int index) const { return index - fScrollIndex; }
+ void invalSelection();
+ bool getRowRect(int index, SkRect*) const;
+ void ensureSelectionIsVisible();
+
+ typedef SkWidgetView INHERITED;
+};
+
+//////////////////////////////////////////////////////////
+
+class SkGridView : public SkWidgetView {
+public:
+ SkGridView(uint32_t flags = 0);
+ virtual ~SkGridView();
+
+ void getCellSize(SkPoint*) const;
+ void setCellSize(SkScalar x, SkScalar y);
+
+ /** Return the index of the selected item, or -1 if none
+ */
+ int getSelection() const { return fCurrIndex; }
+ /** Set the index of the selected row, or -1 for none
+ */
+ void setSelection(int);
+
+ void moveSelectionUp();
+ void moveSelectionDown();
+
+ enum Attr {
+ kBG_Attr,
+ kHiliteCell_Attr,
+ kAttrCount
+ };
+ SkPaint& paint(Attr);
+
+ SkListSource* getListSource() const { return fSource; }
+ SkListSource* setListSource(SkListSource*);
+
+protected:
+ virtual void onDraw(SkCanvas*);
+ virtual void onSizeChange();
+ virtual bool onEvent(const SkEvent&);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+
+private:
+ SkView* fScrollBar;
+ SkPaint fPaint[kAttrCount];
+ SkListSource* fSource;
+ int fCurrIndex; // logical index
+
+ SkPoint fCellSize;
+ SkIPoint fVisibleCount;
+
+ int logicalToVisualIndex(int index) const { return index; }
+ void invalSelection();
+ bool getCellRect(int index, SkRect*) const;
+ void ensureSelectionIsVisible();
+
+ typedef SkWidgetView INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkWidgetViews.h b/include/views/SkWidgetViews.h
new file mode 100644
index 0000000000..9b3a8160d5
--- /dev/null
+++ b/include/views/SkWidgetViews.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkWidgetViews_DEFINED
+#define SkWidgetViews_DEFINED
+
+#include "SkView.h"
+
+
+enum SkWidgetEnum {
+ kBorder_WidgetEnum, //!< <sk-border>
+ kButton_WidgetEnum, //!< <sk-button>
+ kImage_WidgetEnum, //!< <sk-image>
+ kList_WidgetEnum, //!< <sk-list>
+ kProgress_WidgetEnum, //!< <sk-progress>
+ kScroll_WidgetEnum, //!< <sk-scroll>
+ kText_WidgetEnum, //!< <sk-text>
+
+ kWidgetEnumCount
+};
+
+//determines which skin to use
+enum SkinEnum {
+ kBorder_SkinEnum,
+ kButton_SkinEnum,
+ kProgress_SkinEnum,
+ kScroll_SkinEnum,
+ kStaticText_SkinEnum,
+
+ kSkinEnumCount
+};
+
+#include "SkAnimator.h"
+//used for inflates
+const char* get_skin_enum_path(SkinEnum se);
+void init_skin_anim(const char path[], SkAnimator* anim);
+void init_skin_anim(SkinEnum se, SkAnimator* anim);
+void init_skin_paint(SkinEnum se, SkPaint* paint);
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint);
+
+/** Given an enum value, return an instance of the specified widget.
+ If the enum is out of range, returns null
+*/
+SkView* SkWidgetFactory(SkWidgetEnum);
+/** Given the inflate/element name of a widget, return an instance of
+ the specified widget, or null if name does not match any known
+ widget type.
+*/
+SkView* SkWidgetFactory(const char name[]);
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkWidgetView : public SkView {
+public:
+ SkWidgetView();
+
+ const char* getLabel() const;
+ void getLabel(SkString* label) const;
+
+ void setLabel(const char[]);
+ void setLabel(const char[], size_t len);
+ void setLabel(const SkString&);
+
+ SkEvent& event() { return fEvent; }
+ const SkEvent& event() const { return fEvent; }
+
+ /** Returns true if the widget can post its event to its listeners.
+ */
+ bool postWidgetEvent();
+
+ /** Returns the sinkID of the widgetview that posted the event, or 0
+ */
+ static SkEventSinkID GetWidgetEventSinkID(const SkEvent&);
+
+protected:
+ /** called when the label changes. override in subclasses. default action invals the view's bounds.
+ called with the old and new labels, before the label has actually changed.
+ */
+ virtual void onLabelChange(const char oldLabel[], const char newLabel[]);
+ /** called before posting the event to our listeners. Override to add slots to the event
+ before posting. Return true to proceed with posting, or false to not post the event to any
+ listener. Note: the event passed in may not be the same as calling this->event().
+ Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot
+ at modifying the event (and possibly returning false to abort).
+ */
+ virtual bool onPrepareWidgetEvent(SkEvent* evt);
+
+ // overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ SkString fLabel;
+ SkEvent fEvent;
+
+ typedef SkView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkButtonView : public SkWidgetView {
+public:
+ // inflate: "sk-button"
+
+protected:
+ // overrides
+ virtual bool onEvent(const SkEvent&);
+private:
+ typedef SkWidgetView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkCheckButtonView : public SkWidgetView {
+public:
+ SkCheckButtonView();
+
+ // inflate: "sk-checkbutton"
+
+ enum CheckState {
+ kOff_CheckState, //!< inflate: check-state="off"
+ kOn_CheckState, //!< inflate: check-state="on"
+ kUnknown_CheckState //!< inflate: check-state="unknown"
+ };
+ CheckState getCheckState() const { return (CheckState)fCheckState; }
+ void setCheckState(CheckState);
+
+ /** use this to extract the CheckState from an event (i.e. one that as posted
+ by a SkCheckButtonView). Returns true if the proper slot was present in the event,
+ and sets state to that value. If no proper slot is found, returns false and does not
+ modify state.
+ */
+ static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state);
+
+protected:
+ // called when the check-state is about to change, but before it actually has
+ virtual void onCheckStateChange(CheckState oldState, CheckState newState);
+
+ // overrides
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+ virtual bool onPrepareWidgetEvent(SkEvent* evt);
+
+private:
+ uint8_t fCheckState;
+
+ typedef SkWidgetView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkTextBox.h"
+
+class SkStaticTextView : public SkView {
+public:
+ SkStaticTextView();
+ virtual ~SkStaticTextView();
+
+ enum Mode {
+ kFixedSize_Mode,
+ kAutoWidth_Mode,
+ kAutoHeight_Mode,
+
+ kModeCount
+ };
+ Mode getMode() const { return (Mode)fMode; }
+ void setMode(Mode);
+
+ SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; }
+ void setSpacingAlign(SkTextBox::SpacingAlign);
+
+ void getMargin(SkPoint* margin) const;
+ void setMargin(SkScalar dx, SkScalar dy);
+
+ size_t getText(SkString* text = NULL) const;
+ size_t getText(char text[] = NULL) const;
+ void setText(const SkString&);
+ void setText(const char text[]);
+ void setText(const char text[], size_t len);
+
+ void getPaint(SkPaint*) const;
+ void setPaint(const SkPaint&);
+
+protected:
+ // overrides
+ virtual void onDraw(SkCanvas*);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+ SkPoint fMargin;
+ SkString fText;
+ SkPaint fPaint;
+ uint8_t fMode;
+ uint8_t fSpacingAlign;
+
+ void computeSize();
+
+ typedef SkView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkAnimator;
+class SkListSource;
+class SkScrollBarView;
+
+class SkListView : public SkWidgetView {
+public:
+ SkListView();
+ virtual ~SkListView();
+
+ bool hasScrollBar() const { return fScrollBar != NULL; }
+ void setHasScrollBar(bool);
+
+ /** Return the number of visible rows
+ */
+ int getVisibleRowCount() const { return fVisibleRowCount; }
+ /** Return the index of the selected row, or -1 if none
+ */
+ int getSelection() const { return fCurrIndex; }
+ /** Set the index of the selected row, or -1 for none
+ */
+ void setSelection(int);
+ /** If possible, move the selection up and return true,
+ else do nothing and return false
+ If nothing is selected, select the last item (unless there are no items).
+ */
+ bool moveSelectionUp();
+ /** If possible, move the selection down and return true,
+ else do nothing and return false.
+ If nothing is selected, select the first item (unless there are no items).
+ */
+ bool moveSelectionDown();
+
+ SkListSource* getListSource() const { return fSource; }
+ SkListSource* setListSource(SkListSource*);
+
+ /** Call this in your event handler. If the specified event is from a SkListView,
+ then it returns the index of the selected item in this list, otherwise it
+ returns -1
+ */
+ static int GetWidgetEventListIndex(const SkEvent&);
+
+protected:
+ // overrides
+ virtual void onDraw(SkCanvas*);
+ virtual void onSizeChange();
+ virtual bool onEvent(const SkEvent&);
+ virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+ virtual bool onPrepareWidgetEvent(SkEvent*);
+
+private:
+ enum DirtyFlags {
+ kAnimCount_DirtyFlag = 0x01,
+ kAnimContent_DirtyFlag = 0x02
+ };
+ void dirtyCache(unsigned dirtyFlags);
+ bool ensureCache();
+
+ int logicalToVisualIndex(int index) const { return index - fScrollIndex; }
+ void invalSelection();
+ SkScalar getContentWidth() const;
+ bool getRowRect(int index, SkRect*) const;
+ void ensureSelectionIsVisible();
+ void ensureVisibleRowCount();
+
+ struct BindingRec;
+
+ enum Heights {
+ kNormal_Height,
+ kSelected_Height
+ };
+ SkListSource* fSource;
+ SkScrollBarView* fScrollBar;
+ SkAnimator* fAnims;
+ BindingRec* fBindings;
+ SkString fSkinName;
+ SkScalar fHeights[2];
+ int16_t fScrollIndex, fCurrIndex;
+ uint16_t fVisibleRowCount, fBindingCount;
+ SkBool8 fAnimContentDirty;
+ SkBool8 fAnimFocusDirty;
+
+ typedef SkWidgetView INHERITED;
+};
+
+class SkListSource : public SkRefCnt {
+public:
+ virtual int countFields();
+ virtual void getFieldName(int index, SkString* field);
+ /** Return the index of the named field, or -1 if not found */
+ virtual int findFieldIndex(const char field[]);
+
+ virtual int countRecords();
+ virtual void getRecord(int rowIndex, int fieldIndex, SkString* data);
+
+ virtual bool prepareWidgetEvent(SkEvent*, int rowIndex);
+
+ static SkListSource* Factory(const char name[]);
+};
+
+#endif
diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h
new file mode 100644
index 0000000000..fd4ce0a91c
--- /dev/null
+++ b/include/views/SkWindow.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkWindow_DEFINED
+#define SkWindow_DEFINED
+
+#include "SkView.h"
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkTDArray.h"
+
+#ifdef SK_BUILD_FOR_WINCEx
+ #define SHOW_FPS
+#endif
+//#define USE_GX_SCREEN
+
+class SkCanvas;
+
+class SkOSMenu;
+
+class SkWindow : public SkView {
+public:
+ SkWindow();
+ virtual ~SkWindow();
+
+ const SkBitmap& getBitmap() const { return fBitmap; }
+
+ void setConfig(SkBitmap::Config);
+ void resize(int width, int height, SkBitmap::Config config = SkBitmap::kNo_Config);
+ void eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
+ void eraseRGB(U8CPU r, U8CPU g, U8CPU b);
+
+ bool isDirty() const { return !fDirtyRgn.isEmpty(); }
+ bool update(SkIRect* updateArea, SkCanvas* = NULL);
+ // does not call through to onHandleInval(), but does force the fDirtyRgn
+ // to be wide open. Call before update() to ensure we redraw everything.
+ void forceInvalAll();
+ // return the bounds of the dirty/inval rgn, or [0,0,0,0] if none
+ const SkIRect& getDirtyBounds() const { return fDirtyRgn.getBounds(); }
+
+ bool handleClick(int x, int y, Click::State);
+ bool handleChar(SkUnichar);
+ bool handleKey(SkKey);
+ bool handleKeyUp(SkKey);
+ bool handleMenu(uint32_t os_cmd);
+
+ void addMenu(SkOSMenu*);
+
+ const char* getTitle() const { return fTitle.c_str(); }
+ void setTitle(const char title[]);
+
+ const SkMatrix& getMatrix() const { return fMatrix; }
+ void setMatrix(const SkMatrix&);
+ void preConcat(const SkMatrix&);
+ void postConcat(const SkMatrix&);
+
+protected:
+ virtual bool onEvent(const SkEvent&);
+ virtual bool onDispatchClick(int x, int y, Click::State);
+ // called if part of our bitmap is invalidated
+ virtual void onHandleInval(const SkIRect&);
+ virtual bool onHandleChar(SkUnichar);
+ virtual bool onHandleKey(SkKey);
+ virtual bool onHandleKeyUp(SkKey);
+ virtual void onAddMenu(const SkOSMenu*) {}
+ virtual void onSetTitle(const char title[]) {}
+
+ // overrides from SkView
+ virtual bool handleInval(const SkRect*);
+ virtual bool onGetFocusView(SkView** focus) const;
+ virtual bool onSetFocusView(SkView* focus);
+
+private:
+ SkBitmap::Config fConfig;
+ SkBitmap fBitmap;
+ SkRegion fDirtyRgn;
+ Click* fClick; // to track clicks
+
+ SkTDArray<SkOSMenu*> fMenus;
+
+ SkView* fFocusView;
+ bool fWaitingOnInval;
+
+ SkString fTitle;
+ SkMatrix fMatrix;
+
+ typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////
+
+#ifdef SK_USE_WXWIDGETS
+ #include "SkOSWindow_wxwidgets.h"
+#elif defined(SK_BUILD_FOR_MAC)
+ #include "SkOSWindow_Mac.h"
+#elif defined(SK_BUILD_FOR_WIN)
+ #include "SkOSWindow_Win.h"
+#elif defined(ANDROID)
+ #include "SkOSWindow_Android.h"
+#elif defined(SK_BUILD_FOR_UNIX)
+ #include "SkOSWindow_Unix.h"
+#elif defined(SK_BUILD_FOR_SDL)
+ #include "SkOSWindow_SDL.h"
+#elif defined(SK_BUILD_FOR_IOS)
+ #include "SkOSWindow_iOS.h"
+#endif
+
+#endif
+
diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
new file mode 100644
index 0000000000..c829b69b60
--- /dev/null
+++ b/samplecode/ClockFaceView.cpp
@@ -0,0 +1,255 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+static inline SkPMColor rgb2gray(SkPMColor c)
+{
+ unsigned r = SkGetPackedR32(c);
+ unsigned g = SkGetPackedG32(c);
+ unsigned b = SkGetPackedB32(c);
+
+ unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
+
+ return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
+}
+
+class SkGrayScaleColorFilter : public SkColorFilter {
+public:
+ virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
+ {
+ for (int i = 0; i < count; i++)
+ result[i] = rgb2gray(src[i]);
+ }
+};
+
+class SkChannelMaskColorFilter : public SkColorFilter {
+public:
+ SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask)
+ {
+ fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
+ }
+
+ virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
+ {
+ SkPMColor mask = fMask;
+ for (int i = 0; i < count; i++)
+ result[i] = src[i] & mask;
+ }
+
+private:
+ SkPMColor fMask;
+};
+
+///////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+#include "Sk2DPathEffect.h"
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+ Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix,
+ SkTDArray<SkPoint>* pts)
+ : Sk2DPathEffect(matrix), fRadius(radius), fPts(pts) {}
+
+ virtual void flatten(SkFlattenableWriteBuffer& buffer)
+ {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeScalar(fRadius);
+ }
+ virtual Factory getFactory() { return CreateProc; }
+
+protected:
+ virtual void begin(const SkIRect& uvBounds, SkPath* dst) {
+ if (fPts) {
+ fPts->reset();
+ }
+ this->INHERITED::begin(uvBounds, dst);
+ }
+// virtual void end(SkPath* dst) {}
+ virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
+ {
+ if (fPts) {
+ *fPts->append() = loc;
+ }
+ dst->addCircle(loc.fX, loc.fY, fRadius);
+ }
+
+ Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
+ {
+ fRadius = buffer.readScalar();
+ fPts = NULL;
+ }
+private:
+ SkScalar fRadius;
+ SkTDArray<SkPoint>* fPts;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+ {
+ return new Dot2DPathEffect(buffer);
+ }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+class InverseFillPE : public SkPathEffect {
+public:
+ InverseFillPE() {}
+ virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+ *dst = src;
+ dst->setFillType(SkPath::kInverseWinding_FillType);
+ return true;
+ }
+ virtual Factory getFactory() { return Factory; }
+protected:
+// InverseFillPE(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+private:
+ static SkFlattenable* Factory(SkFlattenableReadBuffer& buffer) {
+ return new InverseFillPE;
+ }
+ typedef SkPathEffect INHERITED;
+};
+
+static SkPathEffect* makepe(float interp, SkTDArray<SkPoint>* pts) {
+ SkMatrix lattice;
+ SkScalar rad = 3 + SkIntToScalar(4) * (1 - interp);
+ lattice.setScale(rad*2, rad*2, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ return new Dot2DPathEffect(rad, lattice, pts);
+}
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p, SkScalar interp) {
+ p.setPathEffect(makepe(interp, NULL))->unref();
+ rast->addLayer(p);
+#if 0
+ p.setPathEffect(new InverseFillPE())->unref();
+ p.setXfermodeMode(SkXfermode::kSrcIn_Mode);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ p.setAlpha((1 - interp) * 255);
+ rast->addLayer(p);
+#endif
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+#include "SkXfermode.h"
+
+static void apply_shader(SkPaint* paint, float scale)
+{
+ SkPaint p;
+ SkLayerRasterizer* rast = new SkLayerRasterizer;
+
+ p.setAntiAlias(true);
+ r7(rast, p, scale);
+ paint->setRasterizer(rast)->unref();
+
+ paint->setColor(SK_ColorBLUE);
+}
+
+class ClockFaceView : public SkView {
+ SkTypeface* fFace;
+ SkScalar fInterp;
+ SkScalar fDx;
+public:
+ ClockFaceView()
+ {
+ fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
+ fInterp = 0;
+ fDx = SK_Scalar1/64;
+ }
+
+ virtual ~ClockFaceView()
+ {
+ SkSafeUnref(fFace);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt)
+ {
+ if (SampleCode::TitleQ(*evt))
+ {
+ SampleCode::TitleR(evt, "Text Effects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas)
+ {
+// canvas->drawColor(0xFFDDDDDD);
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ static void drawdots(SkCanvas* canvas, const SkPaint& orig) {
+ SkTDArray<SkPoint> pts;
+ SkPathEffect* pe = makepe(0, &pts);
+
+ SkScalar width = -1;
+ SkPath path, dstPath;
+ orig.getTextPath("9", 1, 0, 0, &path);
+ pe->filterPath(&dstPath, path, &width);
+
+ SkPaint p;
+ p.setAntiAlias(true);
+ p.setStrokeWidth(10);
+ p.setColor(SK_ColorRED);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, pts.count(), pts.begin(),
+ p);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ SkScalar x = SkIntToScalar(20);
+ SkScalar y = SkIntToScalar(300);
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(240));
+ paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
+ SkTypeface::kBold));
+
+ SkString str("9");
+
+ paint.setTypeface(fFace);
+
+ apply_shader(&paint, fInterp);
+ canvas->drawText(str.c_str(), str.size(), x, y, paint);
+
+ // drawdots(canvas, paint);
+
+ if (false) {
+ fInterp += fDx;
+ if (fInterp > 1) {
+ fInterp = 1;
+ fDx = -fDx;
+ } else if (fInterp < 0) {
+ fInterp = 0;
+ fDx = -fDx;
+ }
+ this->inval(NULL);
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ClockFaceView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp
new file mode 100644
index 0000000000..2ae21196e0
--- /dev/null
+++ b/samplecode/OverView.cpp
@@ -0,0 +1,94 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkView.h"
+
+static const int N = 8;
+const SkScalar W = SkIntToScalar(640);
+const SkScalar H = SkIntToScalar(480);
+
+class OverView : public SkView {
+public:
+ OverView(int count, const SkViewFactory factories[]);
+ virtual ~OverView();
+
+protected:
+ virtual bool onEvent(const SkEvent&);
+ virtual void onSizeChange();
+
+ virtual void onDraw(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorLTGRAY);
+ }
+
+ virtual SkCanvas* beforeChildren(SkCanvas*);
+
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Overview");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual bool onSendClickToChildren(SkScalar x, SkScalar y) {
+ return false;
+ }
+
+ virtual Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ int ix = (int)(SkScalarDiv(x * N, W));
+ int iy = (int)(SkScalarDiv(y * N, H));
+ if (ix >= 0 && iy >= 0) {
+ SkEvent evt("set-curr-index");
+ evt.setFast32(iy * N + ix);
+ this->sendEventToParents(evt);
+ }
+ return NULL;
+ }
+
+private:
+ int fCount;
+ const SkViewFactory* fFactories;
+
+ typedef SkView INHERITED;
+};
+
+SkView* create_overview(int count, const SkViewFactory factories[]);
+SkView* create_overview(int count, const SkViewFactory factories[]) {
+ return SkNEW_ARGS(OverView, (count, factories));
+};
+
+OverView::OverView(int count, const SkViewFactory factories[]) {
+ fCount = count;
+ fFactories = factories;
+}
+
+OverView::~OverView() {
+}
+
+bool OverView::onEvent(const SkEvent& evt) {
+ return this->INHERITED::onEvent(evt);
+}
+
+void OverView::onSizeChange() {
+ this->detachAllChildren();
+
+ SkScalar locX = 0;
+ SkScalar locY = 0;
+ for (int i = 0; i < fCount; i++) {
+ SkView* view = fFactories[i]();
+ view->setVisibleP(true);
+ this->attachChildToBack(view)->unref();
+ view->setLoc(locX, locY);
+ view->setSize(W, H);
+ locX += W;
+ if ((i % N) == N - 1) {
+ locY += H;
+ locX = 0;
+ }
+ }
+}
+
+SkCanvas* OverView::beforeChildren(SkCanvas* canvas) {
+ canvas->scale(SK_Scalar1 / N, SK_Scalar1 / N);
+ return canvas;
+}
+
diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp
new file mode 100644
index 0000000000..34a33b0748
--- /dev/null
+++ b/samplecode/SampleAARects.cpp
@@ -0,0 +1,191 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+static SkBitmap createBitmap(int n) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
+ bitmap.allocPixels();
+ bitmap.eraseColor(SK_ColorGREEN);
+
+ SkCanvas canvas(bitmap);
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ paint.setColor(SK_ColorRED);
+ canvas.drawOval(r, paint);
+ paint.setColor(SK_ColorBLUE);
+ paint.setStrokeWidth(SkIntToScalar(n)/15);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas.drawLine(0, 0, r.fRight, r.fBottom, paint);
+ canvas.drawLine(0, r.fBottom, r.fRight, 0, paint);
+
+ return bitmap;
+}
+
+class AARectView : public SampleView {
+ SkBitmap fBitmap;
+ enum {
+ N = 64
+ };
+public:
+ AARectView() {
+ fBitmap = createBitmap(N);
+
+ fWidth = N;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "AA Rects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+ SkPaint bluePaint;
+ bluePaint.setARGB(0xff, 0x0, 0x0, 0xff);
+ SkPaint bmpPaint;
+ SkShader* bmpShader = SkShader::CreateBitmapShader(fBitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
+ bmpPaint.setShader(bmpShader);
+ bmpShader->unref();
+
+ bluePaint.setStrokeWidth(3);
+ bmpPaint.setStrokeWidth(3);
+
+ SkPaint paints[] = { bluePaint, bmpPaint };
+
+ SkRect rect;
+
+ SkScalar dx = SkIntToScalar(80);
+ SkScalar dy = SkIntToScalar(100);
+ SkMatrix matrix;
+ for (size_t p = 0; p < SK_ARRAY_COUNT(paints); ++p) {
+ for (int stroke = 0; stroke < 2; ++stroke) {
+ paints[p].setStyle(stroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+ for (int a = 0; a < 3; ++ a) {
+ paints[p].setAntiAlias(a > 0);
+ paints[p].setAlpha(a > 1 ? 0x80 : 0xff);
+
+ canvas->save();
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.f),
+ SkFloatToScalar(0.f),
+ SkFloatToScalar(40.f),
+ SkFloatToScalar(40.f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(40.5f),
+ SkFloatToScalar(40.5f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(40.f),
+ SkFloatToScalar(40.f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.75f),
+ SkFloatToScalar(0.75f),
+ SkFloatToScalar(40.75f),
+ SkFloatToScalar(40.75f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ canvas->translate(SkFloatToScalar(.33f), SkFloatToScalar(.67f));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f),
+ SkFloatToScalar(0.0f),
+ SkFloatToScalar(40.0f),
+ SkFloatToScalar(40.0f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ matrix.setRotate(SkFloatToScalar(45.f));
+ canvas->concat(matrix);
+ canvas->translate(SkFloatToScalar(20.0f / sqrtf(2.f)),
+ SkFloatToScalar(20.0f / sqrtf(2.f)));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(-20.0f),
+ SkFloatToScalar(-20.0f),
+ SkFloatToScalar(20.0f),
+ SkFloatToScalar(20.0f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ canvas->rotate(SkFloatToScalar(90.f));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f),
+ SkFloatToScalar(0.0f),
+ SkFloatToScalar(40.0f),
+ SkFloatToScalar(-40.0f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ canvas->rotate(SkFloatToScalar(90.f));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(40.5f),
+ SkFloatToScalar(-40.5f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ matrix.setScale(SkFloatToScalar(-1.f), SkFloatToScalar(-1.f));
+ canvas->concat(matrix);
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(-40.5f),
+ SkFloatToScalar(-40.5f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ matrix.setScale(SkFloatToScalar(2.1f), SkFloatToScalar(4.1f));
+ canvas->concat(matrix);
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.1f),
+ SkFloatToScalar(0.1f),
+ SkFloatToScalar(19.1f),
+ SkFloatToScalar(9.1f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->restore();
+ canvas->translate(0, dy);
+ }
+ }
+ }
+ }
+
+private:
+ int fWidth;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AARectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp
new file mode 100644
index 0000000000..abbf8f991d
--- /dev/null
+++ b/samplecode/SampleAll.cpp
@@ -0,0 +1,715 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkView.h"
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkAvoidXfermode.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+#include "SkEmbossMaskFilter.h"
+#include "SkGradientShader.h"
+#include "SkImageDecoder.h"
+#include "SkLayerRasterizer.h"
+#include "SkMath.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkComposeShader.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkTransparentShader.h"
+#include "SkTypeface.h"
+#include "SkUnitMappers.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#include <math.h>
+
+static inline SkPMColor rgb2gray(SkPMColor c) {
+ unsigned r = SkGetPackedR32(c);
+ unsigned g = SkGetPackedG32(c);
+ unsigned b = SkGetPackedB32(c);
+
+ unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
+
+ return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
+}
+
+class SkGrayScaleColorFilter : public SkColorFilter {
+public:
+ virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+ for (int i = 0; i < count; i++)
+ result[i] = rgb2gray(src[i]);
+ }
+};
+
+class SkChannelMaskColorFilter : public SkColorFilter {
+public:
+ SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
+ fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
+ }
+
+ virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+ SkPMColor mask = fMask;
+ for (int i = 0; i < count; i++) {
+ result[i] = src[i] & mask;
+ }
+ }
+
+private:
+ SkPMColor fMask;
+};
+
+///////////////////////////////////////////////////////////
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+ p.setMaskFilter(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+
+ p.setAlpha(0x11);
+ p.setStyle(SkPaint::kFill_Style);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ p.setAlpha(0x40);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*2);
+ rast->addLayer(p);
+}
+
+static void r2(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setStyle(SkPaint::kStrokeAndFill_Style);
+ p.setStrokeWidth(SK_Scalar1*4);
+ rast->addLayer(p);
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*3/2);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*3);
+ rast->addLayer(p);
+
+ p.setAlpha(0x20);
+ p.setStyle(SkPaint::kFill_Style);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setAlpha(0x60);
+ rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+ p.setAlpha(0xFF);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+
+ p.setXfermode(NULL);
+ rast->addLayer(p);
+}
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+ p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+ rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ p.setAntiAlias(false);
+ SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+ r5(rast2, p);
+ p.setRasterizer(rast2)->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+}
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+ Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
+ : Sk2DPathEffect(matrix), fRadius(radius) {}
+
+ virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeScalar(fRadius);
+ }
+ virtual Factory getFactory() { return CreateProc; }
+
+protected:
+ virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+ dst->addCircle(loc.fX, loc.fY, fRadius);
+ }
+
+ Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+ fRadius = buffer.readScalar();
+ }
+private:
+ SkScalar fRadius;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return new Dot2DPathEffect(buffer);
+ }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p) {
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
+ rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+
+ p.setPathEffect(NULL);
+ p.setXfermode(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+}
+
+class Line2DPathEffect : public Sk2DPathEffect {
+public:
+ Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
+ : Sk2DPathEffect(matrix), fWidth(width) {}
+
+ virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+ if (this->INHERITED::filterPath(dst, src, width)) {
+ *width = fWidth;
+ return true;
+ }
+ return false;
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+ virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fWidth);
+ }
+protected:
+ virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
+ if (ucount > 1) {
+ SkPoint src[2], dstP[2];
+
+ src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
+ SkIntToScalar(v) + SK_ScalarHalf);
+ src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
+ SkIntToScalar(v) + SK_ScalarHalf);
+ this->getMatrix().mapPoints(dstP, src, 2);
+
+ dst->moveTo(dstP[0]);
+ dst->lineTo(dstP[1]);
+ }
+ }
+
+ Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+ fWidth = buffer.readScalar();
+ }
+
+private:
+ SkScalar fWidth;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return new Line2DPathEffect(buffer); }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+ lattice.postRotate(SkIntToScalar(30), 0, 0);
+ p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+
+ p.setPathEffect(NULL);
+ p.setXfermode(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+static const struct {
+ SkColor fMul, fAdd;
+} gLightingColors[] = {
+ { 0x808080, 0x800000 }, // general case
+ { 0x707070, 0x707070 }, // no-pin case
+ { 0xFFFFFF, 0x800000 }, // just-add case
+ { 0x808080, 0x000000 }, // just-mul case
+ { 0xFFFFFF, 0x000000 } // identity case
+};
+
+static unsigned color_dist16(uint16_t a, uint16_t b) {
+ unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b));
+ unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b));
+ unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b));
+
+ return SkMax32(dr, SkMax32(dg, db));
+}
+
+static unsigned scale_dist(unsigned dist, unsigned scale) {
+ dist >>= 6;
+ dist = (dist << 2) | dist;
+ dist = (dist << 4) | dist;
+ return dist;
+
+// return SkAlphaMul(dist, scale);
+}
+
+static void apply_shader(SkPaint* paint, int index) {
+ raster_proc proc = gRastProcs[index];
+ if (proc) {
+ SkPaint p;
+ SkLayerRasterizer* rast = new SkLayerRasterizer;
+
+ p.setAntiAlias(true);
+ proc(rast, p);
+ paint->setRasterizer(rast)->unref();
+ }
+
+#if 1
+ SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
+ paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
+ paint->setColor(SK_ColorBLUE);
+#endif
+}
+
+class DemoView : public SampleView {
+public:
+ DemoView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Demo");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+ void makePath(SkPath& path) {
+ path.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(20),
+ SkPath::kCCW_Direction);
+ for (int index = 0; index < 10; index++) {
+ SkScalar x = SkFloatToScalar(cos(index / 10.0f * 2 * 3.1415925358f));
+ SkScalar y = SkFloatToScalar(sin(index / 10.0f * 2 * 3.1415925358f));
+ x *= index & 1 ? 7 : 14;
+ y *= index & 1 ? 7 : 14;
+ x += SkIntToScalar(20);
+ y += SkIntToScalar(20);
+ if (index == 0)
+ path.moveTo(x, y);
+ else
+ path.lineTo(x, y);
+ }
+ path.close();
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->save();
+ drawPicture(canvas, 0);
+ canvas->restore();
+
+ {
+ SkPicture picture;
+ SkCanvas* record = picture.beginRecording(320, 480);
+ drawPicture(record, 120);
+ canvas->translate(0, SkIntToScalar(120));
+
+ SkRect clip;
+ clip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
+ do {
+ canvas->save();
+ canvas->clipRect(clip);
+ picture.draw(canvas);
+ canvas->restore();
+ if (clip.fRight < SkIntToScalar(320))
+ clip.offset(SkIntToScalar(160), 0);
+ else if (clip.fBottom < SkIntToScalar(480))
+ clip.offset(-SkIntToScalar(320), SkIntToScalar(160));
+ else
+ break;
+ } while (true);
+ }
+ }
+
+ void drawPicture(SkCanvas* canvas, int spriteOffset) {
+ SkMatrix matrix; matrix.reset();
+ SkPaint paint;
+ SkPath path;
+ SkPoint start = {0, 0};
+ SkPoint stop = { SkIntToScalar(40), SkIntToScalar(40) };
+ SkRect rect = {0, 0, SkIntToScalar(40), SkIntToScalar(40) };
+ SkRect rect2 = {0, 0, SkIntToScalar(65), SkIntToScalar(20) };
+ SkScalar left = 0, top = 0, x = 0, y = 0;
+ size_t index;
+
+ char ascii[] = "ascii...";
+ size_t asciiLength = sizeof(ascii) - 1;
+ char utf8[] = "utf8" "\xe2\x80\xa6";
+ short utf16[] = {'u', 't', 'f', '1', '6', 0x2026 };
+ short utf16simple[] = {'u', 't', 'f', '1', '6', '!' };
+
+ makePath(path);
+ SkTDArray<SkPoint>(pos);
+ pos.setCount(asciiLength);
+ for (index = 0; index < asciiLength; index++)
+ pos[index].set(SkIntToScalar(index * 10), SkIntToScalar(index * 2));
+ SkTDArray<SkPoint>(pos2);
+ pos2.setCount(asciiLength);
+ for (index = 0; index < asciiLength; index++)
+ pos2[index].set(SkIntToScalar(index * 10), SkIntToScalar(20));
+
+ // shaders
+ SkPoint linearPoints[] = { { 0, 0, }, { SkIntToScalar(40), SkIntToScalar(40) } };
+ SkColor linearColors[] = { SK_ColorRED, SK_ColorBLUE };
+ SkScalar* linearPos = NULL;
+ int linearCount = 2;
+ SkShader::TileMode linearMode = SkShader::kMirror_TileMode;
+ SkUnitMapper* linearMapper = new SkDiscreteMapper(3);
+ SkAutoUnref unmapLinearMapper(linearMapper);
+ SkShader* linear = SkGradientShader::CreateLinear(linearPoints,
+ linearColors, linearPos, linearCount, linearMode, linearMapper);
+
+ SkPoint radialCenter = { SkIntToScalar(25), SkIntToScalar(25) };
+ SkScalar radialRadius = SkIntToScalar(25);
+ SkColor radialColors[] = { SK_ColorGREEN, SK_ColorGRAY, SK_ColorRED };
+ SkScalar radialPos[] = { 0, SkIntToScalar(3) / 5, SkIntToScalar(1)};
+ int radialCount = 3;
+ SkShader::TileMode radialMode = SkShader::kRepeat_TileMode;
+ SkUnitMapper* radialMapper = new SkCosineMapper();
+ SkAutoUnref unmapRadialMapper(radialMapper);
+ SkShader* radial = SkGradientShader::CreateRadial(radialCenter,
+ radialRadius, radialColors, radialPos, radialCount,
+ radialMode, radialMapper);
+
+ SkTransparentShader* transparentShader = new SkTransparentShader();
+ SkEmbossMaskFilter::Light light;
+ light.fDirection[0] = SK_Scalar1/2;
+ light.fDirection[1] = SK_Scalar1/2;
+ light.fDirection[2] = SK_Scalar1/3;
+ light.fAmbient = 0x48;
+ light.fSpecular = 0x80;
+ SkScalar radius = SkIntToScalar(12)/5;
+ SkEmbossMaskFilter* embossFilter = new SkEmbossMaskFilter(light,
+ radius);
+
+ SkXfermode* xfermode = SkXfermode::Create(SkXfermode::kXor_Mode);
+ SkColorFilter* lightingFilter = SkColorFilter::CreateLightingFilter(
+ 0xff89bc45, 0xff112233);
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(0), SkIntToScalar(5));
+ paint.setFlags(SkPaint::kAntiAlias_Flag | SkPaint::kFilterBitmap_Flag);
+ // !!! draw through a clip
+ paint.setColor(SK_ColorLTGRAY);
+ paint.setStyle(SkPaint::kFill_Style);
+ SkRect clip = {0, 0, SkIntToScalar(320), SkIntToScalar(120)};
+ canvas->clipRect(clip);
+ paint.setShader(SkShader::CreateBitmapShader(fTx,
+ SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode))->unref();
+ canvas->drawPaint(paint);
+ canvas->save();
+
+ // line (exercises xfermode, colorShader, colorFilter, filterShader)
+ paint.setColor(SK_ColorGREEN);
+ paint.setStrokeWidth(SkIntToScalar(10));
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setXfermode(xfermode)->unref();
+ paint.setColorFilter(lightingFilter)->unref();
+ canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green
+ paint.setXfermode(NULL);
+ paint.setColorFilter(NULL);
+
+ // rectangle
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->translate(SkIntToScalar(50), 0);
+ paint.setColor(SK_ColorYELLOW);
+ paint.setShader(linear)->unref();
+ paint.setPathEffect(pathEffectTest())->unref();
+ canvas->drawRect(rect, paint);
+ paint.setPathEffect(NULL);
+
+ // circle w/ emboss & transparent (exercises 3dshader)
+ canvas->translate(SkIntToScalar(50), 0);
+ paint.setMaskFilter(embossFilter)->unref();
+ canvas->drawOval(rect, paint);
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+ paint.setShader(transparentShader)->unref();
+ canvas->drawOval(rect, paint);
+ canvas->translate(0, SkIntToScalar(-10));
+
+ // path
+ canvas->translate(SkIntToScalar(50), 0);
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(5));
+ paint.setShader(radial)->unref();
+ paint.setMaskFilter(NULL);
+ canvas->drawPath(path, paint);
+
+ paint.setShader(NULL);
+ // bitmap, sprite
+ canvas->translate(SkIntToScalar(50), 0);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawBitmap(fBug, left, top, &paint);
+ canvas->translate(SkIntToScalar(30), 0);
+ canvas->drawSprite(fTb,
+ SkScalarRound(canvas->getTotalMatrix().getTranslateX()),
+ spriteOffset + 10, &paint);
+
+ canvas->translate(-SkIntToScalar(30), SkIntToScalar(30));
+ paint.setShader(shaderTest())->unref(); // test compose shader
+ canvas->drawRect(rect2, paint);
+ paint.setShader(NULL);
+
+ canvas->restore();
+ // text
+ canvas->translate(0, SkIntToScalar(60));
+ canvas->save();
+ paint.setColor(SK_ColorGRAY);
+ canvas->drawPosText(ascii, asciiLength, pos.begin(), paint);
+ canvas->drawPosText(ascii, asciiLength, pos2.begin(), paint);
+
+ canvas->translate(SkIntToScalar(50), 0);
+ paint.setColor(SK_ColorCYAN);
+ canvas->drawText(utf8, sizeof(utf8) - 1, x, y, paint);
+
+ canvas->translate(SkIntToScalar(30), 0);
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ matrix.setTranslate(SkIntToScalar(10), SkIntToScalar(10));
+ canvas->drawTextOnPath((void*) utf16, sizeof(utf16), path, &matrix, paint);
+ canvas->translate(0, SkIntToScalar(20));
+ canvas->drawTextOnPath((void*) utf16simple, sizeof(utf16simple), path, &matrix, paint);
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(60));
+ paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+ canvas->restore();
+ }
+
+ /*
+./SkColorFilter.h:25:class SkColorFilter : public SkFlattenable { -- abstract
+ static SkColorFilter* CreatXfermodeFilter() *** untested ***
+ static SkColorFilter* CreatePorterDuffFilter() *** untested ***
+ static SkColorFilter* CreateLightingFilter() -- tested
+./SkDrawLooper.h:9:class SkDrawLooper : public SkFlattenable { -- virtually abstract
+ ./SkBlurDrawLooper.h:9:class SkBlurDrawLooper : public SkDrawLooper { *** untested ***
+./SkMaskFilter.h:41:class SkMaskFilter : public SkFlattenable { -- abstract chmod +w .h
+ ./SkEmbossMaskFilter.h:27:class SkEmbossMaskFilter : public SkMaskFilter { -- tested
+./SkPathEffect.h:33:class SkPathEffect : public SkFlattenable { -- abstract
+ ./Sk1DPathEffect.h:27:class Sk1DPathEffect : public SkPathEffect { -- abstract
+ ./Sk1DPathEffect.h:48:class SkPath1DPathEffect : public Sk1DPathEffect { -- tested
+ ./Sk2DPathEffect.h:25:class Sk2DPathEffect : public SkPathEffect { *** untested ***
+ ./SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect { *** untested ***
+ ./SkDashPathEffect.h:27:class SkDashPathEffect : public SkPathEffect {
+ ./SkDiscretePathEffect.h:27:class SkDiscretePathEffect : public SkPathEffect {
+ ./SkPaint.h:760:class SkStrokePathEffect : public SkPathEffect {
+ ./SkPathEffect.h:58:class SkPairPathEffect : public SkPathEffect {
+ ./SkPathEffect.h:78:class SkComposePathEffect : public SkPairPathEffect {
+ ./SkPathEffect.h:114:class SkSumPathEffect : public SkPairPathEffect {
+./SkRasterizer.h:29:class SkRasterizer : public SkFlattenable {
+ ./SkLayerRasterizer.h:27:class SkLayerRasterizer : public SkRasterizer {
+./SkShader.h:36:class SkShader : public SkFlattenable {
+ ./SkColorFilter.h:59:class SkFilterShader : public SkShader {
+ ./SkColorShader.h:26:class SkColorShader : public SkShader {
+ ./SkShaderExtras.h:31:class SkComposeShader : public SkShader {
+ ./SkTransparentShader.h:23:class SkTransparentShader : public SkShader {
+./SkUnitMapper.h:24:class SkUnitMapper : public SkFlattenable {
+ ./SkUnitMapper.h:33:class SkDiscreteMapper : public SkUnitMapper {
+ ./SkUnitMapper.h:51:class SkFlipCosineMapper : public SkUnitMapper {
+./SkXfermode.h:32:class SkXfermode : public SkFlattenable {
+ ./SkAvoidXfermode.h:28:class SkAvoidXfermode : public SkXfermode { *** not done *** chmod +w .h .cpp
+ ./SkXfermode.h:54:class SkProcXfermode : public SkXfermode {
+ */
+
+ /*
+./SkBlurMaskFilter.h:25:class SkBlurMaskFilter {
+ chmod +w SkBlurMaskFilter.cpp
+./SkGradientShader.h:30:class SkGradientShader {
+ */
+ // save layer, bounder, looper
+ // matrix
+ // clip /path/region
+ // bitmap proc shader ?
+
+/* untested:
+SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect {
+*/
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fClickPt.set(x, y);
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ SkPathEffect* pathEffectTest() {
+ static const int gXY[] = { 1, 0, 0, -1, 2, -1, 3, 0, 2, 1, 0, 1 };
+ SkScalar gPhase = 0;
+ SkPath path;
+ path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
+ for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
+ path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
+ path.close();
+ path.offset(SkIntToScalar(-6), 0);
+ SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12),
+ gPhase, SkPath1DPathEffect::kRotate_Style);
+ SkPathEffect* inner = new SkDiscretePathEffect(SkIntToScalar(2),
+ SkIntToScalar(1)/10); // SkCornerPathEffect(SkIntToScalar(2));
+ SkPathEffect* result = new SkComposePathEffect(outer, inner);
+ outer->unref();
+ inner->unref();
+ return result;
+ }
+
+ SkPathEffect* pathEffectTest2() { // unsure this works (has no visible effect)
+ SkPathEffect* outer = new SkStrokePathEffect(SkIntToScalar(4),
+ SkPaint::kStroke_Style, SkPaint::kMiter_Join, SkPaint::kButt_Cap);
+ static const SkScalar intervals[] = {SkIntToScalar(1), SkIntToScalar(2),
+ SkIntToScalar(2), SkIntToScalar(1)};
+ SkPathEffect* inner = new SkDashPathEffect(intervals,
+ sizeof(intervals) / sizeof(intervals[0]), 0);
+ SkPathEffect* result = new SkSumPathEffect(outer, inner);
+ outer->unref();
+ inner->unref();
+ return result;
+ }
+
+ SkShader* shaderTest() {
+ SkPoint pts[] = { { 0, 0, }, { SkIntToScalar(100), 0 } };
+ SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+ SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL,
+ 2, SkShader::kClamp_TileMode);
+ pts[1].set(0, SkIntToScalar(100));
+ SkColor colors2[] = {SK_ColorBLACK, SkColorSetARGB(0x80, 0, 0, 0)};
+ SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors2, NULL,
+ 2, SkShader::kClamp_TileMode);
+ SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+ SkShader* result = new SkComposeShader(shaderA, shaderB, mode);
+ shaderA->unref();
+ shaderB->unref();
+ mode->unref();
+ return result;
+ }
+
+ virtual void startTest() {
+ SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/bugcirc.gif", &fBug);
+ SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/tbcirc.gif", &fTb);
+ SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/05psp04.gif", &fTx);
+ }
+
+ void drawRaster(SkCanvas* canvas) {
+ for (size_t index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++)
+ drawOneRaster(canvas);
+ }
+
+ void drawOneRaster(SkCanvas* canvas) {
+ canvas->save();
+
+ SkScalar x = SkIntToScalar(20);
+ SkScalar y = SkIntToScalar(40);
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(48));
+ paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
+ SkTypeface::kBold));
+
+ SkString str("GOOGLE");
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+ apply_shader(&paint, i);
+
+ // paint.setMaskFilter(NULL);
+ // paint.setColor(SK_ColorBLACK);
+
+#if 01
+ int index = i % SK_ARRAY_COUNT(gLightingColors);
+ paint.setColorFilter(SkColorFilter::CreateLightingFilter(
+ gLightingColors[index].fMul,
+ gLightingColors[index].fAdd))->unref();
+#endif
+
+ canvas->drawText(str.c_str(), str.size(), x, y, paint);
+ SkRect oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y };
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawOval(oval, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ y += paint.getFontSpacing();
+ }
+
+ canvas->restore();
+
+ if (1) {
+ SkAvoidXfermode mode(SK_ColorWHITE, 0xFF,
+ SkAvoidXfermode::kTargetColor_Mode);
+ SkPaint paint;
+ x += SkIntToScalar(20);
+ SkRect r = { x, 0, x + SkIntToScalar(360), SkIntToScalar(700) };
+ paint.setXfermode(&mode);
+ paint.setColor(SK_ColorGREEN);
+ paint.setAntiAlias(true);
+ canvas->drawOval(r, paint);
+ }
+ }
+
+private:
+ SkPoint fClickPt;
+ SkBitmap fBug, fTb, fTx;
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DemoView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleAnimatedGradient.cpp b/samplecode/SampleAnimatedGradient.cpp
new file mode 100644
index 0000000000..a7b2a4638f
--- /dev/null
+++ b/samplecode/SampleAnimatedGradient.cpp
@@ -0,0 +1,90 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+
+class GradientView : public SampleView {
+public:
+ GradientView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ struct GradData {
+ int fCount;
+ const SkColor* fColors;
+ const SkScalar* fPos;
+ };
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Gradients");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkPoint p = SkPoint::Make(0,0);
+ SkPoint q = SkPoint::Make(100,100);
+ SkPoint pts[] = {p, q};
+
+ SkScalar t, temp, x, y;
+ SkColor gColors[] = {
+ SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+ };
+ t = SampleCode::GetAnimScalar(SkIntToScalar(2), SkIntToScalar(20));
+ temp = SampleCode::GetAnimScalar(SkIntToScalar(1), SkIntToScalar(8));
+ SkScalar step = SK_ScalarPI / (10);
+ SkScalar angle = t * step;
+ x = SkScalarSinCos(angle, &y);
+ SkScalar colorPositions[] = { 0, 0.1 + x, 0.4 + y, 0.9 - x + y, 1.0};
+ GradData data = { 5, gColors, colorPositions };
+
+
+ SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(200) };
+ SkShader* shader1 = SkGradientShader::CreateLinear(
+ pts, data.fColors, data.fPos,data.fCount,
+ SkShader::kMirror_TileMode);
+ paint.setShader(shader1)->unref();
+
+ canvas->drawRect(r, paint);
+
+
+ SkPoint s = SkPoint::Make(100,100);
+ SkShader* shader2 = SkGradientShader::CreateRadial(
+ s, 100, data.fColors, data.fPos, data.fCount,
+ SkShader::kMirror_TileMode);
+ paint.setShader(shader2)->unref();
+ canvas->translate(250, 0);
+ canvas->drawRect(r, paint);
+
+ SkShader* shader3 = SkGradientShader::CreateTwoPointRadial(
+ p, 0, q, 100, data.fColors, data.fPos, data.fCount,
+ SkShader::kMirror_TileMode);
+ paint.setShader(shader3)->unref();
+ canvas->translate(0, 250);
+ canvas->drawRect(r, paint);
+
+ SkShader* shader4 = SkGradientShader::CreateSweep(
+ 100, 100, data.fColors, data.fPos, data.fCount);
+
+ paint.setShader(shader4)->unref();
+ canvas->translate(-250, 0);
+ canvas->drawRect(r, paint);
+
+ this->inval(NULL);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GradientView; }
+static SkViewRegister reg(MyFactory); \ No newline at end of file
diff --git a/samplecode/SampleAnimator.cpp b/samplecode/SampleAnimator.cpp
new file mode 100644
index 0000000000..99173fc8ab
--- /dev/null
+++ b/samplecode/SampleAnimator.cpp
@@ -0,0 +1,159 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+#include "SkAnimator.h"
+#include "SkStream.h"
+#include "SkDOM.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkAnimatorView : public SkView {
+public:
+ SkAnimatorView();
+ virtual ~SkAnimatorView();
+
+ void setURIBase(const char dir[]);
+
+ SkAnimator* getAnimator() const { return fAnimator; }
+
+ bool decodeFile(const char path[]);
+ bool decodeMemory(const void* buffer, size_t size);
+ bool decodeStream(SkStream* stream);
+
+protected:
+ // overrides
+ virtual void onDraw(SkCanvas*);
+
+private:
+ SkString fBaseURI;
+ SkAnimator* fAnimator;
+
+ typedef SkView INHERITED;
+};
+
+SkAnimatorView::SkAnimatorView() : fAnimator(NULL) {}
+
+SkAnimatorView::~SkAnimatorView() {
+ delete fAnimator;
+}
+
+void SkAnimatorView::setURIBase(const char dir[]) {
+ fBaseURI.set(dir);
+}
+
+bool SkAnimatorView::decodeFile(const char path[]) {
+ SkFILEStream* is = new SkFILEStream(path);
+ SkAutoUnref aur(is);
+ return is->isValid() && this->decodeStream(is);
+}
+
+bool SkAnimatorView::decodeMemory(const void* buffer, size_t size) {
+ SkMemoryStream* is = new SkMemoryStream(buffer, size);
+ SkAutoUnref aur(is);
+ return this->decodeStream(is);
+}
+
+static const SkDOMNode* find_nodeID(const SkDOM& dom,
+ const SkDOMNode* node, const char name[]) {
+ if (NULL == node) {
+ node = dom.getRootNode();
+ }
+ do {
+ const char* idval = dom.findAttr(node, "id");
+ if (idval && !strcmp(idval, name)) {
+ return node;
+ }
+ const SkDOMNode* child = dom.getFirstChild(node);
+ if (child) {
+ const SkDOMNode* found = find_nodeID(dom, child, name);
+ if (found) {
+ return found;
+ }
+ }
+ } while ((node = dom.getNextSibling(node)) != NULL);
+ return NULL;
+}
+
+bool SkAnimatorView::decodeStream(SkStream* stream) {
+ delete fAnimator;
+ fAnimator = new SkAnimator;
+ fAnimator->setURIBase(fBaseURI.c_str());
+#if 0
+ if (!fAnimator->decodeStream(stream)) {
+ delete fAnimator;
+ fAnimator = NULL;
+ return false;
+ }
+#else
+ size_t len = stream->getLength();
+ char* text = (char*)sk_malloc_throw(len);
+ stream->read(text, len);
+ SkDOM dom;
+ const SkDOM::Node* root = dom.build(text, len);
+ if (NULL == root) {
+ return false;
+ }
+ if (!fAnimator->decodeDOM(dom, root)) {
+ delete fAnimator;
+ fAnimator = NULL;
+ return false;
+ }
+ for (int i = 0; i <= 10; i++) {
+ SkString name("glyph");
+ name.appendS32(i);
+ const SkDOM::Node* node = find_nodeID(dom, NULL, name.c_str());
+ SkASSERT(node);
+ SkRect r;
+ dom.findScalar(node, "left", &r.fLeft);
+ dom.findScalar(node, "top", &r.fTop);
+ dom.findScalar(node, "width", &r.fRight); r.fRight += r.fLeft;
+ dom.findScalar(node, "height", &r.fBottom); r.fBottom += r.fTop;
+ SkDebugf("--- %s [%g %g %g %g]\n", name.c_str(),
+ r.fLeft, r.fTop, r.fRight, r.fBottom);
+ }
+#endif
+ return true;
+}
+
+#include "SkTime.h"
+
+void SkAnimatorView::onDraw(SkCanvas* canvas) {
+ if (fAnimator) {
+ canvas->drawColor(SK_ColorWHITE);
+ fAnimator->draw(canvas, 0);
+#if 0
+ canvas->save();
+ canvas->translate(120, 30);
+ canvas->scale(0.5, 0.5);
+ fAnimator->draw(canvas, 0);
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(190, 40);
+ canvas->scale(0.25, 0.25);
+ fAnimator->draw(canvas, 0);
+ canvas->restore();
+
+ this->inval(NULL);
+#endif
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() {
+ SkAnimatorView* av = new SkAnimatorView;
+// av->decodeFile("/skimages/test.xml");
+#if 0
+ av->setURIBase("/skia/trunk/animations/");
+ av->decodeFile("/skia/trunk/animations/checkbox.xml");
+#else
+ av->setURIBase("/");
+ av->decodeFile("/testanim.txt");
+#endif
+ return av;
+}
+
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
new file mode 100644
index 0000000000..3efffe6de7
--- /dev/null
+++ b/samplecode/SampleApp.cpp
@@ -0,0 +1,1647 @@
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGpuCanvas.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkTime.h"
+#include "SkWindow.h"
+
+#include "SampleCode.h"
+#include "GrContext.h"
+#include "SkTouchGesture.h"
+#include "SkTypeface.h"
+
+#define TEST_GPIPEx
+
+#ifdef TEST_GPIPE
+#define PIPE_FILE
+#define FILE_PATH "/path/to/drawing.data"
+#endif
+
+#define USE_ARROWS_FOR_ZOOM true
+//#define DEFAULT_TO_GPU
+
+extern SkView* create_overview(int, const SkViewFactory[]);
+
+#define SK_SUPPORT_GL
+
+#define ANIMATING_EVENTTYPE "nextSample"
+#define ANIMATING_DELAY 750
+
+#ifdef SK_DEBUG
+ #define FPS_REPEAT_MULTIPLIER 1
+#else
+ #define FPS_REPEAT_MULTIPLIER 10
+#endif
+#define FPS_REPEAT_COUNT (10 * FPS_REPEAT_MULTIPLIER)
+
+#ifdef SK_SUPPORT_GL
+ #include "GrGLConfig.h"
+#endif
+
+///////////////
+static const char view_inval_msg[] = "view-inval-msg";
+
+static void postInvalDelay(SkEventSinkID sinkID) {
+ SkEvent* evt = new SkEvent(view_inval_msg);
+ evt->post(sinkID, 1);
+}
+
+static bool isInvalEvent(const SkEvent& evt) {
+ return evt.isType(view_inval_msg);
+}
+//////////////////
+
+SkViewRegister* SkViewRegister::gHead;
+SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
+ static bool gOnce;
+ if (!gOnce) {
+ gHead = NULL;
+ gOnce = true;
+ }
+
+ fChain = gHead;
+ gHead = this;
+}
+
+#if defined(SK_SUPPORT_GL)
+ #define SK_USE_SHADERS
+#endif
+
+#ifdef SK_BUILD_FOR_MAC
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFURLAccess.h>
+
+static void testpdf() {
+ CFStringRef path = CFStringCreateWithCString(NULL, "/test.pdf",
+ kCFStringEncodingUTF8);
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path,
+ kCFURLPOSIXPathStyle,
+ false);
+ CFRelease(path);
+ CGRect box = CGRectMake(0, 0, 8*72, 10*72);
+ CGContextRef cg = CGPDFContextCreateWithURL(url, &box, NULL);
+ CFRelease(url);
+
+ CGContextBeginPage(cg, &box);
+ CGRect r = CGRectMake(10, 10, 40 + 0.5, 50 + 0.5);
+ CGContextFillEllipseInRect(cg, r);
+ CGContextEndPage(cg);
+ CGContextRelease(cg);
+
+ if (false) {
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kA8_Config, 64, 64);
+ bm.allocPixels();
+ bm.eraseColor(0);
+
+ SkCanvas canvas(bm);
+
+ }
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+enum FlipAxisEnum {
+ kFlipAxis_X = (1 << 0),
+ kFlipAxis_Y = (1 << 1)
+};
+
+enum SkTriState {
+ kFalse_SkTriState,
+ kTrue_SkTriState,
+ kUnknown_SkTriState,
+};
+
+static SkTriState cycle_tristate(SkTriState state) {
+ static const SkTriState gCycle[] = {
+ /* kFalse_SkTriState -> */ kUnknown_SkTriState,
+ /* kTrue_SkTriState -> */ kFalse_SkTriState,
+ /* kUnknown_SkTriState -> */ kTrue_SkTriState,
+ };
+ return gCycle[state];
+}
+
+#include "SkDrawFilter.h"
+
+class FlagsDrawFilter : public SkDrawFilter {
+public:
+ FlagsDrawFilter(SkTriState lcd, SkTriState aa, SkTriState filter,
+ SkTriState hinting) :
+ fLCDState(lcd), fAAState(aa), fFilterState(filter), fHintingState(hinting) {}
+
+ virtual void filter(SkPaint* paint, Type t) {
+ if (kText_Type == t && kUnknown_SkTriState != fLCDState) {
+ paint->setLCDRenderText(kTrue_SkTriState == fLCDState);
+ }
+ if (kUnknown_SkTriState != fAAState) {
+ paint->setAntiAlias(kTrue_SkTriState == fAAState);
+ }
+ if (kUnknown_SkTriState != fFilterState) {
+ paint->setFilterBitmap(kTrue_SkTriState == fFilterState);
+ }
+ if (kUnknown_SkTriState != fHintingState) {
+ paint->setHinting(kTrue_SkTriState == fHintingState ?
+ SkPaint::kNormal_Hinting :
+ SkPaint::kSlight_Hinting);
+ }
+ }
+
+private:
+ SkTriState fLCDState;
+ SkTriState fAAState;
+ SkTriState fFilterState;
+ SkTriState fHintingState;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define MAX_ZOOM_LEVEL 8
+#define MIN_ZOOM_LEVEL -8
+
+static const char gCharEvtName[] = "SampleCode_Char_Event";
+static const char gKeyEvtName[] = "SampleCode_Key_Event";
+static const char gTitleEvtName[] = "SampleCode_Title_Event";
+static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
+static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
+
+bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
+ if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
+ if (outUni) {
+ *outUni = evt.getFast32();
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
+ if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
+ if (outKey) {
+ *outKey = (SkKey)evt.getFast32();
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SampleCode::TitleQ(const SkEvent& evt) {
+ return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
+}
+
+void SampleCode::TitleR(SkEvent* evt, const char title[]) {
+ SkASSERT(evt && TitleQ(*evt));
+ evt->setString(gTitleEvtName, title);
+}
+
+bool SampleCode::PrefSizeQ(const SkEvent& evt) {
+ return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
+}
+
+void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
+ SkASSERT(evt && PrefSizeQ(*evt));
+ SkScalar size[2];
+ size[0] = width;
+ size[1] = height;
+ evt->setScalars(gPrefSizeEvtName, 2, size);
+}
+
+bool SampleCode::FastTextQ(const SkEvent& evt) {
+ return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkMSec gAnimTime;
+static SkMSec gAnimTimePrev;
+
+SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
+SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
+SkScalar SampleCode::GetAnimSecondsDelta() {
+ return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
+}
+
+SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
+ // since gAnimTime can be up to 32 bits, we can't convert it to a float
+ // or we'll lose the low bits. Hence we use doubles for the intermediate
+ // calculations
+ double seconds = (double)gAnimTime / 1000.0;
+ double value = SkScalarToDouble(speed) * seconds;
+ if (period) {
+ value = ::fmod(value, SkScalarToDouble(period));
+ }
+ return SkDoubleToScalar(value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* curr_view(SkWindow* wind) {
+ SkView::F2BIter iter(wind);
+ return iter.next();
+}
+
+class SampleWindow : public SkOSWindow {
+ SkTDArray<SkViewFactory> fSamples;
+public:
+ SampleWindow(void* hwnd);
+ virtual ~SampleWindow();
+
+ virtual void draw(SkCanvas* canvas);
+
+protected:
+ virtual void onDraw(SkCanvas* canvas);
+ virtual bool onHandleKey(SkKey key);
+ virtual bool onHandleChar(SkUnichar);
+ virtual void onSizeChange();
+
+ virtual SkCanvas* beforeChildren(SkCanvas*);
+ virtual void afterChildren(SkCanvas*);
+ virtual void beforeChild(SkView* child, SkCanvas* canvas);
+ virtual void afterChild(SkView* child, SkCanvas* canvas);
+
+ virtual bool onEvent(const SkEvent& evt);
+ virtual bool onQuery(SkEvent* evt);
+
+ virtual bool onDispatchClick(int x, int y, Click::State);
+ virtual bool onClick(Click* click);
+ virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+
+#if 0
+ virtual bool handleChar(SkUnichar uni);
+ virtual bool handleEvent(const SkEvent& evt);
+ virtual bool handleKey(SkKey key);
+ virtual bool handleKeyUp(SkKey key);
+ virtual bool onHandleKeyUp(SkKey key);
+#endif
+
+private:
+ int fCurrIndex;
+
+ SkPicture* fPicture;
+ SkGpuCanvas* fGpuCanvas;
+ GrContext* fGrContext;
+ SkPath fClipPath;
+
+ SkTouchGesture fGesture;
+ int fZoomLevel;
+ SkScalar fZoomScale;
+
+ enum CanvasType {
+ kRaster_CanvasType,
+ kPicture_CanvasType,
+ kGPU_CanvasType
+ };
+ CanvasType fCanvasType;
+
+ bool fUseClip;
+ bool fNClip;
+ bool fRepeatDrawing;
+ bool fAnimating;
+ bool fRotate;
+ bool fScale;
+ bool fRequestGrabImage;
+ bool fUsePipe;
+ bool fMeasureFPS;
+ SkMSec fMeasureFPS_Time;
+
+ // The following are for the 'fatbits' drawing
+ // Latest position of the mouse.
+ int fMouseX, fMouseY;
+ int fFatBitsScale;
+ // Used by the text showing position and color values.
+ SkTypeface* fTypeface;
+ bool fShowZoomer;
+
+ SkTriState fLCDState;
+ SkTriState fAAState;
+ SkTriState fFilterState;
+ SkTriState fHintingState;
+ unsigned fFlipAxis;
+
+ int fScrollTestX, fScrollTestY;
+
+ bool make3DReady();
+ void changeZoomLevel(int delta);
+
+ void loadView(SkView*);
+ void updateTitle();
+ bool nextSample();
+
+ void toggleZoomer();
+ bool zoomIn();
+ bool zoomOut();
+ void updatePointer(int x, int y);
+ void showZoomer(SkCanvas* canvas);
+
+ void postAnimatingEvent() {
+ if (fAnimating) {
+ SkEvent* evt = new SkEvent(ANIMATING_EVENTTYPE);
+ evt->post(this->getSinkID(), ANIMATING_DELAY);
+ }
+ }
+
+
+ static CanvasType cycle_canvastype(CanvasType);
+
+ typedef SkOSWindow INHERITED;
+};
+
+bool SampleWindow::zoomIn()
+{
+ // Arbitrarily decided
+ if (fFatBitsScale == 25) return false;
+ fFatBitsScale++;
+ this->inval(NULL);
+ return true;
+}
+
+bool SampleWindow::zoomOut()
+{
+ if (fFatBitsScale == 1) return false;
+ fFatBitsScale--;
+ this->inval(NULL);
+ return true;
+}
+
+void SampleWindow::toggleZoomer()
+{
+ fShowZoomer = !fShowZoomer;
+ this->inval(NULL);
+}
+
+void SampleWindow::updatePointer(int x, int y)
+{
+ fMouseX = x;
+ fMouseY = y;
+ if (fShowZoomer) {
+ this->inval(NULL);
+ }
+}
+
+bool SampleWindow::make3DReady() {
+
+#if defined(SK_SUPPORT_GL)
+ if (attachGL()) {
+ if (NULL != fGrContext) {
+ // various gr lifecycle tests
+ #if 0
+ fGrContext->freeGpuResources();
+ #elif 0
+ // this will leak resources.
+ fGrContext->contextLost();
+ #elif 0
+ GrAssert(1 == fGrContext->refcnt());
+ fGrContext->unref();
+ fGrContext = NULL;
+ #endif
+ }
+
+ if (NULL == fGrContext) {
+ #if defined(SK_USE_SHADERS)
+ fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL);
+ #else
+ fGrContext = GrContext::Create(kOpenGL_Fixed_GrEngine, NULL);
+ #endif
+ SkDebugf("---- constructor\n");
+ }
+
+ if (NULL != fGrContext) {
+ return true;
+ } else {
+ detachGL();
+ }
+ }
+#endif
+ SkDebugf("Failed to setup 3D");
+ return false;
+}
+
+SampleWindow::CanvasType SampleWindow::cycle_canvastype(CanvasType ct) {
+ static const CanvasType gCT[] = {
+ kPicture_CanvasType,
+ kGPU_CanvasType,
+ kRaster_CanvasType
+ };
+ return gCT[ct];
+}
+
+SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) {
+#ifdef PIPE_FILE
+ //Clear existing file or create file if it doesn't exist
+ FILE* f = fopen(FILE_PATH, "wb");
+ fclose(f);
+#endif
+
+ fPicture = NULL;
+ fGpuCanvas = NULL;
+
+ fGrContext = NULL;
+
+#ifdef DEFAULT_TO_GPU
+ fCanvasType = kGPU_CanvasType;
+#else
+ fCanvasType = kRaster_CanvasType;
+#endif
+ fUseClip = false;
+ fNClip = false;
+ fRepeatDrawing = false;
+ fAnimating = false;
+ fRotate = false;
+ fScale = false;
+ fRequestGrabImage = false;
+ fUsePipe = false;
+ fMeasureFPS = false;
+ fLCDState = kUnknown_SkTriState;
+ fAAState = kUnknown_SkTriState;
+ fFilterState = kUnknown_SkTriState;
+ fHintingState = kUnknown_SkTriState;
+ fFlipAxis = 0;
+ fScrollTestX = fScrollTestY = 0;
+
+ fMouseX = fMouseY = 0;
+ fFatBitsScale = 8;
+ fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
+ fShowZoomer = false;
+
+ fZoomLevel = 0;
+ fZoomScale = SK_Scalar1;
+
+// this->setConfig(SkBitmap::kRGB_565_Config);
+ this->setConfig(SkBitmap::kARGB_8888_Config);
+ this->setVisibleP(true);
+ this->setClipToBounds(false);
+
+ {
+ const SkViewRegister* reg = SkViewRegister::Head();
+ while (reg) {
+ *fSamples.append() = reg->factory();
+ reg = reg->next();
+ }
+ }
+ fCurrIndex = 0;
+ this->loadView(fSamples[fCurrIndex]());
+
+#ifdef SK_BUILD_FOR_MAC
+ testpdf();
+#endif
+}
+
+SampleWindow::~SampleWindow() {
+ delete fPicture;
+ delete fGpuCanvas;
+ if (NULL != fGrContext) {
+ fGrContext->unref();
+ }
+ fTypeface->unref();
+}
+
+static SkBitmap capture_bitmap(SkCanvas* canvas) {
+ SkBitmap bm;
+ const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
+ src.copyTo(&bm, src.config());
+ return bm;
+}
+
+static bool bitmap_diff(SkCanvas* canvas, const SkBitmap& orig,
+ SkBitmap* diff) {
+ const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
+
+ SkAutoLockPixels alp0(src);
+ SkAutoLockPixels alp1(orig);
+ for (int y = 0; y < src.height(); y++) {
+ const void* srcP = src.getAddr(0, y);
+ const void* origP = orig.getAddr(0, y);
+ size_t bytes = src.width() * src.bytesPerPixel();
+ if (memcmp(srcP, origP, bytes)) {
+ SkDebugf("---------- difference on line %d\n", y);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void drawText(SkCanvas* canvas, SkString string, SkScalar left, SkScalar top, SkPaint& paint)
+{
+ SkColor desiredColor = paint.getColor();
+ paint.setColor(SK_ColorWHITE);
+ const char* c_str = string.c_str();
+ size_t size = string.size();
+ SkRect bounds;
+ paint.measureText(c_str, size, &bounds);
+ bounds.offset(left, top);
+ SkScalar inset = SkIntToScalar(-2);
+ bounds.inset(inset, inset);
+ canvas->drawRect(bounds, paint);
+ if (desiredColor != SK_ColorBLACK) {
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawText(c_str, size, left + SK_Scalar1, top + SK_Scalar1, paint);
+ }
+ paint.setColor(desiredColor);
+ canvas->drawText(c_str, size, left, top, paint);
+}
+
+#define XCLIP_N 8
+#define YCLIP_N 8
+
+void SampleWindow::draw(SkCanvas* canvas) {
+ // update the animation time
+ gAnimTimePrev = gAnimTime;
+ gAnimTime = SkTime::GetMSecs();
+
+ SkScalar cx = SkScalarHalf(this->width());
+ SkScalar cy = SkScalarHalf(this->height());
+
+ if (fZoomLevel) {
+ SkMatrix m;
+ SkPoint center;
+ m = canvas->getTotalMatrix();//.invert(&m);
+ m.mapXY(cx, cy, &center);
+ cx = center.fX;
+ cy = center.fY;
+
+ m.setTranslate(-cx, -cy);
+ m.postScale(fZoomScale, fZoomScale);
+ m.postTranslate(cx, cy);
+
+ canvas->concat(m);
+ }
+
+ if (fFlipAxis) {
+ SkMatrix m;
+ m.setTranslate(cx, cy);
+ if (fFlipAxis & kFlipAxis_X) {
+ m.preScale(-SK_Scalar1, SK_Scalar1);
+ }
+ if (fFlipAxis & kFlipAxis_Y) {
+ m.preScale(SK_Scalar1, -SK_Scalar1);
+ }
+ m.preTranslate(-cx, -cy);
+ canvas->concat(m);
+ }
+
+ // Apply any gesture matrix
+ if (true) {
+ const SkMatrix& localM = fGesture.localM();
+ if (localM.getType() & SkMatrix::kScale_Mask) {
+ canvas->setExternalMatrix(&localM);
+ }
+ canvas->concat(localM);
+ canvas->concat(fGesture.globalM());
+
+ if (fGesture.isActive()) {
+ this->inval(NULL);
+ }
+ }
+
+ if (fNClip) {
+ this->INHERITED::draw(canvas);
+ SkBitmap orig = capture_bitmap(canvas);
+
+ const SkScalar w = this->width();
+ const SkScalar h = this->height();
+ const SkScalar cw = w / XCLIP_N;
+ const SkScalar ch = h / YCLIP_N;
+ for (int y = 0; y < YCLIP_N; y++) {
+ SkRect r;
+ r.fTop = y * ch;
+ r.fBottom = (y + 1) * ch;
+ if (y == YCLIP_N - 1) {
+ r.fBottom = h;
+ }
+ for (int x = 0; x < XCLIP_N; x++) {
+ SkAutoCanvasRestore acr(canvas, true);
+ r.fLeft = x * cw;
+ r.fRight = (x + 1) * cw;
+ if (x == XCLIP_N - 1) {
+ r.fRight = w;
+ }
+ canvas->clipRect(r);
+ this->INHERITED::draw(canvas);
+ }
+ }
+
+ SkBitmap diff;
+ if (bitmap_diff(canvas, orig, &diff)) {
+ }
+ } else {
+ this->INHERITED::draw(canvas);
+ }
+ if (fShowZoomer && fCanvasType != kGPU_CanvasType) {
+ // In the GPU case, INHERITED::draw calls beforeChildren, which
+ // creates an SkGpuCanvas. All further draw calls are directed
+ // at that canvas, which is deleted in afterChildren (which is
+ // also called by draw), so we cannot show the zoomer here.
+ // Instead, we call it inside afterChildren.
+ showZoomer(canvas);
+ }
+}
+
+void SampleWindow::showZoomer(SkCanvas* canvas) {
+ int count = canvas->save();
+ canvas->resetMatrix();
+ // Ensure the mouse position is on screen.
+ int width = SkScalarRound(this->width());
+ int height = SkScalarRound(this->height());
+ if (fMouseX >= width) fMouseX = width - 1;
+ else if (fMouseX < 0) fMouseX = 0;
+ if (fMouseY >= height) fMouseY = height - 1;
+ else if (fMouseY < 0) fMouseY = 0;
+
+ SkBitmap bitmap = capture_bitmap(canvas);
+ bitmap.lockPixels();
+
+ // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle.
+ int zoomedWidth = (width >> 1) | 1;
+ int zoomedHeight = (height >> 1) | 1;
+ SkIRect src;
+ src.set(0, 0, zoomedWidth / fFatBitsScale, zoomedHeight / fFatBitsScale);
+ src.offset(fMouseX - (src.width()>>1), fMouseY - (src.height()>>1));
+ SkRect dest;
+ dest.set(0, 0, SkIntToScalar(zoomedWidth), SkIntToScalar(zoomedHeight));
+ dest.offset(SkIntToScalar(width - zoomedWidth), SkIntToScalar(height - zoomedHeight));
+ SkPaint paint;
+ // Clear the background behind our zoomed in view
+ paint.setColor(SK_ColorWHITE);
+ canvas->drawRect(dest, paint);
+ canvas->drawBitmapRect(bitmap, &src, dest);
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kStroke_Style);
+ // Draw a border around the pixel in the middle
+ SkRect originalPixel;
+ originalPixel.set(SkIntToScalar(fMouseX), SkIntToScalar(fMouseY), SkIntToScalar(fMouseX + 1), SkIntToScalar(fMouseY + 1));
+ SkMatrix matrix;
+ SkRect scalarSrc;
+ scalarSrc.set(src);
+ SkColor color = bitmap.getColor(fMouseX, fMouseY);
+ if (matrix.setRectToRect(scalarSrc, dest, SkMatrix::kFill_ScaleToFit)) {
+ SkRect pixel;
+ matrix.mapRect(&pixel, originalPixel);
+ // TODO Perhaps measure the values and make the outline white if it's "dark"
+ if (color == SK_ColorBLACK) {
+ paint.setColor(SK_ColorWHITE);
+ }
+ canvas->drawRect(pixel, paint);
+ }
+ paint.setColor(SK_ColorBLACK);
+ // Draw a border around the destination rectangle
+ canvas->drawRect(dest, paint);
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ // Identify the pixel and its color on screen
+ paint.setTypeface(fTypeface);
+ paint.setAntiAlias(true);
+ SkScalar lineHeight = paint.getFontMetrics(NULL);
+ SkString string;
+ string.appendf("(%i, %i)", fMouseX, fMouseY);
+ SkScalar left = dest.fLeft + SkIntToScalar(3);
+ SkScalar i = SK_Scalar1;
+ drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
+ // Alpha
+ i += SK_Scalar1;
+ string.reset();
+ string.appendf("A: %X", SkColorGetA(color));
+ drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
+ // Red
+ i += SK_Scalar1;
+ string.reset();
+ string.appendf("R: %X", SkColorGetR(color));
+ paint.setColor(SK_ColorRED);
+ drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
+ // Green
+ i += SK_Scalar1;
+ string.reset();
+ string.appendf("G: %X", SkColorGetG(color));
+ paint.setColor(SK_ColorGREEN);
+ drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
+ // Blue
+ i += SK_Scalar1;
+ string.reset();
+ string.appendf("B: %X", SkColorGetB(color));
+ paint.setColor(SK_ColorBLUE);
+ drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
+ canvas->restoreToCount(count);
+}
+
+void SampleWindow::onDraw(SkCanvas* canvas) {
+ if (fRepeatDrawing) {
+ this->inval(NULL);
+ }
+}
+
+#include "SkColorPriv.h"
+
+static void reverseRedAndBlue(const SkBitmap& bm) {
+ SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
+ uint8_t* p = (uint8_t*)bm.getPixels();
+ uint8_t* stop = p + bm.getSize();
+ while (p < stop) {
+ // swap red/blue (to go from ARGB(int) to RGBA(memory) and premultiply
+ unsigned scale = SkAlpha255To256(p[3]);
+ unsigned r = p[2];
+ unsigned b = p[0];
+ p[0] = SkAlphaMul(r, scale);
+ p[1] = SkAlphaMul(p[1], scale);
+ p[2] = SkAlphaMul(b, scale);
+ p += 4;
+ }
+}
+
+SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
+ if (kGPU_CanvasType != fCanvasType) {
+#ifdef SK_SUPPORT_GL
+ detachGL();
+#endif
+ }
+
+ switch (fCanvasType) {
+ case kRaster_CanvasType:
+ canvas = this->INHERITED::beforeChildren(canvas);
+ break;
+ case kPicture_CanvasType:
+ fPicture = new SkPicture;
+ canvas = fPicture->beginRecording(9999, 9999);
+ break;
+ case kGPU_CanvasType: {
+ if (make3DReady()) {
+ SkDevice* device = canvas->getDevice();
+ const SkBitmap& bitmap = device->accessBitmap(true);
+
+ GrRenderTarget* renderTarget;
+ renderTarget = fGrContext->createRenderTargetFrom3DApiState();
+ fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget);
+ renderTarget->unref();
+
+ device = fGpuCanvas->createDevice(SkBitmap::kARGB_8888_Config,
+ bitmap.width(), bitmap.height(),
+ false, false);
+ fGpuCanvas->setDevice(device)->unref();
+
+ fGpuCanvas->concat(canvas->getTotalMatrix());
+ canvas = fGpuCanvas;
+
+ } else {
+ canvas = this->INHERITED::beforeChildren(canvas);
+ }
+ break;
+ }
+ }
+
+ if (fUseClip) {
+ canvas->drawColor(0xFFFF88FF);
+ canvas->clipPath(fClipPath);
+ }
+
+ return canvas;
+}
+
+static void paint_rgn(const SkBitmap& bm, const SkIRect& r,
+ const SkRegion& rgn) {
+ SkCanvas canvas(bm);
+ SkRegion inval(rgn);
+
+ inval.translate(r.fLeft, r.fTop);
+ canvas.clipRegion(inval);
+ canvas.drawColor(0xFFFF8080);
+}
+
+void SampleWindow::afterChildren(SkCanvas* orig) {
+ if (fRequestGrabImage) {
+ fRequestGrabImage = false;
+
+ SkCanvas* canvas = fGpuCanvas ? fGpuCanvas : orig;
+ SkDevice* device = canvas->getDevice();
+ SkBitmap bmp;
+ if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) {
+ static int gSampleGrabCounter;
+ SkString name;
+ name.printf("sample_grab_%d", gSampleGrabCounter++);
+ SkImageEncoder::EncodeFile(name.c_str(), bmp,
+ SkImageEncoder::kPNG_Type, 100);
+ }
+ }
+
+ switch (fCanvasType) {
+ case kRaster_CanvasType:
+ break;
+ case kPicture_CanvasType:
+ if (true) {
+ SkPicture* pict = new SkPicture(*fPicture);
+ fPicture->unref();
+ orig->drawPicture(*pict);
+ pict->unref();
+ } else if (true) {
+ SkDynamicMemoryWStream ostream;
+ fPicture->serialize(&ostream);
+ fPicture->unref();
+
+ SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
+ SkPicture pict(&istream);
+ orig->drawPicture(pict);
+ } else {
+ fPicture->draw(orig);
+ fPicture->unref();
+ }
+ fPicture = NULL;
+ break;
+#ifdef SK_SUPPORT_GL
+ case kGPU_CanvasType:
+ if (fShowZoomer) {
+ this->showZoomer(fGpuCanvas);
+ }
+ delete fGpuCanvas;
+ fGpuCanvas = NULL;
+ presentGL();
+ break;
+#endif
+ }
+
+ // Do this after presentGL and other finishing, rather than in afterChild
+ if (fMeasureFPS && fMeasureFPS_Time) {
+ fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
+ this->updateTitle();
+ postInvalDelay(this->getSinkID());
+ }
+
+ // if ((fScrollTestX | fScrollTestY) != 0)
+ if (false) {
+ const SkBitmap& bm = orig->getDevice()->accessBitmap(true);
+ int dx = fScrollTestX * 7;
+ int dy = fScrollTestY * 7;
+ SkIRect r;
+ SkRegion inval;
+
+ r.set(50, 50, 50+100, 50+100);
+ bm.scrollRect(&r, dx, dy, &inval);
+ paint_rgn(bm, r, inval);
+ }
+}
+
+void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
+ if (fScale) {
+ SkScalar scale = SK_Scalar1 * 7 / 10;
+ SkScalar cx = this->width() / 2;
+ SkScalar cy = this->height() / 2;
+ canvas->translate(cx, cy);
+ canvas->scale(scale, scale);
+ canvas->translate(-cx, -cy);
+ }
+ if (fRotate) {
+ SkScalar cx = this->width() / 2;
+ SkScalar cy = this->height() / 2;
+ canvas->translate(cx, cy);
+ canvas->rotate(SkIntToScalar(30));
+ canvas->translate(-cx, -cy);
+ }
+
+ canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState,
+ fFilterState, fHintingState))->unref();
+
+ if (fMeasureFPS) {
+ fMeasureFPS_Time = 0; // 0 means the child is not aware of repeat-draw
+ if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) {
+ fMeasureFPS_Time = SkTime::GetMSecs();
+ }
+ } else {
+ (void)SampleView::SetRepeatDraw(child, 1);
+ }
+ (void)SampleView::SetUsePipe(child, fUsePipe);
+}
+
+void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
+ canvas->setDrawFilter(NULL);
+}
+
+static SkBitmap::Config gConfigCycle[] = {
+ SkBitmap::kNo_Config, // none -> none
+ SkBitmap::kNo_Config, // a1 -> none
+ SkBitmap::kNo_Config, // a8 -> none
+ SkBitmap::kNo_Config, // index8 -> none
+ SkBitmap::kARGB_4444_Config, // 565 -> 4444
+ SkBitmap::kARGB_8888_Config, // 4444 -> 8888
+ SkBitmap::kRGB_565_Config // 8888 -> 565
+};
+
+static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
+ return gConfigCycle[c];
+}
+
+void SampleWindow::changeZoomLevel(int delta) {
+ fZoomLevel += delta;
+ if (fZoomLevel > 0) {
+ fZoomLevel = SkMin32(fZoomLevel, MAX_ZOOM_LEVEL);
+ fZoomScale = SkIntToScalar(fZoomLevel + 1);
+ } else if (fZoomLevel < 0) {
+ fZoomLevel = SkMax32(fZoomLevel, MIN_ZOOM_LEVEL);
+ fZoomScale = SK_Scalar1 / (1 - fZoomLevel);
+ } else {
+ fZoomScale = SK_Scalar1;
+ }
+
+ this->inval(NULL);
+}
+
+bool SampleWindow::nextSample() {
+ fCurrIndex = (fCurrIndex + 1) % fSamples.count();
+ this->loadView(fSamples[fCurrIndex]());
+ return true;
+}
+
+bool SampleWindow::onEvent(const SkEvent& evt) {
+ if (evt.isType(ANIMATING_EVENTTYPE)) {
+ if (fAnimating) {
+ this->nextSample();
+ this->postAnimatingEvent();
+ }
+ return true;
+ }
+ if (evt.isType("set-curr-index")) {
+ fCurrIndex = evt.getFast32() % fSamples.count();
+ this->loadView(fSamples[fCurrIndex]());
+ return true;
+ }
+ if (isInvalEvent(evt)) {
+ this->inval(NULL);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+bool SampleWindow::onQuery(SkEvent* query) {
+ if (query->isType("get-slide-count")) {
+ query->setFast32(fSamples.count());
+ return true;
+ }
+ if (query->isType("get-slide-title")) {
+ SkView* view = fSamples[query->getFast32()]();
+ SkEvent evt(gTitleEvtName);
+ if (view->doQuery(&evt)) {
+ query->setString("title", evt.findString(gTitleEvtName));
+ }
+ SkSafeUnref(view);
+ return true;
+ }
+ if (query->isType("use-fast-text")) {
+ SkEvent evt(gFastTextEvtName);
+ return curr_view(this)->doQuery(&evt);
+ }
+ return this->INHERITED::onQuery(query);
+}
+
+static void cleanup_for_filename(SkString* name) {
+ char* str = name->writable_str();
+ for (size_t i = 0; i < name->size(); i++) {
+ switch (str[i]) {
+ case ':': str[i] = '-'; break;
+ case '/': str[i] = '-'; break;
+ case ' ': str[i] = '_'; break;
+ default: break;
+ }
+ }
+}
+
+bool SampleWindow::onHandleChar(SkUnichar uni) {
+ {
+ SkView* view = curr_view(this);
+ if (view) {
+ SkEvent evt(gCharEvtName);
+ evt.setFast32(uni);
+ if (view->doQuery(&evt)) {
+ return true;
+ }
+ }
+ }
+
+ int dx = 0xFF;
+ int dy = 0xFF;
+
+ switch (uni) {
+ case '5': dx = 0; dy = 0; break;
+ case '8': dx = 0; dy = -1; break;
+ case '6': dx = 1; dy = 0; break;
+ case '2': dx = 0; dy = 1; break;
+ case '4': dx = -1; dy = 0; break;
+ case '7': dx = -1; dy = -1; break;
+ case '9': dx = 1; dy = -1; break;
+ case '3': dx = 1; dy = 1; break;
+ case '1': dx = -1; dy = 1; break;
+
+ default:
+ break;
+ }
+
+ if (0xFF != dx && 0xFF != dy) {
+ if ((dx | dy) == 0) {
+ fScrollTestX = fScrollTestY = 0;
+ } else {
+ fScrollTestX += dx;
+ fScrollTestY += dy;
+ }
+ this->inval(NULL);
+ return true;
+ }
+
+ switch (uni) {
+ case 'a':
+ fAnimating = !fAnimating;
+ this->postAnimatingEvent();
+ this->updateTitle();
+ return true;
+ case 'b':
+ fAAState = cycle_tristate(fAAState);
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'c':
+ fUseClip = !fUseClip;
+ this->inval(NULL);
+ this->updateTitle();
+ return true;
+ case 'd':
+ SkGraphics::SetFontCacheUsed(0);
+ return true;
+ case 'f':
+ fMeasureFPS = !fMeasureFPS;
+ this->inval(NULL);
+ break;
+ case 'g':
+ fRequestGrabImage = true;
+ this->inval(NULL);
+ break;
+ case 'h':
+ fHintingState = cycle_tristate(fHintingState);
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'i':
+ this->zoomIn();
+ break;
+ case 'l':
+ fLCDState = cycle_tristate(fLCDState);
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'n':
+ fFilterState = cycle_tristate(fFilterState);
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'o':
+ this->zoomOut();
+ break;
+ case 'p':
+ fUsePipe = !fUsePipe;
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'r':
+ fRotate = !fRotate;
+ this->inval(NULL);
+ this->updateTitle();
+ return true;
+ case 's':
+ fScale = !fScale;
+ this->inval(NULL);
+ this->updateTitle();
+ return true;
+ case 'x':
+ fFlipAxis ^= kFlipAxis_X;
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'y':
+ fFlipAxis ^= kFlipAxis_Y;
+ this->updateTitle();
+ this->inval(NULL);
+ break;
+ case 'z':
+ this->toggleZoomer();
+ break;
+ default:
+ break;
+ }
+
+ return this->INHERITED::onHandleChar(uni);
+}
+
+#include "SkDumpCanvas.h"
+
+bool SampleWindow::onHandleKey(SkKey key) {
+ {
+ SkView* view = curr_view(this);
+ if (view) {
+ SkEvent evt(gKeyEvtName);
+ evt.setFast32(key);
+ if (view->doQuery(&evt)) {
+ return true;
+ }
+ }
+ }
+
+ switch (key) {
+ case kRight_SkKey:
+ if (this->nextSample()) {
+ return true;
+ }
+ break;
+ case kLeft_SkKey:
+ fCanvasType = cycle_canvastype(fCanvasType);
+ this->updateTitle();
+ this->inval(NULL);
+ return true;
+ case kUp_SkKey:
+ if (USE_ARROWS_FOR_ZOOM) {
+ this->changeZoomLevel(1);
+ } else {
+ fNClip = !fNClip;
+ this->inval(NULL);
+ }
+ this->updateTitle();
+ return true;
+ case kDown_SkKey:
+ if (USE_ARROWS_FOR_ZOOM) {
+ this->changeZoomLevel(-1);
+ } else {
+ this->setConfig(cycle_configs(this->getBitmap().config()));
+ }
+ this->updateTitle();
+ return true;
+ case kOK_SkKey:
+ if (false) {
+ SkDebugfDumper dumper;
+ SkDumpCanvas dc(&dumper);
+ this->draw(&dc);
+ } else {
+ fRepeatDrawing = !fRepeatDrawing;
+ if (fRepeatDrawing) {
+ this->inval(NULL);
+ }
+ }
+ return true;
+ case kBack_SkKey:
+ this->loadView(NULL);
+ return true;
+ default:
+ break;
+ }
+ return this->INHERITED::onHandleKey(key);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char gGestureClickType[] = "GestureClickType";
+
+bool SampleWindow::onDispatchClick(int x, int y, Click::State state) {
+ if (Click::kMoved_State == state) {
+ updatePointer(x, y);
+ }
+ int w = SkScalarRound(this->width());
+ int h = SkScalarRound(this->height());
+
+ // check for the resize-box
+ if (w - x < 16 && h - y < 16) {
+ return false; // let the OS handle the click
+ } else {
+ return this->INHERITED::onDispatchClick(x, y, state);
+ }
+}
+
+class GestureClick : public SkView::Click {
+public:
+ GestureClick(SkView* target) : SkView::Click(target) {
+ this->setType(gGestureClickType);
+ }
+
+ static bool IsGesture(Click* click) {
+ return click->isType(gGestureClickType);
+ }
+};
+
+SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
+ return new GestureClick(this);
+}
+
+bool SampleWindow::onClick(Click* click) {
+ if (GestureClick::IsGesture(click)) {
+ float x = SkScalarToFloat(click->fCurr.fX);
+ float y = SkScalarToFloat(click->fCurr.fY);
+ switch (click->fState) {
+ case SkView::Click::kDown_State:
+ fGesture.touchBegin(click, x, y);
+ break;
+ case SkView::Click::kMoved_State:
+ fGesture.touchMoved(click, x, y);
+ this->inval(NULL);
+ break;
+ case SkView::Click::kUp_State:
+ fGesture.touchEnd(click);
+ this->inval(NULL);
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SampleWindow::loadView(SkView* view) {
+ SkView::F2BIter iter(this);
+ SkView* prev = iter.next();
+ if (prev) {
+ prev->detachFromParent();
+ }
+
+ if (NULL == view) {
+ view = create_overview(fSamples.count(), fSamples.begin());
+ }
+ view->setVisibleP(true);
+ view->setClipToBounds(false);
+ this->attachChildToFront(view)->unref();
+ view->setSize(this->width(), this->height());
+
+ this->updateTitle();
+}
+
+static const char* gConfigNames[] = {
+ "unknown config",
+ "A1",
+ "A8",
+ "Index8",
+ "565",
+ "4444",
+ "8888"
+};
+
+static const char* configToString(SkBitmap::Config c) {
+ return gConfigNames[c];
+}
+
+static const char* gCanvasTypePrefix[] = {
+ "raster: ",
+ "picture: ",
+ "opengl: "
+};
+
+static const char* trystate_str(SkTriState state,
+ const char trueStr[], const char falseStr[]) {
+ if (kTrue_SkTriState == state) {
+ return trueStr;
+ } else if (kFalse_SkTriState == state) {
+ return falseStr;
+ }
+ return NULL;
+}
+
+void SampleWindow::updateTitle() {
+ SkString title;
+
+ SkView::F2BIter iter(this);
+ SkView* view = iter.next();
+ SkEvent evt(gTitleEvtName);
+ if (view->doQuery(&evt)) {
+ title.set(evt.findString(gTitleEvtName));
+ }
+ if (title.size() == 0) {
+ title.set("<unknown>");
+ }
+
+ title.prepend(gCanvasTypePrefix[fCanvasType]);
+
+ title.prepend(" ");
+ title.prepend(configToString(this->getBitmap().config()));
+
+ if (fAnimating) {
+ title.prepend("<A> ");
+ }
+ if (fScale) {
+ title.prepend("<S> ");
+ }
+ if (fRotate) {
+ title.prepend("<R> ");
+ }
+ if (fNClip) {
+ title.prepend("<C> ");
+ }
+
+ title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
+ title.prepend(trystate_str(fAAState, "AA ", "aa "));
+ title.prepend(trystate_str(fFilterState, "H ", "h "));
+ title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
+ title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
+
+ if (fZoomLevel) {
+ title.prependf("{%d} ", fZoomLevel);
+ }
+
+ if (fMeasureFPS) {
+ title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER);
+ }
+ if (fUsePipe && SampleView::IsSampleView(view)) {
+ title.prepend("<P> ");
+ }
+ if (SampleView::IsSampleView(view)) {
+ title.prepend("! ");
+ }
+
+ this->setTitle(title.c_str());
+}
+
+void SampleWindow::onSizeChange() {
+ this->INHERITED::onSizeChange();
+
+ SkView::F2BIter iter(this);
+ SkView* view = iter.next();
+ view->setSize(this->width(), this->height());
+
+ // rebuild our clippath
+ {
+ const SkScalar W = this->width();
+ const SkScalar H = this->height();
+
+ fClipPath.reset();
+#if 0
+ for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
+ SkRect r;
+ r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
+ for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
+ fClipPath.addRect(r);
+ }
+#else
+ SkRect r;
+ r.set(0, 0, W, H);
+ fClipPath.addRect(r, SkPath::kCCW_Direction);
+ r.set(W/4, H/4, W*3/4, H*3/4);
+ fClipPath.addRect(r, SkPath::kCW_Direction);
+#endif
+ }
+
+ this->updateTitle(); // to refresh our config
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const char is_sample_view_tag[] = "sample-is-sample-view";
+static const char repeat_count_tag[] = "sample-set-repeat-count";
+static const char set_use_pipe_tag[] = "sample-set-use-pipe";
+
+bool SampleView::IsSampleView(SkView* view) {
+ SkEvent evt(is_sample_view_tag);
+ return view->doQuery(&evt);
+}
+
+bool SampleView::SetRepeatDraw(SkView* view, int count) {
+ SkEvent evt(repeat_count_tag);
+ evt.setFast32(count);
+ return view->doEvent(evt);
+}
+
+bool SampleView::SetUsePipe(SkView* view, bool pred) {
+ SkEvent evt(set_use_pipe_tag);
+ evt.setFast32(pred);
+ return view->doEvent(evt);
+}
+
+bool SampleView::onEvent(const SkEvent& evt) {
+ if (evt.isType(repeat_count_tag)) {
+ fRepeatCount = evt.getFast32();
+ return true;
+ }
+ if (evt.isType(set_use_pipe_tag)) {
+ fUsePipe = !!evt.getFast32();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+bool SampleView::onQuery(SkEvent* evt) {
+ if (evt->isType(is_sample_view_tag)) {
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+}
+
+#ifdef TEST_GPIPE
+ #include "SkGPipe.h"
+
+class SimplePC : public SkGPipeController {
+public:
+ SimplePC(SkCanvas* target);
+ ~SimplePC();
+
+ virtual void* requestBlock(size_t minRequest, size_t* actual);
+ virtual void notifyWritten(size_t bytes);
+
+private:
+ SkGPipeReader fReader;
+ void* fBlock;
+ size_t fBlockSize;
+ size_t fBytesWritten;
+ int fAtomsWritten;
+ SkGPipeReader::Status fStatus;
+
+ size_t fTotalWritten;
+};
+
+SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
+ fBlock = NULL;
+ fBlockSize = fBytesWritten = 0;
+ fStatus = SkGPipeReader::kDone_Status;
+ fTotalWritten = 0;
+ fAtomsWritten = 0;
+}
+
+SimplePC::~SimplePC() {
+// SkASSERT(SkGPipeReader::kDone_Status == fStatus);
+ sk_free(fBlock);
+
+ if (fTotalWritten) {
+ SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
+ fAtomsWritten, fStatus);
+ }
+}
+
+void* SimplePC::requestBlock(size_t minRequest, size_t* actual) {
+ sk_free(fBlock);
+
+ fBlockSize = minRequest * 4;
+ fBlock = sk_malloc_throw(fBlockSize);
+ fBytesWritten = 0;
+ *actual = fBlockSize;
+ return fBlock;
+}
+
+void SimplePC::notifyWritten(size_t bytes) {
+ SkASSERT(fBytesWritten + bytes <= fBlockSize);
+
+#ifdef PIPE_FILE
+ //File is open in append mode
+ FILE* f = fopen(FILE_PATH, "ab");
+ SkASSERT(f != NULL);
+ fwrite((const char*)fBlock + fBytesWritten, 1, bytes, f);
+ fclose(f);
+#endif
+
+ fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
+ SkASSERT(SkGPipeReader::kError_Status != fStatus);
+ fBytesWritten += bytes;
+ fTotalWritten += bytes;
+
+ fAtomsWritten += 1;
+}
+
+#endif
+
+
+void SampleView::onDraw(SkCanvas* canvas) {
+#ifdef TEST_GPIPE
+ SimplePC controller(canvas);
+ SkGPipeWriter writer;
+ if (fUsePipe) {
+ uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
+// flags = 0;
+ canvas = writer.startRecording(&controller, flags);
+ }
+#endif
+
+ this->onDrawBackground(canvas);
+
+ for (int i = 0; i < fRepeatCount; i++) {
+ SkAutoCanvasRestore acr(canvas, true);
+ this->onDrawContent(canvas);
+ }
+}
+
+void SampleView::onDrawBackground(SkCanvas* canvas) {
+ canvas->drawColor(fBGColor);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> void SkTBSort(T array[], int count) {
+ for (int i = 1; i < count - 1; i++) {
+ bool didSwap = false;
+ for (int j = count - 1; j > i; --j) {
+ if (array[j] < array[j-1]) {
+ T tmp(array[j-1]);
+ array[j-1] = array[j];
+ array[j] = tmp;
+ didSwap = true;
+ }
+ }
+ if (!didSwap) {
+ break;
+ }
+ }
+
+ for (int k = 0; k < count - 1; k++) {
+ SkASSERT(!(array[k+1] < array[k]));
+ }
+}
+
+#include "SkRandom.h"
+
+static void rand_rect(SkIRect* rect, SkRandom& rand) {
+ int bits = 8;
+ int shift = 32 - bits;
+ rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
+ rand.nextU() >> shift, rand.nextU() >> shift);
+ rect->sort();
+}
+
+static void dumpRect(const SkIRect& r) {
+ SkDebugf(" { %d, %d, %d, %d },\n",
+ r.fLeft, r.fTop,
+ r.fRight, r.fBottom);
+}
+
+static void test_rects(const SkIRect rect[], int count) {
+ SkRegion rgn0, rgn1;
+
+ for (int i = 0; i < count; i++) {
+ rgn0.op(rect[i], SkRegion::kUnion_Op);
+ // dumpRect(rect[i]);
+ }
+ rgn1.setRects(rect, count);
+
+ if (rgn0 != rgn1) {
+ SkDebugf("\n");
+ for (int i = 0; i < count; i++) {
+ dumpRect(rect[i]);
+ }
+ SkDebugf("\n");
+ }
+}
+
+static void test() {
+ size_t i;
+
+ const SkIRect r0[] = {
+ { 0, 0, 1, 1 },
+ { 2, 2, 3, 3 },
+ };
+ const SkIRect r1[] = {
+ { 0, 0, 1, 3 },
+ { 1, 1, 2, 2 },
+ { 2, 0, 3, 3 },
+ };
+ const SkIRect r2[] = {
+ { 0, 0, 1, 2 },
+ { 2, 1, 3, 3 },
+ { 4, 0, 5, 1 },
+ { 6, 0, 7, 4 },
+ };
+
+ static const struct {
+ const SkIRect* fRects;
+ int fCount;
+ } gRecs[] = {
+ { r0, SK_ARRAY_COUNT(r0) },
+ { r1, SK_ARRAY_COUNT(r1) },
+ { r2, SK_ARRAY_COUNT(r2) },
+ };
+
+ for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
+ test_rects(gRecs[i].fRects, gRecs[i].fCount);
+ }
+
+ SkRandom rand;
+ for (i = 0; i < 10000; i++) {
+ SkRegion rgn0, rgn1;
+
+ const int N = 8;
+ SkIRect rect[N];
+ for (int j = 0; j < N; j++) {
+ rand_rect(&rect[j], rand);
+ }
+ test_rects(rect, N);
+ }
+}
+
+SkOSWindow* create_sk_window(void* hwnd) {
+// test();
+ return new SampleWindow(hwnd);
+}
+
+void get_preferred_size(int* x, int* y, int* width, int* height) {
+ *x = 10;
+ *y = 50;
+ *width = 640;
+ *height = 480;
+}
+
+void application_init() {
+// setenv("ANDROID_ROOT", "../../../data", 0);
+#ifdef SK_BUILD_FOR_MAC
+ setenv("ANDROID_ROOT", "/android/device/data", 0);
+#endif
+ SkGraphics::Init();
+ SkEvent::Init();
+}
+
+void application_term() {
+ SkEvent::Term();
+ SkGraphics::Term();
+}
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
new file mode 100644
index 0000000000..8e3ad88cb4
--- /dev/null
+++ b/samplecode/SampleArc.cpp
@@ -0,0 +1,184 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkComposeShader.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkLayerRasterizer.h"
+
+#include "SkParsePath.h"
+static void testparse() {
+ SkRect r;
+ r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
+ SkPath p, p2;
+ SkString str, str2;
+
+ p.addRect(r);
+ SkParsePath::ToSVGString(p, &str);
+ SkParsePath::FromSVGString(str.c_str(), &p2);
+ SkParsePath::ToSVGString(p2, &str2);
+}
+
+class ArcsView : public SampleView {
+public:
+ ArcsView() {
+ testparse();
+ fSweep = SkIntToScalar(100);
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Arcs");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static void drawRectWithLines(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+ canvas->drawRect(r, p);
+ canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, p);
+ canvas->drawLine(r.fLeft, r.fBottom, r.fRight, r.fTop, p);
+ canvas->drawLine(r.fLeft, r.centerY(), r.fRight, r.centerY(), p);
+ canvas->drawLine(r.centerX(), r.fTop, r.centerX(), r.fBottom, p);
+ }
+
+ static void draw_label(SkCanvas* canvas, const SkRect& rect,
+ int start, int sweep) {
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setTextAlign(SkPaint::kCenter_Align);
+
+ SkString str;
+
+ str.appendS32(start);
+ str.append(", ");
+ str.appendS32(sweep);
+ canvas->drawText(str.c_str(), str.size(), rect.centerX(),
+ rect.fBottom + paint.getTextSize() * 5/4, paint);
+ }
+
+ static void drawArcs(SkCanvas* canvas) {
+ SkPaint paint;
+ SkRect r;
+ SkScalar w = SkIntToScalar(75);
+ SkScalar h = SkIntToScalar(50);
+
+ r.set(0, 0, w, h);
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(300));
+
+ paint.setStrokeWidth(SkIntToScalar(1));
+
+ static const int gAngles[] = {
+ 0, 360,
+ 0, 45,
+ 0, -45,
+ 720, 135,
+ -90, 269,
+ -90, 270,
+ -90, 271,
+ -180, -270,
+ 225, 90
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gAngles); i += 2) {
+ paint.setColor(SK_ColorBLACK);
+ drawRectWithLines(canvas, r, paint);
+
+ paint.setColor(SK_ColorRED);
+ canvas->drawArc(r, SkIntToScalar(gAngles[i]),
+ SkIntToScalar(gAngles[i+1]), false, paint);
+
+ draw_label(canvas, r, gAngles[i], gAngles[i+1]);
+
+ canvas->translate(w * 8 / 7, 0);
+ }
+
+ canvas->restore();
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ fSweep = SampleCode::GetAnimScalar(SkIntToScalar(360)/24,
+ SkIntToScalar(360));
+// fSweep = SkFloatToScalar(359.99f);
+
+ SkRect r;
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(SkIntToScalar(2));
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ r.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
+ r.offset(SkIntToScalar(20), SkIntToScalar(20));
+
+ if (false) {
+ const SkScalar d = SkIntToScalar(3);
+ const SkScalar rad[] = { d, d, d, d, d, d, d, d };
+ SkPath path;
+ path.addRoundRect(r, rad);
+ canvas->drawPath(path, paint);
+ return;
+ }
+
+ drawRectWithLines(canvas, r, paint);
+
+ // printf("----- sweep %g %X\n", SkScalarToFloat(fSweep), SkDegreesToRadians(fSweep));
+
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0x800000FF);
+ canvas->drawArc(r, 0, fSweep, true, paint);
+
+ paint.setColor(0x800FF000);
+ canvas->drawArc(r, 0, fSweep, false, paint);
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorRED);
+ canvas->drawArc(r, 0, fSweep, true, paint);
+
+ paint.setStrokeWidth(0);
+ paint.setColor(SK_ColorBLUE);
+ canvas->drawArc(r, 0, fSweep, false, paint);
+
+ drawArcs(canvas);
+ this->inval(NULL);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ // fSweep += SK_Scalar1;
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ SkScalar fSweep;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ArcsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleAvoid.cpp b/samplecode/SampleAvoid.cpp
new file mode 100644
index 0000000000..868a67c62f
--- /dev/null
+++ b/samplecode/SampleAvoid.cpp
@@ -0,0 +1,99 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkAvoidXfermode.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AvoidView : public SampleView {
+ SkShader* fShader;
+
+ enum {
+ W = 480,
+ H = 320
+ };
+public:
+ AvoidView() {
+ SkColor colors[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorCYAN, SK_ColorBLUE };
+
+#if 0
+ SkPoint pts[] = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+ fShader = SkGradientShader::CreateLinear(pts, colors, NULL,
+ SK_ARRAY_COUNT(colors),
+ SkShader::kMirror_TileMode);
+#else
+ SkPoint pts[] = { { SkIntToScalar(W)/2, SkIntToScalar(H)/2 } };
+ fShader = SkGradientShader::CreateRadial(pts[0], SkIntToScalar(H)/5,
+ colors, NULL,
+ SK_ARRAY_COUNT(colors),
+ SkShader::kMirror_TileMode);
+#endif
+ }
+
+ virtual ~AvoidView() {
+ fShader->unref();
+ }
+
+protected:
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "AvoidXfermode");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
+
+ canvas->translate(r.width() / 6, r.height() / 6);
+
+ paint.setShader(fShader);
+ canvas->drawRect(r, paint);
+
+ static const struct {
+ int fTolerance;
+ SkAvoidXfermode::Mode fMode;
+ float fDX, fDY;
+ } gData[] = {
+ { 16, SkAvoidXfermode::kAvoidColor_Mode, 0, 0 },
+ { 255-16, SkAvoidXfermode::kAvoidColor_Mode, 1, 0 },
+ { 16, SkAvoidXfermode::kTargetColor_Mode, 0, 1 },
+ { 255-16, SkAvoidXfermode::kTargetColor_Mode, 1, 1 },
+ };
+
+ paint.setShader(NULL);
+ paint.setColor(SK_ColorMAGENTA);
+
+ SkPaint frameP;
+ frameP.setStyle(SkPaint::kStroke_Style);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gData); i++) {
+ SkAvoidXfermode mode(SK_ColorGREEN, gData[i].fTolerance,
+ gData[i].fMode);
+ paint.setXfermode(&mode);
+ int div = 3;
+ SkRect rr = { 0, 0, r.width()/div, r.height()/div };
+ rr.offset(r.width()/4 - rr.width()/2, r.height()/4 - rr.height()/2);
+ rr.offset(r.width() * gData[i].fDX/2, r.height() * gData[i].fDY/2);
+ canvas->drawRect(rr, paint);
+ paint.setXfermode(NULL);
+
+ canvas->drawRect(rr, frameP);
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() {
+ return new AvoidView;
+}
+
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleBigGradient.cpp b/samplecode/SampleBigGradient.cpp
new file mode 100644
index 0000000000..5ebb5162f3
--- /dev/null
+++ b/samplecode/SampleBigGradient.cpp
@@ -0,0 +1,43 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+
+static SkShader* make_grad(SkScalar w, SkScalar h) {
+ SkColor colors[] = { 0xFF000000, 0xFF333333 };
+ SkPoint pts[] = { { 0, 0 }, { w, h } };
+ return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode);
+}
+
+class BigGradientView : public SampleView {
+public:
+ BigGradientView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "BigGradient");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkRect r;
+ r.set(0, 0, this->width(), this->height());
+ SkPaint p;
+ p.setShader(make_grad(this->width(), this->height()))->unref();
+ canvas->drawRect(r, p);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new BigGradientView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
new file mode 100644
index 0000000000..002b2b9b17
--- /dev/null
+++ b/samplecode/SampleBitmapRect.cpp
@@ -0,0 +1,92 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+static SkBitmap make_bitmap() {
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
+ bm.allocPixels();
+ SkCanvas canvas(bm);
+ canvas.drawColor(SK_ColorRED);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ const SkPoint pts[] = { { 0, 0 }, { 64, 64 } };
+ const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
+ paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode))->unref();
+ canvas.drawCircle(32, 32, 32, paint);
+ return bm;
+}
+
+class BitmapRectView : public SampleView {
+public:
+ SkBitmap fBitmap;
+
+ BitmapRectView() {
+ fBitmap = make_bitmap();
+ this->setBGColor(SK_ColorGRAY);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "BitmapRect");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ const SkIRect src[] = {
+ { 0, 0, 32, 32 },
+ { 0, 0, 80, 80 },
+ { 32, 32, 96, 96 },
+ { -32, -32, 32, 32, }
+ };
+
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorGREEN);
+
+ SkRect dstR = { 0, 200, 128, 380 };
+
+ canvas->translate(16, 40);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
+ SkRect srcR;
+ srcR.set(src[i]);
+
+ canvas->drawBitmap(fBitmap, 0, 0, &paint);
+ canvas->drawBitmapRect(fBitmap, &src[i], dstR, &paint);
+
+ canvas->drawRect(dstR, paint);
+ canvas->drawRect(srcR, paint);
+
+ canvas->translate(160, 0);
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new BitmapRectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp
new file mode 100644
index 0000000000..d2ea2b0aeb
--- /dev/null
+++ b/samplecode/SampleBlur.cpp
@@ -0,0 +1,131 @@
+#include "SampleCode.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorPriv.h"
+#include "SkGradientShader.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkUtils.h"
+
+static SkBitmap make_bitmap() {
+ SkBitmap bm;
+ SkColorTable* ctable = new SkColorTable(256);
+
+ SkPMColor* c = ctable->lockColors();
+ for (int i = 0; i < 256; i++) {
+ c[i] = SkPackARGB32(255 - i, 0, 0, 0);
+ }
+ ctable->unlockColors(true);
+ bm.setConfig(SkBitmap::kIndex8_Config, 256, 256);
+ bm.allocPixels(ctable);
+ ctable->unref();
+
+ bm.lockPixels();
+ const float cx = bm.width() * 0.5f;
+ const float cy = bm.height() * 0.5f;
+ for (int y = 0; y < bm.height(); y++) {
+ float dy = y - cy;
+ dy *= dy;
+ uint8_t* p = bm.getAddr8(0, y);
+ for (int x = 0; x < 256; x++) {
+ float dx = x - cx;
+ dx *= dx;
+ float d = (dx + dy) / (cx/2);
+ int id = (int)d;
+ if (id > 255) {
+ id = 255;
+ }
+ p[x] = id;
+ }
+ }
+ bm.unlockPixels();
+ return bm;
+}
+
+class BlurView : public SkView {
+ SkBitmap fBM;
+public:
+ BlurView() {
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Blur");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ drawBG(canvas);
+
+ SkBlurMaskFilter::BlurStyle NONE = SkBlurMaskFilter::BlurStyle(-999);
+ static const struct {
+ SkBlurMaskFilter::BlurStyle fStyle;
+ int fCx, fCy;
+ } gRecs[] = {
+ { NONE, 0, 0 },
+ { SkBlurMaskFilter::kInner_BlurStyle, -1, 0 },
+ { SkBlurMaskFilter::kNormal_BlurStyle, 0, 1 },
+ { SkBlurMaskFilter::kSolid_BlurStyle, 0, -1 },
+ { SkBlurMaskFilter::kOuter_BlurStyle, 1, 0 },
+ };
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(25);
+ canvas->translate(-40, 0);
+
+ SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag;
+ for (int j = 0; j < 2; j++) {
+ canvas->save();
+ paint.setColor(SK_ColorBLUE);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
+ if (gRecs[i].fStyle != NONE) {
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(20,
+ gRecs[i].fStyle,
+ flags);
+ paint.setMaskFilter(mf)->unref();
+ } else {
+ paint.setMaskFilter(NULL);
+ }
+ canvas->drawCircle(200 + gRecs[i].fCx*100.f,
+ 200 + gRecs[i].fCy*100.f, 50, paint);
+ }
+ // draw text
+ {
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(4,
+ SkBlurMaskFilter::kNormal_BlurStyle,
+ flags);
+ paint.setMaskFilter(mf)->unref();
+ SkScalar x = SkIntToScalar(70);
+ SkScalar y = SkIntToScalar(400);
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawText("Hamburgefons Style", 18, x, y, paint);
+ canvas->drawText("Hamburgefons Style", 18, x, y + SkIntToScalar(50), paint);
+ paint.setMaskFilter(NULL);
+ paint.setColor(SK_ColorWHITE);
+ x -= SkIntToScalar(2);
+ y -= SkIntToScalar(2);
+ canvas->drawText("Hamburgefons Style", 18, x, y, paint);
+ }
+ canvas->restore();
+ flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
+ canvas->translate(350, 0);
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new BlurView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleBox.cpp b/samplecode/SampleBox.cpp
new file mode 100644
index 0000000000..d445df7596
--- /dev/null
+++ b/samplecode/SampleBox.cpp
@@ -0,0 +1,48 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+class SimpleView : public SampleView {
+public:
+ SimpleView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Box Gradient");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkRect r;
+ SkScalar x,y;
+ x = 10;
+ y = 10;
+
+ r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
+ for (int i = 0; i < 256; ++i) {
+ canvas->translate(1, 1);
+ paint.setColor(0xFF000000 + i * 0x00010000);
+ canvas->drawRect(r, paint);
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SimpleView; }
+static SkViewRegister reg(MyFactory); \ No newline at end of file
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
new file mode 100644
index 0000000000..2db396889d
--- /dev/null
+++ b/samplecode/SampleCamera.cpp
@@ -0,0 +1,99 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkCamera.h"
+#include "SkEmbossMaskFilter.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkRandom.h"
+#include "SkImageDecoder.h"
+
+class CameraView : public SampleView {
+ SkTDArray<SkShader*> fShaders;
+ int fShaderIndex;
+ bool fFrontFace;
+public:
+ CameraView() {
+ fRX = fRY = fRZ = 0;
+ fShaderIndex = 0;
+ fFrontFace = false;
+
+ for (int i = 0;; i++) {
+ SkString str;
+ str.printf("/skimages/elephant%d.jpeg", i);
+ SkBitmap bm;
+ if (SkImageDecoder::DecodeFile(str.c_str(), &bm)) {
+ SkShader* s = SkShader::CreateBitmapShader(bm,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+
+ SkRect src = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
+ SkRect dst = { -150, -150, 150, 150 };
+ SkMatrix matrix;
+ matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
+ s->setLocalMatrix(matrix);
+ *fShaders.append() = s;
+ } else {
+ break;
+ }
+ }
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~CameraView() {
+ fShaders.unrefAll();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt))
+ {
+ SampleCode::TitleR(evt, "Camera");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(this->width()/2, this->height()/2);
+
+ Sk3DView view;
+ view.rotateX(fRX);
+ view.rotateY(fRY);
+ view.applyToCanvas(canvas);
+
+ SkPaint paint;
+ if (fShaders.count() > 0) {
+ bool frontFace = view.dotWithNormal(0, 0, SK_Scalar1) < 0;
+ if (frontFace != fFrontFace) {
+ fFrontFace = frontFace;
+ fShaderIndex = (fShaderIndex + 1) % fShaders.count();
+ }
+
+ paint.setAntiAlias(true);
+ paint.setShader(fShaders[fShaderIndex]);
+ SkRect r = { -150, -150, 150, 150 };
+ canvas->drawRoundRect(r, 30, 30, paint);
+ }
+
+ fRY += SampleCode::GetAnimSecondsDelta() * 90;
+ if (fRY >= SkIntToScalar(360)) {
+ fRY = 0;
+ }
+ this->inval(NULL);
+ }
+
+private:
+ SkScalar fRX, fRY, fRZ;
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new CameraView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleCircle.cpp b/samplecode/SampleCircle.cpp
new file mode 100644
index 0000000000..2abc28de3e
--- /dev/null
+++ b/samplecode/SampleCircle.cpp
@@ -0,0 +1,126 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+// ensure that we don't accidentally screw up the bounds when the oval is
+// fractional, and the impl computes the center and radii, and uses them to
+// reconstruct the edges of the circle.
+// see bug# 1504910
+static void test_circlebounds(SkCanvas* canvas) {
+#ifdef SK_SCALAR_IS_FLOAT
+ SkRect r = { 1.39999998f, 1, 21.3999996f, 21 };
+ SkPath p;
+ p.addOval(r);
+ SkASSERT(r == p.getBounds());
+#endif
+}
+
+class CircleView : public SampleView {
+public:
+ static const SkScalar ANIM_DX;
+ static const SkScalar ANIM_DY;
+ static const SkScalar ANIM_RAD;
+ SkScalar fDX, fDY, fRAD;
+
+ CircleView() {
+ fDX = fDY = fRAD = 0;
+ fN = 3;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Circles");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void circle(SkCanvas* canvas, int width, bool aa) {
+ SkPaint paint;
+
+ paint.setAntiAlias(aa);
+ if (width < 0) {
+ paint.setStyle(SkPaint::kFill_Style);
+ } else {
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(width));
+ }
+ canvas->drawCircle(0, 0, SkIntToScalar(9) + fRAD, paint);
+ }
+
+ void drawSix(SkCanvas* canvas, SkScalar dx, SkScalar dy) {
+ for (int width = -1; width <= 1; width++) {
+ canvas->save();
+ circle(canvas, width, false);
+ canvas->translate(0, dy);
+ circle(canvas, width, true);
+ canvas->restore();
+ canvas->translate(dx, 0);
+ }
+ }
+
+ static void blowup(SkCanvas* canvas, const SkIRect& src, const SkRect& dst) {
+ SkDevice* device = canvas->getDevice();
+ const SkBitmap& bm = device->accessBitmap(false);
+ canvas->drawBitmapRect(bm, &src, dst, NULL);
+ }
+
+ static void make_poly(SkPath* path, int n) {
+ if (n <= 0) {
+ return;
+ }
+ path->incReserve(n + 1);
+ path->moveTo(SK_Scalar1, 0);
+ SkScalar step = SK_ScalarPI * 2 / n;
+ SkScalar angle = 0;
+ for (int i = 1; i < n; i++) {
+ angle += step;
+ SkScalar c, s = SkScalarSinCos(angle, &c);
+ path->lineTo(c, s);
+ }
+ path->close();
+ }
+
+ static void rotate(SkCanvas* canvas, SkScalar angle, SkScalar px, SkScalar py) {
+ canvas->translate(-px, -py);
+ canvas->rotate(angle);
+ canvas->translate(px, py);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+// canvas->drawCircle(250, 250, 220, paint);
+ SkMatrix matrix;
+ matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
+ matrix.postTranslate(SkIntToScalar(200), SkIntToScalar(200));
+ canvas->concat(matrix);
+ for (int n = 3; n < 20; n++) {
+ SkPath path;
+ make_poly(&path, n);
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->rotate(SkIntToScalar(10) * (n - 3));
+ canvas->translate(-SK_Scalar1, 0);
+ canvas->drawPath(path, paint);
+ }
+ }
+
+private:
+ int fN;
+ typedef SampleView INHERITED;
+};
+
+const SkScalar CircleView::ANIM_DX(SK_Scalar1 / 67);
+const SkScalar CircleView::ANIM_DY(SK_Scalar1 / 29);
+const SkScalar CircleView::ANIM_RAD(SK_Scalar1 / 19);
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new CircleView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleClamp.cpp b/samplecode/SampleClamp.cpp
new file mode 100644
index 0000000000..88c1b9179b
--- /dev/null
+++ b/samplecode/SampleClamp.cpp
@@ -0,0 +1,61 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkGradientShader.h"
+#include "SkPicture.h"
+
+static SkShader* make_linear() {
+ SkPoint pts[] = { 0, 0, SK_Scalar1/500, SK_Scalar1/500 };
+ SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+ return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode);
+}
+
+class ClampView : public SampleView {
+ SkShader* fGrad;
+
+public:
+ ClampView() {
+ fGrad = make_linear();
+ }
+
+ virtual ~ClampView() {
+ fGrad->unref();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Clamp");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setShader(fGrad);
+
+// canvas->translate(this->width()/2, this->height()/2);
+ canvas->translate(64, 64);
+ canvas->drawPaint(paint);
+
+ SkPicture pic;
+ SkCanvas* c = pic.beginRecording(100, 100, 0);
+ SkCanvas::LayerIter layerIterator(c, false);
+ layerIterator.next();
+ layerIterator.done();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ClampView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleCode.h b/samplecode/SampleCode.h
new file mode 100644
index 0000000000..c42ee25801
--- /dev/null
+++ b/samplecode/SampleCode.h
@@ -0,0 +1,81 @@
+#ifndef SampleCode_DEFINED
+#define SampleCode_DEFINED
+
+#include "SkColor.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkView.h"
+
+class SampleCode {
+public:
+ static bool KeyQ(const SkEvent&, SkKey* outKey);
+ static bool CharQ(const SkEvent&, SkUnichar* outUni);
+
+ static bool TitleQ(const SkEvent&);
+ static void TitleR(SkEvent*, const char title[]);
+
+ static bool PrefSizeQ(const SkEvent&);
+ static void PrefSizeR(SkEvent*, SkScalar width, SkScalar height);
+
+ static bool FastTextQ(const SkEvent&);
+
+ static SkMSec GetAnimTime();
+ static SkMSec GetAnimTimeDelta();
+ static SkScalar GetAnimSecondsDelta();
+ static SkScalar GetAnimScalar(SkScalar speedPerSec, SkScalar period = 0);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+typedef SkView* (*SkViewFactory)();
+
+class SkViewRegister : SkNoncopyable {
+public:
+ explicit SkViewRegister(SkViewFactory);
+
+ static const SkViewRegister* Head() { return gHead; }
+
+ SkViewRegister* next() const { return fChain; }
+ SkViewFactory factory() const { return fFact; }
+
+private:
+ SkViewFactory fFact;
+ SkViewRegister* fChain;
+
+ static SkViewRegister* gHead;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SampleView : public SkView {
+public:
+ SampleView() : fRepeatCount(1), fBGColor(SK_ColorWHITE) {
+ fUsePipe = false;
+ }
+
+ void setBGColor(SkColor color) { fBGColor = color; }
+
+ static bool IsSampleView(SkView*);
+ static bool SetRepeatDraw(SkView*, int count);
+ static bool SetUsePipe(SkView*, bool);
+
+protected:
+ virtual void onDrawBackground(SkCanvas*);
+ virtual void onDrawContent(SkCanvas*) = 0;
+
+ // overrides
+ virtual bool onEvent(const SkEvent& evt);
+ virtual bool onQuery(SkEvent* evt);
+ virtual void onDraw(SkCanvas*);
+
+private:
+ int fRepeatCount;
+ SkColor fBGColor;
+
+ bool fUsePipe;
+
+ typedef SkView INHERITED;
+};
+
+#endif
+
diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp
new file mode 100644
index 0000000000..0e1fb11f5a
--- /dev/null
+++ b/samplecode/SampleColorFilter.cpp
@@ -0,0 +1,210 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+static int inflate5To8(int x) {
+ return (x << 3) | (x >> 2);
+}
+
+static int trunc5(int x) {
+ return x >> 3;
+}
+
+#define SK_R16_BITS 5
+
+static int round5_slow(int x) {
+ int orig = x & 7;
+ int fake = x >> 5;
+ int trunc = x >> 3;
+
+ int diff = fake - orig;
+
+ int bias = 0;
+ if (diff > 4) {
+ bias = -1;
+ } else if (diff < -4) {
+ bias = 1;
+ }
+ return trunc + bias;
+}
+
+static int round5_fast(int x) {
+ int result = x + 3 - (x >> 5) + (x >> 7);
+ result >>= 3;
+
+ {
+ int r2 = round5_slow(x);
+ SkASSERT(r2 == result);
+ }
+ return result;
+}
+
+static void test_5bits() {
+ int e0 = 0;
+ int e1 = 0;
+ int e2 = 0;
+ int ae0 = 0;
+ int ae1 = 0;
+ int ae2 = 0;
+ for (int i = 0; i < 256; i++) {
+ int t0 = trunc5(i);
+ int t1 = round5_fast(i);
+ int t2 = trunc5(i);
+ int v0 = inflate5To8(t0);
+ int v1 = inflate5To8(t1);
+ int v2 = inflate5To8(t2);
+ int err0 = i - v0;
+ int err1 = i - v1;
+ int err2 = i - v2;
+ SkDebugf("--- %3d : trunc=%3d (%2d) round:%3d (%2d) \n"/*new:%d (%2d)\n"*/, i,
+ v0, err0, v1, err1, v2, err2);
+
+
+ e0 += err0;
+ e1 += err1;
+ e2 += err2;
+ ae0 += SkAbs32(err0);
+ ae1 += SkAbs32(err1);
+ ae2 += SkAbs32(err2);
+ }
+ SkDebugf("--- trunc: %d %d round: %d %d new: %d %d\n", e0, ae0, e1, ae1, e2, ae2);
+}
+
+static SkShader* createChecker() {
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ bm.allocPixels();
+ bm.lockPixels();
+ *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(0xFFFFFFFF);
+ *bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(0xFFCCCCCC);
+ SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+
+ SkMatrix m;
+ m.setScale(12, 12);
+ s->setLocalMatrix(m);
+ return s;
+}
+
+static SkBitmap createBitmap(int n) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+
+ SkCanvas canvas(bitmap);
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
+ r.inset(SK_Scalar1, SK_Scalar1);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ paint.setColor(SK_ColorRED);
+ canvas.drawOval(r, paint);
+
+ r.inset(SK_Scalar1*n/4, SK_Scalar1*n/4);
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ paint.setColor(0x800000FF);
+ canvas.drawOval(r, paint);
+
+ return bitmap;
+}
+
+class ColorFilterView : public SampleView {
+ SkBitmap fBitmap;
+ SkShader* fShader;
+ enum {
+ N = 64
+ };
+public:
+ ColorFilterView() {
+ fBitmap = createBitmap(N);
+ fShader = createChecker();
+
+// test_5bits();
+ }
+
+ virtual ~ColorFilterView() {
+ fShader->unref();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "ColorFilter");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawBackground(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setShader(fShader);
+ canvas->drawPaint(paint);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ if (false) {
+ SkPaint p;
+ p.setAntiAlias(true);
+ SkRect r = { 20.4f, 10, 20.6f, 20 };
+ canvas->drawRect(r, p);
+ r.set(30.9f, 10, 31.1f, 20);
+ canvas->drawRect(r, p);
+ return;
+ }
+
+ static const SkXfermode::Mode gModes[] = {
+ SkXfermode::kClear_Mode,
+ SkXfermode::kSrc_Mode,
+ SkXfermode::kDst_Mode,
+ SkXfermode::kSrcOver_Mode,
+ SkXfermode::kDstOver_Mode,
+ SkXfermode::kSrcIn_Mode,
+ SkXfermode::kDstIn_Mode,
+ SkXfermode::kSrcOut_Mode,
+ SkXfermode::kDstOut_Mode,
+ SkXfermode::kSrcATop_Mode,
+ SkXfermode::kDstATop_Mode,
+ SkXfermode::kXor_Mode,
+ SkXfermode::kPlus_Mode,
+ SkXfermode::kMultiply_Mode,
+ };
+
+ static const SkColor gColors[] = {
+ 0xFF000000,
+ 0x80000000,
+ 0xFF00FF00,
+ 0x8000FF00,
+ 0x00000000,
+ };
+
+ float scale = 1.5f;
+ SkPaint paint;
+ canvas->translate(N / 8, N / 8);
+
+ for (size_t y = 0; y < SK_ARRAY_COUNT(gColors); y++) {
+ for (size_t x = 0; x < SK_ARRAY_COUNT(gModes); x++) {
+ SkColorFilter* cf = SkColorFilter::CreateModeFilter(gColors[y], gModes[x]);
+ SkSafeUnref(paint.setColorFilter(cf));
+ canvas->drawBitmap(fBitmap, x * N * 1.25f, y * N * scale, &paint);
+ }
+ }
+
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ColorFilterView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp
new file mode 100644
index 0000000000..672d0555c5
--- /dev/null
+++ b/samplecode/SampleComplexClip.cpp
@@ -0,0 +1,147 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkView.h"
+
+class ComplexClipView : public SampleView {
+public:
+ ComplexClipView() {
+ this->setBGColor(0xFFA0DDA0);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "ComplexClip");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPath path;
+ path.moveTo(SkIntToScalar(0), SkIntToScalar(50));
+ path.quadTo(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(50), SkIntToScalar(0));
+ path.lineTo(SkIntToScalar(175), SkIntToScalar(0));
+ path.quadTo(SkIntToScalar(200), SkIntToScalar(0), SkIntToScalar(200), SkIntToScalar(25));
+ path.lineTo(SkIntToScalar(200), SkIntToScalar(150));
+ path.quadTo(SkIntToScalar(200), SkIntToScalar(200), SkIntToScalar(150), SkIntToScalar(200));
+ path.lineTo(SkIntToScalar(0), SkIntToScalar(200));
+ path.close();
+ path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
+ path.lineTo(SkIntToScalar(150), SkIntToScalar(50));
+ path.lineTo(SkIntToScalar(150), SkIntToScalar(125));
+ path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150));
+ path.lineTo(SkIntToScalar(50), SkIntToScalar(150));
+ path.close();
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ SkColor pathColor = SK_ColorBLACK;
+ SkPaint pathPaint;
+ pathPaint.setAntiAlias(true);
+ pathPaint.setColor(pathColor);
+
+ SkPath clipA;
+ clipA.moveTo(SkIntToScalar(10), SkIntToScalar(20));
+ clipA.lineTo(SkIntToScalar(165), SkIntToScalar(22));
+ clipA.lineTo(SkIntToScalar(70), SkIntToScalar(105));
+ clipA.lineTo(SkIntToScalar(165), SkIntToScalar(177));
+ clipA.lineTo(SkIntToScalar(-5), SkIntToScalar(180));
+ clipA.close();
+ SkColor colorA = SK_ColorCYAN;
+
+ SkPath clipB;
+ clipB.moveTo(SkIntToScalar(40), SkIntToScalar(10));
+ clipB.lineTo(SkIntToScalar(190), SkIntToScalar(15));
+ clipB.lineTo(SkIntToScalar(195), SkIntToScalar(190));
+ clipB.lineTo(SkIntToScalar(40), SkIntToScalar(185));
+ clipB.lineTo(SkIntToScalar(155), SkIntToScalar(100));
+ clipB.close();
+ SkColor colorB = SK_ColorRED;
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(0);
+
+ canvas->translate(SkIntToScalar(10),SkIntToScalar(10));
+ canvas->drawPath(path, pathPaint);
+ paint.setColor(colorA);
+ canvas->drawPath(clipA, paint);
+ paint.setColor(colorB);
+ canvas->drawPath(clipB, paint);
+
+ static const struct {
+ SkRegion::Op fOp;
+ const char* fName;
+ } gOps[] = { //extra spaces in names for measureText
+ {SkRegion::kIntersect_Op, "Isect "},
+ {SkRegion::kDifference_Op, "Diff " },
+ {SkRegion::kUnion_Op, "Union "},
+ {SkRegion::kXOR_Op, "Xor " },
+ {SkRegion::kReverseDifference_Op, "RDiff "}
+ };
+
+ canvas->translate(0, SkIntToScalar(40));
+ canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4);
+ canvas->save();
+
+ for (int invA = 0; invA < 2; ++invA) {
+ for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
+ int idx = invA * SK_ARRAY_COUNT(gOps) + op;
+ if (!(idx % 3)) {
+ canvas->restore();
+ canvas->translate(0, SkIntToScalar(250));
+ canvas->save();
+ }
+ canvas->save();
+ // set clip
+ clipA.setFillType(invA ? SkPath::kInverseEvenOdd_FillType :
+ SkPath::kEvenOdd_FillType);
+ canvas->clipPath(clipA);
+ canvas->clipPath(clipB, gOps[op].fOp);
+
+ // draw path clipped
+ canvas->drawPath(path, pathPaint);
+ canvas->restore();
+
+ // draw path in hairline
+ paint.setColor(pathColor);
+ canvas->drawPath(path, paint);
+
+ // draw clips in hair line
+ paint.setColor(colorA);
+ canvas->drawPath(clipA, paint);
+ paint.setColor(colorB);
+ canvas->drawPath(clipB, paint);
+
+ paint.setTextSize(SkIntToScalar(20));
+
+ SkScalar txtX = SkIntToScalar(55);
+ paint.setColor(colorA);
+ const char* aTxt = invA ? "InverseA " : "A ";
+ canvas->drawText(aTxt, strlen(aTxt), txtX, SkIntToScalar(220), paint);
+ txtX += paint.measureText(aTxt, strlen(aTxt));
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawText(gOps[op].fName, strlen(gOps[op].fName),
+ txtX, SkIntToScalar(220), paint);
+ txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName));
+ paint.setColor(colorB);
+ canvas->drawText("B", 1, txtX, SkIntToScalar(220), paint);
+
+ canvas->translate(SkIntToScalar(250),0);
+ }
+ }
+ canvas->restore();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ComplexClipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleCull.cpp b/samplecode/SampleCull.cpp
new file mode 100644
index 0000000000..7b4eab605d
--- /dev/null
+++ b/samplecode/SampleCull.cpp
@@ -0,0 +1,189 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkCornerPathEffect.h"
+#include "SkCullPoints.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkRandom.h"
+
+static void addbump(SkPath* path, const SkPoint pts[2], SkScalar bump) {
+ SkVector tang;
+
+ tang.setLength(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY, bump);
+
+ path->lineTo(SkScalarHalf(pts[0].fX + pts[1].fX) - tang.fY,
+ SkScalarHalf(pts[0].fY + pts[1].fY) + tang.fX);
+ path->lineTo(pts[1]);
+}
+
+static void subdivide(SkPath* path, SkScalar bump) {
+ SkPath::Iter iter(*path, false);
+ SkPoint pts[4];
+ SkPath tmp;
+
+ for (;;)
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb:
+ tmp.moveTo(pts[0]);
+ break;
+ case SkPath::kLine_Verb:
+ addbump(&tmp, pts, bump);
+ bump = -bump;
+ break;
+ case SkPath::kDone_Verb:
+ goto FINISH;
+ default:
+ break;
+ }
+
+FINISH:
+ path->swap(tmp);
+}
+
+static SkIPoint* getpts(const SkPath& path, int* count) {
+ SkPoint pts[4];
+ int n = 1;
+ SkIPoint* array;
+
+ {
+ SkPath::Iter iter(path, false);
+ for (;;)
+ switch (iter.next(pts)) {
+ case SkPath::kLine_Verb:
+ n += 1;
+ break;
+ case SkPath::kDone_Verb:
+ goto FINISHED;
+ default:
+ break;
+ }
+ }
+
+FINISHED:
+ array = new SkIPoint[n];
+ n = 0;
+
+ {
+ SkPath::Iter iter(path, false);
+ for (;;)
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb:
+ array[n++].set(SkScalarRound(pts[0].fX), SkScalarRound(pts[0].fY));
+ break;
+ case SkPath::kLine_Verb:
+ array[n++].set(SkScalarRound(pts[1].fX), SkScalarRound(pts[1].fY));
+ break;
+ case SkPath::kDone_Verb:
+ goto FINISHED2;
+ default:
+ break;
+ }
+ }
+
+FINISHED2:
+ *count = n;
+ return array;
+}
+
+static SkScalar nextScalarRange(SkRandom& rand, SkScalar min, SkScalar max) {
+ return min + SkScalarMul(rand.nextUScalar1(), max - min);
+}
+
+class CullView : public SampleView {
+public:
+ CullView() {
+ fClip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
+
+ SkRandom rand;
+
+ for (int i = 0; i < 50; i++) {
+ SkScalar x = nextScalarRange(rand, -fClip.width()*1, fClip.width()*2);
+ SkScalar y = nextScalarRange(rand, -fClip.height()*1, fClip.height()*2);
+ if (i == 0)
+ fPath.moveTo(x, y);
+ else
+ fPath.lineTo(x, y);
+ }
+
+ SkScalar bump = fClip.width()/8;
+ subdivide(&fPath, bump);
+ subdivide(&fPath, bump);
+ subdivide(&fPath, bump);
+ fPoints = getpts(fPath, &fPtCount);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~CullView() {
+ delete[] fPoints;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Culling");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkAutoCanvasRestore ar(canvas, true);
+
+ canvas->translate( SkScalarHalf(this->width() - fClip.width()),
+ SkScalarHalf(this->height() - fClip.height()));
+
+ // canvas->scale(SK_Scalar1*3, SK_Scalar1*3, 0, 0);
+
+ SkPaint paint;
+
+ // paint.setAntiAliasOn(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ canvas->drawRect(fClip, paint);
+
+#if 1
+ paint.setColor(0xFF555555);
+ paint.setStrokeWidth(SkIntToScalar(2));
+// paint.setPathEffect(new SkCornerPathEffect(SkIntToScalar(30)))->unref();
+ canvas->drawPath(fPath, paint);
+// paint.setPathEffect(NULL);
+#endif
+
+ SkPath tmp;
+ SkIRect iclip;
+ fClip.round(&iclip);
+
+ SkCullPointsPath cpp(iclip, &tmp);
+
+ cpp.moveTo(fPoints[0].fX, fPoints[0].fY);
+ for (int i = 0; i < fPtCount; i++)
+ cpp.lineTo(fPoints[i].fX, fPoints[i].fY);
+
+ paint.setColor(SK_ColorRED);
+ paint.setStrokeWidth(SkIntToScalar(3));
+ paint.setStrokeJoin(SkPaint::kRound_Join);
+ canvas->drawPath(tmp, paint);
+
+ this->inval(NULL);
+ }
+
+private:
+ SkRect fClip;
+ SkIPoint* fPoints;
+ SkPath fPath;
+ int fPtCount;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new CullView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDash.cpp b/samplecode/SampleDash.cpp
new file mode 100644
index 0000000000..4cef07f208
--- /dev/null
+++ b/samplecode/SampleDash.cpp
@@ -0,0 +1,88 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkDashPathEffect.h"
+#include "SkShader.h"
+
+static void setBitmapDash(SkPaint* paint, int width) {
+ SkColor c = paint->getColor();
+
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
+ bm.allocPixels();
+ bm.lockPixels();
+ *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
+ SkColorGetG(c), SkColorGetB(c));
+ *bm.getAddr32(1, 0) = 0;
+ bm.unlockPixels();
+
+ SkMatrix matrix;
+ matrix.setScale(SkIntToScalar(width), SK_Scalar1);
+
+ SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+ SkShader::kClamp_TileMode);
+ s->setLocalMatrix(matrix);
+
+ paint->setShader(s)->unref();
+}
+
+class DashView : public SampleView {
+public:
+ DashView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Dash");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ static const char* gStr[] = {
+ "11",
+ "44",
+ "112233",
+ "411327463524",
+ };
+
+ SkPaint paint;
+ paint.setStrokeWidth(SkIntToScalar(1));
+
+ SkScalar x0 = SkIntToScalar(10);
+ SkScalar y0 = SkIntToScalar(10);
+ SkScalar x1 = x0 + SkIntToScalar(1000);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gStr); i++) {
+ SkScalar interval[12];
+ size_t len = SkMin32(strlen(gStr[i]), SK_ARRAY_COUNT(interval));
+ for (size_t j = 0; j < len; j++) {
+ interval[j] = SkIntToScalar(gStr[i][j] - '0');
+ }
+
+ SkDashPathEffect dash(interval, len, 0);
+ paint.setPathEffect(&dash);
+ canvas->drawLine(x0, y0, x1, y0, paint);
+ paint.setPathEffect(NULL);
+
+ y0 += paint.getStrokeWidth() * 3;
+ }
+
+ setBitmapDash(&paint, 3);
+ canvas->drawLine(x0, y0, x1, y0, paint);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DashView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDecode.cpp b/samplecode/SampleDecode.cpp
new file mode 100644
index 0000000000..b192c5d599
--- /dev/null
+++ b/samplecode/SampleDecode.cpp
@@ -0,0 +1,69 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+
+static const struct {
+ SkBitmap::Config fPrefConfig;
+ bool fDither;
+} gRec[] = {
+ { SkBitmap::kIndex8_Config, false },
+ { SkBitmap::kARGB_8888_Config, false },
+ { SkBitmap::kARGB_4444_Config, false },
+ { SkBitmap::kARGB_4444_Config, true },
+ { SkBitmap::kRGB_565_Config, false },
+ { SkBitmap::kRGB_565_Config, true },
+};
+
+class DecodeView : public SkView {
+public:
+ SkBitmap fBitmap[SK_ARRAY_COUNT(gRec)];
+
+ DecodeView() {
+ SkFILEStream stream("/skimages/index.png");
+ SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+ if (codec) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+ stream.rewind();
+ codec->setDitherImage(gRec[i].fDither);
+ codec->decode(&stream, &fBitmap[i], gRec[i].fPrefConfig,
+ SkImageDecoder::kDecodePixels_Mode);
+ }
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "ImageDecoder");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+// canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fBitmap); i++) {
+ canvas->drawBitmap(fBitmap[i], 0, 0);
+ canvas->translate(SkIntToScalar(fBitmap[i].width()), 0);
+ }
+ }
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DecodeView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
new file mode 100644
index 0000000000..3e77a5deef
--- /dev/null
+++ b/samplecode/SampleDither.cpp
@@ -0,0 +1,178 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDither.h"
+
+static void draw_sweep(SkCanvas* c, int width, int height, SkScalar angle) {
+ SkRect r;
+ SkPaint p;
+
+ p.setAntiAlias(true);
+// p.setDither(true);
+ p.setStrokeWidth(SkIntToScalar(width/10));
+ p.setStyle(SkPaint::kStroke_Style);
+
+ r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
+
+ // SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN, SK_ColorCYAN };
+ SkColor colors[] = { 0x4c737373, 0x4c737373, 0xffffd300 };
+ SkShader* s = SkGradientShader::CreateSweep(r.centerX(), r.centerY(),
+ colors, NULL, SK_ARRAY_COUNT(colors));
+ p.setShader(s)->unref();
+
+ SkAutoCanvasRestore acr(c, true);
+
+ c->translate(r.centerX(), r.centerY());
+ c->rotate(angle);
+ c->translate(-r.centerX(), -r.centerY());
+
+ SkRect bounds = r;
+ r.inset(p.getStrokeWidth(), p.getStrokeWidth());
+ SkRect innerBounds = r;
+
+ if (true) {
+ c->drawOval(r, p);
+ } else {
+ SkScalar x = r.centerX();
+ SkScalar y = r.centerY();
+ SkScalar radius = r.width() / 2;
+ SkScalar thickness = p.getStrokeWidth();
+ SkScalar sweep = SkFloatToScalar(360.0f);
+ SkPath path;
+
+ path.moveTo(x + radius, y);
+ // outer top
+ path.lineTo(x + radius + thickness, y);
+ // outer arc
+ path.arcTo(bounds, 0, sweep, false);
+ // inner arc
+ path.arcTo(innerBounds, sweep, -sweep, false);
+ path.close();
+ }
+}
+
+static void make_bm(SkBitmap* bm) {
+ bm->setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+ bm->allocPixels();
+#if 0
+ bm->eraseColor(SK_ColorBLUE);
+ return;
+#else
+ bm->eraseColor(0);
+#endif
+
+ SkCanvas c(*bm);
+ draw_sweep(&c, bm->width(), bm->height(), 0);
+}
+
+static void pre_dither(const SkBitmap& bm) {
+ SkAutoLockPixels alp(bm);
+
+ for (int y = 0; y < bm.height(); y++) {
+ DITHER_4444_SCAN(y);
+
+ SkPMColor* p = bm.getAddr32(0, y);
+ for (int x = 0; x < bm.width(); x++) {
+ SkPMColor c = *p;
+
+ unsigned a = SkGetPackedA32(c);
+ unsigned r = SkGetPackedR32(c);
+ unsigned g = SkGetPackedG32(c);
+ unsigned b = SkGetPackedB32(c);
+
+ unsigned d = DITHER_VALUE(x);
+
+ a = SkDITHER_A32To4444(a, d);
+ r = SkDITHER_R32To4444(r, d);
+ g = SkDITHER_G32To4444(g, d);
+ b = SkDITHER_B32To4444(b, d);
+
+ a = SkA4444ToA32(a);
+ r = SkR4444ToR32(r);
+ g = SkG4444ToG32(g);
+ b = SkB4444ToB32(b);
+
+ *p++ = SkPackARGB32(a, r, g, b);
+ }
+ }
+}
+
+class DitherView : public SampleView {
+public:
+ SkBitmap fBM, fBMPreDither, fBM16;
+ SkScalar fAngle;
+
+ DitherView() {
+ make_bm(&fBM);
+ make_bm(&fBMPreDither);
+ pre_dither(fBMPreDither);
+ fBM.copyTo(&fBM16, SkBitmap::kARGB_4444_Config);
+
+ fAngle = 0;
+
+ this->setBGColor(0xFF181818);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Dither");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(10);
+ const SkScalar DX = SkIntToScalar(fBM.width() + 10);
+
+ paint.setAntiAlias(true);
+
+ if (true) {
+ canvas->drawBitmap(fBM, x, y, &paint);
+ x += DX;
+ paint.setDither(true);
+ canvas->drawBitmap(fBM, x, y, &paint);
+
+ x += DX;
+ paint.setDither(false);
+ canvas->drawBitmap(fBMPreDither, x, y, &paint);
+
+ x += DX;
+ canvas->drawBitmap(fBM16, x, y, &paint);
+ }
+
+ canvas->translate(DX, DX*2);
+ draw_sweep(canvas, fBM.width(), fBM.height(), fAngle);
+ canvas->translate(DX, 0);
+ draw_sweep(canvas, fBM.width()>>1, fBM.height()>>1, fAngle);
+ canvas->translate(DX, 0);
+ draw_sweep(canvas, fBM.width()>>2, fBM.height()>>2, fAngle);
+
+ fAngle += SK_Scalar1/2;
+ this->inval(NULL);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DitherView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDitherBitmap.cpp b/samplecode/SampleDitherBitmap.cpp
new file mode 100644
index 0000000000..0d62446dec
--- /dev/null
+++ b/samplecode/SampleDitherBitmap.cpp
@@ -0,0 +1,141 @@
+#include "SampleCode.h"
+#include "SkColorPriv.h"
+#include "SkGradientShader.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkUtils.h"
+
+static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+ canvas->drawRect(r, p);
+
+ SkPaint frame(p);
+ frame.setShader(NULL);
+ frame.setStyle(SkPaint::kStroke_Style);
+ canvas->drawRect(r, frame);
+}
+
+static void draw_gradient(SkCanvas* canvas) {
+ SkRect r = { 0, 0, SkIntToScalar(256), SkIntToScalar(32) };
+ SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } };
+ SkColor colors[] = { 0xFF000000, 0xFFFF0000 };
+ SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode);
+
+ SkPaint p;
+ p.setShader(s)->unref();
+ draw_rect(canvas, r, p);
+
+ canvas->translate(0, SkIntToScalar(40));
+ p.setDither(true);
+ draw_rect(canvas, r, p);
+}
+
+static void test_pathregion() {
+ SkPath path;
+ SkRegion region;
+ path.moveTo(25071800.f, -141823808.f);
+ path.lineTo(25075500.f, -141824000.f);
+ path.lineTo(25075400.f, -141827712.f);
+ path.lineTo(25071810.f, -141827600.f);
+ path.close();
+
+ SkIRect bounds;
+ path.getBounds().round(&bounds);
+ SkRegion clip(bounds);
+ bool result = region.setPath(path, clip); // <-- !! DOWN !!
+ SkDebugf("----- result %d\n", result);
+}
+
+static SkBitmap make_bitmap() {
+ SkBitmap bm;
+ SkColorTable* ctable = new SkColorTable(256);
+
+ SkPMColor* c = ctable->lockColors();
+ for (int i = 0; i < 256; i++) {
+ c[i] = SkPackARGB32(0xFF, i, 0, 0);
+ }
+ ctable->unlockColors(true);
+ bm.setConfig(SkBitmap::kIndex8_Config, 256, 32);
+ bm.allocPixels(ctable);
+ ctable->unref();
+
+ bm.lockPixels();
+ for (int y = 0; y < bm.height(); y++) {
+ uint8_t* p = bm.getAddr8(0, y);
+ for (int x = 0; x < 256; x++) {
+ p[x] = x;
+ }
+ }
+ bm.unlockPixels();
+ return bm;
+}
+
+class DitherBitmapView : public SampleView {
+ SkBitmap fBM8;
+ SkBitmap fBM32;
+public:
+ DitherBitmapView() {
+ test_pathregion();
+ fBM8 = make_bitmap();
+ fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "DitherBitmap");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) {
+ SkAutoLockPixels alp(*bm); // needed for ctable
+ bm->setIsOpaque(isOpaque);
+ SkColorTable* ctable = bm->getColorTable();
+ if (ctable) {
+ ctable->setIsOpaque(isOpaque);
+ }
+ }
+
+ static void draw2(SkCanvas* canvas, const SkBitmap& bm) {
+ SkPaint paint;
+ SkBitmap bitmap(bm);
+
+ setBitmapOpaque(&bitmap, false);
+ paint.setDither(false);
+ canvas->drawBitmap(bitmap, 0, 0, &paint);
+ paint.setDither(true);
+ canvas->drawBitmap(bitmap, 0, SkIntToScalar(bm.height() + 10), &paint);
+
+ setBitmapOpaque(&bitmap, true);
+ SkScalar x = SkIntToScalar(bm.width() + 10);
+ paint.setDither(false);
+ canvas->drawBitmap(bitmap, x, 0, &paint);
+ paint.setDither(true);
+ canvas->drawBitmap(bitmap, x, SkIntToScalar(bm.height() + 10), &paint);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+ draw2(canvas, fBM8);
+ canvas->translate(0, SkIntToScalar(fBM8.height() *3));
+ draw2(canvas, fBM32);
+
+ canvas->translate(0, SkIntToScalar(fBM8.height() *3));
+ draw_gradient(canvas);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DitherBitmapView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDraw.cpp b/samplecode/SampleDraw.cpp
new file mode 100644
index 0000000000..deb1fb2728
--- /dev/null
+++ b/samplecode/SampleDraw.cpp
@@ -0,0 +1,373 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+
+static void test_clearonlayers(SkCanvas* canvas) {
+ SkCanvas& c = *canvas;
+
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ SkRect rect = SkRect::MakeXYWH(25, 25, 50, 50);
+ c.drawRect(rect, paint);
+
+ c.clipRect(rect);
+
+ c.saveLayer(NULL, NULL);
+ rect = SkRect::MakeXYWH(50, 10, 40, 80);
+ c.clipRect(rect, SkRegion::kUnion_Op);
+
+ rect = SkRect::MakeXYWH(50, 0, 50, 100);
+ // You might draw something here, but it's not necessary.
+ // paint.setColor(SK_ColorRED);
+ // c.drawRect(rect, paint);
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ c.drawRect(rect, paint);
+ c.restore();
+}
+
+static void test_strokerect(SkCanvas* canvas, const SkRect& r) {
+ SkPaint p;
+
+ p.setAntiAlias(true);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(4);
+
+ canvas->drawRect(r, p);
+
+ SkPath path;
+ SkRect r2(r);
+ r2.offset(18, 0);
+ path.addRect(r2);
+
+ canvas->drawPath(path, p);
+}
+
+static void test_strokerect(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+
+ SkRect r;
+
+ r.set(10, 10, 14, 14);
+ r.offset(0.25f, 0.3333f);
+ test_strokerect(canvas, r);
+ canvas->translate(0, 20);
+
+ r.set(10, 10, 14.5f, 14.5f);
+ r.offset(0.25f, 0.3333f);
+ test_strokerect(canvas, r);
+ canvas->translate(0, 20);
+
+ r.set(10, 10, 14.5f, 20);
+ r.offset(0.25f, 0.3333f);
+ test_strokerect(canvas, r);
+ canvas->translate(0, 20);
+
+ r.set(10, 10, 20, 14.5f);
+ r.offset(0.25f, 0.3333f);
+ test_strokerect(canvas, r);
+ canvas->translate(0, 20);
+
+ r.set(10, 10, 20, 20);
+ r.offset(0.25f, 0.3333f);
+ test_strokerect(canvas, r);
+ canvas->translate(0, 20);
+
+}
+
+class Draw : public SkRefCnt {
+public:
+ Draw() : fFlags(0) {}
+
+ enum Flags {
+ kSelected_Flag = 1 << 0
+ };
+ int getFlags() const { return fFlags; }
+ void setFlags(int flags);
+
+ bool isSelected() const { return SkToBool(fFlags & kSelected_Flag); }
+ void setSelected(bool pred) {
+ if (pred) {
+ fFlags |= kSelected_Flag;
+ } else {
+ fFlags &= ~kSelected_Flag;
+ }
+ }
+
+ void draw(SkCanvas* canvas) {
+ int sc = canvas->save();
+ this->onDraw(canvas);
+ canvas->restoreToCount(sc);
+
+ if (this->isSelected()) {
+ this->drawSelection(canvas);
+ }
+ }
+
+ void drawSelection(SkCanvas* canvas) {
+ int sc = canvas->save();
+ this->onDrawSelection(canvas);
+ canvas->restoreToCount(sc);
+ }
+
+ void getBounds(SkRect* bounds) {
+ this->onGetBounds(bounds);
+ }
+
+ bool hitTest(SkScalar x, SkScalar y) {
+ return this->onHitTest(x, y);
+ }
+
+ void offset(SkScalar dx, SkScalar dy) {
+ if (dx || dy) {
+ this->onOffset(dx, dy);
+ }
+ }
+
+protected:
+ virtual void onDraw(SkCanvas*) = 0;
+ virtual void onGetBounds(SkRect*) = 0;
+ virtual void onOffset(SkScalar dx, SkScalar dy) = 0;
+ virtual void onDrawSelection(SkCanvas* canvas) {
+ SkRect r;
+ this->getBounds(&r);
+ SkPaint paint;
+ SkPoint pts[4];
+ r.toQuad(pts);
+ paint.setStrokeWidth(SkIntToScalar(10));
+ paint.setColor(0x80FF8844);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, pts, paint);
+ }
+ virtual bool onHitTest(SkScalar x, SkScalar y) {
+ SkRect bounds;
+ this->getBounds(&bounds);
+ return bounds.contains(x, y);
+ }
+
+private:
+ int fFlags;
+};
+
+class RDraw : public Draw {
+public:
+ enum Style {
+ kRect_Style,
+ kOval_Style,
+ kRRect_Style,
+ kFrame_Style
+ };
+
+ RDraw(const SkRect& r, Style s) : fRect(r), fStyle(s) {}
+
+ void setRect(const SkRect& r) {
+ fRect = r;
+ }
+
+ void setPaint(const SkPaint& p) {
+ fPaint = p;
+ }
+
+protected:
+ virtual void onDraw(SkCanvas* canvas) {
+ switch (fStyle) {
+ case kRect_Style:
+ canvas->drawRect(fRect, fPaint);
+ break;
+ case kOval_Style:
+ canvas->drawOval(fRect, fPaint);
+ break;
+ case kRRect_Style: {
+ SkScalar rx = fRect.width() / 5;
+ SkScalar ry = fRect.height() / 5;
+ if (rx < ry) {
+ ry = rx;
+ } else {
+ rx = ry;
+ }
+ canvas->drawRoundRect(fRect, rx, ry, fPaint);
+ break;
+ }
+ case kFrame_Style: {
+ SkPath path;
+ path.addOval(fRect, SkPath::kCW_Direction);
+ SkRect r = fRect;
+ r.inset(fRect.width()/6, 0);
+ path.addOval(r, SkPath::kCCW_Direction);
+ canvas->drawPath(path, fPaint);
+ break;
+ }
+ }
+ }
+
+ virtual void onGetBounds(SkRect* bounds) {
+ *bounds = fRect;
+ }
+
+ virtual void onOffset(SkScalar dx, SkScalar dy) {
+ fRect.offset(dx, dy);
+ }
+
+private:
+ SkRect fRect;
+ SkPaint fPaint;
+ Style fStyle;
+};
+
+class DrawFactory {
+public:
+ DrawFactory() {
+ fPaint.setAntiAlias(true);
+ }
+
+ const SkPaint& getPaint() const { return fPaint; }
+
+ void setPaint(const SkPaint& p) {
+ fPaint = p;
+ }
+
+ virtual Draw* create(const SkPoint&, const SkPoint&) = 0;
+
+private:
+ SkPaint fPaint;
+};
+
+class RectFactory : public DrawFactory {
+public:
+ virtual Draw* create(const SkPoint& p0, const SkPoint& p1) {
+ SkRect r;
+ r.set(p0.x(), p0.y(), p1.x(), p1.y());
+ r.sort();
+
+// RDraw* d = new RDraw(r, RDraw::kRRect_Style);
+ RDraw* d = new RDraw(r, RDraw::kFrame_Style);
+ d->setPaint(this->getPaint());
+ return d;
+ }
+};
+
+class DrawView : public SkView {
+ Draw* fDraw;
+ DrawFactory* fFactory;
+ SkRandom fRand;
+ SkTDArray<Draw*> fList;
+
+public:
+ DrawView() : fDraw(NULL) {
+ fFactory = new RectFactory;
+ }
+
+ virtual ~DrawView() {
+ fList.unrefAll();
+ SkSafeUnref(fDraw);
+ delete fFactory;
+ }
+
+ Draw* setDraw(Draw* d) {
+ SkRefCnt_SafeAssign(fDraw, d);
+ return d;
+ }
+
+ SkColor randColor() {
+ return (SkColor)fRand.nextU() | 0xFF000000;
+ }
+
+ Draw* hitTestList(SkScalar x, SkScalar y) const {
+ Draw** first = fList.begin();
+ for (Draw** iter = fList.end(); iter > first;) {
+ --iter;
+ if ((*iter)->hitTest(x, y)) {
+ return *iter;
+ }
+ }
+ return NULL;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Draw");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+// canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+ test_clearonlayers(canvas); return;
+ // test_strokerect(canvas); return;
+
+ for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
+ (*iter)->draw(canvas);
+ }
+ if (fDraw) {
+ fDraw->draw(canvas);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
+ (*iter)->setSelected(false);
+ }
+
+ Click* c = new Click(this);
+ Draw* d = this->hitTestList(x, y);
+ if (d) {
+ d->setSelected(true);
+ c->setType("dragger");
+ } else {
+ c->setType("maker");
+ }
+ return c;
+ }
+
+ virtual bool onClick(Click* click) {
+ if (Click::kUp_State == click->fState) {
+ if (click->isType("maker")) {
+ if (SkPoint::Distance(click->fOrig, click->fCurr) > SkIntToScalar(3)) {
+ *fList.append() = fDraw;
+ } else {
+ fDraw->unref();
+ }
+ fDraw = NULL;
+ }
+ return true;
+ }
+
+ if (Click::kDown_State == click->fState) {
+ SkPaint p = fFactory->getPaint();
+ p.setColor(this->randColor());
+ fFactory->setPaint(p);
+ }
+
+ if (click->isType("maker")) {
+ this->setDraw(fFactory->create(click->fOrig, click->fCurr))->unref();
+ } else if (click->isType("dragger")) {
+ for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
+ if ((*iter)->isSelected()) {
+ (*iter)->offset(click->fCurr.x() - click->fPrev.x(),
+ click->fCurr.y() - click->fPrev.y());
+ }
+ }
+ }
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DrawView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleDrawLooper.cpp b/samplecode/SampleDrawLooper.cpp
new file mode 100644
index 0000000000..30879f75fe
--- /dev/null
+++ b/samplecode/SampleDrawLooper.cpp
@@ -0,0 +1,92 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkLayerDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+
+#define WIDTH 200
+#define HEIGHT 200
+
+class LooperView : public SampleView {
+public:
+
+ SkLayerDrawLooper* fLooper;
+
+ LooperView() {
+ static const struct {
+ SkColor fColor;
+ SkPaint::Style fStyle;
+ SkScalar fWidth;
+ SkScalar fOffset;
+ int fBlur;
+ } gParams[] = {
+ { SK_ColorWHITE, SkPaint::kStroke_Style, SkIntToScalar(1)*3/4, 0, 0 },
+ { SK_ColorRED, SkPaint::kStroke_Style, SkIntToScalar(4), 0, 0 },
+ { SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0 },
+ { 0x88000000, SkPaint::kFill_Style, 0, SkIntToScalar(10), 3 }
+ };
+
+ fLooper = new SkLayerDrawLooper;
+
+ SkLayerDrawLooper::LayerInfo info;
+ info.fFlagsMask = SkPaint::kAntiAlias_Flag;
+ info.fPaintBits = SkLayerDrawLooper::kStyle_Bit | SkLayerDrawLooper::kMaskFilter_Bit;
+ info.fColorMode = SkXfermode::kSrc_Mode;
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gParams); i++) {
+ info.fOffset.set(gParams[i].fOffset, gParams[i].fOffset);
+ SkPaint* paint = fLooper->addLayer(info);
+ paint->setAntiAlias(true);
+ paint->setColor(gParams[i].fColor);
+ paint->setStyle(gParams[i].fStyle);
+ paint->setStrokeWidth(gParams[i].fWidth);
+ if (gParams[i].fBlur > 0) {
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(gParams[i].fBlur),
+ SkBlurMaskFilter::kNormal_BlurStyle);
+ paint->setMaskFilter(mf)->unref();
+ }
+ }
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~LooperView() {
+ SkSafeUnref(fLooper);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "DrawLooper");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setTextSize(SkIntToScalar(72));
+ paint.setLooper(fLooper);
+
+ canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+ SkIntToScalar(30), paint);
+
+ canvas->drawRectCoords(SkIntToScalar(150), SkIntToScalar(50),
+ SkIntToScalar(200), SkIntToScalar(100), paint);
+
+ canvas->drawText("Looper", 6, SkIntToScalar(230), SkIntToScalar(100),
+ paint);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LooperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleEffects.cpp b/samplecode/SampleEffects.cpp
new file mode 100644
index 0000000000..a63c08d3f5
--- /dev/null
+++ b/samplecode/SampleEffects.cpp
@@ -0,0 +1,130 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkView.h"
+
+#include "SkBlurMaskFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "SkDiscretePathEffect.h"
+#include "SkGradientShader.h"
+
+#include "SkEdgeClipper.h"
+
+static void test_edgeclipper() {
+ SkPoint pts[] = {
+ { -8.38822452e+21f, -7.69721471e+19f },
+ { 1.57645875e+23f, 1.44634003e+21f },
+ { 1.61519691e+23f, 1.48208059e+21f },
+ { 3.13963584e+23f, 2.88057438e+21f }
+ };
+ SkRect clip = { 0, 0, 300, 200 };
+
+ SkEdgeClipper clipper;
+ clipper.clipCubic(pts, clip);
+}
+
+///////////
+
+//#define COLOR 0xFFFF8844
+#define COLOR 0xFF888888
+
+static void paint_proc0(SkPaint* paint) {
+}
+
+static void paint_proc1(SkPaint* paint) {
+ paint->setMaskFilter(SkBlurMaskFilter::Create(2,
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+}
+
+static void paint_proc2(SkPaint* paint) {
+ SkScalar dir[3] = { 1, 1, 1};
+ paint->setMaskFilter(
+ SkBlurMaskFilter::CreateEmboss(dir, 0.1f, 0.05f, 1))->unref();
+}
+
+static void paint_proc3(SkPaint* paint) {
+ SkColor colors[] = { SK_ColorRED, COLOR, SK_ColorBLUE };
+ SkPoint pts[] = { { 3, 0 }, { 7, 5 } };
+ paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+ SkShader::kMirror_TileMode))->unref();
+}
+
+static void paint_proc5(SkPaint* paint) {
+ paint_proc3(paint);
+ paint_proc2(paint);
+}
+
+typedef void (*PaintProc)(SkPaint*);
+const PaintProc gPaintProcs[] = {
+ paint_proc0,
+ paint_proc1,
+ paint_proc2,
+ paint_proc3,
+ paint_proc5,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class EffectsView : public SampleView {
+public:
+ SkPath fPath;
+ SkPaint fPaint[SK_ARRAY_COUNT(gPaintProcs)];
+
+ EffectsView() {
+ size_t i;
+ const float pts[] = {
+ 0, 0,
+ 10, 0,
+ 10, 5,
+ 20, -5,
+ 10, -15,
+ 10, -10,
+ 0, -10
+ };
+ fPath.moveTo(pts[0], pts[1]);
+ for (i = 2; i < SK_ARRAY_COUNT(pts); i += 2) {
+ fPath.lineTo(pts[i], pts[i+1]);
+ }
+
+ for (i = 0; i < SK_ARRAY_COUNT(gPaintProcs); i++) {
+ fPaint[i].setAntiAlias(true);
+ fPaint[i].setColor(COLOR);
+ gPaintProcs[i](&fPaint[i]);
+ }
+
+ test_edgeclipper();
+ SkColorMatrix cm;
+ cm.setRotate(SkColorMatrix::kG_Axis, 180);
+ cm.setIdentity();
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Effects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->scale(3, 3);
+ canvas->translate(10, 30);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fPaint); i++) {
+ canvas->drawPath(fPath, fPaint[i]);
+ canvas->translate(32, 0);
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new EffectsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleEmboss.cpp b/samplecode/SampleEmboss.cpp
new file mode 100644
index 0000000000..8b3f194ccd
--- /dev/null
+++ b/samplecode/SampleEmboss.cpp
@@ -0,0 +1,66 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkColorShader.h"
+#include "SkEmbossMaskFilter.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+class EmbossView : public SampleView {
+ SkEmbossMaskFilter::Light fLight;
+public:
+ EmbossView() {
+ fLight.fDirection[0] = SK_Scalar1;
+ fLight.fDirection[1] = SK_Scalar1;
+ fLight.fDirection[2] = SK_Scalar1;
+ fLight.fAmbient = 128;
+ fLight.fSpecular = 16*2;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Emboss");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(10));
+ paint.setMaskFilter(new SkEmbossMaskFilter(fLight, SkIntToScalar(4)))->unref();
+ paint.setShader(new SkColorShader(SK_ColorBLUE))->unref();
+ paint.setDither(true);
+
+ canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+ SkIntToScalar(30), paint);
+ }
+
+private:
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new EmbossView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleEncode.cpp b/samplecode/SampleEncode.cpp
new file mode 100644
index 0000000000..f4ea1950aa
--- /dev/null
+++ b/samplecode/SampleEncode.cpp
@@ -0,0 +1,224 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkStream.h"
+
+static void make_image(SkBitmap* bm, SkBitmap::Config config, int configIndex) {
+ const int width = 98;
+ const int height = 100;
+ SkBitmap device;
+
+ device.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ device.allocPixels();
+
+ SkCanvas canvas(device);
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ canvas.drawColor(SK_ColorRED);
+ paint.setColor(SK_ColorBLUE);
+ canvas.drawCircle(SkIntToScalar(width)/2, SkIntToScalar(height)/2,
+ SkIntToScalar(width)/2, paint);
+
+ bm->setConfig(config, width, height);
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ bm->swap(device);
+ break;
+ case SkBitmap::kRGB_565_Config: {
+ bm->allocPixels();
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ *bm->getAddr16(x, y) = SkPixel32ToPixel16(*device.getAddr32(x, y));
+ }
+ }
+ break;
+ }
+ case SkBitmap::kIndex8_Config: {
+ SkPMColor colors[256];
+ for (int i = 0; i < 256; i++) {
+ if (configIndex & 1) {
+ colors[i] = SkPackARGB32(255-i, 0, 0, 255-i);
+ } else {
+ colors[i] = SkPackARGB32(0xFF, i, 0, 255-i);
+ }
+ }
+ SkColorTable* ctable = new SkColorTable(colors, 256);
+ bm->allocPixels(ctable);
+ ctable->unref();
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ *bm->getAddr8(x, y) = SkGetPackedR32(*device.getAddr32(x, y));
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+// configs to build the original bitmap in. Can be at most these 3
+static const SkBitmap::Config gConfigs[] = {
+ SkBitmap::kARGB_8888_Config,
+ SkBitmap::kRGB_565_Config,
+ SkBitmap::kIndex8_Config, // opaque
+ SkBitmap::kIndex8_Config // alpha
+};
+
+static const char* const gConfigLabels[] = {
+ "8888", "565", "Index8", "Index8 alpha"
+};
+
+// types to encode into. Can be at most these 3. Must match up with gExt[]
+static const SkImageEncoder::Type gTypes[] = {
+ SkImageEncoder::kJPEG_Type,
+ SkImageEncoder::kPNG_Type
+};
+
+// must match up with gTypes[]
+static const char* const gExt[] = {
+ ".jpg", ".png"
+};
+
+static const char* gPath = "/encoded/";
+
+static void make_name(SkString* name, int config, int ext) {
+ name->set(gPath);
+ name->append(gConfigLabels[config]);
+ name->append(gExt[ext]);
+}
+
+#include <sys/stat.h>
+
+class EncodeView : public SampleView {
+public:
+ SkBitmap* fBitmaps;
+ size_t fBitmapCount;
+
+ EncodeView() {
+ #if 1
+ (void)mkdir(gPath, S_IRWXU | S_IRWXG | S_IRWXO);
+
+ fBitmapCount = SK_ARRAY_COUNT(gConfigs);
+ fBitmaps = new SkBitmap[fBitmapCount];
+ for (size_t i = 0; i < fBitmapCount; i++) {
+ make_image(&fBitmaps[i], gConfigs[i], i);
+
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) {
+ SkString path;
+ make_name(&path, i, j);
+
+ // remove any previous run of this file
+ remove(path.c_str());
+
+ SkImageEncoder* codec = SkImageEncoder::Create(gTypes[j]);
+ if (NULL == codec ||
+ !codec->encodeFile(path.c_str(), fBitmaps[i], 100)) {
+ SkDebugf("------ failed to encode %s\n", path.c_str());
+ remove(path.c_str()); // remove any partial file
+ }
+ delete codec;
+ }
+ }
+ #else
+ fBitmaps = NULL;
+ fBitmapCount = 0;
+ #endif
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~EncodeView() {
+ delete[] fBitmaps;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "ImageEncoder");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ if (fBitmapCount == 0) {
+ return;
+ }
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextAlign(SkPaint::kCenter_Align);
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+ SkScalar x = 0, y = 0, maxX = 0;
+ const int SPACER = 10;
+
+ for (size_t i = 0; i < fBitmapCount; i++) {
+ canvas->drawText(gConfigLabels[i], strlen(gConfigLabels[i]),
+ x + SkIntToScalar(fBitmaps[i].width()) / 2, 0,
+ paint);
+ y = paint.getTextSize();
+
+ canvas->drawBitmap(fBitmaps[i], x, y);
+
+ SkScalar yy = y;
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) {
+ yy += SkIntToScalar(fBitmaps[i].height() + 10);
+
+ SkBitmap bm;
+ SkString name;
+
+ make_name(&name, i, j);
+
+ SkImageDecoder::DecodeFile(name.c_str(), &bm);
+ canvas->drawBitmap(bm, x, yy);
+ }
+
+ x += SkIntToScalar(fBitmaps[i].width() + SPACER);
+ if (x > maxX) {
+ maxX = x;
+ }
+ }
+
+ y = (paint.getTextSize() + SkIntToScalar(fBitmaps[0].height())) * 3 / 2;
+ x = maxX + SkIntToScalar(10);
+ paint.setTextAlign(SkPaint::kLeft_Align);
+
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) {
+ canvas->drawText(gExt[j], strlen(gExt[j]), x, y, paint);
+ y += SkIntToScalar(fBitmaps[0].height() + SPACER);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new EncodeView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleExtractAlpha.cpp b/samplecode/SampleExtractAlpha.cpp
new file mode 100644
index 0000000000..860272d892
--- /dev/null
+++ b/samplecode/SampleExtractAlpha.cpp
@@ -0,0 +1,90 @@
+#include "SampleCode.h"
+#include "SkColorPriv.h"
+#include "SkGradientShader.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkUtils.h"
+
+static SkBitmap make_bitmap() {
+ SkBitmap bm;
+ SkColorTable* ctable = new SkColorTable(256);
+
+ SkPMColor* c = ctable->lockColors();
+ for (int i = 0; i < 256; i++) {
+ c[i] = SkPackARGB32(255 - i, 0, 0, 0);
+ }
+ ctable->unlockColors(true);
+ bm.setConfig(SkBitmap::kIndex8_Config, 256, 256);
+ bm.allocPixels(ctable);
+ ctable->unref();
+
+ bm.lockPixels();
+ const float cx = bm.width() * 0.5f;
+ const float cy = bm.height() * 0.5f;
+ for (int y = 0; y < bm.height(); y++) {
+ float dy = y - cy;
+ dy *= dy;
+ uint8_t* p = bm.getAddr8(0, y);
+ for (int x = 0; x < 256; x++) {
+ float dx = x - cx;
+ dx *= dx;
+ float d = (dx + dy) / (cx/2);
+ int id = (int)d;
+ if (id > 255) {
+ id = 255;
+ }
+ p[x] = id;
+ }
+ }
+ bm.unlockPixels();
+ return bm;
+}
+
+class ExtractAlphaView : public SampleView {
+ SkBitmap fBM8;
+ SkBitmap fBM32;
+ SkBitmap fBM4;
+public:
+ ExtractAlphaView() {
+ fBM8 = make_bitmap();
+ fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
+ fBM8.copyTo(&fBM4, SkBitmap::kARGB_4444_Config);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "DitherBitmap");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkMatrix matrix;
+ matrix.setScale(3.55f, 80.f);
+ canvas->setMatrix(matrix);
+
+ paint.setStrokeWidth(0.0588f);
+ canvas->drawLine(10, 5, 30, 4.8f, paint);
+
+ paint.setStrokeWidth(0.06f);
+ canvas->drawLine(20, 5, 40, 4.8f, paint);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ExtractAlphaView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp
new file mode 100644
index 0000000000..393c5f7ba3
--- /dev/null
+++ b/samplecode/SampleFillType.cpp
@@ -0,0 +1,90 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkCornerPathEffect.h"
+#include "SkCullPoints.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+
+class FillTypeView : public SampleView {
+ SkPath fPath;
+public:
+ FillTypeView() {
+ const SkScalar radius = SkIntToScalar(45);
+ fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
+ fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "FillType");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+ SkScalar scale, const SkPaint& paint) {
+
+ const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+ canvas->clipRect(r);
+ canvas->drawColor(SK_ColorWHITE);
+ fPath.setFillType(ft);
+ canvas->translate(r.centerX(), r.centerY());
+ canvas->scale(scale, scale);
+ canvas->translate(-r.centerX(), -r.centerY());
+ canvas->drawPath(fPath, paint);
+ canvas->restore();
+ }
+
+ void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) {
+ showPath(canvas, 0, 0, SkPath::kWinding_FillType,
+ scale, paint);
+ showPath(canvas, 200, 0, SkPath::kEvenOdd_FillType,
+ scale, paint);
+ showPath(canvas, 00, 200, SkPath::kInverseWinding_FillType,
+ scale, paint);
+ showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+ scale, paint);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+ SkPaint paint;
+ const SkScalar scale = SkIntToScalar(5)/4;
+
+ paint.setAntiAlias(false);
+ paint.setColor(0x8000FF00);
+
+ showFour(canvas, SK_Scalar1, paint);
+ canvas->translate(SkIntToScalar(450), 0);
+ showFour(canvas, scale, paint);
+
+ paint.setAntiAlias(true);
+
+ canvas->translate(SkIntToScalar(-450), SkIntToScalar(450));
+ showFour(canvas, SK_Scalar1, paint);
+ canvas->translate(SkIntToScalar(450), 0);
+ showFour(canvas, scale, paint);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new FillTypeView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp
new file mode 100644
index 0000000000..a9089fa344
--- /dev/null
+++ b/samplecode/SampleFilter.cpp
@@ -0,0 +1,138 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDither.h"
+
+static void make_bm(SkBitmap* bm) {
+ const SkColor colors[] = {
+ SK_ColorRED, SK_ColorGREEN,
+ SK_ColorBLUE, SK_ColorWHITE
+ };
+ SkColorTable* ctable = new SkColorTable(colors, 4);
+ bm->setConfig(SkBitmap::kIndex8_Config, 2, 2);
+ bm->allocPixels(ctable);
+ ctable->unref();
+
+ *bm->getAddr8(0, 0) = 0;
+ *bm->getAddr8(1, 0) = 1;
+ *bm->getAddr8(0, 1) = 2;
+ *bm->getAddr8(1, 1) = 3;
+}
+
+static SkScalar draw_bm(SkCanvas* canvas, const SkBitmap& bm,
+ SkScalar x, SkScalar y, SkPaint* paint) {
+#if 1
+ canvas->drawBitmap(bm, x, y, paint);
+ return SkIntToScalar(bm.width()) * 5/4;
+#else
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(x, y);
+
+ SkScalar w = SkIntToScalar(bm.width());
+ SkScalar h = SkIntToScalar(bm.height());
+ SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ paint->setShader(s)->unref();
+ canvas->drawRect(SkRect::MakeWH(w, h), *paint);
+ paint->setShader(NULL);
+ return w * 5/4;
+#endif
+}
+
+static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x, SkPaint* p) {
+ x += draw_bm(c, bm, x, 0, p);
+ p->setFilterBitmap(true);
+ x += draw_bm(c, bm, x, 0, p);
+ p->setDither(true);
+ return x + draw_bm(c, bm, x, 0, p);
+}
+
+static const char* gConfigNames[] = {
+ "unknown config",
+ "A1",
+ "A8",
+ "Index8",
+ "565",
+ "4444",
+ "8888"
+};
+
+static SkScalar draw_row(SkCanvas* canvas, const SkBitmap& bm) {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ SkPaint paint;
+ SkScalar x = 0;
+ const int scale = 32;
+
+ paint.setAntiAlias(true);
+ const char* name = gConfigNames[bm.config()];
+ canvas->drawText(name, strlen(name), x, SkIntToScalar(bm.height())*scale*5/8,
+ paint);
+ canvas->translate(SkIntToScalar(48), 0);
+
+ canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale));
+
+ x += draw_set(canvas, bm, 0, &paint);
+ paint.reset();
+ paint.setAlpha(0x80);
+ draw_set(canvas, bm, x, &paint);
+ return x * scale / 3;
+}
+
+class FilterView : public SampleView {
+public:
+ SkBitmap fBM8, fBM4444, fBM16, fBM32;
+
+ FilterView() {
+ make_bm(&fBM8);
+ fBM8.copyTo(&fBM4444, SkBitmap::kARGB_4444_Config);
+ fBM8.copyTo(&fBM16, SkBitmap::kRGB_565_Config);
+ fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Filter");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(10);
+
+ canvas->translate(x, y);
+ y = draw_row(canvas, fBM8);
+ canvas->translate(0, y);
+ y = draw_row(canvas, fBM4444);
+ canvas->translate(0, y);
+ y = draw_row(canvas, fBM16);
+ canvas->translate(0, y);
+ draw_row(canvas, fBM32);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new FilterView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFilter2.cpp b/samplecode/SampleFilter2.cpp
new file mode 100644
index 0000000000..c1a16a8d9f
--- /dev/null
+++ b/samplecode/SampleFilter2.cpp
@@ -0,0 +1,116 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+
+static const char* gNames[] = {
+ "/skimages/background_01.png"
+};
+
+class Filter2View : public SampleView {
+public:
+ SkBitmap* fBitmaps;
+ int fBitmapCount;
+ int fCurrIndex;
+
+ Filter2View() {
+ fBitmapCount = SK_ARRAY_COUNT(gNames)*2;
+ fBitmaps = new SkBitmap[fBitmapCount];
+
+ for (int i = 0; i < fBitmapCount/2; i++) {
+ SkImageDecoder::DecodeFile(gNames[i], &fBitmaps[i],
+ SkBitmap::kARGB_8888_Config,
+ SkImageDecoder::kDecodePixels_Mode, NULL);
+ }
+ for (int i = fBitmapCount/2; i < fBitmapCount; i++) {
+ SkImageDecoder::DecodeFile(gNames[i-fBitmapCount/2], &fBitmaps[i],
+ SkBitmap::kRGB_565_Config,
+ SkImageDecoder::kDecodePixels_Mode, NULL);
+ }
+ fCurrIndex = 0;
+
+ this->setBGColor(SK_ColorGRAY);
+ }
+
+ virtual ~Filter2View() {
+ delete[] fBitmaps;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SkString str("Filter/Dither ");
+ str.append(gNames[fCurrIndex]);
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(50));
+
+ const SkScalar W = SkIntToScalar(fBitmaps[0].width() + 1);
+ const SkScalar H = SkIntToScalar(fBitmaps[0].height() + 1);
+ SkPaint paint;
+
+ const SkScalar scale = SkFloatToScalar(0.897917f);
+ canvas->scale(SK_Scalar1, scale);
+
+ for (int k = 0; k < 2; k++) {
+ paint.setFilterBitmap(k == 1);
+ for (int j = 0; j < 2; j++) {
+ paint.setDither(j == 1);
+ for (int i = 0; i < fBitmapCount; i++) {
+ SkScalar x = (k * fBitmapCount + j) * W;
+ SkScalar y = i * H;
+ x = SkIntToScalar(SkScalarRound(x));
+ y = SkIntToScalar(SkScalarRound(y));
+ canvas->drawBitmap(fBitmaps[i], x, y, &paint);
+ if (i == 0) {
+ SkPaint p;
+ p.setAntiAlias(true);
+ p.setTextAlign(SkPaint::kCenter_Align);
+ p.setTextSize(SkIntToScalar(18));
+ SkString s("dither=");
+ s.appendS32(paint.isDither());
+ s.append(" filter=");
+ s.appendS32(paint.isFilterBitmap());
+ canvas->drawText(s.c_str(), s.size(), x + W/2,
+ y - p.getTextSize(), p);
+ }
+ if (k+j == 2) {
+ SkPaint p;
+ p.setAntiAlias(true);
+ p.setTextSize(SkIntToScalar(18));
+ SkString s;
+ s.append(" depth=");
+ s.appendS32(fBitmaps[i].config() == SkBitmap::kRGB_565_Config ? 16 : 32);
+ canvas->drawText(s.c_str(), s.size(), x + W + SkIntToScalar(4),
+ y + H/2, p);
+ }
+ }
+ }
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new Filter2View; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFontCache.cpp b/samplecode/SampleFontCache.cpp
new file mode 100644
index 0000000000..0b8187a020
--- /dev/null
+++ b/samplecode/SampleFontCache.cpp
@@ -0,0 +1,135 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+
+#include <pthread.h>
+
+static void call_measure() {
+ SkPaint paint;
+ uint16_t text[32];
+ SkRandom rand;
+
+ paint.setAntiAlias(true);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ for (int j = 0; j < SK_ARRAY_COUNT(text); j++)
+ text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32);
+
+ for (int i = 9; i < 36; i++) {
+ SkPaint::FontMetrics m;
+
+ paint.setTextSize(SkIntToScalar(i));
+ paint.getFontMetrics(&m);
+ paint.measureText(text, sizeof(text));
+ }
+}
+
+static void call_draw(SkCanvas* canvas) {
+ SkPaint paint;
+ uint16_t text[32];
+ SkRandom rand;
+
+ paint.setAntiAlias(true);
+ paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ for (int j = 0; j < SK_ARRAY_COUNT(text); j++)
+ text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32);
+
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(20);
+
+ canvas->drawColor(SK_ColorWHITE);
+ for (int i = 9; i < 36; i++)
+ {
+ SkPaint::FontMetrics m;
+
+ paint.setTextSize(SkIntToScalar(i));
+ paint.getFontMetrics(&m);
+ canvas->drawText(text, sizeof(text), x, y, paint);
+ y += m.fDescent - m.fAscent;
+ }
+}
+
+static bool gDone;
+
+static void* measure_proc(void* context) {
+ while (!gDone) {
+ call_measure();
+ }
+ return NULL;
+}
+
+static void* draw_proc(void* context) {
+ SkBitmap* bm = (SkBitmap*)context;
+ SkCanvas canvas(*bm);
+
+ while (!gDone) {
+ call_draw(&canvas);
+ }
+ return NULL;
+}
+
+class FontCacheView : public SampleView {
+public:
+ enum { N = 4 };
+
+ pthread_t fMThreads[N];
+ pthread_t fDThreads[N];
+ SkBitmap fBitmaps[N];
+
+ FontCacheView() {
+ gDone = false;
+ for (int i = 0; i < N; i++) {
+ int status;
+
+ status = pthread_create(&fMThreads[i], NULL, measure_proc, NULL);
+ SkASSERT(0 == status);
+
+ fBitmaps[i].setConfig(SkBitmap::kRGB_565_Config, 320, 240);
+ fBitmaps[i].allocPixels();
+ status = pthread_create(&fDThreads[i], NULL, draw_proc, &fBitmaps[i]);
+ SkASSERT(0 == status);
+ }
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~FontCacheView() {
+ gDone = true;
+ for (int i = 0; i < N; i++) {
+ void* ret;
+ int status = pthread_join(fMThreads[i], &ret);
+ SkASSERT(0 == status);
+ status = pthread_join(fDThreads[i], &ret);
+ SkASSERT(0 == status);
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "FontCache");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkScalar x = 0;
+ SkScalar y = 0;
+ for (int i = 0; i < N; i++) {
+ canvas->drawBitmap(fBitmaps[i], x, y);
+ x += SkIntToScalar(fBitmaps[i].width());
+ }
+ this->inval(NULL);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new FontCacheView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFontScalerTest.cpp b/samplecode/SampleFontScalerTest.cpp
new file mode 100644
index 0000000000..0b0d349fa8
--- /dev/null
+++ b/samplecode/SampleFontScalerTest.cpp
@@ -0,0 +1,117 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkTypeface.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDither.h"
+
+static const struct {
+ const char* fName;
+ SkTypeface::Style fStyle;
+} gFaces[] = {
+ { NULL, SkTypeface::kNormal },
+ { NULL, SkTypeface::kBold },
+ { "serif", SkTypeface::kNormal },
+ { "serif", SkTypeface::kBold },
+ { "serif", SkTypeface::kItalic },
+ { "serif", SkTypeface::kBoldItalic },
+ { "monospace", SkTypeface::kNormal }
+};
+
+static const int gFaceCount = SK_ARRAY_COUNT(gFaces);
+
+class FontScalerTestView : public SampleView {
+ SkTypeface* fFaces[gFaceCount];
+
+public:
+ FontScalerTestView() {
+ for (int i = 0; i < gFaceCount; i++) {
+ fFaces[i] = SkTypeface::CreateFromName(gFaces[i].fName,
+ gFaces[i].fStyle);
+ }
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~FontScalerTestView() {
+ for (int i = 0; i < gFaceCount; i++) {
+ SkSafeUnref(fFaces[i]);
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "FontScaler Test");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+
+ // test handling of obscene cubic values (currently broken)
+ if (false) {
+ SkPoint pts[4];
+ pts[0].set(1.61061274e+09f, 6291456);
+ pts[1].set(-7.18397061e+15f, -1.53091184e+13f);
+ pts[2].set(-1.30077315e+16f, -2.77196141e+13f);
+ pts[3].set(-1.30077315e+16f, -2.77196162e+13f);
+
+ SkPath path;
+ path.moveTo(pts[0]);
+ path.cubicTo(pts[1], pts[2], pts[3]);
+ canvas->drawPath(path, paint);
+ }
+
+ canvas->translate(200, 20);
+ canvas->rotate(30);
+
+ paint.setAntiAlias(true);
+ paint.setLCDRenderText(true);
+ SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal)));
+
+// const char* text = "abcdefghijklmnopqrstuvwxyz";
+ const char* text = "HnHnHnHnHnHnHnHnH";
+ size_t textLen = strlen(text);
+
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(20);
+
+ {
+ SkPaint p;
+ p.setColor(SK_ColorRED);
+ SkRect r;
+ r.set(0, 0, x, y*20);
+ canvas->drawRect(r, p);
+ }
+
+ int index = 0;
+ for (int ps = 9; ps <= 24; ps++) {
+ textLen = strlen(text);
+ paint.setTextSize(SkIntToScalar(ps));
+ canvas->drawText(text, textLen, x, y, paint);
+ y += paint.getFontMetrics(NULL);
+ index += 1;
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new FontScalerTestView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleFuzz.cpp b/samplecode/SampleFuzz.cpp
new file mode 100644
index 0000000000..5c41886b8b
--- /dev/null
+++ b/samplecode/SampleFuzz.cpp
@@ -0,0 +1,363 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkBlurMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkXfermode.h"
+#include "SkMatrix.h"
+#include "SkColor.h"
+#include "SkRandom.h"
+
+static void set2x3(SkMatrix* m, float a, float b, float c, float d, float e, float f) {
+ m->reset();
+ m->set(0, a);
+ m->set(1, b);
+ m->set(2, c);
+ m->set(3, d);
+ m->set(4, e);
+ m->set(5, f);
+}
+
+static SkRandom gRand;
+static bool return_large;
+static bool return_undef;
+static bool quick;
+static bool scale_large;
+static int scval = 1;
+static float transval = 0;
+
+static int R(float x) {
+ return (int)floor(SkScalarToFloat(gRand.nextUScalar1()) * x);
+}
+
+static float huge() {
+ double d = 1e100;
+ float f = (float)d;
+ return f;
+}
+
+static float make_number() {
+ float v;
+ int sel;
+
+ if (return_large == true && R(3) == 1) sel = R(6); else sel = R(4);
+ if (return_undef == false && sel == 0) sel = 1;
+
+ if (R(2) == 1) v = (float)R(100); else
+
+ switch (sel) {
+ case 0: break;
+ case 1: v = 0; break;
+ case 2: v = 0.000001f; break;
+ case 3: v = 10000; break;
+ case 4: v = 2000000000; break;
+ case 5: v = huge(); break;
+ }
+
+ if (R(4) == 1) v = -v;
+ return v;
+}
+
+static SkColor make_color() {
+ if (R(2) == 1) return 0xFFC0F0A0; else return 0xFF000090;
+}
+
+
+static SkColor make_fill() {
+#if 0
+ int sel;
+
+ if (quick == true) sel = 0; else sel = R(6);
+
+ switch (sel) {
+
+ case 0:
+ case 1:
+ case 2:
+ return make_color();
+ break;
+
+ case 3:
+ var r = ctx.createLinearGradient(make_number(),make_number(),make_number(),make_number());
+ for (i=0;i<4;i++)
+ r.addColorStop(make_number(),make_color());
+ return r;
+ break;
+
+ case 4:
+ var r = ctx.createRadialGradient(make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
+ for (i=0;i<4;i++)
+ r.addColorStop(make_number(),make_color());
+ return r;
+ break;
+
+ case 5:
+ var r = ctx.createPattern(imgObj,"repeat");
+ if (R(6) == 0)
+ r.addColorStop(make_number(),make_color());
+ return r;
+ break;
+ }
+#else
+ return make_color();
+#endif
+}
+
+
+static void do_fuzz(SkCanvas* canvas) {
+ SkPath path;
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ for (int i=0;i<100;i++) {
+ switch (R(33)) {
+
+ case 0:
+ paint.setColor(make_fill());
+ break;
+
+ case 1:
+ paint.setAlpha(gRand.nextU() & 0xFF);
+ break;
+
+ case 2: {
+ SkXfermode::Mode mode;
+ switch (R(3)) {
+ case 0: mode = SkXfermode::kSrc_Mode; break;
+ case 1: mode = SkXfermode::kXor_Mode; break;
+ case 2: mode = SkXfermode::kSrcOver_Mode; break;
+ }
+ paint.setXfermodeMode(mode);
+ }
+ break;
+
+ case 3:
+ switch (R(2)) {
+ case 0: paint.setStrokeCap(SkPaint::kRound_Cap); break;
+ case 1: paint.setStrokeCap(SkPaint::kButt_Cap); break;
+ }
+ break;
+
+ case 4:
+ switch (R(2)) {
+ case 0: paint.setStrokeJoin(SkPaint::kRound_Join); break;
+ case 1: paint.setStrokeJoin(SkPaint::kMiter_Join); break;
+ }
+ break;
+
+ case 5:
+ paint.setStrokeWidth(make_number());
+ break;
+
+ case 6:
+ paint.setStrokeMiter(make_number());
+ break;
+
+ case 7:
+ if (quick == true) break;
+ SkSafeUnref(paint.setMaskFilter(SkBlurMaskFilter::Create(make_number(), SkBlurMaskFilter::kNormal_BlurStyle)));
+ break;
+
+ case 8:
+ if (quick == true) break;
+ //ctx.shadowColor = make_fill();
+ break;
+
+ case 9:
+ if (quick == true) break;
+ //ctx.shadowOffsetX = make_number();
+ //ctx.shadowOffsetY = make_number();
+ break;
+
+ case 10:
+ canvas->restore();
+ break;
+
+ case 11:
+ canvas->rotate(make_number());
+ break;
+
+ case 12:
+ canvas->save();
+ break;
+
+ case 13:
+ canvas->scale(-1,-1);
+ break;
+
+ case 14:
+
+ if (quick == true) break;
+
+ if (transval == 0) {
+ transval = make_number();
+ canvas->translate(transval,0);
+ } else {
+ canvas->translate(-transval,0);
+ transval = 0;
+ }
+
+ break;
+
+ case 15: {
+ SkRect r;
+ r.set(make_number(),make_number(),make_number(),make_number());
+ SkPaint::Style s = paint.getStyle();
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRect(r, paint);
+ paint.setStyle(s);
+ // clearrect
+ } break;
+
+ case 16:
+ if (quick == true) break;
+// ctx.drawImage(imgObj,make_number(),make_number(),make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
+ break;
+
+ case 17: {
+ SkRect r;
+ r.set(make_number(),make_number(),make_number(),make_number());
+ SkPaint::Style s = paint.getStyle();
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRect(r, paint);
+ paint.setStyle(s);
+ } break;
+
+ case 18:
+ path.reset();
+ break;
+
+ case 19:
+ // ctx.clip() is evil.
+ break;
+
+ case 20:
+ path.close();
+ break;
+
+ case 21: {
+ SkPaint::Style s = paint.getStyle();
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawPath(path, paint);
+ paint.setStyle(s);
+ } break;
+
+ case 22: {
+ SkPaint::Style s = paint.getStyle();
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawPath(path, paint);
+ paint.setStyle(s);
+ } break;
+
+ case 23: {
+ SkRect r;
+ r.set(make_number(),make_number(),make_number(),make_number());
+ SkPaint::Style s = paint.getStyle();
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawRect(r, paint);
+ paint.setStyle(s);
+ } break;
+
+ case 24:
+ if (quick == true) break;
+ //ctx.arc(make_number(),make_number(),make_number(),make_number(),make_number(),true);
+ break;
+
+ case 25:
+ if (quick == true) break;
+ //ctx.arcTo(make_number(),make_number(),make_number(),make_number(),make_number());
+ break;
+
+ case 26:
+ if (quick == true) break;
+ //ctx.bezierCurveTo(make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
+ break;
+
+ case 27:
+ path.lineTo(make_number(),make_number());
+ break;
+
+ case 28:
+ path.moveTo(make_number(),make_number());
+ break;
+
+ case 29:
+ if (quick == true) break;
+ path.quadTo(make_number(),make_number(),make_number(),make_number());
+ break;
+
+ case 30: {
+ if (quick == true) break;
+ SkMatrix matrix;
+ set2x3(&matrix, make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
+ canvas->concat(matrix);
+ } break;
+
+ case 31: {
+ if (quick == true) break;
+ SkMatrix matrix;
+ set2x3(&matrix, make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
+ canvas->setMatrix(matrix);
+ } break;
+
+ case 32:
+
+ if (scale_large == true) {
+
+ switch (scval) {
+ case 0: canvas->scale(-1000000000,1);
+ canvas->scale(-1000000000,1);
+ scval = 1; break;
+ case 1: canvas->scale(-.000000001f,1); scval = 2; break;
+ case 2: canvas->scale(-.000000001f,1); scval = 0; break;
+ }
+
+ }
+
+ break;
+
+
+
+ }
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class FuzzView : public SampleView {
+public:
+ FuzzView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Fuzzer");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkIRect r = canvas->getTotalClip().getBounds();
+ do_fuzz(canvas);
+ this->inval(NULL);
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new FuzzView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleGM.cpp b/samplecode/SampleGM.cpp
new file mode 100644
index 0000000000..ec5b22a936
--- /dev/null
+++ b/samplecode/SampleGM.cpp
@@ -0,0 +1,114 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+#include "gm.h"
+
+using namespace skiagm;
+
+// need to explicitly declare this, or we get some weird infinite loop llist
+template GMRegistry* GMRegistry::gHead;
+
+class Iter {
+public:
+ Iter() {
+ fReg = GMRegistry::Head();
+ }
+
+ void reset() {
+ fReg = GMRegistry::Head();
+ }
+
+ GM* next() {
+ if (fReg) {
+ GMRegistry::Factory fact = fReg->factory();
+ fReg = fReg->next();
+ return fact(0);
+ }
+ return NULL;
+ }
+
+ static int Count() {
+ const GMRegistry* reg = GMRegistry::Head();
+ int count = 0;
+ while (reg) {
+ count += 1;
+ reg = reg->next();
+ }
+ return count;
+ }
+
+private:
+ const GMRegistry* fReg;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GMView : public SampleView {
+ Iter fIter;
+ GM* fGM;
+public:
+ GMView() {
+ fGM = fIter.next();
+ this->postNextGM();
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~GMView() {
+ delete fGM;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "GM");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual bool onEvent(const SkEvent& evt) {
+ if (evt.isType("next-gm")) {
+ delete fGM;
+ if (!(fGM = fIter.next())) {
+ fIter.reset();
+ fGM = fIter.next();
+ }
+ this->inval(NULL);
+ this->postNextGM();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ fGM->draw(canvas);
+ }
+
+private:
+ void postNextGM() {
+ (new SkEvent("next-gm"))->post(this->getSinkID(), 1500);
+ }
+
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GMView; }
+static SkViewRegister reg(MyFactory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+using namespace skiagm;
+
+GM::GM() {}
+GM::~GM() {}
+
+void GM::draw(SkCanvas* canvas) {
+ this->onDraw(canvas);
+}
+
+
diff --git a/samplecode/SampleGradients.cpp b/samplecode/SampleGradients.cpp
new file mode 100644
index 0000000000..902b0bd6e0
--- /dev/null
+++ b/samplecode/SampleGradients.cpp
@@ -0,0 +1,176 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+
+static SkShader* setgrad(const SkRect& r, SkColor c0, SkColor c1) {
+ SkColor colors[] = { c0, c1 };
+ SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } };
+ return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode, NULL);
+}
+
+static void test_alphagradients(SkCanvas* canvas) {
+ SkRect r;
+ r.set(SkIntToScalar(10), SkIntToScalar(10),
+ SkIntToScalar(410), SkIntToScalar(30));
+ SkPaint p, p2;
+ p2.setStyle(SkPaint::kStroke_Style);
+
+ p.setShader(setgrad(r, 0xFF00FF00, 0x0000FF00))->unref();
+ canvas->drawRect(r, p);
+ canvas->drawRect(r, p2);
+
+ r.offset(0, r.height() + SkIntToScalar(4));
+ p.setShader(setgrad(r, 0xFF00FF00, 0x00000000))->unref();
+ canvas->drawRect(r, p);
+ canvas->drawRect(r, p2);
+
+ r.offset(0, r.height() + SkIntToScalar(4));
+ p.setShader(setgrad(r, 0xFF00FF00, 0x00FF0000))->unref();
+ canvas->drawRect(r, p);
+ canvas->drawRect(r, p2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GradData {
+ int fCount;
+ const SkColor* fColors;
+ const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+ SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+static const SkScalar gPos0[] = { 0, SK_Scalar1 };
+static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
+static const SkScalar gPos2[] = {
+ 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
+};
+
+static const GradData gGradData[] = {
+ { 2, gColors, NULL },
+ { 2, gColors, gPos0 },
+ { 2, gColors, gPos1 },
+ { 5, gColors, NULL },
+ { 5, gColors, gPos2 }
+};
+
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
+ data.fCount, tm, mapper);
+}
+
+static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+ data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
+ data.fPos, data.fCount, mapper);
+}
+
+static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center0, center1;
+ center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+ SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+ return SkGradientShader::CreateTwoPointRadial(
+ center1, (pts[1].fX - pts[0].fX) / 7,
+ center0, (pts[1].fX - pts[0].fX) / 2,
+ data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* Make2RadialConcentric(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateTwoPointRadial(
+ center, (pts[1].fX - pts[0].fX) / 7,
+ center, (pts[1].fX - pts[0].fX) / 2,
+ data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper);
+static const GradMaker gGradMakers[] = {
+ MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2RadialConcentric
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GradientsView : public SampleView {
+public:
+ GradientsView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Gradients");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPoint pts[2] = {
+ { 0, 0 },
+ { SkIntToScalar(100), SkIntToScalar(100) }
+ };
+ SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+ SkPaint paint;
+ paint.setDither(true);
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
+
+ for (int tm = 0; tm < SkShader::kTileModeCount; ++tm) {
+ canvas->save();
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+ canvas->save();
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
+ SkShader* shader;
+ shader = gGradMakers[j](pts, gGradData[i], (SkShader::TileMode)tm, NULL);
+ paint.setShader(shader)->unref();
+ canvas->drawRect(r, paint);
+ canvas->translate(0, SkIntToScalar(120));
+ }
+ canvas->restore();
+ canvas->translate(SkIntToScalar(120), 0);
+ }
+ canvas->restore();
+ canvas->translate(SK_ARRAY_COUNT(gGradData)*SkIntToScalar(120), 0);
+ }
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(370));
+ // test_alphagradients(canvas);
+ this->inval(NULL);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GradientsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
new file mode 100644
index 0000000000..8368f5be43
--- /dev/null
+++ b/samplecode/SampleHairline.cpp
@@ -0,0 +1,272 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+
+static SkRandom gRand;
+
+static void test_chromium_9005() {
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 800, 600);
+ bm.allocPixels();
+
+ SkCanvas canvas(bm);
+
+ SkPoint pt0 = { SkFloatToScalar(799.33374f), SkFloatToScalar(1.2360189f) };
+ SkPoint pt1 = { SkFloatToScalar(808.49969f), SkFloatToScalar(-7.4338055f) };
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ canvas.drawLine(pt0.fX, pt0.fY, pt1.fX, pt1.fY, paint);
+}
+
+static void generate_pts(SkPoint pts[], int count, int w, int h) {
+ for (int i = 0; i < count; i++) {
+ pts[i].set(gRand.nextUScalar1() * 3 * w - SkIntToScalar(w),
+ gRand.nextUScalar1() * 3 * h - SkIntToScalar(h));
+ }
+}
+
+static bool check_zeros(const SkPMColor pixels[], int count, int skip) {
+ for (int i = 0; i < count; i++) {
+ if (*pixels) {
+ return false;
+ }
+ pixels += skip;
+ }
+ return true;
+}
+
+static bool check_bitmap_margin(const SkBitmap& bm, int margin) {
+ size_t rb = bm.rowBytes();
+ for (int i = 0; i < margin; i++) {
+ if (!check_zeros(bm.getAddr32(0, i), bm.width(), 1)) {
+ return false;
+ }
+ int bottom = bm.height() - i - 1;
+ if (!check_zeros(bm.getAddr32(0, bottom), bm.width(), 1)) {
+ return false;
+ }
+ // left column
+ if (!check_zeros(bm.getAddr32(i, 0), bm.height(), rb >> 2)) {
+ return false;
+ }
+ int right = bm.width() - margin + i;
+ if (!check_zeros(bm.getAddr32(right, 0), bm.height(), rb >> 2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#define WIDTH 620
+#define HEIGHT 460
+#define MARGIN 10
+
+static void line_proc(SkCanvas* canvas, const SkPaint& paint,
+ const SkBitmap& bm) {
+ const int N = 2;
+ SkPoint pts[N];
+ for (int i = 0; i < 400; i++) {
+ generate_pts(pts, N, WIDTH, HEIGHT);
+
+ canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
+ if (!check_bitmap_margin(bm, MARGIN)) {
+ SkDebugf("---- hairline failure (%g %g) (%g %g)\n",
+ pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
+ break;
+ }
+ }
+}
+
+static void poly_proc(SkCanvas* canvas, const SkPaint& paint,
+ const SkBitmap& bm) {
+ const int N = 8;
+ SkPoint pts[N];
+ for (int i = 0; i < 50; i++) {
+ generate_pts(pts, N, WIDTH, HEIGHT);
+
+ SkPath path;
+ path.moveTo(pts[0]);
+ for (int j = 1; j < N; j++) {
+ path.lineTo(pts[j]);
+ }
+ canvas->drawPath(path, paint);
+ }
+}
+
+static SkPoint ave(const SkPoint& a, const SkPoint& b) {
+ SkPoint c = a + b;
+ c.fX = SkScalarHalf(c.fX);
+ c.fY = SkScalarHalf(c.fY);
+ return c;
+}
+
+static void quad_proc(SkCanvas* canvas, const SkPaint& paint,
+ const SkBitmap& bm) {
+ const int N = 30;
+ SkPoint pts[N];
+ for (int i = 0; i < 10; i++) {
+ generate_pts(pts, N, WIDTH, HEIGHT);
+
+ SkPath path;
+ path.moveTo(pts[0]);
+ for (int j = 1; j < N - 2; j++) {
+ path.quadTo(pts[j], ave(pts[j], pts[j+1]));
+ }
+ path.quadTo(pts[N - 2], pts[N - 1]);
+
+ canvas->drawPath(path, paint);
+ }
+}
+
+static void add_cubic(SkPath* path, const SkPoint& mid, const SkPoint& end) {
+ SkPoint start;
+ path->getLastPt(&start);
+ path->cubicTo(ave(start, mid), ave(mid, end), end);
+}
+
+static void cube_proc(SkCanvas* canvas, const SkPaint& paint,
+ const SkBitmap& bm) {
+ const int N = 30;
+ SkPoint pts[N];
+ for (int i = 0; i < 10; i++) {
+ generate_pts(pts, N, WIDTH, HEIGHT);
+
+ SkPath path;
+ path.moveTo(pts[0]);
+ for (int j = 1; j < N - 2; j++) {
+ add_cubic(&path, pts[j], ave(pts[j], pts[j+1]));
+ }
+ add_cubic(&path, pts[N - 2], pts[N - 1]);
+
+ canvas->drawPath(path, paint);
+ }
+}
+
+typedef void (*HairProc)(SkCanvas*, const SkPaint&, const SkBitmap&);
+
+static const struct {
+ const char* fName;
+ HairProc fProc;
+} gProcs[] = {
+ { "line", line_proc },
+ { "poly", poly_proc },
+ { "quad", quad_proc },
+ { "cube", cube_proc },
+};
+
+static int cycle_hairproc_index(int index) {
+ return (index + 1) % SK_ARRAY_COUNT(gProcs);
+}
+
+class HairlineView : public SampleView {
+ SkMSec fNow;
+ int fProcIndex;
+ bool fDoAA;
+public:
+ HairlineView() {
+ fCounter = 0;
+ fProcIndex = 0;
+ fDoAA = true;
+ fNow = 0;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SkString str;
+ str.printf("Hair-%s", gProcs[fProcIndex].fName);
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void show_bitmaps(SkCanvas* canvas, const SkBitmap& b0, const SkBitmap& b1,
+ const SkIRect& inset) {
+ canvas->drawBitmap(b0, 0, 0, NULL);
+ canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, NULL);
+ }
+
+ int fCounter;
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ gRand.setSeed(fNow);
+
+ if (false) {
+ test_chromium_9005();
+ }
+
+ SkBitmap bm, bm2;
+ bm.setConfig(SkBitmap::kARGB_8888_Config,
+ WIDTH + MARGIN*2,
+ HEIGHT + MARGIN*2);
+ bm.allocPixels();
+ // this will erase our margin, which we want to always stay 0
+ bm.eraseColor(0);
+
+ bm2.setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT,
+ bm.rowBytes());
+ bm2.setPixels(bm.getAddr32(MARGIN, MARGIN));
+
+ SkCanvas c2(bm2);
+ SkPaint paint;
+ paint.setAntiAlias(fDoAA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ bm2.eraseColor(0);
+ gProcs[fProcIndex].fProc(&c2, paint, bm);
+ canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), NULL);
+
+ SkMSec now = SampleCode::GetAnimTime();
+ if (fNow != now) {
+ fNow = now;
+ fCounter += 1;
+ fDoAA = !fDoAA;
+ if (fCounter > 50) {
+ fProcIndex = cycle_hairproc_index(fProcIndex);
+ // todo: signal that we want to rebuild our TITLE
+ fCounter = 0;
+ }
+ this->inval(NULL);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fDoAA = !fDoAA;
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new HairlineView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleImage.cpp b/samplecode/SampleImage.cpp
new file mode 100644
index 0000000000..29442991a7
--- /dev/null
+++ b/samplecode/SampleImage.cpp
@@ -0,0 +1,156 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef_GlobalPool.h"
+#include "SkStream.h"
+
+static const char* gNames[] = {
+ "1.bmp", "1.gif", "1.jpg", "1.png",
+ "2.bmp", "2.gif", "2.jpg", "2.png"
+};
+
+static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
+ SkBitmap::Config pref, const char name[] = NULL)
+{
+ if (SkImageDecoder::DecodeStream(stream, bitmap, pref,
+ SkImageDecoder::kDecodeBounds_Mode, NULL)) {
+ SkASSERT(bitmap->config() != SkBitmap::kNo_Config);
+
+ SkImageRef* ref = new SkImageRef_GlobalPool(stream, bitmap->config());
+ ref->setURI(name);
+ bitmap->setPixelRef(ref)->unref();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+class ImageView : public SkView {
+public:
+ SkBitmap* fBitmaps;
+ SkShader* fShader;
+
+ ImageView() {
+ SkImageRef_GlobalPool::SetRAMBudget(32 * 1024);
+
+ int i, N = SK_ARRAY_COUNT(gNames);
+ fBitmaps = new SkBitmap[N];
+
+ for (i = 0; i < N; i++) {
+ SkString str("/skimages/");
+ str.append(gNames[i]);
+ SkFILEStream* stream = new SkFILEStream(str.c_str());
+
+ SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config, gNames[i]);
+ if (i & 1)
+ fBitmaps[i].buildMipMap();
+ stream->unref();
+ }
+
+ fShader = SkShader::CreateBitmapShader(fBitmaps[5],
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+
+ if (true) {
+ SkMatrix m;
+
+ m.setRotate(SkIntToScalar(30));
+ fShader->setLocalMatrix(m);
+ }
+
+#if 0
+ SkImageRef::DumpPool();
+ for (i = 0; i < N; i++) {
+ SkBitmap& bm = fBitmaps[i];
+
+ SkDebugf("<%s> addr=%p", gNames[i], bm.getPixels());
+ bool success = bm.lockPixels();
+ SkDebugf(" addr=%d", bm.getPixels());
+ if (success)
+ bm.unlockPixels();
+ SkDebugf(" addr=%p", bm.getPixels());
+ success = bm.lockPixels();
+ SkDebugf(" addr=%d", bm.getPixels());
+ if (success)
+ bm.unlockPixels();
+ SkDebugf("\n");
+ }
+ SkImageRef::DumpPool();
+#endif
+ }
+
+ virtual ~ImageView() {
+ delete[] fBitmaps;
+ delete fShader;
+
+ SkImageRef_GlobalPool::DumpPool();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Image");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+// canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+ SkScalar x = 0, y = 0;
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) {
+ canvas->drawBitmap(fBitmaps[i], x, y);
+ x += SkIntToScalar(fBitmaps[i].width() + 10);
+ }
+
+ canvas->translate(0, SkIntToScalar(120));
+
+ SkPaint paint;
+ paint.setShader(fShader);
+ paint.setFilterBitmap(true);
+ SkRect r = { 0, 0, SkIntToScalar(300), SkIntToScalar(100) };
+
+ canvas->drawRect(r, paint);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ImageView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleImageDir.cpp b/samplecode/SampleImageDir.cpp
new file mode 100644
index 0000000000..8ef59ad77e
--- /dev/null
+++ b/samplecode/SampleImageDir.cpp
@@ -0,0 +1,310 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkComposeShader.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkImageRef_GlobalPool.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+#include "SkBlurDrawLooper.h"
+#include "SkColorMatrixFilter.h"
+
+static void drawmarshmallow(SkCanvas* canvas) {
+ SkBitmap bitmap;
+ SkPaint paint;
+ SkRect r;
+ SkMatrix m;
+
+ SkImageDecoder::DecodeFile("/Users/reed/Downloads/3elfs.jpg", &bitmap);
+ SkShader* s = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ paint.setShader(s)->unref();
+ m.setTranslate(SkIntToScalar(250), SkIntToScalar(134));
+ s->setLocalMatrix(m);
+
+ r.set(SkIntToScalar(250),
+ SkIntToScalar(134),
+ SkIntToScalar(250 + 449),
+ SkIntToScalar(134 + 701));
+ paint.setFlags(2);
+
+ canvas->drawRect(r, paint);
+}
+
+static void DrawRoundRect(SkCanvas& canvas) {
+ bool ret = false;
+ SkPaint paint;
+ SkBitmap bitmap;
+ SkMatrix matrix;
+ matrix.reset();
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
+ bitmap.allocPixels();
+#if 0
+ SkCanvas canvas;
+ canvas.setBitmapDevice(bitmap);
+#endif
+
+ // set up clipper
+ SkRect skclip;
+ skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708));
+
+// ret = canvas.clipRect(skclip);
+// SkASSERT(ret);
+
+ matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28));
+ matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50));
+
+ matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171));
+ matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043));
+
+ matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968));
+ matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876));
+
+ matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0));
+ matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0));
+
+ ret = canvas.concat(matrix);
+
+ paint.setAntiAlias(true);
+ paint.setColor(0xb2202020);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkFloatToFixed(68.13));
+
+ SkRect r;
+ r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541));
+ canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint);
+}
+
+static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
+ SkBitmap::Config pref, const char name[] = NULL) {
+#if 0
+ // test buffer streams
+ SkStream* str = new SkBufferStream(stream, 717);
+ stream->unref();
+ stream = str;
+#endif
+
+ SkImageRef* ref = new SkImageRef_GlobalPool(stream, pref, 1);
+ ref->setURI(name);
+ if (!ref->getInfo(bitmap)) {
+ delete ref;
+ return false;
+ }
+ bitmap->setPixelRef(ref)->unref();
+ return true;
+}
+
+//#define SPECIFIC_IMAGE "/skimages/72.jpg"
+#define SPECIFIC_IMAGE "/Users/reed/Downloads/3elfs.jpg"
+
+#define IMAGE_DIR "/skimages/"
+#define IMAGE_SUFFIX ".gif"
+
+class ImageDirView : public SkView {
+public:
+ SkBitmap* fBitmaps;
+ SkString* fStrings;
+ int fBitmapCount;
+ int fCurrIndex;
+ SkScalar fSaturation;
+ SkScalar fAngle;
+
+ ImageDirView() {
+ SkImageRef_GlobalPool::SetRAMBudget(320 * 1024);
+
+#ifdef SPECIFIC_IMAGE
+ fBitmaps = new SkBitmap[3];
+ fStrings = new SkString[3];
+ fBitmapCount = 3;
+ const SkBitmap::Config configs[] = {
+ SkBitmap::kARGB_8888_Config,
+ SkBitmap::kRGB_565_Config,
+ SkBitmap::kARGB_4444_Config
+ };
+ for (int i = 0; i < fBitmapCount; i++) {
+#if 1
+ SkStream* stream = new SkFILEStream(SPECIFIC_IMAGE);
+ SetImageRef(&fBitmaps[i], stream, configs[i], SPECIFIC_IMAGE);
+ stream->unref();
+#else
+ SkImageDecoder::DecodeFile(SPECIFIC_IMAGE, &fBitmaps[i]);
+#endif
+ }
+#else
+ int i, N = 0;
+ SkOSFile::Iter iter(IMAGE_DIR, IMAGE_SUFFIX);
+ SkString name;
+ while (iter.next(&name)) {
+ N += 1;
+ }
+ fBitmaps = new SkBitmap[N];
+ fStrings = new SkString[N];
+ iter.reset(IMAGE_DIR, IMAGE_SUFFIX);
+ for (i = 0; i < N; i++) {
+ iter.next(&name);
+ SkString path(IMAGE_DIR);
+ path.append(name);
+ SkStream* stream = new SkFILEStream(path.c_str());
+
+ SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config,
+ name.c_str());
+ stream->unref();
+ fStrings[i] = name;
+ }
+ fBitmapCount = N;
+#endif
+ fCurrIndex = 0;
+ fDX = fDY = 0;
+
+ fSaturation = SK_Scalar1;
+ fAngle = 0;
+
+ fScale = SK_Scalar1;
+ }
+
+ virtual ~ImageDirView() {
+ delete[] fBitmaps;
+ delete[] fStrings;
+
+ SkImageRef_GlobalPool::DumpPool();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SkString str("ImageDir: ");
+#ifdef SPECIFIC_IMAGE
+ str.append(SPECIFIC_IMAGE);
+#else
+ str.append(IMAGE_DIR);
+#endif
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+// canvas->drawColor(0xFFDDDDDD);
+ canvas->drawColor(SK_ColorGRAY);
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ SkScalar fScale;
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ if (true) {
+ canvas->scale(SkIntToScalar(2), SkIntToScalar(2));
+ drawmarshmallow(canvas);
+ return;
+ }
+
+ if (false) {
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SkIntToScalar(4));
+ canvas->drawCircle(SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(50), p);
+ p.setAntiAlias(true);
+ canvas->drawCircle(SkIntToScalar(300), SkIntToScalar(100), SkIntToScalar(50), p);
+ }
+ if (false) {
+ SkScalar cx = this->width()/2;
+ SkScalar cy = this->height()/2;
+ canvas->translate(cx, cy);
+ canvas->scale(fScale, fScale);
+ canvas->translate(-cx, -cy);
+ DrawRoundRect(*canvas);
+ return;
+ }
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+ SkScalar x = SkIntToScalar(32), y = SkIntToScalar(32);
+ SkPaint paint;
+
+#if 0
+ for (int i = 0; i < fBitmapCount; i++) {
+ SkPaint p;
+
+#if 1
+ const SkScalar cm[] = {
+ SkIntToScalar(2), 0, 0, 0, SkIntToScalar(-255),
+ 0, SkIntToScalar(2), 0, 0, SkIntToScalar(-255),
+ 0, 0, SkIntToScalar(2), 0, SkIntToScalar(-255),
+ 0, 0, 0, SkIntToScalar(1), 0
+ };
+ SkColorFilter* cf = new SkColorMatrixFilter(cm);
+ p.setColorFilter(cf)->unref();
+#endif
+
+ canvas->drawBitmap(fBitmaps[i], x, y, &p);
+ x += SkIntToScalar(fBitmaps[i].width() + 10);
+ }
+ return;
+#endif
+
+ canvas->drawBitmap(fBitmaps[fCurrIndex], x, y, &paint);
+#ifndef SPECIFIC_IMAGE
+ if (true) {
+ fCurrIndex += 1;
+ if (fCurrIndex >= fBitmapCount) {
+ fCurrIndex = 0;
+ }
+ this->inval(NULL);
+ }
+#endif
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ if (true) {
+ fCurrIndex += 1;
+ if (fCurrIndex >= fBitmapCount)
+ fCurrIndex = 0;
+ this->inval(NULL);
+ }
+ return new Click(this);
+ }
+
+ virtual bool onClick(Click* click) {
+ SkScalar center = this->width()/2;
+ fSaturation = SkScalarDiv(click->fCurr.fX - center, center/2);
+ center = this->height()/2;
+ fAngle = SkScalarDiv(click->fCurr.fY - center, center) * 180;
+
+ fDX += click->fCurr.fX - click->fPrev.fX;
+ fDY += click->fCurr.fY - click->fPrev.fY;
+
+ fScale = SkScalarDiv(click->fCurr.fX, this->width());
+
+ this->inval(NULL);
+ return true;
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ SkScalar fDX, fDY;
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ImageDirView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLCD.cpp b/samplecode/SampleLCD.cpp
new file mode 100644
index 0000000000..098958f4e0
--- /dev/null
+++ b/samplecode/SampleLCD.cpp
@@ -0,0 +1,61 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+class LCDView : public SkView {
+public:
+ LCDView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "LCD Text");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ SkScalar textSize = SkIntToScalar(6);
+ SkScalar delta = SK_Scalar1;
+ const char* text = "HHHamburgefonts iii";
+ size_t len = strlen(text);
+ SkScalar x0 = SkIntToScalar(10);
+ SkScalar x1 = SkIntToScalar(310);
+ SkScalar y = SkIntToScalar(20);
+
+ for (int i = 0; i < 20; i++) {
+ paint.setTextSize(textSize);
+ textSize += delta;
+
+ paint.setLCDRenderText(false);
+ canvas->drawText(text, len, x0, y, paint);
+ paint.setLCDRenderText(true);
+ canvas->drawText(text, len, x1, y, paint);
+
+ y += paint.getFontSpacing();
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LCDView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLayerMask.cpp b/samplecode/SampleLayerMask.cpp
new file mode 100644
index 0000000000..9bd00ae5af
--- /dev/null
+++ b/samplecode/SampleLayerMask.cpp
@@ -0,0 +1,68 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkView.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class LayerMaskView : public SampleView {
+public:
+ LayerMaskView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "LayerMask");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawMask(SkCanvas* canvas, const SkRect& r) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ if (true) {
+ SkBitmap mask;
+ int w = SkScalarRound(r.width());
+ int h = SkScalarRound(r.height());
+ mask.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ mask.allocPixels();
+ mask.eraseColor(0);
+ SkCanvas c(mask);
+ SkRect bounds = r;
+ bounds.offset(-bounds.fLeft, -bounds.fTop);
+ c.drawOval(bounds, paint);
+
+ paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+ canvas->drawBitmap(mask, r.fLeft, r.fTop, &paint);
+ } else {
+ SkPath p;
+ p.addOval(r);
+ p.setFillType(SkPath::kInverseWinding_FillType);
+ paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
+ canvas->drawPath(p, paint);
+ }
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkRect r;
+ r.set(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(120), SkIntToScalar(120));
+ canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas->drawColor(SK_ColorRED);
+ drawMask(canvas, r);
+ canvas->restore();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LayerMaskView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp
new file mode 100644
index 0000000000..6fc9c83492
--- /dev/null
+++ b/samplecode/SampleLayers.cpp
@@ -0,0 +1,271 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCamera.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkGradientShader.h"
+#include "SkImageDecoder.h"
+#include "SkInterpolator.h"
+#include "SkMaskFilter.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+#include "SkKey.h"
+#include "SkXfermode.h"
+#include "SkDrawFilter.h"
+
+static void make_paint(SkPaint* paint) {
+ SkColor colors[] = { 0, SK_ColorWHITE };
+ SkPoint pts[] = { { 0, 0 }, { 0, SK_Scalar1*20 } };
+ SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+ paint->setShader(s)->unref();
+ paint->setXfermodeMode(SkXfermode::kDstIn_Mode);
+}
+
+static void dump_layers(const char label[], SkCanvas* canvas) {
+ SkDebugf("Dump Layers(%s)\n", label);
+
+ SkCanvas::LayerIter iter(canvas, true);
+ int index = 0;
+ while (!iter.done()) {
+ const SkBitmap& bm = iter.device()->accessBitmap(false);
+ const SkIRect& clip = iter.clip().getBounds();
+ SkDebugf("Layer[%d] bitmap [%d %d] X=%d Y=%d clip=[%d %d %d %d] alpha=%d\n", index++,
+ bm.width(), bm.height(), iter.x(), iter.y(),
+ clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
+ iter.paint().getAlpha());
+ iter.next();
+ }
+}
+
+// test drawing with strips of fading gradient above and below
+static void test_fade(SkCanvas* canvas) {
+ SkAutoCanvasRestore ar(canvas, true);
+
+ SkRect r;
+
+ SkPaint p;
+ p.setAlpha(0x88);
+
+ SkAutoCanvasRestore(canvas, false);
+
+ // create the layers
+
+ r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+ canvas->clipRect(r);
+
+ r.fBottom = SkIntToScalar(20);
+ canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+
+ r.fTop = SkIntToScalar(80);
+ r.fBottom = SkIntToScalar(100);
+ canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+
+ // now draw the "content"
+
+ if (true) {
+ r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+
+ canvas->saveLayerAlpha(&r, 0x80);
+
+ SkPaint p;
+ p.setColor(SK_ColorRED);
+ p.setAntiAlias(true);
+ canvas->drawOval(r, p);
+
+ dump_layers("inside layer alpha", canvas);
+
+ canvas->restore();
+ } else {
+ r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
+
+ SkPaint p;
+ p.setColor(SK_ColorRED);
+ p.setAntiAlias(true);
+ canvas->drawOval(r, p);
+ }
+
+// return;
+
+ dump_layers("outside layer alpha", canvas);
+
+ // now apply an effect
+
+ SkPaint paint;
+ make_paint(&paint);
+ r.set(0, 0, SkIntToScalar(100), SkIntToScalar(20));
+// SkDebugf("--------- draw top grad\n");
+ canvas->drawRect(r, paint);
+
+ SkMatrix m;
+ SkShader* s = paint.getShader();
+ m.setScale(SK_Scalar1, -SK_Scalar1);
+ m.postTranslate(0, SkIntToScalar(100));
+ s->setLocalMatrix(m);
+
+ r.fTop = SkIntToScalar(80);
+ r.fBottom = SkIntToScalar(100);
+// SkDebugf("--------- draw bot grad\n");
+ canvas->drawRect(r, paint);
+}
+
+class RedFilter : public SkDrawFilter {
+public:
+ virtual bool filter(SkCanvas*, SkPaint* p, SkDrawFilter::Type) {
+ fColor = p->getColor();
+ if (fColor == SK_ColorRED) {
+ p->setColor(SK_ColorGREEN);
+ }
+ return true;
+ }
+ virtual void restore(SkCanvas*, SkPaint* p, SkDrawFilter::Type) {
+ p->setColor(fColor);
+ }
+
+private:
+ SkColor fColor;
+};
+
+class LayersView : public SkView {
+public:
+ LayersView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Layers");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorGRAY);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ if (true) {
+ SkRect r;
+ r.set(SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(220), SkIntToScalar(120));
+ SkPaint p;
+ canvas->saveLayer(&r, &p);
+ canvas->drawColor(0xFFFF0000);
+ p.setAlpha(0); // or 0
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ canvas->drawOval(r, p);
+ canvas->restore();
+ return;
+ }
+
+ if (false) {
+ SkRect r;
+ r.set(SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(220), SkIntToScalar(120));
+ SkPaint p;
+ p.setAlpha(0x88);
+ p.setAntiAlias(true);
+
+ if (true) {
+ canvas->saveLayer(&r, &p);
+ p.setColor(0xFFFF0000);
+ canvas->drawOval(r, p);
+ canvas->restore();
+ }
+
+ p.setColor(0xFF0000FF);
+ r.offset(SkIntToScalar(20), SkIntToScalar(50));
+ canvas->drawOval(r, p);
+ }
+
+ if (false) {
+ SkPaint p;
+ p.setAlpha(0x88);
+ p.setAntiAlias(true);
+
+ canvas->translate(SkIntToScalar(300), 0);
+
+ SkRect r;
+ r.set(SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(220), SkIntToScalar(60));
+
+ canvas->saveLayer(&r, &p, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
+// canvas->clipRect(r, SkRegion::kDifference_Op);
+// canvas->clipRect(r, SkRegion::kIntersect_Op);
+
+ r.set(SkIntToScalar(0), SkIntToScalar(0),
+ SkIntToScalar(220), SkIntToScalar(120));
+ p.setColor(SK_ColorBLUE);
+ canvas->drawOval(r, p);
+ canvas->restore();
+ return;
+ }
+
+ //canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+ test_fade(canvas);
+ return;
+
+ // canvas->setDrawFilter(new RedFilter)->unref();
+
+ SkRect r;
+ SkPaint p;
+
+ canvas->translate(SkIntToScalar(220), SkIntToScalar(20));
+
+ p.setAntiAlias(true);
+ r.set(SkIntToScalar(20), SkIntToScalar(20),
+ SkIntToScalar(220), SkIntToScalar(120));
+
+ p.setColor(SK_ColorBLUE);
+ // p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(8), SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ canvas->drawRect(r, p);
+ p.setMaskFilter(NULL);
+
+ SkRect bounds = r;
+ bounds.fBottom = bounds.centerY();
+ canvas->saveLayer(&bounds, NULL, SkCanvas::kARGB_NoClipLayer_SaveFlag);
+
+ p.setColor(SK_ColorRED);
+ canvas->drawOval(r, p);
+
+ p.setAlpha(0x80);
+ p.setXfermodeMode(SkXfermode::kDstIn_Mode);
+ canvas->drawRect(bounds, p);
+
+ canvas->restore();
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+ virtual bool handleKey(SkKey key) {
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LayersView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLineClipper.cpp b/samplecode/SampleLineClipper.cpp
new file mode 100644
index 0000000000..ac6b013ac8
--- /dev/null
+++ b/samplecode/SampleLineClipper.cpp
@@ -0,0 +1,255 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkRandom.h"
+
+#include "SkLineClipper.h"
+#include "SkEdgeClipper.h"
+
+#define AUTO_ANIMATE true
+
+static int test0(SkPoint pts[], SkRect* clip) {
+ pts[0].set(200000, 140);
+ pts[1].set(-740000, 483);
+ pts[2].set(1.00000102e-06f, 9.10000017e-05f);
+ clip->set(0, 0, 640, 480);
+ return 2;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawQuad(SkCanvas* canvas, const SkPoint pts[3], const SkPaint& p) {
+ SkPath path;
+ path.moveTo(pts[0]);
+ path.quadTo(pts[1], pts[2]);
+ canvas->drawPath(path, p);
+}
+
+static void drawCubic(SkCanvas* canvas, const SkPoint pts[4], const SkPaint& p) {
+ SkPath path;
+ path.moveTo(pts[0]);
+ path.cubicTo(pts[1], pts[2], pts[3]);
+ canvas->drawPath(path, p);
+}
+
+typedef void (*clipper_proc)(const SkPoint src[], const SkRect& clip,
+ SkCanvas*, const SkPaint&, const SkPaint&);
+
+static void check_clipper(int count, const SkPoint pts[], const SkRect& clip) {
+ for (int i = 0; i < count; i++) {
+ SkASSERT(pts[i].fX >= clip.fLeft);
+ SkASSERT(pts[i].fX <= clip.fRight);
+ SkASSERT(pts[i].fY >= clip.fTop);
+ SkASSERT(pts[i].fY <= clip.fBottom);
+ }
+
+ if (count > 1) {
+ sk_assert_monotonic_y(pts, count);
+ }
+}
+
+static void line_intersector(const SkPoint src[], const SkRect& clip,
+ SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+ canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1);
+
+ SkPoint dst[2];
+ if (SkLineClipper::IntersectLine(src, clip, dst)) {
+ check_clipper(2, dst, clip);
+ canvas->drawPoints(SkCanvas::kLines_PointMode, 2, dst, p0);
+ }
+}
+
+static void line_clipper(const SkPoint src[], const SkRect& clip,
+ SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+ canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1);
+
+ SkPoint dst[SkLineClipper::kMaxPoints];
+ int count = SkLineClipper::ClipLine(src, clip, dst);
+ for (int i = 0; i < count; i++) {
+ check_clipper(2, &dst[i], clip);
+ canvas->drawPoints(SkCanvas::kLines_PointMode, 2, &dst[i], p0);
+ }
+}
+
+static void quad_clipper(const SkPoint src[], const SkRect& clip,
+ SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+ drawQuad(canvas, src, p1);
+
+ SkEdgeClipper clipper;
+ if (clipper.clipQuad(src, clip)) {
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ check_clipper(2, pts, clip);
+ canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0);
+ break;
+ case SkPath::kQuad_Verb:
+ check_clipper(3, pts, clip);
+ drawQuad(canvas, pts, p0);
+ break;
+ default:
+ SkASSERT(!"unexpected verb");
+ }
+ }
+ }
+}
+
+static void cubic_clipper(const SkPoint src[], const SkRect& clip,
+ SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
+ drawCubic(canvas, src, p1);
+
+ SkEdgeClipper clipper;
+ if (clipper.clipCubic(src, clip)) {
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ check_clipper(2, pts, clip);
+ canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0);
+ break;
+ case SkPath::kCubic_Verb:
+ // check_clipper(4, pts, clip);
+ drawCubic(canvas, pts, p0);
+ break;
+ default:
+ SkASSERT(!"unexpected verb");
+ }
+ }
+ }
+}
+
+static const clipper_proc gProcs[] = {
+ line_intersector,
+ line_clipper,
+ quad_clipper,
+ cubic_clipper
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum {
+ W = 640/3,
+ H = 480/3
+};
+
+class LineClipperView : public SkView {
+ SkMSec fNow;
+ int fCounter;
+ int fProcIndex;
+ SkRect fClip;
+ SkRandom fRand;
+ SkPoint fPts[4];
+
+ void randPts() {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
+ fPts[i].set(fRand.nextUScalar1() * 640,
+ fRand.nextUScalar1() * 480);
+ }
+ fCounter += 1;
+ }
+
+public:
+ LineClipperView() {
+ fProcIndex = 0;
+ fCounter = 0;
+ fNow = 0;
+
+ int x = (640 - W)/2;
+ int y = (480 - H)/2;
+ fClip.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + W), SkIntToScalar(y + H));
+ this->randPts();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "LineClipper");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ static void drawVLine(SkCanvas* canvas, SkScalar x, const SkPaint& paint) {
+ canvas->drawLine(x, -999, x, 999, paint);
+ }
+
+ static void drawHLine(SkCanvas* canvas, SkScalar y, const SkPaint& paint) {
+ canvas->drawLine(-999, y, 999, y, paint);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ SkMSec now = SampleCode::GetAnimTime();
+ if (fNow != now) {
+ fNow = now;
+ this->randPts();
+ this->inval(NULL);
+ }
+
+ // fProcIndex = test0(fPts, &fClip);
+
+ SkPaint paint, paint1;
+
+ drawVLine(canvas, fClip.fLeft + SK_ScalarHalf, paint);
+ drawVLine(canvas, fClip.fRight - SK_ScalarHalf, paint);
+ drawHLine(canvas, fClip.fTop + SK_ScalarHalf, paint);
+ drawHLine(canvas, fClip.fBottom - SK_ScalarHalf, paint);
+
+ paint.setColor(SK_ColorLTGRAY);
+ canvas->drawRect(fClip, paint);
+
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorBLUE);
+ paint.setStyle(SkPaint::kStroke_Style);
+ // paint.setStrokeWidth(SkIntToScalar(3));
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ paint1.setAntiAlias(true);
+ paint1.setColor(SK_ColorRED);
+ paint1.setStyle(SkPaint::kStroke_Style);
+ gProcs[fProcIndex](fPts, fClip, canvas, paint, paint1);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ // fProcIndex = (fProcIndex + 1) % SK_ARRAY_COUNT(gProcs);
+ if (x < 50 && y < 50) {
+ this->randPts();
+ }
+ this->inval(NULL);
+ return NULL;
+ }
+
+ virtual bool onClick(Click* click) {
+ return false;
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LineClipperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp
new file mode 100644
index 0000000000..03dd30feed
--- /dev/null
+++ b/samplecode/SampleLines.cpp
@@ -0,0 +1,109 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+
+class LinesView : public SampleView {
+public:
+ LinesView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Lines");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ /*
+ 0x1F * x + 0x1F * (32 - x)
+ */
+ void drawRings(SkCanvas* canvas) {
+ canvas->scale(SkIntToScalar(1)/2, SkIntToScalar(1)/2);
+
+ SkRect r;
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(10);
+ r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
+
+ SkPaint paint;
+ // paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+ paint.setColor(0xFFFF8800);
+ // paint.setColor(0xFFFFFFFF);
+ canvas->drawRect(r, paint);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkBitmap bm;
+ SkImageDecoder::DecodeFile("/kill.gif", &bm);
+ canvas->drawBitmap(bm, 0, 0, NULL);
+
+ this->drawRings(canvas);
+ return;
+
+ SkPaint paint;
+
+ // fAlpha = 0x80;
+ paint.setColor(SK_ColorWHITE);
+ paint.setAlpha(fAlpha & 0xFF);
+ SkRect r;
+
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(10);
+ r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
+ canvas->drawRect(r, paint);
+ return;
+
+ paint.setColor(0xffffff00); // yellow
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(2));
+
+// y += SK_Scalar1/2;
+
+ canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint);
+
+ paint.setAntiAlias(true); // with anti-aliasing
+ y += SkIntToScalar(10);
+ canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fAlpha = SkScalarRound(y);
+ this->inval(NULL);
+ return NULL;
+ }
+private:
+
+ int fAlpha;
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new LinesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleMeasure.cpp b/samplecode/SampleMeasure.cpp
new file mode 100644
index 0000000000..8078e03df1
--- /dev/null
+++ b/samplecode/SampleMeasure.cpp
@@ -0,0 +1,115 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDither.h"
+
+// exercise scale/linear/devkern
+struct Setting {
+ SkScalar fScale;
+ bool fLinearText;
+ bool fDevKernText;
+};
+
+static const SkScalar ONE = SkIntToScalar(9999)/10000;
+
+static const Setting gSettings[] = {
+ { 0, false, false },
+ { 0, false, true },
+ { 0, true, false },
+ { 0, true, true },
+ { ONE, false, false },
+ { ONE, false, true },
+ { ONE, true, false },
+ { ONE, true, true }
+};
+
+static void doMeasure(SkCanvas* canvas, const SkPaint& paint, const char text[]) {
+ SkScalar dy = paint.getFontMetrics(NULL);
+
+ size_t len = strlen(text);
+ SkAutoTMalloc<SkScalar> autoWidths(len);
+ SkScalar* widths = autoWidths.get();
+ SkAutoTMalloc<SkRect> autoRects(len);
+ SkRect* rects = autoRects.get();
+ SkRect bounds;
+
+ SkPaint p(paint);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gSettings); i++) {
+ p.setLinearText(gSettings[i].fLinearText);
+ p.setDevKernText(gSettings[i].fDevKernText);
+ SkScalar scale = gSettings[i].fScale;
+
+ int n = p.getTextWidths(text, len, widths, rects);
+ SkScalar w = p.measureText(text, len, &bounds, scale);
+
+ p.setStyle(SkPaint::kFill_Style);
+ p.setColor(0x8888FF88);
+ canvas->drawRect(bounds, p);
+ p.setColor(0xFF000000);
+ canvas->drawText(text, len, 0, 0, p);
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(0);
+ p.setColor(0xFFFF0000);
+ SkScalar x = 0;
+ for (int j = 0; j < n; j++) {
+ SkRect r = rects[j];
+ r.offset(x, 0);
+ canvas->drawRect(r, p);
+ x += widths[j];
+ }
+
+ p.setColor(0xFF0000FF);
+ canvas->drawLine(0, 0, w, 0, p);
+ p.setStrokeWidth(SkIntToScalar(4));
+ canvas->drawPoint(x, 0, p);
+
+ canvas->translate(0, dy);
+ }
+}
+
+class MeasureView : public SampleView {
+public:
+ SkPaint fPaint;
+
+ MeasureView() {
+ fPaint.setAntiAlias(true);
+ fPaint.setTextSize(SkIntToScalar(64));
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Measure");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(fPaint.getTextSize(), fPaint.getTextSize());
+ doMeasure(canvas, fPaint, "Hamburgefons");
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new MeasureView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleMipMap.cpp b/samplecode/SampleMipMap.cpp
new file mode 100644
index 0000000000..3d9515691e
--- /dev/null
+++ b/samplecode/SampleMipMap.cpp
@@ -0,0 +1,143 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+static SkBitmap createBitmap(int n) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+
+ SkCanvas canvas(bitmap);
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ paint.setColor(SK_ColorRED);
+ canvas.drawOval(r, paint);
+ paint.setColor(SK_ColorBLUE);
+ paint.setStrokeWidth(SkIntToScalar(n)/15);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas.drawLine(0, 0, r.fRight, r.fBottom, paint);
+ canvas.drawLine(0, r.fBottom, r.fRight, 0, paint);
+
+ return bitmap;
+}
+
+class MipMapView : public SampleView {
+ SkBitmap fBitmap;
+ enum {
+ N = 64
+ };
+public:
+ MipMapView() {
+ fBitmap = createBitmap(N);
+
+ fWidth = N;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "MipMaps");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawN(SkCanvas* canvas, const SkBitmap& bitmap) {
+ SkAutoCanvasRestore acr(canvas, true);
+ for (int i = N; i > 1; i >>= 1) {
+ canvas->drawBitmap(bitmap, 0, 0, NULL);
+ canvas->translate(SkIntToScalar(N + 8), 0);
+ canvas->scale(SK_ScalarHalf, SK_ScalarHalf);
+ }
+ }
+
+ void drawN2(SkCanvas* canvas, const SkBitmap& bitmap) {
+ SkBitmap bg;
+ bg.setConfig(SkBitmap::kARGB_8888_Config, N, N);
+ bg.allocPixels();
+
+ SkAutoCanvasRestore acr(canvas, true);
+ for (int i = 0; i < 6; i++) {
+ bg.eraseColor(0);
+ SkCanvas c(bg);
+ c.scale(SK_Scalar1 / (1 << i), SK_Scalar1 / (1 << i));
+ c.drawBitmap(bitmap, 0, 0, NULL);
+
+ canvas->save();
+ canvas->scale(SkIntToScalar(1 << i), SkIntToScalar(1 << i));
+ canvas->drawBitmap(bg, 0, 0, NULL);
+ canvas->restore();
+ canvas->translate(SkIntToScalar(N + 8), 0);
+ }
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+ canvas->scale(1.00000001f, 0.9999999f);
+
+ drawN2(canvas, fBitmap);
+
+ canvas->translate(0, SkIntToScalar(N + 8));
+ SkBitmap bitmap(fBitmap);
+ bitmap.buildMipMap();
+ drawN2(canvas, bitmap);
+
+ SkScalar time = SampleCode::GetAnimScalar(SkIntToScalar(1)/4,
+ SkIntToScalar(2));
+ if (time >= SK_Scalar1) {
+ time = SkIntToScalar(2) - time;
+ }
+ fWidth = 8 + SkScalarRound(N * time);
+
+ SkRect dst;
+ dst.set(0, 0, SkIntToScalar(fWidth), SkIntToScalar(fWidth));
+
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+ paint.setAntiAlias(true);
+
+ canvas->translate(0, SkIntToScalar(N + 8));
+ canvas->drawBitmapRect(fBitmap, NULL, dst, NULL);
+ canvas->translate(SkIntToScalar(N + 8), 0);
+ canvas->drawBitmapRect(fBitmap, NULL, dst, &paint);
+ canvas->translate(-SkIntToScalar(N + 8), SkIntToScalar(N + 8));
+ canvas->drawBitmapRect(bitmap, NULL, dst, NULL);
+ canvas->translate(SkIntToScalar(N + 8), 0);
+ canvas->drawBitmapRect(bitmap, NULL, dst, &paint);
+
+ SkShader* s = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ paint.setShader(s)->unref();
+ SkMatrix m;
+ m.setScale(SkIntToScalar(fWidth) / N,
+ SkIntToScalar(fWidth) / N);
+ s->setLocalMatrix(m);
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(4*N), SkIntToScalar(5*N/2));
+ r.offset(SkIntToScalar(N + 12), -SkIntToScalar(N + 4));
+ canvas->drawRect(r, paint);
+
+ this->inval(NULL);
+ }
+
+private:
+ int fWidth;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new MipMapView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleMovie.cpp b/samplecode/SampleMovie.cpp
new file mode 100644
index 0000000000..af34198191
--- /dev/null
+++ b/samplecode/SampleMovie.cpp
@@ -0,0 +1,61 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkMovie.h"
+#include "SkTime.h"
+#include <new>
+
+class AnimGifView : public SkView {
+ SkMovie* fMovie;
+public:
+ AnimGifView() {
+ fMovie = SkMovie::DecodeFile("/skimages/dollarblk.gif");
+ }
+
+ virtual ~AnimGifView() {
+ SkSafeUnref(fMovie);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Animated Gif");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xFFDDDDDD);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ if (fMovie) {
+ if (fMovie->duration()) {
+ fMovie->setTime(SkTime::GetMSecs() % fMovie->duration());
+ } else {
+ fMovie->setTime(0);
+ }
+ canvas->drawBitmap(fMovie->bitmap(), SkIntToScalar(20),
+ SkIntToScalar(20));
+ this->inval(NULL);
+ }
+ }
+
+private:
+ SkRect fClip;
+ SkIPoint* fPoints;
+ SkPath fPath;
+ int fPtCount;
+
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AnimGifView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleNinePatch.cpp b/samplecode/SampleNinePatch.cpp
new file mode 100644
index 0000000000..e158287523
--- /dev/null
+++ b/samplecode/SampleNinePatch.cpp
@@ -0,0 +1,114 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkNinePatch.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+class NinePatchView : public SampleView {
+public:
+ SkBitmap fBM;
+
+ NinePatchView() {
+ SkImageDecoder::DecodeFile("/skimages/btn_default_normal_disable.9.png", &fBM);
+
+ // trim off the edge guide-lines
+ SkBitmap tmp;
+ SkIRect r;
+ r.set(1, 1, fBM.width() - 1, fBM.height() - 1);
+ fBM.extractSubset(&tmp, r);
+ fBM.swap(tmp);
+
+ fX = SkIntToScalar(fBM.width());
+ fY = 0;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "NinePatch");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawBackground(SkCanvas* canvas) {
+ SkPaint p;
+ p.setDither(true);
+ p.setColor(0xFF909090);
+ canvas->drawPaint(p);
+ }
+
+ static void test_rects(SkCanvas* canvas, const SkBitmap& bm, const SkPaint* paint) {
+ static const SkIRect src[] = {
+ { 0, 0, 18, 34 },
+ { 18, 0, 19, 34 },
+ { 19, 0, 36, 34 },
+ { 0, 34, 18, 35 },
+ { 18, 34, 19, 35 },
+ { 19, 34, 36, 35 },
+ { 0, 35, 18, 72 },
+ { 18, 35, 19, 72 },
+ { 19, 35, 36, 72 },
+ };
+ static const SkRect dst[] = {
+ { 0, 0, 18, 34 },
+ { 18, 0, 283, 34 },
+ { 283, 0, 300, 34 },
+ { 0, 34, 18, 163 },
+ { 18, 34, 283, 163 },
+ { 283, 34, 300, 163 },
+ { 0, 163, 18, 200 },
+ { 18, 163, 283, 200 },
+ { 283, 163, 300, 200 },
+ };
+ for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
+ canvas->drawBitmapRect(bm, &src[i], dst[i], paint);
+ }
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->drawBitmap(fBM, 0, 0);
+
+ SkIRect margins;
+ SkRect dst;
+ int d = 25;
+
+ margins.set(d, d, d, d);
+ margins.fLeft = fBM.width()/2 - 1;
+ margins.fTop = fBM.height()/2 - 1;
+ margins.fRight = fBM.width() - margins.fLeft - 1;
+ margins.fBottom = fBM.height() - margins.fTop - 1;
+
+ // canvas->translate(fX/5, fY/5);
+ canvas->translate(0, 76);
+
+ dst.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
+
+ SkPaint paint;
+ paint.setAntiAlias(false);
+ paint.setDither(true);
+ paint.setFilterBitmap(false);
+ // SkNinePatch::DrawNine(canvas, dst, fBM, margins, &paint);
+ test_rects(canvas, fBM, &paint);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fX = x / 1.5f;
+ fY = y / 1.5f;
+ fX = x; fY = y;
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+private:
+ SkScalar fX, fY;
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new NinePatchView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleOvalTest.cpp b/samplecode/SampleOvalTest.cpp
new file mode 100644
index 0000000000..f62552946a
--- /dev/null
+++ b/samplecode/SampleOvalTest.cpp
@@ -0,0 +1,110 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+static const int kILimit = 101;
+static const SkScalar kLimit = SK_Scalar1 * kILimit;
+
+class OvalTestView : public SampleView {
+public:
+ SkSize fSize;
+ SkPMColor fInsideColor; // signals an interior pixel that was not set
+ SkPMColor fOutsideColor; // signals an exterior pixels that was set
+ SkBitmap fBitmap;
+
+ OvalTestView() {
+ fSize.set(SK_Scalar1, SK_Scalar1);
+
+ fBitmap.setConfig(SkBitmap::kARGB_8888_Config, kILimit, kILimit);
+ fBitmap.allocPixels();
+
+ fInsideColor = SkPreMultiplyColor(SK_ColorRED);
+ fOutsideColor = SkPreMultiplyColor(SK_ColorGREEN);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "OvalTest");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawOval() {
+ SkCanvas canvas(fBitmap);
+ SkPaint p;
+
+ fBitmap.eraseColor(0);
+ canvas.drawOval(SkRect::MakeSize(fSize), p);
+ }
+
+ int checkOval(int* flatCount, int* buldgeCount) {
+ int flatc = 0;
+ int buldgec = 0;
+ const SkScalar rad = SkScalarHalf(fSize.width());
+ SkScalar cx = SkScalarHalf(fSize.width());
+ SkScalar cy = SkScalarHalf(fSize.height());
+ for (int y = 0; y < kILimit; y++) {
+ for (int x = 0; x < kILimit; x++) {
+ // measure from pixel centers
+ SkScalar px = SkIntToScalar(x) + SK_ScalarHalf;
+ SkScalar py = SkIntToScalar(y) + SK_ScalarHalf;
+
+ SkPMColor* ptr = fBitmap.getAddr32(x, y);
+ SkScalar dist = SkPoint::Length(px - cx, py - cy);
+ if (dist <= rad && !*ptr) {
+ flatc++;
+ *ptr = fInsideColor;
+ } else if (dist > rad && *ptr) {
+ buldgec++;
+ *ptr = fOutsideColor;
+ }
+ }
+ }
+ if (flatCount) *flatCount = flatc;
+ if (buldgeCount) *buldgeCount = buldgec;
+ return flatc + buldgec;
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ this->drawOval();
+ int flatCount, buldgeCount;
+ this->checkOval(&flatCount, &buldgeCount);
+ this->inval(NULL);
+
+ canvas->drawBitmap(fBitmap, SkIntToScalar(20), SkIntToScalar(20), NULL);
+
+
+ static int gFlatCount;
+ static int gBuldgeCount;
+ gFlatCount += flatCount;
+ gBuldgeCount += buldgeCount;
+
+ if (fSize.fWidth < kLimit) {
+ SkDebugf("--- width=%g, flat=%d buldge=%d total: flat=%d buldge=%d\n", fSize.fWidth,
+ flatCount, buldgeCount, gFlatCount, gBuldgeCount);
+ fSize.fWidth += SK_Scalar1;
+ fSize.fHeight += SK_Scalar1;
+ } else {
+ // fSize.set(SK_Scalar1, SK_Scalar1);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+ return NULL;
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new OvalTestView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleOverflow.cpp b/samplecode/SampleOverflow.cpp
new file mode 100644
index 0000000000..d3ecff751b
--- /dev/null
+++ b/samplecode/SampleOverflow.cpp
@@ -0,0 +1,100 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+static void DrawRoundRect() {
+#ifdef SK_SCALAR_IS_FIXED
+ bool ret = false;
+ SkPaint paint;
+ SkBitmap bitmap;
+ SkCanvas canvas;
+ SkMatrix matrix;
+ matrix.reset();
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
+ bitmap.allocPixels();
+ canvas.setBitmapDevice(bitmap);
+
+ // set up clipper
+ SkRect skclip;
+ skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708));
+
+ ret = canvas.clipRect(skclip);
+ SkASSERT(ret);
+
+ matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28));
+ matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50));
+
+ matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171));
+ matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043));
+
+ matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968));
+ matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876));
+
+ matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0));
+ matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0));
+
+ ret = canvas.concat(matrix);
+
+ paint.setAntiAlias(true);
+ paint.setColor(0xb2202020);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkFloatToFixed(68.13));
+
+ SkRect r;
+ r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541));
+ canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint);
+#endif
+}
+
+static bool HitTestPath(const SkPath& path, SkScalar x, SkScalar y) {
+ SkRegion rgn, clip;
+
+ int ix = SkScalarFloor(x);
+ int iy = SkScalarFloor(y);
+
+ clip.setRect(ix, iy, ix + 1, iy + 1);
+
+ bool contains = rgn.setPath(path, clip);
+ return contains;
+}
+
+static void TestOverflowHitTest() {
+ SkPath path;
+
+#ifdef SK_SCALAR_IS_FLOATx
+ path.addCircle(0, 0, 70000, SkPath::kCCW_Direction);
+ SkASSERT(HitTestPath(path, 40000, 40000));
+#endif
+}
+
+class OverflowView : public SampleView {
+public:
+ OverflowView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Circles");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ DrawRoundRect();
+ TestOverflowHitTest();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new OverflowView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePageFlip.cpp b/samplecode/SamplePageFlip.cpp
new file mode 100644
index 0000000000..7c5bf480e8
--- /dev/null
+++ b/samplecode/SamplePageFlip.cpp
@@ -0,0 +1,167 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkFlipPixelRef.h"
+#include "SkPageFlipper.h"
+
+#include <pthread.h>
+
+#define WIDTH 200
+#define HEIGHT 200
+
+static bool gDone;
+
+static void bounce(SkScalar* x, SkScalar* dx, const int max) {
+ *x += *dx;
+ if (*x < 0) {
+ *x = 0;
+ if (*dx < 0) {
+ *dx = -*dx;
+ }
+ } else if (*x > SkIntToScalar(max)) {
+ *x = SkIntToScalar(max);
+ if (*dx > 0) {
+ *dx = -*dx;
+ }
+ }
+}
+
+static void* draw_proc(void* context) {
+ const int OVALW = 32;
+ const int OVALH = 32;
+
+ const SkBitmap* bm = static_cast<const SkBitmap*>(context);
+ SkFlipPixelRef* ref = static_cast<SkFlipPixelRef*>(bm->pixelRef());
+
+ const int DSCALE = 1;
+ SkScalar dx = SkIntToScalar(7) / DSCALE;
+ SkScalar dy = SkIntToScalar(5) / DSCALE;
+ SkScalar x = 0;
+ SkScalar y = 0;
+
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorRED);
+
+ SkRect oval;
+ oval.setEmpty();
+
+ while (!gDone) {
+ ref->inval(oval, true);
+ oval.set(x, y, x + SkIntToScalar(OVALW), y + SkIntToScalar(OVALH));
+ ref->inval(oval, true);
+
+ SkAutoFlipUpdate update(ref);
+
+ if (!update.dirty().isEmpty()) {
+ // this must be local to the loop, since it needs to forget the pixels
+ // its writing to after each iteration, since we do the swap
+ SkCanvas canvas(update.bitmap());
+
+// SkDebugf("----- dirty [%d %d %d %d]\n", dirty.getBounds().fLeft, dirty.getBounds().fTop, dirty.getBounds().width(), dirty.getBounds().height());
+ canvas.clipRegion(update.dirty());
+
+ canvas.drawColor(0, SkXfermode::kClear_Mode);
+ canvas.drawOval(oval, paint);
+ }
+ bounce(&x, &dx, WIDTH-OVALW);
+ bounce(&y, &dy, HEIGHT-OVALH);
+
+#if 1
+ for (int i = 0; i < 1000; i++) {
+ for (int j = 0; j < 10000; j++) {
+ SkFixedMul(j, 10);
+ }
+ }
+#endif
+ }
+ return NULL;
+}
+
+static const SkBitmap::Config gConfigs[] = {
+ SkBitmap::kARGB_8888_Config,
+#if 1
+ SkBitmap::kRGB_565_Config,
+ SkBitmap::kARGB_4444_Config,
+ SkBitmap::kA8_Config
+#endif
+};
+
+class PageFlipView : public SampleView {
+public:
+
+ enum { N = SK_ARRAY_COUNT(gConfigs) };
+
+ pthread_t fThreads[N];
+ SkBitmap fBitmaps[N];
+
+ PageFlipView() {
+ gDone = false;
+ for (int i = 0; i < N; i++) {
+ int status;
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ SkASSERT(0 == status);
+
+ fBitmaps[i].setConfig(gConfigs[i], WIDTH, HEIGHT);
+ SkFlipPixelRef* pr = new SkFlipPixelRef(gConfigs[i], WIDTH, HEIGHT);
+ fBitmaps[i].setPixelRef(pr)->unref();
+ fBitmaps[i].eraseColor(0);
+
+ status = pthread_create(&fThreads[i], &attr, draw_proc, &fBitmaps[i]);
+ SkASSERT(0 == status);
+ }
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~PageFlipView() {
+ gDone = true;
+ for (int i = 0; i < N; i++) {
+ void* ret;
+ int status = pthread_join(fThreads[i], &ret);
+ SkASSERT(0 == status);
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "PageFlip");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(10);
+ for (int i = 0; i < N; i++) {
+ canvas->drawBitmap(fBitmaps[i], x, y);
+ x += SkIntToScalar(fBitmaps[i].width() + 20);
+ }
+ this->inval(NULL);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PageFlipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp
new file mode 100644
index 0000000000..ea365c719d
--- /dev/null
+++ b/samplecode/SamplePatch.cpp
@@ -0,0 +1,342 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+#include "SkGeometry.h" // private include :(
+
+static void drawtriangle(SkCanvas* canvas, const SkPaint& paint,
+ const SkPoint pts[3]) {
+ SkPath path;
+
+ path.moveTo(pts[0]);
+ path.lineTo(pts[1]);
+ path.lineTo(pts[2]);
+
+ canvas->drawPath(path, paint);
+}
+
+static SkShader* make_shader0(SkIPoint* size) {
+ SkBitmap bm;
+
+// SkImageDecoder::DecodeFile("/skimages/progressivejpg.jpg", &bm);
+ SkImageDecoder::DecodeFile("/skimages/logo.png", &bm);
+ size->set(bm.width(), bm.height());
+ return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+}
+
+static SkShader* make_shader1(const SkIPoint& size) {
+ SkPoint pts[] = { { 0, 0, },
+ { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
+ SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
+ return SkGradientShader::CreateLinear(pts, colors, NULL,
+ SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Patch {
+public:
+ Patch() { sk_bzero(fPts, sizeof(fPts)); }
+ ~Patch() {}
+
+ void setPatch(const SkPoint pts[12]) {
+ memcpy(fPts, pts, 12 * sizeof(SkPoint));
+ fPts[12] = pts[0]; // the last shall be first
+ }
+ void setBounds(int w, int h) { fW = w; fH = h; }
+
+ void draw(SkCanvas*, const SkPaint&, int segsU, int segsV,
+ bool doTextures, bool doColors);
+
+private:
+ SkPoint fPts[13];
+ int fW, fH;
+};
+
+static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) {
+ SkScalar t = 0;
+ SkScalar dt = SK_Scalar1 / segs;
+
+ samples[0] = cubic[0];
+ for (int i = 1; i < segs; i++) {
+ t += dt;
+ SkEvalCubicAt(cubic, t, &samples[i], NULL, NULL);
+ }
+}
+
+static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv,
+ SkPoint* pt) {
+ const int TL = 0;
+ const int TR = nu;
+ const int BR = TR + nv;
+ const int BL = BR + nu;
+
+ SkScalar u = SkIntToScalar(iu) / nu;
+ SkScalar v = SkIntToScalar(iv) / nv;
+
+ SkScalar uv = SkScalarMul(u, v);
+ SkScalar Uv = SkScalarMul(SK_Scalar1 - u, v);
+ SkScalar uV = SkScalarMul(u, SK_Scalar1 - v);
+ SkScalar UV = SkScalarMul(SK_Scalar1 - u, SK_Scalar1 - v);
+
+ SkScalar x0 = SkScalarMul(UV, edge[TL].fX) + SkScalarMul(uV, edge[TR].fX) +
+ SkScalarMul(Uv, edge[BL].fX) + SkScalarMul(uv, edge[BR].fX);
+ SkScalar y0 = SkScalarMul(UV, edge[TL].fY) + SkScalarMul(uV, edge[TR].fY) +
+ SkScalarMul(Uv, edge[BL].fY) + SkScalarMul(uv, edge[BR].fY);
+
+ SkScalar x = SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fX) +
+ SkScalarMul(u, edge[TR+iv].fX) +
+ SkScalarMul(v, edge[BR+nu-iu].fX) +
+ SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fX) - x0;
+ SkScalar y = SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fY) +
+ SkScalarMul(u, edge[TR+iv].fY) +
+ SkScalarMul(v, edge[BR+nu-iu].fY) +
+ SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fY) - y0;
+ pt->set(x, y);
+}
+
+static int ScalarTo255(SkScalar v) {
+ int scale = SkScalarToFixed(v) >> 8;
+ if (scale < 0) {
+ scale = 0;
+ } else if (scale > 255) {
+ scale = 255;
+ }
+ return scale;
+}
+
+static SkColor make_color(SkScalar s, SkScalar t) {
+ int cs = ScalarTo255(s);
+ int ct = ScalarTo255(t);
+ return SkColorSetARGB(0xFF, cs, 0, 0) + SkColorSetARGB(0, 0, ct, 0);
+}
+
+void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
+ bool doTextures, bool doColors) {
+ if (nu < 1 || nv < 1) {
+ return;
+ }
+
+ int i, npts = (nu + nv) * 2;
+ SkAutoSTMalloc<16, SkPoint> storage(npts + 1);
+ SkPoint* edge0 = storage.get();
+ SkPoint* edge1 = edge0 + nu;
+ SkPoint* edge2 = edge1 + nv;
+ SkPoint* edge3 = edge2 + nu;
+
+ // evaluate the edge points
+ eval_patch_edge(fPts + 0, edge0, nu);
+ eval_patch_edge(fPts + 3, edge1, nv);
+ eval_patch_edge(fPts + 6, edge2, nu);
+ eval_patch_edge(fPts + 9, edge3, nv);
+ edge3[nv] = edge0[0]; // the last shall be first
+
+ for (i = 0; i < npts; i++) {
+// canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint);
+ }
+
+ int row, vertCount = (nu + 1) * (nv + 1);
+ SkAutoTMalloc<SkPoint> vertStorage(vertCount);
+ SkPoint* verts = vertStorage.get();
+
+ // first row
+ memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint));
+ // rows
+ SkPoint* r = verts;
+ for (row = 1; row < nv; row++) {
+ r += nu + 1;
+ r[0] = edge3[nv - row];
+ for (int col = 1; col < nu; col++) {
+ eval_sheet(edge0, nu, nv, col, row, &r[col]);
+ }
+ r[nu] = edge1[row];
+ }
+ // last row
+ SkPoint* last = verts + nv * (nu + 1);
+ for (i = 0; i <= nu; i++) {
+ last[i] = edge2[nu - i];
+ }
+
+// canvas->drawPoints(verts, vertCount, paint);
+
+ int stripCount = (nu + 1) * 2;
+ SkAutoTMalloc<SkPoint> stripStorage(stripCount * 2);
+ SkAutoTMalloc<SkColor> colorStorage(stripCount);
+ SkPoint* strip = stripStorage.get();
+ SkPoint* tex = strip + stripCount;
+ SkColor* colors = colorStorage.get();
+ SkScalar t = 0;
+ const SkScalar ds = SK_Scalar1 * fW / nu;
+ const SkScalar dt = SK_Scalar1 * fH / nv;
+ r = verts;
+ for (row = 0; row < nv; row++) {
+ SkPoint* upper = r;
+ SkPoint* lower = r + nu + 1;
+ r = lower;
+ SkScalar s = 0;
+ for (i = 0; i <= nu; i++) {
+ strip[i*2 + 0] = *upper++;
+ strip[i*2 + 1] = *lower++;
+ tex[i*2 + 0].set(s, t);
+ tex[i*2 + 1].set(s, t + dt);
+ colors[i*2 + 0] = make_color(s/fW, t/fH);
+ colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH);
+ s += ds;
+ }
+ t += dt;
+ canvas->drawVertices(SkCanvas::kTriangleStrip_VertexMode, stripCount,
+ strip, doTextures ? tex : NULL,
+ doColors ? colors : NULL, NULL,
+ NULL, 0, paint);
+ }
+}
+
+static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
+ Patch* patch) {
+
+ SkAutoCanvasRestore ar(canvas, true);
+
+ patch->draw(canvas, paint, 10, 10, false, false);
+ canvas->translate(SkIntToScalar(180), 0);
+ patch->draw(canvas, paint, 10, 10, true, false);
+ canvas->translate(SkIntToScalar(180), 0);
+ patch->draw(canvas, paint, 10, 10, false, true);
+ canvas->translate(SkIntToScalar(180), 0);
+ patch->draw(canvas, paint, 10, 10, true, true);
+}
+
+class PatchView : public SampleView {
+ SkShader* fShader0;
+ SkShader* fShader1;
+ SkIPoint fSize0, fSize1;
+ SkPoint fPts[12];
+
+public:
+ PatchView() {
+ fShader0 = make_shader0(&fSize0);
+ fSize1 = fSize0;
+ if (fSize0.fX == 0 || fSize0.fY == 0) {
+ fSize1.set(2, 2);
+ }
+ fShader1 = make_shader1(fSize1);
+
+ const SkScalar S = SkIntToScalar(50);
+ const SkScalar T = SkIntToScalar(40);
+ fPts[0].set(S*0, T);
+ fPts[1].set(S*1, T);
+ fPts[2].set(S*2, T);
+ fPts[3].set(S*3, T);
+ fPts[4].set(S*3, T*2);
+ fPts[5].set(S*3, T*3);
+ fPts[6].set(S*3, T*4);
+ fPts[7].set(S*2, T*4);
+ fPts[8].set(S*1, T*4);
+ fPts[9].set(S*0, T*4);
+ fPts[10].set(S*0, T*3);
+ fPts[11].set(S*0, T*2);
+
+ this->setBGColor(SK_ColorGRAY);
+ }
+
+ virtual ~PatchView() {
+ SkSafeUnref(fShader0);
+ SkSafeUnref(fShader1);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt))
+ {
+ SkString str("Patch");
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setDither(true);
+ paint.setFilterBitmap(true);
+
+ canvas->translate(SkIntToScalar(20), 0);
+
+ Patch patch;
+
+ paint.setShader(fShader0);
+ if (fSize0.fX == 0) {
+ fSize0.fX = 1;
+ }
+ if (fSize0.fY == 0) {
+ fSize0.fY = 1;
+ }
+ patch.setBounds(fSize0.fX, fSize0.fY);
+
+ patch.setPatch(fPts);
+ drawpatches(canvas, paint, 10, 10, &patch);
+
+ paint.setShader(NULL);
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(SkIntToScalar(5));
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint);
+
+ canvas->translate(0, SkIntToScalar(300));
+
+ paint.setAntiAlias(false);
+ paint.setShader(fShader1);
+ patch.setBounds(fSize1.fX, fSize1.fY);
+ drawpatches(canvas, paint, 10, 10, &patch);
+ }
+
+ class PtClick : public Click {
+ public:
+ int fIndex;
+ PtClick(SkView* view, int index) : Click(view), fIndex(index) {}
+ };
+
+ static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
+ return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
+ if (hittest(fPts[i], x, y)) {
+ return new PtClick(this, i);
+ }
+ }
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX, click->fCurr.fY);
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PatchView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp
new file mode 100644
index 0000000000..cd45ed95e0
--- /dev/null
+++ b/samplecode/SamplePath.cpp
@@ -0,0 +1,200 @@
+
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkParsePath.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkGeometry.h"
+
+// http://code.google.com/p/skia/issues/detail?id=32
+static void test_cubic() {
+ SkPoint src[4] = {
+ { 556.25000f, 523.03003f },
+ { 556.23999f, 522.96002f },
+ { 556.21997f, 522.89001f },
+ { 556.21997f, 522.82001f }
+ };
+ SkPoint dst[11];
+ dst[10].set(42, -42); // one past the end, that we don't clobber these
+ SkScalar tval[] = { 0.33333334f, 0.99999994f };
+
+ SkChopCubicAt(src, dst, tval, 2);
+
+#if 0
+ for (int i = 0; i < 11; i++) {
+ SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY);
+ }
+#endif
+}
+
+static void test_cubic2() {
+ const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z";
+ SkPath path;
+ SkParsePath::FromSVGString(str, &path);
+
+ {
+#ifdef SK_BUILD_FOR_WIN
+ // windows doesn't have strtof
+ float x = (float)strtod("9.94099e+07", NULL);
+#else
+ float x = strtof("9.94099e+07", NULL);
+#endif
+ int ix = (int)x;
+ int fx = (int)(x * 65536);
+ int ffx = SkScalarToFixed(x);
+ printf("%g %x %x %x\n", x, ix, fx, ffx);
+
+ SkRect r = path.getBounds();
+ SkIRect ir;
+ r.round(&ir);
+ printf("[%g %g %g %g] [%x %x %x %x]\n",
+ r.fLeft, r.fTop, r.fRight, r.fBottom,
+ ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
+ }
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
+ bitmap.allocPixels();
+
+ SkCanvas canvas(bitmap);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ canvas.drawPath(path, paint);
+}
+
+class PathView : public SampleView {
+public:
+ int fDStroke, fStroke, fMinStroke, fMaxStroke;
+ SkPath fPath[6];
+ bool fShowHairline;
+
+ PathView() {
+ test_cubic();
+ test_cubic2();
+
+ fShowHairline = false;
+
+ fDStroke = 1;
+ fStroke = 10;
+ fMinStroke = 10;
+ fMaxStroke = 180;
+
+ const int V = 85;
+
+ fPath[0].moveTo(SkIntToScalar(40), SkIntToScalar(70));
+ fPath[0].lineTo(SkIntToScalar(70), SkIntToScalar(70) + SK_Scalar1/1);
+ fPath[0].lineTo(SkIntToScalar(110), SkIntToScalar(70));
+
+ fPath[1].moveTo(SkIntToScalar(40), SkIntToScalar(70));
+ fPath[1].lineTo(SkIntToScalar(70), SkIntToScalar(70) - SK_Scalar1/1);
+ fPath[1].lineTo(SkIntToScalar(110), SkIntToScalar(70));
+
+ fPath[2].moveTo(SkIntToScalar(V), SkIntToScalar(V));
+ fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+ fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(50));
+
+ fPath[3].moveTo(SkIntToScalar(50), SkIntToScalar(50));
+ fPath[3].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+ fPath[3].lineTo(SkIntToScalar(V), SkIntToScalar(V));
+
+ fPath[4].moveTo(SkIntToScalar(50), SkIntToScalar(50));
+ fPath[4].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+ fPath[4].lineTo(SkIntToScalar(52), SkIntToScalar(50));
+
+ fPath[5].moveTo(SkIntToScalar(52), SkIntToScalar(50));
+ fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(V));
+ fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(50));
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ void nextStroke() {
+ fStroke += fDStroke;
+ if (fStroke > fMaxStroke || fStroke < fMinStroke)
+ fDStroke = -fDStroke;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Paths");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeJoin(j);
+ paint.setStrokeWidth(SkIntToScalar(fStroke));
+
+ if (fShowHairline) {
+ SkPath fill;
+
+ paint.getFillPath(path, &fill);
+ paint.setStrokeWidth(0);
+ canvas->drawPath(fill, paint);
+ } else {
+ canvas->drawPath(path, paint);
+ }
+
+ paint.setColor(SK_ColorRED);
+ paint.setStrokeWidth(0);
+ canvas->drawPath(path, paint);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
+
+ static const SkPaint::Join gJoins[] = {
+ SkPaint::kBevel_Join,
+ SkPaint::kMiter_Join,
+ SkPaint::kRound_Join
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) {
+ canvas->save();
+ for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) {
+ this->drawPath(canvas, fPath[j], gJoins[i]);
+ canvas->translate(SkIntToScalar(200), 0);
+ }
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(200));
+ }
+
+ this->nextStroke();
+ this->inval(NULL);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fShowHairline = !fShowHairline;
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp
new file mode 100644
index 0000000000..81391717e2
--- /dev/null
+++ b/samplecode/SamplePathClip.cpp
@@ -0,0 +1,84 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+class PathClipView : public SampleView {
+public:
+ SkRect fOval;
+ SkPoint fCenter;
+
+ PathClipView() {
+ fOval.set(0, 0, SkIntToScalar(200), SkIntToScalar(50));
+ fCenter.set(SkIntToScalar(250), SkIntToScalar(250));
+
+// test_ats();
+ }
+
+ virtual ~PathClipView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "PathClip");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkRect oval = fOval;
+ oval.offset(fCenter.fX - oval.centerX(), fCenter.fY - oval.centerY());
+
+ SkPaint p;
+ p.setAntiAlias(true);
+
+ p.setStyle(SkPaint::kStroke_Style);
+ canvas->drawOval(oval, p);
+
+ SkRect r;
+ r.set(SkIntToScalar(200), SkIntToScalar(200),
+ SkIntToScalar(300), SkIntToScalar(300));
+ canvas->clipRect(r);
+
+ p.setStyle(SkPaint::kFill_Style);
+ p.setColor(SK_ColorRED);
+ canvas->drawRect(r, p);
+
+ p.setColor(0x800000FF);
+ r.set(SkIntToScalar(150), SkIntToScalar(10),
+ SkIntToScalar(250), SkIntToScalar(400));
+ canvas->drawOval(oval, p);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ return new Click(this);
+ }
+
+ virtual bool onClick(Click* click) {
+ fCenter.set(click->fCurr.fX, click->fCurr.fY);
+ this->inval(NULL);
+ return NULL;
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathClipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
new file mode 100644
index 0000000000..75566b0412
--- /dev/null
+++ b/samplecode/SamplePathEffects.cpp
@@ -0,0 +1,184 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkPixelXorXfermode.h"
+
+#define CORNER_RADIUS 12
+static SkScalar gPhase;
+
+static const int gXY[] = {
+ 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
+};
+
+static SkPathEffect* make_pe(int flags) {
+ if (flags == 1)
+ return new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS));
+
+ SkPath path;
+ path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
+ for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
+ path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
+ path.close();
+ path.offset(SkIntToScalar(-6), 0);
+
+ SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), gPhase, SkPath1DPathEffect::kRotate_Style);
+
+ if (flags == 2)
+ return outer;
+
+ SkPathEffect* inner = new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS));
+
+ SkPathEffect* pe = new SkComposePathEffect(outer, inner);
+ outer->unref();
+ inner->unref();
+ return pe;
+}
+
+static SkPathEffect* make_warp_pe() {
+ SkPath path;
+ path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
+ for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
+ path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
+ path.close();
+ path.offset(SkIntToScalar(-6), 0);
+
+ SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), gPhase, SkPath1DPathEffect::kMorph_Style);
+ SkPathEffect* inner = new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS));
+
+ SkPathEffect* pe = new SkComposePathEffect(outer, inner);
+ outer->unref();
+ inner->unref();
+ return pe;
+}
+
+///////////////////////////////////////////////////////////
+
+#include "SkColorFilter.h"
+#include "SkLayerRasterizer.h"
+
+class testrast : public SkLayerRasterizer {
+public:
+ testrast() {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+#if 0
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SK_Scalar1*4);
+ this->addLayer(paint);
+
+ paint.setStrokeWidth(SK_Scalar1*1);
+ paint.setXfermode(SkXfermode::kClear_Mode);
+ this->addLayer(paint);
+#else
+ paint.setAlpha(0x66);
+ this->addLayer(paint, SkIntToScalar(4), SkIntToScalar(4));
+
+ paint.setAlpha(0xFF);
+ this->addLayer(paint);
+#endif
+ }
+};
+
+class PathEffectView : public SampleView {
+ SkPath fPath;
+ SkPoint fClickPt;
+public:
+ PathEffectView() {
+ SkRandom rand;
+ int steps = 20;
+ SkScalar dist = SkIntToScalar(400);
+ SkScalar x = SkIntToScalar(20);
+ SkScalar y = SkIntToScalar(50);
+
+ fPath.moveTo(x, y);
+ for (int i = 0; i < steps; i++) {
+ x += dist/steps;
+ SkScalar tmpY = y + SkIntToScalar(rand.nextS() % 25);
+ if (i == steps/2) {
+ fPath.moveTo(x, tmpY);
+ } else {
+ fPath.lineTo(x, tmpY);
+ }
+ }
+
+ {
+ SkRect oval;
+ oval.set(SkIntToScalar(20), SkIntToScalar(30),
+ SkIntToScalar(100), SkIntToScalar(60));
+ oval.offset(x, 0);
+ fPath.addRoundRect(oval, SkIntToScalar(8), SkIntToScalar(8));
+ }
+
+ fClickPt.set(SkIntToScalar(200), SkIntToScalar(200));
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "PathEffects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ gPhase -= SampleCode::GetAnimSecondsDelta() * 40;
+ this->inval(NULL);
+
+ SkPaint paint;
+
+#if 0
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(5));
+ canvas->drawPath(fPath, paint);
+ paint.setStrokeWidth(0);
+
+ paint.setColor(SK_ColorWHITE);
+ paint.setPathEffect(make_pe(1))->unref();
+ canvas->drawPath(fPath, paint);
+#endif
+
+ canvas->translate(0, SkIntToScalar(50));
+
+ paint.setColor(SK_ColorBLUE);
+ paint.setPathEffect(make_pe(2))->unref();
+ canvas->drawPath(fPath, paint);
+
+ canvas->translate(0, SkIntToScalar(50));
+
+ paint.setARGB(0xFF, 0, 0xBB, 0);
+ paint.setPathEffect(make_pe(3))->unref();
+ canvas->drawPath(fPath, paint);
+
+ canvas->translate(0, SkIntToScalar(50));
+
+ paint.setARGB(0xFF, 0, 0, 0);
+ paint.setPathEffect(make_warp_pe())->unref();
+ paint.setRasterizer(new testrast)->unref();
+ canvas->drawPath(fPath, paint);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathEffectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathFill.cpp b/samplecode/SamplePathFill.cpp
new file mode 100644
index 0000000000..845b7a88f9
--- /dev/null
+++ b/samplecode/SamplePathFill.cpp
@@ -0,0 +1,140 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkBlurDrawLooper.h"
+#include "SkGradientShader.h"
+
+typedef SkScalar (*MakePathProc)(SkPath*);
+
+static SkScalar make_frame(SkPath* path) {
+ SkRect r = { 10, 10, 630, 470 };
+ path->addRoundRect(r, 15, 15);
+
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(5);
+ paint.getFillPath(*path, path);
+ return 15;
+}
+
+static SkScalar make_triangle(SkPath* path) {
+ static const int gCoord[] = {
+ 10, 20, 15, 5, 30, 30
+ };
+ path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
+ path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
+ path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
+ path->close();
+ path->offset(10, 0);
+ return SkIntToScalar(30);
+}
+
+static SkScalar make_rect(SkPath* path) {
+ SkRect r = { 10, 10, 30, 30 };
+ path->addRect(r);
+ path->offset(10, 0);
+ return SkIntToScalar(30);
+}
+
+static SkScalar make_oval(SkPath* path) {
+ SkRect r = { 10, 10, 30, 30 };
+ path->addOval(r);
+ path->offset(10, 0);
+ return SkIntToScalar(30);
+}
+
+static SkScalar make_sawtooth(SkPath* path) {
+ SkScalar x = SkIntToScalar(20);
+ SkScalar y = SkIntToScalar(20);
+ const SkScalar x0 = x;
+ const SkScalar dx = SK_Scalar1 * 5;
+ const SkScalar dy = SK_Scalar1 * 10;
+
+ path->moveTo(x, y);
+ for (int i = 0; i < 32; i++) {
+ x += dx;
+ path->lineTo(x, y - dy);
+ x += dx;
+ path->lineTo(x, y + dy);
+ }
+ path->lineTo(x, y + 2 * dy);
+ path->lineTo(x0, y + 2 * dy);
+ path->close();
+ return SkIntToScalar(30);
+}
+
+static SkScalar make_star(SkPath* path, int n) {
+ const SkScalar c = SkIntToScalar(45);
+ const SkScalar r = SkIntToScalar(20);
+
+ SkScalar rad = -SK_ScalarPI / 2;
+ const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
+
+ path->moveTo(c, c - r);
+ for (int i = 1; i < n; i++) {
+ rad += drad;
+ SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
+ path->lineTo(c + SkScalarMul(cosV, r), c + SkScalarMul(sinV, r));
+ }
+ path->close();
+ return r * 2 * 6 / 5;
+}
+
+static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); }
+static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); }
+
+static const MakePathProc gProcs[] = {
+ make_frame,
+ make_triangle,
+ make_rect,
+ make_oval,
+ make_sawtooth,
+ make_star_5,
+ make_star_13
+};
+
+#define N SK_ARRAY_COUNT(gProcs)
+
+class PathFillView : public SampleView {
+ SkPath fPath[N];
+ SkScalar fDY[N];
+
+public:
+ PathFillView() {
+ for (size_t i = 0; i < N; i++) {
+ fDY[i] = gProcs[i](&fPath[i]);
+ }
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "PathFill");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ for (size_t i = 0; i < N; i++) {
+ canvas->drawPath(fPath[i], paint);
+ canvas->translate(0, fDY[i]);
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathFillView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
new file mode 100644
index 0000000000..d7b6b229f4
--- /dev/null
+++ b/samplecode/SamplePicture.cpp
@@ -0,0 +1,254 @@
+#include "SampleCode.h"
+#include "SkDumpCanvas.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkShape.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+class SignalShape : public SkShape {
+public:
+ SignalShape() : fSignal(0) {}
+
+ SkShape* setSignal(int n) {
+ fSignal = n;
+ return this;
+ }
+
+protected:
+ virtual void onDraw(SkCanvas* canvas) {
+ // SkDebugf("---- sc %d\n", canvas->getSaveCount() - 1);
+ }
+
+private:
+ int fSignal;
+};
+
+static SkPMColor SignalProc(SkPMColor src, SkPMColor dst) {
+ return dst;
+}
+
+/* Picture playback will skip blocks of draw calls that follow a clip() call
+ that returns empty, and jump down to the corresponding restore() call.
+
+ This is a great preformance win for drawing very large/tall pictures with
+ a small visible window (think scrolling a long document). These tests make
+ sure that (a) we are performing the culling, and (b) we don't get confused
+ by nested save() calls, nor by calls to restoreToCount().
+ */
+static void test_saveRestoreCulling() {
+ SkPaint signalPaint;
+ SignalShape signalShape;
+
+ SkPicture pic;
+ SkRect r = SkRect::MakeWH(0, 0);
+ int n;
+ SkCanvas* canvas = pic.beginRecording(100, 100);
+ int startN = canvas->getSaveCount();
+ SkDebugf("---- start sc %d\n", startN);
+ canvas->drawShape(signalShape.setSignal(1));
+ canvas->save();
+ canvas->drawShape(signalShape.setSignal(2));
+ n = canvas->save();
+ canvas->drawShape(signalShape.setSignal(3));
+ canvas->save();
+ canvas->clipRect(r);
+ canvas->drawShape(signalShape.setSignal(4));
+ canvas->restoreToCount(n);
+ canvas->drawShape(signalShape.setSignal(5));
+ canvas->restore();
+ canvas->drawShape(signalShape.setSignal(6));
+ SkASSERT(canvas->getSaveCount() == startN);
+
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+ bm.allocPixels();
+ SkCanvas c(bm);
+ c.drawPicture(pic);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageRef_GlobalPool.h"
+
+static SkBitmap load_bitmap() {
+ SkStream* stream = new SkFILEStream("/skimages/sesame_street_ensemble-hp.jpg");
+ SkAutoUnref aur(stream);
+
+ SkBitmap bm;
+ if (SkImageDecoder::DecodeStream(stream, &bm, SkBitmap::kNo_Config,
+ SkImageDecoder::kDecodeBounds_Mode)) {
+ SkPixelRef* pr = new SkImageRef_GlobalPool(stream, bm.config(), 1);
+ bm.setPixelRef(pr)->unref();
+ }
+ return bm;
+}
+
+static void drawCircle(SkCanvas* canvas, int r, SkColor color) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(color);
+
+ canvas->drawCircle(SkIntToScalar(r), SkIntToScalar(r), SkIntToScalar(r),
+ paint);
+}
+
+class PictureView : public SampleView {
+ SkBitmap fBitmap;
+public:
+ PictureView() {
+ SkImageRef_GlobalPool::SetRAMBudget(16 * 1024);
+
+ fBitmap = load_bitmap();
+
+ fPicture = new SkPicture;
+ SkCanvas* canvas = fPicture->beginRecording(100, 100);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ canvas->drawBitmap(fBitmap, 0, 0, NULL);
+
+ drawCircle(canvas, 50, SK_ColorBLACK);
+ fSubPicture = new SkPicture;
+ canvas->drawPicture(*fSubPicture);
+ canvas->translate(SkIntToScalar(50), 0);
+ canvas->drawPicture(*fSubPicture);
+ canvas->translate(0, SkIntToScalar(50));
+ canvas->drawPicture(*fSubPicture);
+ canvas->translate(SkIntToScalar(-50), 0);
+ canvas->drawPicture(*fSubPicture);
+ // fPicture now has (4) references to us. We can release ours, and just
+ // unref fPicture in our destructor, and it will in turn take care of
+ // the other references to fSubPicture
+ fSubPicture->unref();
+
+ test_saveRestoreCulling();
+ }
+
+ virtual ~PictureView() {
+ fPicture->unref();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Picture");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawSomething(SkCanvas* canvas) {
+ SkPaint paint;
+
+ canvas->save();
+ canvas->scale(0.5f, 0.5f);
+ canvas->drawBitmap(fBitmap, 0, 0, NULL);
+ canvas->restore();
+
+ const char beforeStr[] = "before circle";
+ const char afterStr[] = "after circle";
+
+ paint.setAntiAlias(true);
+
+ paint.setColor(SK_ColorRED);
+ canvas->drawData(beforeStr, sizeof(beforeStr));
+ canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+ SkIntToScalar(40), paint);
+ canvas->drawData(afterStr, sizeof(afterStr));
+ paint.setColor(SK_ColorBLACK);
+ paint.setTextSize(SkIntToScalar(40));
+ canvas->drawText("Picture", 7, SkIntToScalar(50), SkIntToScalar(62),
+ paint);
+
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ drawSomething(canvas);
+
+ SkPicture* pict = new SkPicture;
+ SkAutoUnref aur(pict);
+
+ drawSomething(pict->beginRecording(100, 100));
+ pict->endRecording();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(300), SkIntToScalar(50));
+ canvas->scale(-SK_Scalar1, -SK_Scalar1);
+ canvas->translate(-SkIntToScalar(100), -SkIntToScalar(50));
+ canvas->drawPicture(*pict);
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(200), SkIntToScalar(150));
+ canvas->scale(SK_Scalar1, -SK_Scalar1);
+ canvas->translate(0, -SkIntToScalar(50));
+ canvas->drawPicture(*pict);
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
+ canvas->scale(-SK_Scalar1, SK_Scalar1);
+ canvas->translate(-SkIntToScalar(100), 0);
+ canvas->drawPicture(*pict);
+ canvas->restore();
+
+ if (false) {
+ SkDebugfDumper dumper;
+ SkDumpCanvas dumpCanvas(&dumper);
+ dumpCanvas.drawPicture(*pict);
+ }
+
+ // test that we can re-record a subpicture, and see the results
+
+ SkRandom rand(SampleCode::GetAnimTime());
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(250));
+ drawCircle(fSubPicture->beginRecording(50, 50), 25,
+ rand.nextU() | 0xFF000000);
+ canvas->drawPicture(*fPicture);
+ delayInval(500);
+ }
+
+private:
+ #define INVAL_ALL_TYPE "inval-all"
+
+ void delayInval(SkMSec delay) {
+ (new SkEvent(INVAL_ALL_TYPE))->post(this->getSinkID(), delay);
+ }
+
+ virtual bool onEvent(const SkEvent& evt) {
+ if (evt.isType(INVAL_ALL_TYPE)) {
+ this->inval(NULL);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+ }
+
+ SkPicture* fPicture;
+ SkPicture* fSubPicture;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PictureView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePoints.cpp b/samplecode/SamplePoints.cpp
new file mode 100644
index 0000000000..a2804b42b1
--- /dev/null
+++ b/samplecode/SamplePoints.cpp
@@ -0,0 +1,78 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+class PointsView : public SampleView {
+public:
+ PointsView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Points");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
+ for (size_t i = 0; i < n; i++)
+ pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SK_Scalar1, SK_Scalar1);
+
+ SkRandom rand;
+ SkPaint p0, p1, p2, p3;
+ const size_t n = 99;
+
+ p0.setColor(SK_ColorRED);
+ p1.setColor(SK_ColorGREEN);
+ p2.setColor(SK_ColorBLUE);
+ p3.setColor(SK_ColorWHITE);
+
+ p0.setStrokeWidth(SkIntToScalar(4));
+ p2.setStrokeCap(SkPaint::kRound_Cap);
+ p2.setStrokeWidth(SkIntToScalar(6));
+
+ SkPoint* pts = new SkPoint[n];
+ fill_pts(pts, n, &rand);
+
+ canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts, p0);
+ canvas->drawPoints(SkCanvas::kLines_PointMode, n, pts, p1);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, p2);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, p3);
+
+ delete[] pts;
+ }
+
+private:
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PointsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePolyToPoly.cpp b/samplecode/SamplePolyToPoly.cpp
new file mode 100644
index 0000000000..aea0cb4457
--- /dev/null
+++ b/samplecode/SamplePolyToPoly.cpp
@@ -0,0 +1,161 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkTime.h"
+
+extern bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]);
+
+class PolyToPolyView : public SampleView {
+public:
+ PolyToPolyView() {
+ // tests
+ {
+ SkPoint src[] = { { 0, 0 },
+ { SK_Scalar1, 0 },
+ { 0, SK_Scalar1 } };
+ SkPoint dst[] = { { 0, 0 },
+ { 2*SK_Scalar1, 0 },
+ { 0, 2*SK_Scalar1 } };
+ SkMatrix m1, m2;
+ bool success;
+
+ success = m1.setPolyToPoly(src, dst, 3);
+
+ m2.reset();
+ m2.set(SkMatrix::kMScaleX, dst[1].fX - dst[0].fX);
+ m2.set(SkMatrix::kMSkewX, dst[2].fX - dst[0].fX);
+ m2.set(SkMatrix::kMTransX, dst[0].fX);
+ m2.set(SkMatrix::kMSkewY, dst[1].fY - dst[0].fY);
+ m2.set(SkMatrix::kMScaleY, dst[2].fY - dst[0].fY);
+ m2.set(SkMatrix::kMTransY, dst[0].fY);
+
+ m1.reset();
+
+ const SkScalar src1[] = {
+ 0, 0, 0, SkFloatToScalar(427), SkFloatToScalar(316), SkFloatToScalar(427), SkFloatToScalar(316), 0
+ };
+ const SkScalar dst1[] = {
+ SkFloatToScalar(158), SkFloatToScalar(177.5f), SkFloatToScalar(158), SkFloatToScalar(249.5f),
+ SkFloatToScalar(158), SkFloatToScalar(604.5f), SkFloatToScalar(158), SkFloatToScalar(-177.5f)
+ };
+
+ success = m2.setPolyToPoly((const SkPoint*)src1, (SkPoint*)dst1, 4);
+
+ {
+ const SkPoint src[] = {
+ { SkIntToScalar(1), SkIntToScalar(0) },
+ { SkIntToScalar(4), SkIntToScalar(7) },
+ { SkIntToScalar(10), SkIntToScalar(2) }
+ };
+ const SkPoint dst[] = {
+ { SkIntToScalar(4), SkIntToScalar(2) },
+ { SkIntToScalar(45), SkIntToScalar(26) },
+ { SkIntToScalar(32), SkIntToScalar(17) }
+ };
+
+ SkMatrix m0, m1;
+ m0.setPolyToPoly(src, dst, 3);
+ // SkSetPoly3To3(&m1, src, dst);
+ // m0.dump();
+ // m1.dump();
+ }
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SkString str("PolyToPolyView");
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static void doDraw(SkCanvas* canvas, SkPaint* paint, const int isrc[],
+ const int idst[], int count) {
+ SkMatrix matrix;
+ SkPoint src[4], dst[4];
+
+ for (int i = 0; i < count; i++) {
+ src[i].set(SkIntToScalar(isrc[2*i+0]), SkIntToScalar(isrc[2*i+1]));
+ dst[i].set(SkIntToScalar(idst[2*i+0]), SkIntToScalar(idst[2*i+1]));
+ }
+
+ canvas->save();
+ matrix.setPolyToPoly(src, dst, count);
+ canvas->concat(matrix);
+
+ paint->setColor(SK_ColorGRAY);
+ paint->setStyle(SkPaint::kStroke_Style);
+ const SkScalar D = SkIntToScalar(64);
+ canvas->drawRectCoords(0, 0, D, D, *paint);
+ canvas->drawLine(0, 0, D, D, *paint);
+ canvas->drawLine(0, D, D, 0, *paint);
+
+ SkPaint::FontMetrics fm;
+ paint->getFontMetrics(&fm);
+ paint->setColor(SK_ColorRED);
+ paint->setStyle(SkPaint::kFill_Style);
+ SkScalar x = D/2;
+ float y = D/2 - (fm.fAscent + fm.fDescent)/2;
+ SkString str;
+ str.appendS32(count);
+ canvas->drawText(str.c_str(), str.size(), x, y, *paint);
+
+ canvas->restore();
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(SkIntToScalar(4));
+ paint.setTextSize(SkIntToScalar(40));
+ paint.setTextAlign(SkPaint::kCenter_Align);
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+ // translate (1 point)
+ const int src1[] = { 0, 0 };
+ const int dst1[] = { 5, 5 };
+ doDraw(canvas, &paint, src1, dst1, 1);
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(160), SkIntToScalar(10));
+ // rotate/uniform-scale (2 points)
+ const int src2[] = { 32, 32, 64, 32 };
+ const int dst2[] = { 32, 32, 64, 48 };
+ doDraw(canvas, &paint, src2, dst2, 2);
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(110));
+ // rotate/skew (3 points)
+ const int src3[] = { 0, 0, 64, 0, 0, 64 };
+ const int dst3[] = { 0, 0, 96, 0, 24, 64 };
+ doDraw(canvas, &paint, src3, dst3, 3);
+ canvas->restore();
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(160), SkIntToScalar(110));
+ // perspective (4 points)
+ const int src4[] = { 0, 0, 64, 0, 64, 64, 0, 64 };
+ const int dst4[] = { 0, 0, 96, 0, 64, 96, 0, 64 };
+ doDraw(canvas, &paint, src4, dst4, 4);
+ canvas->restore();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PolyToPolyView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
new file mode 100644
index 0000000000..822bd6fa47
--- /dev/null
+++ b/samplecode/SampleRegion.cpp
@@ -0,0 +1,273 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkImageDecoder.h"
+
+#ifdef SK_BUILD_FOR_WIN
+// windows doesn't have roundf
+inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
+#endif
+
+#ifdef SK_DEBUG
+static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
+ size_t count, int32_t runs[]) {
+ SkIRect r;
+ r.set(left, top, right, bottom);
+
+ rgn->debugSetRuns(runs, count);
+ SkASSERT(rgn->getBounds() == r);
+}
+
+static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
+ static int32_t dataA[] = {
+ 0x00000001, 0x000001dd,
+ 0x00000001, 0x0000000c, 0x0000000d, 0x00000025,
+ 0x7fffffff, 0x000001de, 0x00000001, 0x00000025,
+ 0x7fffffff, 0x000004b3, 0x00000001, 0x00000026,
+ 0x7fffffff, 0x000004b4, 0x0000000c, 0x00000026,
+ 0x7fffffff, 0x00000579, 0x00000000, 0x0000013a,
+ 0x7fffffff, 0x000005d8, 0x00000000, 0x0000013b,
+ 0x7fffffff, 0x7fffffff
+ };
+ make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
+
+ static int32_t dataB[] = {
+ 0x000000b6, 0x000000c4,
+ 0x000000a1, 0x000000f0, 0x7fffffff, 0x000000d6,
+ 0x7fffffff, 0x000000e4, 0x00000070, 0x00000079,
+ 0x000000a1, 0x000000b0, 0x7fffffff, 0x000000e6,
+ 0x7fffffff, 0x000000f4, 0x00000070, 0x00000079,
+ 0x000000a1, 0x000000b0, 0x7fffffff, 0x000000f6,
+ 0x7fffffff, 0x00000104, 0x000000a1, 0x000000b0,
+ 0x7fffffff, 0x7fffffff
+ };
+ make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
+
+ rc->op(*ra, *rb, SkRegion::kUnion_Op);
+}
+#endif
+
+static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
+ dst->fLeft = (int)::roundf(src.fLeft * scale);
+ dst->fTop = (int)::roundf(src.fTop * scale);
+ dst->fRight = (int)::roundf(src.fRight * scale);
+ dst->fBottom = (int)::roundf(src.fBottom * scale);
+}
+
+static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
+ SkRegion tmp;
+ SkRegion::Iterator iter(src);
+
+ for (; !iter.done(); iter.next()) {
+ SkIRect r;
+ scale_rect(&r, iter.rect(), scale);
+ tmp.op(r, SkRegion::kUnion_Op);
+ }
+ dst->swap(tmp);
+}
+
+static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
+ const SkPaint& paint) {
+ SkRegion scaled;
+ scale_rgn(&scaled, rgn, 0.5f);
+
+ SkRegion::Iterator iter(rgn);
+
+ for (; !iter.done(); iter.next())
+ {
+ SkRect r;
+ r.set(iter.rect());
+ canvas->drawRect(r, paint);
+ }
+}
+
+class RegionView : public SampleView {
+public:
+ RegionView() {
+ fBase.set(100, 100, 150, 150);
+ fRect = fBase;
+ fRect.inset(5, 5);
+ fRect.offset(25, 25);
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ void build_rgn(SkRegion* rgn, SkRegion::Op op) {
+ rgn->setRect(fBase);
+ SkIRect r = fBase;
+ r.offset(75, 20);
+ rgn->op(r, SkRegion::kUnion_Op);
+ rgn->op(fRect, op);
+ }
+
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Regions");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawOrig(SkCanvas* canvas, bool bg) {
+ SkRect r;
+ SkPaint paint;
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ if (bg)
+ paint.setColor(0xFFBBBBBB);
+
+ r.set(fBase);
+ canvas->drawRect(r, paint);
+ r.set(fRect);
+ canvas->drawRect(r, paint);
+ }
+
+ void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
+ SkRegion rgn;
+
+ this->build_rgn(&rgn, op);
+
+ {
+ SkRegion tmp, tmp2(rgn);
+
+ tmp = tmp2;
+ tmp.translate(5, -3);
+
+ {
+ char buffer[1000];
+ size_t size = tmp.flatten(NULL);
+ SkASSERT(size <= sizeof(buffer));
+ size_t size2 = tmp.flatten(buffer);
+ SkASSERT(size == size2);
+
+ SkRegion tmp3;
+ size2 = tmp3.unflatten(buffer);
+ SkASSERT(size == size2);
+
+ SkASSERT(tmp3 == tmp);
+ }
+
+ rgn.translate(20, 30, &tmp);
+ SkASSERT(rgn.isEmpty() || tmp != rgn);
+ tmp.translate(-20, -30);
+ SkASSERT(tmp == rgn);
+ }
+
+ this->drawOrig(canvas, true);
+
+ SkPaint paint;
+ paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
+ paint_rgn(canvas, rgn, paint);
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(color);
+ paint_rgn(canvas, rgn, paint);
+ }
+
+ void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
+ SkRegion rgn;
+ SkPath path;
+
+ this->build_rgn(&rgn, op);
+ rgn.getBoundaryPath(&path);
+
+ this->drawOrig(canvas, true);
+
+ SkPaint paint;
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
+ canvas->drawPath(path, paint);
+ paint.setColor(color);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawPath(path, paint);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+#ifdef SK_DEBUG
+ if (true) {
+ SkRegion a, b, c;
+ test_union_bug_1505668(&a, &b, &c);
+
+ if (false) { // draw the result of the test
+ SkPaint paint;
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+ paint.setColor(SK_ColorRED);
+ paint_rgn(canvas, a, paint);
+ paint.setColor(0x800000FF);
+ paint_rgn(canvas, b, paint);
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kStroke_Style);
+ // paint_rgn(canvas, c, paint);
+ return;
+ }
+ }
+#endif
+
+ static const struct {
+ SkColor fColor;
+ const char* fName;
+ SkRegion::Op fOp;
+ } gOps[] = {
+ { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op },
+ { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op },
+ { 0xFF008800, "Union", SkRegion::kUnion_Op },
+ { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op }
+ };
+
+ SkPaint textPaint;
+ textPaint.setAntiAlias(true);
+ textPaint.setTextSize(SK_Scalar1*24);
+
+ this->drawOrig(canvas, false);
+ canvas->save();
+ canvas->translate(SkIntToScalar(200), 0);
+ this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(200));
+
+ for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
+ canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
+
+ this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
+
+ canvas->save();
+ canvas->translate(0, SkIntToScalar(200));
+ this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
+ canvas->restore();
+
+ canvas->translate(SkIntToScalar(200), 0);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
+ }
+
+ virtual bool onClick(Click* click) {
+ fRect.offset(click->fICurr.fX - click->fIPrev.fX,
+ click->fICurr.fY - click->fIPrev.fY);
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ SkIRect fBase, fRect;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new RegionView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleRepeatTile.cpp b/samplecode/SampleRepeatTile.cpp
new file mode 100644
index 0000000000..9867074a1a
--- /dev/null
+++ b/samplecode/SampleRepeatTile.cpp
@@ -0,0 +1,86 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkKey.h"
+
+static void make_bitmap(SkBitmap* bm) {
+ const int W = 100;
+ const int H = 100;
+ bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
+ bm->allocPixels();
+
+ SkPaint paint;
+ SkCanvas canvas(*bm);
+ canvas.drawColor(SK_ColorWHITE);
+
+ const SkColor colors[] = {
+ SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
+ };
+
+ for (int ix = 0; ix < W; ix += 1) {
+ SkScalar x = SkIntToScalar(ix) + SK_ScalarHalf;
+ paint.setColor(colors[ix & 3]);
+ canvas.drawLine(x, 0, x, SkIntToScalar(H - 1), paint);
+ }
+ paint.setColor(SK_ColorGRAY);
+ canvas.drawLine(0, 0, SkIntToScalar(W), 0, paint);
+}
+
+static void make_paint(SkPaint* paint, SkShader::TileMode tm) {
+ SkBitmap bm;
+ make_bitmap(&bm);
+
+ SkShader* shader = SkShader::CreateBitmapShader(bm, tm, tm);
+ paint->setShader(shader)->unref();
+}
+
+class RepeatTileView : public SampleView {
+public:
+ RepeatTileView() {
+ this->setBGColor(SK_ColorGRAY);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "RepeatTile");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ make_paint(&paint, SkShader::kRepeat_TileMode);
+
+// canvas->scale(SK_Scalar1*2, SK_Scalar1);
+ canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
+ canvas->drawPaint(paint);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+ virtual bool handleKey(SkKey key) {
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new RepeatTileView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleShaderText.cpp b/samplecode/SampleShaderText.cpp
new file mode 100644
index 0000000000..2748b559a2
--- /dev/null
+++ b/samplecode/SampleShaderText.cpp
@@ -0,0 +1,195 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkUnitMappers.h"
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+ bm->setConfig(config, w, h);
+ bm->allocPixels();
+ bm->eraseColor(0);
+
+ SkCanvas canvas(*bm);
+ SkScalar s = SkIntToScalar(w < h ? w : h);
+ SkPoint pts[] = { { 0, 0 }, { s, s } };
+ SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
+ SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+ SkPaint paint;
+
+ SkUnitMapper* um = NULL;
+
+ um = new SkCosineMapper;
+
+ SkAutoUnref au(um);
+
+ paint.setDither(true);
+ paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
+ SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
+ canvas.drawPaint(paint);
+}
+
+SkShader* MakeBitmapShader(SkShader::TileMode tx, SkShader::TileMode ty,
+ int w, int h) {
+ static SkBitmap bmp;
+ if (bmp.isNull()) {
+ makebm(&bmp, SkBitmap::kARGB_8888_Config, w/2, h/4);
+ }
+ return SkShader::CreateBitmapShader(bmp, tx, ty);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GradData {
+ int fCount;
+ const SkColor* fColors;
+ const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+ SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+
+static const GradData gGradData[] = {
+ { 2, gColors, NULL },
+ { 5, gColors, NULL },
+};
+
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
+ data.fCount, tm, mapper);
+}
+
+static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+ data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
+ data.fPos, data.fCount, mapper);
+}
+
+static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center0, center1;
+ center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+ SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+ return SkGradientShader::CreateTwoPointRadial(
+ center1, (pts[1].fX - pts[0].fX) / 7,
+ center0, (pts[1].fX - pts[0].fX) / 2,
+ data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper);
+static const GradMaker gGradMakers[] = {
+ MakeLinear, MakeRadial, MakeSweep, Make2Radial
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ShaderTextView : public SampleView {
+public:
+ ShaderTextView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Shader Text");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ const char text[] = "Shaded Text";
+ const int textLen = SK_ARRAY_COUNT(text) - 1;
+ static int pointSize = 48;
+
+ int w = pointSize * textLen;
+ int h = pointSize;
+
+ SkPoint pts[2] = {
+ { 0, 0 },
+ { SkIntToScalar(w), SkIntToScalar(h) }
+ };
+ SkScalar textBase = SkIntToScalar(h/2);
+
+ SkShader::TileMode tileModes[] = {
+ SkShader::kClamp_TileMode,
+ SkShader::kRepeat_TileMode,
+ SkShader::kMirror_TileMode
+ };
+
+ static const int gradCount = SK_ARRAY_COUNT(gGradData) *
+ SK_ARRAY_COUNT(gGradMakers);
+ static const int bmpCount = SK_ARRAY_COUNT(tileModes) *
+ SK_ARRAY_COUNT(tileModes);
+ SkShader* shaders[gradCount + bmpCount];
+
+ int shdIdx = 0;
+ for (size_t d = 0; d < SK_ARRAY_COUNT(gGradData); ++d) {
+ for (size_t m = 0; m < SK_ARRAY_COUNT(gGradMakers); ++m) {
+ shaders[shdIdx++] = gGradMakers[m](pts,
+ gGradData[d],
+ SkShader::kClamp_TileMode,
+ NULL);
+ }
+ }
+ for (size_t tx = 0; tx < SK_ARRAY_COUNT(tileModes); ++tx) {
+ for (size_t ty = 0; ty < SK_ARRAY_COUNT(tileModes); ++ty) {
+ shaders[shdIdx++] = MakeBitmapShader(tileModes[tx],
+ tileModes[ty],
+ w/8, h);
+ }
+ }
+
+ SkPaint paint;
+ paint.setDither(true);
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(pointSize));
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
+
+ static const int testsPerCol = 8;
+ static const int rowHeight = 60;
+ static const int colWidth = 300;
+ canvas->save();
+ for (size_t s = 0; s < SK_ARRAY_COUNT(shaders); s++) {
+ canvas->save();
+ canvas->translate(SkIntToScalar((s / testsPerCol) * colWidth),
+ SkIntToScalar((s % testsPerCol) * rowHeight));
+ paint.setShader(shaders[s])->unref();
+ canvas->drawText(text, textLen, 0, textBase, paint);
+ canvas->restore();
+ }
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(370));
+ this->inval(NULL);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShaderTextView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleShaders.cpp b/samplecode/SampleShaders.cpp
new file mode 100644
index 0000000000..c1bb0fda21
--- /dev/null
+++ b/samplecode/SampleShaders.cpp
@@ -0,0 +1,134 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkComposeShader.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTransparentShader.h"
+#include "SkTypeface.h"
+
+static SkShader* make_bitmapfade(const SkBitmap& bm)
+{
+ SkPoint pts[2];
+ SkColor colors[2];
+
+ pts[0].set(0, 0);
+ pts[1].set(0, SkIntToScalar(bm.height()));
+ colors[0] = SK_ColorBLACK;
+ colors[1] = SkColorSetARGB(0, 0, 0, 0);
+ SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+ SkShader* shaderB = SkShader::CreateBitmapShader(bm,
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+
+ SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+
+ SkShader* shader = new SkComposeShader(shaderB, shaderA, mode);
+ shaderA->unref();
+ shaderB->unref();
+ mode->unref();
+
+ return shader;
+}
+
+class ShaderView : public SampleView {
+public:
+ SkShader* fShader;
+ SkBitmap fBitmap;
+
+ ShaderView() {
+ SkImageDecoder::DecodeFile("/skimages/logo.gif", &fBitmap);
+
+ SkPoint pts[2];
+ SkColor colors[2];
+
+ pts[0].set(0, 0);
+ pts[1].set(SkIntToScalar(100), 0);
+ colors[0] = SK_ColorRED;
+ colors[1] = SK_ColorBLUE;
+ SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+ pts[0].set(0, 0);
+ pts[1].set(0, SkIntToScalar(100));
+ colors[0] = SK_ColorBLACK;
+ colors[1] = SkColorSetARGB(0x80, 0, 0, 0);
+ SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
+
+ SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+
+ fShader = new SkComposeShader(shaderA, shaderB, mode);
+ shaderA->unref();
+ shaderB->unref();
+ mode->unref();
+ }
+ virtual ~ShaderView() {
+ SkSafeUnref(fShader);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Shaders");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->drawBitmap(fBitmap, 0, 0);
+
+ canvas->translate(SkIntToScalar(20), SkIntToScalar(120));
+
+ SkPaint paint;
+ SkRect r;
+
+ paint.setColor(SK_ColorGREEN);
+ canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
+ paint.setShader(fShader);
+ canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
+
+ canvas->translate(SkIntToScalar(110), 0);
+
+ int w = fBitmap.width();
+ int h = fBitmap.height();
+ w = 120;
+ h = 80;
+ r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
+
+ paint.setShader(NULL);
+ canvas->drawRect(r, paint);
+ paint.setShader(make_bitmapfade(fBitmap))->unref();
+ canvas->drawRect(r, paint);
+
+ paint.setShader(new SkTransparentShader)->unref();
+ canvas->drawRect(r, paint);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShaderView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleShapes.cpp b/samplecode/SampleShapes.cpp
new file mode 100644
index 0000000000..dc10f1a454
--- /dev/null
+++ b/samplecode/SampleShapes.cpp
@@ -0,0 +1,160 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkView.h"
+
+#define DO_AA true
+
+#include "SkRectShape.h"
+#include "SkGroupShape.h"
+
+static SkRect make_rect(int l, int t, int r, int b) {
+ SkRect rect;
+ rect.set(SkIntToScalar(l), SkIntToScalar(t),
+ SkIntToScalar(r), SkIntToScalar(b));
+ return rect;
+}
+
+static SkShape* make_shape0(bool red) {
+ SkRectShape* s = new SkRectShape;
+ s->setRect(make_rect(10, 10, 90, 90));
+ if (red) {
+ s->paint().setColor(SK_ColorRED);
+ }
+ s->paint().setAntiAlias(DO_AA);
+ return s;
+}
+
+static SkShape* make_shape1() {
+ SkRectShape* s = new SkRectShape;
+ s->setOval(make_rect(10, 10, 90, 90));
+ s->paint().setColor(SK_ColorBLUE);
+ s->paint().setAntiAlias(DO_AA);
+ return s;
+}
+
+static SkShape* make_shape2() {
+ SkRectShape* s = new SkRectShape;
+ s->setRRect(make_rect(10, 10, 90, 90),
+ SkIntToScalar(20), SkIntToScalar(20));
+ s->paint().setColor(SK_ColorGREEN);
+ s->paint().setAntiAlias(DO_AA);
+ return s;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ShapesView : public SampleView {
+ SkGroupShape fGroup;
+ SkMatrixRef* fMatrixRefs[4];
+public:
+ ShapesView() {
+ SkMatrix m;
+ fGroup.appendShape(make_shape0(false))->unref();
+ m.setRotate(SkIntToScalar(30), SkIntToScalar(50), SkIntToScalar(50));
+ m.postTranslate(0, SkIntToScalar(120));
+ fGroup.appendShape(make_shape0(true), m)->unref();
+
+ m.setTranslate(SkIntToScalar(120), 0);
+ fGroup.appendShape(make_shape1(), m)->unref();
+ m.postTranslate(0, SkIntToScalar(120));
+ fGroup.appendShape(make_shape2(), m)->unref();
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
+ SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
+ }
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~ShapesView() {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
+ SkSafeUnref(fMatrixRefs[i]);
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Shapes");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawpicture(SkCanvas* canvas, SkPicture& pict) {
+#if 0
+ SkDynamicMemoryWStream ostream;
+ pict.serialize(&ostream);
+
+ SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
+ SkPicture* newPict = new SkPicture(&istream);
+ canvas->drawPicture(*newPict);
+ newPict->unref();
+#else
+ canvas->drawPicture(pict);
+#endif
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkScalar angle = SampleCode::GetAnimScalar(SkIntToScalar(180),
+ SkIntToScalar(360));
+
+ SkMatrix saveM = *fMatrixRefs[3];
+ SkScalar c = SkIntToScalar(50);
+ fMatrixRefs[3]->preRotate(angle, c, c);
+
+ const SkScalar dx = 350;
+ const SkScalar dy = 500;
+ const int N = 1;
+ for (int v = -N; v <= N; v++) {
+ for (int h = -N; h <= N; h++) {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(h * dx, v * dy);
+
+ SkMatrix matrix;
+
+ SkGroupShape* gs = new SkGroupShape;
+ SkAutoUnref aur(gs);
+ gs->appendShape(&fGroup);
+ matrix.setScale(-SK_Scalar1, SK_Scalar1);
+ matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
+ gs->appendShape(&fGroup, matrix);
+ matrix.setTranslate(SkIntToScalar(240), 0);
+ matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
+ gs->appendShape(&fGroup, matrix);
+
+#if 0
+ canvas->drawShape(gs);
+#else
+ SkPicture* pict = new SkPicture;
+ SkCanvas* cv = pict->beginRecording(1000, 1000);
+ cv->scale(SK_ScalarHalf, SK_ScalarHalf);
+ cv->drawShape(gs);
+ cv->translate(SkIntToScalar(680), SkIntToScalar(480));
+ cv->scale(-SK_Scalar1, SK_Scalar1);
+ cv->drawShape(gs);
+ pict->endRecording();
+
+ drawpicture(canvas, *pict);
+ pict->unref();
+#endif
+
+ }}
+
+ *fMatrixRefs[3] = saveM;
+ this->inval(NULL);
+}
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShapesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleSkLayer.cpp b/samplecode/SampleSkLayer.cpp
new file mode 100644
index 0000000000..11976e9da6
--- /dev/null
+++ b/samplecode/SampleSkLayer.cpp
@@ -0,0 +1,239 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkView.h"
+#include "SkLayer.h"
+
+#include "SkMatrix44.h"
+static void test_inv(const char label[], const SkMatrix44& mat) {
+ SkDebugf("%s\n", label);
+ mat.dump();
+
+ SkMatrix44 inv;
+ if (mat.invert(&inv)) {
+ inv.dump();
+ } else {
+ SkDebugf("--- invert failed\n");
+ }
+
+ SkMatrix44 a, b;
+ a.setConcat(mat, inv);
+ b.setConcat(inv, mat);
+ SkDebugf("concat mat with inverse pre=%d post=%d\n", a.isIdentity(), b.isIdentity());
+ if (!a.isIdentity()) {
+ a.dump();
+ }
+ if (!b.isIdentity()) {
+ b.dump();
+ }
+ SkDebugf("\n");
+}
+
+static void test_map(SkScalar x0, SkScalar y0, SkScalar z0,
+ const SkMatrix44& mat,
+ SkScalar x1, SkScalar y1, SkScalar z1) {
+ SkVector4 src, dst;
+ src.set(x0, y0, z0);
+ dst = mat * src;
+ SkDebugf("map: src: %g %g %g dst: %g %g %g (%g) expected: %g %g %g match: %d\n",
+ x0, y0, z0,
+ dst.fData[0], dst.fData[1], dst.fData[2], dst.fData[3],
+ x1, y1, z1,
+ dst.fData[0] == x1 && dst.fData[1] == y1 && dst.fData[2] == z1);
+}
+
+static void test_33(const SkMatrix44& mat,
+ SkScalar x0, SkScalar x1, SkScalar x2,
+ SkScalar y0, SkScalar y1, SkScalar y2) {
+ SkMatrix dst = mat;
+ if (dst[0] != x0 || dst[1] != x1 || dst[2] != x2 ||
+ dst[3] != y0 || dst[4] != y1 || dst[5] != y2) {
+ SkString str;
+ dst.toDumpString(&str);
+ SkDebugf("3x3: expected 3x3 [%g %g %g] [%g %g %g] bug got %s\n",
+ x0, x1, x2, y0, y1, y2, str.c_str());
+ }
+}
+
+static void test44() {
+ SkMatrix44 m0, m1, m2;
+
+ test_inv("identity", m0);
+ m0.setTranslate(2,3,4);
+ test_inv("translate", m0);
+ m0.setScale(2,3,4);
+ test_inv("scale", m0);
+ m0.postTranslate(5, 6, 7);
+ test_inv("postTranslate", m0);
+ m0.setScale(2,3,4);
+ m1.setTranslate(5, 6, 7);
+ m0.setConcat(m0, m1);
+ test_inv("postTranslate2", m0);
+ m0.setScale(2,3,4);
+ m0.preTranslate(5, 6, 7);
+ test_inv("preTranslate", m0);
+
+ m0.setScale(2, 4, 6);
+ m0.postScale(SkDoubleToMScalar(0.5));
+ test_inv("scale/postscale to 1,2,3", m0);
+
+ m0.reset();
+ test_map(1, 0, 0, m0, 1, 0, 0);
+ test_map(0, 1, 0, m0, 0, 1, 0);
+ test_map(0, 0, 1, m0, 0, 0, 1);
+ m0.setScale(2, 3, 4);
+ test_map(1, 0, 0, m0, 2, 0, 0);
+ test_map(0, 1, 0, m0, 0, 3, 0);
+ test_map(0, 0, 1, m0, 0, 0, 4);
+ m0.setTranslate(2, 3, 4);
+ test_map(0, 0, 0, m0, 2, 3, 4);
+ m0.preScale(5, 6, 7);
+ test_map(1, 0, 0, m0, 7, 3, 4);
+ test_map(0, 1, 0, m0, 2, 9, 4);
+ test_map(0, 0, 1, m0, 2, 3, 11);
+
+ SkMScalar deg = 45;
+ m0.setRotateDegreesAbout(0, 0, 1, deg);
+ test_map(1, 0, 0, m0, 0.707106769, -0.707106769, 0);
+
+ m0.reset();
+ test_33(m0, 1, 0, 0, 0, 1, 0);
+ m0.setTranslate(3, 4, 5);
+ test_33(m0, 1, 0, 3, 0, 1, 4);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void dump_layers(const SkLayer* layer, int tab = 0) {
+ SkMatrix matrix;
+ SkString matrixStr;
+
+ layer->getLocalTransform(&matrix);
+ matrix.toDumpString(&matrixStr);
+
+ for (int j = 0; j < tab; j++) {
+ SkDebugf(" ");
+ }
+ SkDebugf("layer=%p parent=%p size=[%g %g] transform=%s\n",
+ layer, layer->getParent(), layer->getWidth(), layer->getHeight(),
+ matrixStr.c_str());
+ for (int i = 0; i < layer->countChildren(); i++) {
+ dump_layers(layer->getChild(i), tab + 4);
+ }
+}
+
+class TestLayer : public SkLayer {
+public:
+ TestLayer(SkColor c) : fColor(c) {}
+
+protected:
+ virtual void onDraw(SkCanvas* canvas, SkScalar opacity) {
+ SkRect r;
+ r.set(0, 0, this->getWidth(), this->getHeight());
+
+ SkPaint paint;
+ paint.setColor(fColor);
+ paint.setAlpha(SkScalarRound(opacity * 255));
+
+ canvas->drawRect(r, paint);
+ }
+
+private:
+ SkColor fColor;
+};
+
+class SkLayerView : public SkView {
+private:
+ SkLayer* fRootLayer;
+ SkLayer* fLastChild;
+public:
+ SkLayerView() {
+ test44();
+ static const int W = 600;
+ static const int H = 440;
+ static const struct {
+ int fWidth;
+ int fHeight;
+ SkColor fColor;
+ int fPosX;
+ int fPosY;
+ } gData[] = {
+ { 120, 80, SK_ColorRED, 0, 0 },
+ { 120, 80, SK_ColorGREEN, W - 120, 0 },
+ { 120, 80, SK_ColorBLUE, 0, H - 80 },
+ { 120, 80, SK_ColorMAGENTA, W - 120, H - 80 },
+ };
+
+ fRootLayer = new TestLayer(0xFFDDDDDD);
+ fRootLayer->setSize(W, H);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gData); i++) {
+ SkLayer* child = new TestLayer(gData[i].fColor);
+ child->setSize(gData[i].fWidth, gData[i].fHeight);
+ child->setPosition(gData[i].fPosX, gData[i].fPosY);
+ fRootLayer->addChild(child)->unref();
+ }
+
+ SkLayer* child = new TestLayer(0xFFDD8844);
+ child->setSize(120, 80);
+ child->setPosition(fRootLayer->getWidth()/2 - child->getWidth()/2,
+ fRootLayer->getHeight()/2 - child->getHeight()/2);
+ child->setAnchorPoint(SK_ScalarHalf, SK_ScalarHalf);
+ {
+ SkMatrix m;
+ m.setRotate(SkIntToScalar(30));
+ child->setMatrix(m);
+ }
+ fLastChild = child;
+ fRootLayer->addChild(child)->unref();
+
+ if (false) {
+ SkMatrix matrix;
+ matrix.setScale(0.5, 0.5);
+ fRootLayer->setMatrix(matrix);
+ }
+
+// dump_layers(fRootLayer);
+ }
+
+ virtual ~SkLayerView() {
+ SkSafeUnref(fRootLayer);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "SkLayer");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+
+ canvas->translate(20, 20);
+ fRootLayer->draw(canvas);
+
+ // visual test of getLocalTransform
+ if (true) {
+ SkMatrix matrix;
+ fLastChild->localToGlobal(&matrix);
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(5);
+ paint.setColor(0x88FF0000);
+ canvas->concat(matrix);
+ canvas->drawRect(SkRect::MakeSize(fLastChild->getSize()), paint);
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SkLayerView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp
new file mode 100644
index 0000000000..3b7d05b0d2
--- /dev/null
+++ b/samplecode/SampleSlides.cpp
@@ -0,0 +1,804 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+#define BG_COLOR 0xFFDDDDDD
+
+typedef void (*SlideProc)(SkCanvas*);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+
+static void compose_pe(SkPaint* paint) {
+ SkPathEffect* pe = paint->getPathEffect();
+ SkPathEffect* corner = new SkCornerPathEffect(25);
+ SkPathEffect* compose;
+ if (pe) {
+ compose = new SkComposePathEffect(pe, corner);
+ corner->unref();
+ } else {
+ compose = corner;
+ }
+ paint->setPathEffect(compose)->unref();
+}
+
+static void hair_pe(SkPaint* paint) {
+ paint->setStrokeWidth(0);
+}
+
+static void hair2_pe(SkPaint* paint) {
+ paint->setStrokeWidth(0);
+ compose_pe(paint);
+}
+
+static void stroke_pe(SkPaint* paint) {
+ paint->setStrokeWidth(12);
+ compose_pe(paint);
+}
+
+static void dash_pe(SkPaint* paint) {
+ SkScalar inter[] = { 20, 10, 10, 10 };
+ paint->setStrokeWidth(12);
+ paint->setPathEffect(new SkDashPathEffect(inter, SK_ARRAY_COUNT(inter),
+ 0))->unref();
+ compose_pe(paint);
+}
+
+static const int gXY[] = {
+4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
+};
+
+static void scale(SkPath* path, SkScalar scale) {
+ SkMatrix m;
+ m.setScale(scale, scale);
+ path->transform(m);
+}
+
+static void one_d_pe(SkPaint* paint) {
+ SkPath path;
+ path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
+ for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
+ path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
+ path.close();
+ path.offset(SkIntToScalar(-6), 0);
+ scale(&path, 1.5);
+
+ paint->setPathEffect(new SkPath1DPathEffect(path, SkIntToScalar(21), 0,
+ SkPath1DPathEffect::kRotate_Style))->unref();
+ compose_pe(paint);
+}
+
+typedef void (*PE_Proc)(SkPaint*);
+static const PE_Proc gPE[] = { hair_pe, hair2_pe, stroke_pe, dash_pe, one_d_pe };
+
+static void fill_pe(SkPaint* paint) {
+ paint->setStyle(SkPaint::kFill_Style);
+ paint->setPathEffect(NULL);
+}
+
+static void discrete_pe(SkPaint* paint) {
+ paint->setPathEffect(new SkDiscretePathEffect(10, 4))->unref();
+}
+
+class TilePathEffect : public Sk2DPathEffect {
+ static SkMatrix make_mat() {
+ SkMatrix m;
+ m.setScale(12, 12);
+ return m;
+ }
+public:
+ TilePathEffect() : Sk2DPathEffect(make_mat()) {}
+
+protected:
+ virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+ dst->addCircle(loc.fX, loc.fY, 5);
+ }
+};
+
+static void tile_pe(SkPaint* paint) {
+ paint->setPathEffect(new TilePathEffect)->unref();
+}
+
+static const PE_Proc gPE2[] = { fill_pe, discrete_pe, tile_pe };
+
+static void patheffect_slide(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ path.moveTo(20, 20);
+ path.lineTo(70, 120);
+ path.lineTo(120, 30);
+ path.lineTo(170, 80);
+ path.lineTo(240, 50);
+
+ size_t i;
+ canvas->save();
+ for (i = 0; i < SK_ARRAY_COUNT(gPE); i++) {
+ gPE[i](&paint);
+ canvas->drawPath(path, paint);
+ canvas->translate(0, 75);
+ }
+ canvas->restore();
+
+ path.reset();
+ SkRect r = { 0, 0, 250, 120 };
+ path.addOval(r, SkPath::kCW_Direction);
+ r.inset(50, 50);
+ path.addRect(r, SkPath::kCCW_Direction);
+
+ canvas->translate(320, 20);
+ for (i = 0; i < SK_ARRAY_COUNT(gPE2); i++) {
+ gPE2[i](&paint);
+ canvas->drawPath(path, paint);
+ canvas->translate(0, 160);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+
+struct GradData {
+ int fCount;
+ const SkColor* fColors;
+ const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+static const SkScalar gPos0[] = { 0, SK_Scalar1 };
+static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
+static const SkScalar gPos2[] = {
+0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
+};
+
+static const GradData gGradData[] = {
+{ 2, gColors, NULL },
+{ 2, gColors, gPos0 },
+{ 2, gColors, gPos1 },
+{ 5, gColors, NULL },
+{ 5, gColors, gPos2 }
+};
+
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
+ data.fCount, tm, mapper);
+}
+
+static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+ data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center;
+ center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
+ data.fPos, data.fCount, mapper);
+}
+
+static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper) {
+ SkPoint center0, center1;
+ center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
+ SkScalarAve(pts[0].fY, pts[1].fY));
+ center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
+ SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
+ return SkGradientShader::CreateTwoPointRadial(
+ center1, (pts[1].fX - pts[0].fX) / 7,
+ center0, (pts[1].fX - pts[0].fX) / 2,
+ data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, SkUnitMapper* mapper);
+static const GradMaker gGradMakers[] = {
+ MakeLinear, MakeRadial, MakeSweep, Make2Radial
+};
+
+static void gradient_slide(SkCanvas* canvas) {
+ SkPoint pts[2] = {
+ { 0, 0 },
+ { SkIntToScalar(100), SkIntToScalar(100) }
+ };
+ SkShader::TileMode tm = SkShader::kClamp_TileMode;
+ SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setDither(true);
+
+ canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+ canvas->save();
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
+ SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);
+ paint.setShader(shader);
+ canvas->drawRect(r, paint);
+ shader->unref();
+ canvas->translate(0, SkIntToScalar(120));
+ }
+ canvas->restore();
+ canvas->translate(SkIntToScalar(120), 0);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkPathMeasure.h"
+
+static SkScalar getpathlen(const SkPath& path) {
+ SkPathMeasure meas(path, false);
+ return meas.getLength();
+}
+
+static void textonpath_slide(SkCanvas* canvas) {
+ const char* text = "Displacement";
+ size_t len =strlen(text);
+ SkPath path;
+ path.moveTo(100, 300);
+ path.quadTo(300, 100, 500, 300);
+ path.offset(0, -100);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(40);
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawPath(path, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkScalar x = 50;
+ paint.setColor(0xFF008800);
+ canvas->drawTextOnPathHV(text, len, path,
+ x, paint.getTextSize()*2/3, paint);
+ paint.setColor(SK_ColorRED);
+ canvas->drawTextOnPathHV(text, len, path,
+ x + 60, 0, paint);
+ paint.setColor(SK_ColorBLUE);
+ canvas->drawTextOnPathHV(text, len, path,
+ x + 120, -paint.getTextSize()*2/3, paint);
+
+ path.offset(0, 200);
+ paint.setTextAlign(SkPaint::kRight_Align);
+
+ text = "Matrices";
+ len = strlen(text);
+ SkScalar pathLen = getpathlen(path);
+ SkMatrix matrix;
+
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas->drawPath(path, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ paint.setTextSize(50);
+ canvas->drawTextOnPath(text, len, path, NULL, paint);
+
+ paint.setColor(SK_ColorRED);
+ matrix.setScale(-SK_Scalar1, SK_Scalar1);
+ matrix.postTranslate(pathLen, 0);
+ canvas->drawTextOnPath(text, len, path, &matrix, paint);
+
+ paint.setColor(SK_ColorBLUE);
+ matrix.setScale(SK_Scalar1, -SK_Scalar1);
+ canvas->drawTextOnPath(text, len, path, &matrix, paint);
+
+ paint.setColor(0xFF008800);
+ matrix.setScale(-SK_Scalar1, -SK_Scalar1);
+ matrix.postTranslate(pathLen, 0);
+ canvas->drawTextOnPath(text, len, path, &matrix, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+#include "SkOSFile.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+#include "SkNinePatch.h"
+
+static SkShader* make_shader0(SkIPoint* size) {
+ SkBitmap bm;
+
+ SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
+ size->set(bm.width(), bm.height());
+ return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode);
+}
+
+static SkShader* make_shader1(const SkIPoint& size) {
+ SkPoint pts[] = { { 0, 0 },
+ { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
+ SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
+ return SkGradientShader::CreateLinear(pts, colors, NULL,
+ SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
+}
+
+class Rec {
+public:
+ SkCanvas::VertexMode fMode;
+ int fCount;
+ SkPoint* fVerts;
+ SkPoint* fTexs;
+
+ Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {}
+ ~Rec() { delete[] fVerts; delete[] fTexs; }
+};
+
+void make_tris(Rec* rec) {
+ int n = 10;
+ SkRandom rand;
+
+ rec->fMode = SkCanvas::kTriangles_VertexMode;
+ rec->fCount = n * 3;
+ rec->fVerts = new SkPoint[rec->fCount];
+
+ for (int i = 0; i < n; i++) {
+ SkPoint* v = &rec->fVerts[i*3];
+ for (int j = 0; j < 3; j++) {
+ v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250);
+ }
+ }
+}
+
+void make_fan(Rec* rec, int texWidth, int texHeight) {
+ const SkScalar tx = SkIntToScalar(texWidth);
+ const SkScalar ty = SkIntToScalar(texHeight);
+ const int n = 24;
+
+ rec->fMode = SkCanvas::kTriangleFan_VertexMode;
+ rec->fCount = n + 2;
+ rec->fVerts = new SkPoint[rec->fCount];
+ rec->fTexs = new SkPoint[rec->fCount];
+
+ SkPoint* v = rec->fVerts;
+ SkPoint* t = rec->fTexs;
+
+ v[0].set(0, 0);
+ t[0].set(0, 0);
+ for (int i = 0; i < n; i++) {
+ SkScalar cos;
+ SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+ v[i+1].set(cos, sin);
+ t[i+1].set(i*tx/n, ty);
+ }
+ v[n+1] = v[1];
+ t[n+1].set(tx, ty);
+
+ SkMatrix m;
+ m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+ m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+ m.mapPoints(v, rec->fCount);
+}
+
+void make_strip(Rec* rec, int texWidth, int texHeight) {
+ const SkScalar tx = SkIntToScalar(texWidth);
+ const SkScalar ty = SkIntToScalar(texHeight);
+ const int n = 24;
+
+ rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
+ rec->fCount = 2 * (n + 1);
+ rec->fVerts = new SkPoint[rec->fCount];
+ rec->fTexs = new SkPoint[rec->fCount];
+
+ SkPoint* v = rec->fVerts;
+ SkPoint* t = rec->fTexs;
+
+ for (int i = 0; i < n; i++) {
+ SkScalar cos;
+ SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+ v[i*2 + 0].set(cos/2, sin/2);
+ v[i*2 + 1].set(cos, sin);
+
+ t[i*2 + 0].set(tx * i / n, ty);
+ t[i*2 + 1].set(tx * i / n, 0);
+ }
+ v[2*n + 0] = v[0];
+ v[2*n + 1] = v[1];
+
+ t[2*n + 0].set(tx, ty);
+ t[2*n + 1].set(tx, 0);
+
+ SkMatrix m;
+ m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+ m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+ m.mapPoints(v, rec->fCount);
+}
+
+static void mesh_slide(SkCanvas* canvas) {
+ Rec fRecs[3];
+ SkIPoint size;
+
+ SkShader* fShader0 = make_shader0(&size);
+ SkShader* fShader1 = make_shader1(size);
+
+ SkAutoUnref aur0(fShader0);
+ SkAutoUnref aur1(fShader1);
+
+ make_strip(&fRecs[0], size.fX, size.fY);
+ make_fan(&fRecs[1], size.fX, size.fY);
+ make_tris(&fRecs[2]);
+
+ SkPaint paint;
+ paint.setDither(true);
+ paint.setFilterBitmap(true);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
+ canvas->save();
+
+ paint.setShader(NULL);
+ canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+ fRecs[i].fVerts, fRecs[i].fTexs,
+ NULL, NULL, NULL, 0, paint);
+
+ canvas->translate(SkIntToScalar(210), 0);
+
+ paint.setShader(fShader0);
+ canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+ fRecs[i].fVerts, fRecs[i].fTexs,
+ NULL, NULL, NULL, 0, paint);
+
+ canvas->translate(SkIntToScalar(210), 0);
+
+ paint.setShader(fShader1);
+ canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+ fRecs[i].fVerts, fRecs[i].fTexs,
+ NULL, NULL, NULL, 0, paint);
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(250));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p)
+{
+ p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+ p.setMaskFilter(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+
+ p.setAlpha(0x11);
+ p.setStyle(SkPaint::kFill_Style);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p)
+{
+ rast->addLayer(p);
+
+ p.setAlpha(0x40);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*2);
+ rast->addLayer(p);
+}
+
+static void r2(SkLayerRasterizer* rast, SkPaint& p)
+{
+ p.setStyle(SkPaint::kStrokeAndFill_Style);
+ p.setStrokeWidth(SK_Scalar1*4);
+ rast->addLayer(p);
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*3/2);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p)
+{
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*3);
+ rast->addLayer(p);
+
+ p.setAlpha(0x20);
+ p.setStyle(SkPaint::kFill_Style);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p)
+{
+ p.setAlpha(0x60);
+ rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+ p.setAlpha(0xFF);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+
+ p.setXfermode(NULL);
+ rast->addLayer(p);
+}
+
+#include "SkDiscretePathEffect.h"
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p)
+{
+ rast->addLayer(p);
+
+ p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+ p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+ rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p)
+{
+ rast->addLayer(p);
+
+ p.setAntiAlias(false);
+ SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+ r5(rast2, p);
+ p.setRasterizer(rast2)->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+}
+
+#include "Sk2DPathEffect.h"
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+ Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
+ : Sk2DPathEffect(matrix), fRadius(radius) {}
+
+ virtual void flatten(SkFlattenableWriteBuffer& buffer)
+ {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeScalar(fRadius);
+ }
+ virtual Factory getFactory() { return CreateProc; }
+
+protected:
+ virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
+ {
+ dst->addCircle(loc.fX, loc.fY, fRadius);
+ }
+
+ Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
+ {
+ fRadius = buffer.readScalar();
+ }
+private:
+ SkScalar fRadius;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+ {
+ return new Dot2DPathEffect(buffer);
+ }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p)
+{
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
+ rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p)
+{
+ rast->addLayer(p);
+
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+
+ p.setPathEffect(NULL);
+ p.setXfermode(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+}
+
+class Line2DPathEffect : public Sk2DPathEffect {
+public:
+ Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
+ : Sk2DPathEffect(matrix), fWidth(width) {}
+
+ virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
+ {
+ if (this->INHERITED::filterPath(dst, src, width))
+ {
+ *width = fWidth;
+ return true;
+ }
+ return false;
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+ virtual void flatten(SkFlattenableWriteBuffer& buffer)
+ {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fWidth);
+ }
+protected:
+ virtual void nextSpan(int u, int v, int ucount, SkPath* dst)
+ {
+ if (ucount > 1)
+ {
+ SkPoint src[2], dstP[2];
+
+ src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
+ SkIntToScalar(v) + SK_ScalarHalf);
+ src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
+ SkIntToScalar(v) + SK_ScalarHalf);
+ this->getMatrix().mapPoints(dstP, src, 2);
+
+ dst->moveTo(dstP[0]);
+ dst->lineTo(dstP[1]);
+ }
+ }
+
+ Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
+ {
+ fWidth = buffer.readScalar();
+ }
+
+private:
+ SkScalar fWidth;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
+ {
+ return new Line2DPathEffect(buffer);
+ }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p)
+{
+ rast->addLayer(p);
+
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+ lattice.postRotate(SkIntToScalar(30), 0, 0);
+ p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+
+ p.setPathEffect(NULL);
+ p.setXfermode(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+static void apply_shader(SkPaint* paint, int index) {
+ raster_proc proc = gRastProcs[index];
+ SkPaint p;
+ SkLayerRasterizer* rast = new SkLayerRasterizer;
+
+ p.setAntiAlias(true);
+ proc(rast, p);
+ paint->setRasterizer(rast)->unref();
+ paint->setColor(SK_ColorBLUE);
+}
+
+#include "SkTypeface.h"
+
+static void texteffect_slide(SkCanvas* canvas) {
+ const char* str = "Google";
+ size_t len = strlen(str);
+ SkScalar x = 20;
+ SkScalar y = 80;
+ SkPaint paint;
+ paint.setTypeface(SkTypeface::CreateFromName("Georgia", SkTypeface::kItalic));
+ paint.setTextSize(75);
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorBLUE);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+ apply_shader(&paint, i);
+ canvas->drawText(str, len, x, y, paint);
+ y += 80;
+ if (i == 4) {
+ x += 320;
+ y = 80;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageEncoder.h"
+
+static const SlideProc gProc[] = {
+ patheffect_slide,
+ gradient_slide,
+ textonpath_slide,
+ mesh_slide,
+ texteffect_slide
+};
+
+class SlideView : public SampleView {
+ int fIndex;
+public:
+ SlideView() {
+ fIndex = 0;
+
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config, 1024, 768);
+ bm.allocPixels();
+ SkCanvas canvas(bm);
+ SkScalar s = SkIntToScalar(1024) / 640;
+ canvas.scale(s, s);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); i++) {
+ canvas.save();
+ canvas.drawColor(BG_COLOR);
+ gProc[i](&canvas);
+ canvas.restore();
+ SkString str;
+ str.printf("/skimages/slide_%d.png", i);
+ SkImageEncoder::EncodeFile(str.c_str(), bm, SkImageEncoder::kPNG_Type, 100);
+ }
+ this->setBGColor(BG_COLOR);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Slides");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ gProc[fIndex](canvas);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fIndex = (fIndex + 1) % SK_ARRAY_COUNT(gProc);
+ this->inval(NULL);
+ return NULL;
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SlideView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleSpiral.cpp b/samplecode/SampleSpiral.cpp
new file mode 100644
index 0000000000..1a41440c6a
--- /dev/null
+++ b/samplecode/SampleSpiral.cpp
@@ -0,0 +1,56 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+class SpiralView : public SampleView {
+public:
+ SpiralView() {
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Spiral");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkRect r;
+ SkScalar l,t,x,y;
+ l = SampleCode::GetAnimScalar(SkIntToScalar(10),
+ SkIntToScalar(400));
+ t = SampleCode::GetAnimScalar(SkIntToScalar(5),
+ SkIntToScalar(200));
+
+ canvas->translate(320,240);
+ for (int i = 0; i < 35; i++) {
+ paint.setColor(0xFFF00FF0 - i * 0x04000000);
+ SkScalar step = SK_ScalarPI / (55 - i);
+ SkScalar angle = t * step;
+ x = (20 + SkIntToScalar(i) * 5) * SkScalarSinCos(angle, &y);
+ y *= (20 + SkIntToScalar(i) * 5);
+ r.set(x, y, x + SkIntToScalar(10), y + SkIntToScalar(10));
+ canvas->drawRect(r, paint);
+ }
+
+ this->inval(NULL);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new SpiralView; }
+static SkViewRegister reg(MyFactory); \ No newline at end of file
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
new file mode 100644
index 0000000000..ae630ef30e
--- /dev/null
+++ b/samplecode/SampleStrokePath.cpp
@@ -0,0 +1,217 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkParsePath.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkView.h"
+
+#include "SkBlurMaskFilter.h"
+
+static void test_huge_stroke(SkCanvas* canvas) {
+ SkRect srcR = { 0, 0, 72000, 54000 };
+ SkRect dstR = { 0, 0, 640, 480 };
+
+ SkPath path;
+ path.moveTo(17600, 8000);
+ path.lineTo(52800, 8000);
+ path.lineTo(52800, 41600);
+ path.lineTo(17600, 41600);
+ path.close();
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(8000);
+ paint.setStrokeMiter(10);
+ paint.setStrokeCap(SkPaint::kButt_Cap);
+ paint.setStrokeJoin(SkPaint::kRound_Join);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkMatrix matrix;
+ matrix.setRectToRect(srcR, dstR, SkMatrix::kCenter_ScaleToFit);
+ canvas->concat(matrix);
+
+ canvas->drawPath(path, paint);
+}
+
+#if 0
+#include "SkBlurMask.h"
+static void test_blur() {
+ uint8_t cell[9];
+ memset(cell, 0xFF, sizeof(cell));
+ SkMask src;
+ src.fImage = cell;
+ src.fFormat = SkMask::kA8_Format;
+ SkMask dst;
+
+ for (int y = 1; y <= 3; y++) {
+ for (int x = 1; x <= 3; x++) {
+ src.fBounds.set(0, 0, x, y);
+ src.fRowBytes = src.fBounds.width();
+
+ SkScalar radius = 1.f;
+
+ printf("src [%d %d %d %d] radius %g\n", src.fBounds.fLeft, src.fBounds.fTop,
+ src.fBounds.fRight, src.fBounds.fBottom, radius);
+
+ SkBlurMask::Blur(&dst, src, radius, SkBlurMask::kNormal_Style);
+ uint8_t* dstPtr = dst.fImage;
+
+ for (int y = 0; y < dst.fBounds.height(); y++) {
+ for (int x = 0; x < dst.fBounds.width(); x++) {
+ printf(" %02X", dstPtr[x]);
+ }
+ printf("\n");
+ dstPtr += dst.fRowBytes;
+ }
+ }
+ }
+}
+#endif
+
+static void scale_to_width(SkPath* path, SkScalar dstWidth) {
+ const SkRect& bounds = path->getBounds();
+ SkScalar scale = dstWidth / bounds.width();
+ SkMatrix matrix;
+
+ matrix.setScale(scale, scale);
+ path->transform(matrix);
+}
+
+static const struct {
+ SkPaint::Style fStyle;
+ SkPaint::Join fJoin;
+ int fStrokeWidth;
+} gRec[] = {
+ { SkPaint::kFill_Style, SkPaint::kMiter_Join, 0 },
+ { SkPaint::kStroke_Style, SkPaint::kMiter_Join, 0 },
+ { SkPaint::kStroke_Style, SkPaint::kMiter_Join, 10 },
+ { SkPaint::kStrokeAndFill_Style, SkPaint::kMiter_Join, 10 },
+};
+
+class StrokePathView : public SampleView {
+ SkScalar fWidth;
+ SkPath fPath;
+public:
+ StrokePathView() {
+// test_blur();
+ fWidth = SkIntToScalar(120);
+
+#if 0
+ const char str[] =
+ "M 0, 3"
+ "C 10, -10, 30, -10, 0, 28"
+ "C -30, -10, -10, -10, 0, 3"
+ "Z";
+ SkParsePath::FromSVGString(str, &fPath);
+#else
+ fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction);
+ fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction);
+#endif
+
+ scale_to_width(&fPath, fWidth);
+ const SkRect& bounds = fPath.getBounds();
+ fPath.offset(-bounds.fLeft, -bounds.fTop);
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "StrokePath");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ SkRandom rand;
+
+ void drawSet(SkCanvas* canvas, SkPaint* paint) {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+ paint->setStyle(gRec[i].fStyle);
+ paint->setStrokeJoin(gRec[i].fJoin);
+ paint->setStrokeWidth(SkIntToScalar(gRec[i].fStrokeWidth));
+ canvas->drawPath(fPath, *paint);
+ canvas->translate(fWidth * 5 / 4, 0);
+ }
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ test_huge_stroke(canvas); return;
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ if (true) {
+ canvas->drawColor(SK_ColorBLACK);
+
+ paint.setTextSize(24);
+ paint.setColor(SK_ColorWHITE);
+ canvas->translate(10, 30);
+
+ static const SkBlurMaskFilter::BlurStyle gStyle[] = {
+ SkBlurMaskFilter::kNormal_BlurStyle,
+ SkBlurMaskFilter::kInner_BlurStyle,
+ SkBlurMaskFilter::kOuter_BlurStyle,
+ SkBlurMaskFilter::kSolid_BlurStyle,
+ };
+ for (int x = 0; x < 5; x++) {
+ SkMaskFilter* mf;
+ SkScalar radius = 4;
+ for (int y = 0; y < 10; y++) {
+ if (x) {
+ mf = SkBlurMaskFilter::Create(radius, gStyle[x - 1]);
+ paint.setMaskFilter(mf)->unref();
+ }
+ canvas->drawText("Title Bar", 9, x*SkIntToScalar(100), y*SkIntToScalar(30), paint);
+ radius *= 0.75f;
+ }
+
+ }
+ return;
+ }
+
+ paint.setColor(SK_ColorBLUE);
+
+#if 1
+ SkPath p;
+ float r = rand.nextUScalar1() + 0.5f;
+ SkScalar x = 0, y = 0;
+ p.moveTo(x, y);
+#if 0
+ p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r);
+ p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y);
+#else
+ p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r);
+ p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y);
+#endif
+ p.close();
+ fPath = p;
+ fPath.offset(100, 0);
+#endif
+
+ fPath.setFillType(SkPath::kWinding_FillType);
+ drawSet(canvas, &paint);
+
+ canvas->translate(0, fPath.getBounds().height() * 5 / 4);
+ fPath.setFillType(SkPath::kEvenOdd_FillType);
+ drawSet(canvas, &paint);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new StrokePathView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleStrokeRect.cpp b/samplecode/SampleStrokeRect.cpp
new file mode 100644
index 0000000000..20c9e2ce88
--- /dev/null
+++ b/samplecode/SampleStrokeRect.cpp
@@ -0,0 +1,69 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+
+class StrokeRectSample : public SampleView {
+public:
+ StrokeRectSample() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Stroke Rects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(20));
+
+ SkPaint hair;
+ hair.setStyle(SkPaint::kStroke_Style);
+ hair.setColor(SK_ColorRED);
+
+ static const SkISize gSize[] = {
+ { 100, 50 },
+ { 100, 0 },
+ { 0, 50 },
+ { 0, 0 }
+ };
+
+ static const SkPaint::Join gJoin[] = {
+ SkPaint::kMiter_Join,
+ SkPaint::kRound_Join,
+ SkPaint::kBevel_Join
+ };
+
+ canvas->translate(paint.getStrokeWidth(), paint.getStrokeWidth());
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gJoin); ++i) {
+ paint.setStrokeJoin(gJoin[i]);
+
+ canvas->save();
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gSize); ++j) {
+ SkRect r = SkRect::MakeWH(SkIntToScalar(gSize[j].fWidth),
+ SkIntToScalar(gSize[j].fHeight));
+ canvas->drawRect(r, paint);
+ canvas->drawRect(r, hair);
+ canvas->translate(0, SkIntToScalar(100));
+ }
+ canvas->restore();
+ canvas->translate(SkIntToScalar(150), 0);
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new StrokeRectSample; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleStrokeText.cpp b/samplecode/SampleStrokeText.cpp
new file mode 100644
index 0000000000..bcb9e4fca0
--- /dev/null
+++ b/samplecode/SampleStrokeText.cpp
@@ -0,0 +1,140 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+static void lettersToBitmap(SkBitmap* dst, const char chars[],
+ const SkPaint& original, SkBitmap::Config config) {
+ SkPath path;
+ SkScalar x = 0;
+ SkScalar width;
+ SkPath p;
+ for (size_t i = 0; i < strlen(chars); i++) {
+ original.getTextPath(&chars[i], 1, x, 0, &p);
+ path.addPath(p);
+ original.getTextWidths(&chars[i], 1, &width);
+ x += width;
+ }
+ SkRect bounds = path.getBounds();
+ SkScalar sw = -original.getStrokeWidth();
+ bounds.inset(sw, sw);
+ path.offset(-bounds.fLeft, -bounds.fTop);
+ bounds.offset(-bounds.fLeft, -bounds.fTop);
+
+ int w = SkScalarRound(bounds.width());
+ int h = SkScalarRound(bounds.height());
+ SkPaint paint(original);
+ SkBitmap src;
+ src.setConfig(config, w, h);
+ src.allocPixels();
+ src.eraseColor(0);
+ {
+ SkCanvas canvas(src);
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas.drawPath(path, paint);
+ }
+
+ dst->setConfig(config, w, h);
+ dst->allocPixels();
+ dst->eraseColor(SK_ColorWHITE);
+ {
+ SkCanvas canvas(*dst);
+ paint.setXfermodeMode(SkXfermode::kDstATop_Mode);
+ canvas.drawBitmap(src, 0, 0, &paint);
+ paint.setColor(original.getColor());
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas.drawPath(path, paint);
+ }
+}
+
+static void lettersToBitmap2(SkBitmap* dst, const char chars[],
+ const SkPaint& original, SkBitmap::Config config) {
+ SkPath path;
+ SkScalar x = 0;
+ SkScalar width;
+ SkPath p;
+ for (size_t i = 0; i < strlen(chars); i++) {
+ original.getTextPath(&chars[i], 1, x, 0, &p);
+ path.addPath(p);
+ original.getTextWidths(&chars[i], 1, &width);
+ x += width;
+ }
+ SkRect bounds = path.getBounds();
+ SkScalar sw = -original.getStrokeWidth();
+ bounds.inset(sw, sw);
+ path.offset(-bounds.fLeft, -bounds.fTop);
+ bounds.offset(-bounds.fLeft, -bounds.fTop);
+
+ int w = SkScalarRound(bounds.width());
+ int h = SkScalarRound(bounds.height());
+ SkPaint paint(original);
+
+ paint.setAntiAlias(true);
+ paint.setXfermodeMode(SkXfermode::kDstATop_Mode);
+ paint.setColor(original.getColor());
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ dst->setConfig(config, w, h);
+ dst->allocPixels();
+ dst->eraseColor(SK_ColorWHITE);
+
+ SkCanvas canvas(*dst);
+ canvas.drawPath(path, paint);
+}
+
+class StrokeTextView : public SampleView {
+ bool fAA;
+public:
+ StrokeTextView() : fAA(false) {
+ this->setBGColor(0xFFCC8844);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "StrokeText");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkBitmap bm;
+ SkPaint paint;
+
+ paint.setStrokeWidth(SkIntToScalar(6));
+ paint.setTextSize(SkIntToScalar(80));
+// paint.setTypeface(Typeface.DEFAULT_BOLD);
+
+ lettersToBitmap(&bm, "Test Case", paint, SkBitmap::kARGB_4444_Config);
+
+ canvas->drawBitmap(bm, 0, 0);
+ }
+
+private:
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new StrokeTextView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTests.cpp b/samplecode/SampleTests.cpp
new file mode 100644
index 0000000000..4ce86403f6
--- /dev/null
+++ b/samplecode/SampleTests.cpp
@@ -0,0 +1,111 @@
+utils#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+#include "test.h"
+
+namespace skiatest {
+
+class MyReporter : public Reporter {
+protected:
+ virtual void onStart(Test* test) {}
+ virtual void onReport(const char desc[], Reporter::Result result) {
+ SkASSERT(Reporter::kPassed == result);
+ }
+ virtual void onEnd(Test* test) {}
+};
+
+class Iter {
+public:
+ Iter(Reporter* r) : fReporter(r) {
+ r->ref();
+ fReg = TestRegistry::Head();
+ }
+
+ ~Iter() {
+ fReporter->unref();
+ }
+
+ Test* next() {
+ if (fReg) {
+ TestRegistry::Factory fact = fReg->factory();
+ fReg = fReg->next();
+ Test* test = fact(NULL);
+ test->setReporter(fReporter);
+ return test;
+ }
+ return NULL;
+ }
+
+ static int Count() {
+ const TestRegistry* reg = TestRegistry::Head();
+ int count = 0;
+ while (reg) {
+ count += 1;
+ reg = reg->next();
+ }
+ return count;
+ }
+
+private:
+ Reporter* fReporter;
+ const TestRegistry* fReg;
+};
+}
+
+class TestsView : public SkView {
+public:
+ TestsView() {}
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Tests");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ skiatest::MyReporter reporter;
+ skiatest::Iter iter(&reporter);
+ skiatest::Test* test;
+
+ while ((test = iter.next()) != NULL) {
+ test->run();
+ SkDELETE(test);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ this->inval(NULL);
+
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ this->inval(NULL);
+ return this->INHERITED::onClick(click);
+ }
+
+ virtual bool handleKey(SkKey key) {
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TestsView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleText.cpp b/samplecode/SampleText.cpp
new file mode 100644
index 0000000000..267653006c
--- /dev/null
+++ b/samplecode/SampleText.cpp
@@ -0,0 +1,396 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+
+static const int gKernel[3][3] = {
+// { -1, -2, -1 }, { -2, 12, -2 }, { -1, -2, -1 }
+ { 1, 2, 1 }, { 2, 64-12, 2 }, { 1, 2, 1 }
+};
+static const int gShift = 6;
+
+class ReduceNoise : public SkKernel33ProcMaskFilter {
+public:
+ ReduceNoise(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
+ virtual uint8_t computeValue(uint8_t* const* srcRows)
+ {
+ int c = srcRows[1][1];
+ int min = 255, max = 0;
+ for (int i = 0; i < 3; i++)
+ for (int j = 0; j < 3; j++)
+ if (i != 1 || j != 1)
+ {
+ int v = srcRows[i][j];
+ if (max < v)
+ max = v;
+ if (min > v)
+ min = v;
+ }
+ if (c > max) c = max;
+ // if (c < min) c = min;
+ return c;
+ }
+ virtual Factory getFactory() { return Create; }
+private:
+ ReduceNoise(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
+ static SkFlattenable* Create(SkFlattenableReadBuffer& rb) {
+ return new ReduceNoise(rb);
+ }
+};
+
+class Darken : public SkKernel33ProcMaskFilter {
+public:
+ Darken(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
+ virtual uint8_t computeValue(uint8_t* const* srcRows)
+ {
+ int c = srcRows[1][1];
+ float f = c / 255.f;
+
+ if (c >= 0) {
+ f = sqrtf(f);
+ } else {
+ f *= f;
+ }
+ SkASSERT(f >= 0 && f <= 1);
+ return (int)(f * 255);
+ }
+ virtual Factory getFactory() { return Create; }
+private:
+ Darken(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
+ static SkFlattenable* Create(SkFlattenableReadBuffer& rb) {
+ return new Darken(rb);
+ }
+};
+
+static SkMaskFilter* makemf() { return new Darken(0x30); }
+
+static void test_breakText() {
+ SkPaint paint;
+ const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj";
+ size_t length = strlen(text);
+ SkScalar width = paint.measureText(text, length);
+
+ SkScalar mm = 0;
+ SkScalar nn = 0;
+ for (SkScalar w = 0; w <= width; w += SK_Scalar1) {
+ SkScalar m;
+ size_t n = paint.breakText(text, length, w, &m,
+ SkPaint::kBackward_TextBufferDirection);
+
+ SkASSERT(n <= length);
+ SkASSERT(m <= width);
+
+ if (n == 0) {
+ SkASSERT(m == 0);
+ } else {
+ // now assert that we're monotonic
+ if (n == nn) {
+ SkASSERT(m == mm);
+ } else {
+ SkASSERT(n > nn);
+ SkASSERT(m > mm);
+ }
+ }
+ nn = SkIntToScalar(n);
+ mm = m;
+ }
+
+ SkDEBUGCODE(size_t length2 =) paint.breakText(text, length, width, &mm);
+ SkASSERT(length2 == length);
+ SkASSERT(mm == width);
+}
+
+static SkRandom gRand;
+
+class SkPowerMode : public SkXfermode {
+public:
+ SkPowerMode(SkScalar exponent) { this->init(exponent); }
+
+ virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]);
+
+ typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
+
+ // overrides for SkFlattenable
+ virtual Factory getFactory() { return Create; }
+ virtual void flatten(SkFlattenableWriteBuffer& b) {
+ // this->INHERITED::flatten(b); How can we know if this is legal????
+ b.write32(SkScalarToFixed(fExp));
+ }
+
+private:
+ SkScalar fExp; // user's value
+ uint8_t fTable[256]; // cache
+
+ void init(SkScalar exponent);
+ SkPowerMode(SkFlattenableReadBuffer& b) : SkXfermode(b) {
+ // read the exponent
+ this->init(SkFixedToScalar(b.readS32()));
+ }
+ static SkFlattenable* Create(SkFlattenableReadBuffer& b) {
+ return SkNEW_ARGS(SkPowerMode, (b));
+ }
+
+ typedef SkXfermode INHERITED;
+};
+
+void SkPowerMode::init(SkScalar e) {
+ fExp = e;
+ float ee = SkScalarToFloat(e);
+
+ printf("------ %g\n", ee);
+ for (int i = 0; i < 256; i++) {
+ float x = i / 255.f;
+ // printf(" %d %g", i, x);
+ x = powf(x, ee);
+ // printf(" %g", x);
+ int xx = SkScalarRound(SkFloatToScalar(x * 255));
+ // printf(" %d\n", xx);
+ fTable[i] = SkToU8(xx);
+ }
+}
+
+void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) {
+ for (int i = 0; i < count; i++) {
+ SkPMColor c = src[i];
+ int r = SkGetPackedR32(c);
+ int g = SkGetPackedG32(c);
+ int b = SkGetPackedB32(c);
+ r = fTable[r];
+ g = fTable[g];
+ b = fTable[b];
+ dst[i] = SkPack888ToRGB16(r, g, b);
+ }
+}
+
+static const struct {
+ const char* fName;
+ uint32_t fFlags;
+ bool fFlushCache;
+} gHints[] = {
+ { "Linear", SkPaint::kLinearText_Flag, false },
+ { "Normal", 0, true },
+ { "Subpixel", SkPaint::kSubpixelText_Flag, true }
+};
+
+static int count_char_points(const SkPaint& paint, char c) {
+ SkPath path;
+
+ paint.getTextPath(&c, 1, 0, 0, &path);
+ return path.getPoints(NULL, 0);
+}
+
+static int gOld, gNew, gCount;
+
+static void dump(int c, int oldc, int newc) {
+ if (oldc != newc) {
+ gOld += oldc;
+ gNew += newc;
+ gCount += 1;
+ printf("char %c: old = %3d, new = %3d, reduction %g%%\n", c, oldc, newc, 100. * (oldc - newc) / oldc);
+ }
+}
+
+static void tab(int n) {
+// printf("[%d] ", n); return;
+ SkASSERT(n >= 0);
+ for (int i = 0; i < n; i++)
+ printf(" ");
+}
+
+static void draw_rgn(const SkRegion& rgn, SkCanvas* canvas, const SkPaint& paint) {
+ SkRect r;
+ SkRegion::Iterator iter(rgn);
+
+ for (; !iter.done(); iter.next()) {
+ r.set(iter.rect());
+ canvas->drawRect(r, paint);
+ }
+}
+
+static void test_break(SkCanvas* canvas, const char text[], size_t length,
+ SkScalar x, SkScalar y, const SkPaint& paint,
+ SkScalar clickX) {
+ SkPaint linePaint;
+
+ linePaint.setAntiAlias(true);
+
+ SkScalar measured;
+
+ if (paint.breakText(text, length, clickX - x, &measured,
+ SkPaint::kForward_TextBufferDirection)) {
+ linePaint.setColor(SK_ColorRED);
+ canvas->drawLine(x, y, x + measured, y, linePaint);
+ }
+
+ x += paint.measureText(text, length);
+ if (paint.breakText(text, length, x - clickX, &measured,
+ SkPaint::kBackward_TextBufferDirection)) {
+ linePaint.setColor(SK_ColorBLUE);
+ canvas->drawLine(x - measured, y, x, y, linePaint);
+ }
+}
+
+static void DrawTheText(SkCanvas* canvas, const char text[], size_t length,
+ SkScalar x, SkScalar y, const SkPaint& paint,
+ SkScalar clickX, SkMaskFilter* mf) {
+ SkPaint p(paint);
+
+#if 0
+ canvas->drawText(text, length, x, y, paint);
+#else
+ {
+ SkPoint pts[1000];
+ SkScalar xpos = x;
+ SkASSERT(length <= SK_ARRAY_COUNT(pts));
+ for (size_t i = 0; i < length; i++) {
+ pts[i].set(xpos, y), xpos += paint.getTextSize();
+ }
+ canvas->drawPosText(text, length, pts, paint);
+ }
+#endif
+
+ p.setSubpixelText(true);
+ x += SkIntToScalar(180);
+ canvas->drawText(text, length, x, y, p);
+
+#ifdef SK_DEBUG
+ if (true) {
+ // p.setMaskFilter(mf);
+ p.setSubpixelText(false);
+ p.setLinearText(true);
+ x += SkIntToScalar(180);
+ canvas->drawText(text, length, x, y, p);
+ }
+#endif
+}
+
+class TextSpeedView : public SampleView {
+public:
+ TextSpeedView() {
+ fMF = makemf();
+
+ fHints = 0;
+ fClickX = 0;
+
+ test_breakText();
+ }
+
+ virtual ~TextSpeedView() {
+ SkSafeUnref(fMF);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Text");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static void make_textstrip(SkBitmap* bm) {
+ bm->setConfig(SkBitmap::kRGB_565_Config, 200, 18);
+ bm->allocPixels();
+ bm->eraseColor(SK_ColorWHITE);
+
+ SkCanvas canvas(*bm);
+ SkPaint paint;
+ const char* s = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit";
+
+ paint.setFlags(paint.getFlags() | SkPaint::kAntiAlias_Flag
+ | SkPaint::kDevKernText_Flag);
+ paint.setTextSize(SkIntToScalar(14));
+ canvas.drawText(s, strlen(s), SkIntToScalar(8), SkIntToScalar(14), paint);
+ }
+
+ static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
+ for (size_t i = 0; i < n; i++)
+ pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkAutoCanvasRestore restore(canvas, false);
+ {
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(1000), SkIntToScalar(20));
+ // canvas->saveLayer(&r, NULL, SkCanvas::kHasAlphaLayer_SaveFlag);
+ }
+
+ SkPaint paint;
+// const uint16_t glyphs[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 };
+ int index = fHints % SK_ARRAY_COUNT(gHints);
+ index = 1;
+// const char* style = gHints[index].fName;
+
+// canvas->translate(0, SkIntToScalar(50));
+
+ // canvas->drawText(style, strlen(style), SkIntToScalar(20), SkIntToScalar(20), paint);
+
+ SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromFile("/skimages/samplefont.ttf")));
+ paint.setAntiAlias(true);
+ paint.setFlags(paint.getFlags() | gHints[index].fFlags);
+
+ SkRect clip;
+ clip.set(SkIntToScalar(25), SkIntToScalar(34), SkIntToScalar(88), SkIntToScalar(155));
+
+ const char* text = "Hamburgefons";
+ size_t length = strlen(text);
+
+ SkScalar y = SkIntToScalar(0);
+ for (int i = 9; i <= 24; i++) {
+ paint.setTextSize(SkIntToScalar(i) /*+ (gRand.nextU() & 0xFFFF)*/);
+ for (SkScalar dx = 0; dx <= SkIntToScalar(3)/4;
+ dx += SkIntToScalar(1) /* /4 */) {
+ y += paint.getFontSpacing();
+ DrawTheText(canvas, text, length, SkIntToScalar(20) + dx, y,
+ paint, fClickX, fMF);
+ }
+ }
+ if (gHints[index].fFlushCache) {
+// SkGraphics::SetFontCacheUsed(0);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fClickX = x;
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ int fHints;
+ SkScalar fClickX;
+ SkMaskFilter* fMF;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextSpeedView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp
new file mode 100644
index 0000000000..ccfed68e6d
--- /dev/null
+++ b/samplecode/SampleTextAlpha.cpp
@@ -0,0 +1,114 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkOSFile.h"
+#include "SkStream.h"
+
+static void check_for_nonwhite(const SkBitmap& bm, int alpha) {
+ if (bm.config() != SkBitmap::kRGB_565_Config) {
+ return;
+ }
+
+ for (int y = 0; y < bm.height(); y++) {
+ for (int x = 0; x < bm.width(); x++) {
+ uint16_t c = *bm.getAddr16(x, y);
+ if (c != 0xFFFF) {
+ SkDebugf("------ nonwhite alpha=%x [%d %d] %x\n", alpha, x, y, c);
+ return;
+ }
+ }
+ }
+}
+
+class TextAlphaView : public SampleView {
+public:
+ TextAlphaView() {
+ fByte = 0xFF;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SkString str("TextAlpha");
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ SkPaint paint;
+ SkScalar x = SkIntToScalar(10);
+ SkScalar y = SkIntToScalar(20);
+
+ paint.setFlags(0x105);
+
+ paint.setARGB(fByte, 0xFF, 0xFF, 0xFF);
+
+ paint.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+ SkBlurMaskFilter::kNormal_BlurStyle));
+ paint.getMaskFilter()->unref();
+
+ SkRandom rand;
+
+ for (int ps = 6; ps <= 35; ps++) {
+ paint.setColor(rand.nextU() | (0xFF << 24));
+ paint.setTextSize(SkIntToScalar(ps));
+ paint.setTextSize(SkIntToScalar(24));
+ canvas->drawText(str, strlen(str), x, y, paint);
+ y += paint.getFontMetrics(NULL);
+ }
+ //check_for_nonwhite(canvas->getDevice()->accessBitmap(), fByte);
+ //SkDebugf("------ byte %x\n", fByte);
+
+ if (false) {
+ fByte += 1;
+ fByte &= 0xFF;
+ this->inval(NULL);
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ return new Click(this);
+ }
+
+ virtual bool onClick(Click* click) {
+ int y = click->fICurr.fY;
+ if (y < 0) {
+ y = 0;
+ } else if (y > 255) {
+ y = 255;
+ }
+ fByte = y;
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ int fByte;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextAlphaView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextBox.cpp b/samplecode/SampleTextBox.cpp
new file mode 100644
index 0000000000..37a6be0936
--- /dev/null
+++ b/samplecode/SampleTextBox.cpp
@@ -0,0 +1,91 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkTextBox.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkKey.h"
+
+#ifdef SK_BUILD_FOR_WIN
+extern SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
+#endif
+
+static const char gText[] =
+ "When in the Course of human events it becomes necessary for one people "
+ "to dissolve the political bands which have connected them with another "
+ "and to assume among the powers of the earth, the separate and equal "
+ "station to which the Laws of Nature and of Nature's God entitle them, "
+ "a decent respect to the opinions of mankind requires that they should "
+ "declare the causes which impel them to the separation.";
+
+class TextBoxView : public SampleView {
+public:
+ TextBoxView() {
+#ifdef SK_BUILD_FOR_WIN
+ LOGFONT lf;
+ sk_bzero(&lf, sizeof(lf));
+ lf.lfHeight = 9;
+ SkTypeface* tf0 = SkCreateTypefaceFromLOGFONT(lf);
+ lf.lfHeight = 12;
+ SkTypeface* tf1 = SkCreateTypefaceFromLOGFONT(lf);
+ // we assert that different sizes should not affect which face we get
+ SkASSERT(tf0 == tf1);
+ tf0->unref();
+ tf1->unref();
+#endif
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SkString str("TextBox");
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkScalar margin = 20;
+ SkTextBox tbox;
+ tbox.setMode(SkTextBox::kLineBreak_Mode);
+ tbox.setBox(margin, margin,
+ this->width() - margin, this->height() - margin);
+ tbox.setSpacing(SkIntToScalar(3)/3, 0);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setLCDRenderText(true);
+ tbox.setText(gText, strlen(gText), paint);
+
+ for (int i = 9; i < 24; i += 2) {
+ paint.setTextSize(SkIntToScalar(i));
+ tbox.draw(canvas);
+ canvas->translate(0, tbox.getTextHeight() + paint.getFontSpacing());
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextBoxView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextEffects.cpp b/samplecode/SampleTextEffects.cpp
new file mode 100644
index 0000000000..f256b2ef18
--- /dev/null
+++ b/samplecode/SampleTextEffects.cpp
@@ -0,0 +1,397 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+static inline SkPMColor rgb2gray(SkPMColor c) {
+ unsigned r = SkGetPackedR32(c);
+ unsigned g = SkGetPackedG32(c);
+ unsigned b = SkGetPackedB32(c);
+
+ unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
+
+ return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
+}
+
+class SkGrayScaleColorFilter : public SkColorFilter {
+public:
+ virtual void filterSpan(const SkPMColor src[], int count,
+ SkPMColor result[]) {
+ for (int i = 0; i < count; i++) {
+ result[i] = rgb2gray(src[i]);
+ }
+ }
+};
+
+class SkChannelMaskColorFilter : public SkColorFilter {
+public:
+ SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
+ fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
+ }
+
+ virtual void filterSpan(const SkPMColor src[], int count,
+ SkPMColor result[]) {
+ SkPMColor mask = fMask;
+ for (int i = 0; i < count; i++) {
+ result[i] = src[i] & mask;
+ }
+ }
+
+private:
+ SkPMColor fMask;
+};
+
+///////////////////////////////////////////////////////////
+
+#include "SkGradientShader.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+ SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+ rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+ p.setMaskFilter(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+
+ p.setAlpha(0x11);
+ p.setStyle(SkPaint::kFill_Style);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ p.setAlpha(0x40);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*2);
+ rast->addLayer(p);
+}
+
+static void r2(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setStyle(SkPaint::kStrokeAndFill_Style);
+ p.setStrokeWidth(SK_Scalar1*4);
+ rast->addLayer(p);
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*3/2);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1*3);
+ rast->addLayer(p);
+
+ p.setAlpha(0x20);
+ p.setStyle(SkPaint::kFill_Style);
+ p.setXfermodeMode(SkXfermode::kSrc_Mode);
+ rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p) {
+ p.setAlpha(0x60);
+ rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+ p.setAlpha(0xFF);
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+
+ p.setXfermode(NULL);
+ rast->addLayer(p);
+}
+
+#include "SkDiscretePathEffect.h"
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+ p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+ rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ p.setAntiAlias(false);
+ SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+ r5(rast2, p);
+ p.setRasterizer(rast2)->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+}
+
+#include "Sk2DPathEffect.h"
+
+class Dot2DPathEffect : public Sk2DPathEffect {
+public:
+ Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
+ : Sk2DPathEffect(matrix), fRadius(radius) {}
+
+ virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+
+ buffer.writeScalar(fRadius);
+ }
+ virtual Factory getFactory() { return CreateProc; }
+
+protected:
+ virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+ dst->addCircle(loc.fX, loc.fY, fRadius);
+ }
+
+ Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+ fRadius = buffer.readScalar();
+ }
+private:
+ SkScalar fRadius;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return new Dot2DPathEffect(buffer);
+ }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p) {
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
+ rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+ lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+ p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+
+ p.setPathEffect(NULL);
+ p.setXfermode(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+}
+
+class Line2DPathEffect : public Sk2DPathEffect {
+public:
+ Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
+ : Sk2DPathEffect(matrix), fWidth(width) {}
+
+ virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+ if (this->INHERITED::filterPath(dst, src, width)) {
+ *width = fWidth;
+ return true;
+ }
+ return false;
+ }
+
+ virtual Factory getFactory() { return CreateProc; }
+ virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+ this->INHERITED::flatten(buffer);
+ buffer.writeScalar(fWidth);
+ }
+
+protected:
+ virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
+ if (ucount > 1) {
+ SkPoint src[2], dstP[2];
+
+ src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
+ SkIntToScalar(v) + SK_ScalarHalf);
+ src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
+ SkIntToScalar(v) + SK_ScalarHalf);
+ this->getMatrix().mapPoints(dstP, src, 2);
+
+ dst->moveTo(dstP[0]);
+ dst->lineTo(dstP[1]);
+ }
+ }
+
+ Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
+ fWidth = buffer.readScalar();
+ }
+
+private:
+ SkScalar fWidth;
+
+ static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
+ return new Line2DPathEffect(buffer);
+ }
+
+ typedef Sk2DPathEffect INHERITED;
+};
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p) {
+ rast->addLayer(p);
+
+ SkMatrix lattice;
+ lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+ lattice.postRotate(SkIntToScalar(30), 0, 0);
+ p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
+ p.setXfermodeMode(SkXfermode::kClear_Mode);
+ rast->addLayer(p);
+
+ p.setPathEffect(NULL);
+ p.setXfermode(NULL);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+static const struct {
+ SkColor fMul, fAdd;
+} gLightingColors[] = {
+ { 0x808080, 0x800000 }, // general case
+ { 0x707070, 0x707070 }, // no-pin case
+ { 0xFFFFFF, 0x800000 }, // just-add case
+ { 0x808080, 0x000000 }, // just-mul case
+ { 0xFFFFFF, 0x000000 } // identity case
+};
+
+#include "SkXfermode.h"
+
+static unsigned color_dist16(uint16_t a, uint16_t b) {
+ unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b));
+ unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b));
+ unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b));
+
+ return SkMax32(dr, SkMax32(dg, db));
+}
+
+static unsigned scale_dist(unsigned dist, unsigned scale) {
+ dist >>= 6;
+ dist = (dist << 2) | dist;
+ dist = (dist << 4) | dist;
+ return dist;
+
+// return SkAlphaMul(dist, scale);
+}
+
+static void apply_shader(SkPaint* paint, int index) {
+ raster_proc proc = gRastProcs[index];
+ if (proc)
+ {
+ SkPaint p;
+ SkLayerRasterizer* rast = new SkLayerRasterizer;
+
+ p.setAntiAlias(true);
+ proc(rast, p);
+ paint->setRasterizer(rast)->unref();
+ }
+
+#if 0
+ SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
+ paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
+#endif
+ paint->setColor(SK_ColorBLUE);
+}
+
+static int gRastIndex;
+
+class TextEffectView : public SampleView {
+ SkTypeface* fFace;
+public:
+ TextEffectView() {
+ fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
+ }
+
+ virtual ~TextEffectView() {
+ SkSafeUnref(fFace);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Text Effects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->save();
+// canvas->scale(SK_Scalar1*2, SK_Scalar1*2, 0, 0);
+
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(56));
+ paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
+ SkTypeface::kBold));
+
+ SkScalar x = SkIntToScalar(20);
+ SkScalar y = paint.getTextSize();
+
+ SkString str("TextEffects");
+
+ paint.setTypeface(fFace);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+ apply_shader(&paint, i);
+
+ // paint.setMaskFilter(NULL);
+ // paint.setColor(SK_ColorBLACK);
+
+#if 1
+ int index = i % SK_ARRAY_COUNT(gLightingColors);
+ paint.setColorFilter(SkColorFilter::CreateLightingFilter(
+ gLightingColors[index].fMul,
+ gLightingColors[index].fAdd))->unref();
+#endif
+
+ canvas->drawText(str.c_str(), str.size(), x, y, paint);
+
+ y += paint.getFontSpacing();
+ }
+
+ canvas->restore();
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ gRastIndex = (gRastIndex + 1) % SK_ARRAY_COUNT(gRastProcs);
+ this->inval(NULL);
+
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextEffectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp
new file mode 100644
index 0000000000..96e8c9a9f7
--- /dev/null
+++ b/samplecode/SampleTextOnPath.cpp
@@ -0,0 +1,284 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPackBits.h"
+#include "SkPath.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+#include "SkAvoidXfermode.h"
+
+#define REPEAT_COUNT 0
+
+static const char gText[] = "Hamburgefons";
+
+static bool gDevKern;
+
+static void rand_text(char text[], SkRandom& rand, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ text[i] = rand.nextU() & 0x7F;
+ }
+}
+
+static SkScalar sum_widths(const SkScalar widths[], int count) {
+ SkScalar w = 0;
+ for (int i = 0; i < count; i++) {
+ w += widths[i];
+ }
+ return w;
+}
+
+static void test_measure(const SkPaint& paint) {
+ char text[256];
+ SkScalar widths[256];
+ SkRect rects[256];
+ SkRect bounds;
+ int count = 256;
+
+ SkRandom rand;
+
+ for (int i = 0; i < 100; i++) {
+ rand_text(text, rand, 256);
+ paint.getTextWidths(text, count, widths, NULL);
+ SkDEBUGCODE(SkScalar tw0 = sum_widths(widths, count);)
+ paint.getTextWidths(text, count, widths, rects);
+ SkDEBUGCODE(SkScalar tw1 = sum_widths(widths, count);)
+ SkASSERT(tw0 == tw1);
+
+ SkDEBUGCODE(SkScalar w0 = paint.measureText(text, count, NULL);)
+ SkDEBUGCODE(SkScalar w1 = paint.measureText(text, count, &bounds);)
+ SkASSERT(w0 == w1);
+ SkASSERT(w0 == tw0);
+
+ SkRect r = rects[0];
+ SkScalar x = 0;
+ for (int j = 1; j < count; j++) {
+ x += widths[j-1];
+ rects[j].offset(x, 0);
+ r.join(rects[j]);
+ }
+ SkASSERT(r == bounds);
+
+ if (r != bounds) {
+ printf("flags=%x i=%d [%g %g %g %g] [%g %g %g %g]\n",
+ paint.getFlags(), i,
+ SkScalarToFloat(r.fLeft),
+ SkScalarToFloat(r.fTop),
+ SkScalarToFloat(r.fRight),
+ SkScalarToFloat(r.fBottom),
+ SkScalarToFloat(bounds.fLeft),
+ SkScalarToFloat(bounds.fTop),
+ SkScalarToFloat(bounds.fRight),
+ SkScalarToFloat(bounds.fBottom));
+ }
+ }
+}
+
+static void test_measure() {
+ SkPaint paint;
+
+ for (int i = 0; i <= SkPaint::kAllFlags; i++) {
+ paint.setFlags(i);
+ test_measure(paint);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void test_textBounds(SkCanvas* canvas) {
+// canvas->scale(SK_Scalar1/2, SK_Scalar1/2);
+
+// canvas->rotate(SkIntToScalar(30));
+
+ gDevKern = !gDevKern;
+
+ SkScalar x = SkIntToScalar(50);
+ SkScalar y = SkIntToScalar(150);
+ SkScalar w[100];
+ SkRect r[100], bounds;
+
+ SkPaint paint;
+ paint.setTextSize(SkIntToScalar(64));
+ paint.setAntiAlias(true);
+ paint.setDevKernText(gDevKern);
+
+ (void)paint.measureText(gText, strlen(gText), &bounds, NULL);
+ paint.setColor(SK_ColorGREEN);
+ bounds.offset(x, y);
+ canvas->drawRect(bounds, paint);
+
+ int count = paint.getTextWidths(gText, strlen(gText), w, r);
+
+ paint.setColor(SK_ColorRED);
+ for (int i = 0; i < count; i++) {
+ r[i].offset(x, y);
+ canvas->drawRect(r[i], paint);
+ x += w[i];
+ }
+ x = SkIntToScalar(50);
+ paint.setColor(gDevKern ? SK_ColorDKGRAY : SK_ColorBLACK);
+ canvas->drawText(gText, strlen(gText), x, y, paint);
+}
+
+static void create_src(SkBitmap* bitmap, SkBitmap::Config config) {
+ bitmap->setConfig(config, 100, 100);
+ bitmap->allocPixels();
+ bitmap->eraseColor(0);
+
+ SkCanvas canvas(*bitmap);
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ canvas.drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+ SkIntToScalar(50), paint);
+}
+
+static void blur(SkBitmap* dst, const SkBitmap& src, SkScalar radius) {
+ *dst = src;
+}
+
+static void test_bitmap_blur(SkCanvas* canvas) {
+ SkBitmap src, dst;
+
+ create_src(&src, SkBitmap::kARGB_8888_Config);
+ blur(&dst, src, SkIntToScalar(4));
+
+ SkPaint paint;
+
+ paint.setColor(SK_ColorRED);
+
+ canvas->drawBitmap(dst, SkIntToScalar(30), SkIntToScalar(60), &paint);
+}
+
+static SkScalar getpathlen(const SkPath& path) {
+ SkPathMeasure meas(path, false);
+ return meas.getLength();
+}
+
+static void test_textpathmatrix(SkCanvas* canvas) {
+ SkPaint paint;
+ SkPath path;
+ SkMatrix matrix;
+
+ path.moveTo(SkIntToScalar(200), SkIntToScalar(300));
+ path.quadTo(SkIntToScalar(400), SkIntToScalar(100),
+ SkIntToScalar(600), SkIntToScalar(300));
+
+ paint.setAntiAlias(true);
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ // canvas->drawPath(path, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setTextSize(SkIntToScalar(48));
+ paint.setTextAlign(SkPaint::kRight_Align);
+
+ const char* text = "Reflection";
+ size_t len = strlen(text);
+ SkScalar pathLen = getpathlen(path);
+
+ canvas->drawTextOnPath(text, len, path, NULL, paint);
+
+ paint.setColor(SK_ColorRED);
+ matrix.setScale(-SK_Scalar1, SK_Scalar1);
+ matrix.postTranslate(pathLen, 0);
+ canvas->drawTextOnPath(text, len, path, &matrix, paint);
+
+ paint.setColor(SK_ColorBLUE);
+ matrix.setScale(SK_Scalar1, -SK_Scalar1);
+ canvas->drawTextOnPath(text, len, path, &matrix, paint);
+
+ paint.setColor(SK_ColorGREEN);
+ matrix.setScale(-SK_Scalar1, -SK_Scalar1);
+ matrix.postTranslate(pathLen, 0);
+ canvas->drawTextOnPath(text, len, path, &matrix, paint);
+}
+
+class TextOnPathView : public SampleView {
+public:
+ SkPath fPath;
+ SkScalar fHOffset;
+
+ TextOnPathView() {
+ SkRect r;
+ r.set(SkIntToScalar(100), SkIntToScalar(100),
+ SkIntToScalar(300), SkIntToScalar(300));
+ fPath.addOval(r);
+ fPath.offset(SkIntToScalar(200), 0);
+
+ fHOffset = SkIntToScalar(50);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Text On Path");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(50));
+
+ for (int j = 0; j < REPEAT_COUNT; j++) {
+ SkScalar x = fHOffset;
+
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
+ x, paint.getTextSize()/2, paint);
+
+ paint.setColor(SK_ColorRED);
+ canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
+ x + SkIntToScalar(50), 0, paint);
+
+ paint.setColor(SK_ColorBLUE);
+ canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
+ x + SkIntToScalar(100), -paint.getTextSize()/2, paint);
+ }
+
+ paint.setColor(SK_ColorGREEN);
+ paint.setStyle(SkPaint::kStroke_Style);
+// canvas->drawPath(fPath, paint);
+
+ canvas->translate(0, SkIntToScalar(100));
+ test_textpathmatrix(canvas);
+
+ if (REPEAT_COUNT > 1)
+ this->inval(NULL);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fHints += 1;
+ this->inval(NULL);
+ return this->INHERITED::onFindClickHandler(x, y);
+ }
+
+ virtual bool onClick(Click* click) {
+ return this->INHERITED::onClick(click);
+ }
+
+private:
+ int fHints;
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() {
+ return new TextOnPathView;
+}
+
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
new file mode 100755
index 0000000000..be000f95f2
--- /dev/null
+++ b/samplecode/SampleTextureDomain.cpp
@@ -0,0 +1,80 @@
+#include "SampleCode.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+
+namespace {
+SkBitmap make_bitmap() {
+ SkBitmap bm;
+ bm.setConfig(SkBitmap::kARGB_8888_Config , 5, 5);
+ bm.allocPixels();
+
+ for (int y = 0; y < bm.height(); y++) {
+ uint32_t* p = bm.getAddr32(0, y);
+ for (int x = 0; x < bm.width(); x++) {
+ p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
+ }
+ }
+ bm.unlockPixels();
+ return bm;
+}
+} // unnamed namespace
+
+class TextureDomainView : public SampleView {
+ SkBitmap fBM;
+
+public:
+ TextureDomainView(){
+ fBM = make_bitmap();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Texture Domian");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkIRect srcRect;
+ SkRect dstRect;
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+
+ // Test that bitmap draws from malloc-backed bitmaps respect
+ // the constrained texture domain.
+ srcRect.setXYWH(1, 1, 3, 3);
+ dstRect.setXYWH(5.0f, 5.0f, 305.0f, 305.0f);
+ canvas->drawBitmapRect(fBM, &srcRect, dstRect, &paint);
+
+ // Test that bitmap draws across separate devices also respect
+ // the constrainted texture domain.
+ // Note: GPU-backed bitmaps follow a different rendering path
+ // when copying from one GPU device to another.
+ SkRefPtr<SkDevice> primaryDevice(canvas->getDevice());
+ SkRefPtr<SkDevice> secondDevice(canvas->createDevice(
+ SkBitmap::kARGB_8888_Config, 5, 5, true, true));
+ secondDevice->unref();
+ SkCanvas secondCanvas(secondDevice.get());
+
+ srcRect.setXYWH(1, 1, 3, 3);
+ dstRect.setXYWH(1.0f, 1.0f, 3.0f, 3.0f);
+ secondCanvas.drawBitmapRect(fBM, &srcRect, dstRect, &paint);
+
+ SkBitmap deviceBitmap = secondDevice->accessBitmap(false);
+
+ srcRect.setXYWH(1, 1, 3, 3);
+ dstRect.setXYWH(405.0f, 5.0f, 305.0f, 305.0f);
+ canvas->drawBitmapRect(deviceBitmap, &srcRect, dstRect, &paint);
+ }
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TextureDomainView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp
new file mode 100644
index 0000000000..4752ed1eba
--- /dev/null
+++ b/samplecode/SampleTiling.cpp
@@ -0,0 +1,162 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkPicture.h"
+#include "SkTypeface.h"
+
+// effects
+#include "SkGradientShader.h"
+#include "SkUnitMappers.h"
+#include "SkBlurDrawLooper.h"
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+ bm->setConfig(config, w, h);
+ bm->allocPixels();
+ bm->eraseColor(0);
+
+ SkCanvas canvas(*bm);
+ SkPoint pts[] = { { 0, 0 }, { SkIntToScalar(w), SkIntToScalar(h) } };
+ SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
+ SkScalar pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+ SkPaint paint;
+
+ SkUnitMapper* um = NULL;
+
+ um = new SkCosineMapper;
+// um = new SkDiscreteMapper(12);
+
+ SkAutoUnref au(um);
+
+ paint.setDither(true);
+ paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
+ SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
+ canvas.drawPaint(paint);
+}
+
+static void setup(SkPaint* paint, const SkBitmap& bm, bool filter,
+ SkShader::TileMode tmx, SkShader::TileMode tmy) {
+ SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy);
+ paint->setShader(shader)->unref();
+ paint->setFilterBitmap(filter);
+}
+
+static const SkBitmap::Config gConfigs[] = {
+ SkBitmap::kARGB_8888_Config,
+ SkBitmap::kRGB_565_Config,
+ SkBitmap::kARGB_4444_Config
+};
+static const int gWidth = 32;
+static const int gHeight = 32;
+
+class TilingView : public SampleView {
+ SkPicture fTextPicture;
+ SkBlurDrawLooper fLooper;
+public:
+ TilingView()
+ : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2),
+ 0x88000000) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+ makebm(&fTexture[i], gConfigs[i], gWidth, gHeight);
+ }
+ }
+
+ SkBitmap fTexture[SK_ARRAY_COUNT(gConfigs)];
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Tiling");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) };
+
+ static const char* gConfigNames[] = { "8888", "565", "4444" };
+
+ static const bool gFilters[] = { false, true };
+ static const char* gFilterNames[] = { "point", "bilinear" };
+
+ static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
+ static const char* gModeNames[] = { "C", "R", "M" };
+
+ SkScalar y = SkIntToScalar(24);
+ SkScalar x = SkIntToScalar(10);
+
+ SkCanvas* textCanvas = NULL;
+ if (fTextPicture.width() == 0) {
+ textCanvas = fTextPicture.beginRecording(1000, 1000);
+ }
+
+ if (textCanvas) {
+ for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+ for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+ SkPaint p;
+ SkString str;
+ p.setAntiAlias(true);
+ p.setDither(true);
+ p.setLooper(&fLooper);
+ str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
+
+ p.setTextAlign(SkPaint::kCenter_Align);
+ textCanvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+
+ x += r.width() * 4 / 3;
+ }
+ }
+ }
+
+ y += SkIntToScalar(16);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
+ x = SkIntToScalar(10);
+ for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+ for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+ SkPaint paint;
+ setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
+ paint.setDither(true);
+
+ canvas->save();
+ canvas->translate(x, y);
+ canvas->drawRect(r, paint);
+ canvas->restore();
+
+ x += r.width() * 4 / 3;
+ }
+ }
+ if (textCanvas) {
+ SkPaint p;
+ SkString str;
+ p.setAntiAlias(true);
+ p.setLooper(&fLooper);
+ str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
+ textCanvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
+ }
+
+ y += r.height() * 4 / 3;
+ }
+ }
+
+ canvas->drawPicture(fTextPicture);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TilingView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTinyBitmap.cpp b/samplecode/SampleTinyBitmap.cpp
new file mode 100644
index 0000000000..0841474db0
--- /dev/null
+++ b/samplecode/SampleTinyBitmap.cpp
@@ -0,0 +1,76 @@
+#include "SampleCode.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkUtils.h"
+
+static SkBitmap make_bitmap() {
+ SkBitmap bm;
+ const int N = 1;
+ SkColorTable* ctable = new SkColorTable(N);
+
+ SkPMColor* c = ctable->lockColors();
+ for (int i = 0; i < N; i++) {
+ c[i] = SkPackARGB32(0x80, 0x80, 0, 0);
+ }
+ ctable->unlockColors(true);
+ bm.setConfig(SkBitmap::kIndex8_Config, 1, 1);
+ bm.allocPixels(ctable);
+ ctable->unref();
+
+ bm.lockPixels();
+ for (int y = 0; y < bm.height(); y++) {
+ uint8_t* p = bm.getAddr8(0, y);
+ for (int x = 0; x < bm.width(); x++) {
+ p[x] = 0;
+ }
+ }
+ bm.unlockPixels();
+ return bm;
+}
+
+class TinyBitmapView : public SampleView {
+ SkBitmap fBM;
+public:
+ TinyBitmapView() {
+ fBM = make_bitmap();
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "TinyBitmap");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) {
+ SkAutoLockPixels alp(*bm); // needed for ctable
+ bm->setIsOpaque(isOpaque);
+ SkColorTable* ctable = bm->getColorTable();
+ if (ctable) {
+ ctable->setIsOpaque(isOpaque);
+ }
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkShader* s = SkShader::CreateBitmapShader(fBM, SkShader::kRepeat_TileMode,
+ SkShader::kMirror_TileMode);
+ SkPaint paint;
+ paint.setShader(s)->unref();
+ canvas->drawPaint(paint);
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TinyBitmapView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTriangles.cpp b/samplecode/SampleTriangles.cpp
new file mode 100644
index 0000000000..be9da8fb1d
--- /dev/null
+++ b/samplecode/SampleTriangles.cpp
@@ -0,0 +1,118 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkConcaveToTriangles.h"
+
+#define SIZE SkIntToScalar(150)
+
+typedef void (*PathProc)(SkPath*);
+
+static void make_path0(SkPath* path) {
+ SkRect r;
+ r.set(0, 0, SIZE, SIZE);
+ path->addRect(r);
+}
+
+static void make_path1(SkPath* path) {
+ SkRect r;
+ r.set(0, 0, SIZE, SIZE);
+ path->addRoundRect(r, SIZE/4, SIZE/4);
+}
+
+static void make_path2(SkPath* path) {
+ SkRect r;
+ r.set(0, 0, SIZE, SIZE);
+ path->addOval(r);
+}
+
+static const PathProc gProcs[] = {
+ make_path0,
+ make_path1,
+ make_path2,
+};
+
+#define COUNT_PROCS SK_ARRAY_COUNT(gProcs)
+
+class TriangleView : public SkView {
+public:
+ SkPath fPaths[COUNT_PROCS];
+
+ TriangleView() {
+ for (size_t i = 0; i < COUNT_PROCS; i++) {
+ gProcs[i](&fPaths[i]);
+ }
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Triangles");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorGRAY);
+ }
+
+ static void draw_path(SkCanvas* canvas, const SkPaint& pathPaint,
+ const SkPath& path, const SkPaint& triPaint) {
+ canvas->drawPath(path, pathPaint);
+
+ int n = path.getPoints(NULL, 0);
+ SkPoint* pts = new SkPoint[n];
+ path.getPoints(pts, n);
+
+ SkTDArray<SkPoint> triangles;
+ if (SkConcaveToTriangles(n, pts, &triangles)) {
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
+ triangles.count(), triangles.begin(), NULL,
+ NULL, NULL, NULL, 0, triPaint);
+ }
+
+ SkPaint paint;
+ paint.setColor(SK_ColorGREEN);
+ paint.setStrokeWidth(SkIntToScalar(4));
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, paint);
+ delete[] pts;
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ canvas->translate(SIZE/2, SIZE/2);
+
+ SkPaint pathPaint, triPaint;
+
+ pathPaint.setColor(SK_ColorBLUE);
+ pathPaint.setStrokeWidth(SIZE / 12);
+
+ triPaint.setColor(SK_ColorRED);
+ triPaint.setStyle(SkPaint::kStroke_Style);
+
+ for (size_t i = 0; i < COUNT_PROCS; i++) {
+ pathPaint.setStyle(SkPaint::kFill_Style);
+ draw_path(canvas, pathPaint, fPaths[i], triPaint);
+
+ canvas->save();
+ canvas->translate(0, SIZE * 6 / 5);
+
+ pathPaint.setStyle(SkPaint::kStroke_Style);
+ draw_path(canvas, pathPaint, fPaths[i], triPaint);
+
+ canvas->restore();
+ canvas->translate(SIZE * 6 / 5, 0);
+ }
+ }
+
+private:
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TriangleView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleTypeface.cpp b/samplecode/SampleTypeface.cpp
new file mode 100644
index 0000000000..63f1d5adba
--- /dev/null
+++ b/samplecode/SampleTypeface.cpp
@@ -0,0 +1,128 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkTypeface.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "Sk1DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkPathMeasure.h"
+#include "SkRandom.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDither.h"
+#include "SkTypefaceCache.h"
+
+static int dither_4444(int x) {
+ return ((x << 1) - ((x >> 4 << 4) | (x >> 4))) >> 4;
+}
+
+/** Ensure that the max of the original and dithered value (for alpha) is always
+ >= any other dithered value. We apply this "max" in colorpriv.h when we
+ predither down to 4444, to be sure that we stay in legal premultiplied form
+ */
+static void test_4444_dither() {
+ int buckets[16];
+ sk_bzero(buckets, sizeof(buckets));
+
+ for (int a = 0; a <= 0xFF; a++) {
+ int da = dither_4444(a);
+ int maxa = SkMax32(a >> 4, da);
+ // SkDebugf("--- %02X %X\n", a, da);
+ buckets[da] += 1;
+ for (int c = 0; c <= a; c++) {
+ int dc = dither_4444(c);
+ if (maxa < dc) {
+ SkDebugf("------------ error a=%d da=%d c=%d dc=%d\n", a, da,
+ c, dc);
+ }
+ }
+ }
+ for (int i = 0; i < 16; i++) {
+ // SkDebugf("[%d] = %d\n", i, buckets[i]);
+ }
+}
+
+static const struct {
+ const char* fName;
+ SkTypeface::Style fStyle;
+} gFaces[] = {
+ { "sans-serif", SkTypeface::kNormal },
+ { "sans-serif", SkTypeface::kBold },
+ { "sans-serif", SkTypeface::kItalic },
+ { "sans-serif", SkTypeface::kBoldItalic },
+ { "serif", SkTypeface::kNormal },
+ { "serif", SkTypeface::kBold },
+ { "serif", SkTypeface::kItalic },
+ { "serif", SkTypeface::kBoldItalic },
+ { "monospace", SkTypeface::kNormal },
+ { "monospace", SkTypeface::kBold },
+ { "monospace", SkTypeface::kItalic },
+ { "monospace", SkTypeface::kBoldItalic },
+};
+
+static const int gFaceCount = SK_ARRAY_COUNT(gFaces);
+
+class TypefaceView : public SampleView {
+ SkTypeface* fFaces[gFaceCount];
+
+public:
+ TypefaceView() {
+ test_4444_dither();
+ for (int i = 0; i < gFaceCount; i++) {
+ fFaces[i] = SkTypeface::CreateFromName(gFaces[i].fName,
+ gFaces[i].fStyle);
+ }
+
+ this->setBGColor(0xFFDDDDDD);
+ }
+
+ virtual ~TypefaceView() {
+ for (int i = 0; i < gFaceCount; i++) {
+ SkSafeUnref(fFaces[i]);
+ }
+
+ SkTypefaceCache::Dump();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Typefaces");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(30));
+
+ const char* text = "Hamburgefons";
+ const size_t textLen = strlen(text);
+
+ SkScalar x = SkIntToScalar(10);
+ SkScalar dy = paint.getFontMetrics(NULL);
+ SkScalar y = dy;
+
+ paint.setLinearText(true);
+ for (int i = 0; i < gFaceCount; i++) {
+ paint.setTypeface(fFaces[i]);
+ canvas->drawText(text, textLen, x, y, paint);
+ y += dy;
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new TypefaceView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleUnitMapper.cpp b/samplecode/SampleUnitMapper.cpp
new file mode 100644
index 0000000000..b20aece316
--- /dev/null
+++ b/samplecode/SampleUnitMapper.cpp
@@ -0,0 +1,157 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkUnitMappers.h"
+#include "SkCubicInterval.h"
+
+#include "SkWidgetViews.h"
+
+static SkStaticTextView* make_textview(SkView* parent,
+ const SkRect& bounds,
+ const SkPaint& paint) {
+ SkStaticTextView* view = new SkStaticTextView;
+ view->setMode(SkStaticTextView::kFixedSize_Mode);
+ view->setPaint(paint);
+ view->setVisibleP(true);
+ view->setSize(bounds.width(), bounds.height());
+ view->setLoc(bounds.fLeft, bounds.fTop);
+ parent->attachChildToFront(view)->unref();
+ return view;
+}
+
+static void set_scalar(SkStaticTextView* view, SkScalar value) {
+ SkString str;
+ str.appendScalar(value);
+ view->setText(str);
+}
+
+class UnitMapperView : public SampleView {
+ SkPoint fPts[4];
+ SkMatrix fMatrix;
+ SkStaticTextView* fViews[4];
+
+ void setViews() {
+ set_scalar(fViews[0], fPts[1].fX);
+ set_scalar(fViews[1], fPts[1].fY);
+ set_scalar(fViews[2], fPts[2].fX);
+ set_scalar(fViews[3], fPts[2].fY);
+ }
+
+public:
+ UnitMapperView() {
+ fPts[0].set(0, 0);
+ fPts[1].set(SK_Scalar1 / 3, SK_Scalar1 / 3);
+ fPts[2].set(SK_Scalar1 * 2 / 3, SK_Scalar1 * 2 / 3);
+ fPts[3].set(SK_Scalar1, SK_Scalar1);
+
+ fMatrix.setScale(SK_Scalar1 * 200, -SK_Scalar1 * 200);
+ fMatrix.postTranslate(SkIntToScalar(100), SkIntToScalar(300));
+
+ SkRect r = {
+ SkIntToScalar(350), SkIntToScalar(100),
+ SkIntToScalar(500), SkIntToScalar(130)
+ };
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(SkIntToScalar(25));
+ for (int i = 0; i < 4; i++) {
+ fViews[i] = make_textview(this, r, paint);
+ r.offset(0, r.height());
+ }
+ this->setViews();
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "UnitMapper");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF8888FF);
+
+ SkRect r = { 0, 0, SK_Scalar1, SK_Scalar1 };
+
+ canvas->concat(fMatrix);
+ canvas->drawRect(r, paint);
+
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(0);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ SkPath path;
+ path.moveTo(fPts[0]);
+ path.cubicTo(fPts[1], fPts[2], fPts[3]);
+ canvas->drawPath(path, paint);
+
+ paint.setColor(SK_ColorRED);
+ paint.setStrokeWidth(0);
+ canvas->drawLine(0, 0, SK_Scalar1, SK_Scalar1, paint);
+
+ paint.setColor(SK_ColorBLUE);
+ paint.setStrokeWidth(SK_Scalar1 / 60);
+ for (int i = 0; i < 50; i++) {
+ SkScalar x = i * SK_Scalar1 / 49;
+ canvas->drawPoint(x, SkEvalCubicInterval(&fPts[1], x), paint);
+ }
+
+ paint.setStrokeWidth(SK_Scalar1 / 20);
+ paint.setColor(SK_ColorGREEN);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, &fPts[1], paint);
+ }
+
+ SkPoint invertPt(SkScalar x, SkScalar y) {
+ SkPoint pt;
+ SkMatrix m;
+ fMatrix.invert(&m);
+ m.mapXY(x, y, &pt);
+ return pt;
+ }
+
+ int hittest(SkScalar x, SkScalar y) {
+ SkPoint target = { x, y };
+ SkPoint pts[2] = { fPts[1], fPts[2] };
+ fMatrix.mapPoints(pts, 2);
+ for (int i = 0; i < 2; i++) {
+ if (SkPoint::Distance(pts[i], target) < SkIntToScalar(4)) {
+ return i + 1;
+ }
+ }
+ return -1;
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fDragIndex = hittest(x, y);
+ return fDragIndex >= 0 ? new Click(this) : NULL;
+ }
+
+ virtual bool onClick(Click* click) {
+ if (fDragIndex >= 0) {
+ fPts[fDragIndex] = invertPt(click->fCurr.fX, click->fCurr.fY);
+ this->setViews();
+ this->inval(NULL);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ int fDragIndex;
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new UnitMapperView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
new file mode 100644
index 0000000000..74e757f43e
--- /dev/null
+++ b/samplecode/SampleVertices.cpp
@@ -0,0 +1,230 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkNinePatch.h"
+
+static SkShader* make_shader0(SkIPoint* size) {
+ SkBitmap bm;
+ size->set(2, 2);
+ bm.setConfig(SkBitmap::kARGB_8888_Config, size->fX, size->fY);
+ SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80);
+ SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff);
+ bm.allocPixels();
+ bm.eraseColor(color0);
+ bm.lockPixels();
+ uint32_t* pixels = (uint32_t*) bm.getPixels();
+ pixels[0] = pixels[2] = color0;
+ pixels[1] = pixels[3] = color1;
+ bm.unlockPixels();
+
+ return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+}
+
+static SkShader* make_shader1(const SkIPoint& size) {
+ SkPoint pts[] = { { 0, 0 },
+ { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
+ SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
+ return SkGradientShader::CreateLinear(pts, colors, NULL,
+ SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
+}
+
+class VerticesView : public SampleView {
+ SkShader* fShader0;
+ SkShader* fShader1;
+
+public:
+ VerticesView() {
+ SkIPoint size;
+
+ fShader0 = make_shader0(&size);
+ fShader1 = make_shader1(size);
+
+ make_strip(&fRecs[0], size.fX, size.fY);
+ make_fan(&fRecs[1], size.fX, size.fY);
+ make_tris(&fRecs[2]);
+
+ fScale = SK_Scalar1;
+
+ this->setBGColor(SK_ColorGRAY);
+ }
+
+ virtual ~VerticesView() {
+ SkSafeUnref(fShader0);
+ SkSafeUnref(fShader1);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt))
+ {
+ SkString str("Vertices");
+ SampleCode::TitleR(evt, str.c_str());
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ SkScalar fScale;
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ SkPaint paint;
+ paint.setDither(true);
+ paint.setFilterBitmap(true);
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
+ canvas->save();
+
+ paint.setShader(NULL);
+ canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+ fRecs[i].fVerts, fRecs[i].fTexs,
+ NULL, NULL, NULL, 0, paint);
+
+ canvas->translate(SkIntToScalar(250), 0);
+
+ paint.setShader(fShader0);
+ canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+ fRecs[i].fVerts, fRecs[i].fTexs,
+ NULL, NULL, NULL, 0, paint);
+
+ canvas->translate(SkIntToScalar(250), 0);
+
+ paint.setShader(fShader1);
+ canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
+ fRecs[i].fVerts, fRecs[i].fTexs,
+ NULL, NULL, NULL, 0, paint);
+ canvas->restore();
+
+ canvas->translate(0, SkIntToScalar(250));
+ }
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ return new Click(this);
+ }
+
+ virtual bool onClick(Click* click) {
+ // fCurrX = click->fICurr.fX;
+ // fCurrY = click->fICurr.fY;
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ struct Rec {
+ SkCanvas::VertexMode fMode;
+ int fCount;
+ SkPoint* fVerts;
+ SkPoint* fTexs;
+
+ Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {}
+ ~Rec() { delete[] fVerts; delete[] fTexs; }
+ };
+
+ void make_tris(Rec* rec) {
+ int n = 10;
+ SkRandom rand;
+
+ rec->fMode = SkCanvas::kTriangles_VertexMode;
+ rec->fCount = n * 3;
+ rec->fVerts = new SkPoint[rec->fCount];
+
+ for (int i = 0; i < n; i++) {
+ SkPoint* v = &rec->fVerts[i*3];
+ for (int j = 0; j < 3; j++) {
+ v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250);
+ }
+ }
+ }
+
+ void make_fan(Rec* rec, int texWidth, int texHeight) {
+ const SkScalar tx = SkIntToScalar(texWidth);
+ const SkScalar ty = SkIntToScalar(texHeight);
+ const int n = 24;
+
+ rec->fMode = SkCanvas::kTriangleFan_VertexMode;
+ rec->fCount = n + 2;
+ rec->fVerts = new SkPoint[rec->fCount];
+ rec->fTexs = new SkPoint[rec->fCount];
+
+ SkPoint* v = rec->fVerts;
+ SkPoint* t = rec->fTexs;
+
+ v[0].set(0, 0);
+ t[0].set(0, 0);
+ for (int i = 0; i < n; i++) {
+ SkScalar cos;
+ SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+ v[i+1].set(cos, sin);
+ t[i+1].set(i*tx/n, ty);
+ }
+ v[n+1] = v[1];
+ t[n+1].set(tx, ty);
+
+ SkMatrix m;
+ m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+ m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+ m.mapPoints(v, rec->fCount);
+ }
+
+ void make_strip(Rec* rec, int texWidth, int texHeight) {
+ const SkScalar tx = SkIntToScalar(texWidth);
+ const SkScalar ty = SkIntToScalar(texHeight);
+ const int n = 24;
+
+ rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
+ rec->fCount = 2 * (n + 1);
+ rec->fVerts = new SkPoint[rec->fCount];
+ rec->fTexs = new SkPoint[rec->fCount];
+
+ SkPoint* v = rec->fVerts;
+ SkPoint* t = rec->fTexs;
+
+ for (int i = 0; i < n; i++) {
+ SkScalar cos;
+ SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
+ v[i*2 + 0].set(cos/2, sin/2);
+ v[i*2 + 1].set(cos, sin);
+
+ t[i*2 + 0].set(tx * i / n, ty);
+ t[i*2 + 1].set(tx * i / n, 0);
+ }
+ v[2*n + 0] = v[0];
+ v[2*n + 1] = v[1];
+
+ t[2*n + 0].set(tx, ty);
+ t[2*n + 1].set(tx, 0);
+
+ SkMatrix m;
+ m.setScale(SkIntToScalar(100), SkIntToScalar(100));
+ m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
+ m.mapPoints(v, rec->fCount);
+ }
+
+ Rec fRecs[3];
+
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new VerticesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleWarp.cpp b/samplecode/SampleWarp.cpp
new file mode 100644
index 0000000000..bf4ef0dbed
--- /dev/null
+++ b/samplecode/SampleWarp.cpp
@@ -0,0 +1,467 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkImageDecoder.h"
+
+#include "SkBlurMaskFilter.h"
+#include "SkTableMaskFilter.h"
+
+#define kNearlyZero (SK_Scalar1 / 8092)
+
+static void test_bigblur(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorBLACK);
+
+ SkBitmap orig, mask;
+ SkImageDecoder::DecodeFile("/skimages/app_icon.png", &orig);
+
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(8, SkBlurMaskFilter::kNormal_BlurStyle);
+ SkPaint paint;
+ paint.setMaskFilter(mf)->unref();
+ SkIPoint offset;
+ orig.extractAlpha(&mask, &paint, &offset);
+
+ paint.setColor(0xFFBB8800);
+ paint.setColor(SK_ColorWHITE);
+
+ int i;
+ canvas->save();
+ float gamma = 0.8;
+ for (i = 0; i < 5; i++) {
+ paint.setMaskFilter(SkTableMaskFilter::CreateGamma(gamma))->unref();
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ paint.setMaskFilter(NULL);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+ gamma -= 0.1;
+ canvas->translate(120, 0);
+ }
+ canvas->restore();
+ canvas->translate(0, 160);
+
+ for (i = 0; i < 5; i++) {
+ paint.setMaskFilter(SkTableMaskFilter::CreateClip(i*30, 255 - 20))->unref();
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ paint.setMaskFilter(NULL);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+ canvas->translate(120, 0);
+ }
+
+#if 0
+ paint.setColor(0xFFFFFFFF);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ paint.setMaskFilter(NULL);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+
+ canvas->translate(120, 0);
+
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+
+ canvas->translate(120, 0);
+
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+
+ canvas->translate(120, 0);
+
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+
+ canvas->translate(120, 0);
+
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(mask, 0, 0, &paint);
+ canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
+#endif
+}
+
+#include "SkMeshUtils.h"
+
+static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
+ SkPoint pt;
+ pt.set(x, y);
+ return pt;
+}
+
+static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
+ return SkMakePoint(SkScalarInterp(a.fX, b.fX, t),
+ SkScalarInterp(a.fY, b.fY, t));
+}
+
+#include "SkBoundaryPatch.h"
+
+static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
+ SkScalar x3, SkScalar y3, SkScalar scale = 1) {
+ SkPoint tmp, tmp2;
+
+ pts[0].set(x0, y0);
+ pts[3].set(x3, y3);
+
+ tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3);
+ tmp2 = pts[0] - tmp;
+ tmp2.rotateCW();
+ tmp2.scale(scale);
+ pts[1] = tmp + tmp2;
+
+ tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3);
+ tmp2 = pts[3] - tmp;
+ tmp2.rotateCW();
+ tmp2.scale(scale);
+ pts[2] = tmp + tmp2;
+}
+
+static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
+ SkCubicBoundary cubic;
+ set_cubic(cubic.fPts + 0, 0, 0, 100, 0, scale);
+ set_cubic(cubic.fPts + 3, 100, 0, 100, 100, scale);
+ set_cubic(cubic.fPts + 6, 100, 100, 0, 100, -scale);
+ set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
+
+ SkBoundaryPatch patch;
+ patch.setBoundary(&cubic);
+
+ const int Rows = 16;
+ const int Cols = 16;
+ SkPoint pts[Rows * Cols];
+ patch.evalPatch(pts, Rows, Cols);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setFilterBitmap(true);
+ paint.setStrokeWidth(1);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ canvas->translate(50, 50);
+ canvas->scale(3, 3);
+
+ SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
+}
+
+static void test_drag(SkCanvas* canvas, const SkBitmap& bm,
+ const SkPoint& p0, const SkPoint& p1) {
+ SkCubicBoundary cubic;
+ set_cubic(cubic.fPts + 0, 0, 0, 100, 0, 0);
+ set_cubic(cubic.fPts + 3, 100, 0, 100, 100, 0);
+ set_cubic(cubic.fPts + 6, 100, 100, 0, 100, 0);
+ set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
+
+#if 0
+ cubic.fPts[1] += p1 - p0;
+ cubic.fPts[2] += p1 - p0;
+#else
+ SkScalar dx = p1.fX - p0.fX;
+ if (dx > 0) dx = 0;
+ SkScalar dy = p1.fY - p0.fY;
+ if (dy > 0) dy = 0;
+
+ cubic.fPts[1].fY += dy;
+ cubic.fPts[2].fY += dy;
+ cubic.fPts[10].fX += dx;
+ cubic.fPts[11].fX += dx;
+#endif
+
+ SkBoundaryPatch patch;
+ patch.setBoundary(&cubic);
+
+ const int Rows = 16;
+ const int Cols = 16;
+ SkPoint pts[Rows * Cols];
+ patch.evalPatch(pts, Rows, Cols);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setFilterBitmap(true);
+ paint.setStrokeWidth(1);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ canvas->translate(50, 50);
+ canvas->scale(3, 3);
+
+ SkAutoCanvasRestore acr(canvas, true);
+
+ SkRect r = { 0, 0, 100, 100 };
+ canvas->clipRect(r);
+ SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Mesh {
+public:
+ Mesh();
+ ~Mesh();
+
+ Mesh& operator=(const Mesh& src);
+
+ void init(const SkRect& bounds, int rows, int cols,
+ const SkRect& texture);
+
+ const SkRect& bounds() const { return fBounds; }
+
+ int rows() const { return fRows; }
+ int cols() const { return fCols; }
+ SkPoint& pt(int row, int col) {
+ return fPts[row * (fRows + 1) + col];
+ }
+
+ void draw(SkCanvas*, const SkPaint&);
+ void drawWireframe(SkCanvas* canvas, const SkPaint& paint);
+
+private:
+ SkRect fBounds;
+ int fRows, fCols;
+ SkPoint* fPts;
+ SkPoint* fTex; // just points into fPts, not separately allocated
+ int fCount;
+ uint16_t* fIndices;
+ int fIndexCount;
+};
+
+Mesh::Mesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {}
+
+Mesh::~Mesh() {
+ delete[] fPts;
+ delete[] fIndices;
+}
+
+Mesh& Mesh::operator=(const Mesh& src) {
+ delete[] fPts;
+ delete[] fIndices;
+
+ fBounds = src.fBounds;
+ fRows = src.fRows;
+ fCols = src.fCols;
+
+ fCount = src.fCount;
+ fPts = new SkPoint[fCount * 2];
+ fTex = fPts + fCount;
+ memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint));
+
+ delete[] fIndices;
+ fIndexCount = src.fIndexCount;
+ fIndices = new uint16_t[fIndexCount];
+ memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t));
+
+ return *this;
+}
+
+void Mesh::init(const SkRect& bounds, int rows, int cols,
+ const SkRect& texture) {
+ SkASSERT(rows > 0 && cols > 0);
+
+ fBounds = bounds;
+ fRows = rows;
+ fCols = cols;
+
+ delete[] fPts;
+ fCount = (rows + 1) * (cols + 1);
+ fPts = new SkPoint[fCount * 2];
+ fTex = fPts + fCount;
+
+ delete[] fIndices;
+ fIndexCount = rows * cols * 6;
+ fIndices = new uint16_t[fIndexCount];
+
+ SkPoint* pts = fPts;
+ const SkScalar dx = bounds.width() / rows;
+ const SkScalar dy = bounds.height() / cols;
+ SkPoint* tex = fTex;
+ const SkScalar dtx = texture.width() / rows;
+ const SkScalar dty = texture.height() / cols;
+ uint16_t* idx = fIndices;
+ int index = 0;
+ for (int y = 0; y <= cols; y++) {
+ for (int x = 0; x <= rows; x++) {
+ pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy);
+ pts += 1;
+ tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty);
+ tex += 1;
+
+ if (y < cols && x < rows) {
+ *idx++ = index;
+ *idx++ = index + rows + 1;
+ *idx++ = index + 1;
+
+ *idx++ = index + 1;
+ *idx++ = index + rows + 1;
+ *idx++ = index + rows + 2;
+
+ index += 1;
+ }
+ }
+ index += 1;
+ }
+}
+
+void Mesh::draw(SkCanvas* canvas, const SkPaint& paint) {
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
+ fPts, fTex, NULL, NULL, fIndices, fIndexCount,
+ paint);
+}
+
+void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) {
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
+ fPts, NULL, NULL, NULL, fIndices, fIndexCount,
+ paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class WarpView : public SkView {
+ Mesh fMesh, fOrig;
+ SkBitmap fBitmap;
+ SkMatrix fMatrix, fInverse;
+public:
+ WarpView() {
+ SkBitmap bm;
+// SkImageDecoder::DecodeFile("/skimages/marker.png", &bm);
+ SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
+ // SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
+ fBitmap = bm;
+
+ SkRect bounds, texture;
+ texture.set(0, 0, SkIntToScalar(fBitmap.width()),
+ SkIntToScalar(fBitmap.height()));
+ bounds = texture;
+
+// fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
+ fMesh.init(bounds, fBitmap.width()/16, fBitmap.height()/16, texture);
+ fOrig = fMesh;
+
+ fP0.set(0, 0);
+ fP1 = fP0;
+
+ fMatrix.setScale(2, 2);
+ fMatrix.invert(&fInverse);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Warp");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ static SkPoint apply_warp(const SkVector& drag, SkScalar dragLength,
+ const SkPoint& dragStart, const SkPoint& dragCurr,
+ const SkPoint& orig) {
+ SkVector delta = orig - dragCurr;
+ SkScalar length = SkPoint::Normalize(&delta);
+ if (length <= kNearlyZero) {
+ return orig;
+ }
+
+ const SkScalar period = 20;
+ const SkScalar mag = dragLength / 3;
+
+ SkScalar d = length / (period);
+ d = mag * SkScalarSin(d) / d;
+ SkScalar dx = delta.fX * d;
+ SkScalar dy = delta.fY * d;
+ SkScalar px = orig.fX + dx;
+ SkScalar py = orig.fY + dy;
+ return SkPoint::Make(px, py);
+ }
+
+ static SkPoint apply_warp2(const SkVector& drag, SkScalar dragLength,
+ const SkPoint& dragStart, const SkPoint& dragCurr,
+ const SkPoint& orig) {
+ SkVector delta = orig - dragCurr;
+ SkScalar length = SkPoint::Normalize(&delta);
+ if (length <= kNearlyZero) {
+ return orig;
+ }
+
+ const SkScalar period = 10 + dragLength/4;
+ const SkScalar mag = dragLength / 3;
+
+ SkScalar d = length / (period);
+ if (d > SK_ScalarPI) {
+ d = SK_ScalarPI;
+ }
+
+ d = -mag * SkScalarSin(d);
+
+ SkScalar dx = delta.fX * d;
+ SkScalar dy = delta.fY * d;
+ SkScalar px = orig.fX + dx;
+ SkScalar py = orig.fY + dy;
+ return SkPoint::Make(px, py);
+ }
+
+ typedef SkPoint (*WarpProc)(const SkVector& drag, SkScalar dragLength,
+ const SkPoint& dragStart, const SkPoint& dragCurr,
+ const SkPoint& orig);
+
+ void warp(const SkPoint& p0, const SkPoint& p1) {
+ WarpProc proc = apply_warp2;
+ SkPoint delta = p1 - p0;
+ SkScalar length = SkPoint::Normalize(&delta);
+ for (int y = 0; y < fMesh.rows(); y++) {
+ for (int x = 0; x < fMesh.cols(); x++) {
+ fMesh.pt(x, y) = proc(delta, length, p0, p1, fOrig.pt(x, y));
+ }
+ }
+ fP0 = p0;
+ fP1 = p1;
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorLTGRAY);
+ // test_bigblur(canvas); return;
+
+ canvas->concat(fMatrix);
+
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+ paint.setShader(SkShader::CreateBitmapShader(fBitmap,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode))->unref();
+ fMesh.draw(canvas, paint); //return;
+
+ paint.setShader(NULL);
+ paint.setColor(SK_ColorRED);
+ fMesh.draw(canvas, paint);
+
+ // test_drag(canvas, fBitmap, fP0, fP1);
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ return new Click(this);
+ }
+
+ virtual bool onClick(Click* click) {
+ SkPoint pts[2] = { click->fOrig, click->fCurr };
+ fInverse.mapPoints(pts, 2);
+ this->warp(pts[0], pts[1]);
+ this->inval(NULL);
+ return true;
+ }
+
+private:
+ SkIRect fBase, fRect;
+ SkPoint fP0, fP1;
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new WarpView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleXfermodes.cpp b/samplecode/SampleXfermodes.cpp
new file mode 100644
index 0000000000..0a3c4c721e
--- /dev/null
+++ b/samplecode/SampleXfermodes.cpp
@@ -0,0 +1,250 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+
+static void setNamedTypeface(SkPaint* paint, const char name[]) {
+ SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+ paint->setTypeface(face);
+ SkSafeUnref(face);
+}
+
+#if 0
+static int newscale(U8CPU a, U8CPU b, int shift) {
+ unsigned prod = a * b + (1 << (shift - 1));
+ return (prod + (prod >> shift)) >> shift;
+}
+
+static void test_srcover565(SkCanvas* canvas) {
+ const int width = 32;
+ SkBitmap bm1, bm2, bm3;
+ bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL);
+ bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL);
+ bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL);
+
+ int rgb = 0x18;
+ int r = rgb >> 3;
+ int g = rgb >> 2;
+ uint16_t dst = SkPackRGB16(r, g, r);
+ for (int alpha = 0; alpha <= 255; alpha++) {
+ SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb);
+ uint16_t newdst = SkSrcOver32To16(pm, dst);
+ sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width());
+
+ int ia = 255 - alpha;
+ int iscale = SkAlpha255To256(ia);
+ int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3;
+ int dg = (SkGetPackedG32(pm) + (g * iscale >> 6)) >> 2;
+
+ sk_memset16(bm2.getAddr16(0, alpha), SkPackRGB16(dr, dg, dr), bm2.width());
+
+ int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3;
+ int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2;
+
+ sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width());
+
+// if (mr != dr || mg != dg)
+ {
+// SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2);
+ }
+ }
+
+ SkScalar dx = SkIntToScalar(width+4);
+
+ canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0);
+ canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0);
+ canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0);
+
+ SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) };
+ SkPaint p;
+ p.setARGB(0xFF, rgb, rgb, rgb);
+ canvas->drawRect(rect, p);
+}
+#endif
+
+static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
+ src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ src->allocPixels();
+ src->eraseColor(0);
+
+ SkCanvas c(*src);
+ SkPaint p;
+ SkRect r;
+ SkScalar ww = SkIntToScalar(w);
+ SkScalar hh = SkIntToScalar(h);
+
+ p.setAntiAlias(true);
+ p.setColor(0xFFFFCC44);
+ r.set(0, 0, ww*3/4, hh*3/4);
+ c.drawOval(r, p);
+
+ dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ dst->allocPixels();
+ dst->eraseColor(0);
+ c.setBitmapDevice(*dst);
+
+ p.setColor(0xFF66AAFF);
+ r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+ c.drawRect(r, p);
+}
+
+static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
+class XfermodesView : public SampleView {
+ SkBitmap fBG;
+ SkBitmap fSrcB, fDstB;
+
+ void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
+ SkScalar x, SkScalar y) {
+ SkPaint p;
+
+ canvas->drawBitmap(fSrcB, x, y, &p);
+ p.setAlpha(alpha);
+ p.setXfermode(mode);
+ canvas->drawBitmap(fDstB, x, y, &p);
+ }
+
+public:
+ const static int W = 64;
+ const static int H = 64;
+ XfermodesView() {
+ const int W = 64;
+ const int H = 64;
+
+ fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+ fBG.setPixels(gBG);
+ fBG.setIsOpaque(true);
+
+ make_bitmaps(W, H, &fSrcB, &fDstB);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "Xfermodes");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+ const struct {
+ SkXfermode::Mode fMode;
+ const char* fLabel;
+ } gModes[] = {
+ { SkXfermode::kClear_Mode, "Clear" },
+ { SkXfermode::kSrc_Mode, "Src" },
+ { SkXfermode::kDst_Mode, "Dst" },
+ { SkXfermode::kSrcOver_Mode, "SrcOver" },
+ { SkXfermode::kDstOver_Mode, "DstOver" },
+ { SkXfermode::kSrcIn_Mode, "SrcIn" },
+ { SkXfermode::kDstIn_Mode, "DstIn" },
+ { SkXfermode::kSrcOut_Mode, "SrcOut" },
+ { SkXfermode::kDstOut_Mode, "DstOut" },
+ { SkXfermode::kSrcATop_Mode, "SrcATop" },
+ { SkXfermode::kDstATop_Mode, "DstATop" },
+ { SkXfermode::kXor_Mode, "Xor" },
+
+ { SkXfermode::kPlus_Mode, "Plus" },
+ { SkXfermode::kMultiply_Mode, "Multiply" },
+ { SkXfermode::kScreen_Mode, "Screen" },
+ { SkXfermode::kOverlay_Mode, "Overlay" },
+ { SkXfermode::kDarken_Mode, "Darken" },
+ { SkXfermode::kLighten_Mode, "Lighten" },
+ { SkXfermode::kColorDodge_Mode, "ColorDodge" },
+ { SkXfermode::kColorBurn_Mode, "ColorBurn" },
+ { SkXfermode::kHardLight_Mode, "HardLight" },
+ { SkXfermode::kSoftLight_Mode, "SoftLight" },
+ { SkXfermode::kDifference_Mode, "Difference" },
+ { SkXfermode::kExclusion_Mode, "Exclusion" },
+ };
+
+ const SkScalar w = SkIntToScalar(W);
+ const SkScalar h = SkIntToScalar(H);
+ SkShader* s = SkShader::CreateBitmapShader(fBG,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ SkMatrix m;
+ m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+ s->setLocalMatrix(m);
+
+ SkPaint labelP;
+ labelP.setAntiAlias(true);
+ labelP.setLCDRenderText(true);
+ labelP.setTextAlign(SkPaint::kCenter_Align);
+ setNamedTypeface(&labelP, "Menlo Regular");
+// labelP.setTextSize(SkIntToScalar(11));
+
+ const int W = 5;
+
+ SkScalar x0 = 0;
+ for (int twice = 0; twice < 2; twice++) {
+ SkScalar x = x0, y = 0;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+ SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+ SkAutoUnref aur(mode);
+ SkRect r;
+ r.set(x, y, x+w, y+h);
+
+ SkPaint p;
+ p.setStyle(SkPaint::kFill_Style);
+ p.setShader(s);
+ canvas->drawRect(r, p);
+
+ canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ // canvas->save();
+ draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
+ canvas->restore();
+
+ r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setShader(NULL);
+ canvas->drawRect(r, p);
+
+#if 1
+ canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
+ x + w/2, y - labelP.getTextSize()/2, labelP);
+#endif
+ x += w + SkIntToScalar(10);
+ if ((i % W) == W - 1) {
+ x = x0;
+ y += h + SkIntToScalar(30);
+ }
+ }
+ x0 += SkIntToScalar(400);
+ }
+ s->unref();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new XfermodesView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
new file mode 100644
index 0000000000..166e4e5c7c
--- /dev/null
+++ b/samplecode/SampleXfermodesBlur.cpp
@@ -0,0 +1,182 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+#include "SkBlurMaskFilter.h"
+
+static void setNamedTypeface(SkPaint* paint, const char name[]) {
+ SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+ paint->setTypeface(face);
+ SkSafeUnref(face);
+}
+
+static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
+class XfermodesBlurView : public SampleView {
+ SkBitmap fBG;
+ SkBitmap fSrcB, fDstB;
+
+ void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
+ SkScalar x, SkScalar y) {
+ SkPaint p;
+ SkMaskFilter* mf = SkBlurMaskFilter::Create(5, SkBlurMaskFilter::kNormal_BlurStyle, 0);
+ p.setMaskFilter(mf)->unref();
+
+ SkScalar ww = SkIntToScalar(W);
+ SkScalar hh = SkIntToScalar(H);
+
+ // draw a circle covering the upper
+ // left three quarters of the canvas
+ p.setColor(0xFFCC44FF);
+ SkRect r;
+ r.set(0, 0, ww*3/4, hh*3/4);
+ r.offset(x, y);
+ canvas->drawOval(r, p);
+
+ p.setXfermode(mode);
+
+ // draw a square overlapping the circle
+ // in the lower right of the canvas
+ p.setColor(0x00AA6633 | alpha << 24);
+ r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+ r.offset(x, y);
+ canvas->drawRect(r, p);
+ }
+
+public:
+ const static int W = 64;
+ const static int H = 64;
+ XfermodesBlurView() {
+ const int W = 64;
+ const int H = 64;
+
+ fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+ fBG.setPixels(gBG);
+ fBG.setIsOpaque(true);
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "XfermodesBlur");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ virtual void onDrawContent(SkCanvas* canvas) {
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+ const struct {
+ SkXfermode::Mode fMode;
+ const char* fLabel;
+ } gModes[] = {
+ { SkXfermode::kClear_Mode, "Clear" },
+ { SkXfermode::kSrc_Mode, "Src" },
+ { SkXfermode::kDst_Mode, "Dst" },
+ { SkXfermode::kSrcOver_Mode, "SrcOver" },
+ { SkXfermode::kDstOver_Mode, "DstOver" },
+ { SkXfermode::kSrcIn_Mode, "SrcIn" },
+ { SkXfermode::kDstIn_Mode, "DstIn" },
+ { SkXfermode::kSrcOut_Mode, "SrcOut" },
+ { SkXfermode::kDstOut_Mode, "DstOut" },
+ { SkXfermode::kSrcATop_Mode, "SrcATop" },
+ { SkXfermode::kDstATop_Mode, "DstATop" },
+ { SkXfermode::kXor_Mode, "Xor" },
+
+ { SkXfermode::kPlus_Mode, "Plus" },
+ /*{ SkXfermode::kMultiply_Mode, "Multiply" },
+ { SkXfermode::kScreen_Mode, "Screen" },
+ { SkXfermode::kOverlay_Mode, "Overlay" },
+ { SkXfermode::kDarken_Mode, "Darken" },
+ { SkXfermode::kLighten_Mode, "Lighten" },
+ { SkXfermode::kColorDodge_Mode, "ColorDodge" },
+ { SkXfermode::kColorBurn_Mode, "ColorBurn" },
+ { SkXfermode::kHardLight_Mode, "HardLight" },
+ { SkXfermode::kSoftLight_Mode, "SoftLight" },
+ { SkXfermode::kDifference_Mode, "Difference" },
+ { SkXfermode::kExclusion_Mode, "Exclusion" },*/
+ };
+
+ const SkScalar w = SkIntToScalar(W);
+ const SkScalar h = SkIntToScalar(H);
+ SkShader* s = SkShader::CreateBitmapShader(fBG,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+ SkMatrix m;
+ m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+ s->setLocalMatrix(m);
+
+ SkPaint labelP;
+ labelP.setAntiAlias(true);
+ labelP.setLCDRenderText(true);
+ labelP.setTextAlign(SkPaint::kCenter_Align);
+ setNamedTypeface(&labelP, "Menlo Regular");
+
+ const int W = 5;
+
+ SkScalar x0 = 0;
+ for (int twice = 0; twice < 2; twice++) {
+ SkScalar x = x0, y = 0;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+ SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+ SkAutoUnref aur(mode);
+ SkRect r;
+ r.set(x, y, x+w, y+h);
+
+ SkPaint p;
+ p.setStyle(SkPaint::kFill_Style);
+ p.setShader(s);
+ canvas->drawRect(r, p);
+
+ canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
+ canvas->restore();
+
+ r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setShader(NULL);
+ canvas->drawRect(r, p);
+
+ canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
+ x + w/2, y - labelP.getTextSize()/2, labelP);
+ x += w + SkIntToScalar(10);
+ if ((i % W) == W - 1) {
+ x = x0;
+ y += h + SkIntToScalar(30);
+ }
+ }
+ x0 += SkIntToScalar(400);
+ }
+ s->unref();
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new XfermodesBlurView; }
+static SkViewRegister reg(MyFactory);
diff --git a/samplecode/samplecode_files.mk b/samplecode/samplecode_files.mk
new file mode 100644
index 0000000000..4c660e5bb3
--- /dev/null
+++ b/samplecode/samplecode_files.mk
@@ -0,0 +1,69 @@
+SOURCE := \
+ SampleBitmapRect.cpp \
+ SamplePathClip.cpp \
+ SampleComplexClip.cpp \
+ SampleNinePatch.cpp \
+ SampleAvoid.cpp \
+ SampleMeasure.cpp \
+ SampleArc.cpp \
+ SampleRepeatTile.cpp \
+ SampleApp.cpp \
+ vertexdump.cpp \
+ SampleShapes.cpp \
+ SampleMipMap.cpp \
+ SampleLCD.cpp \
+ SampleCamera.cpp \
+ SampleVertices.cpp \
+ SampleFontScalerTest.cpp \
+ SampleBigGradient.cpp \
+ SampleAll.cpp \
+ SampleShaderText.cpp \
+ SamplePolyToPoly.cpp \
+ SampleBlur.cpp \
+ SampleHairline.cpp \
+ SampleCircle.cpp \
+ SampleOvalTest.cpp \
+ SampleLines.cpp \
+ SampleOverflow.cpp \
+ SampleStrokePath.cpp \
+ SampleSlides.cpp \
+ SampleLayers.cpp \
+ SampleTiling.cpp \
+ SampleTinyBitmap.cpp \
+ SampleXfermodes.cpp \
+ SampleDrawLooper.cpp \
+ SampleTextEffects.cpp \
+ SampleTextOnPath.cpp \
+ SampleDitherBitmap.cpp \
+ SampleExtractAlpha.cpp \
+ SampleDither.cpp \
+ SampleEncode.cpp \
+ SampleFontCache.cpp \
+ SampleGradients.cpp \
+ SampleTypeface.cpp \
+ SampleFillType.cpp \
+ SamplePath.cpp \
+ SampleLayerMask.cpp \
+ SampleStrokeText.cpp \
+ SamplePathEffects.cpp \
+ SampleTextAlpha.cpp \
+ ClockFaceView.cpp \
+ SampleEmboss.cpp \
+ SamplePoints.cpp \
+ SampleFilter2.cpp \
+ SamplePatch.cpp \
+ SampleFilter.cpp \
+ OverView.cpp \
+ SampleFuzz.cpp \
+ SampleShaders.cpp \
+ SampleText.cpp \
+ SampleTextBox.cpp \
+ SampleImage.cpp \
+ SampleMovie.cpp \
+ SampleImageDir.cpp \
+ SampleWarp.cpp \
+ SamplePageFlip.cpp \
+ SamplePicture.cpp \
+ SampleLineClipper.cpp \
+ SampleRegion.cpp \
+ SampleDecode.cpp \ Crashes
diff --git a/samplecode/vertexdump.cpp b/samplecode/vertexdump.cpp
new file mode 100644
index 0000000000..c124ad85b0
--- /dev/null
+++ b/samplecode/vertexdump.cpp
@@ -0,0 +1,88 @@
+#include "SkPoint.h"
+
+void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]);
+
+void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]) {
+ verts[0].set(SkFloatToScalar(107), SkFloatToScalar(189));
+ texs[0].set(SkFloatToScalar(0), SkFloatToScalar(0));
+ verts[1].set(SkFloatToScalar(116), SkFloatToScalar(189));
+ texs[1].set(SkFloatToScalar(9), SkFloatToScalar(0));
+ verts[2].set(SkFloatToScalar(203), SkFloatToScalar(189));
+ texs[2].set(SkFloatToScalar(35), SkFloatToScalar(0));
+ verts[3].set(SkFloatToScalar(212), SkFloatToScalar(189));
+ texs[3].set(SkFloatToScalar(44), SkFloatToScalar(0));
+ verts[4].set(SkFloatToScalar(107), SkFloatToScalar(198));
+ texs[4].set(SkFloatToScalar(0), SkFloatToScalar(9));
+ verts[5].set(SkFloatToScalar(116), SkFloatToScalar(198));
+ texs[5].set(SkFloatToScalar(9), SkFloatToScalar(9));
+ verts[6].set(SkFloatToScalar(203), SkFloatToScalar(198));
+ texs[6].set(SkFloatToScalar(35), SkFloatToScalar(9));
+ verts[7].set(SkFloatToScalar(212), SkFloatToScalar(198));
+ texs[7].set(SkFloatToScalar(44), SkFloatToScalar(9));
+ verts[8].set(SkFloatToScalar(107), SkFloatToScalar(224));
+ texs[8].set(SkFloatToScalar(0), SkFloatToScalar(39));
+ verts[9].set(SkFloatToScalar(116), SkFloatToScalar(224));
+ texs[9].set(SkFloatToScalar(9), SkFloatToScalar(39));
+ verts[10].set(SkFloatToScalar(203), SkFloatToScalar(224));
+ texs[10].set(SkFloatToScalar(35), SkFloatToScalar(39));
+ verts[11].set(SkFloatToScalar(212), SkFloatToScalar(224));
+ texs[11].set(SkFloatToScalar(44), SkFloatToScalar(39));
+ verts[12].set(SkFloatToScalar(107), SkFloatToScalar(233));
+ texs[12].set(SkFloatToScalar(0), SkFloatToScalar(48));
+ verts[13].set(SkFloatToScalar(116), SkFloatToScalar(233));
+ texs[13].set(SkFloatToScalar(9), SkFloatToScalar(48));
+ verts[14].set(SkFloatToScalar(203), SkFloatToScalar(233));
+ texs[14].set(SkFloatToScalar(35), SkFloatToScalar(48));
+ verts[15].set(SkFloatToScalar(212), SkFloatToScalar(233));
+ texs[15].set(SkFloatToScalar(44), SkFloatToScalar(48));
+ index[0] = 0; index[1] = 5; index[2] = 1;
+ index[3] = 0; index[4] = 4; index[5] = 5;
+#if 0
+ index[6] = 1; index[7] = 6; index[8] = 2;
+#else
+ index[6] = 6; index[7] = 2; index[8] = 1;
+#endif
+ index[9] = 1; index[10] = 5; index[11] = 6;
+ index[12] = 2;
+ index[13] = 7;
+ index[14] = 3;
+ index[15] = 2;
+ index[16] = 6;
+ index[17] = 7;
+ index[18] = 4;
+ index[19] = 9;
+ index[20] = 5;
+ index[21] = 4;
+ index[22] = 8;
+ index[23] = 9;
+ index[24] = 5;
+ index[25] = 10;
+ index[26] = 6;
+ index[27] = 5;
+ index[28] = 9;
+ index[29] = 10;
+ index[30] = 6;
+ index[31] = 11;
+ index[32] = 7;
+ index[33] = 6;
+ index[34] = 10;
+ index[35] = 11;
+ index[36] = 8;
+ index[37] = 13;
+ index[38] = 9;
+ index[39] = 8;
+ index[40] = 12;
+ index[41] = 13;
+ index[42] = 9;
+ index[43] = 14;
+ index[44] = 10;
+ index[45] = 9;
+ index[46] = 13;
+ index[47] = 14;
+ index[48] = 10;
+ index[49] = 15;
+ index[50] = 11;
+ index[51] = 10;
+ index[52] = 14;
+ index[53] = 15;
+}
diff --git a/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
index b9e65f741f..e456f26118 100644
--- a/src/animator/SkDisplayApply.cpp
+++ b/src/animator/SkDisplayApply.cpp
@@ -294,7 +294,7 @@ bool SkApply::enable(SkAnimateMaker& maker) {
if ((mode == kMode_immediate || mode == kMode_create) && scope == NULL)
return false; // !!! error?
bool enableMe = scope && (scope->hasEnable() || scope->isApply() || scope->isDrawable() == false);
- if (mode == kMode_immediate && enableMe || mode == kMode_create)
+ if ((mode == kMode_immediate && enableMe) || mode == kMode_create)
activate(maker); // for non-drawables like post, prime them here
if (mode == kMode_immediate && enableMe)
fActive->enable();
@@ -356,7 +356,7 @@ bool SkApply::enable(SkAnimateMaker& maker) {
if (old < 0)
goto append;
else if (fContainsScope) {
- if ((*parentList)[old] != this || restore == true) {
+ if ((*parentList)[old] != this || restore) {
append:
if (parentGroup)
parentGroup->markCopySize(old);
@@ -479,7 +479,7 @@ void SkApply::endSave(int index) {
} else {
SkScriptValue scriptValue;
bool success = target->getProperty(info->propertyIndex(), &scriptValue);
- SkASSERT(success = true);
+ SkASSERT(success == true);
last[0] = scriptValue.fOperand;
scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
target->setProperty(info->propertyIndex(), scriptValue);
@@ -624,8 +624,8 @@ bool SkApply::interpolate(SkAnimateMaker& maker, SkMSec rawTime) {
SkInterpolatorBase::Result interpResult = fActive->fInterpolators[inner]->timeToValues(
innerTime, values.get());
result |= (interpResult != SkInterpolatorBase::kFreezeEnd_Result);
- if ((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result ||
- transition == SkApply::kTransition_reverse && fLastTime == 0) && state.fUnpostedEndEvent) {
+ if (((transition != SkApply::kTransition_reverse && interpResult == SkInterpolatorBase::kFreezeEnd_Result) ||
+ (transition == SkApply::kTransition_reverse && fLastTime == 0)) && state.fUnpostedEndEvent) {
// SkDEBUGF(("interpolate: post on end\n"));
state.fUnpostedEndEvent = false;
maker.postOnEnd(animate, state.fBegin + state.fDuration);
diff --git a/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
index a94b8480b0..db6c83848c 100644
--- a/src/animator/SkDisplayXMLParser.cpp
+++ b/src/animator/SkDisplayXMLParser.cpp
@@ -304,8 +304,8 @@ const SkMemberInfo* SkDisplayXMLParser::searchContainer(const SkMemberInfo* info
}
return info;
next:
- if (type == SkType_Drawable || type == SkType_Displayable &&
- container->fDisplayable->isDrawable()) {
+ if (type == SkType_Drawable || (type == SkType_Displayable &&
+ container->fDisplayable->isDrawable())) {
rectNext:
if (fParents.count() > 1) {
Parent* parent = fParents.end() - 2;
diff --git a/src/animator/SkDump.cpp b/src/animator/SkDump.cpp
index eac956c136..426a1e649f 100644
--- a/src/animator/SkDump.cpp
+++ b/src/animator/SkDump.cpp
@@ -58,7 +58,7 @@ bool SkDump::enable(SkAnimateMaker& maker ) {
maker.fEvents.dump(maker);
if ((hasAttr |= (name.size() > 0)) == true)
maker.dump(name.c_str());
- if (displayList > 0 || displayList != 0 && hasAttr == false)
+ if (displayList > 0 || (displayList != 0 && hasAttr == false))
maker.fDisplayList.dump(&maker);
return true;
}
diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
index f81147d4a2..96b73cdb2e 100644
--- a/src/animator/SkScript.cpp
+++ b/src/animator/SkScript.cpp
@@ -305,8 +305,8 @@ twoChar:
} while (true);
signed char topPrecedence = gPrecedence[compare];
SkASSERT(topPrecedence != -1);
- if (topPrecedence > precedence || topPrecedence == precedence &&
- gOpAttributes[op].fLeftType == kNoType) {
+ if (topPrecedence > precedence || (topPrecedence == precedence &&
+ gOpAttributes[op].fLeftType == kNoType)) {
break;
}
if (processOp() == false)
@@ -1207,7 +1207,7 @@ noMatch:
} break;
case kElse:
flipSuppress:
- if (fSuppressStack.top().fElse == true)
+ if (fSuppressStack.top().fElse)
fSuppressStack.pop();
fSuppressStack.top().fElse = true;
fSuppressStack.top().fSuppress ^= true;
diff --git a/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp
index d582d335d4..98db1fb327 100644
--- a/src/animator/SkScriptDecompile.cpp
+++ b/src/animator/SkScriptDecompile.cpp
@@ -114,7 +114,7 @@ static size_t gOperandNamesSize = sizeof(gOperandNames) / sizeof(gOperandNames[0
// check to see that there are no missing or duplicate entries
void SkScriptEngine2::ValidateDecompileTable() {
SkScriptEngine2::TypeOp op = SkScriptEngine2::kNop;
- int index;
+ size_t index;
for (index = 0; index < gOpNamesSize; index++) {
SkASSERT(gOpNames[index].fOp == op);
op = (SkScriptEngine2::TypeOp) (op + 1);
@@ -132,9 +132,9 @@ void SkScriptEngine2::decompile(const unsigned char* start, size_t length) {
SkASSERT(length > 0);
const unsigned char* opCode = start;
do {
- SkASSERT(opCode - start < length);
+ SkASSERT((size_t)(opCode - start) < length);
SkScriptEngine2::TypeOp op = (SkScriptEngine2::TypeOp) *opCode++;
- SkASSERT(op < gOpNamesSize);
+ SkASSERT((size_t)op < gOpNamesSize);
SkDebugf("%d: %s", opCode - start - 1, gOpNames[op].fName);
switch (op) {
case SkScriptEngine2::kCallback: {
@@ -184,7 +184,7 @@ void SkScriptEngine2::decompile(const unsigned char* start, size_t length) {
SkOperand2::OpType type;
memcpy(&type, opCode, sizeof(type));
opCode += sizeof(type);
- int index = 0;
+ size_t index = 0;
if (type == 0)
SkDebugf(" type: %s", gOperandNames[index].fName);
else {
@@ -211,6 +211,8 @@ void SkScriptEngine2::decompile(const unsigned char* start, size_t length) {
goto done;
case SkScriptEngine2::kNop:
SkASSERT(0);
+ default:
+ break;
}
SkDebugf("\n");
} while (true);
diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
index 6d8c208d2d..12841983bd 100644
--- a/src/animator/SkScriptRuntime.cpp
+++ b/src/animator/SkScriptRuntime.cpp
@@ -304,6 +304,8 @@ bool SkScriptRuntime::executeTokens(unsigned char* opCode) {
goto done;
case SkScriptEngine2::kNop:
SkASSERT(0);
+ default:
+ break;
}
} while (true);
done:
diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
index efd187291c..edcc2af441 100644
--- a/src/animator/SkScriptTokenizer.cpp
+++ b/src/animator/SkScriptTokenizer.cpp
@@ -8,34 +8,34 @@
#include "SkOpArray.h"
const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = {
-{ SkOperand2::kNoType },
+{ SkOperand2::kNoType, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean },
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
- SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString }, // kAdd
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitAnd
-{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kBitNot
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kBitOr
+ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString, kResultIsNotBoolean }, // kAdd
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitAnd
+{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitNot
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitOr
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
- SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kDivide
+ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kDivide
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber,
kResultIsBoolean }, // kEqual
-{ SkOperand2::kS32 }, // kFlipOps
+{ SkOperand2::kS32, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean }, // kFlipOps
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsNumber,
kResultIsBoolean }, // kGreaterEqual
-{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalAnd (really, ToBool)
-{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias }, // kLogicalNot
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kLogicalOr
-{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMinus
+{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalAnd (really, ToBool)
+{ SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalNot
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalOr
+{ SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMinus
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
- SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias }, // kModulo
+ SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias, kResultIsNotBoolean }, // kModulo
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
- SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kMultiply
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftLeft
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias }, // kShiftRight
+ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMultiply
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftLeft
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftRight
{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
- SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias }, // kSubtract
-{ SkOperand2::kS32, SkOperand2::kS32, kNoBias } // kXor
+ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kSubtract
+{ SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean } // kXor
};
#define kBracketPrecedence 16
@@ -308,8 +308,8 @@ twoChar:
} while (true);
signed char topPrecedence = gPrecedence[compare];
SkASSERT(topPrecedence != -1);
- if (topPrecedence > precedence || topPrecedence == precedence &&
- gOpAttributes[op].fLeftType == SkOperand2::kNoType) {
+ if (topPrecedence > precedence || (topPrecedence == precedence &&
+ gOpAttributes[op].fLeftType == SkOperand2::kNoType)) {
break;
}
processOp();
@@ -1051,7 +1051,8 @@ bool SkScriptEngine2::processOp() {
fOpStack.pop(&op);
op = (Op) (op & ~kArtificialOp);
const OperatorAttributes* attributes = &gOpAttributes[op];
- SkScriptValue2 value1 = { 0 };
+ SkScriptValue2 value1;
+ memset(&value1, 0, sizeof(SkScriptValue2));
SkScriptValue2 value2;
fValueStack.pop(&value2);
value2.fIsWritten = SkScriptValue2::kUnwritten;
@@ -1230,7 +1231,7 @@ bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toTy
SkScalar SkScriptEngine2::IntToScalar(int32_t s32) {
SkScalar scalar;
- if (s32 == SK_NaN32)
+ if (s32 == (int32_t) SK_NaN32)
scalar = SK_ScalarNaN;
else if (SkAbs32(s32) == SK_MaxS32)
scalar = SkSign32(s32) * SK_ScalarMax;
@@ -1261,21 +1262,21 @@ bool SkScriptEngine2::ValueToString(const SkScriptValue2& value, SkString* strin
#ifdef SK_DEBUG
-#define testInt(expression) { #expression, SkOperand2::kS32, expression }
+#define testInt(expression) { #expression, SkOperand2::kS32, expression, 0, NULL }
#ifdef SK_SCALAR_IS_FLOAT
-#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression }
-#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2) }
+#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression, NULL }
+#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2), NULL }
#else
#ifdef SK_CAN_USE_FLOAT
-#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f) }
-#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2) * 65536.0f) }
+#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f), NULL }
+#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2) * 65536.0f), NULL }
#endif
#endif
-#define testTrue(expression) { #expression, SkOperand2::kS32, 1 }
-#define testFalse(expression) { #expression, SkOperand2::kS32, 0 }
+#define testTrue(expression) { #expression, SkOperand2::kS32, 1, 0, NULL }
+#define testFalse(expression) { #expression, SkOperand2::kS32, 0, 0, NULL }
static const SkScriptNAnswer2 scriptTests[] = {
- testInt(1||0&&3),
+ testInt(1||(0&&3)),
#ifdef SK_CAN_USE_FLOAT
testScalar(- -5.5- -1.5),
testScalar(1.0+5),
@@ -1307,12 +1308,12 @@ static const SkScriptNAnswer2 scriptTests[] = {
{ "'123'+\"456\"", SkOperand2::kString, 0, 0, "123456" },
{ "123+\"456\"", SkOperand2::kString, 0, 0, "123456" },
{ "'123'+456", SkOperand2::kString, 0, 0, "123456" },
- { "'123'|\"456\"", SkOperand2::kS32, 123|456 },
- { "123|\"456\"", SkOperand2::kS32, 123|456 },
- { "'123'|456", SkOperand2::kS32, 123|456 },
- { "'2'<11", SkOperand2::kS32, 1 },
- { "2<'11'", SkOperand2::kS32, 1 },
- { "'2'<'11'", SkOperand2::kS32, 0 },
+ { "'123'|\"456\"", SkOperand2::kS32, 123|456, 0, NULL },
+ { "123|\"456\"", SkOperand2::kS32, 123|456, 0, NULL },
+ { "'123'|456", SkOperand2::kS32, 123|456, 0, NULL },
+ { "'2'<11", SkOperand2::kS32, 1, 0, NULL },
+ { "2<'11'", SkOperand2::kS32, 1, 0, NULL },
+ { "'2'<'11'", SkOperand2::kS32, 0, 0, NULL },
testInt(123),
testInt(-345),
testInt(+678),
@@ -1461,15 +1462,15 @@ static const SkScriptNAnswer2 scriptTests[] = {
// logic
testInt(1?2:3),
testInt(0?2:3),
- testInt(1&&2||3),
- testInt(1&&0||3),
- testInt(1&&0||0),
- testInt(1||0&&3),
- testInt(0||0&&3),
- testInt(0||1&&3),
+ testInt((1&&2)||3),
+ testInt((1&&0)||3),
+ testInt((1&&0)||0),
+ testInt(1||(0&&3)),
+ testInt(0||(0&&3)),
+ testInt(0||(1&&3)),
testInt(0&&1?2:3)
#ifdef SK_CAN_USE_FLOAT
- , { "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2 }
+ , { "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2, NULL }
#endif
};
diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp
index 7e8a03087b..05d5c28e6a 100644
--- a/src/core/SkAdvancedTypefaceMetrics.cpp
+++ b/src/core/SkAdvancedTypefaceMetrics.cpp
@@ -22,6 +22,10 @@
#include FT_FREETYPE_H
#endif
+#ifdef SK_BUILD_FOR_MAC
+#include <Carbon/Carbon.h>
+#endif
+
namespace skia_advanced_typeface_metrics_utils {
template <typename Data>
@@ -142,6 +146,11 @@ template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
FT_Face face,
int num_glyphs,
bool (*getAdvance)(FT_Face face, int gId, int16_t* data));
+#elif defined(SK_BUILD_FOR_MAC)
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+ CTFontRef ctFont,
+ int num_glyphs,
+ bool (*getAdvance)(CTFontRef ctFont, int gId, int16_t* data));
#endif
template void resetRange(
SkAdvancedTypefaceMetrics::WidthRange* range,
diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
index 4125b583fd..a5fc3c9f9f 100644
--- a/src/core/SkAlphaRuns.cpp
+++ b/src/core/SkAlphaRuns.cpp
@@ -16,10 +16,14 @@
*/
#include "SkAntiRun.h"
+#include "SkUtils.h"
void SkAlphaRuns::reset(int width) {
SkASSERT(width > 0);
+#ifdef SK_DEBUG
+ sk_memset16((uint16_t*)fRuns, (uint16_t)(-42), width);
+#endif
fRuns[0] = SkToS16(width);
fRuns[width] = 0;
fAlpha[0] = 0;
@@ -75,13 +79,17 @@ void SkAlphaRuns::Break(int16_t runs[], uint8_t alpha[], int x, int count) {
}
}
-void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
- U8CPU maxValue) {
+int SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
+ U8CPU maxValue, int offsetX) {
SkASSERT(middleCount >= 0);
SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth);
- int16_t* runs = fRuns;
- uint8_t* alpha = fAlpha;
+ SkASSERT(fRuns[offsetX] >= 0);
+
+ int16_t* runs = fRuns + offsetX;
+ uint8_t* alpha = fAlpha + offsetX;
+ uint8_t* lastAlpha = alpha;
+ x -= offsetX;
if (startAlpha) {
SkAlphaRuns::Break(runs, alpha, x, 1);
@@ -97,6 +105,7 @@ void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
runs += x + 1;
alpha += x + 1;
x = 0;
+ lastAlpha += x; // we don't want the +1
SkDEBUGCODE(this->validate();)
}
@@ -114,13 +123,18 @@ void SkAlphaRuns::add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
middleCount -= n;
} while (middleCount > 0);
SkDEBUGCODE(this->validate();)
+ lastAlpha = alpha;
}
if (stopAlpha) {
SkAlphaRuns::Break(runs, alpha, x, 1);
- alpha[x] = SkToU8(alpha[x] + stopAlpha);
+ alpha += x;
+ alpha[0] = SkToU8(alpha[0] + stopAlpha);
SkDEBUGCODE(this->validate();)
+ lastAlpha = alpha;
}
+
+ return lastAlpha - fAlpha; // new offsetX
}
#ifdef SK_DEBUG
diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
index 89e54819c4..669e5d227a 100644
--- a/src/core/SkAntiRun.h
+++ b/src/core/SkAntiRun.h
@@ -31,7 +31,15 @@ public:
}
void reset(int width);
- void add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha, U8CPU maxValue);
+
+ /**
+ * Returns the offsetX value that should be passed on the next call,
+ * assuming we're on the same scanline. If the caller is switching
+ * scanlines, then offsetX should be 0 when this is called.
+ */
+ int add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
+ U8CPU maxValue, int offsetX);
+
SkDEBUGCODE(void assertValid(int y, int maxStep) const;)
SkDEBUGCODE(void dump() const;)
diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
index 9ae8b178e7..553bd898ad 100644
--- a/src/core/SkBitmapProcState_matrix.h
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -1,3 +1,19 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
#define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale)
#define SCALE_FILTER_NAME MAKENAME(_filter_scale)
diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h
index 15831b67bf..9a9e8c40be 100644
--- a/src/core/SkBitmapProcState_shaderproc.h
+++ b/src/core/SkBitmapProcState_shaderproc.h
@@ -1,3 +1,20 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
#define SCALE_FILTER_NAME MAKENAME(_filter_DX_shaderproc)
static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y,
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 5ffa5b1d95..4ad4d41926 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -188,6 +188,11 @@ bool operator==(const SkClipStack::B2FIter::Clip& a,
((a.fPath == NULL && b.fPath == NULL) || *a.fPath == *b.fPath);
}
+bool operator!=(const SkClipStack::B2FIter::Clip& a,
+ const SkClipStack::B2FIter::Clip& b) {
+ return !(a == b);
+}
+
SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
this->reset(stack);
}
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 4a7693673f..aee5a5ea2a 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -25,13 +25,13 @@ SkDeviceFactory::~SkDeviceFactory() {
///////////////////////////////////////////////////////////////////////////////
-SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas), fMetaData(NULL), fMatrixClipObserver(NULL) {
+SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas), fMetaData(NULL) {
fOrigin.setZero();
fCachedDeviceFactory = NULL;
}
SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer)
- : fCanvas(canvas), fBitmap(bitmap), fMetaData(NULL), fMatrixClipObserver(NULL) {
+ : fCanvas(canvas), fBitmap(bitmap), fMetaData(NULL) {
fOrigin.setZero();
// auto-allocate if we're for offscreen drawing
if (isForLayer) {
@@ -48,7 +48,6 @@ SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer)
SkDevice::~SkDevice() {
delete fMetaData;
SkSafeUnref(fCachedDeviceFactory);
- SkSafeUnref(fMatrixClipObserver);
}
SkDeviceFactory* SkDevice::onNewDeviceFactory() {
@@ -108,13 +107,6 @@ void SkDevice::onAccessBitmap(SkBitmap* bitmap) {}
void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region,
const SkClipStack& clipStack) {
- if (fMatrixClipObserver) {
- fMatrixClipObserver->matrixClipChanged(matrix, region, clipStack);
- }
-}
-
-void SkDevice::setMatrixClipObserver(SkMatrixClipObserver* observer) {
- SkRefCnt_SafeAssign(fMatrixClipObserver, observer);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 983c8d21f2..06bca1e59d 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -119,6 +119,21 @@ uint8_t SkMatrix::computeTypeMask() const {
///////////////////////////////////////////////////////////////////////////////
+#ifdef SK_SCALAR_IS_FLOAT
+
+bool operator==(const SkMatrix& a, const SkMatrix& b) {
+ const SkScalar* SK_RESTRICT ma = a.fMat;
+ const SkScalar* SK_RESTRICT mb = b.fMat;
+
+ return ma[0] == mb[0] && ma[1] == mb[1] && ma[2] == mb[2] &&
+ ma[3] == mb[3] && ma[4] == mb[4] && ma[5] == mb[5] &&
+ ma[6] == mb[6] && ma[7] == mb[7] && ma[8] == mb[8];
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
fMat[kMTransX] = dx;
@@ -202,9 +217,27 @@ bool SkMatrix::preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
}
bool SkMatrix::preScale(SkScalar sx, SkScalar sy) {
+#ifdef SK_SCALAR_IS_FIXED
SkMatrix m;
m.setScale(sx, sy);
return this->preConcat(m);
+#else
+ // the assumption is that these multiplies are very cheap, and that
+ // a full concat and/or just computing the matrix type is more expensive.
+ // Also, the fixed-point case checks for overflow, but the float doesn't,
+ // so we can get away with these blind multiplies.
+
+ fMat[kMScaleX] = SkScalarMul(fMat[kMScaleX], sx);
+ fMat[kMSkewY] = SkScalarMul(fMat[kMSkewY], sx);
+ fMat[kMPersp0] = SkScalarMul(fMat[kMPersp0], sx);
+
+ fMat[kMSkewX] = SkScalarMul(fMat[kMSkewX], sy);
+ fMat[kMScaleY] = SkScalarMul(fMat[kMScaleY], sy);
+ fMat[kMPersp1] = SkScalarMul(fMat[kMPersp1], sy);
+
+ this->orTypeMask(kScale_Mask);
+ return true;
+#endif
}
bool SkMatrix::postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 52b0163afa..a60742449a 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -1303,17 +1303,36 @@ void SkScalerContext::MakeRec(const SkPaint& paint,
}
rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
- rec->fFlags = SkToU8(flags);
- rec->setHinting(computeHinting(paint));
+
+ if (SkMask::kLCD16_Format == rec->fMaskFormat) {
+ SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
+ SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
+ if (SkFontHost::kNONE_LCDOrder == order) {
+ // eeek, can't support LCD
+ rec->fMaskFormat = SkMask::kA8_Format;
+ } else {
+ if (SkFontHost::kVertical_LCDOrientation == orient) {
+ flags |= SkScalerContext::kLCD_Vertical_Flag;
+ }
+ if (SkFontHost::kBGR_LCDOrder == order) {
+ flags |= SkScalerContext::kLCD_BGROrder_Flag;
+ }
+ }
+ }
+
if (paint.isEmbeddedBitmapText()) {
- rec->fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag;
+ flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
}
if (paint.isSubpixelText()) {
- rec->fFlags |= SkScalerContext::kSubpixelPositioning_Flag;
+ flags |= SkScalerContext::kSubpixelPositioning_Flag;
}
if (paint.isAutohinted()) {
- rec->fFlags |= SkScalerContext::kAutohinting_Flag;
+ flags |= SkScalerContext::kAutohinting_Flag;
}
+ rec->fFlags = SkToU16(flags);
+
+ // setHinting modifies fFlags, so do this last
+ rec->setHinting(computeHinting(paint));
/* Allow the fonthost to modify our rec before we use it as a key into the
cache. This way if we're asking for something that they will ignore,
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 68eb35d1b1..ca57237a53 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -254,6 +254,12 @@ void SkPath::setConvexity(Convexity c) {
//////////////////////////////////////////////////////////////////////////////
// Construction methods
+#define DIRTY_AFTER_EDIT \
+ do { \
+ fBoundsIsDirty = true; \
+ fConvexity = kUnknown_Convexity;\
+ } while (0)
+
void SkPath::incReserve(U16CPU inc) {
SkDEBUGCODE(this->validate();)
@@ -278,7 +284,7 @@ void SkPath::moveTo(SkScalar x, SkScalar y) {
pt->set(x, y);
GEN_ID_INC;
- fBoundsIsDirty = true;
+ DIRTY_AFTER_EDIT;
}
void SkPath::rMoveTo(SkScalar x, SkScalar y) {
@@ -298,7 +304,7 @@ void SkPath::lineTo(SkScalar x, SkScalar y) {
*fVerbs.append() = kLine_Verb;
GEN_ID_INC;
- fBoundsIsDirty = true;
+ DIRTY_AFTER_EDIT;
}
void SkPath::rLineTo(SkScalar x, SkScalar y) {
@@ -321,7 +327,7 @@ void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
*fVerbs.append() = kQuad_Verb;
GEN_ID_INC;
- fBoundsIsDirty = true;
+ DIRTY_AFTER_EDIT;
}
void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
@@ -345,7 +351,7 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
*fVerbs.append() = kCubic_Verb;
GEN_ID_INC;
- fBoundsIsDirty = true;
+ DIRTY_AFTER_EDIT;
}
void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
@@ -1293,7 +1299,7 @@ void SkPath::unflatten(SkReader32& buffer) {
buffer.read(fVerbs.begin(), fVerbs.count());
GEN_ID_INC;
- fBoundsIsDirty = true;
+ DIRTY_AFTER_EDIT;
SkDEBUGCODE(this->validate();)
}
@@ -1410,7 +1416,7 @@ static int CrossProductSign(const SkVector& a, const SkVector& b) {
// only valid for a single contour
struct Convexicator {
- Convexicator() : fPtCount(0), fConvexity(SkPath::kUnknown_Convexity) {
+ Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) {
fSign = 0;
// warnings
fCurrPt.set(0, 0);
@@ -1472,9 +1478,7 @@ private:
if (0 == fSign) {
fSign = sign;
} else if (sign) {
- if (fSign == sign) {
- fConvexity = SkPath::kConvex_Convexity;
- } else {
+ if (fSign != sign) {
fConvexity = SkPath::kConcave_Convexity;
}
}
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index 5c5ea0b7c9..105df3ce83 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -28,9 +28,9 @@ SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap,
}
SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) {
- int32_t size = sizeof(SkMatrix);
+ size_t size = matrix.flatten(NULL);
SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index);
- memcpy(&result->fMatrixData, &matrix, sizeof(SkMatrix));
+ matrix.flatten(&result->fMatrixData);
return result;
}
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index dae7b8aed0..697b399a6a 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -147,7 +147,7 @@ public:
static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
void unflatten(SkMatrix* result) const {
- memcpy(result, fMatrixData, sizeof(SkMatrix));
+ result->unflatten(fMatrixData);
}
#ifdef SK_DEBUG_DUMP
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index 5457b59cdc..398f786c95 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -53,7 +53,7 @@ protected:
int fWidth, fLeft, fSuperLeft;
SkDEBUGCODE(int fCurrX;)
- SkDEBUGCODE(int fCurrY;)
+ int fCurrY;
};
BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
@@ -69,7 +69,8 @@ BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
fSuperLeft = left << SHIFT;
fWidth = right - left;
fCurrIY = -1;
- SkDEBUGCODE(fCurrX = -1; fCurrY = -1;)
+ fCurrY = -1;
+ SkDEBUGCODE(fCurrX = -1;)
}
class SuperBlitter : public BaseSuperBlitter {
@@ -89,6 +90,7 @@ public:
private:
SkAlphaRuns fRuns;
+ int fOffsetX;
};
SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
@@ -100,6 +102,8 @@ SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
fRuns.fRuns = (int16_t*)sk_malloc_throw((width + 1 + (width + 2)/2) * sizeof(int16_t));
fRuns.fAlpha = (uint8_t*)(fRuns.fRuns + width + 1);
fRuns.reset(width);
+
+ fOffsetX = 0;
}
void SuperBlitter::flush() {
@@ -108,6 +112,7 @@ void SuperBlitter::flush() {
// SkDEBUGCODE(fRuns.dump();)
fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns);
fRuns.reset(fWidth);
+ fOffsetX = 0;
}
fCurrIY = -1;
SkDEBUGCODE(fCurrX = -1;)
@@ -134,11 +139,14 @@ void SuperBlitter::blitH(int x, int y, int width) {
}
#ifdef SK_DEBUG
- SkASSERT(y >= fCurrY);
SkASSERT(y != fCurrY || x >= fCurrX);
- fCurrY = y;
#endif
-
+ SkASSERT(y >= fCurrY);
+ if (fCurrY != y) {
+ fOffsetX = 0;
+ fCurrY = y;
+ }
+
if (iy != fCurrIY) { // new scanline
this->flush();
fCurrIY = iy;
@@ -148,36 +156,29 @@ void SuperBlitter::blitH(int x, int y, int width) {
// hit 256 as a summed max, but 255.
// int maxValue = (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT);
-#if 0
- SkAntiRun<SHIFT> arun;
- arun.set(x, x + width);
- fRuns.add(x >> SHIFT, arun.getStartAlpha(), arun.getMiddleCount(),
- arun.getStopAlpha(), maxValue);
-#else
- {
- int start = x;
- int stop = x + width;
-
- SkASSERT(start >= 0 && stop > start);
- int fb = start & SUPER_Mask;
- int fe = stop & SUPER_Mask;
- int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
-
- if (n < 0) {
- fb = fe - fb;
- n = 0;
- fe = 0;
+ int start = x;
+ int stop = x + width;
+
+ SkASSERT(start >= 0 && stop > start);
+ int fb = start & SUPER_Mask;
+ int fe = stop & SUPER_Mask;
+ int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
+
+ if (n < 0) {
+ fb = fe - fb;
+ n = 0;
+ fe = 0;
+ } else {
+ if (fb == 0) {
+ n += 1;
} else {
- if (fb == 0) {
- n += 1;
- } else {
- fb = (1 << SHIFT) - fb;
- }
+ fb = (1 << SHIFT) - fb;
}
- fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe),
- (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT));
}
-#endif
+
+ fOffsetX = fRuns.add(x >> SHIFT, coverage_to_alpha(fb), n, coverage_to_alpha(fe),
+ (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT),
+ fOffsetX);
#ifdef SK_DEBUG
fRuns.assertValid(y & MASK, (1 << (8 - SHIFT)));
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
index b84c576c15..52f2a321aa 100644
--- a/src/core/SkScan_Antihair.cpp
+++ b/src/core/SkScan_Antihair.cpp
@@ -653,6 +653,8 @@ void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
}
}
+#endif // SK_SCALAR_IS_FLOAT
+
///////////////////////////////////////////////////////////////////////////////
#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
@@ -811,7 +813,3 @@ void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
innerstrokedot8(L, T, R, B, blitter);
}
}
-
-#endif
-
-
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index 141f751982..9a60b8e66f 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -185,3 +185,72 @@ bool SkWriter32::writeToStream(SkWStream* stream) {
return true;
}
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkReader32.h"
+
+const char* SkReader32::readString(size_t* outLen) {
+ // we need to read at least 1-4 bytes
+ SkASSERT(this->isAvailable(4));
+ const uint8_t* base = (const uint8_t*)this->peek();
+ const uint8_t* ptr = base;
+
+ size_t len = *ptr++;
+ if (0xFF == len) {
+ len = (ptr[0] << 8) | ptr[1];
+ ptr += 2;
+ SkASSERT(len < 0xFFFF);
+ }
+
+ // skip what we've read, and 0..3 pad bytes
+ // add 1 for the terminating 0 that writeString() included
+ size_t alignedSize = SkAlign4(len + (ptr - base) + 1);
+ this->skip(alignedSize);
+
+ if (outLen) {
+ *outLen = len;
+ }
+ return (const char*)ptr;
+}
+
+void SkWriter32::writeString(const char str[], size_t len) {
+ if ((long)len < 0) {
+ SkASSERT(str);
+ len = strlen(str);
+ }
+ size_t lenBytes = 1;
+ if (len >= 0xFF) {
+ lenBytes = 3;
+ SkASSERT(len < 0xFFFF);
+ }
+ // add 1 since we also write a terminating 0
+ size_t alignedLen = SkAlign4(lenBytes + len + 1);
+ uint8_t* ptr = (uint8_t*)this->reserve(alignedLen);
+ if (1 == lenBytes) {
+ *ptr++ = SkToU8(len);
+ } else {
+ *ptr++ = 0xFF;
+ *ptr++ = SkToU8(len >> 8);
+ *ptr++ = len & 0xFF;
+ }
+ memcpy(ptr, str, len);
+ ptr[len] = 0;
+ // we may have left 0,1,2,3 bytes uninitialized, since we reserved align4
+ // number of bytes. That's ok, since the reader will know to skip those
+}
+
+size_t SkWriter32::WriteStringSize(const char* str, size_t len) {
+ if ((long)len < 0) {
+ SkASSERT(str);
+ len = strlen(str);
+ }
+ size_t lenBytes = 1;
+ if (len >= 0xFF) {
+ lenBytes = 3;
+ SkASSERT(len < 0xFFFF);
+ }
+ // add 1 since we also write a terminating 0
+ return SkAlign4(lenBytes + len + 1);
+}
+
+
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
index 603deb7ce4..1dfc24d1a1 100644
--- a/src/effects/Sk2DPathEffect.cpp
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -82,12 +82,19 @@ void Sk2DPathEffect::end(SkPath* dst) {}
void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer)
{
- buffer.writeMul4(&fMatrix, sizeof(fMatrix));
+ char storage[SkMatrix::kMaxFlattenSize];
+ uint32_t size = fMatrix.flatten(storage);
+ buffer.write32(size);
+ buffer.write(storage, size);
}
Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer)
{
- buffer.read(&fMatrix, sizeof(fMatrix));
+ char storage[SkMatrix::kMaxFlattenSize];
+ uint32_t size = buffer.readS32();
+ SkASSERT(size <= sizeof(storage));
+ buffer.read(storage, size);
+ fMatrix.unflatten(storage);
fMatrix.invert(&fInverse);
}
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
index c6caba3165..6e68b5e9ef 100644
--- a/src/effects/SkTransparentShader.cpp
+++ b/src/effects/SkTransparentShader.cpp
@@ -53,7 +53,10 @@ void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
switch (fDevice->getConfig()) {
case SkBitmap::kARGB_8888_Config:
if (scale == 256) {
- memcpy(span, fDevice->getAddr32(x, y), count * sizeof(SkPMColor));
+ SkPMColor* src = fDevice->getAddr32(x, y);
+ if (src != span) {
+ memcpy(span, src, count * sizeof(SkPMColor));
+ }
} else {
const SkPMColor* src = fDevice->getAddr32(x, y);
for (int i = count - 1; i >= 0; --i) {
@@ -125,6 +128,9 @@ void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
SkASSERT(fDevice->getConfig() == SkBitmap::kRGB_565_Config);
- memcpy(span, fDevice->getAddr16(x, y), count << 1);
+ uint16_t* src = fDevice->getAddr16(x, y);
+ if (src != span) {
+ memcpy(span, src, count << 1);
+ }
}
diff --git a/src/gpu/GrPrintf_skia.cpp b/src/gpu/GrPrintf_skia.cpp
index fa8b6a7647..6da8822cc4 100644
--- a/src/gpu/GrPrintf_skia.cpp
+++ b/src/gpu/GrPrintf_skia.cpp
@@ -23,7 +23,7 @@
#include "SkTypes.h"
void GrPrintf(const char format[], ...) {
- const size_t MAX_BUFFER_SIZE = 512;
+ const size_t MAX_BUFFER_SIZE = 2048;
char buffer[MAX_BUFFER_SIZE + 1];
va_list args;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 5b4f529bdb..7166a07f51 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -40,6 +40,13 @@
#define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
#endif
+// we use the same texture slot on GrPaint for bitmaps and shaders
+// (since drawBitmap, drawSprite, and drawDevice ignore skia's shader)
+enum {
+ kBitmapTextureIdx = 0,
+ kShaderTextureIdx = 0
+};
+
///////////////////////////////////////////////////////////////////////////////
SkGpuDevice::SkAutoCachedTexture::
@@ -130,9 +137,9 @@ SkGpuDevice::SkGpuDevice(GrContext* context,
SkASSERT(NULL != fTexture->asRenderTarget());
}
#else
- const GrGpu::TextureDesc desc = {
- GrGpu::kRenderTarget_TextureFlag,
- GrGpu::kNone_AALevel,
+ const GrTextureDesc desc = {
+ kRenderTarget_GrTextureFlagBit,
+ kNone_GrAALevel,
this->width(),
this->height(),
SkGr::Bitmap2PixelConfig(bm)
@@ -316,7 +323,7 @@ void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) {
if (NULL != fTexture) {
- paint->setTexture(fTexture);
+ paint->setTexture(kBitmapTextureIdx, fTexture);
return true;
}
return false;
@@ -342,7 +349,8 @@ static const GrSamplerState::SampleMode sk_bmp_type_to_sample_mode[] = {
bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
bool justAlpha,
- GrPaint* grPaint) {
+ GrPaint* grPaint,
+ bool constantColor) {
grPaint->fDither = skPaint.isDither();
grPaint->fAntiAlias = skPaint.isAntiAlias();
@@ -365,33 +373,44 @@ bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
if (justAlpha) {
uint8_t alpha = skPaint.getAlpha();
grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
+ // justAlpha is currently set to true only if there is a texture,
+ // so constantColor should not also be true.
+ GrAssert(!constantColor);
} else {
grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
- grPaint->setTexture(NULL);
+ grPaint->setTexture(kShaderTextureIdx, NULL);
}
SkColorFilter* colorFilter = skPaint.getColorFilter();
SkColor color;
SkXfermode::Mode filterMode;
if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
- grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
- grPaint->fColorFilterXfermode = filterMode;
- } else {
- grPaint->resetColorFilter();
+ if (!constantColor) {
+ grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
+ grPaint->fColorFilterXfermode = filterMode;
+ return true;
+ }
+ SkColor filtered = colorFilter->filterColor(skPaint.getColor());
+ grPaint->fColor = SkGr::SkColor2GrColor(filtered);
}
+ grPaint->resetColorFilter();
return true;
}
bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
SkAutoCachedTexture* act,
const SkMatrix& ctm,
- GrPaint* grPaint) {
+ GrPaint* grPaint,
+ bool constantColor) {
SkASSERT(NULL != act);
SkShader* shader = skPaint.getShader();
if (NULL == shader) {
- return this->skPaint2GrPaintNoShader(skPaint, false, grPaint);
- } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint)) {
+ return this->skPaint2GrPaintNoShader(skPaint,
+ false,
+ grPaint,
+ constantColor);
+ } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint, false)) {
return false;
}
@@ -411,26 +430,27 @@ bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
SkDebugf("shader->asABitmap() == kNone_BitmapType\n");
return false;
}
- grPaint->fSampler.setSampleMode(sampleMode);
+ GrSamplerState* sampler = grPaint->getTextureSampler(kShaderTextureIdx);
+ sampler->setSampleMode(sampleMode);
if (skPaint.isFilterBitmap()) {
- grPaint->fSampler.setFilter(GrSamplerState::kBilinear_Filter);
+ sampler->setFilter(GrSamplerState::kBilinear_Filter);
} else {
- grPaint->fSampler.setFilter(GrSamplerState::kNearest_Filter);
+ sampler->setFilter(GrSamplerState::kNearest_Filter);
}
- grPaint->fSampler.setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
- grPaint->fSampler.setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
+ sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
+ sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
- grPaint->fSampler.setRadial2Params(twoPointParams[0],
- twoPointParams[1],
- twoPointParams[2] < 0);
+ sampler->setRadial2Params(twoPointParams[0],
+ twoPointParams[1],
+ twoPointParams[2] < 0);
}
- GrTexture* texture = act->set(this, bitmap, grPaint->fSampler);
+ GrTexture* texture = act->set(this, bitmap, *sampler);
if (NULL == texture) {
SkDebugf("Couldn't convert bitmap to texture.\n");
return false;
}
- grPaint->setTexture(texture);
+ grPaint->setTexture(kShaderTextureIdx, texture);
// since our texture coords will be in local space, we wack the texture
// matrix to map them back into 0...1 before we load it
@@ -449,7 +469,7 @@ bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width());
matrix.postScale(s, s);
}
- grPaint->fSampler.setMatrix(matrix);
+ sampler->setMatrix(matrix);
return true;
}
@@ -595,7 +615,11 @@ void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
GrPaint grPaint;
SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ if (!this->skPaint2GrPaintShader(paint,
+ &act,
+ *draw.fMatrix,
+ &grPaint,
+ true)) {
return;
}
@@ -626,7 +650,11 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
GrPaint grPaint;
SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ if (!this->skPaint2GrPaintShader(paint,
+ &act,
+ *draw.fMatrix,
+ &grPaint,
+ true)) {
return;
}
@@ -665,6 +693,10 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
if (paint.getMaskFilter()) {
usePath = true;
}
+ // until we aa rotated rects...
+ if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) {
+ usePath = true;
+ }
if (usePath) {
SkPath path;
@@ -675,7 +707,11 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
GrPaint grPaint;
SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ if (!this->skPaint2GrPaintShader(paint,
+ &act,
+ *draw.fMatrix,
+ &grPaint,
+ true)) {
return;
}
fContext->drawRect(grPaint, rect, doStroke ? width : -1);
@@ -713,6 +749,9 @@ static bool drawWithMaskFilter(GrContext* context, const SkPath& path,
// we now have a device-aligned 8bit mask in dstM, ready to be drawn using
// the current clip (and identity matrix) and grpaint settings
+ // used to compute inverse view, if necessary
+ GrMatrix ivm = context->getMatrix();
+
GrAutoMatrix avm(context, GrMatrix::I());
const GrTextureDesc desc = {
@@ -729,18 +768,29 @@ static bool drawWithMaskFilter(GrContext* context, const SkPath& path,
return false;
}
- grp->setTexture(texture);
+ if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
+ grp->preConcatActiveSamplerMatrices(ivm);
+ }
+
+ static const int MASK_IDX = GrPaint::kMaxMasks - 1;
+ // we assume the last mask index is available for use
+ GrAssert(NULL == grp->getMask(MASK_IDX));
+ grp->setMask(MASK_IDX, texture);
texture->unref();
- grp->fSampler.setClampNoFilter();
+ grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
GrRect d;
d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
GrIntToScalar(dstM.fBounds.fTop),
GrIntToScalar(dstM.fBounds.fRight),
GrIntToScalar(dstM.fBounds.fBottom));
- GrRect s;
- s.setLTRB(0, 0, GR_Scalar1, GR_Scalar1);
- context->drawRectToRect(*grp, d, s);
+
+ GrMatrix m;
+ m.setTranslate(-dstM.fBounds.fLeft, -dstM.fBounds.fTop);
+ m.postIDiv(dstM.fBounds.width(), dstM.fBounds.height());
+ grp->getMaskSampler(MASK_IDX)->setMatrix(m);
+
+ context->drawRect(*grp, d);
return true;
}
@@ -751,7 +801,11 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
GrPaint grPaint;
SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ if (!this->skPaint2GrPaintShader(paint,
+ &act,
+ *draw.fMatrix,
+ &grPaint,
+ true)) {
return;
}
@@ -832,8 +886,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
}
}
- SkGrPathIter iter(*pathPtr);
- fContext->drawPath(grPaint, &iter, fill);
+ fContext->drawPath(grPaint, *pathPtr, fill);
}
void SkGpuDevice::drawBitmap(const SkDraw& draw,
@@ -851,15 +904,15 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw,
}
GrPaint grPaint;
- if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint)) {
+ if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
return;
}
+ GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
if (paint.isFilterBitmap()) {
- grPaint.fSampler.setFilter(GrSamplerState::kBilinear_Filter);
+ sampler->setFilter(GrSamplerState::kBilinear_Filter);
} else {
- grPaint.fSampler.setFilter(GrSamplerState::kNearest_Filter);
+ sampler->setFilter(GrSamplerState::kNearest_Filter);
}
-
const int maxTextureDim = fContext->getMaxTextureDimension();
if (bitmap.getTexture() || (bitmap.width() <= maxTextureDim &&
@@ -941,27 +994,54 @@ void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
return;
}
- grPaint->fSampler.setWrapX(GrSamplerState::kClamp_WrapMode);
- grPaint->fSampler.setWrapY(GrSamplerState::kClamp_WrapMode);
- grPaint->fSampler.setSampleMode(GrSamplerState::kNormal_SampleMode);
- grPaint->fSampler.setMatrix(GrMatrix::I());
+ GrSamplerState* sampler = grPaint->getTextureSampler(kBitmapTextureIdx);
+
+ sampler->setWrapX(GrSamplerState::kClamp_WrapMode);
+ sampler->setWrapY(GrSamplerState::kClamp_WrapMode);
+ sampler->setSampleMode(GrSamplerState::kNormal_SampleMode);
+ sampler->setMatrix(GrMatrix::I());
GrTexture* texture;
- SkAutoCachedTexture act(this, bitmap, grPaint->fSampler, &texture);
+ SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
if (NULL == texture) {
return;
}
- grPaint->setTexture(texture);
+ grPaint->setTexture(kShaderTextureIdx, texture);
GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()),
GrIntToScalar(srcRect.height()));
GrRect paintRect;
- paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()),
- GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()),
- GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
+ paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()),
+ GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()),
+ GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height()));
+ if (GrSamplerState::kNearest_Filter != sampler->getFilter() &&
+ (srcRect.width() < bitmap.width() ||
+ srcRect.height() < bitmap.height())) {
+ // If drawing a subrect of the bitmap and filtering is enabled,
+ // use a constrained texture domain to avoid color bleeding
+ GrScalar left, top, right, bottom;
+ if (srcRect.width() > 1) {
+ GrScalar border = GR_ScalarHalf / bitmap.width();
+ left = paintRect.left() + border;
+ right = paintRect.right() - border;
+ } else {
+ left = right = GrScalarHalf(paintRect.left() + paintRect.right());
+ }
+ if (srcRect.height() > 1) {
+ GrScalar border = GR_ScalarHalf / bitmap.height();
+ top = paintRect.top() + border;
+ bottom = paintRect.bottom() - border;
+ } else {
+ top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom());
+ }
+ GrRect textureDomain;
+ textureDomain.setLTRB(left, top, right, bottom);
+ sampler->setTextureDomain(textureDomain);
+ }
+
fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
}
@@ -975,17 +1055,19 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
}
GrPaint grPaint;
- if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint)) {
+ if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
return;
}
GrAutoMatrix avm(fContext, GrMatrix::I());
+ GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
+
GrTexture* texture;
- grPaint.fSampler.setClampNoFilter();
- SkAutoCachedTexture act(this, bitmap, grPaint.fSampler, &texture);
+ sampler->setClampNoFilter();
+ SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
- grPaint.setTexture(texture);
+ grPaint.setTexture(kBitmapTextureIdx, texture);
fContext->drawRectToRect(grPaint,
GrRect::MakeXYWH(GrIntToScalar(left),
@@ -1001,11 +1083,11 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
GrPaint grPaint;
if (!((SkGpuDevice*)dev)->bindDeviceAsTexture(&grPaint) ||
- !this->skPaint2GrPaintNoShader(paint, true, &grPaint)) {
+ !this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
return;
}
- SkASSERT(NULL != grPaint.getTexture());
+ SkASSERT(NULL != grPaint.getTexture(0));
const SkBitmap& bm = dev->accessBitmap(false);
int w = bm.width();
@@ -1013,7 +1095,7 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
GrAutoMatrix avm(fContext, GrMatrix::I());
- grPaint.fSampler.setClampNoFilter();
+ grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
fContext->drawRectToRect(grPaint,
GrRect::MakeXYWH(GrIntToScalar(x),
@@ -1044,13 +1126,17 @@ void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
SkAutoCachedTexture act;
// we ignore the shader if texs is null.
if (NULL == texs) {
- if (!this->skPaint2GrPaintNoShader(paint, false, &grPaint)) {
+ if (!this->skPaint2GrPaintNoShader(paint,
+ false,
+ &grPaint,
+ NULL == colors)) {
return;
}
} else {
if (!this->skPaint2GrPaintShader(paint, &act,
*draw.fMatrix,
- &grPaint)) {
+ &grPaint,
+ NULL == colors)) {
return;
}
}
@@ -1167,7 +1253,11 @@ void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
GrPaint grPaint;
SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ if (!this->skPaint2GrPaintShader(paint,
+ &act,
+ *draw.fMatrix,
+ &grPaint,
+ true)) {
return;
}
GrTextContext context(fContext, grPaint, draw.fExtMatrix);
@@ -1191,7 +1281,11 @@ void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
GrPaint grPaint;
SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ if (!this->skPaint2GrPaintShader(paint,
+ &act,
+ *draw.fMatrix,
+ &grPaint,
+ true)) {
return;
}
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index f65cf1ee14..600c3361cb 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -112,42 +112,6 @@ GrTextureEntry* sk_gr_create_bitmap_texture(GrContext* ctx,
bitmap->rowBytes());
}
-////////////////////////////////////////////////////////////////////////////////
-
-
-GrPathCmd SkGrPathIter::next(GrPoint pts[]) {
- GrAssert(NULL != pts);
-#if SK_SCALAR_IS_GR_SCALAR
- return sk_path_verb_to_gr_path_command(fIter.next((SkPoint*)pts));
-#else
- Command cmd = sk_path_verb_to_gr_path_command(fIter.next(fPoints));
- int n = NumCommandPoints(cmd);
- for (int i = 0; i < n; ++i) {
- pts[i].fX = SkScalarToGrScalar(fPoints[i].fX);
- pts[i].fY = SkScalarToGrScalar(fPoints[i].fY);
- }
- return cmd;
-#endif
-}
-
-GrPathCmd SkGrPathIter::next() {
- return sk_path_verb_to_gr_path_command(fIter.next(NULL));
-}
-
-void SkGrPathIter::rewind() {
- fIter.setPath(*fPath, false);
-}
-
-GrConvexHint SkGrPathIter::convexHint() const {
- return fPath->isConvex() ? kConvex_ConvexHint :
- kNone_ConvexHint;
-}
-
-bool SkGrPathIter::getConservativeBounds(GrRect* rect) const {
- *rect = fPath->getBounds();
- return true;
-}
-
///////////////////////////////////////////////////////////////////////////////
void SkGrClipIterator::reset(const SkClipStack& clipStack) {
diff --git a/src/gpu/SkGrFontScaler.cpp b/src/gpu/SkGrFontScaler.cpp
index e58f035fc7..eb260fb826 100644
--- a/src/gpu/SkGrFontScaler.cpp
+++ b/src/gpu/SkGrFontScaler.cpp
@@ -163,13 +163,13 @@ bool SkGrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed,
return true;
}
+// we should just return const SkPath* (NULL means false)
bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, GrPath* path) {
const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID);
const SkPath* skPath = fStrike->findPath(glyph);
if (skPath) {
- SkGrPathIter iter(*skPath);
- path->resetFromIter(&iter);
+ *path = *skPath;
return true;
}
return false;
diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp
new file mode 100644
index 0000000000..afa9d9a1c9
--- /dev/null
+++ b/src/pdf/SkPDFCatalog.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+SkPDFCatalog::SkPDFCatalog()
+ : fFirstPageCount(0),
+ fNextObjNum(1),
+ fNextFirstPageObjNum(0) {
+}
+
+SkPDFCatalog::~SkPDFCatalog() {}
+
+SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
+ SkASSERT(findObjectIndex(obj) == -1);
+ SkASSERT(fNextFirstPageObjNum == 0);
+ if (onFirstPage)
+ fFirstPageCount++;
+
+ struct Rec newEntry(obj, onFirstPage);
+ fCatalog.append(1, &newEntry);
+ return obj;
+}
+
+size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, size_t offset) {
+ int objIndex = assignObjNum(obj) - 1;
+ SkASSERT(fCatalog[objIndex].fObjNumAssigned);
+ SkASSERT(fCatalog[objIndex].fFileOffset == 0);
+ fCatalog[objIndex].fFileOffset = offset;
+
+ return obj->getOutputSize(this, true);
+}
+
+void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) {
+ stream->writeDecAsText(assignObjNum(obj));
+ stream->writeText(" 0"); // Generation number is always 0.
+}
+
+size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) {
+ SkDynamicMemoryWStream buffer;
+ emitObjectNumber(&buffer, obj);
+ return buffer.getOffset();
+}
+
+int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const {
+ for (int i = 0; i < fCatalog.count(); i++) {
+ if (fCatalog[i].fObject == obj)
+ return i;
+ }
+ return -1;
+}
+
+int SkPDFCatalog::assignObjNum(SkPDFObject* obj) {
+ int pos = findObjectIndex(obj);
+ // If this assert fails, it means you probably forgot to add an object
+ // to the resource list.
+ SkASSERT(pos >= 0);
+ uint32_t currentIndex = pos;
+ if (fCatalog[currentIndex].fObjNumAssigned)
+ return currentIndex + 1;
+
+ // First assignment.
+ if (fNextFirstPageObjNum == 0)
+ fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1;
+
+ uint32_t objNum;
+ if (fCatalog[currentIndex].fOnFirstPage) {
+ objNum = fNextFirstPageObjNum;
+ fNextFirstPageObjNum++;
+ } else {
+ objNum = fNextObjNum;
+ fNextObjNum++;
+ }
+
+ // When we assign an object an object number, we put it in that array
+ // offset (minus 1 because object number 0 is reserved).
+ SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned);
+ if (objNum - 1 != currentIndex)
+ SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]);
+ fCatalog[objNum - 1].fObjNumAssigned = true;
+ return objNum;
+}
+
+int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) {
+ int first = -1;
+ int last = fCatalog.count() - 1;
+ // TODO(vandebo) support linearized format.
+ //int last = fCatalog.count() - fFirstPageCount - 1;
+ //if (firstPage) {
+ // first = fCatalog.count() - fFirstPageCount;
+ // last = fCatalog.count() - 1;
+ //}
+
+ stream->writeText("xref\n");
+ stream->writeDecAsText(first + 1);
+ stream->writeText(" ");
+ stream->writeDecAsText(last - first + 1);
+ stream->writeText("\n");
+
+ if (first == -1) {
+ stream->writeText("0000000000 65535 f \n");
+ first++;
+ }
+ for (int i = first; i <= last; i++) {
+ SkASSERT(fCatalog[i].fFileOffset > 0);
+ SkASSERT(fCatalog[i].fFileOffset <= 9999999999LL);
+ stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10);
+ stream->writeText(" 00000 n \n");
+ }
+
+ return fCatalog.count() + 1;
+}
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
new file mode 100644
index 0000000000..c6ddf39966
--- /dev/null
+++ b/src/pdf/SkPDFDevice.cpp
@@ -0,0 +1,1488 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFDevice.h"
+
+#include "SkColor.h"
+#include "SkClipStack.h"
+#include "SkDraw.h"
+#include "SkGlyphCache.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPDFFont.h"
+#include "SkPDFFormXObject.h"
+#include "SkPDFGraphicState.h"
+#include "SkPDFImage.h"
+#include "SkPDFShader.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
+#include "SkRect.h"
+#include "SkString.h"
+#include "SkTextFormatParams.h"
+#include "SkTypeface.h"
+#include "SkTypes.h"
+
+// Utility functions
+
+static void emit_pdf_color(SkColor color, SkWStream* result) {
+ SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
+ SkScalar colorMax = SkIntToScalar(0xFF);
+ SkPDFScalar::Append(
+ SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
+ result->writeText(" ");
+ SkPDFScalar::Append(
+ SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
+ result->writeText(" ");
+ SkPDFScalar::Append(
+ SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
+ result->writeText(" ");
+}
+
+static SkPaint calculate_text_paint(const SkPaint& paint) {
+ SkPaint result = paint;
+ if (result.isFakeBoldText()) {
+ SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
+ kStdFakeBoldInterpKeys,
+ kStdFakeBoldInterpValues,
+ kStdFakeBoldInterpLength);
+ SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
+ if (result.getStyle() == SkPaint::kFill_Style)
+ result.setStyle(SkPaint::kStrokeAndFill_Style);
+ else
+ width += result.getStrokeWidth();
+ result.setStrokeWidth(width);
+ }
+ return result;
+}
+
+// Stolen from measure_text in SkDraw.cpp and then tweaked.
+static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
+ const uint16_t* glyphs, size_t len, SkScalar* x,
+ SkScalar* y, SkScalar* width) {
+ if (paint.getTextAlign() == SkPaint::kLeft_Align && width == NULL)
+ return;
+
+ SkMatrix ident;
+ ident.reset();
+ SkAutoGlyphCache autoCache(paint, &ident);
+ SkGlyphCache* cache = autoCache.getCache();
+
+ const char* start = (char*)glyphs;
+ const char* stop = (char*)(glyphs + len);
+ SkFixed xAdv = 0, yAdv = 0;
+
+ // TODO(vandebo) This probably needs to take kerning into account.
+ while (start < stop) {
+ const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
+ xAdv += glyph.fAdvanceX;
+ yAdv += glyph.fAdvanceY;
+ };
+ if (width)
+ *width = SkFixedToScalar(xAdv);
+ if (paint.getTextAlign() == SkPaint::kLeft_Align)
+ return;
+
+ SkScalar xAdj = SkFixedToScalar(xAdv);
+ SkScalar yAdj = SkFixedToScalar(yAdv);
+ if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+ xAdj = SkScalarHalf(xAdj);
+ yAdj = SkScalarHalf(yAdj);
+ }
+ *x = *x - xAdj;
+ *y = *y - yAdj;
+}
+
+static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
+ SkWStream* content) {
+ // Flip the text about the x-axis to account for origin swap and include
+ // the passed parameters.
+ content->writeText("1 0 ");
+ SkPDFScalar::Append(0 - textSkewX, content);
+ content->writeText(" -1 ");
+ SkPDFScalar::Append(x, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(y, content);
+ content->writeText(" Tm\n");
+}
+
+// It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
+// later being our representation of an object in the PDF file.
+struct GraphicStateEntry {
+ GraphicStateEntry();
+
+ // Compare the fields we care about when setting up a new content entry.
+ bool compareInitialState(const GraphicStateEntry& b);
+
+ SkMatrix fMatrix;
+ // We can't do set operations on Paths, though PDF natively supports
+ // intersect. If the clip stack does anything other than intersect,
+ // we have to fall back to the region. Treat fClipStack as authoritative.
+ // See http://code.google.com/p/skia/issues/detail?id=221
+ SkClipStack fClipStack;
+ SkRegion fClipRegion;
+
+ // When emitting the content entry, we will ensure the graphic state
+ // is set to these values first.
+ SkColor fColor;
+ SkScalar fTextScaleX; // Zero means we don't care what the value is.
+ SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
+ int fShaderIndex;
+ int fGraphicStateIndex;
+
+ // We may change the font (i.e. for Type1 support) within a
+ // ContentEntry. This is the one currently in effect, or NULL if none.
+ SkPDFFont* fFont;
+ // In PDF, text size has no default value. It is only valid if fFont is
+ // not NULL.
+ SkScalar fTextSize;
+};
+
+GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
+ fTextScaleX(SK_Scalar1),
+ fTextFill(SkPaint::kFill_Style),
+ fShaderIndex(-1),
+ fGraphicStateIndex(-1),
+ fFont(NULL),
+ fTextSize(SK_ScalarNaN) {
+ fMatrix.reset();
+}
+
+bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) {
+ return fColor == b.fColor &&
+ fShaderIndex == b.fShaderIndex &&
+ fGraphicStateIndex == b.fGraphicStateIndex &&
+ fMatrix == b.fMatrix &&
+ fClipStack == b.fClipStack &&
+ (fTextScaleX == 0 ||
+ b.fTextScaleX == 0 ||
+ (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill));
+}
+
+class GraphicStackState {
+public:
+ GraphicStackState(const SkClipStack& existingClipStack,
+ const SkRegion& existingClipRegion,
+ SkWStream* contentStream)
+ : fStackDepth(0),
+ fContentStream(contentStream) {
+ fEntries[0].fClipStack = existingClipStack;
+ fEntries[0].fClipRegion = existingClipRegion;
+ }
+
+ void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion,
+ const SkIPoint& translation);
+ void updateMatrix(const SkMatrix& matrix);
+ void updateDrawingState(const GraphicStateEntry& state);
+
+ void drainStack();
+
+private:
+ void push();
+ void pop();
+ GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
+
+ // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
+ static const int kMaxStackDepth = 12;
+ GraphicStateEntry fEntries[kMaxStackDepth + 1];
+ int fStackDepth;
+ SkWStream* fContentStream;
+};
+
+void GraphicStackState::drainStack() {
+ while (fStackDepth) {
+ pop();
+ }
+}
+
+void GraphicStackState::push() {
+ SkASSERT(fStackDepth < kMaxStackDepth);
+ fContentStream->writeText("q\n");
+ fStackDepth++;
+ fEntries[fStackDepth] = fEntries[fStackDepth - 1];
+}
+
+void GraphicStackState::pop() {
+ SkASSERT(fStackDepth > 0);
+ fContentStream->writeText("Q\n");
+ fStackDepth--;
+}
+
+// This function initializes iter to be an interator on the "stack" argument
+// and then skips over the leading entries as specified in prefix. It requires
+// and asserts that "prefix" will be a prefix to "stack."
+static void skip_clip_stack_prefix(const SkClipStack& prefix,
+ const SkClipStack& stack,
+ SkClipStack::B2FIter* iter) {
+ SkClipStack::B2FIter prefixIter(prefix);
+ iter->reset(stack);
+
+ const SkClipStack::B2FIter::Clip* prefixEntry;
+ const SkClipStack::B2FIter::Clip* iterEntry;
+
+ int count = 0;
+ for (prefixEntry = prefixIter.next(); prefixEntry;
+ prefixEntry = prefixIter.next(), count++) {
+ iterEntry = iter->next();
+ SkASSERT(iterEntry);
+ // Because of SkClipStack does internal intersection, the last clip
+ // entry may differ.
+ if(*prefixEntry != *iterEntry) {
+ SkASSERT(prefixEntry->fOp == SkRegion::kIntersect_Op);
+ SkASSERT(iterEntry->fOp == SkRegion::kIntersect_Op);
+ SkASSERT((iterEntry->fRect == NULL) ==
+ (prefixEntry->fRect == NULL));
+ SkASSERT((iterEntry->fPath == NULL) ==
+ (prefixEntry->fPath == NULL));
+ // We need to back up the iterator by one but don't have that
+ // function, so reset and go forward by one less.
+ iter->reset(stack);
+ for (int i = 0; i < count; i++) {
+ iter->next();
+ }
+ prefixEntry = prefixIter.next();
+ break;
+ }
+ }
+
+ SkASSERT(prefixEntry == NULL);
+}
+
+static void emit_clip(SkPath* clipPath, SkRect* clipRect,
+ SkWStream* contentStream) {
+ SkASSERT(clipPath || clipRect);
+
+ SkPath::FillType clipFill;
+ if (clipPath) {
+ SkPDFUtils::EmitPath(*clipPath, contentStream);
+ clipFill = clipPath->getFillType();
+ } else if (clipRect) {
+ SkPDFUtils::AppendRectangle(*clipRect, contentStream);
+ clipFill = SkPath::kWinding_FillType;
+ }
+
+ NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
+ NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
+ if (clipFill == SkPath::kEvenOdd_FillType) {
+ contentStream->writeText("W* n\n");
+ } else {
+ contentStream->writeText("W n\n");
+ }
+}
+
+// TODO(vandebo) Take advantage of SkClipStack::getSaveCount(), the PDF
+// graphic state stack, and the fact that we can know all the clips used
+// on the page to optimize this.
+void GraphicStackState::updateClip(const SkClipStack& clipStack,
+ const SkRegion& clipRegion,
+ const SkIPoint& translation) {
+ if (clipStack == currentEntry()->fClipStack) {
+ return;
+ }
+
+ while (fStackDepth > 0) {
+ pop();
+ if (clipStack == currentEntry()->fClipStack) {
+ return;
+ }
+ }
+ push();
+
+ // gsState->initialEntry()->fClipStack/Region specifies the clip that has
+ // already been applied. (If this is a top level device, then it specifies
+ // a clip to the content area. If this is a layer, then it specifies
+ // the clip in effect when the layer was created.) There's no need to
+ // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the
+ // initial clip on the parent layer. (This means there's a bug if the user
+ // expands the clip and then uses any xfer mode that uses dst:
+ // http://code.google.com/p/skia/issues/detail?id=228 )
+ SkClipStack::B2FIter iter;
+ skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
+
+ // If the clip stack does anything other than intersect or if it uses
+ // an inverse fill type, we have to fall back to the clip region.
+ bool needRegion = false;
+ const SkClipStack::B2FIter::Clip* clipEntry;
+ for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
+ if (clipEntry->fOp != SkRegion::kIntersect_Op ||
+ (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) {
+ needRegion = true;
+ break;
+ }
+ }
+
+ if (needRegion) {
+ SkPath clipPath;
+ SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
+ emit_clip(&clipPath, NULL, fContentStream);
+ } else {
+ skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
+ SkMatrix transform;
+ transform.setTranslate(translation.fX, translation.fY);
+ const SkClipStack::B2FIter::Clip* clipEntry;
+ for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
+ SkASSERT(clipEntry->fOp == SkRegion::kIntersect_Op);
+ if (clipEntry->fRect) {
+ SkRect translatedClip;
+ transform.mapRect(&translatedClip, *clipEntry->fRect);
+ emit_clip(NULL, &translatedClip, fContentStream);
+ } else if (clipEntry->fPath) {
+ SkPath translatedPath;
+ clipEntry->fPath->transform(transform, &translatedPath);
+ emit_clip(&translatedPath, NULL, fContentStream);
+ } else {
+ SkASSERT(false);
+ }
+ }
+ }
+ currentEntry()->fClipStack = clipStack;
+ currentEntry()->fClipRegion = clipRegion;
+}
+
+void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
+ if (matrix == currentEntry()->fMatrix) {
+ return;
+ }
+
+ if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
+ SkASSERT(fStackDepth > 0);
+ SkASSERT(fEntries[fStackDepth].fClipStack ==
+ fEntries[fStackDepth -1].fClipStack);
+ pop();
+
+ SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
+ }
+ if (matrix.getType() == SkMatrix::kIdentity_Mask) {
+ return;
+ }
+
+ push();
+ SkPDFUtils::AppendTransform(matrix, fContentStream);
+ currentEntry()->fMatrix = matrix;
+}
+
+void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
+ // PDF treats a shader as a color, so we only set one or the other.
+ if (state.fShaderIndex >= 0) {
+ if (state.fShaderIndex != currentEntry()->fShaderIndex) {
+ fContentStream->writeText("/Pattern CS /Pattern cs /P");
+ fContentStream->writeDecAsText(state.fShaderIndex);
+ fContentStream->writeText(" SCN /P");
+ fContentStream->writeDecAsText(state.fShaderIndex);
+ fContentStream->writeText(" scn\n");
+ currentEntry()->fShaderIndex = state.fShaderIndex;
+ }
+ } else {
+ if (state.fColor != currentEntry()->fColor ||
+ currentEntry()->fShaderIndex >= 0) {
+ emit_pdf_color(state.fColor, fContentStream);
+ fContentStream->writeText("RG ");
+ emit_pdf_color(state.fColor, fContentStream);
+ fContentStream->writeText("rg\n");
+ currentEntry()->fColor = state.fColor;
+ currentEntry()->fShaderIndex = -1;
+ }
+ }
+
+ if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
+ SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
+ currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
+ }
+
+ if (state.fTextScaleX) {
+ if (state.fTextScaleX != currentEntry()->fTextScaleX) {
+ SkScalar pdfScale = SkScalarMul(state.fTextScaleX,
+ SkIntToScalar(100));
+ SkPDFScalar::Append(pdfScale, fContentStream);
+ fContentStream->writeText(" Tz\n");
+ currentEntry()->fTextScaleX = state.fTextScaleX;
+ }
+ if (state.fTextFill != currentEntry()->fTextFill) {
+ SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
+ SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
+ enum_must_match_value);
+ SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
+ enum_must_match_value);
+ fContentStream->writeDecAsText(state.fTextFill);
+ fContentStream->writeText(" Tr\n");
+ currentEntry()->fTextFill = state.fTextFill;
+ }
+ }
+}
+
+struct ContentEntry {
+ GraphicStateEntry fState;
+ SkDynamicMemoryWStream fContent;
+ SkTScopedPtr<ContentEntry> fNext;
+};
+
+// A helper class to automatically finish a ContentEntry at the end of a
+// drawing method and maintain the state needed between set up and finish.
+class ScopedContentEntry {
+public:
+ ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw,
+ const SkPaint& paint, bool hasText = false)
+ : fDevice(device),
+ fContentEntry(NULL),
+ fXfermode(SkXfermode::kSrcOver_Mode) {
+ init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
+ }
+ ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
+ const SkRegion& clipRegion, const SkMatrix& matrix,
+ const SkPaint& paint, bool hasText = false)
+ : fDevice(device),
+ fContentEntry(NULL),
+ fXfermode(SkXfermode::kSrcOver_Mode) {
+ init(clipStack, clipRegion, matrix, paint, hasText);
+ }
+
+ ~ScopedContentEntry() {
+ if (fContentEntry) {
+ fDevice->finishContentEntry(fXfermode, fDstFormXObject.get());
+ }
+ }
+
+ ContentEntry* entry() { return fContentEntry; }
+private:
+ SkPDFDevice* fDevice;
+ ContentEntry* fContentEntry;
+ SkXfermode::Mode fXfermode;
+ SkRefPtr<SkPDFFormXObject> fDstFormXObject;
+
+ void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
+ const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
+ if (paint.getXfermode()) {
+ paint.getXfermode()->asMode(&fXfermode);
+ }
+ fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
+ matrix, paint, hasText,
+ &fDstFormXObject);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkPDFDeviceFactory::newDevice(SkCanvas* c, SkBitmap::Config config,
+ int width, int height, bool isOpaque,
+ bool isForLayer) {
+ SkMatrix initialTransform;
+ initialTransform.reset();
+ SkISize size = SkISize::Make(width, height);
+ if (isForLayer) {
+ return SkNEW_ARGS(SkPDFDevice, (size, c->getTotalClipStack(),
+ c->getTotalClip()));
+ } else {
+ return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
+ }
+}
+
+static inline SkBitmap makeContentBitmap(const SkISize& contentSize,
+ const SkMatrix* initialTransform) {
+ SkBitmap bitmap;
+ if (initialTransform) {
+ // Compute the size of the drawing area.
+ SkVector drawingSize;
+ SkMatrix inverse;
+ drawingSize.set(contentSize.fWidth, contentSize.fHeight);
+ initialTransform->invert(&inverse);
+ inverse.mapVectors(&drawingSize, 1);
+ SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound();
+ bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth),
+ abs(size.fHeight));
+ } else {
+ bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth),
+ abs(contentSize.fHeight));
+ }
+
+ return bitmap;
+}
+
+SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
+ const SkMatrix& initialTransform)
+ : SkDevice(NULL, makeContentBitmap(contentSize, &initialTransform), false),
+ fPageSize(pageSize),
+ fContentSize(contentSize),
+ fLastContentEntry(NULL) {
+ // Skia generally uses the top left as the origin but PDF natively has the
+ // origin at the bottom left. This matrix corrects for that. But that only
+ // needs to be done once, we don't do it when layering.
+ fInitialTransform.setTranslate(0, pageSize.fHeight);
+ fInitialTransform.preScale(1, -1);
+ fInitialTransform.preConcat(initialTransform);
+
+ SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height());
+ fExistingClipStack.clipDevRect(existingClip);
+ fExistingClipRegion.setRect(existingClip);
+
+ this->init();
+}
+
+SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
+ const SkClipStack& existingClipStack,
+ const SkRegion& existingClipRegion)
+ : SkDevice(NULL, makeContentBitmap(layerSize, NULL), false),
+ fPageSize(layerSize),
+ fContentSize(layerSize),
+ fExistingClipStack(existingClipStack),
+ fExistingClipRegion(existingClipRegion),
+ fLastContentEntry(NULL) {
+ fInitialTransform.reset();
+ this->init();
+}
+
+SkPDFDevice::~SkPDFDevice() {
+ this->cleanUp();
+}
+
+void SkPDFDevice::init() {
+ fResourceDict = NULL;
+ fContentEntries.reset();
+ fLastContentEntry = NULL;
+}
+
+SkDeviceFactory* SkPDFDevice::onNewDeviceFactory() {
+ return SkNEW(SkPDFDeviceFactory);
+}
+
+void SkPDFDevice::cleanUp() {
+ fGraphicStateResources.unrefAll();
+ fXObjectResources.unrefAll();
+ fFontResources.unrefAll();
+ fShaderResources.unrefAll();
+}
+
+void SkPDFDevice::clear(SkColor color) {
+ this->cleanUp();
+ this->init();
+
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStyle(SkPaint::kFill_Style);
+ SkMatrix identity;
+ identity.reset();
+ ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
+ identity, paint);
+ internalDrawPaint(paint, content.entry());
+}
+
+void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
+ SkPaint newPaint = paint;
+ newPaint.setStyle(SkPaint::kFill_Style);
+ ScopedContentEntry content(this, d, newPaint);
+ internalDrawPaint(newPaint, content.entry());
+}
+
+void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
+ ContentEntry* contentEntry) {
+ if (!contentEntry) {
+ return;
+ }
+ SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
+ SkIntToScalar(this->height()));
+ SkMatrix totalTransform = fInitialTransform;
+ totalTransform.preConcat(contentEntry->fState.fMatrix);
+ SkMatrix inverse;
+ inverse.reset();
+ totalTransform.invert(&inverse);
+ inverse.mapRect(&bbox);
+
+ SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
+ SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
+ &contentEntry->fContent);
+}
+
+void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
+ size_t count, const SkPoint* points,
+ const SkPaint& passedPaint) {
+ if (count == 0) {
+ return;
+ }
+
+ // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
+ // We only use this when there's a path effect because of the overhead
+ // of multiple calls to setUpContentEntry it causes.
+ if (passedPaint.getPathEffect()) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+ SkDraw pointDraw(d);
+ pointDraw.fDevice = this;
+ pointDraw.drawPoints(mode, count, points, passedPaint, true);
+ return;
+ }
+
+ const SkPaint* paint = &passedPaint;
+ SkPaint modifiedPaint;
+
+ if (mode == SkCanvas::kPoints_PointMode &&
+ paint->getStrokeCap() != SkPaint::kRound_Cap) {
+ modifiedPaint = *paint;
+ paint = &modifiedPaint;
+ if (paint->getStrokeWidth()) {
+ // PDF won't draw a single point with square/butt caps because the
+ // orientation is ambiguous. Draw a rectangle instead.
+ modifiedPaint.setStyle(SkPaint::kFill_Style);
+ SkScalar strokeWidth = paint->getStrokeWidth();
+ SkScalar halfStroke = SkScalarHalf(strokeWidth);
+ for (size_t i = 0; i < count; i++) {
+ SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
+ r.inset(-halfStroke, -halfStroke);
+ drawRect(d, r, modifiedPaint);
+ }
+ return;
+ } else {
+ modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
+ }
+ }
+
+ ScopedContentEntry content(this, d, *paint);
+ if (!content.entry()) {
+ return;
+ }
+
+ switch (mode) {
+ case SkCanvas::kPolygon_PointMode:
+ SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
+ &content.entry()->fContent);
+ for (size_t i = 1; i < count; i++) {
+ SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
+ &content.entry()->fContent);
+ }
+ SkPDFUtils::StrokePath(&content.entry()->fContent);
+ break;
+ case SkCanvas::kLines_PointMode:
+ for (size_t i = 0; i < count/2; i++) {
+ SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
+ &content.entry()->fContent);
+ SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
+ points[i * 2 + 1].fY,
+ &content.entry()->fContent);
+ SkPDFUtils::StrokePath(&content.entry()->fContent);
+ }
+ break;
+ case SkCanvas::kPoints_PointMode:
+ SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
+ for (size_t i = 0; i < count; i++) {
+ SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
+ &content.entry()->fContent);
+ SkPDFUtils::ClosePath(&content.entry()->fContent);
+ SkPDFUtils::StrokePath(&content.entry()->fContent);
+ }
+ break;
+ default:
+ SkASSERT(false);
+ }
+}
+
+void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
+ const SkPaint& paint) {
+ if (paint.getPathEffect()) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+ SkPath path;
+ path.addRect(r);
+ drawPath(d, path, paint, NULL, true);
+ return;
+ }
+
+ ScopedContentEntry content(this, d, paint);
+ if (!content.entry()) {
+ return;
+ }
+ SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
+ SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
+ &content.entry()->fContent);
+}
+
+void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
+ const SkPaint& paint, const SkMatrix* prePathMatrix,
+ bool pathIsMutable) {
+ SkPath modifiedPath;
+ SkPath* pathPtr = const_cast<SkPath*>(&origPath);
+
+ SkMatrix matrix = *d.fMatrix;
+ if (prePathMatrix) {
+ if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
+ if (!pathIsMutable) {
+ pathPtr = &modifiedPath;
+ pathIsMutable = true;
+ }
+ origPath.transform(*prePathMatrix, pathPtr);
+ } else {
+ if (!matrix.preConcat(*prePathMatrix)) {
+ return;
+ }
+ }
+ }
+
+ if (paint.getPathEffect()) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+ if (!pathIsMutable) {
+ pathPtr = &modifiedPath;
+ pathIsMutable = true;
+ }
+ bool fill = paint.getFillPath(origPath, pathPtr);
+
+ SkPaint noEffectPaint(paint);
+ noEffectPaint.setPathEffect(NULL);
+ if (fill) {
+ noEffectPaint.setStyle(SkPaint::kFill_Style);
+ } else {
+ noEffectPaint.setStyle(SkPaint::kStroke_Style);
+ noEffectPaint.setStrokeWidth(0);
+ }
+ drawPath(d, *pathPtr, noEffectPaint, NULL, true);
+ return;
+ }
+
+ ScopedContentEntry content(this, d, paint);
+ if (!content.entry()) {
+ return;
+ }
+ SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent);
+ SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
+ &content.entry()->fContent);
+}
+
+void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
+ const SkIRect* srcRect, const SkMatrix& matrix,
+ const SkPaint& paint) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+
+ SkMatrix transform = matrix;
+ transform.postConcat(*d.fMatrix);
+ internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, srcRect,
+ paint);
+}
+
+void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
+ internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint);
+}
+
+void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ SkPaint textPaint = calculate_text_paint(paint);
+ ScopedContentEntry content(this, d, textPaint, true);
+ if (!content.entry()) {
+ return;
+ }
+
+ // We want the text in glyph id encoding and a writable buffer, so we end
+ // up making a copy either way.
+ size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
+ uint16_t* glyphIDs =
+ (uint16_t*)sk_malloc_flags(numGlyphs * 2,
+ SK_MALLOC_TEMP | SK_MALLOC_THROW);
+ SkAutoFree autoFreeGlyphIDs(glyphIDs);
+ if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+ paint.textToGlyphs(text, len, glyphIDs);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ } else {
+ SkASSERT((len & 1) == 0);
+ SkASSERT(len / 2 == numGlyphs);
+ memcpy(glyphIDs, text, len);
+ }
+
+ SkScalar width;
+ SkScalar* widthPtr = NULL;
+ if (textPaint.isUnderlineText() || textPaint.isStrikeThruText())
+ widthPtr = &width;
+
+ SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
+ align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y,
+ widthPtr);
+ content.entry()->fContent.writeText("BT\n");
+ set_text_transform(x, y, textPaint.getTextSkewX(),
+ &content.entry()->fContent);
+ size_t consumedGlyphCount = 0;
+ while (numGlyphs > consumedGlyphCount) {
+ updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry());
+ SkPDFFont* font = content.entry()->fState.fFont;
+ size_t availableGlyphs =
+ font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
+ numGlyphs - consumedGlyphCount);
+ SkString encodedString =
+ SkPDFString::formatString(glyphIDs + consumedGlyphCount,
+ availableGlyphs, font->multiByteGlyphs());
+ content.entry()->fContent.writeText(encodedString.c_str());
+ consumedGlyphCount += availableGlyphs;
+ content.entry()->fContent.writeText(" Tj\n");
+ }
+ content.entry()->fContent.writeText("ET\n");
+
+ // Draw underline and/or strikethrough if the paint has them.
+ // drawPosText() and drawTextOnPath() don't draw underline or strikethrough
+ // because the raster versions don't. Use paint instead of textPaint
+ // because we may have changed strokeWidth to do fakeBold text.
+ if (paint.isUnderlineText() || paint.isStrikeThruText()) {
+ SkScalar textSize = paint.getTextSize();
+ SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
+
+ if (paint.isUnderlineText()) {
+ SkScalar top = SkScalarMulAdd(textSize, kStdUnderline_Offset, y);
+ SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
+ drawRect(d, r, paint);
+ }
+ if (paint.isStrikeThruText()) {
+ SkScalar top = SkScalarMulAdd(textSize, kStdStrikeThru_Offset, y);
+ SkRect r = SkRect::MakeXYWH(x, top - height, width, height);
+ drawRect(d, r, paint);
+ }
+ }
+}
+
+void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint) {
+ SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
+ SkPaint textPaint = calculate_text_paint(paint);
+ ScopedContentEntry content(this, d, textPaint, true);
+ if (!content.entry()) {
+ return;
+ }
+
+ // Make sure we have a glyph id encoding.
+ SkAutoFree glyphStorage;
+ uint16_t* glyphIDs;
+ size_t numGlyphs;
+ if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+ numGlyphs = paint.textToGlyphs(text, len, NULL);
+ glyphIDs = (uint16_t*)sk_malloc_flags(numGlyphs * 2,
+ SK_MALLOC_TEMP | SK_MALLOC_THROW);
+ glyphStorage.set(glyphIDs);
+ paint.textToGlyphs(text, len, glyphIDs);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ } else {
+ SkASSERT((len & 1) == 0);
+ numGlyphs = len / 2;
+ glyphIDs = (uint16_t*)text;
+ }
+
+ SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
+ content.entry()->fContent.writeText("BT\n");
+ updateFont(textPaint, glyphIDs[0], content.entry());
+ for (size_t i = 0; i < numGlyphs; i++) {
+ SkPDFFont* font = content.entry()->fState.fFont;
+ uint16_t encodedValue = glyphIDs[i];
+ if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
+ updateFont(textPaint, glyphIDs[i], content.entry());
+ i--;
+ continue;
+ }
+ SkScalar x = pos[i * scalarsPerPos];
+ SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
+ align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y, NULL);
+ set_text_transform(x, y, textPaint.getTextSkewX(),
+ &content.entry()->fContent);
+ SkString encodedString =
+ SkPDFString::formatString(&encodedValue, 1,
+ font->multiByteGlyphs());
+ content.entry()->fContent.writeText(encodedString.c_str());
+ content.entry()->fContent.writeText(" Tj\n");
+ }
+ content.entry()->fContent.writeText("ET\n");
+}
+
+void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+ NOT_IMPLEMENTED("drawTextOnPath", true);
+}
+
+void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
+ int vertexCount, const SkPoint verts[],
+ const SkPoint texs[], const SkColor colors[],
+ SkXfermode* xmode, const uint16_t indices[],
+ int indexCount, const SkPaint& paint) {
+ if (d.fClip->isEmpty()) {
+ return;
+ }
+ NOT_IMPLEMENTED("drawVerticies", true);
+}
+
+void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
+ const SkPaint& paint) {
+ if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
+ // If we somehow get a raster device, do what our parent would do.
+ SkDevice::drawDevice(d, device, x, y, paint);
+ return;
+ }
+
+ // Assume that a vector capable device means that it's a PDF Device.
+ SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
+ if (pdfDevice->isContentEmpty()) {
+ return;
+ }
+
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
+ ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
+ if (!content.entry()) {
+ return;
+ }
+
+ SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
+ fXObjectResources.push(xobject); // Transfer reference.
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+ &content.entry()->fContent);
+}
+
+const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
+ if (fResourceDict.get() == NULL) {
+ fResourceDict = new SkPDFDict;
+ fResourceDict->unref(); // SkRefPtr and new both took a reference.
+
+ if (fGraphicStateResources.count()) {
+ SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
+ extGState->unref(); // SkRefPtr and new both took a reference.
+ for (int i = 0; i < fGraphicStateResources.count(); i++) {
+ SkString nameString("G");
+ nameString.appendS32(i);
+ extGState->insert(
+ nameString.c_str(),
+ new SkPDFObjRef(fGraphicStateResources[i]))->unref();
+ }
+ fResourceDict->insert("ExtGState", extGState.get());
+ }
+
+ if (fXObjectResources.count()) {
+ SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
+ xObjects->unref(); // SkRefPtr and new both took a reference.
+ for (int i = 0; i < fXObjectResources.count(); i++) {
+ SkString nameString("X");
+ nameString.appendS32(i);
+ xObjects->insert(
+ nameString.c_str(),
+ new SkPDFObjRef(fXObjectResources[i]))->unref();
+ }
+ fResourceDict->insert("XObject", xObjects.get());
+ }
+
+ if (fFontResources.count()) {
+ SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
+ fonts->unref(); // SkRefPtr and new both took a reference.
+ for (int i = 0; i < fFontResources.count(); i++) {
+ SkString nameString("F");
+ nameString.appendS32(i);
+ fonts->insert(nameString.c_str(),
+ new SkPDFObjRef(fFontResources[i]))->unref();
+ }
+ fResourceDict->insert("Font", fonts.get());
+ }
+
+ if (fShaderResources.count()) {
+ SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
+ patterns->unref(); // SkRefPtr and new both took a reference.
+ for (int i = 0; i < fShaderResources.count(); i++) {
+ SkString nameString("P");
+ nameString.appendS32(i);
+ patterns->insert(nameString.c_str(),
+ new SkPDFObjRef(fShaderResources[i]))->unref();
+ }
+ fResourceDict->insert("Pattern", patterns.get());
+ }
+
+ // For compatibility, add all proc sets (only used for output to PS
+ // devices).
+ const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
+ SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
+ procSets->unref(); // SkRefPtr and new both took a reference.
+ procSets->reserve(SK_ARRAY_COUNT(procs));
+ for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
+ procSets->append(new SkPDFName(procs[i]))->unref();
+ fResourceDict->insert("ProcSet", procSets.get());
+ }
+ return fResourceDict;
+}
+
+void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
+ resourceList->setReserve(resourceList->count() +
+ fGraphicStateResources.count() +
+ fXObjectResources.count() +
+ fFontResources.count() +
+ fShaderResources.count());
+ for (int i = 0; i < fGraphicStateResources.count(); i++) {
+ resourceList->push(fGraphicStateResources[i]);
+ fGraphicStateResources[i]->ref();
+ fGraphicStateResources[i]->getResources(resourceList);
+ }
+ for (int i = 0; i < fXObjectResources.count(); i++) {
+ resourceList->push(fXObjectResources[i]);
+ fXObjectResources[i]->ref();
+ fXObjectResources[i]->getResources(resourceList);
+ }
+ for (int i = 0; i < fFontResources.count(); i++) {
+ resourceList->push(fFontResources[i]);
+ fFontResources[i]->ref();
+ fFontResources[i]->getResources(resourceList);
+ }
+ for (int i = 0; i < fShaderResources.count(); i++) {
+ resourceList->push(fShaderResources[i]);
+ fShaderResources[i]->ref();
+ fShaderResources[i]->getResources(resourceList);
+ }
+}
+
+const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
+ return fFontResources;
+}
+
+SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
+ SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
+ zero->unref(); // SkRefPtr and new both took a reference.
+
+ SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
+ mediaBox->unref(); // SkRefPtr and new both took a reference.
+ mediaBox->reserve(4);
+ mediaBox->append(zero.get());
+ mediaBox->append(zero.get());
+ mediaBox->append(new SkPDFInt(fPageSize.fWidth))->unref();
+ mediaBox->append(new SkPDFInt(fPageSize.fHeight))->unref();
+ return mediaBox;
+}
+
+SkStream* SkPDFDevice::content() const {
+ SkDynamicMemoryWStream data;
+ if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
+ SkPDFUtils::AppendTransform(fInitialTransform, &data);
+ }
+ // If the content area is the entire page, then we don't need to clip
+ // the content area (PDF area clips to the page size). Otherwise,
+ // we have to clip to the content area; we've already applied the
+ // initial transform, so just clip to the device size.
+ if (fPageSize != fContentSize) {
+ SkRect r = SkRect::MakeWH(this->width(), this->height());
+ emit_clip(NULL, &r, &data);
+ }
+
+ GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, &data);
+ for (ContentEntry* entry = fContentEntries.get();
+ entry != NULL;
+ entry = entry->fNext.get()) {
+ SkIPoint translation = this->getOrigin();
+ translation.negate();
+ gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
+ translation);
+ gsState.updateMatrix(entry->fState.fMatrix);
+ gsState.updateDrawingState(entry->fState);
+ data.write(entry->fContent.getStream(), entry->fContent.getOffset());
+ }
+ gsState.drainStack();
+
+ SkMemoryStream* result = new SkMemoryStream;
+ result->setMemoryOwned(data.detach(), data.getOffset());
+ return result;
+}
+
+void SkPDFDevice::createFormXObjectFromDevice(
+ SkRefPtr<SkPDFFormXObject>* xobject) {
+ *xobject = new SkPDFFormXObject(this);
+ (*xobject)->unref(); // SkRefPtr and new both took a reference.
+ cleanUp(); // Reset this device to have no content.
+ init();
+}
+
+void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack,
+ const SkRegion& clipRegion) {
+ if (clipRegion.isEmpty() || isContentEmpty()) {
+ return;
+ }
+ SkRefPtr<SkPDFFormXObject> curContent;
+ createFormXObjectFromDevice(&curContent);
+
+ // Redraw what we already had, but with the clip as a mask.
+ drawFormXObjectWithClip(curContent.get(), clipStack, clipRegion, true);
+}
+
+void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject,
+ const SkClipStack* clipStack,
+ const SkRegion& clipRegion,
+ bool invertClip) {
+ if (clipRegion.isEmpty() && !invertClip) {
+ return;
+ }
+
+ // Create the mask.
+ SkMatrix identity;
+ identity.reset();
+ SkDraw draw;
+ draw.fMatrix = &identity;
+ draw.fClip = &clipRegion;
+ draw.fClipStack = clipStack;
+ SkPaint stockPaint;
+ this->drawPaint(draw, stockPaint);
+ SkRefPtr<SkPDFFormXObject> maskFormXObject;
+ createFormXObjectFromDevice(&maskFormXObject);
+ SkRefPtr<SkPDFGraphicState> sMaskGS =
+ SkPDFGraphicState::getSMaskGraphicState(maskFormXObject.get(),
+ invertClip);
+ sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref.
+
+ // Draw the xobject with the clip as a mask.
+ ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
+ identity, stockPaint);
+ if (!content.entry()) {
+ return;
+ }
+ SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+ &content.entry()->fContent);
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count(),
+ &content.entry()->fContent);
+ fXObjectResources.push(xobject);
+ xobject->ref();
+
+ sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState();
+ sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref.
+ SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+ &content.entry()->fContent);
+}
+
+ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
+ const SkRegion& clipRegion,
+ const SkMatrix& matrix,
+ const SkPaint& paint,
+ bool hasText,
+ SkRefPtr<SkPDFFormXObject>* dst) {
+ if (clipRegion.isEmpty()) {
+ return NULL;
+ }
+
+ // The clip stack can come from an SkDraw where it is technically optional.
+ SkClipStack synthesizedClipStack;
+ if (clipStack == NULL) {
+ if (clipRegion == fExistingClipRegion) {
+ clipStack = &fExistingClipStack;
+ } else {
+ // GraphicStackState::updateClip expects the clip stack to have
+ // fExistingClip as a prefix, so start there, then set the clip
+ // to the passed region.
+ synthesizedClipStack = fExistingClipStack;
+ SkPath clipPath;
+ clipRegion.getBoundaryPath(&clipPath);
+ synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op);
+ clipStack = &synthesizedClipStack;
+ }
+ }
+
+ SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+ if (paint.getXfermode()) {
+ paint.getXfermode()->asMode(&xfermode);
+ }
+
+ if (xfermode == SkXfermode::kClear_Mode ||
+ xfermode == SkXfermode::kSrc_Mode) {
+ this->clearClipFromContent(clipStack, clipRegion);
+ } else if (xfermode == SkXfermode::kSrcIn_Mode ||
+ xfermode == SkXfermode::kDstIn_Mode ||
+ xfermode == SkXfermode::kSrcOut_Mode ||
+ xfermode == SkXfermode::kDstOut_Mode) {
+ // For the following modes, we use both source and destination, but
+ // we use one as a smask for the other, so we have to make form xobjects
+ // out of both of them: SrcIn, DstIn, SrcOut, DstOut.
+ if (isContentEmpty()) {
+ return NULL;
+ } else {
+ createFormXObjectFromDevice(dst);
+ }
+ }
+ // TODO(vandebo) Figure out how/if we can handle the following modes:
+ // SrcAtop, DestAtop, Xor, Plus.
+
+ // These xfer modes don't draw source at all.
+ if (xfermode == SkXfermode::kClear_Mode ||
+ xfermode == SkXfermode::kDst_Mode) {
+ return NULL;
+ }
+
+ ContentEntry* entry;
+ SkTScopedPtr<ContentEntry> newEntry;
+ if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) {
+ entry = fLastContentEntry;
+ } else {
+ newEntry.reset(new ContentEntry);
+ entry = newEntry.get();
+ }
+
+ populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
+ hasText, &entry->fState);
+ if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
+ entry->fState.compareInitialState(fLastContentEntry->fState)) {
+ return fLastContentEntry;
+ }
+
+ if (!fLastContentEntry) {
+ fContentEntries.reset(entry);
+ fLastContentEntry = entry;
+ } else if (xfermode == SkXfermode::kDstOver_Mode) {
+ entry->fNext.reset(fContentEntries.release());
+ fContentEntries.reset(entry);
+ } else {
+ fLastContentEntry->fNext.reset(entry);
+ fLastContentEntry = entry;
+ }
+ newEntry.release();
+ return entry;
+}
+
+void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
+ SkPDFFormXObject* dst) {
+ if (xfermode != SkXfermode::kSrcIn_Mode &&
+ xfermode != SkXfermode::kDstIn_Mode &&
+ xfermode != SkXfermode::kSrcOut_Mode &&
+ xfermode != SkXfermode::kDstOut_Mode) {
+ SkASSERT(!dst);
+ return;
+ }
+ SkASSERT(dst);
+ SkASSERT(!fContentEntries->fNext.get());
+
+ // We have to make a copy of these here because changing the current
+ // content into a form xobject will destroy them.
+ SkClipStack clipStack = fContentEntries->fState.fClipStack;
+ SkRegion clipRegion = fContentEntries->fState.fClipRegion;
+
+ SkRefPtr<SkPDFFormXObject> srcFormXObject;
+ if (!isContentEmpty()) {
+ createFormXObjectFromDevice(&srcFormXObject);
+ }
+
+ drawFormXObjectWithClip(dst, &clipStack, clipRegion, true);
+
+ // We've redrawn dst minus the clip area, if there's no src, we're done.
+ if (!srcFormXObject.get()) {
+ return;
+ }
+
+ SkMatrix identity;
+ identity.reset();
+ SkPaint stockPaint;
+ ScopedContentEntry inClipContentEntry(this, &fExistingClipStack,
+ fExistingClipRegion, identity,
+ stockPaint);
+ if (!inClipContentEntry.entry()) {
+ return;
+ }
+
+ SkRefPtr<SkPDFGraphicState> sMaskGS;
+ if (xfermode == SkXfermode::kSrcIn_Mode ||
+ xfermode == SkXfermode::kSrcOut_Mode) {
+ sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
+ dst, xfermode == SkXfermode::kSrcOut_Mode);
+ fXObjectResources.push(srcFormXObject.get());
+ srcFormXObject->ref();
+ } else {
+ sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
+ srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode);
+ // dst already added to fXObjectResources in drawFormXObjectWithClip.
+ }
+ sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref.
+ SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+ &inClipContentEntry.entry()->fContent);
+
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+ &inClipContentEntry.entry()->fContent);
+
+ sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState();
+ sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref.
+ SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+ &inClipContentEntry.entry()->fContent);
+}
+
+bool SkPDFDevice::isContentEmpty() {
+ if (!fContentEntries.get() || fContentEntries->fContent.getOffset() == 0) {
+ SkASSERT(!fContentEntries.get() || !fContentEntries->fNext.get());
+ return true;
+ }
+ return false;
+}
+
+
+void SkPDFDevice::populateGraphicStateEntryFromPaint(
+ const SkMatrix& matrix,
+ const SkClipStack& clipStack,
+ const SkRegion& clipRegion,
+ const SkPaint& paint,
+ bool hasText,
+ GraphicStateEntry* entry) {
+ SkASSERT(paint.getPathEffect() == NULL);
+
+ NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
+ NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
+
+ entry->fMatrix = matrix;
+ entry->fClipStack = clipStack;
+ entry->fClipRegion = clipRegion;
+
+ // PDF treats a shader as a color, so we only set one or the other.
+ SkRefPtr<SkPDFShader> pdfShader;
+ const SkShader* shader = paint.getShader();
+ SkColor color = paint.getColor();
+ if (shader) {
+ // PDF positions patterns relative to the initial transform, so
+ // we need to apply the current transform to the shader parameters.
+ SkMatrix transform = matrix;
+ transform.postConcat(fInitialTransform);
+
+ // PDF doesn't support kClamp_TileMode, so we simulate it by making
+ // a pattern the size of the current clip.
+ SkIRect bounds = clipRegion.getBounds();
+ pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
+ SkSafeUnref(pdfShader.get()); // getShader and SkRefPtr both took a ref
+
+ // A color shader is treated as an invalid shader so we don't have
+ // to set a shader just for a color.
+ if (pdfShader.get() == NULL) {
+ entry->fColor = 0;
+ color = 0;
+
+ // Check for a color shader.
+ SkShader::GradientInfo gradientInfo;
+ SkColor gradientColor;
+ gradientInfo.fColors = &gradientColor;
+ gradientInfo.fColorOffsets = NULL;
+ gradientInfo.fColorCount = 1;
+ if (shader->asAGradient(&gradientInfo) ==
+ SkShader::kColor_GradientType) {
+ entry->fColor = SkColorSetA(gradientColor, 0xFF);
+ color = gradientColor;
+ }
+ }
+ }
+
+ if (pdfShader) {
+ // pdfShader has been canonicalized so we can directly compare
+ // pointers.
+ int resourceIndex = fShaderResources.find(pdfShader.get());
+ if (resourceIndex < 0) {
+ resourceIndex = fShaderResources.count();
+ fShaderResources.push(pdfShader.get());
+ pdfShader->ref();
+ }
+ entry->fShaderIndex = resourceIndex;
+ } else {
+ entry->fShaderIndex = -1;
+ entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
+ color = paint.getColor();
+ }
+
+ SkRefPtr<SkPDFGraphicState> newGraphicState;
+ if (color == paint.getColor()) {
+ newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(paint);
+ } else {
+ SkPaint newPaint = paint;
+ newPaint.setColor(color);
+ newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint);
+ }
+ newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
+ int resourceIndex = addGraphicStateResource(newGraphicState.get());
+ entry->fGraphicStateIndex = resourceIndex;
+
+ if (hasText) {
+ entry->fTextScaleX = paint.getTextScaleX();
+ entry->fTextFill = paint.getStyle();
+ } else {
+ entry->fTextScaleX = 0;
+ }
+}
+
+int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
+ // Assumes that gs has been canonicalized (so we can directly compare
+ // pointers).
+ int result = fGraphicStateResources.find(gs);
+ if (result < 0) {
+ result = fGraphicStateResources.count();
+ fGraphicStateResources.push(gs);
+ gs->ref();
+ }
+ return result;
+}
+
+void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
+ ContentEntry* contentEntry) {
+ SkTypeface* typeface = paint.getTypeface();
+ if (contentEntry->fState.fFont == NULL ||
+ contentEntry->fState.fTextSize != paint.getTextSize() ||
+ !contentEntry->fState.fFont->hasGlyph(glyphID)) {
+ int fontIndex = getFontResourceIndex(typeface, glyphID);
+ contentEntry->fContent.writeText("/F");
+ contentEntry->fContent.writeDecAsText(fontIndex);
+ contentEntry->fContent.writeText(" ");
+ SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent);
+ contentEntry->fContent.writeText(" Tf\n");
+ contentEntry->fState.fFont = fFontResources[fontIndex];
+ }
+}
+
+int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
+ SkRefPtr<SkPDFFont> newFont = SkPDFFont::getFontResource(typeface, glyphID);
+ newFont->unref(); // getFontResource and SkRefPtr both took a ref.
+ int resourceIndex = fFontResources.find(newFont.get());
+ if (resourceIndex < 0) {
+ resourceIndex = fFontResources.count();
+ fFontResources.push(newFont.get());
+ newFont->ref();
+ }
+ return resourceIndex;
+}
+
+void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
+ const SkClipStack* clipStack,
+ const SkRegion& clipRegion,
+ const SkBitmap& bitmap,
+ const SkIRect* srcRect,
+ const SkPaint& paint) {
+ SkMatrix scaled;
+ // Adjust for origin flip.
+ scaled.setScale(1, -1);
+ scaled.postTranslate(0, 1);
+ // Scale the image up from 1x1 to WxH.
+ SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+ scaled.postScale(SkIntToScalar(subset.width()),
+ SkIntToScalar(subset.height()));
+ scaled.postConcat(matrix);
+ ScopedContentEntry content(this, clipStack, clipRegion, scaled, paint);
+ if (!content.entry()) {
+ return;
+ }
+
+ if (srcRect && !subset.intersect(*srcRect)) {
+ return;
+ }
+
+ SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
+ if (!image) {
+ return;
+ }
+
+ fXObjectResources.push(image); // Transfer reference.
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+ &content.entry()->fContent);
+}
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
new file mode 100644
index 0000000000..95370b4642
--- /dev/null
+++ b/src/pdf/SkPDFDocument.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkPDFPage.h"
+#include "SkStream.h"
+
+// Add the resources, starting at firstIndex to the catalog, removing any dupes.
+// A hash table would be really nice here.
+void addResourcesToCatalog(int firstIndex, bool firstPage,
+ SkTDArray<SkPDFObject*>* resourceList,
+ SkPDFCatalog* catalog) {
+ for (int i = firstIndex; i < resourceList->count(); i++) {
+ int index = resourceList->find((*resourceList)[i]);
+ if (index != i) {
+ (*resourceList)[i]->unref();
+ resourceList->removeShuffle(i);
+ i--;
+ } else {
+ catalog->addObject((*resourceList)[i], firstPage);
+ }
+ }
+}
+
+SkPDFDocument::SkPDFDocument() : fXRefFileOffset(0) {
+ fDocCatalog = new SkPDFDict("Catalog");
+ fDocCatalog->unref(); // SkRefPtr and new both took a reference.
+ fCatalog.addObject(fDocCatalog.get(), true);
+}
+
+SkPDFDocument::~SkPDFDocument() {
+ fPages.safeUnrefAll();
+
+ // The page tree has both child and parent pointers, so it creates a
+ // reference cycle. We must clear that cycle to properly reclaim memory.
+ for (int i = 0; i < fPageTree.count(); i++)
+ fPageTree[i]->clear();
+ fPageTree.safeUnrefAll();
+ fPageResources.safeUnrefAll();
+}
+
+bool SkPDFDocument::emitPDF(SkWStream* stream) {
+ if (fPages.isEmpty())
+ return false;
+
+ // We haven't emitted the document before if fPageTree is empty.
+ if (fPageTree.count() == 0) {
+ SkPDFDict* pageTreeRoot;
+ SkPDFPage::generatePageTree(fPages, &fCatalog, &fPageTree,
+ &pageTreeRoot);
+ fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
+
+ /* TODO(vandebo) output intent
+ SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
+ outputIntent->unref(); // SkRefPtr and new both took a reference.
+ outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref();
+ outputIntent->insert("OutputConditionIdentifier",
+ new SkPDFString("sRGB"))->unref();
+ SkRefPtr<SkPDFArray> intentArray = new SkPDFArray;
+ intentArray->unref(); // SkRefPtr and new both took a reference.
+ intentArray->append(outputIntent.get());
+ fDocCatalog->insert("OutputIntent", intentArray.get());
+ */
+
+ bool first_page = true;
+ for (int i = 0; i < fPages.count(); i++) {
+ int resourceCount = fPageResources.count();
+ fPages[i]->finalizePage(&fCatalog, first_page, &fPageResources);
+ addResourcesToCatalog(resourceCount, first_page, &fPageResources,
+ &fCatalog);
+ if (i == 0) {
+ first_page = false;
+ fSecondPageFirstResourceIndex = fPageResources.count();
+ }
+ }
+
+ // Figure out the size of things and inform the catalog of file offsets.
+ off_t fileOffset = headerSize();
+ fileOffset += fCatalog.setFileOffset(fDocCatalog.get(), fileOffset);
+ fileOffset += fCatalog.setFileOffset(fPages[0], fileOffset);
+ fileOffset += fPages[0]->getPageSize(&fCatalog, fileOffset);
+ for (int i = 0; i < fSecondPageFirstResourceIndex; i++)
+ fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset);
+ if (fPages.count() > 1) {
+ // TODO(vandebo) For linearized format, save the start of the
+ // first page xref table and calculate the size.
+ }
+
+ for (int i = 0; i < fPageTree.count(); i++)
+ fileOffset += fCatalog.setFileOffset(fPageTree[i], fileOffset);
+
+ for (int i = 1; i < fPages.count(); i++)
+ fileOffset += fPages[i]->getPageSize(&fCatalog, fileOffset);
+
+ for (int i = fSecondPageFirstResourceIndex;
+ i < fPageResources.count();
+ i++)
+ fileOffset += fCatalog.setFileOffset(fPageResources[i], fileOffset);
+
+ fXRefFileOffset = fileOffset;
+ }
+
+ emitHeader(stream);
+ fDocCatalog->emitObject(stream, &fCatalog, true);
+ fPages[0]->emitObject(stream, &fCatalog, true);
+ fPages[0]->emitPage(stream, &fCatalog);
+ for (int i = 0; i < fSecondPageFirstResourceIndex; i++)
+ fPageResources[i]->emitObject(stream, &fCatalog, true);
+ // TODO(vandebo) support linearized format
+ //if (fPages.size() > 1) {
+ // // TODO(vandebo) save the file offset for the first page xref table.
+ // fCatalog.emitXrefTable(stream, true);
+ //}
+
+ for (int i = 0; i < fPageTree.count(); i++)
+ fPageTree[i]->emitObject(stream, &fCatalog, true);
+
+ for (int i = 1; i < fPages.count(); i++)
+ fPages[i]->emitPage(stream, &fCatalog);
+
+ for (int i = fSecondPageFirstResourceIndex; i < fPageResources.count(); i++)
+ fPageResources[i]->emitObject(stream, &fCatalog, true);
+
+ int64_t objCount = fCatalog.emitXrefTable(stream, fPages.count() > 1);
+ emitFooter(stream, objCount);
+ return true;
+}
+
+bool SkPDFDocument::appendPage(const SkRefPtr<SkPDFDevice>& pdfDevice) {
+ if (fPageTree.count() != 0)
+ return false;
+
+ SkPDFPage* page = new SkPDFPage(pdfDevice);
+ fPages.push(page); // Reference from new passed to fPages.
+ // The rest of the pages will be added to the catalog along with the rest
+ // of the page tree. But the first page has to be marked as such, so we
+ // handle it here.
+ if (fPages.count() == 1)
+ fCatalog.addObject(page, true);
+ return true;
+}
+
+const SkTDArray<SkPDFPage*>& SkPDFDocument::getPages() {
+ return fPages;
+}
+
+void SkPDFDocument::emitHeader(SkWStream* stream) {
+ stream->writeText("%PDF-1.4\n%");
+ // The PDF spec recommends including a comment with four bytes, all
+ // with their high bits set. This is "Skia" with the high bits set.
+ stream->write32(0xD3EBE9E1);
+ stream->writeText("\n");
+}
+
+size_t SkPDFDocument::headerSize() {
+ SkDynamicMemoryWStream buffer;
+ emitHeader(&buffer);
+ return buffer.getOffset();
+}
+
+void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) {
+ if (fTrailerDict.get() == NULL) {
+ fTrailerDict = new SkPDFDict();
+ fTrailerDict->unref(); // SkRefPtr and new both took a reference.
+
+ // TODO(vandebo) Linearized format will take a Prev entry too.
+ // TODO(vandebo) PDF/A requires an ID entry.
+ fTrailerDict->insert("Size", new SkPDFInt(objCount))->unref();
+ fTrailerDict->insert("Root",
+ new SkPDFObjRef(fDocCatalog.get()))->unref();
+ }
+
+ stream->writeText("trailer\n");
+ fTrailerDict->emitObject(stream, &fCatalog, false);
+ stream->writeText("\nstartxref\n");
+ stream->writeBigDecAsText(fXRefFileOffset);
+ stream->writeText("\n%%EOF");
+}
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
new file mode 100755
index 0000000000..277ed12ac3
--- /dev/null
+++ b/src/pdf/SkPDFFont.cpp
@@ -0,0 +1,976 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+
+#include "SkFontHost.h"
+#include "SkGlyphCache.h"
+#include "SkPaint.h"
+#include "SkPDFDevice.h"
+#include "SkPDFFont.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+#include "SkTypes.h"
+#include "SkUtils.h"
+
+namespace {
+
+bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
+ size_t* size) {
+ // PFB sections have a two or six bytes header. 0x80 and a one byte
+ // section type followed by a four byte section length. Type one is
+ // an ASCII section (includes a length), type two is a binary section
+ // (includes a length) and type three is an EOF marker with no length.
+ const uint8_t* buf = *src;
+ if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType)
+ return false;
+ if (buf[1] == 3)
+ return true;
+ if (*len < 6)
+ return false;
+
+ *size = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24);
+ size_t consumed = *size + 6;
+ if (consumed > *len)
+ return false;
+ *src = *src + consumed;
+ *len = *len - consumed;
+ return true;
+}
+
+bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen,
+ size_t* dataLen, size_t* trailerLen) {
+ const uint8_t* srcPtr = src;
+ size_t remaining = size;
+
+ return parsePFBSection(&srcPtr, &remaining, 1, headerLen) &&
+ parsePFBSection(&srcPtr, &remaining, 2, dataLen) &&
+ parsePFBSection(&srcPtr, &remaining, 1, trailerLen) &&
+ parsePFBSection(&srcPtr, &remaining, 3, NULL);
+}
+
+/* The sections of a PFA file are implicitly defined. The body starts
+ * after the line containing "eexec," and the trailer starts with 512
+ * literal 0's followed by "cleartomark" (plus arbitrary white space).
+ *
+ * This function assumes that src is NUL terminated, but the NUL
+ * termination is not included in size.
+ *
+ */
+bool parsePFA(const char* src, size_t size, size_t* headerLen,
+ size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) {
+ const char* end = src + size;
+
+ const char* dataPos = strstr(src, "eexec");
+ if (!dataPos)
+ return false;
+ dataPos += strlen("eexec");
+ while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') &&
+ dataPos < end)
+ dataPos++;
+ *headerLen = dataPos - src;
+
+ const char* trailerPos = strstr(dataPos, "cleartomark");
+ if (!trailerPos)
+ return false;
+ int zeroCount = 0;
+ for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) {
+ if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') {
+ continue;
+ } else if (*trailerPos == '0') {
+ zeroCount++;
+ } else {
+ return false;
+ }
+ }
+ if (zeroCount != 512)
+ return false;
+
+ *hexDataLen = trailerPos - src - *headerLen;
+ *trailerLen = size - *headerLen - *hexDataLen;
+
+ // Verify that the data section is hex encoded and count the bytes.
+ int nibbles = 0;
+ for (; dataPos < trailerPos; dataPos++) {
+ if (isspace(*dataPos))
+ continue;
+ if (!isxdigit(*dataPos))
+ return false;
+ nibbles++;
+ }
+ *dataLen = (nibbles + 1) / 2;
+
+ return true;
+}
+
+int8_t hexToBin(uint8_t c) {
+ if (!isxdigit(c))
+ return -1;
+ if (c <= '9') return c - '0';
+ if (c <= 'F') return c - 'A' + 10;
+ if (c <= 'f') return c - 'a' + 10;
+ return -1;
+}
+
+SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen,
+ size_t* dataLen, size_t* trailerLen) {
+ // srcStream may be backed by a file or a unseekable fd, so we may not be
+ // able to use skip(), rewind(), or getMemoryBase(). read()ing through
+ // the input only once is doable, but very ugly. Furthermore, it'd be nice
+ // if the data was NUL terminated so that we can use strstr() to search it.
+ // Make as few copies as possible given these constraints.
+ SkDynamicMemoryWStream dynamicStream;
+ SkRefPtr<SkMemoryStream> staticStream;
+ const uint8_t* src;
+ size_t srcLen;
+ if ((srcLen = srcStream->getLength()) > 0) {
+ staticStream = new SkMemoryStream(srcLen + 1);
+ staticStream->unref(); // new and SkRefPtr both took a ref.
+ src = (const uint8_t*)staticStream->getMemoryBase();
+ if (srcStream->getMemoryBase() != NULL) {
+ memcpy((void *)src, srcStream->getMemoryBase(), srcLen);
+ } else {
+ size_t read = 0;
+ while (read < srcLen) {
+ size_t got = srcStream->read((void *)staticStream->getAtPos(),
+ srcLen - read);
+ if (got == 0)
+ return NULL;
+ read += got;
+ staticStream->seek(read);
+ }
+ }
+ ((uint8_t *)src)[srcLen] = 0;
+ } else {
+ static const size_t bufSize = 4096;
+ uint8_t buf[bufSize];
+ size_t amount;
+ while ((amount = srcStream->read(buf, bufSize)) > 0)
+ dynamicStream.write(buf, amount);
+ amount = 0;
+ dynamicStream.write(&amount, 1); // NULL terminator.
+ // getStream makes another copy, but we couldn't do any better.
+ src = (const uint8_t*)dynamicStream.getStream();
+ srcLen = dynamicStream.getOffset() - 1;
+ }
+
+ if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) {
+ SkMemoryStream* result =
+ new SkMemoryStream(*headerLen + *dataLen + *trailerLen);
+ memcpy((char*)result->getAtPos(), src + 6, *headerLen);
+ result->seek(*headerLen);
+ memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen);
+ result->seek(*headerLen + *dataLen);
+ memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen,
+ *trailerLen);
+ result->rewind();
+ return result;
+ }
+
+ // A PFA has to be converted for PDF.
+ size_t hexDataLen;
+ if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen,
+ trailerLen)) {
+ SkMemoryStream* result =
+ new SkMemoryStream(*headerLen + *dataLen + *trailerLen);
+ memcpy((char*)result->getAtPos(), src, *headerLen);
+ result->seek(*headerLen);
+
+ const uint8_t* hexData = src + *headerLen;
+ const uint8_t* trailer = hexData + hexDataLen;
+ size_t outputOffset = 0;
+ uint8_t dataByte = 0; // To hush compiler.
+ bool highNibble = true;
+ for (; hexData < trailer; hexData++) {
+ char curNibble = hexToBin(*hexData);
+ if (curNibble < 0)
+ continue;
+ if (highNibble) {
+ dataByte = curNibble << 4;
+ highNibble = false;
+ } else {
+ dataByte |= curNibble;
+ highNibble = true;
+ ((char *)result->getAtPos())[outputOffset++] = dataByte;
+ }
+ }
+ if (!highNibble)
+ ((char *)result->getAtPos())[outputOffset++] = dataByte;
+ SkASSERT(outputOffset == *dataLen);
+ result->seek(*headerLen + outputOffset);
+
+ memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen,
+ *trailerLen);
+ result->rewind();
+ return result;
+ }
+
+ return NULL;
+}
+
+// scale from em-units to base-1000, returning as a SkScalar
+SkScalar scaleFromFontUnits(int16_t val, uint16_t emSize) {
+ SkScalar scaled = SkIntToScalar(val);
+ if (emSize == 1000) {
+ return scaled;
+ } else {
+ return SkScalarMulDiv(scaled, 1000, emSize);
+ }
+}
+
+void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box,
+ SkWStream* content) {
+ // Specify width and bounding box for the glyph.
+ SkPDFScalar::Append(width, content);
+ content->writeText(" 0 ");
+ content->writeDecAsText(box.fLeft);
+ content->writeText(" ");
+ content->writeDecAsText(box.fTop);
+ content->writeText(" ");
+ content->writeDecAsText(box.fRight);
+ content->writeText(" ");
+ content->writeDecAsText(box.fBottom);
+ content->writeText(" d1\n");
+}
+
+SkPDFArray* makeFontBBox(SkIRect glyphBBox, uint16_t emSize) {
+ SkPDFArray* bbox = new SkPDFArray;
+ bbox->reserve(4);
+ bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fLeft,
+ emSize)))->unref();
+ bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fBottom,
+ emSize)))->unref();
+ bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fRight,
+ emSize)))->unref();
+ bbox->append(new SkPDFScalar(scaleFromFontUnits(glyphBBox.fTop,
+ emSize)))->unref();
+ return bbox;
+}
+
+SkPDFArray* appendWidth(const int16_t& width, uint16_t emSize,
+ SkPDFArray* array) {
+ array->append(new SkPDFScalar(scaleFromFontUnits(width, emSize)))->unref();
+ return array;
+}
+
+SkPDFArray* appendVerticalAdvance(
+ const SkAdvancedTypefaceMetrics::VerticalMetric& advance,
+ uint16_t emSize, SkPDFArray* array) {
+ appendWidth(advance.fVerticalAdvance, emSize, array);
+ appendWidth(advance.fOriginXDisp, emSize, array);
+ appendWidth(advance.fOriginYDisp, emSize, array);
+ return array;
+}
+
+template <typename Data>
+SkPDFArray* composeAdvanceData(
+ SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* advanceInfo,
+ uint16_t emSize,
+ SkPDFArray* (*appendAdvance)(const Data& advance, uint16_t emSize,
+ SkPDFArray* array),
+ Data* defaultAdvance) {
+ SkPDFArray* result = new SkPDFArray();
+ for (; advanceInfo != NULL; advanceInfo = advanceInfo->fNext.get()) {
+ switch (advanceInfo->fType) {
+ case SkAdvancedTypefaceMetrics::WidthRange::kDefault: {
+ SkASSERT(advanceInfo->fAdvance.count() == 1);
+ *defaultAdvance = advanceInfo->fAdvance[0];
+ break;
+ }
+ case SkAdvancedTypefaceMetrics::WidthRange::kRange: {
+ SkRefPtr<SkPDFArray> advanceArray = new SkPDFArray();
+ advanceArray->unref(); // SkRefPtr and new both took a ref.
+ for (int j = 0; j < advanceInfo->fAdvance.count(); j++)
+ appendAdvance(advanceInfo->fAdvance[j], emSize,
+ advanceArray.get());
+ result->append(new SkPDFInt(advanceInfo->fStartId))->unref();
+ result->append(advanceArray.get());
+ break;
+ }
+ case SkAdvancedTypefaceMetrics::WidthRange::kRun: {
+ SkASSERT(advanceInfo->fAdvance.count() == 1);
+ result->append(new SkPDFInt(advanceInfo->fStartId))->unref();
+ result->append(new SkPDFInt(advanceInfo->fEndId))->unref();
+ appendAdvance(advanceInfo->fAdvance[0], emSize, result);
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+} // namespace
+
+static void append_tounicode_header(SkDynamicMemoryWStream* cmap) {
+ // 12 dict begin: 12 is an Adobe-suggested value. Shall not change.
+ // It's there to prevent old version Adobe Readers from malfunctioning.
+ const char* kHeader =
+ "/CIDInit /ProcSet findresource begin\n"
+ "12 dict begin\n"
+ "begincmap\n";
+ cmap->writeText(kHeader);
+
+ // The /CIDSystemInfo must be consistent to the one in
+ // SkPDFFont::populateCIDFont().
+ // We can not pass over the system info object here because the format is
+ // different. This is not a reference object.
+ const char* kSysInfo =
+ "/CIDSystemInfo\n"
+ "<< /Registry (Adobe)\n"
+ "/Ordering (UCS)\n"
+ "/Supplement 0\n"
+ ">> def\n";
+ cmap->writeText(kSysInfo);
+
+ // The CMapName must be consistent to /CIDSystemInfo above.
+ // /CMapType 2 means ToUnicode.
+ // We specify codespacerange from 0x0000 to 0xFFFF because we convert our
+ // code table from unsigned short (16-bits). Codespace range just tells the
+ // PDF processor the valid range. It does not matter whether a complete
+ // mapping is provided or not.
+ const char* kTypeInfo =
+ "/CMapName /Adobe-Identity-UCS def\n"
+ "/CMapType 2 def\n"
+ "1 begincodespacerange\n"
+ "<0000> <FFFF>\n"
+ "endcodespacerange\n";
+ cmap->writeText(kTypeInfo);
+}
+
+static void append_cmap_bfchar_table(uint16_t* glyph_id, SkUnichar* unicode,
+ size_t count,
+ SkDynamicMemoryWStream* cmap) {
+ cmap->writeDecAsText(count);
+ cmap->writeText(" beginbfchar\n");
+ for (size_t i = 0; i < count; ++i) {
+ cmap->writeText("<");
+ cmap->writeHexAsText(glyph_id[i], 4);
+ cmap->writeText("> <");
+ cmap->writeHexAsText(unicode[i], 4);
+ cmap->writeText(">\n");
+ }
+ cmap->writeText("endbfchar\n");
+}
+
+static void append_cmap_footer(SkDynamicMemoryWStream* cmap) {
+ const char* kFooter =
+ "endcmap\n"
+ "CMapName currentdict /CMap defineresource pop\n"
+ "end\n"
+ "end";
+ cmap->writeText(kFooter);
+}
+
+// Generate <bfchar> table according to PDF spec 1.4 and Adobe Technote 5014.
+static void append_cmap_bfchar_sections(
+ const SkTDArray<SkUnichar>& glyphUnicode,
+ SkDynamicMemoryWStream* cmap) {
+ // PDF spec defines that every bf* list can have at most 100 entries.
+ const size_t kMaxEntries = 100;
+ uint16_t glyphId[kMaxEntries];
+ SkUnichar unicode[kMaxEntries];
+ size_t index = 0;
+ for (int i = 0; i < glyphUnicode.count(); i++) {
+ if (glyphUnicode[i]) {
+ glyphId[index] = i;
+ unicode[index] = glyphUnicode[i];
+ ++index;
+ }
+ if (index == kMaxEntries) {
+ append_cmap_bfchar_table(glyphId, unicode, index, cmap);
+ index = 0;
+ }
+ }
+
+ if (index) {
+ append_cmap_bfchar_table(glyphId, unicode, index, cmap);
+ }
+}
+
+/* Font subset design: It would be nice to be able to subset fonts
+ * (particularly type 3 fonts), but it's a lot of work and not a priority.
+ *
+ * Resources are canonicalized and uniqueified by pointer so there has to be
+ * some additional state indicating which subset of the font is used. It
+ * must be maintained at the page granularity and then combined at the document
+ * granularity. a) change SkPDFFont to fill in its state on demand, kind of
+ * like SkPDFGraphicState. b) maintain a per font glyph usage class in each
+ * page/pdf device. c) in the document, retrieve the per font glyph usage
+ * from each page and combine it and ask for a resource with that subset.
+ */
+
+SkPDFFont::~SkPDFFont() {
+ SkAutoMutexAcquire lock(canonicalFontsMutex());
+ int index;
+ if (find(SkTypeface::UniqueID(fTypeface.get()), fFirstGlyphID, &index)) {
+ canonicalFonts().removeShuffle(index);
+#ifdef SK_DEBUG
+ SkASSERT(!fDescendant);
+ } else {
+ SkASSERT(fDescendant);
+#endif
+ }
+ fResources.unrefAll();
+}
+
+void SkPDFFont::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+ resourceList->setReserve(resourceList->count() + fResources.count());
+ for (int i = 0; i < fResources.count(); i++) {
+ resourceList->push(fResources[i]);
+ fResources[i]->ref();
+ fResources[i]->getResources(resourceList);
+ }
+}
+
+SkTypeface* SkPDFFont::typeface() {
+ return fTypeface.get();
+}
+
+SkAdvancedTypefaceMetrics::FontType SkPDFFont::getType() {
+ return fType;
+}
+
+bool SkPDFFont::hasGlyph(uint16_t id) {
+ return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0;
+}
+
+bool SkPDFFont::multiByteGlyphs() {
+ return fMultiByteGlyphs;
+}
+
+size_t SkPDFFont::glyphsToPDFFontEncoding(uint16_t* glyphIDs,
+ size_t numGlyphs) {
+ // A font with multibyte glyphs will support all glyph IDs in a single font.
+ if (fMultiByteGlyphs) {
+ return numGlyphs;
+ }
+
+ for (size_t i = 0; i < numGlyphs; i++) {
+ if (glyphIDs[i] == 0) {
+ continue;
+ }
+ if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) {
+ return i;
+ }
+ glyphIDs[i] -= (fFirstGlyphID - 1);
+ }
+
+ return numGlyphs;
+}
+
+// static
+SkPDFFont* SkPDFFont::getFontResource(SkTypeface* typeface, uint16_t glyphID) {
+ SkAutoMutexAcquire lock(canonicalFontsMutex());
+ const uint32_t fontID = SkTypeface::UniqueID(typeface);
+ int index;
+ if (find(fontID, glyphID, &index)) {
+ canonicalFonts()[index].fFont->ref();
+ return canonicalFonts()[index].fFont;
+ }
+
+ SkRefPtr<SkAdvancedTypefaceMetrics> fontInfo;
+ SkPDFDict* fontDescriptor = NULL;
+ if (index >= 0) {
+ SkPDFFont* relatedFont = canonicalFonts()[index].fFont;
+ SkASSERT(relatedFont->fFontInfo.get());
+ fontInfo = relatedFont->fFontInfo;
+ fontDescriptor = relatedFont->fDescriptor.get();
+ } else {
+ SkAdvancedTypefaceMetrics::PerGlyphInfo info;
+ info = SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo;
+ info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
+ info, SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo);
+ info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
+ info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo);
+ fontInfo = SkFontHost::GetAdvancedTypefaceMetrics(fontID, info);
+ SkSafeUnref(fontInfo.get()); // SkRefPtr and Get both took a reference.
+ }
+
+ SkPDFFont* font = new SkPDFFont(fontInfo.get(), typeface, glyphID, false,
+ fontDescriptor);
+ FontRec newEntry(font, fontID, font->fFirstGlyphID);
+ index = canonicalFonts().count();
+ canonicalFonts().push(newEntry);
+ return font; // Return the reference new SkPDFFont() created.
+}
+
+// static
+SkTDArray<SkPDFFont::FontRec>& SkPDFFont::canonicalFonts() {
+ // This initialization is only thread safe with gcc.
+ static SkTDArray<FontRec> gCanonicalFonts;
+ return gCanonicalFonts;
+}
+
+// static
+SkMutex& SkPDFFont::canonicalFontsMutex() {
+ // This initialization is only thread safe with gcc.
+ static SkMutex gCanonicalFontsMutex;
+ return gCanonicalFontsMutex;
+}
+
+// static
+bool SkPDFFont::find(uint32_t fontID, uint16_t glyphID, int* index) {
+ // TODO(vandebo) optimize this, do only one search?
+ FontRec search(NULL, fontID, glyphID);
+ *index = canonicalFonts().find(search);
+ if (*index >= 0)
+ return true;
+ search.fGlyphID = 0;
+ *index = canonicalFonts().find(search);
+ return false;
+}
+
+SkPDFFont::SkPDFFont(class SkAdvancedTypefaceMetrics* fontInfo,
+ SkTypeface* typeface,
+ uint16_t glyphID,
+ bool descendantFont,
+ SkPDFDict* fontDescriptor)
+ : SkPDFDict("Font"),
+ fTypeface(typeface),
+ fType(fontInfo ? fontInfo->fType :
+ SkAdvancedTypefaceMetrics::kNotEmbeddable_Font),
+#ifdef SK_DEBUG
+ fDescendant(descendantFont),
+#endif
+ fMultiByteGlyphs(false),
+ fFirstGlyphID(1),
+ fLastGlyphID(fontInfo ? fontInfo->fLastGlyphID : 0),
+ fFontInfo(fontInfo),
+ fDescriptor(fontDescriptor) {
+ if (fontInfo && fontInfo->fMultiMaster) {
+ NOT_IMPLEMENTED(true, true);
+ fType = SkAdvancedTypefaceMetrics::kOther_Font;
+ }
+ if (fType == SkAdvancedTypefaceMetrics::kType1CID_Font ||
+ fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+ if (descendantFont) {
+ populateCIDFont();
+ } else {
+ populateType0Font();
+ }
+ // No need to hold onto the font info for fonts types that
+ // support multibyte glyphs.
+ fFontInfo = NULL;
+ return;
+ }
+
+ if (fType == SkAdvancedTypefaceMetrics::kType1_Font &&
+ populateType1Font(glyphID)) {
+ return;
+ }
+
+ SkASSERT(fType == SkAdvancedTypefaceMetrics::kType1_Font ||
+ fType == SkAdvancedTypefaceMetrics::kCFF_Font ||
+ fType == SkAdvancedTypefaceMetrics::kOther_Font ||
+ fType == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font);
+ populateType3Font(glyphID);
+}
+
+void SkPDFFont::populateType0Font() {
+ fMultiByteGlyphs = true;
+
+ insert("Subtype", new SkPDFName("Type0"))->unref();
+ insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref();
+ insert("Encoding", new SkPDFName("Identity-H"))->unref();
+
+ SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
+ descendantFonts->unref(); // SkRefPtr and new took a reference.
+
+ // Pass ref new created to fResources.
+ fResources.push(
+ new SkPDFFont(fFontInfo.get(), fTypeface.get(), 1, true, NULL));
+ descendantFonts->append(new SkPDFObjRef(fResources.top()))->unref();
+ insert("DescendantFonts", descendantFonts.get());
+
+ populateToUnicodeTable();
+}
+
+void SkPDFFont::populateToUnicodeTable() {
+ if (fFontInfo.get() == NULL ||
+ fFontInfo->fGlyphToUnicode.begin() == NULL) {
+ return;
+ }
+
+ SkDynamicMemoryWStream cmap;
+ append_tounicode_header(&cmap);
+ append_cmap_bfchar_sections(fFontInfo->fGlyphToUnicode, &cmap);
+ append_cmap_footer(&cmap);
+ SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream();
+ cmapStream->unref(); // SkRefPtr and new took a reference.
+ cmapStream->setMemoryOwned(cmap.detach(), cmap.getOffset());
+ SkRefPtr<SkPDFStream> pdfCmap = new SkPDFStream(cmapStream.get());
+ fResources.push(pdfCmap.get()); // Pass reference from new.
+ insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
+}
+
+void SkPDFFont::populateCIDFont() {
+ fMultiByteGlyphs = true;
+ insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref();
+
+ if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kType1CID_Font) {
+ insert("Subtype", new SkPDFName("CIDFontType0"))->unref();
+ } else if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+ insert("Subtype", new SkPDFName("CIDFontType2"))->unref();
+ insert("CIDToGIDMap", new SkPDFName("Identity"))->unref();
+ } else {
+ SkASSERT(false);
+ }
+
+ SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict;
+ sysInfo->unref(); // SkRefPtr and new both took a reference.
+ sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref();
+ sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref();
+ sysInfo->insert("Supplement", new SkPDFInt(0))->unref();
+ insert("CIDSystemInfo", sysInfo.get());
+
+ addFontDescriptor(0);
+
+ if (fFontInfo->fGlyphWidths.get()) {
+ int16_t defaultWidth = 0;
+ SkRefPtr<SkPDFArray> widths =
+ composeAdvanceData(fFontInfo->fGlyphWidths.get(),
+ fFontInfo->fEmSize, &appendWidth, &defaultWidth);
+ widths->unref(); // SkRefPtr and compose both took a reference.
+ if (widths->size())
+ insert("W", widths.get());
+ if (defaultWidth != 0) {
+ insert("DW", new SkPDFScalar(scaleFromFontUnits(
+ defaultWidth, fFontInfo->fEmSize)))->unref();
+ }
+ }
+ if (fFontInfo->fVerticalMetrics.get()) {
+ struct SkAdvancedTypefaceMetrics::VerticalMetric defaultAdvance;
+ defaultAdvance.fVerticalAdvance = 0;
+ defaultAdvance.fOriginXDisp = 0;
+ defaultAdvance.fOriginYDisp = 0;
+ SkRefPtr<SkPDFArray> advances =
+ composeAdvanceData(fFontInfo->fVerticalMetrics.get(),
+ fFontInfo->fEmSize, &appendVerticalAdvance,
+ &defaultAdvance);
+ advances->unref(); // SkRefPtr and compose both took a ref.
+ if (advances->size())
+ insert("W2", advances.get());
+ if (defaultAdvance.fVerticalAdvance ||
+ defaultAdvance.fOriginXDisp ||
+ defaultAdvance.fOriginYDisp) {
+ insert("DW2", appendVerticalAdvance(defaultAdvance,
+ fFontInfo->fEmSize,
+ new SkPDFArray))->unref();
+ }
+ }
+}
+
+bool SkPDFFont::populateType1Font(int16_t glyphID) {
+ SkASSERT(!fFontInfo->fVerticalMetrics.get());
+ SkASSERT(fFontInfo->fGlyphWidths.get());
+
+ adjustGlyphRangeForSingleByteEncoding(glyphID);
+
+ int16_t defaultWidth = 0;
+ const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = NULL;
+ const SkAdvancedTypefaceMetrics::WidthRange* widthEntry;
+ for (widthEntry = fFontInfo.get()->fGlyphWidths.get();
+ widthEntry != NULL;
+ widthEntry = widthEntry->fNext.get()) {
+ switch (widthEntry->fType) {
+ case SkAdvancedTypefaceMetrics::WidthRange::kDefault:
+ defaultWidth = widthEntry->fAdvance[0];
+ break;
+ case SkAdvancedTypefaceMetrics::WidthRange::kRun:
+ SkASSERT(false);
+ break;
+ case SkAdvancedTypefaceMetrics::WidthRange::kRange:
+ SkASSERT(widthRangeEntry == NULL);
+ widthRangeEntry = widthEntry;
+ break;
+ }
+ }
+
+ if (!addFontDescriptor(defaultWidth))
+ return false;
+
+ insert("Subtype", new SkPDFName("Type1"))->unref();
+ insert("BaseFont", new SkPDFName(fFontInfo->fFontName))->unref();
+
+ addWidthInfoFromRange(defaultWidth, widthRangeEntry);
+
+ SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
+ encoding->unref(); // SkRefPtr and new both took a reference.
+ insert("Encoding", encoding.get());
+
+ SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
+ encDiffs->unref(); // SkRefPtr and new both took a reference.
+ encoding->insert("Differences", encDiffs.get());
+
+ encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2);
+ encDiffs->append(new SkPDFInt(1))->unref();
+ for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
+ encDiffs->append(
+ new SkPDFName(fFontInfo->fGlyphNames->get()[gID]))->unref();
+ }
+
+ if (fFontInfo->fLastGlyphID <= 255)
+ fFontInfo = NULL;
+ return true;
+}
+
+void SkPDFFont::populateType3Font(int16_t glyphID) {
+ SkPaint paint;
+ paint.setTypeface(fTypeface.get());
+ paint.setTextSize(1000);
+ SkAutoGlyphCache autoCache(paint, NULL);
+ SkGlyphCache* cache = autoCache.getCache();
+ // If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
+ if (fLastGlyphID == 0) {
+ fLastGlyphID = cache->getGlyphCount() - 1;
+ }
+
+ adjustGlyphRangeForSingleByteEncoding(glyphID);
+
+ insert("Subtype", new SkPDFName("Type3"))->unref();
+ // Flip about the x-axis and scale by 1/1000.
+ SkMatrix fontMatrix;
+ fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000));
+ insert("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix))->unref();
+
+ SkRefPtr<SkPDFDict> charProcs = new SkPDFDict;
+ charProcs->unref(); // SkRefPtr and new both took a reference.
+ insert("CharProcs", charProcs.get());
+
+ SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
+ encoding->unref(); // SkRefPtr and new both took a reference.
+ insert("Encoding", encoding.get());
+
+ SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
+ encDiffs->unref(); // SkRefPtr and new both took a reference.
+ encoding->insert("Differences", encDiffs.get());
+ encDiffs->reserve(fLastGlyphID - fFirstGlyphID + 2);
+ encDiffs->append(new SkPDFInt(1))->unref();
+
+ SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
+ widthArray->unref(); // SkRefPtr and new both took a ref.
+
+ SkIRect bbox = SkIRect::MakeEmpty();
+ for (int gID = fFirstGlyphID; gID <= fLastGlyphID; gID++) {
+ SkString characterName;
+ characterName.printf("gid%d", gID);
+ encDiffs->append(new SkPDFName(characterName))->unref();
+
+ const SkGlyph& glyph = cache->getGlyphIDMetrics(gID);
+ widthArray->append(new SkPDFScalar(SkFixedToScalar(glyph.fAdvanceX)))->unref();
+ SkIRect glyphBBox = SkIRect::MakeXYWH(glyph.fLeft, glyph.fTop,
+ glyph.fWidth, glyph.fHeight);
+ bbox.join(glyphBBox);
+
+ SkDynamicMemoryWStream content;
+ setGlyphWidthAndBoundingBox(SkFixedToScalar(glyph.fAdvanceX), glyphBBox,
+ &content);
+ const SkPath* path = cache->findPath(glyph);
+ if (path) {
+ SkPDFUtils::EmitPath(*path, &content);
+ SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(),
+ &content);
+ }
+ SkRefPtr<SkMemoryStream> glyphStream = new SkMemoryStream();
+ glyphStream->unref(); // SkRefPtr and new both took a ref.
+ glyphStream->setMemoryOwned(content.detach(), content.getOffset());
+
+ SkRefPtr<SkPDFStream> glyphDescription =
+ new SkPDFStream(glyphStream.get());
+ // SkRefPtr and new both ref()'d charProcs, pass one.
+ fResources.push(glyphDescription.get());
+ charProcs->insert(characterName.c_str(),
+ new SkPDFObjRef(glyphDescription.get()))->unref();
+ }
+
+ insert("FontBBox", makeFontBBox(bbox, 1000))->unref();
+ insert("FirstChar", new SkPDFInt(fFirstGlyphID))->unref();
+ insert("LastChar", new SkPDFInt(fLastGlyphID))->unref();
+ insert("Widths", widthArray.get());
+ insert("CIDToGIDMap", new SkPDFName("Identity"))->unref();
+
+ if (fFontInfo && fFontInfo->fLastGlyphID <= 255)
+ fFontInfo = NULL;
+
+ populateToUnicodeTable();
+}
+
+bool SkPDFFont::addFontDescriptor(int16_t defaultWidth) {
+ if (fDescriptor.get() != NULL) {
+ fResources.push(fDescriptor.get());
+ fDescriptor->ref();
+ insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref();
+ return true;
+ }
+
+ fDescriptor = new SkPDFDict("FontDescriptor");
+ fDescriptor->unref(); // SkRefPtr and new both took a ref.
+
+ switch (fFontInfo->fType) {
+ case SkAdvancedTypefaceMetrics::kType1_Font: {
+ size_t header SK_INIT_TO_AVOID_WARNING;
+ size_t data SK_INIT_TO_AVOID_WARNING;
+ size_t trailer SK_INIT_TO_AVOID_WARNING;
+ SkRefPtr<SkStream> rawFontData =
+ SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
+ rawFontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkStream* fontData = handleType1Stream(rawFontData.get(), &header,
+ &data, &trailer);
+ if (fontData == NULL)
+ return false;
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData);
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+ fontStream->insert("Length1", new SkPDFInt(header))->unref();
+ fontStream->insert("Length2", new SkPDFInt(data))->unref();
+ fontStream->insert("Length3", new SkPDFInt(trailer))->unref();
+ fDescriptor->insert("FontFile",
+ new SkPDFObjRef(fontStream.get()))->unref();
+ break;
+ }
+ case SkAdvancedTypefaceMetrics::kTrueType_Font: {
+ SkRefPtr<SkStream> fontData =
+ SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
+ fontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ fontStream->insert("Length1",
+ new SkPDFInt(fontData->getLength()))->unref();
+ fDescriptor->insert("FontFile2",
+ new SkPDFObjRef(fontStream.get()))->unref();
+ break;
+ }
+ case SkAdvancedTypefaceMetrics::kCFF_Font:
+ case SkAdvancedTypefaceMetrics::kType1CID_Font: {
+ SkRefPtr<SkStream> fontData =
+ SkFontHost::OpenStream(SkTypeface::UniqueID(fTypeface.get()));
+ fontData->unref(); // SkRefPtr and OpenStream both took a ref.
+ SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
+ // SkRefPtr and new both ref()'d fontStream, pass one.
+ fResources.push(fontStream.get());
+
+ if (fFontInfo->fType == SkAdvancedTypefaceMetrics::kCFF_Font) {
+ fontStream->insert("Subtype", new SkPDFName("Type1C"))->unref();
+ } else {
+ fontStream->insert("Subtype",
+ new SkPDFName("CIDFontType0c"))->unref();
+ }
+ fDescriptor->insert("FontFile3",
+ new SkPDFObjRef(fontStream.get()))->unref();
+ break;
+ }
+ default:
+ SkASSERT(false);
+ }
+
+ const uint16_t emSize = fFontInfo->fEmSize;
+ fResources.push(fDescriptor.get());
+ fDescriptor->ref();
+ insert("FontDescriptor", new SkPDFObjRef(fDescriptor.get()))->unref();
+
+ fDescriptor->insert("FontName", new SkPDFName(
+ fFontInfo->fFontName))->unref();
+ fDescriptor->insert("Flags", new SkPDFInt(fFontInfo->fStyle))->unref();
+ fDescriptor->insert("Ascent", new SkPDFScalar(
+ scaleFromFontUnits(fFontInfo->fAscent, emSize)))->unref();
+ fDescriptor->insert("Descent", new SkPDFScalar(
+ scaleFromFontUnits(fFontInfo->fDescent, emSize)))->unref();
+ fDescriptor->insert("StemV", new SkPDFScalar(
+ scaleFromFontUnits(fFontInfo->fStemV, emSize)))->unref();
+ fDescriptor->insert("CapHeight", new SkPDFScalar(
+ scaleFromFontUnits(fFontInfo->fCapHeight, emSize)))->unref();
+ fDescriptor->insert("ItalicAngle", new SkPDFInt(
+ fFontInfo->fItalicAngle))->unref();
+ fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox,
+ fFontInfo->fEmSize))->unref();
+
+ if (defaultWidth > 0) {
+ fDescriptor->insert("MissingWidth", new SkPDFScalar(
+ scaleFromFontUnits(defaultWidth, emSize)))->unref();
+ }
+ return true;
+}
+void SkPDFFont::addWidthInfoFromRange(
+ int16_t defaultWidth,
+ const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
+ SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
+ widthArray->unref(); // SkRefPtr and new both took a ref.
+ int firstChar = 0;
+ if (widthRangeEntry) {
+ const uint16_t emSize = fFontInfo->fEmSize;
+ int startIndex = fFirstGlyphID - widthRangeEntry->fStartId;
+ int endIndex = startIndex + fLastGlyphID - fFirstGlyphID + 1;
+ if (startIndex < 0)
+ startIndex = 0;
+ if (endIndex > widthRangeEntry->fAdvance.count())
+ endIndex = widthRangeEntry->fAdvance.count();
+ if (widthRangeEntry->fStartId == 0) {
+ appendWidth(widthRangeEntry->fAdvance[0], emSize, widthArray.get());
+ } else {
+ firstChar = startIndex + widthRangeEntry->fStartId;
+ }
+ for (int i = startIndex; i < endIndex; i++)
+ appendWidth(widthRangeEntry->fAdvance[i], emSize, widthArray.get());
+ } else {
+ appendWidth(defaultWidth, 1000, widthArray.get());
+ }
+ insert("FirstChar", new SkPDFInt(firstChar))->unref();
+ insert("LastChar",
+ new SkPDFInt(firstChar + widthArray->size() - 1))->unref();
+ insert("Widths", widthArray.get());
+}
+
+void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(int16_t glyphID) {
+ // Single byte glyph encoding supports a max of 255 glyphs.
+ fFirstGlyphID = glyphID - (glyphID - 1) % 255;
+ if (fLastGlyphID > fFirstGlyphID + 255 - 1) {
+ fLastGlyphID = fFirstGlyphID + 255 - 1;
+ }
+}
+
+
+bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const {
+ if (fFontID != b.fFontID)
+ return false;
+ if (fFont != NULL && b.fFont != NULL) {
+ return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID &&
+ fFont->fLastGlyphID == b.fFont->fLastGlyphID;
+ }
+ if (fGlyphID == 0 || b.fGlyphID == 0)
+ return true;
+
+ if (fFont != NULL) {
+ return fFont->fFirstGlyphID <= b.fGlyphID &&
+ b.fGlyphID <= fFont->fLastGlyphID;
+ } else if (b.fFont != NULL) {
+ return b.fFont->fFirstGlyphID <= fGlyphID &&
+ fGlyphID <= b.fFont->fLastGlyphID;
+ }
+ return fGlyphID == b.fGlyphID;
+}
+
+SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID)
+ : fFont(font),
+ fFontID(fontID),
+ fGlyphID(glyphID) {
+}
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
new file mode 100644
index 0000000000..40a5564847
--- /dev/null
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFFormXObject.h"
+
+#include "SkMatrix.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFUtils.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) {
+ // We don't want to keep around device because we'd have two copies
+ // of content, so reference or copy everything we need (content and
+ // resources).
+ device->getResources(&fResources);
+
+ SkRefPtr<SkStream> content = device->content();
+ content->unref(); // SkRefPtr and content() both took a reference.
+ fStream = new SkPDFStream(content.get());
+ fStream->unref(); // SkRefPtr and new both took a reference.
+
+ insert("Type", new SkPDFName("XObject"))->unref();
+ insert("Subtype", new SkPDFName("Form"))->unref();
+ insert("BBox", device->getMediaBox().get());
+ insert("Resources", device->getResourceDict().get());
+
+ // We invert the initial transform and apply that to the xobject so that
+ // it doesn't get applied twice. We can't just undo it because it's
+ // embedded in things like shaders and images.
+ if (!device->initialTransform().isIdentity()) {
+ SkMatrix inverse;
+ inverse.reset();
+ device->initialTransform().invert(&inverse);
+ insert("Matrix", SkPDFUtils::MatrixToArray(inverse))->unref();
+ }
+
+ // Right now SkPDFFormXObject is only used for saveLayer, which implies
+ // isolated blending. Do this conditionally if that changes.
+ SkRefPtr<SkPDFDict> group = new SkPDFDict("Group");
+ group->unref(); // SkRefPtr and new both took a reference.
+ group->insert("S", new SkPDFName("Transparency"))->unref();
+ group->insert("I", new SkPDFBool(true))->unref(); // Isolated.
+ insert("Group", group.get());
+}
+
+SkPDFFormXObject::~SkPDFFormXObject() {
+ fResources.unrefAll();
+}
+
+void SkPDFFormXObject::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ fStream->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFFormXObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ return fStream->getOutputSize(catalog, indirect);
+}
+
+void SkPDFFormXObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+ resourceList->setReserve(resourceList->count() + fResources.count());
+ for (int i = 0; i < fResources.count(); i++) {
+ resourceList->push(fResources[i]);
+ fResources[i]->ref();
+ }
+}
+
+SkPDFObject* SkPDFFormXObject::insert(SkPDFName* key, SkPDFObject* value) {
+ return fStream->insert(key, value);
+}
+
+SkPDFObject* SkPDFFormXObject::insert(const char key[], SkPDFObject* value) {
+ return fStream->insert(key, value);
+}
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
new file mode 100644
index 0000000000..b08bf24c26
--- /dev/null
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFFormXObject.h"
+#include "SkPDFGraphicState.h"
+#include "SkPDFUtils.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
+ switch (mode) {
+ case SkXfermode::kSrcOver_Mode: return "Normal";
+ case SkXfermode::kMultiply_Mode: return "Multiply";
+ case SkXfermode::kScreen_Mode: return "Screen";
+ case SkXfermode::kOverlay_Mode: return "Overlay";
+ case SkXfermode::kDarken_Mode: return "Darken";
+ case SkXfermode::kLighten_Mode: return "Lighten";
+ case SkXfermode::kColorDodge_Mode: return "ColorDodge";
+ case SkXfermode::kColorBurn_Mode: return "ColorBurn";
+ case SkXfermode::kHardLight_Mode: return "HardLight";
+ case SkXfermode::kSoftLight_Mode: return "SoftLight";
+ case SkXfermode::kDifference_Mode: return "Difference";
+ case SkXfermode::kExclusion_Mode: return "Exclusion";
+
+ // These are handled in SkPDFDevice::setUpContentEntry.
+ case SkXfermode::kClear_Mode:
+ case SkXfermode::kSrc_Mode:
+ case SkXfermode::kDst_Mode:
+ case SkXfermode::kDstOver_Mode:
+ case SkXfermode::kSrcIn_Mode:
+ case SkXfermode::kDstIn_Mode:
+ case SkXfermode::kSrcOut_Mode:
+ case SkXfermode::kDstOut_Mode:
+ return "Normal";
+
+ // TODO(vandebo) Figure out if we can support more of these modes.
+ case SkXfermode::kSrcATop_Mode:
+ case SkXfermode::kDstATop_Mode:
+ case SkXfermode::kXor_Mode:
+ case SkXfermode::kPlus_Mode:
+ return NULL;
+ }
+ return NULL;
+}
+
+SkPDFGraphicState::~SkPDFGraphicState() {
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+ if (!fSMask) {
+ int index = find(fPaint);
+ SkASSERT(index >= 0);
+ canonicalPaints().removeShuffle(index);
+ }
+ fResources.unrefAll();
+}
+
+void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+ resourceList->setReserve(resourceList->count() + fResources.count());
+ for (int i = 0; i < fResources.count(); i++) {
+ resourceList->push(fResources[i]);
+ fResources[i]->ref();
+ fResources[i]->getResources(resourceList);
+ }
+}
+
+void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ populateDict();
+ SkPDFDict::emitObject(stream, catalog, indirect);
+}
+
+// static
+size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ populateDict();
+ return SkPDFDict::getOutputSize(catalog, indirect);
+}
+
+// static
+SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
+SkPDFGraphicState::canonicalPaints() {
+ // This initialization is only thread safe with gcc.
+ static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
+ return gCanonicalPaints;
+}
+
+// static
+SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
+ // This initialization is only thread safe with gcc.
+ static SkMutex gCanonicalPaintsMutex;
+ return gCanonicalPaintsMutex;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
+ const SkPaint& paint) {
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+ int index = find(paint);
+ if (index >= 0) {
+ canonicalPaints()[index].fGraphicState->ref();
+ return canonicalPaints()[index].fGraphicState;
+ }
+ GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
+ canonicalPaints().push(newEntry);
+ return newEntry.fGraphicState;
+}
+
+// static
+SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
+ // This assumes that canonicalPaintsMutex is held.
+ static SkPDFStream* invertFunction = NULL;
+ if (!invertFunction) {
+ // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
+ // a type 2 function, so we use a type 4 function.
+ SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
+ domainAndRange->unref(); // SkRefPtr and new both took a reference.
+ domainAndRange->reserve(2);
+ domainAndRange->append(new SkPDFInt(0))->unref();
+ domainAndRange->append(new SkPDFInt(1))->unref();
+
+ static const char psInvert[] = "{1 exch sub}";
+ SkRefPtr<SkMemoryStream> psInvertStream =
+ new SkMemoryStream(&psInvert, strlen(psInvert), true);
+ psInvertStream->unref(); // SkRefPtr and new both took a reference.
+
+ invertFunction = new SkPDFStream(psInvertStream.get());
+ invertFunction->insert("FunctionType", new SkPDFInt(4))->unref();
+ invertFunction->insert("Domain", domainAndRange.get());
+ invertFunction->insert("Range", domainAndRange.get());
+ }
+ return invertFunction;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::getSMaskGraphicState(
+ SkPDFFormXObject* sMask, bool invert) {
+ // The practical chances of using the same mask more than once are unlikely
+ // enough that it's not worth canonicalizing.
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+
+ SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
+ sMaskDict->unref(); // SkRefPtr and new both took a reference.
+ sMaskDict->insert("S", new SkPDFName("Alpha"))->unref();
+ sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
+
+ SkPDFGraphicState* result = new SkPDFGraphicState;
+ result->fPopulated = true;
+ result->fSMask = true;
+ result->insert("Type", new SkPDFName("ExtGState"))->unref();
+ result->insert("SMask", sMaskDict.get());
+ result->fResources.push(sMask);
+ sMask->ref();
+
+ if (invert) {
+ SkPDFObject* invertFunction = GetInvertFunction();
+ result->fResources.push(invertFunction);
+ invertFunction->ref();
+ sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
+ }
+
+ return result;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::getNoSMaskGraphicState() {
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+ static SkPDFGraphicState* noSMaskGS = NULL;
+ if (!noSMaskGS) {
+ noSMaskGS = new SkPDFGraphicState;
+ noSMaskGS->fPopulated = true;
+ noSMaskGS->fSMask = true;
+ noSMaskGS->insert("Type", new SkPDFName("ExtGState"))->unref();
+ noSMaskGS->insert("SMask", new SkPDFName("None"))->unref();
+ }
+ noSMaskGS->ref();
+ return noSMaskGS;
+}
+
+// static
+int SkPDFGraphicState::find(const SkPaint& paint) {
+ GSCanonicalEntry search(&paint);
+ return canonicalPaints().find(search);
+}
+
+SkPDFGraphicState::SkPDFGraphicState()
+ : fPopulated(false),
+ fSMask(false) {
+}
+
+SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
+ : fPaint(paint),
+ fPopulated(false),
+ fSMask(false) {
+}
+
+// populateDict and operator== have to stay in sync with each other.
+void SkPDFGraphicState::populateDict() {
+ if (!fPopulated) {
+ fPopulated = true;
+ insert("Type", new SkPDFName("ExtGState"))->unref();
+
+ SkRefPtr<SkPDFScalar> alpha =
+ new SkPDFScalar(fPaint.getAlpha() * SkScalarInvert(0xFF));
+ alpha->unref(); // SkRefPtr and new both took a reference.
+ insert("CA", alpha.get());
+ insert("ca", alpha.get());
+
+ SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
+ SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
+ SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
+ SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
+ SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
+ insert("LC", new SkPDFInt(fPaint.getStrokeCap()))->unref();
+
+ SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
+ SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
+ SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
+ SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
+ SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
+ insert("LJ", new SkPDFInt(fPaint.getStrokeJoin()))->unref();
+
+ insert("LW", new SkPDFScalar(fPaint.getStrokeWidth()))->unref();
+ insert("ML", new SkPDFScalar(fPaint.getStrokeMiter()))->unref();
+ insert("SA", new SkPDFBool(true))->unref(); // Auto stroke adjustment.
+
+ SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+ // If asMode fails, default to kSrcOver_Mode.
+ if (fPaint.getXfermode())
+ fPaint.getXfermode()->asMode(&xfermode);
+ // If we don't support the mode, just use kSrcOver_Mode.
+ if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
+ blend_mode_from_xfermode(xfermode) == NULL) {
+ xfermode = SkXfermode::kSrcOver_Mode;
+ NOT_IMPLEMENTED("unsupported xfermode", false);
+ }
+ insert("BM",
+ new SkPDFName(blend_mode_from_xfermode(xfermode)))->unref();
+ }
+}
+
+// We're only interested in some fields of the SkPaint, so we have a custom
+// operator== function.
+bool SkPDFGraphicState::GSCanonicalEntry::operator==(
+ const SkPDFGraphicState::GSCanonicalEntry& gs) const {
+ const SkPaint* a = fPaint;
+ const SkPaint* b = gs.fPaint;
+ SkASSERT(a != NULL);
+ SkASSERT(b != NULL);
+
+ if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
+ a->getStrokeCap() != b->getStrokeCap() ||
+ a->getStrokeJoin() != b->getStrokeJoin() ||
+ a->getStrokeWidth() != b->getStrokeWidth() ||
+ a->getStrokeMiter() != b->getStrokeMiter()) {
+ return false;
+ }
+
+ SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
+ SkXfermode* aXfermode = a->getXfermode();
+ if (aXfermode) {
+ aXfermode->asMode(&aXfermodeName);
+ }
+ if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
+ blend_mode_from_xfermode(aXfermodeName) == NULL) {
+ aXfermodeName = SkXfermode::kSrcOver_Mode;
+ }
+ const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
+ SkASSERT(aXfermodeString != NULL);
+
+ SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
+ SkXfermode* bXfermode = b->getXfermode();
+ if (bXfermode) {
+ bXfermode->asMode(&bXfermodeName);
+ }
+ if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
+ blend_mode_from_xfermode(bXfermodeName) == NULL) {
+ bXfermodeName = SkXfermode::kSrcOver_Mode;
+ }
+ const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
+ SkASSERT(bXfermodeString != NULL);
+
+ return strcmp(aXfermodeString, bXfermodeString) == 0;
+}
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
new file mode 100644
index 0000000000..be69f7f757
--- /dev/null
+++ b/src/pdf/SkPDFImage.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFImage.h"
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkPaint.h"
+#include "SkPackBits.h"
+#include "SkPDFCatalog.h"
+#include "SkRect.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkUnPreMultiply.h"
+
+namespace {
+
+void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect,
+ SkStream** imageData, SkStream** alphaData) {
+ SkMemoryStream* image = NULL;
+ SkMemoryStream* alpha = NULL;
+ bool hasAlpha = false;
+ bool isTransparent = false;
+
+ bitmap.lockPixels();
+ switch (bitmap.getConfig()) {
+ case SkBitmap::kIndex8_Config: {
+ const int rowBytes = srcRect.width();
+ image = new SkMemoryStream(rowBytes * srcRect.height());
+ uint8_t* dst = (uint8_t*)image->getMemoryBase();
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
+ dst += rowBytes;
+ }
+ break;
+ }
+ case SkBitmap::kRLE_Index8_Config: {
+ const int rowBytes = srcRect.width();
+ image = new SkMemoryStream(rowBytes * srcRect.height());
+ uint8_t* dst = (uint8_t*)image->getMemoryBase();
+ const SkBitmap::RLEPixels* rle =
+ (const SkBitmap::RLEPixels*)bitmap.getPixels();
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ SkPackBits::Unpack8(dst, srcRect.fLeft, rowBytes,
+ rle->packedAtY(y));
+ dst += rowBytes;
+ }
+ break;
+ }
+ case SkBitmap::kARGB_4444_Config: {
+ isTransparent = true;
+ const int rowBytes = (srcRect.width() * 3 + 1) / 2;
+ const int alphaRowBytes = (srcRect.width() + 1) / 2;
+ image = new SkMemoryStream(rowBytes * srcRect.height());
+ alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
+ uint8_t* dst = (uint8_t*)image->getMemoryBase();
+ uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ uint16_t* src = bitmap.getAddr16(0, y);
+ int x;
+ for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
+ dst[0] = (SkGetPackedR4444(src[x]) << 4) |
+ SkGetPackedG4444(src[x]);
+ dst[1] = (SkGetPackedB4444(src[x]) << 4) |
+ SkGetPackedR4444(src[x + 1]);
+ dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
+ SkGetPackedB4444(src[x + 1]);
+ dst += 3;
+ alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) |
+ SkGetPackedA4444(src[x + 1]);
+ if (alphaDst[0] != 0xFF)
+ hasAlpha = true;
+ if (alphaDst[0])
+ isTransparent = false;
+ alphaDst++;
+ }
+ if (srcRect.width() & 1) {
+ dst[0] = (SkGetPackedR4444(src[x]) << 4) |
+ SkGetPackedG4444(src[x]);
+ dst[1] = (SkGetPackedB4444(src[x]) << 4);
+ dst += 2;
+ alphaDst[0] = (SkGetPackedA4444(src[x]) << 4);
+ if (alphaDst[0] != 0xF0)
+ hasAlpha = true;
+ if (alphaDst[0] & 0xF0)
+ isTransparent = false;
+ alphaDst++;
+ }
+ }
+ break;
+ }
+ case SkBitmap::kRGB_565_Config: {
+ const int rowBytes = srcRect.width() * 3;
+ image = new SkMemoryStream(rowBytes * srcRect.height());
+ uint8_t* dst = (uint8_t*)image->getMemoryBase();
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ uint16_t* src = bitmap.getAddr16(0, y);
+ for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+ dst[0] = SkGetPackedR16(src[x]);
+ dst[1] = SkGetPackedG16(src[x]);
+ dst[2] = SkGetPackedB16(src[x]);
+ dst += 3;
+ }
+ }
+ break;
+ }
+ case SkBitmap::kARGB_8888_Config: {
+ isTransparent = true;
+ const int rowBytes = srcRect.width() * 3;
+ image = new SkMemoryStream(rowBytes * srcRect.height());
+ alpha = new SkMemoryStream(srcRect.width() * srcRect.height());
+ uint8_t* dst = (uint8_t*)image->getMemoryBase();
+ uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ uint32_t* src = bitmap.getAddr32(0, y);
+ for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+ dst[0] = SkGetPackedR32(src[x]);
+ dst[1] = SkGetPackedG32(src[x]);
+ dst[2] = SkGetPackedB32(src[x]);
+ dst += 3;
+ alphaDst[0] = SkGetPackedA32(src[x]);
+ if (alphaDst[0] != 0xFF)
+ hasAlpha = true;
+ if (alphaDst[0])
+ isTransparent = false;
+ alphaDst++;
+ }
+ }
+ break;
+ }
+ case SkBitmap::kA1_Config: {
+ isTransparent = true;
+ image = new SkMemoryStream(1);
+ ((uint8_t*)image->getMemoryBase())[0] = 0;
+
+ const int alphaRowBytes = (srcRect.width() + 7) / 8;
+ alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
+ uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+ int offset1 = srcRect.fLeft % 8;
+ int offset2 = 8 - offset1;
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ uint8_t* src = bitmap.getAddr1(0, y);
+ // This may read up to one byte after src, but the potentially
+ // invalid bits are never used for computation.
+ for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) {
+ if (offset1) {
+ alphaDst[0] = src[x / 8] << offset1 |
+ src[x / 8 + 1] >> offset2;
+ } else {
+ alphaDst[0] = src[x / 8];
+ }
+ if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF)
+ hasAlpha = true;
+ if (x + 7 < srcRect.fRight && alphaDst[0])
+ isTransparent = false;
+ alphaDst++;
+ }
+ // Calculate the mask of bits we're interested in within the
+ // last byte of alphaDst.
+ // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
+ uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
+ if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask)
+ hasAlpha = true;
+ if (srcRect.width() % 8 && (alphaDst[-1] & mask))
+ isTransparent = false;
+ }
+ break;
+ }
+ case SkBitmap::kA8_Config: {
+ isTransparent = true;
+ image = new SkMemoryStream(1);
+ ((uint8_t*)image->getMemoryBase())[0] = 0;
+
+ const int alphaRowBytes = srcRect.width();
+ alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
+ uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+ for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+ uint8_t* src = bitmap.getAddr8(0, y);
+ for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+ alphaDst[0] = src[x];
+ if (alphaDst[0] != 0xFF)
+ hasAlpha = true;
+ if (alphaDst[0])
+ isTransparent = false;
+ alphaDst++;
+ }
+ }
+ break;
+ }
+ default:
+ SkASSERT(false);
+ }
+ bitmap.unlockPixels();
+
+ if (isTransparent) {
+ SkSafeUnref(image);
+ } else {
+ *imageData = image;
+ }
+
+ if (isTransparent || !hasAlpha) {
+ SkSafeUnref(alpha);
+ } else {
+ *alphaData = alpha;
+ }
+}
+
+SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
+ SkPDFArray* result = new SkPDFArray();
+ result->reserve(4);
+ result->append(new SkPDFName("Indexed"))->unref();
+ result->append(new SkPDFName("DeviceRGB"))->unref();;
+ result->append(new SkPDFInt(table->count() - 1))->unref();
+
+ // Potentially, this could be represented in fewer bytes with a stream.
+ // Max size as a string is 1.5k.
+ SkString index;
+ for (int i = 0; i < table->count(); i++) {
+ char buf[3];
+ SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
+ buf[0] = SkGetPackedR32(color);
+ buf[1] = SkGetPackedG32(color);
+ buf[2] = SkGetPackedB32(color);
+ index.append(buf, 3);
+ }
+ result->append(new SkPDFString(index))->unref();
+ return result;
+}
+
+}; // namespace
+
+// static
+SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
+ const SkIRect& srcRect,
+ const SkPaint& paint) {
+ if (bitmap.getConfig() == SkBitmap::kNo_Config)
+ return NULL;
+
+ SkStream* imageData = NULL;
+ SkStream* alphaData = NULL;
+ extractImageData(bitmap, srcRect, &imageData, &alphaData);
+ SkAutoUnref unrefImageData(imageData);
+ SkAutoUnref unrefAlphaData(alphaData);
+ if (!imageData) {
+ SkASSERT(!alphaData);
+ return NULL;
+ }
+
+ SkPDFImage* image =
+ new SkPDFImage(imageData, bitmap, srcRect, false, paint);
+
+ if (alphaData != NULL) {
+ image->addSMask(new SkPDFImage(alphaData, bitmap, srcRect, true,
+ paint))->unref();
+ }
+ return image;
+}
+
+SkPDFImage::~SkPDFImage() {
+ fResources.unrefAll();
+}
+
+SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
+ fResources.push(mask);
+ mask->ref();
+ insert("SMask", new SkPDFObjRef(mask))->unref();
+ return mask;
+}
+
+void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ fStream->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ return fStream->getOutputSize(catalog, indirect);
+}
+
+void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+ if (fResources.count()) {
+ resourceList->setReserve(resourceList->count() + fResources.count());
+ for (int i = 0; i < fResources.count(); i++) {
+ resourceList->push(fResources[i]);
+ fResources[i]->ref();
+ fResources[i]->getResources(resourceList);
+ }
+ }
+}
+
+SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
+ const SkIRect& srcRect, bool doingAlpha,
+ const SkPaint& paint) {
+ fStream = new SkPDFStream(imageData);
+ fStream->unref(); // SkRefPtr and new both took a reference.
+
+ SkBitmap::Config config = bitmap.getConfig();
+ bool alphaOnly = (config == SkBitmap::kA1_Config ||
+ config == SkBitmap::kA8_Config);
+
+ insert("Type", new SkPDFName("XObject"))->unref();
+ insert("Subtype", new SkPDFName("Image"))->unref();
+
+ if (!doingAlpha && alphaOnly) {
+ // For alpha only images, we stretch a single pixel of black for
+ // the color/shape part.
+ SkRefPtr<SkPDFInt> one = new SkPDFInt(1);
+ one->unref(); // SkRefPtr and new both took a reference.
+ insert("Width", one.get());
+ insert("Height", one.get());
+ } else {
+ insert("Width", new SkPDFInt(srcRect.width()))->unref();
+ insert("Height", new SkPDFInt(srcRect.height()))->unref();
+ }
+
+ // if (!image mask) {
+ if (doingAlpha || alphaOnly) {
+ insert("ColorSpace", new SkPDFName("DeviceGray"))->unref();
+ } else if (config == SkBitmap::kIndex8_Config ||
+ config == SkBitmap::kRLE_Index8_Config) {
+ insert("ColorSpace",
+ makeIndexedColorSpace(bitmap.getColorTable()))->unref();
+ } else {
+ insert("ColorSpace", new SkPDFName("DeviceRGB"))->unref();
+ }
+ // }
+
+ int bitsPerComp = 8;
+ if (config == SkBitmap::kARGB_4444_Config)
+ bitsPerComp = 4;
+ else if (doingAlpha && config == SkBitmap::kA1_Config)
+ bitsPerComp = 1;
+ insert("BitsPerComponent", new SkPDFInt(bitsPerComp))->unref();
+
+ if (config == SkBitmap::kRGB_565_Config) {
+ SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
+ zeroVal->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFScalar> scale5Val =
+ new SkPDFScalar(8.2258f); // 255/2^5-1
+ scale5Val->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFScalar> scale6Val =
+ new SkPDFScalar(4.0476f); // 255/2^6-1
+ scale6Val->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
+ decodeValue->unref(); // SkRefPtr and new both took a reference.
+ decodeValue->reserve(6);
+ decodeValue->append(zeroVal.get());
+ decodeValue->append(scale5Val.get());
+ decodeValue->append(zeroVal.get());
+ decodeValue->append(scale6Val.get());
+ decodeValue->append(zeroVal.get());
+ decodeValue->append(scale5Val.get());
+ insert("Decode", decodeValue.get());
+ }
+}
+
+SkPDFObject* SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
+ return fStream->insert(key, value);
+}
+
+SkPDFObject* SkPDFImage::insert(const char key[], SkPDFObject* value) {
+ return fStream->insert(key, value);
+}
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
new file mode 100644
index 0000000000..2a8183d425
--- /dev/null
+++ b/src/pdf/SkPDFPage.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFPage.h"
+#include "SkStream.h"
+
+SkPDFPage::SkPDFPage(const SkRefPtr<SkPDFDevice>& content)
+ : SkPDFDict("Page"),
+ fDevice(content) {
+}
+
+SkPDFPage::~SkPDFPage() {}
+
+void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
+ SkTDArray<SkPDFObject*>* resourceObjects) {
+ if (fContentStream.get() == NULL) {
+ insert("Resources", fDevice->getResourceDict().get());
+ insert("MediaBox", fDevice->getMediaBox().get());
+
+ SkRefPtr<SkStream> content = fDevice->content();
+ content->unref(); // SkRefPtr and content() both took a reference.
+ fContentStream = new SkPDFStream(content.get());
+ fContentStream->unref(); // SkRefPtr and new both took a reference.
+ insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
+ }
+ catalog->addObject(fContentStream.get(), firstPage);
+ fDevice->getResources(resourceObjects);
+}
+
+off_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) {
+ SkASSERT(fContentStream.get() != NULL);
+ catalog->setFileOffset(fContentStream.get(), fileOffset);
+ return fContentStream->getOutputSize(catalog, true);
+}
+
+void SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) {
+ SkASSERT(fContentStream.get() != NULL);
+ fContentStream->emitObject(stream, catalog, true);
+}
+
+// static
+void SkPDFPage::generatePageTree(const SkTDArray<SkPDFPage*>& pages,
+ SkPDFCatalog* catalog,
+ SkTDArray<SkPDFDict*>* pageTree,
+ SkPDFDict** rootNode) {
+ // PDF wants a tree describing all the pages in the document. We arbitrary
+ // choose 8 (kNodeSize) as the number of allowed children. The internal
+ // nodes have type "Pages" with an array of children, a parent pointer, and
+ // the number of leaves below the node as "Count." The leaves are passed
+ // into the method, have type "Page" and need a parent pointer. This method
+ // builds the tree bottom up, skipping internal nodes that would have only
+ // one child.
+ static const int kNodeSize = 8;
+
+ SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids");
+ kidsName->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFName> countName = new SkPDFName("Count");
+ countName->unref(); // SkRefPtr and new both took a reference.
+ SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent");
+ parentName->unref(); // SkRefPtr and new both took a reference.
+
+ // curNodes takes a reference to its items, which it passes to pageTree.
+ SkTDArray<SkPDFDict*> curNodes;
+ curNodes.setReserve(pages.count());
+ for (int i = 0; i < pages.count(); i++) {
+ SkSafeRef(pages[i]);
+ curNodes.push(pages[i]);
+ }
+
+ // nextRoundNodes passes its references to nodes on to curNodes.
+ SkTDArray<SkPDFDict*> nextRoundNodes;
+ nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
+
+ int treeCapacity = kNodeSize;
+ do {
+ for (int i = 0; i < curNodes.count(); ) {
+ if (i > 0 && i + 1 == curNodes.count()) {
+ nextRoundNodes.push(curNodes[i]);
+ break;
+ }
+
+ SkPDFDict* newNode = new SkPDFDict("Pages");
+ SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode);
+ newNodeRef->unref(); // SkRefPtr and new both took a reference.
+
+ SkRefPtr<SkPDFArray> kids = new SkPDFArray;
+ kids->unref(); // SkRefPtr and new both took a reference.
+ kids->reserve(kNodeSize);
+
+ int count = 0;
+ for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
+ curNodes[i]->insert(parentName.get(), newNodeRef.get());
+ kids->append(new SkPDFObjRef(curNodes[i]))->unref();
+
+ // TODO(vandebo) put the objects in strict access order.
+ // Probably doesn't matter because they are so small.
+ if (curNodes[i] != pages[0]) {
+ pageTree->push(curNodes[i]); // Transfer reference.
+ catalog->addObject(curNodes[i], false);
+ } else {
+ SkSafeUnref(curNodes[i]);
+ }
+ }
+
+ newNode->insert(kidsName.get(), kids.get());
+ int pageCount = treeCapacity;
+ if (count < kNodeSize) {
+ pageCount = pages.count() % treeCapacity;
+ }
+ newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
+ nextRoundNodes.push(newNode); // Transfer reference.
+ }
+
+ curNodes = nextRoundNodes;
+ nextRoundNodes.rewind();
+ treeCapacity *= kNodeSize;
+ } while(curNodes.count() > 1);
+
+ pageTree->push(curNodes[0]); // Transfer reference.
+ catalog->addObject(curNodes[0], false);
+ if (rootNode)
+ *rootNode = curNodes[0];
+}
+
+const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
+ return fDevice->getFontResources();
+}
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
new file mode 100644
index 0000000000..6845e09833
--- /dev/null
+++ b/src/pdf/SkPDFShader.cpp
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFShader.h"
+
+#include "SkCanvas.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+#include "SkTypes.h"
+
+static void transformBBox(const SkMatrix& matrix, SkRect* bbox) {
+ SkMatrix inverse;
+ inverse.reset();
+ matrix.invert(&inverse);
+ inverse.mapRect(bbox);
+}
+
+static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
+ SkVector vec = pts[1] - pts[0];
+ SkScalar mag = vec.length();
+ SkScalar inv = mag ? SkScalarInvert(mag) : 0;
+
+ vec.scale(inv);
+ matrix->setSinCos(vec.fY, vec.fX);
+ matrix->preTranslate(pts[0].fX, pts[0].fY);
+ matrix->preScale(mag, mag);
+}
+
+/* Assumes t + startOffset is on the stack and does a linear interpolation on t
+ between startOffset and endOffset from prevColor to curColor (for each color
+ component), leaving the result in component order on the stack.
+ @param range endOffset - startOffset
+ @param curColor[components] The current color components.
+ @param prevColor[components] The previous color components.
+ @param result The result ps function.
+ */
+static void interpolateColorCode(SkScalar range, SkScalar* curColor,
+ SkScalar* prevColor, int components,
+ SkString* result) {
+ // Figure out how to scale each color component.
+ SkAutoSTMalloc<4, SkScalar> multiplierAlloc(components);
+ SkScalar *multiplier = multiplierAlloc.get();
+ for (int i = 0; i < components; i++) {
+ multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range);
+ }
+
+ // Calculate when we no longer need to keep a copy of the input parameter t.
+ // If the last component to use t is i, then dupInput[0..i - 1] = true
+ // and dupInput[i .. components] = false.
+ SkAutoSTMalloc<4, bool> dupInputAlloc(components);
+ bool *dupInput = dupInputAlloc.get();
+ dupInput[components - 1] = false;
+ for (int i = components - 2; i >= 0; i--) {
+ dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
+ }
+
+ if (!dupInput[0] && multiplier[0] == 0) {
+ result->append("pop ");
+ }
+
+ for (int i = 0; i < components; i++) {
+ // If the next components needs t, make a copy.
+ if (dupInput[i]) {
+ result->append("dup ");
+ }
+
+ if (multiplier[i] == 0) {
+ result->appendScalar(prevColor[i]);
+ result->append(" ");
+ } else {
+ if (multiplier[i] != 1) {
+ result->appendScalar(multiplier[i]);
+ result->append(" mul ");
+ }
+ if (prevColor[i] != 0) {
+ result->appendScalar(prevColor[i]);
+ result->append(" add ");
+ }
+ }
+
+ if (dupInput[i]) {
+ result->append("exch\n");
+ }
+ }
+}
+
+/* Generate Type 4 function code to map t=[0,1) to the passed gradient,
+ clamping at the edges of the range. The generated code will be of the form:
+ if (t < 0) {
+ return colorData[0][r,g,b];
+ } else {
+ if (t < info.fColorOffsets[1]) {
+ return linearinterpolation(colorData[0][r,g,b],
+ colorData[1][r,g,b]);
+ } else {
+ if (t < info.fColorOffsets[2]) {
+ return linearinterpolation(colorData[1][r,g,b],
+ colorData[2][r,g,b]);
+ } else {
+
+ ... } else {
+ return colorData[info.fColorCount - 1][r,g,b];
+ }
+ ...
+ }
+ }
+ */
+static void gradientFunctionCode(const SkShader::GradientInfo& info,
+ SkString* result) {
+ /* We want to linearly interpolate from the previous color to the next.
+ Scale the colors from 0..255 to 0..1 and determine the multipliers
+ for interpolation.
+ C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
+ */
+ static const int kColorComponents = 3;
+ typedef SkScalar ColorTuple[kColorComponents];
+ SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
+ ColorTuple *colorData = colorDataAlloc.get();
+ const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
+ for (int i = 0; i < info.fColorCount; i++) {
+ colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
+ colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
+ colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
+ }
+
+ // Clamp the initial color.
+ result->append("dup 0 le {pop ");
+ result->appendScalar(colorData[0][0]);
+ result->append(" ");
+ result->appendScalar(colorData[0][1]);
+ result->append(" ");
+ result->appendScalar(colorData[0][2]);
+ result->append(" }\n");
+
+ // The gradient colors.
+ for (int i = 1 ; i < info.fColorCount; i++) {
+ result->append("{dup ");
+ result->appendScalar(info.fColorOffsets[i]);
+ result->append(" le {");
+ if (info.fColorOffsets[i - 1] != 0) {
+ result->appendScalar(info.fColorOffsets[i - 1]);
+ result->append(" sub\n");
+ }
+
+ interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
+ colorData[i], colorData[i - 1], kColorComponents,
+ result);
+ result->append("}\n");
+ }
+
+ // Clamp the final color.
+ result->append("{pop ");
+ result->appendScalar(colorData[info.fColorCount - 1][0]);
+ result->append(" ");
+ result->appendScalar(colorData[info.fColorCount - 1][1]);
+ result->append(" ");
+ result->appendScalar(colorData[info.fColorCount - 1][2]);
+
+ for (int i = 0 ; i < info.fColorCount; i++) {
+ result->append("} ifelse\n");
+ }
+}
+
+/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
+static void tileModeCode(SkShader::TileMode mode, SkString* result) {
+ if (mode == SkShader::kRepeat_TileMode) {
+ result->append("dup truncate sub\n"); // Get the fractional part.
+ result->append("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1)
+ return;
+ }
+
+ if (mode == SkShader::kMirror_TileMode) {
+ // Map t mod 2 into [0, 1, 1, 0].
+ // Code Stack
+ result->append("abs " // Map negative to positive.
+ "dup " // t.s t.s
+ "truncate " // t.s t
+ "dup " // t.s t t
+ "cvi " // t.s t T
+ "2 mod " // t.s t (i mod 2)
+ "1 eq " // t.s t true|false
+ "3 1 roll " // true|false t.s t
+ "sub " // true|false 0.s
+ "exch " // 0.s true|false
+ "{1 exch sub} if\n"); // 1 - 0.s|0.s
+ }
+}
+
+static SkString linearCode(const SkShader::GradientInfo& info) {
+ SkString function("{pop\n"); // Just ditch the y value.
+ tileModeCode(info.fTileMode, &function);
+ gradientFunctionCode(info, &function);
+ function.append("}");
+ return function;
+}
+
+static SkString radialCode(const SkShader::GradientInfo& info) {
+ SkString function("{");
+ // Find the distance from the origin.
+ function.append("dup " // x y y
+ "mul " // x y^2
+ "exch " // y^2 x
+ "dup " // y^2 x x
+ "mul " // y^2 x^2
+ "add " // y^2+x^2
+ "sqrt\n"); // sqrt(y^2+x^2)
+
+ tileModeCode(info.fTileMode, &function);
+ gradientFunctionCode(info, &function);
+ function.append("}");
+ return function;
+}
+
+/* The math here is all based on the description in Two_Point_Radial_Gradient,
+ with one simplification, the coordinate space has been scaled so that
+ Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2.
+ */
+static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
+ SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
+ SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
+ SkScalar sr = info.fRadius[0];
+ SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1;
+ bool posRoot = info.fRadius[1] > info.fRadius[0];
+
+ // We start with a stack of (x y), copy it and then consume one copy in
+ // order to calculate b and the other to calculate c.
+ SkString function("{");
+ function.append("2 copy ");
+
+ // Calculate -b and b^2.
+ function.appendScalar(dy);
+ function.append(" mul exch ");
+ function.appendScalar(dx);
+ function.append(" mul add ");
+ function.appendScalar(sr);
+ function.append(" sub 2 mul neg dup dup mul\n");
+
+ // Calculate c
+ function.append("4 2 roll dup mul exch dup mul add ");
+ function.appendScalar(SkScalarMul(sr, sr));
+ function.append(" sub\n");
+
+ // Calculate the determinate
+ function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
+ function.append(" mul sub abs sqrt\n");
+
+ // And then the final value of t.
+ if (posRoot) {
+ function.append("sub ");
+ } else {
+ function.append("add ");
+ }
+ function.appendScalar(SkScalarMul(SkIntToScalar(2), a));
+ function.append(" div\n");
+
+ tileModeCode(info.fTileMode, &function);
+ gradientFunctionCode(info, &function);
+ function.append("}");
+ return function;
+}
+
+static SkString sweepCode(const SkShader::GradientInfo& info) {
+ SkString function("{exch atan 360 div\n");
+ tileModeCode(info.fTileMode, &function);
+ gradientFunctionCode(info, &function);
+ function.append("}");
+ return function;
+}
+
+SkPDFShader::~SkPDFShader() {
+ SkAutoMutexAcquire lock(canonicalShadersMutex());
+ ShaderCanonicalEntry entry(this, fState.get());
+ int index = canonicalShaders().find(entry);
+ SkASSERT(index >= 0);
+ canonicalShaders().removeShuffle(index);
+ fResources.unrefAll();
+}
+
+void SkPDFShader::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ fContent->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFShader::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ return fContent->getOutputSize(catalog, indirect);
+}
+
+void SkPDFShader::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+ resourceList->setReserve(resourceList->count() + fResources.count());
+ for (int i = 0; i < fResources.count(); i++) {
+ resourceList->push(fResources[i]);
+ fResources[i]->ref();
+ }
+}
+
+// static
+SkPDFShader* SkPDFShader::getPDFShader(const SkShader& shader,
+ const SkMatrix& matrix,
+ const SkIRect& surfaceBBox) {
+ SkRefPtr<SkPDFShader> pdfShader;
+ SkAutoMutexAcquire lock(canonicalShadersMutex());
+ SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox));
+
+ ShaderCanonicalEntry entry(NULL, shaderState.get());
+ int index = canonicalShaders().find(entry);
+ if (index >= 0) {
+ SkPDFShader* result = canonicalShaders()[index].fPDFShader;
+ result->ref();
+ return result;
+ }
+ // The PDFShader takes ownership of the shaderSate.
+ pdfShader = new SkPDFShader(shaderState.detach());
+ // Check for a valid shader.
+ if (pdfShader->fContent.get() == NULL) {
+ pdfShader->unref();
+ return NULL;
+ }
+ entry.fPDFShader = pdfShader.get();
+ canonicalShaders().push(entry);
+ return pdfShader.get(); // return the reference that came from new.
+}
+
+// static
+SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::canonicalShaders() {
+ // This initialization is only thread safe with gcc.
+ static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders;
+ return gCanonicalShaders;
+}
+
+// static
+SkMutex& SkPDFShader::canonicalShadersMutex() {
+ // This initialization is only thread safe with gcc.
+ static SkMutex gCanonicalShadersMutex;
+ return gCanonicalShadersMutex;
+}
+
+// static
+SkPDFObject* SkPDFShader::rangeObject() {
+ // This initialization is only thread safe with gcc.
+ static SkPDFArray* range = NULL;
+ // This method is only used with canonicalShadersMutex, so it's safe to
+ // populate domain.
+ if (range == NULL) {
+ range = new SkPDFArray;
+ range->reserve(6);
+ range->append(new SkPDFInt(0))->unref();
+ range->append(new SkPDFInt(1))->unref();
+ range->append(new SkPDFInt(0))->unref();
+ range->append(new SkPDFInt(1))->unref();
+ range->append(new SkPDFInt(0))->unref();
+ range->append(new SkPDFInt(1))->unref();
+ }
+ return range;
+}
+
+SkPDFShader::SkPDFShader(State* state) : fState(state) {
+ if (fState.get()->fType == SkShader::kNone_GradientType) {
+ doImageShader();
+ } else {
+ doFunctionShader();
+ }
+}
+
+void SkPDFShader::doFunctionShader() {
+ SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL;
+ SkPoint transformPoints[2];
+
+ // Depending on the type of the gradient, we want to transform the
+ // coordinate space in different ways.
+ const SkShader::GradientInfo* info = &fState.get()->fInfo;
+ transformPoints[0] = info->fPoint[0];
+ transformPoints[1] = info->fPoint[1];
+ switch (fState.get()->fType) {
+ case SkShader::kLinear_GradientType:
+ codeFunction = &linearCode;
+ break;
+ case SkShader::kRadial_GradientType:
+ transformPoints[1] = transformPoints[0];
+ transformPoints[1].fX += info->fRadius[0];
+ codeFunction = &radialCode;
+ break;
+ case SkShader::kRadial2_GradientType: {
+ // Bail out if the radii are the same. Not setting fContent will
+ // cause the higher level code to detect the resulting object
+ // as invalid.
+ if (info->fRadius[0] == info->fRadius[1]) {
+ return;
+ }
+ transformPoints[1] = transformPoints[0];
+ SkScalar dr = info->fRadius[1] - info->fRadius[0];
+ transformPoints[1].fX += dr;
+ codeFunction = &twoPointRadialCode;
+ break;
+ }
+ case SkShader::kSweep_GradientType:
+ transformPoints[1] = transformPoints[0];
+ transformPoints[1].fX += 1;
+ codeFunction = &sweepCode;
+ break;
+ case SkShader::kColor_GradientType:
+ case SkShader::kNone_GradientType:
+ SkASSERT(false);
+ return;
+ }
+
+ // Move any scaling (assuming a unit gradient) or translation
+ // (and rotation for linear gradient), of the final gradient from
+ // info->fPoints to the matrix (updating bbox appropriately). Now
+ // the gradient can be drawn on on the unit segment.
+ SkMatrix mapperMatrix;
+ unitToPointsMatrix(transformPoints, &mapperMatrix);
+ SkMatrix finalMatrix = fState.get()->fCanvasTransform;
+ finalMatrix.preConcat(mapperMatrix);
+ finalMatrix.preConcat(fState.get()->fShaderTransform);
+ SkRect bbox;
+ bbox.set(fState.get()->fBBox);
+ transformBBox(finalMatrix, &bbox);
+
+ SkRefPtr<SkPDFArray> domain = new SkPDFArray;
+ domain->unref(); // SkRefPtr and new both took a reference.
+ domain->reserve(4);
+ domain->append(new SkPDFScalar(bbox.fLeft))->unref();
+ domain->append(new SkPDFScalar(bbox.fRight))->unref();
+ domain->append(new SkPDFScalar(bbox.fTop))->unref();
+ domain->append(new SkPDFScalar(bbox.fBottom))->unref();
+
+ SkString functionCode;
+ // The two point radial gradient further references fState.get()->fInfo
+ // in translating from x, y coordinates to the t parameter. So, we have
+ // to transform the points and radii according to the calculated matrix.
+ if (fState.get()->fType == SkShader::kRadial2_GradientType) {
+ SkShader::GradientInfo twoPointRadialInfo = *info;
+ SkMatrix inverseMapperMatrix;
+ mapperMatrix.invert(&inverseMapperMatrix);
+ inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
+ twoPointRadialInfo.fRadius[0] =
+ inverseMapperMatrix.mapRadius(info->fRadius[0]);
+ twoPointRadialInfo.fRadius[1] =
+ inverseMapperMatrix.mapRadius(info->fRadius[1]);
+ functionCode = codeFunction(twoPointRadialInfo);
+ } else {
+ functionCode = codeFunction(*info);
+ }
+
+ SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get());
+ // Pass one reference to fResources, SkRefPtr and new both took a reference.
+ fResources.push(function.get());
+
+ SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict;
+ pdfShader->unref(); // SkRefPtr and new both took a reference.
+ pdfShader->insert("ShadingType", new SkPDFInt(1))->unref();
+ pdfShader->insert("ColorSpace", new SkPDFName("DeviceRGB"))->unref();
+ pdfShader->insert("Domain", domain.get());
+ pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref();
+
+ fContent = new SkPDFDict("Pattern");
+ fContent->unref(); // SkRefPtr and new both took a reference.
+ fContent->insert("PatternType", new SkPDFInt(2))->unref();
+ fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
+ fContent->insert("Shading", pdfShader.get());
+}
+
+// SkShader* shader, SkMatrix matrix, const SkRect& surfaceBBox
+void SkPDFShader::doImageShader() {
+ fState.get()->fImage.lockPixels();
+
+ SkMatrix finalMatrix = fState.get()->fCanvasTransform;
+ finalMatrix.preConcat(fState.get()->fShaderTransform);
+ SkRect surfaceBBox;
+ surfaceBBox.set(fState.get()->fBBox);
+ transformBBox(finalMatrix, &surfaceBBox);
+
+ SkMatrix unflip;
+ unflip.setTranslate(0, SkScalarRound(surfaceBBox.height()));
+ unflip.preScale(1, -1);
+ SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
+ SkScalarRound(surfaceBBox.height()));
+ SkPDFDevice pattern(size, size, unflip);
+ SkCanvas canvas(&pattern);
+ canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop);
+ finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);
+
+ const SkBitmap* image = &fState.get()->fImage;
+ int width = image->width();
+ int height = image->height();
+ SkShader::TileMode tileModes[2];
+ tileModes[0] = fState.get()->fImageTileModes[0];
+ tileModes[1] = fState.get()->fImageTileModes[1];
+
+ canvas.drawBitmap(*image, 0, 0);
+ SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop,
+ width, height);
+
+ // Tiling is implied. First we handle mirroring.
+ if (tileModes[0] == SkShader::kMirror_TileMode) {
+ SkMatrix xMirror;
+ xMirror.setScale(-1, 1);
+ xMirror.postTranslate(2 * width, 0);
+ canvas.drawBitmapMatrix(*image, xMirror);
+ patternBBox.fRight += width;
+ }
+ if (tileModes[1] == SkShader::kMirror_TileMode) {
+ SkMatrix yMirror;
+ yMirror.setScale(1, -1);
+ yMirror.postTranslate(0, 2 * height);
+ canvas.drawBitmapMatrix(*image, yMirror);
+ patternBBox.fBottom += height;
+ }
+ if (tileModes[0] == SkShader::kMirror_TileMode &&
+ tileModes[1] == SkShader::kMirror_TileMode) {
+ SkMatrix mirror;
+ mirror.setScale(-1, -1);
+ mirror.postTranslate(2 * width, 2 * height);
+ canvas.drawBitmapMatrix(*image, mirror);
+ }
+
+ // Then handle Clamping, which requires expanding the pattern canvas to
+ // cover the entire surfaceBBox.
+
+ // If both x and y are in clamp mode, we start by filling in the corners.
+ // (Which are just a rectangles of the corner colors.)
+ if (tileModes[0] == SkShader::kClamp_TileMode &&
+ tileModes[1] == SkShader::kClamp_TileMode) {
+ SkPaint paint;
+ SkRect rect;
+ rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0);
+ if (!rect.isEmpty()) {
+ paint.setColor(image->getColor(0, 0));
+ canvas.drawRect(rect, paint);
+ }
+
+ rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
+ if (!rect.isEmpty()) {
+ paint.setColor(image->getColor(width - 1, 0));
+ canvas.drawRect(rect, paint);
+ }
+
+ rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
+ surfaceBBox.fBottom);
+ if (!rect.isEmpty()) {
+ paint.setColor(image->getColor(width - 1, height - 1));
+ canvas.drawRect(rect, paint);
+ }
+
+ rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
+ surfaceBBox.fBottom);
+ if (!rect.isEmpty()) {
+ paint.setColor(image->getColor(0, height - 1));
+ canvas.drawRect(rect, paint);
+ }
+ }
+
+ // Then expand the left, right, top, then bottom.
+ if (tileModes[0] == SkShader::kClamp_TileMode) {
+ SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height);
+ if (surfaceBBox.fLeft < 0) {
+ SkBitmap left;
+ SkAssertResult(image->extractSubset(&left, subset));
+
+ SkMatrix leftMatrix;
+ leftMatrix.setScale(-surfaceBBox.fLeft, 1);
+ leftMatrix.postTranslate(surfaceBBox.fLeft, 0);
+ canvas.drawBitmapMatrix(left, leftMatrix);
+
+ if (tileModes[1] == SkShader::kMirror_TileMode) {
+ leftMatrix.postScale(1, -1);
+ leftMatrix.postTranslate(0, 2 * height);
+ canvas.drawBitmapMatrix(left, leftMatrix);
+ }
+ patternBBox.fLeft = 0;
+ }
+
+ if (surfaceBBox.fRight > width) {
+ SkBitmap right;
+ subset.offset(width - 1, 0);
+ SkAssertResult(image->extractSubset(&right, subset));
+
+ SkMatrix rightMatrix;
+ rightMatrix.setScale(surfaceBBox.fRight - width, 1);
+ rightMatrix.postTranslate(width, 0);
+ canvas.drawBitmapMatrix(right, rightMatrix);
+
+ if (tileModes[1] == SkShader::kMirror_TileMode) {
+ rightMatrix.postScale(1, -1);
+ rightMatrix.postTranslate(0, 2 * height);
+ canvas.drawBitmapMatrix(right, rightMatrix);
+ }
+ patternBBox.fRight = surfaceBBox.width();
+ }
+ }
+
+ if (tileModes[1] == SkShader::kClamp_TileMode) {
+ SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1);
+ if (surfaceBBox.fTop < 0) {
+ SkBitmap top;
+ SkAssertResult(image->extractSubset(&top, subset));
+
+ SkMatrix topMatrix;
+ topMatrix.setScale(1, -surfaceBBox.fTop);
+ topMatrix.postTranslate(0, surfaceBBox.fTop);
+ canvas.drawBitmapMatrix(top, topMatrix);
+
+ if (tileModes[0] == SkShader::kMirror_TileMode) {
+ topMatrix.postScale(-1, 1);
+ topMatrix.postTranslate(2 * width, 0);
+ canvas.drawBitmapMatrix(top, topMatrix);
+ }
+ patternBBox.fTop = 0;
+ }
+
+ if (surfaceBBox.fBottom > height) {
+ SkBitmap bottom;
+ subset.offset(0, height - 1);
+ SkAssertResult(image->extractSubset(&bottom, subset));
+
+ SkMatrix bottomMatrix;
+ bottomMatrix.setScale(1, surfaceBBox.fBottom - height);
+ bottomMatrix.postTranslate(0, height);
+ canvas.drawBitmapMatrix(bottom, bottomMatrix);
+
+ if (tileModes[0] == SkShader::kMirror_TileMode) {
+ bottomMatrix.postScale(-1, 1);
+ bottomMatrix.postTranslate(2 * width, 0);
+ canvas.drawBitmapMatrix(bottom, bottomMatrix);
+ }
+ patternBBox.fBottom = surfaceBBox.height();
+ }
+ }
+
+ SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
+ patternBBoxArray->unref(); // SkRefPtr and new both took a reference.
+ patternBBoxArray->reserve(4);
+ patternBBoxArray->append(new SkPDFScalar(patternBBox.fLeft))->unref();
+ patternBBoxArray->append(new SkPDFScalar(patternBBox.fTop))->unref();
+ patternBBoxArray->append(new SkPDFScalar(patternBBox.fRight))->unref();
+ patternBBoxArray->append(new SkPDFScalar(patternBBox.fBottom))->unref();
+
+ // Put the canvas into the pattern stream (fContent).
+ SkRefPtr<SkStream> content = pattern.content();
+ content->unref(); // SkRefPtr and content() both took a reference.
+ pattern.getResources(&fResources);
+
+ fContent = new SkPDFStream(content.get());
+ fContent->unref(); // SkRefPtr and new both took a reference.
+ fContent->insert("Type", new SkPDFName("Pattern"))->unref();
+ fContent->insert("PatternType", new SkPDFInt(1))->unref();
+ fContent->insert("PaintType", new SkPDFInt(1))->unref();
+ fContent->insert("TilingType", new SkPDFInt(1))->unref();
+ fContent->insert("BBox", patternBBoxArray.get());
+ fContent->insert("XStep", new SkPDFScalar(patternBBox.width()))->unref();
+ fContent->insert("YStep", new SkPDFScalar(patternBBox.height()))->unref();
+ fContent->insert("Resources", pattern.getResourceDict().get());
+ fContent->insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
+
+ fState.get()->fImage.unlockPixels();
+}
+
+SkPDFStream* SkPDFShader::makePSFunction(const SkString& psCode,
+ SkPDFArray* domain) {
+ SkRefPtr<SkMemoryStream> funcStream =
+ new SkMemoryStream(psCode.c_str(), psCode.size(), true);
+ funcStream->unref(); // SkRefPtr and new both took a reference.
+
+ SkPDFStream* result = new SkPDFStream(funcStream.get());
+ result->insert("FunctionType", new SkPDFInt(4))->unref();
+ result->insert("Domain", domain);
+ result->insert("Range", rangeObject());
+ return result;
+}
+
+bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
+ if (fType != b.fType ||
+ fCanvasTransform != b.fCanvasTransform ||
+ fShaderTransform != b.fShaderTransform ||
+ fBBox != b.fBBox) {
+ return false;
+ }
+
+ if (fType == SkShader::kNone_GradientType) {
+ if (fPixelGeneration != b.fPixelGeneration ||
+ fPixelGeneration == 0 ||
+ fImageTileModes[0] != b.fImageTileModes[0] ||
+ fImageTileModes[1] != b.fImageTileModes[1]) {
+ return false;
+ }
+ } else {
+ if (fInfo.fColorCount != b.fInfo.fColorCount ||
+ memcmp(fInfo.fColors, b.fInfo.fColors,
+ sizeof(SkColor) * fInfo.fColorCount) != 0 ||
+ memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
+ sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
+ fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
+ fInfo.fTileMode != b.fInfo.fTileMode) {
+ return false;
+ }
+
+ switch (fType) {
+ case SkShader::kLinear_GradientType:
+ if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
+ return false;
+ }
+ break;
+ case SkShader::kRadial_GradientType:
+ if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
+ return false;
+ }
+ break;
+ case SkShader::kRadial2_GradientType:
+ if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
+ fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
+ fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
+ return false;
+ }
+ break;
+ case SkShader::kSweep_GradientType:
+ case SkShader::kNone_GradientType:
+ case SkShader::kColor_GradientType:
+ break;
+ }
+ }
+ return true;
+}
+
+SkPDFShader::State::State(const SkShader& shader,
+ const SkMatrix& canvasTransform, const SkIRect& bbox)
+ : fCanvasTransform(canvasTransform),
+ fBBox(bbox) {
+
+ fInfo.fColorCount = 0;
+ fInfo.fColors = NULL;
+ fInfo.fColorOffsets = NULL;
+ shader.getLocalMatrix(&fShaderTransform);
+
+ fType = shader.asAGradient(&fInfo);
+
+ if (fType == SkShader::kNone_GradientType) {
+ SkShader::BitmapType bitmapType;
+ SkMatrix matrix;
+ bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes, NULL);
+ if (bitmapType != SkShader::kDefault_BitmapType) {
+ fImage.reset();
+ return;
+ }
+ SkASSERT(matrix.isIdentity());
+ fPixelGeneration = fImage.getGenerationID();
+ } else {
+ fColorData.set(sk_malloc_throw(
+ fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
+ fInfo.fColors = (SkColor*)fColorData.get();
+ fInfo.fColorOffsets = (SkScalar*)(fInfo.fColors + fInfo.fColorCount);
+ shader.asAGradient(&fInfo);
+ }
+}
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
new file mode 100644
index 0000000000..b1bd5ff05d
--- /dev/null
+++ b/src/pdf/SkPDFStream.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkFlate.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFStream.h"
+#include "SkStream.h"
+
+SkPDFStream::SkPDFStream(SkStream* stream) {
+ if (SkFlate::HaveFlate())
+ SkAssertResult(SkFlate::Deflate(stream, &fCompressedData));
+
+ if (SkFlate::HaveFlate() &&
+ fCompressedData.getOffset() < stream->getLength()) {
+ fLength = fCompressedData.getOffset();
+ insert("Filter", new SkPDFName("FlateDecode"))->unref();
+ } else {
+ fCompressedData.reset();
+ fPlainData = stream;
+ fLength = fPlainData->getLength();
+ }
+ insert("Length", new SkPDFInt(fLength))->unref();
+}
+
+SkPDFStream::~SkPDFStream() {
+}
+
+void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ this->INHERITED::emitObject(stream, catalog, false);
+ stream->writeText(" stream\n");
+ if (fPlainData.get())
+ stream->write(fPlainData->getMemoryBase(), fLength);
+ else
+ stream->write(fCompressedData.getStream(), fLength);
+ stream->writeText("\nendstream");
+}
+
+size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ return this->INHERITED::getOutputSize(catalog, false) +
+ strlen(" stream\n\nendstream") + fLength;
+}
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
new file mode 100644
index 0000000000..b9420eba83
--- /dev/null
+++ b/src/pdf/SkPDFTypes.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+
+#ifdef SK_BUILD_FOR_WIN
+ #define SNPRINTF _snprintf
+#else
+ #define SNPRINTF snprintf
+#endif
+
+SkPDFObject::SkPDFObject() {}
+SkPDFObject::~SkPDFObject() {}
+
+size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ SkDynamicMemoryWStream buffer;
+ emitObject(&buffer, catalog, indirect);
+ return buffer.getOffset();
+}
+
+void SkPDFObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {}
+
+void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) {
+ catalog->emitObjectNumber(stream, this);
+ stream->writeText(" obj\n");
+ emitObject(stream, catalog, false);
+ stream->writeText("\nendobj\n");
+}
+
+SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
+SkPDFObjRef::~SkPDFObjRef() {}
+
+size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
+ return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
+ this->getOutputSize(catalog, false) + strlen("\nendobj\n");
+}
+
+void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ SkASSERT(!indirect);
+ catalog->emitObjectNumber(stream, fObj.get());
+ stream->writeText(" R");
+}
+
+size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ SkASSERT(!indirect);
+ return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
+}
+
+SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
+SkPDFInt::~SkPDFInt() {}
+
+void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+ stream->writeDecAsText(fValue);
+}
+
+SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
+SkPDFBool::~SkPDFBool() {}
+
+void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ SkASSERT(!indirect);
+ if (fValue) {
+ stream->writeText("true");
+ } else {
+ stream->writeText("false");
+ }
+}
+
+size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ SkASSERT(!indirect);
+ if (fValue)
+ return strlen("true");
+ return strlen("false");
+}
+
+SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
+SkPDFScalar::~SkPDFScalar() {}
+
+void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ Append(fValue, stream);
+}
+
+// static
+void SkPDFScalar::Append(SkScalar value, SkWStream* stream) {
+ // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
+ // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
+ // When using floats that are outside the whole value range, we can use
+ // integers instead.
+
+
+#if defined(SK_SCALAR_IS_FIXED)
+ stream->writeScalarAsText(value);
+ return;
+#endif // SK_SCALAR_IS_FIXED
+
+#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+ if (value > 32767 || value < -32767) {
+ stream->writeDecAsText(SkScalarRound(value));
+ return;
+ }
+
+ char buffer[SkStrAppendScalar_MaxSize];
+ char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
+ stream->write(buffer, end - buffer);
+ return;
+#endif // !SK_ALLOW_LARGE_PDF_SCALARS
+
+#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
+ // Floats have 24bits of significance, so anything outside that range is
+ // no more precise than an int. (Plus PDF doesn't support scientific
+ // notation, so this clamps to SK_Max/MinS32).
+ if (value > (1 << 24) || value < -(1 << 24)) {
+ stream->writeDecAsText(value);
+ return;
+ }
+ // Continue to enforce the PDF limits for small floats.
+ if (value < 1.0f/65536 && value > -1.0f/65536) {
+ stream->writeDecAsText(0);
+ return;
+ }
+ // SkStrAppendFloat might still use scientific notation, so use snprintf
+ // directly..
+ static const int kFloat_MaxSize = 19;
+ char buffer[kFloat_MaxSize];
+ int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
+ // %f always prints trailing 0s, so strip them.
+ for (; buffer[len - 1] == '0' && len > 0; len--) {
+ buffer[len - 1] = '\0';
+ }
+ if (buffer[len - 1] == '.') {
+ buffer[len - 1] = '\0';
+ }
+ stream->writeText(buffer);
+ return;
+#endif // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS
+}
+
+SkPDFString::SkPDFString(const char value[])
+ : fValue(formatString(value, strlen(value))) {
+}
+
+SkPDFString::SkPDFString(const SkString& value)
+ : fValue(formatString(value.c_str(), value.size())) {
+}
+
+SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
+ : fValue(formatString(value, len, wideChars)) {
+}
+
+SkPDFString::~SkPDFString() {}
+
+void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+ stream->write(fValue.c_str(), fValue.size());
+}
+
+size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+ return fValue.size();
+}
+
+// static
+SkString SkPDFString::formatString(const char* input, size_t len) {
+ return doFormatString(input, len, false, false);
+}
+
+SkString SkPDFString::formatString(const uint16_t* input, size_t len,
+ bool wideChars) {
+ return doFormatString(input, len, true, wideChars);
+}
+
+// static
+SkString SkPDFString::doFormatString(const void* input, size_t len,
+ bool wideInput, bool wideOutput) {
+ SkASSERT(len <= kMaxLen);
+ const uint16_t* win = (const uint16_t*) input;
+ const char* cin = (const char*) input;
+
+ if (wideOutput) {
+ SkASSERT(wideInput);
+ SkString result;
+ result.append("<");
+ for (size_t i = 0; i < len; i++)
+ result.appendHex(win[i], 4);
+ result.append(">");
+ return result;
+ }
+
+ // 7-bit clean is a heuristic to decide what string format to use;
+ // a 7-bit clean string should require little escaping.
+ bool sevenBitClean = true;
+ for (size_t i = 0; i < len; i++) {
+ SkASSERT(!wideInput || !(win[i] & ~0xFF));
+ char val = wideInput ? win[i] : cin[i];
+ if (val > '~' || val < ' ') {
+ sevenBitClean = false;
+ break;
+ }
+ }
+
+ SkString result;
+ if (sevenBitClean) {
+ result.append("(");
+ for (size_t i = 0; i < len; i++) {
+ SkASSERT(!wideInput || !(win[i] & ~0xFF));
+ char val = wideInput ? win[i] : cin[i];
+ if (val == '\\' || val == '(' || val == ')')
+ result.append("\\");
+ result.append(&val, 1);
+ }
+ result.append(")");
+ } else {
+ result.append("<");
+ for (size_t i = 0; i < len; i++) {
+ SkASSERT(!wideInput || !(win[i] & ~0xFF));
+ unsigned char val = wideInput ? win[i] : cin[i];
+ result.appendHex(val, 2);
+ }
+ result.append(">");
+ }
+
+ return result;
+}
+
+SkPDFName::SkPDFName(const char name[]) : fValue(formatName(SkString(name))) {}
+SkPDFName::SkPDFName(const SkString& name) : fValue(formatName(name)) {}
+SkPDFName::~SkPDFName() {}
+
+void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ SkASSERT(!indirect);
+ stream->write(fValue.c_str(), fValue.size());
+}
+
+size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ SkASSERT(!indirect);
+ return fValue.size();
+}
+
+// static
+SkString SkPDFName::formatName(const SkString& input) {
+ SkASSERT(input.size() <= kMaxLen);
+
+ SkString result("/");
+ for (size_t i = 0; i < input.size(); i++) {
+ if (input[i] & 0x80 || input[i] < '!' || input[i] == '#') {
+ result.append("#");
+ result.appendHex(input[i], 2);
+ } else {
+ result.append(input.c_str() + i, 1);
+ }
+ }
+
+ return result;
+}
+
+SkPDFArray::SkPDFArray() {}
+SkPDFArray::~SkPDFArray() {
+ fValue.safeUnrefAll();
+}
+
+void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ stream->writeText("[");
+ for (int i = 0; i < fValue.count(); i++) {
+ fValue[i]->emitObject(stream, catalog, false);
+ if (i + 1 < fValue.count())
+ stream->writeText(" ");
+ }
+ stream->writeText("]");
+}
+
+size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ size_t result = strlen("[]");
+ if (fValue.count())
+ result += fValue.count() - 1;
+ for (int i = 0; i < fValue.count(); i++)
+ result += fValue[i]->getOutputSize(catalog, false);
+ return result;
+}
+
+void SkPDFArray::reserve(int length) {
+ SkASSERT(length <= kMaxLen);
+ fValue.setReserve(length);
+}
+
+SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) {
+ SkASSERT(offset < fValue.count());
+ SkSafeUnref(fValue[offset]);
+ fValue[offset] = value;
+ SkSafeRef(fValue[offset]);
+ return value;
+}
+
+SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
+ SkASSERT(fValue.count() < kMaxLen);
+ SkSafeRef(value);
+ fValue.push(value);
+ return value;
+}
+
+SkPDFDict::SkPDFDict() {}
+
+SkPDFDict::SkPDFDict(const char type[]) {
+ insert("Type", new SkPDFName(type))->unref();
+}
+
+SkPDFDict::~SkPDFDict() {
+ clear();
+}
+
+void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+ bool indirect) {
+ if (indirect)
+ return emitIndirectObject(stream, catalog);
+
+ stream->writeText("<<");
+ for (int i = 0; i < fValue.count(); i++) {
+ fValue[i].key->emitObject(stream, catalog, false);
+ stream->writeText(" ");
+ fValue[i].value->emitObject(stream, catalog, false);
+ stream->writeText("\n");
+ }
+ stream->writeText(">>");
+}
+
+size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+ if (indirect)
+ return getIndirectOutputSize(catalog);
+
+ size_t result = strlen("<<>>") + (fValue.count() * 2);
+ for (int i = 0; i < fValue.count(); i++) {
+ result += fValue[i].key->getOutputSize(catalog, false);
+ result += fValue[i].value->getOutputSize(catalog, false);
+ }
+ return result;
+}
+
+SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
+ struct Rec* newEntry = fValue.append();
+ newEntry->key = key;
+ SkSafeRef(newEntry->key);
+ newEntry->value = value;
+ SkSafeRef(newEntry->value);
+ return value;
+}
+
+SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
+ SkRefPtr<SkPDFName> keyName = new SkPDFName(key);
+ keyName->unref(); // SkRefPtr and new both took a reference.
+ return insert(keyName.get(), value);
+}
+
+void SkPDFDict::clear() {
+ for (int i = 0; i < fValue.count(); i++) {
+ SkSafeUnref(fValue[i].key);
+ SkSafeUnref(fValue[i].value);
+ }
+ fValue.reset();
+}
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
new file mode 100644
index 0000000000..a838427c73
--- /dev/null
+++ b/src/pdf/SkPDFUtils.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPDFUtils.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkPDFTypes.h"
+
+// static
+SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
+ SkScalar values[6];
+ SkAssertResult(matrix.pdfTransform(values));
+
+ SkPDFArray* result = new SkPDFArray;
+ result->reserve(6);
+ for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
+ result->append(new SkPDFScalar(values[i]))->unref();
+ }
+ return result;
+}
+
+// static
+void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
+ SkScalar values[6];
+ SkAssertResult(matrix.pdfTransform(values));
+ for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
+ SkPDFScalar::Append(values[i], content);
+ content->writeText(" ");
+ }
+ content->writeText("cm\n");
+}
+
+// static
+void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
+ SkPDFScalar::Append(x, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(y, content);
+ content->writeText(" m\n");
+}
+
+// static
+void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
+ SkPDFScalar::Append(x, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(y, content);
+ content->writeText(" l\n");
+}
+
+// static
+void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+ SkScalar ctl2X, SkScalar ctl2Y,
+ SkScalar dstX, SkScalar dstY, SkWStream* content) {
+ SkString cmd("y\n");
+ SkPDFScalar::Append(ctl1X, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(ctl1Y, content);
+ content->writeText(" ");
+ if (ctl2X != dstX || ctl2Y != dstY) {
+ cmd.set("c\n");
+ SkPDFScalar::Append(ctl2X, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(ctl2Y, content);
+ content->writeText(" ");
+ }
+ SkPDFScalar::Append(dstX, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(dstY, content);
+ content->writeText(" ");
+ content->writeText(cmd.c_str());
+}
+
+// static
+void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
+ // Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
+ SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
+
+ SkPDFScalar::Append(rect.fLeft, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(bottom, content);
+ content->writeText(" ");
+ SkPDFScalar::Append(rect.width(), content);
+ content->writeText(" ");
+ SkPDFScalar::Append(rect.height(), content);
+ content->writeText(" re\n");
+}
+
+// static
+void SkPDFUtils::EmitPath(const SkPath& path, SkWStream* content) {
+ SkPoint args[4];
+ SkPath::Iter iter(path, false);
+ for (SkPath::Verb verb = iter.next(args);
+ verb != SkPath::kDone_Verb;
+ verb = iter.next(args)) {
+ // args gets all the points, even the implicit first point.
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ MoveTo(args[0].fX, args[0].fY, content);
+ break;
+ case SkPath::kLine_Verb:
+ AppendLine(args[1].fX, args[1].fY, content);
+ break;
+ case SkPath::kQuad_Verb: {
+ // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
+ const SkScalar three = SkIntToScalar(3);
+ args[1].scale(SkIntToScalar(2));
+ SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
+ SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
+ SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
+ SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
+ AppendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY,
+ content);
+ break;
+ }
+ case SkPath::kCubic_Verb:
+ AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
+ args[3].fX, args[3].fY, content);
+ break;
+ case SkPath::kClose_Verb:
+ ClosePath(content);
+ break;
+ case SkPath::kDone_Verb:
+ break;
+ default:
+ SkASSERT(false);
+ break;
+ }
+ }
+}
+
+// static
+void SkPDFUtils::ClosePath(SkWStream* content) {
+ content->writeText("h\n");
+}
+
+// static
+void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
+ SkWStream* content) {
+ if (style == SkPaint::kFill_Style)
+ content->writeText("f");
+ else if (style == SkPaint::kStrokeAndFill_Style)
+ content->writeText("B");
+ else if (style == SkPaint::kStroke_Style)
+ content->writeText("S");
+
+ if (style != SkPaint::kStroke_Style) {
+ NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
+ NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
+ if (fill == SkPath::kEvenOdd_FillType)
+ content->writeText("*");
+ }
+ content->writeText("\n");
+}
+
+// static
+void SkPDFUtils::StrokePath(SkWStream* content) {
+ SkPDFUtils::PaintPath(
+ SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
+}
+
+// static
+void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
+ content->writeText("/X");
+ content->writeDecAsText(objectIndex);
+ content->writeText(" Do\n");
+}
+
+// static
+void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
+ content->writeText("/G");
+ content->writeDecAsText(objectIndex);
+ content->writeText(" gs\n");
+}
diff --git a/src/pdf/pdf_files.mk b/src/pdf/pdf_files.mk
new file mode 100644
index 0000000000..c9b4fb41d3
--- /dev/null
+++ b/src/pdf/pdf_files.mk
@@ -0,0 +1,13 @@
+SOURCE := \
+ SkPDFCatalog.cpp \
+ SkPDFDevice.cpp \
+ SkPDFDocument.cpp \
+ SkPDFFont.cpp \
+ SkPDFFormXObject.cpp \
+ SkPDFGraphicState.cpp \
+ SkPDFImage.cpp \
+ SkPDFPage.cpp \
+ SkPDFShader.cpp \
+ SkPDFStream.cpp \
+ SkPDFTypes.cpp \
+ SkPDFUtils.cpp
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
new file mode 100644
index 0000000000..fb1553693d
--- /dev/null
+++ b/src/pipe/SkGPipePriv.h
@@ -0,0 +1,213 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#ifndef SkGPipePriv_DEFINED
+#define SkGPipePriv_DEFINED
+
+#include "SkTypes.h"
+
+#define UNIMPLEMENTED
+
+// these must be contiguous, 0...N-1
+enum PaintFlats {
+ kColorFilter_PaintFlat,
+ kDrawLooper_PaintFlat,
+ kMaskFilter_PaintFlat,
+ kPathEffect_PaintFlat,
+ kRasterizer_PaintFlat,
+ kShader_PaintFlat,
+ kXfermode_PaintFlat,
+
+ kLast_PaintFlat = kXfermode_PaintFlat
+};
+#define kCount_PaintFlats (kLast_PaintFlat + 1)
+
+enum DrawOps {
+ kSkip_DrawOp, // skip an addition N bytes (N == data)
+
+ // these match Canvas apis
+ kClipPath_DrawOp,
+ kClipRegion_DrawOp,
+ kClipRect_DrawOp,
+ kConcat_DrawOp,
+ kDrawBitmap_DrawOp,
+ kDrawBitmapMatrix_DrawOp,
+ kDrawBitmapRect_DrawOp,
+ kDrawClear_DrawOp,
+ kDrawData_DrawOp,
+ kDrawPaint_DrawOp,
+ kDrawPath_DrawOp,
+ kDrawPicture_DrawOp,
+ kDrawPoints_DrawOp,
+ kDrawPosText_DrawOp,
+ kDrawPosTextH_DrawOp,
+ kDrawRect_DrawOp,
+ kDrawShape_DrawOp,
+ kDrawSprite_DrawOp,
+ kDrawText_DrawOp,
+ kDrawTextOnPath_DrawOp,
+ kDrawVertices_DrawOp,
+ kRestore_DrawOp,
+ kRotate_DrawOp,
+ kSave_DrawOp,
+ kSaveLayer_DrawOp,
+ kScale_DrawOp,
+ kSetMatrix_DrawOp,
+ kSkew_DrawOp,
+ kTranslate_DrawOp,
+
+ kPaintOp_DrawOp,
+
+ kDef_Typeface_DrawOp,
+ kDef_Flattenable_DrawOp,
+
+ kName_Flattenable_DrawOp, // index <--> name
+
+ // these are signals to playback, not drawing verbs
+ kDone_DrawOp,
+};
+
+/**
+ * DrawOp packs into a 32bit int as follows
+ *
+ * DrawOp:8 - Flags:4 - Data:20
+ *
+ * Flags and Data are called out separately, so we can reuse Data between
+ * different Ops that might have different Flags. e.g. Data might be a Paint
+ * index for both drawRect (no flags) and saveLayer (does have flags).
+ *
+ * All Ops that take a SkPaint use their Data field to store the index to
+ * the paint (previously defined with kPaintOp_DrawOp).
+ */
+
+#define DRAWOPS_OP_BITS 8
+#define DRAWOPS_FLAG_BITS 4
+#define DRAWOPS_DATA_BITS 20
+
+#define DRAWOPS_OP_MASK ((1 << DRAWOPS_OP_BITS) - 1)
+#define DRAWOPS_FLAG_MASK ((1 << DRAWOPS_FLAG_BITS) - 1)
+#define DRAWOPS_DATA_MASK ((1 << DRAWOPS_DATA_BITS) - 1)
+
+static unsigned DrawOp_unpackOp(uint32_t op32) {
+ return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS));
+}
+
+static unsigned DrawOp_unpackFlags(uint32_t op32) {
+ return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK;
+}
+
+static unsigned DrawOp_unpackData(uint32_t op32) {
+ return op32 & DRAWOPS_DATA_MASK;
+}
+
+static uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) {
+ SkASSERT(0 == (op & ~DRAWOPS_OP_MASK));
+ SkASSERT(0 == (flags & ~DRAWOPS_FLAG_MASK));
+ SkASSERT(0 == (data & ~DRAWOPS_DATA_MASK));
+
+ return (op << DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS) |
+ (flags << DRAWOPS_DATA_BITS) |
+ data;
+}
+
+/** DrawOp specific flag bits
+ */
+
+enum {
+ kSaveLayer_HasBounds_DrawOpFlag = 1 << 0,
+ kSaveLayer_HasPaint_DrawOpFlag = 1 << 1,
+};
+enum {
+ kClear_HasColor_DrawOpFlag = 1 << 0
+};
+enum {
+ kDrawTextOnPath_HasMatrix_DrawOpFlag = 1 << 0
+};
+enum {
+ kDrawVertices_HasTexs_DrawOpFlag = 1 << 0,
+ kDrawVertices_HasColors_DrawOpFlag = 1 << 1,
+ kDrawVertices_HasIndices_DrawOpFlag = 1 << 2,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum PaintOps {
+ kReset_PaintOp, // no arg
+
+ kFlags_PaintOp, // arg inline
+ kColor_PaintOp, // arg 32
+ kStyle_PaintOp, // arg inline
+ kJoin_PaintOp, // arg inline
+ kCap_PaintOp, // arg inline
+ kWidth_PaintOp, // arg scalar
+ kMiter_PaintOp,// arg scalar
+
+ kEncoding_PaintOp, // arg inline - text
+ kHinting_PaintOp, // arg inline - text
+ kAlign_PaintOp, // arg inline - text
+ kTextSize_PaintOp, // arg scalar - text
+ kTextScaleX_PaintOp,// arg scalar - text
+ kTextSkewX_PaintOp, // arg scalar - text
+ kTypeface_PaintOp, // arg inline (index) - text
+
+ kFlatIndex_PaintOp, // flags=paintflat, data=index
+};
+
+#define PAINTOPS_OP_BITS 8
+#define PAINTOPS_FLAG_BITS 4
+#define PAINTOPS_DATA_BITS 20
+
+#define PAINTOPS_OP_MASK ((1 << PAINTOPS_OP_BITS) - 1)
+#define PAINTOPS_FLAG_MASK ((1 << PAINTOPS_FLAG_BITS) - 1)
+#define PAINTOPS_DATA_MASK ((1 << PAINTOPS_DATA_BITS) - 1)
+
+static unsigned PaintOp_unpackOp(uint32_t op32) {
+ return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS));
+}
+
+static unsigned PaintOp_unpackFlags(uint32_t op32) {
+ return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK;
+}
+
+static unsigned PaintOp_unpackData(uint32_t op32) {
+ return op32 & PAINTOPS_DATA_MASK;
+}
+
+static uint32_t PaintOp_packOp(PaintOps op) {
+ SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+
+ return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS);
+}
+
+static uint32_t PaintOp_packOpData(PaintOps op, unsigned data) {
+ SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+ SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
+
+ return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) | data;
+}
+
+static uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) {
+ SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+ SkASSERT(0 == (flags & ~PAINTOPS_FLAG_MASK));
+ SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
+
+ return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) |
+ (flags << PAINTOPS_DATA_BITS) |
+ data;
+}
+
+#endif
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
new file mode 100644
index 0000000000..ecb19fd059
--- /dev/null
+++ b/src/pipe/SkGPipeRead.cpp
@@ -0,0 +1,570 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkGPipe.h"
+#include "SkGPipePriv.h"
+#include "SkReader32.h"
+#include "SkStream.h"
+
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+static void set_paintflat(SkPaint* paint, SkFlattenable* obj, unsigned paintFlat) {
+ SkASSERT(paintFlat < kCount_PaintFlats);
+ switch (paintFlat) {
+ case kColorFilter_PaintFlat:
+ paint->setColorFilter((SkColorFilter*)obj);
+ break;
+ case kDrawLooper_PaintFlat:
+ paint->setLooper((SkDrawLooper*)obj);
+ break;
+ case kMaskFilter_PaintFlat:
+ paint->setMaskFilter((SkMaskFilter*)obj);
+ break;
+ case kPathEffect_PaintFlat:
+ paint->setPathEffect((SkPathEffect*)obj);
+ break;
+ case kRasterizer_PaintFlat:
+ paint->setRasterizer((SkRasterizer*)obj);
+ break;
+ case kShader_PaintFlat:
+ paint->setShader((SkShader*)obj);
+ break;
+ case kXfermode_PaintFlat:
+ paint->setXfermode((SkXfermode*)obj);
+ break;
+ default:
+ SkASSERT(!"never gets here");
+ }
+}
+
+template <typename T> class SkRefCntTDArray : public SkTDArray<T> {
+public:
+ ~SkRefCntTDArray() { this->unrefAll(); }
+};
+
+class SkGPipeState {
+public:
+ SkGPipeState();
+ ~SkGPipeState();
+
+ void setReader(SkFlattenableReadBuffer* reader) {
+ fReader = reader;
+ fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count());
+ }
+
+ const SkPaint& paint() const { return fPaint; }
+ SkPaint* editPaint() { return &fPaint; }
+
+ SkFlattenable* getFlat(unsigned index) const {
+ if (0 == index) {
+ return NULL;
+ }
+ return fFlatArray[index - 1];
+ }
+
+ void defFlattenable(PaintFlats pf, unsigned index) {
+ SkASSERT(index == fFlatArray.count() + 1);
+ SkFlattenable* obj = fReader->readFlattenable();
+ *fFlatArray.append() = obj;
+ }
+
+ void nameFlattenable(PaintFlats pf, unsigned index) {
+ SkASSERT(index == fFactoryArray.count() + 1);
+ const char* name = fReader->readString();
+ SkFlattenable::Factory fact = SkFlattenable::NameToFactory(name);
+ *fFactoryArray.append() = fact;
+
+ // update this each time we grow the array
+ fReader->setFactoryPlayback(fFactoryArray.begin(), fFactoryArray.count());
+ }
+
+ void addTypeface() {
+ size_t size = fReader->readU32();
+ const void* data = fReader->skip(SkAlign4(size));
+ SkMemoryStream stream(data, size, false);
+ *fTypefaces.append() = SkTypeface::Deserialize(&stream);
+ }
+ void setTypeface(SkPaint* paint, unsigned id) {
+ paint->setTypeface(id ? fTypefaces[id - 1] : NULL);
+ }
+
+ SkFlattenableReadBuffer* fReader;
+
+private:
+ SkPaint fPaint;
+ SkTDArray<SkFlattenable*> fFlatArray;
+ SkTDArray<SkTypeface*> fTypefaces;
+ SkTDArray<SkFlattenable::Factory> fFactoryArray;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> const T* skip(SkReader32* reader, int count = 1) {
+ SkASSERT(count >= 0);
+ size_t size = sizeof(T) * count;
+ SkASSERT(SkAlign4(size) == size);
+ return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+template <typename T> const T* skipAlign(SkReader32* reader, int count = 1) {
+ SkASSERT(count >= 0);
+ size_t size = SkAlign4(sizeof(T) * count);
+ return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void clipPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkPath path;
+ path.unflatten(*reader);
+ canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkRegion rgn;
+ SkReadRegion(reader, &rgn);
+ canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ canvas->clipRect(*skip<SkRect>(reader), (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkMatrix matrix;
+ SkReadMatrix(reader, &matrix);
+ canvas->setMatrix(matrix);
+}
+
+static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkMatrix matrix;
+ SkReadMatrix(reader, &matrix);
+ canvas->concat(matrix);
+}
+
+static void scale_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ const SkScalar* param = skip<SkScalar>(reader, 2);
+ canvas->scale(param[0], param[1]);
+}
+
+static void skew_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ const SkScalar* param = skip<SkScalar>(reader, 2);
+ canvas->skew(param[0], param[1]);
+}
+
+static void rotate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ canvas->rotate(reader->readScalar());
+}
+
+static void translate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ const SkScalar* param = skip<SkScalar>(reader, 2);
+ canvas->translate(param[0], param[1]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void save_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ canvas->save((SkCanvas::SaveFlags)DrawOp_unpackData(op32));
+}
+
+static void saveLayer_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ unsigned flags = DrawOp_unpackFlags(op32);
+ SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags)DrawOp_unpackData(op32);
+
+ const SkRect* bounds = NULL;
+ if (flags & kSaveLayer_HasBounds_DrawOpFlag) {
+ bounds = skip<SkRect>(reader);
+ }
+ const SkPaint* paint = NULL;
+ if (flags & kSaveLayer_HasPaint_DrawOpFlag) {
+ paint = &state->paint();
+ }
+ canvas->saveLayer(bounds, paint, saveFlags);
+}
+
+static void restore_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ canvas->restore();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawClear_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkColor color = 0;
+ if (DrawOp_unpackFlags(op32) & kClear_HasColor_DrawOpFlag) {
+ color = reader->readU32();
+ }
+ canvas->clear(color);
+}
+
+static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ canvas->drawPaint(state->paint());
+}
+
+static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkCanvas::PointMode mode = (SkCanvas::PointMode)DrawOp_unpackFlags(op32);
+ size_t count = reader->readU32();
+ const SkPoint* pts = skip<SkPoint>(reader, count);
+ canvas->drawPoints(mode, count, pts, state->paint());
+}
+
+static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ canvas->drawRect(*skip<SkRect>(reader), state->paint());
+}
+
+static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkPath path;
+ path.unflatten(*reader);
+ canvas->drawPath(path, state->paint());
+}
+
+static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ unsigned flags = DrawOp_unpackFlags(op32);
+
+ SkCanvas::VertexMode mode = (SkCanvas::VertexMode)reader->readU32();
+ int vertexCount = reader->readU32();
+ const SkPoint* verts = skip<SkPoint>(reader, vertexCount);
+
+ const SkPoint* texs = NULL;
+ if (flags & kDrawVertices_HasTexs_DrawOpFlag) {
+ texs = skip<SkPoint>(reader, vertexCount);
+ }
+
+ const SkColor* colors = NULL;
+ if (flags & kDrawVertices_HasColors_DrawOpFlag) {
+ colors = skip<SkColor>(reader, vertexCount);
+ }
+
+ // TODO: flatten/unflatten xfermodes
+ SkXfermode* xfer = NULL;
+
+ int indexCount = 0;
+ const uint16_t* indices = NULL;
+ if (flags & kDrawVertices_HasIndices_DrawOpFlag) {
+ indexCount = reader->readU32();
+ indices = skipAlign<uint16_t>(reader, indexCount);
+ }
+
+ canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer,
+ indices, indexCount, state->paint());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ size_t len = reader->readU32();
+ const void* text = reader->skip(SkAlign4(len));
+ const SkScalar* xy = skip<SkScalar>(reader, 2);
+ canvas->drawText(text, len, xy[0], xy[1], state->paint());
+}
+
+static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ size_t len = reader->readU32();
+ const void* text = reader->skip(SkAlign4(len));
+ size_t posCount = reader->readU32(); // compute by our writer
+ const SkPoint* pos = skip<SkPoint>(reader, posCount);
+ canvas->drawPosText(text, len, pos, state->paint());
+}
+
+static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ size_t len = reader->readU32();
+ const void* text = reader->skip(SkAlign4(len));
+ size_t posCount = reader->readU32(); // compute by our writer
+ const SkScalar* xpos = skip<SkScalar>(reader, posCount);
+ SkScalar constY = reader->readScalar();
+ canvas->drawPosTextH(text, len, xpos, constY, state->paint());
+}
+
+static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ size_t len = reader->readU32();
+ const void* text = reader->skip(SkAlign4(len));
+
+ SkPath path;
+ path.unflatten(*reader);
+
+ SkMatrix matrixStorage;
+ const SkMatrix* matrix = NULL;
+ if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) {
+ SkReadMatrix(reader, &matrixStorage);
+ matrix = &matrixStorage;
+ }
+
+ canvas->drawTextOnPath(text, len, path, matrix, state->paint());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ UNIMPLEMENTED
+}
+
+static void drawBitmapMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ UNIMPLEMENTED
+}
+
+static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ UNIMPLEMENTED
+}
+
+static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawData_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ // since we don't have a paint, we can use data for our (small) sizes
+ size_t size = DrawOp_unpackData(op32);
+ if (0 == size) {
+ size = reader->readU32();
+ }
+ const void* data = reader->skip(SkAlign4(size));
+ canvas->drawData(data, size);
+}
+
+static void drawShape_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ UNIMPLEMENTED
+}
+
+static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void paintOp_rp(SkCanvas*, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ size_t offset = reader->offset();
+ size_t stop = offset + PaintOp_unpackData(op32);
+ SkPaint* p = state->editPaint();
+
+ do {
+ uint32_t p32 = reader->readU32();
+ unsigned op = PaintOp_unpackOp(p32);
+ unsigned data = PaintOp_unpackData(p32);
+
+// SkDebugf(" read %08X op=%d flags=%d data=%d\n", p32, op, done, data);
+
+ switch (op) {
+ case kReset_PaintOp: p->reset(); break;
+ case kFlags_PaintOp: p->setFlags(data); break;
+ case kColor_PaintOp: p->setColor(reader->readU32()); break;
+ case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break;
+ case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break;
+ case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break;
+ case kWidth_PaintOp: p->setStrokeWidth(reader->readScalar()); break;
+ case kMiter_PaintOp: p->setStrokeMiter(reader->readScalar()); break;
+ case kEncoding_PaintOp:
+ p->setTextEncoding((SkPaint::TextEncoding)data);
+ break;
+ case kHinting_PaintOp: p->setHinting((SkPaint::Hinting)data); break;
+ case kAlign_PaintOp: p->setTextAlign((SkPaint::Align)data); break;
+ case kTextSize_PaintOp: p->setTextSize(reader->readScalar()); break;
+ case kTextScaleX_PaintOp: p->setTextScaleX(reader->readScalar()); break;
+ case kTextSkewX_PaintOp: p->setTextSkewX(reader->readScalar()); break;
+
+ case kFlatIndex_PaintOp: {
+ PaintFlats pf = (PaintFlats)PaintOp_unpackFlags(p32);
+ unsigned index = data;
+ set_paintflat(p, state->getFlat(index), pf);
+ break;
+ }
+
+ case kTypeface_PaintOp: state->setTypeface(p, data); break;
+ default: SkASSERT(!"bad paintop"); return;
+ }
+ SkASSERT(reader->offset() <= stop);
+ } while (reader->offset() < stop);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void def_Typeface_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState* state) {
+ state->addTypeface();
+}
+
+static void def_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32,
+ SkGPipeState* state) {
+ PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32);
+ unsigned index = DrawOp_unpackData(op32);
+ state->defFlattenable(pf, index);
+}
+
+static void name_PaintFlat_rp(SkCanvas*, SkReader32*, uint32_t op32,
+ SkGPipeState* state) {
+ PaintFlats pf = (PaintFlats)DrawOp_unpackFlags(op32);
+ unsigned index = DrawOp_unpackData(op32);
+ state->nameFlattenable(pf, index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) {
+ size_t bytes = DrawOp_unpackData(op32);
+ (void)reader->skip(bytes);
+}
+
+static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
+
+typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
+
+static const ReadProc gReadTable[] = {
+ skip_rp,
+ clipPath_rp,
+ clipRegion_rp,
+ clipRect_rp,
+ concat_rp,
+ drawBitmap_rp,
+ drawBitmapMatrix_rp,
+ drawBitmapRect_rp,
+ drawClear_rp,
+ drawData_rp,
+ drawPaint_rp,
+ drawPath_rp,
+ drawPicture_rp,
+ drawPoints_rp,
+ drawPosText_rp,
+ drawPosTextH_rp,
+ drawRect_rp,
+ drawShape_rp,
+ drawSprite_rp,
+ drawText_rp,
+ drawTextOnPath_rp,
+ drawVertices_rp,
+ restore_rp,
+ rotate_rp,
+ save_rp,
+ saveLayer_rp,
+ scale_rp,
+ setMatrix_rp,
+ skew_rp,
+ translate_rp,
+
+ paintOp_rp,
+ def_Typeface_rp,
+ def_PaintFlat_rp,
+ name_PaintFlat_rp,
+
+ done_rp
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGPipeState::SkGPipeState() {}
+
+SkGPipeState::~SkGPipeState() {
+ fTypefaces.unrefAll();
+ fFlatArray.unrefAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeReader::SkGPipeReader(SkCanvas* target) {
+ SkSafeRef(target);
+ fCanvas = target;
+ fState = NULL;
+}
+
+SkGPipeReader::~SkGPipeReader() {
+ SkSafeUnref(fCanvas);
+ delete fState;
+}
+
+SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length,
+ size_t* bytesRead) {
+ if (NULL == fCanvas) {
+ return kError_Status;
+ }
+
+ if (NULL == fState) {
+ fState = new SkGPipeState;
+ }
+
+ SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1));
+
+ const ReadProc* table = gReadTable;
+ SkFlattenableReadBuffer reader(data, length);
+ SkCanvas* canvas = fCanvas;
+ Status status = kEOF_Status;
+
+ fState->setReader(&reader);
+ while (!reader.eof()) {
+ uint32_t op32 = reader.readU32();
+ unsigned op = DrawOp_unpackOp(op32);
+ SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;)
+
+ if (op >= SK_ARRAY_COUNT(gReadTable)) {
+ SkDebugf("---- bad op during GPipeState::playback\n");
+ status = kError_Status;
+ break;
+ }
+ if (kDone_DrawOp == op) {
+ status = kDone_Status;
+ break;
+ }
+ table[op](canvas, &reader, op32, fState);
+ }
+
+ if (bytesRead) {
+ *bytesRead = reader.offset();
+ }
+ return status;
+}
+
+
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
new file mode 100644
index 0000000000..428f864fe9
--- /dev/null
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -0,0 +1,813 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkGPipe.h"
+#include "SkGPipePriv.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTypeface.h"
+#include "SkWriter32.h"
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+
+static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
+ SkASSERT(paintFlat < kCount_PaintFlats);
+ switch (paintFlat) {
+ case kColorFilter_PaintFlat: return paint.getColorFilter();
+ case kDrawLooper_PaintFlat: return paint.getLooper();
+ case kMaskFilter_PaintFlat: return paint.getMaskFilter();
+ case kPathEffect_PaintFlat: return paint.getPathEffect();
+ case kRasterizer_PaintFlat: return paint.getRasterizer();
+ case kShader_PaintFlat: return paint.getShader();
+ case kXfermode_PaintFlat: return paint.getXfermode();
+ }
+ SkASSERT(!"never gets here");
+ return NULL;
+}
+
+static size_t estimateFlattenSize(const SkPath& path) {
+ int n = path.countPoints();
+ size_t bytes = 3 * sizeof(int32_t);
+ bytes += n * sizeof(SkPoint);
+ bytes += SkAlign4(n + 2); // verbs: add 2 for move/close extras
+
+#ifdef SK_DEBUG
+ {
+ SkWriter32 writer(1024);
+ path.flatten(writer);
+ SkASSERT(writer.size() <= bytes);
+ }
+#endif
+ return bytes;
+}
+
+static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) {
+ SkASSERT(typeface);
+ SkDynamicMemoryWStream stream;
+ typeface->serialize(&stream);
+ size_t size = stream.getOffset();
+ if (writer) {
+ writer->write32(size);
+ writer->write(stream.getStream(), size);
+ }
+ return 4 + size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeCanvas : public SkCanvas {
+public:
+ SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*);
+ virtual ~SkGPipeCanvas();
+
+ void finish() {
+ if (!fDone) {
+ this->writeOp(kDone_DrawOp);
+ this->doNotify();
+ fDone = true;
+ }
+ }
+
+ // overrides from SkCanvas
+ virtual int save(SaveFlags);
+ virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags);
+ virtual void restore();
+ virtual bool translate(SkScalar dx, SkScalar dy);
+ virtual bool scale(SkScalar sx, SkScalar sy);
+ virtual bool rotate(SkScalar degrees);
+ virtual bool skew(SkScalar sx, SkScalar sy);
+ virtual bool concat(const SkMatrix& matrix);
+ virtual void setMatrix(const SkMatrix& matrix);
+ virtual bool clipRect(const SkRect& rect, SkRegion::Op op);
+ virtual bool clipPath(const SkPath& path, SkRegion::Op op);
+ virtual bool clipRegion(const SkRegion& region, SkRegion::Op op);
+ virtual void clear(SkColor);
+ virtual void drawPaint(const SkPaint& paint);
+ virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
+ const SkPaint&);
+ virtual void drawRect(const SkRect& rect, const SkPaint&);
+ virtual void drawPath(const SkPath& path, const SkPaint&);
+ virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+ const SkPaint*);
+ virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
+ const SkRect& dst, const SkPaint*);
+ virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+ const SkPaint*);
+ virtual void drawSprite(const SkBitmap&, int left, int top,
+ const SkPaint*);
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint&);
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint&);
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY, const SkPaint&);
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint&);
+ virtual void drawPicture(SkPicture& picture);
+ virtual void drawShape(SkShape*);
+ virtual void drawVertices(VertexMode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint&);
+ virtual void drawData(const void*, size_t);
+
+private:
+ SkFactorySet* fFactorySet; // optional, only used if cross-process
+ SkGPipeController* fController;
+ SkWriter32& fWriter;
+ size_t fBlockSize; // amount allocated for writer
+ size_t fBytesNotified;
+ bool fDone;
+
+ SkRefCntSet fTypefaceSet;
+
+ uint32_t getTypefaceID(SkTypeface*);
+
+ inline void writeOp(DrawOps op, unsigned flags, unsigned data) {
+ fWriter.write32(DrawOp_packOpFlagData(op, flags, data));
+ }
+
+ inline void writeOp(DrawOps op) {
+ fWriter.write32(DrawOp_packOpFlagData(op, 0, 0));
+ }
+
+ bool needOpBytes(size_t size = 0);
+
+ inline void doNotify() {
+ if (!fDone) {
+ size_t bytes = fWriter.size() - fBytesNotified;
+ fController->notifyWritten(bytes);
+ fBytesNotified += bytes;
+ }
+ }
+
+ struct FlatData {
+ uint32_t fIndex; // always > 0
+ uint32_t fSize;
+
+ void* data() { return (char*)this + sizeof(*this); }
+
+ static int Compare(const FlatData* a, const FlatData* b) {
+ return memcmp(&a->fSize, &b->fSize, a->fSize + sizeof(a->fSize));
+ }
+ };
+ SkTDArray<FlatData*> fFlatArray;
+ int fCurrFlatIndex[kCount_PaintFlats];
+ int flattenToIndex(SkFlattenable* obj, PaintFlats);
+
+ SkPaint fPaint;
+ void writePaint(const SkPaint&);
+
+ class AutoPipeNotify {
+ public:
+ AutoPipeNotify(SkGPipeCanvas* canvas) : fCanvas(canvas) {}
+ ~AutoPipeNotify() { fCanvas->doNotify(); }
+ private:
+ SkGPipeCanvas* fCanvas;
+ };
+ friend class AutoPipeNotify;
+
+ typedef SkCanvas INHERITED;
+};
+
+// return 0 for NULL (or unflattenable obj), or index-base-1
+int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
+ if (NULL == obj) {
+ return 0;
+ }
+
+ SkFlattenable::Factory fact = obj->getFactory();
+ if (NULL == fact) {
+ return 0;
+ }
+
+ if (fFactorySet) {
+ uint32_t id = fFactorySet->find((void*)fact);
+ if (0 == id) {
+ const char* name = SkFlattenable::FactoryToName(fact);
+ if (NULL == name) {
+ return 0;
+ }
+ size_t len = strlen(name);
+ size_t size = SkWriter32::WriteStringSize(name, len);
+ if (!this->needOpBytes(size)) {
+ return 0;
+ }
+ unsigned id = fFactorySet->add(fact);
+ this->writeOp(kName_Flattenable_DrawOp, paintflat, id);
+ fWriter.writeString(name, len);
+ }
+ }
+
+ SkFlattenableWriteBuffer tmpWriter(1024);
+ tmpWriter.setFactoryRecorder(fFactorySet);
+
+ tmpWriter.writeFlattenable(obj);
+ size_t len = tmpWriter.size();
+ size_t allocSize = len + sizeof(FlatData);
+
+ SkAutoSMalloc<1024> storage(allocSize);
+ FlatData* flat = (FlatData*)storage.get();
+ flat->fSize = len;
+ tmpWriter.flatten(flat->data());
+
+ int index = SkTSearch<FlatData>((const FlatData**)fFlatArray.begin(),
+ fFlatArray.count(), flat, sizeof(flat),
+ &FlatData::Compare);
+ if (index < 0) {
+ index = ~index;
+ FlatData* copy = (FlatData*)sk_malloc_throw(allocSize);
+ memcpy(copy, flat, allocSize);
+ *fFlatArray.insert(index) = copy;
+ // call this after the insert, so that count() will have been grown
+ copy->fIndex = fFlatArray.count();
+// SkDebugf("--- add flattenable[%d] size=%d index=%d\n", paintflat, len, copy->fIndex);
+
+ if (this->needOpBytes(len)) {
+ this->writeOp(kDef_Flattenable_DrawOp, paintflat, copy->fIndex);
+ fWriter.write(copy->data(), len);
+ }
+ }
+ return fFlatArray[index]->fIndex;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MIN_BLOCK_SIZE (16 * 1024)
+
+SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
+ SkWriter32* writer, SkFactorySet* fset)
+ : fWriter(*writer), fFactorySet(fset) {
+ fController = controller;
+ fDone = false;
+ fBlockSize = 0; // need first block from controller
+ sk_bzero(fCurrFlatIndex, sizeof(fCurrFlatIndex));
+
+ // we need a device to limit our clip
+ // should the caller give us the bounds?
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32767, 32767);
+ SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
+ this->setDevice(device)->unref();
+}
+
+SkGPipeCanvas::~SkGPipeCanvas() {
+ this->finish();
+
+ fFlatArray.freeAll();
+}
+
+bool SkGPipeCanvas::needOpBytes(size_t needed) {
+ if (fDone) {
+ return false;
+ }
+
+ needed += 4; // size of DrawOp atom
+ if (fWriter.size() + needed > fBlockSize) {
+ void* block = fController->requestBlock(MIN_BLOCK_SIZE, &fBlockSize);
+ if (NULL == block) {
+ fDone = true;
+ return false;
+ }
+ fWriter.reset(block, fBlockSize);
+ fBytesNotified = 0;
+ }
+ return true;
+}
+
+uint32_t SkGPipeCanvas::getTypefaceID(SkTypeface* face) {
+ uint32_t id = 0; // 0 means default/null typeface
+ if (face) {
+ id = fTypefaceSet.find(face);
+ if (0 == id) {
+ id = fTypefaceSet.add(face);
+ size_t size = writeTypeface(NULL, face);
+ if (this->needOpBytes(size)) {
+ this->writeOp(kDef_Typeface_DrawOp);
+ writeTypeface(&fWriter, face);
+ }
+ }
+ }
+ return id;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define NOTIFY_SETUP(canvas) \
+ AutoPipeNotify apn(canvas)
+
+int SkGPipeCanvas::save(SaveFlags flags) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes()) {
+ this->writeOp(kSave_DrawOp, 0, flags);
+ }
+ return this->INHERITED::save(flags);
+}
+
+int SkGPipeCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags saveFlags) {
+ NOTIFY_SETUP(this);
+ size_t size = 0;
+ unsigned opFlags = 0;
+
+ if (bounds) {
+ opFlags |= kSaveLayer_HasBounds_DrawOpFlag;
+ size += sizeof(SkRect);
+ }
+ if (paint) {
+ opFlags |= kSaveLayer_HasPaint_DrawOpFlag;
+ this->writePaint(*paint);
+ }
+
+ if (this->needOpBytes(size)) {
+ this->writeOp(kSaveLayer_DrawOp, opFlags, saveFlags);
+ if (bounds) {
+ fWriter.writeRect(*bounds);
+ }
+ }
+
+ // we just pass on the save, so we don't create a layer
+ return this->INHERITED::save(saveFlags);
+}
+
+void SkGPipeCanvas::restore() {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes()) {
+ this->writeOp(kRestore_DrawOp);
+ }
+ this->INHERITED::restore();
+}
+
+bool SkGPipeCanvas::translate(SkScalar dx, SkScalar dy) {
+ if (dx || dy) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(2 * sizeof(SkScalar))) {
+ this->writeOp(kTranslate_DrawOp);
+ fWriter.writeScalar(dx);
+ fWriter.writeScalar(dy);
+ }
+ }
+ return this->INHERITED::translate(dx, dy);
+}
+
+bool SkGPipeCanvas::scale(SkScalar sx, SkScalar sy) {
+ if (sx || sy) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(2 * sizeof(SkScalar))) {
+ this->writeOp(kScale_DrawOp);
+ fWriter.writeScalar(sx);
+ fWriter.writeScalar(sy);
+ }
+ }
+ return this->INHERITED::scale(sx, sy);
+}
+
+bool SkGPipeCanvas::rotate(SkScalar degrees) {
+ if (degrees) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(sizeof(SkScalar))) {
+ this->writeOp(kRotate_DrawOp);
+ fWriter.writeScalar(degrees);
+ }
+ }
+ return this->INHERITED::rotate(degrees);
+}
+
+bool SkGPipeCanvas::skew(SkScalar sx, SkScalar sy) {
+ if (sx || sy) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(2 * sizeof(SkScalar))) {
+ this->writeOp(kSkew_DrawOp);
+ fWriter.writeScalar(sx);
+ fWriter.writeScalar(sy);
+ }
+ }
+ return this->INHERITED::skew(sx, sy);
+}
+
+bool SkGPipeCanvas::concat(const SkMatrix& matrix) {
+ if (!matrix.isIdentity()) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(matrix.flatten(NULL))) {
+ this->writeOp(kConcat_DrawOp);
+ SkWriteMatrix(&fWriter, matrix);
+ }
+ }
+ return this->INHERITED::concat(matrix);
+}
+
+void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(matrix.flatten(NULL))) {
+ this->writeOp(kSetMatrix_DrawOp);
+ SkWriteMatrix(&fWriter, matrix);
+ }
+ this->INHERITED::setMatrix(matrix);
+}
+
+bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(sizeof(SkRect))) {
+ this->writeOp(kClipRect_DrawOp, 0, rgnOp);
+ fWriter.writeRect(rect);
+ }
+ return this->INHERITED::clipRect(rect, rgnOp);
+}
+
+bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(estimateFlattenSize(path))) {
+ this->writeOp(kClipPath_DrawOp, 0, rgnOp);
+ path.flatten(fWriter);
+ }
+ // we just pass on the bounds of the path
+ return this->INHERITED::clipRect(path.getBounds(), rgnOp);
+}
+
+bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) {
+ NOTIFY_SETUP(this);
+ if (this->needOpBytes(region.flatten(NULL))) {
+ this->writeOp(kClipRegion_DrawOp, 0, rgnOp);
+ SkWriteRegion(&fWriter, region);
+ }
+ return this->INHERITED::clipRegion(region, rgnOp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGPipeCanvas::clear(SkColor color) {
+ NOTIFY_SETUP(this);
+ unsigned flags = 0;
+ if (color) {
+ flags |= kClear_HasColor_DrawOpFlag;
+ }
+ if (this->needOpBytes(sizeof(SkColor))) {
+ this->writeOp(kDrawClear_DrawOp, flags, 0);
+ if (color) {
+ fWriter.write32(color);
+ }
+ }
+}
+
+void SkGPipeCanvas::drawPaint(const SkPaint& paint) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ if (this->needOpBytes()) {
+ this->writeOp(kDrawPaint_DrawOp);
+ }
+}
+
+void SkGPipeCanvas::drawPoints(PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) {
+ if (count) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ if (this->needOpBytes(4 + count * sizeof(SkPoint))) {
+ this->writeOp(kDrawPoints_DrawOp, mode, 0);
+ fWriter.write32(count);
+ fWriter.write(pts, count * sizeof(SkPoint));
+ }
+ }
+}
+
+void SkGPipeCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ if (this->needOpBytes(sizeof(SkRect))) {
+ this->writeOp(kDrawRect_DrawOp);
+ fWriter.writeRect(rect);
+ }
+}
+
+void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ if (this->needOpBytes(estimateFlattenSize(path))) {
+ this->writeOp(kDrawPath_DrawOp);
+ path.flatten(fWriter);
+ }
+}
+
+void SkGPipeCanvas::drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+ const SkPaint*) {
+ UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapRect(const SkBitmap&, const SkIRect* src,
+ const SkRect& dst, const SkPaint*) {
+ UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+ const SkPaint*) {
+ UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawSprite(const SkBitmap&, int left, int top,
+ const SkPaint*) {
+ UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ if (byteLength) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ if (this->needOpBytes(4 + SkAlign4(byteLength) + 2 * sizeof(SkScalar))) {
+ this->writeOp(kDrawText_DrawOp);
+ fWriter.write32(byteLength);
+ fWriter.writePad(text, byteLength);
+ fWriter.writeScalar(x);
+ fWriter.writeScalar(y);
+ }
+ }
+}
+
+void SkGPipeCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ if (byteLength) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ int count = paint.textToGlyphs(text, byteLength, NULL);
+ if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkPoint))) {
+ this->writeOp(kDrawPosText_DrawOp);
+ fWriter.write32(byteLength);
+ fWriter.writePad(text, byteLength);
+ fWriter.write32(count);
+ fWriter.write(pos, count * sizeof(SkPoint));
+ }
+ }
+}
+
+void SkGPipeCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ if (byteLength) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ int count = paint.textToGlyphs(text, byteLength, NULL);
+ if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkScalar) + 4)) {
+ this->writeOp(kDrawPosTextH_DrawOp);
+ fWriter.write32(byteLength);
+ fWriter.writePad(text, byteLength);
+ fWriter.write32(count);
+ fWriter.write(xpos, count * sizeof(SkScalar));
+ fWriter.writeScalar(constY);
+ }
+ }
+}
+
+void SkGPipeCanvas::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ if (byteLength) {
+ NOTIFY_SETUP(this);
+ unsigned flags = 0;
+ size_t size = 4 + SkAlign4(byteLength) + estimateFlattenSize(path);
+ if (matrix) {
+ flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag;
+ size += matrix->flatten(NULL);
+ }
+ this->writePaint(paint);
+ if (this->needOpBytes(size)) {
+ this->writeOp(kDrawTextOnPath_DrawOp, flags, 0);
+
+ fWriter.write32(byteLength);
+ fWriter.writePad(text, byteLength);
+
+ path.flatten(fWriter);
+ if (matrix) {
+ SkWriteMatrix(&fWriter, *matrix);
+ }
+ }
+ }
+}
+
+void SkGPipeCanvas::drawPicture(SkPicture& picture) {
+ // we want to playback the picture into individual draw calls
+ this->INHERITED::drawPicture(picture);
+}
+
+void SkGPipeCanvas::drawShape(SkShape* shape) {
+ UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawVertices(VertexMode mode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ if (0 == vertexCount) {
+ return;
+ }
+
+ NOTIFY_SETUP(this);
+ size_t size = 4 + vertexCount * sizeof(SkPoint);
+ this->writePaint(paint);
+ unsigned flags = 0;
+ if (texs) {
+ flags |= kDrawVertices_HasTexs_DrawOpFlag;
+ size += vertexCount * sizeof(SkPoint);
+ }
+ if (colors) {
+ flags |= kDrawVertices_HasColors_DrawOpFlag;
+ size += vertexCount * sizeof(SkColor);
+ }
+ if (indices && indexCount > 0) {
+ flags |= kDrawVertices_HasIndices_DrawOpFlag;
+ size += 4 + SkAlign4(indexCount * sizeof(uint16_t));
+ }
+
+ if (this->needOpBytes(size)) {
+ this->writeOp(kDrawVertices_DrawOp, flags, 0);
+ fWriter.write32(mode);
+ fWriter.write32(vertexCount);
+ fWriter.write(vertices, vertexCount * sizeof(SkPoint));
+ if (texs) {
+ fWriter.write(texs, vertexCount * sizeof(SkPoint));
+ }
+ if (colors) {
+ fWriter.write(colors, vertexCount * sizeof(SkColor));
+ }
+
+ // TODO: flatten xfermode
+
+ if (indices && indexCount > 0) {
+ fWriter.write32(indexCount);
+ fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+ }
+ }
+}
+
+void SkGPipeCanvas::drawData(const void* ptr, size_t size) {
+ if (size && ptr) {
+ NOTIFY_SETUP(this);
+ unsigned data = 0;
+ if (size < (1 << DRAWOPS_DATA_BITS)) {
+ data = (unsigned)size;
+ }
+ if (this->needOpBytes(4 + SkAlign4(size))) {
+ this->writeOp(kDrawData_DrawOp, 0, data);
+ if (0 == data) {
+ fWriter.write32(size);
+ }
+ fWriter.writePad(ptr, size);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> uint32_t castToU32(T value) {
+ union {
+ T fSrc;
+ uint32_t fDst;
+ } data;
+ data.fSrc = value;
+ return data.fDst;
+}
+
+void SkGPipeCanvas::writePaint(const SkPaint& paint) {
+ SkPaint& base = fPaint;
+ uint32_t storage[32];
+ uint32_t* ptr = storage;
+
+ if (base.getFlags() != paint.getFlags()) {
+ *ptr++ = PaintOp_packOpData(kFlags_PaintOp, paint.getFlags());
+ base.setFlags(paint.getFlags());
+ }
+ if (base.getColor() != paint.getColor()) {
+ *ptr++ = PaintOp_packOp(kColor_PaintOp);
+ *ptr++ = paint.getColor();
+ base.setColor(paint.getColor());
+ }
+ if (base.getStyle() != paint.getStyle()) {
+ *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle());
+ base.setStyle(paint.getStyle());
+ }
+ if (base.getStrokeJoin() != paint.getStrokeJoin()) {
+ *ptr++ = PaintOp_packOpData(kJoin_PaintOp, paint.getStrokeJoin());
+ base.setStrokeJoin(paint.getStrokeJoin());
+ }
+ if (base.getStrokeCap() != paint.getStrokeCap()) {
+ *ptr++ = PaintOp_packOpData(kCap_PaintOp, paint.getStrokeCap());
+ base.setStrokeCap(paint.getStrokeCap());
+ }
+ if (base.getStrokeWidth() != paint.getStrokeWidth()) {
+ *ptr++ = PaintOp_packOp(kWidth_PaintOp);
+ *ptr++ = castToU32(paint.getStrokeWidth());
+ base.setStrokeWidth(paint.getStrokeWidth());
+ }
+ if (base.getStrokeMiter() != paint.getStrokeMiter()) {
+ *ptr++ = PaintOp_packOp(kMiter_PaintOp);
+ *ptr++ = castToU32(paint.getStrokeMiter());
+ base.setStrokeMiter(paint.getStrokeMiter());
+ }
+ if (base.getTextEncoding() != paint.getTextEncoding()) {
+ *ptr++ = PaintOp_packOpData(kEncoding_PaintOp, paint.getTextEncoding());
+ base.setTextEncoding(paint.getTextEncoding());
+ }
+ if (base.getHinting() != paint.getHinting()) {
+ *ptr++ = PaintOp_packOpData(kHinting_PaintOp, paint.getHinting());
+ base.setHinting(paint.getHinting());
+ }
+ if (base.getTextAlign() != paint.getTextAlign()) {
+ *ptr++ = PaintOp_packOpData(kAlign_PaintOp, paint.getTextAlign());
+ base.setTextAlign(paint.getTextAlign());
+ }
+ if (base.getTextSize() != paint.getTextSize()) {
+ *ptr++ = PaintOp_packOp(kTextSize_PaintOp);
+ *ptr++ = castToU32(paint.getTextSize());
+ base.setTextSize(paint.getTextSize());
+ }
+ if (base.getTextScaleX() != paint.getTextScaleX()) {
+ *ptr++ = PaintOp_packOp(kTextScaleX_PaintOp);
+ *ptr++ = castToU32(paint.getTextScaleX());
+ base.setTextScaleX(paint.getTextScaleX());
+ }
+ if (base.getTextSkewX() != paint.getTextSkewX()) {
+ *ptr++ = PaintOp_packOp(kTextSkewX_PaintOp);
+ *ptr++ = castToU32(paint.getTextSkewX());
+ base.setTextSkewX(paint.getTextSkewX());
+ }
+
+ if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) {
+ uint32_t id = this->getTypefaceID(paint.getTypeface());
+ *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id);
+ base.setTypeface(paint.getTypeface());
+ }
+
+ for (int i = 0; i < kCount_PaintFlats; i++) {
+ int index = this->flattenToIndex(get_paintflat(paint, i), (PaintFlats)i);
+ SkASSERT(index >= 0 && index <= fFlatArray.count());
+ if (index != fCurrFlatIndex[i]) {
+ *ptr++ = PaintOp_packOpFlagData(kFlatIndex_PaintOp, i, index);
+ fCurrFlatIndex[i] = index;
+ }
+ }
+
+ size_t size = (char*)ptr - (char*)storage;
+ if (size && this->needOpBytes(size)) {
+ this->writeOp(kPaintOp_DrawOp, 0, size);
+ fWriter.write(storage, size);
+ for (size_t i = 0; i < size/4; i++) {
+// SkDebugf("[%d] %08X\n", i, storage[i]);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeWriter::SkGPipeWriter() : fWriter(0) {
+ fCanvas = NULL;
+}
+
+SkGPipeWriter::~SkGPipeWriter() {
+ this->endRecording();
+ SkSafeUnref(fCanvas);
+}
+
+SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller,
+ uint32_t flags) {
+ if (NULL == fCanvas) {
+ fWriter.reset(NULL, 0);
+ fFactorySet.reset();
+ fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter,
+ (flags & kCrossProcess_Flag) ?
+ &fFactorySet : NULL));
+ }
+ return fCanvas;
+}
+
+void SkGPipeWriter::endRecording() {
+ if (fCanvas) {
+ fCanvas->finish();
+ fCanvas->unref();
+ fCanvas = NULL;
+ }
+}
+
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index b3cc7832e5..de1ce737d4 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -72,6 +72,14 @@
using namespace skia_advanced_typeface_metrics_utils;
+// SK_FREETYPE_LCD_LERP should be 0...256
+// 0 means no color reduction (e.g. just as returned from FreeType)
+// 256 means 100% color reduction (e.g. gray)
+//
+#ifndef SK_FREETYPE_LCD_LERP
+ #define SK_FREETYPE_LCD_LERP 96
+#endif
+
//////////////////////////////////////////////////////////////////////////
struct SkFaceRec;
@@ -906,7 +914,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
}
switch ( fFace->glyph->format ) {
- case FT_GLYPH_FORMAT_OUTLINE:
+ case FT_GLYPH_FORMAT_OUTLINE: {
FT_BBox bbox;
if (fRec.fFlags & kEmbolden_Flag) {
@@ -934,6 +942,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
glyph->fTop = -SkToS16(bbox.yMax >> 6);
glyph->fLeft = SkToS16(bbox.xMin >> 6);
break;
+ }
case FT_GLYPH_FORMAT_BITMAP:
if (fRec.fFlags & kEmbolden_Flag) {
@@ -979,6 +988,22 @@ extern void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bi
using namespace skia_freetype_support;
#endif
+static int lerp(int start, int end) {
+ SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
+ return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
+}
+
+static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
+ if (SK_FREETYPE_LCD_LERP) {
+ // want (a+b+c)/3, but we approx to avoid the divide
+ unsigned ave = (5 * (r + g + b) + b) >> 4;
+ r = lerp(r, ave);
+ g = lerp(g, ave);
+ b = lerp(b, ave);
+ }
+ return SkPackRGB16(r >> 3, g >> 2, b >> 3);
+}
+
static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap) {
SkASSERT(glyph.fWidth * 3 == bitmap.width - 6);
SkASSERT(glyph.fHeight == bitmap.rows);
@@ -991,7 +1016,7 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap) {
for (int y = 0; y < glyph.fHeight; y++) {
const uint8_t* triple = src;
for (int x = 0; x < width; x++) {
- dst[x] = SkPackRGB16(triple[0] >> 3, triple[1] >> 2, triple[2] >> 3);
+ dst[x] = packTriple(triple[0], triple[1], triple[2]);
triple += 3;
}
src += bitmap.pitch;
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
index 9aabce7db9..37c2c35eb2 100644
--- a/src/ports/SkFontHost_linux.cpp
+++ b/src/ports/SkFontHost_linux.cpp
@@ -391,7 +391,7 @@ static SkTypeface* gDefaultNormal;
static void load_system_fonts() {
// check if we've already be called
if (NULL != gDefaultNormal) {
- printf("---- default font %p\n", gDefaultNormal);
+// printf("---- default font %p\n", gDefaultNormal);
return;
}
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
index e6378777ce..ad57e16f91 100644
--- a/src/ports/SkFontHost_mac_coretext.cpp
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -18,13 +18,17 @@
#include "SkFontHost.h"
#include "SkDescriptor.h"
+#include "SkEndian.h"
#include "SkFloatingPoint.h"
#include "SkPaint.h"
#include "SkString.h"
+#include "SkStream.h"
#include "SkTypeface_mac.h"
#include "SkUtils.h"
#include "SkTypefaceCache.h"
+using namespace skia_advanced_typeface_metrics_utils;
+
static const size_t FONT_CACHE_MEMORY_BUDGET = 1024 * 1024;
static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
@@ -400,6 +404,28 @@ static void bytes_to_bits(uint8_t dst[], const uint8_t src[], int count) {
}
}
+#if 1
+static inline int r32_to_16(int x) { return SkR32ToR16(x); }
+static inline int g32_to_16(int x) { return SkG32ToG16(x); }
+static inline int b32_to_16(int x) { return SkB32ToB16(x); }
+#else
+static inline int round8to5(int x) {
+ return (x + 3 - (x >> 5) + (x >> 7)) >> 3;
+}
+static inline int round8to6(int x) {
+ int xx = (x + 1 - (x >> 6) + (x >> 7)) >> 2;
+ SkASSERT((unsigned)xx <= 63);
+
+ int ix = x >> 2;
+ SkASSERT(SkAbs32(xx - ix) <= 1);
+ return xx;
+}
+
+static inline int r32_to_16(int x) { return round8to5(x); }
+static inline int g32_to_16(int x) { return round8to6(x); }
+static inline int b32_to_16(int x) { return round8to5(x); }
+#endif
+
static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
@@ -411,7 +437,7 @@ static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
g = 255 - g;
b = 255 - b;
- return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
+ return SkPackRGB16(r32_to_16(r), g32_to_16(g), b32_to_16(b));
}
#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
@@ -604,12 +630,141 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
return SkFontHost::CreateTypeface(NULL, NULL, NULL, NULL, SkTypeface::kNormal);
}
+// Construct Glyph to Unicode table.
+// Unicode code points that require conjugate pairs in utf16 are not
+// supported.
+static void populate_glyph_to_unicode(CTFontRef ctFont,
+ const unsigned glyphCount, SkTDArray<SkUnichar>* glyphToUnicode) {
+ CFCharacterSetRef charSet = CTFontCopyCharacterSet(ctFont);
+ CFDataRef bitmap = CFCharacterSetCreateBitmapRepresentation(
+ kCFAllocatorDefault, charSet);
+ if (!bitmap) {
+ return;
+ }
+ CFIndex length = CFDataGetLength(bitmap);
+ if (!length) {
+ CFSafeRelease(bitmap);
+ return;
+ }
+ if (length > 8192) {
+ // TODO: Add support for Unicode above 0xFFFF
+ // Consider only the BMP portion of the Unicode character points.
+ // The bitmap may contain other planes, up to plane 16.
+ // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
+ length = 8192;
+ }
+ const UInt8* bits = CFDataGetBytePtr(bitmap);
+ glyphToUnicode->setCount(glyphCount);
+ SkUnichar* out = glyphToUnicode->begin();
+ sk_bzero(out, glyphCount * sizeof(SkUnichar));
+ for (int i = 0; i < length; i++) {
+ int mask = bits[i];
+ if (!mask) {
+ continue;
+ }
+ for (int j = 0; j < 8; j++) {
+ CGGlyph glyph;
+ UniChar unichar = static_cast<UniChar>((i << 3) + j);
+ if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont,
+ &unichar, &glyph, 1)) {
+ out[glyph] = unichar;
+ }
+ }
+ }
+ CFSafeRelease(bitmap);
+}
+
+static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
+ CGSize advance;
+ advance.width = 0;
+ CGGlyph glyph = gId;
+ CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph,
+ &advance, 1);
+ *data = advance.width;
+ return true;
+}
+
// static
SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
uint32_t fontID,
SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
- SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
- return NULL;
+ CTFontRef ctFont = GetFontRefFromFontID(fontID);
+ SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+ CFStringRef fontName = CTFontCopyPostScriptName(ctFont);
+ int length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(fontName),
+ kCFStringEncodingUTF8);
+ info->fFontName.resize(length);
+ CFStringGetCString(fontName, info->fFontName.writable_str(), length,
+ kCFStringEncodingUTF8);
+ info->fMultiMaster = false;
+ CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
+ info->fLastGlyphID = SkToU16(glyphCount - 1);
+ info->fEmSize = CTFontGetUnitsPerEm(ctFont);
+
+ if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+ populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
+ }
+
+ // TODO: get font type, ala:
+ // CFTypeRef attr = CTFontCopyAttribute(ctFont, kCTFontFormatAttribute);
+ info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+ info->fStyle = 0;
+ CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
+ if (symbolicTraits & kCTFontMonoSpaceTrait) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+ }
+ if (symbolicTraits & kCTFontItalicTrait) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+ }
+ CTFontStylisticClass stylisticClass = symbolicTraits &
+ kCTFontClassMaskTrait;
+ if (stylisticClass & kCTFontSymbolicClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+ }
+ if (stylisticClass >= kCTFontOldStyleSerifsClass
+ && stylisticClass <= kCTFontSlabSerifsClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+ } else if (stylisticClass & kCTFontScriptsClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+ }
+ info->fItalicAngle = CTFontGetSlantAngle(ctFont);
+ info->fAscent = CTFontGetAscent(ctFont);
+ info->fDescent = CTFontGetDescent(ctFont);
+ info->fCapHeight = CTFontGetCapHeight(ctFont);
+ CGRect bbox = CTFontGetBoundingBox(ctFont);
+ info->fBBox = SkIRect::MakeXYWH(bbox.origin.x, bbox.origin.y,
+ bbox.size.width, bbox.size.height);
+
+ // Figure out a good guess for StemV - Min width of i, I, !, 1.
+ // This probably isn't very good with an italic font.
+ int16_t min_width = SHRT_MAX;
+ info->fStemV = 0;
+ static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
+ const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
+ CGGlyph glyphs[count];
+ CGRect boundingRects[count];
+ if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
+ CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
+ glyphs, boundingRects, count);
+ for (size_t i = 0; i < count; i++) {
+ int16_t width = boundingRects[i].size.width;
+ if (width > 0 && width < min_width) {
+ min_width = width;
+ info->fStemV = min_width;
+ }
+ }
+ }
+
+ if (false) { // TODO: haven't figured out how to know if font is embeddable
+ // (information is in the OS/2 table)
+ info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+ } else if (perGlyphInfo &
+ SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+ info->fGlyphWidths.reset(
+ getAdvanceData(ctFont, glyphCount, &getWidthAdvance));
+ }
+
+ return info;
}
///////////////////////////////////////////////////////////////////////////////
@@ -618,9 +773,88 @@ bool SkFontHost::ValidFontID(SkFontID fontID) {
return SkTypefaceCache::FindByID(fontID) != NULL;
}
+struct FontHeader {
+ SkFixed fVersion;
+ uint16_t fNumTables;
+ uint16_t fSearchRange;
+ uint16_t fEntrySelector;
+ uint16_t fRangeShift;
+};
+
+struct TableEntry {
+ uint32_t fTag;
+ uint32_t fCheckSum;
+ uint32_t fOffset;
+ uint32_t fLength;
+};
+
+static uint32 CalcTableCheckSum(uint32 *table, uint32 numberOfBytesInTable) {
+ uint32 sum = 0;
+ uint32 nLongs = (numberOfBytesInTable + 3) / 4;
+
+ while (nLongs-- > 0) {
+ sum += SkEndian_SwapBE32(*table++);
+ }
+ return sum;
+}
+
SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
- SkASSERT(!"SkFontHost::OpenStream unimplemented");
- return(NULL);
+ // get table tags
+ int tableCount = CountTables(uniqueID);
+ SkTDArray<SkFontTableTag> tableTags;
+ tableTags.setCount(tableCount);
+ GetTableTags(uniqueID, tableTags.begin());
+
+ // calc total size for font, save sizes
+ SkTDArray<size_t> tableSizes;
+ size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount;
+ for (int index = 0; index < tableCount; ++index) {
+ size_t tableSize = GetTableSize(uniqueID, tableTags[index]);
+ totalSize += (tableSize + 3) & ~3;
+ *tableSizes.append() = tableSize;
+ }
+
+ // reserve memory for stream, and zero it (tables must be zero padded)
+ SkMemoryStream* stream = new SkMemoryStream(totalSize);
+ char* dataStart = (char*)stream->getMemoryBase();
+ sk_bzero(dataStart, totalSize);
+ char* dataPtr = dataStart;
+
+ // compute font header entries
+ uint16_t entrySelector = 0;
+ uint16_t searchRange = 1;
+ while (searchRange < tableCount >> 1) {
+ entrySelector++;
+ searchRange <<= 1;
+ }
+ searchRange <<= 4;
+ uint16_t rangeShift = (tableCount << 4) - searchRange;
+
+ // write font header (also called sfnt header, offset subtable)
+ FontHeader* offsetTable = (FontHeader*)dataPtr;
+ offsetTable->fVersion = SkEndian_SwapBE32(SK_Fixed1);
+ offsetTable->fNumTables = SkEndian_SwapBE16(tableCount);
+ offsetTable->fSearchRange = SkEndian_SwapBE16(searchRange);
+ offsetTable->fEntrySelector = SkEndian_SwapBE16(entrySelector);
+ offsetTable->fRangeShift = SkEndian_SwapBE16(rangeShift);
+ dataPtr += sizeof(FontHeader);
+
+ // write tables
+ TableEntry* entry = (TableEntry*)dataPtr;
+ dataPtr += sizeof(TableEntry) * tableCount;
+ for (int index = 0; index < tableCount; ++index) {
+ size_t tableSize = tableSizes[index];
+ GetTableData(uniqueID, tableTags[index], 0, tableSize, dataPtr);
+ entry->fTag = SkEndian_SwapBE32(tableTags[index]);
+ entry->fCheckSum = SkEndian_SwapBE32(CalcTableCheckSum(
+ (uint32*)dataPtr, tableSize));
+ entry->fOffset = SkEndian_SwapBE32(dataPtr - dataStart);
+ entry->fLength = SkEndian_SwapBE32(tableSize);
+ dataPtr += (tableSize + 3) & ~3;
+ ++entry;
+ }
+
+ return stream;
}
size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index d9404f3943..f5d126ea6f 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -83,7 +83,8 @@ static inline FIXED SkScalarToFIXED(SkScalar x) {
static unsigned calculateGlyphCount(HDC hdc) {
// The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
- const DWORD maxpTag = *(DWORD*) "maxp";
+ const DWORD maxpTag =
+ SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p'));
uint16_t glyphs;
if (GetFontData(hdc, maxpTag, 4, &glyphs, sizeof(glyphs)) != GDI_ERROR) {
return SkEndian_SwapBE16(glyphs);
@@ -271,6 +272,10 @@ private:
HFONT fFont;
SCRIPT_CACHE fSC;
int fGlyphCount;
+
+ HFONT fHiResFont;
+ MAT2 fMat22Identity;
+ SkMatrix fHiResMatrix;
};
static float mul2float(SkScalar a, SkScalar b) {
@@ -283,6 +288,16 @@ static FIXED float2FIXED(float x) {
static SkMutex gFTMutex;
+#define HIRES_TEXTSIZE 2048
+#define HIRES_SHIFT 11
+static inline SkFixed HiResToFixed(int value) {
+ return value << (16 - HIRES_SHIFT);
+}
+
+static bool needHiResMetrics(const SkScalar mat[2][2]) {
+ return mat[1][0] || mat[0][1];
+}
+
SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
: SkScalerContext(desc), fDDC(0), fFont(0), fSavefont(0), fSC(0)
, fGlyphCount(-1) {
@@ -303,6 +318,7 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
fMat22.eM22 = float2FIXED(-fXform.eM22);
fDDC = ::CreateCompatibleDC(NULL);
+ SetGraphicsMode(fDDC, GM_ADVANCED);
SetBkMode(fDDC, TRANSPARENT);
// Scaling by the DPI is inconsistent with how Skia draws elsewhere
@@ -311,6 +327,21 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
GetLogFontByID(fRec.fFontID, &lf);
lf.lfHeight = -gCanonicalTextSize;
fFont = CreateFontIndirect(&lf);
+
+ // if we're rotated, or want fractional widths, create a hires font
+ fHiResFont = 0;
+ if (needHiResMetrics(fRec.fPost2x2) || (fRec.fFlags & kSubpixelPositioning_Flag)) {
+ lf.lfHeight = -HIRES_TEXTSIZE;
+ fHiResFont = CreateFontIndirect(&lf);
+
+ fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1);
+ fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0);
+
+ // construct a matrix to go from HIRES logical units to our device units
+ fRec.getSingleMatrix(&fHiResMatrix);
+ SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE));
+ fHiResMatrix.preScale(scale, scale);
+ }
fSavefont = (HFONT)SelectObject(fDDC, fFont);
}
@@ -322,6 +353,9 @@ SkScalerContext_Windows::~SkScalerContext_Windows() {
if (fFont) {
::DeleteObject(fFont);
}
+ if (fHiResFont) {
+ ::DeleteObject(fHiResFont);
+ }
if (fSC) {
::ScriptFreeCache(&fSC);
}
@@ -368,7 +402,7 @@ void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
SkASSERT(fDDC);
GLYPHMETRICS gm;
- memset(&gm, 0, sizeof(gm));
+ sk_bzero(&gm, sizeof(gm));
glyph->fRsbDelta = 0;
glyph->fLsbDelta = 0;
@@ -399,6 +433,19 @@ void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
glyph->fTop -= 1;
glyph->fLeft -= 1;
}
+
+ if (fHiResFont) {
+ SelectObject(fDDC, fHiResFont);
+ sk_bzero(&gm, sizeof(gm));
+ ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
+ if (GDI_ERROR != ret) {
+ SkPoint advance;
+ fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
+ glyph->fAdvanceX = SkScalarToFixed(advance.fX);
+ glyph->fAdvanceY = SkScalarToFixed(advance.fY);
+ }
+ SelectObject(fDDC, fFont);
+ }
} else {
glyph->fWidth = 0;
}
@@ -453,34 +500,53 @@ static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
}
+static int alignTo32(int n) {
+ return (n + 31) & ~31;
+}
+
+struct MyBitmapInfo : public BITMAPINFO {
+ RGBQUAD fMoreSpaceForColors[1];
+};
+
void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
SkAutoMutexAcquire ac(gFTMutex);
SkASSERT(fDDC);
- if (SkMask::kLCD16_Format == fRec.fMaskFormat) {
+ const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+ if ((SkMask::kLCD16_Format == fRec.fMaskFormat) || isBW) {
HDC dc = CreateCompatibleDC(0);
void* bits = 0;
- BITMAPINFO info;
+ int biWidth = isBW ? alignTo32(glyph.fWidth) : glyph.fWidth;
+ MyBitmapInfo info;
sk_bzero(&info, sizeof(info));
+ if (isBW) {
+ RGBQUAD blackQuad = { 0, 0, 0, 0 };
+ RGBQUAD whiteQuad = { 0xFF, 0xFF, 0xFF, 0 };
+ info.bmiColors[0] = blackQuad;
+ info.bmiColors[1] = whiteQuad;
+ }
info.bmiHeader.biSize = sizeof(info.bmiHeader);
- info.bmiHeader.biWidth = glyph.fWidth;
+ info.bmiHeader.biWidth = biWidth;
info.bmiHeader.biHeight = glyph.fHeight;
info.bmiHeader.biPlanes = 1;
- info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biBitCount = isBW ? 1 : 32;
info.bmiHeader.biCompression = BI_RGB;
+ if (isBW) {
+ info.bmiHeader.biClrUsed = 2;
+ }
HBITMAP bm = CreateDIBSection(dc, &info, DIB_RGB_COLORS, &bits, 0, 0);
SelectObject(dc, bm);
// erase to white
- size_t srcRB = glyph.fWidth << 2;
+ size_t srcRB = isBW ? (biWidth >> 3) : (glyph.fWidth << 2);
size_t size = glyph.fHeight * srcRB;
- memset(bits, 0xFF, size);
+ memset(bits, isBW ? 0 : 0xFF, size);
+ SetGraphicsMode(dc, GM_ADVANCED);
SetBkMode(dc, TRANSPARENT);
SetTextAlign(dc, TA_LEFT | TA_BASELINE);
- SetGraphicsMode(dc, GM_ADVANCED);
XFORM xform = fXform;
xform.eDx = (float)-glyph.fLeft;
@@ -488,7 +554,7 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
SetWorldTransform(dc, &xform);
HGDIOBJ prevFont = SelectObject(dc, fFont);
- COLORREF color = SetTextColor(dc, 0); // black
+ COLORREF color = SetTextColor(dc, isBW ? 0xFFFFFF : 0);
SkASSERT(color != CLR_INVALID);
uint16_t glyphID = glyph.getGlyphID();
#if defined(UNICODE)
@@ -501,15 +567,26 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
// downsample from rgba to rgb565
int width = glyph.fWidth;
size_t dstRB = glyph.rowBytes();
- const uint32_t* src = (const uint32_t*)bits;
- // gdi's bitmap is upside-down, so we reverse dst walking in Y
- uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd16(src[i]);
+ if (isBW) {
+ const uint8_t* src = (const uint8_t*)bits;
+ // gdi's bitmap is upside-down, so we reverse dst walking in Y
+ uint8_t* dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+ for (int y = 0; y < glyph.fHeight; y++) {
+ memcpy(dst, src, dstRB);
+ src += srcRB;
+ dst -= dstRB;
+ }
+ } else { // LCD16
+ const uint32_t* src = (const uint32_t*)bits;
+ // gdi's bitmap is upside-down, so we reverse dst walking in Y
+ uint16_t* dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd16(src[i]);
+ }
+ src = (const uint32_t*)((const char*)src + srcRB);
+ dst = (uint16_t*)((char*)dst - dstRB);
}
- src = (const uint32_t*)((const char*)src + srcRB);
- dst = (uint16_t*)((char*)dst - dstRB);
}
DeleteDC(dc);
@@ -790,7 +867,8 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
}
SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
- const DWORD kTTCTag = *(DWORD*) "ttcf";
+ const DWORD kTTCTag =
+ SkEndian_SwapBE32(SkSetFourByteTag('t', 't', 'c', 'f'));
LOGFONT lf;
GetLogFontByID(uniqueID, &lf);
@@ -896,10 +974,6 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
if (SkMask::kLCD16_Format == rec->fMaskFormat) {
return;
}
- // we never like BW format
- if (SkMask::kBW_Format == rec->fMaskFormat) {
- rec->fMaskFormat = SkMask::kA8_Format;
- }
if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
rec->fMaskFormat = SkMask::kA8_Format;
diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp
index df861780b2..f4ad1987f9 100644
--- a/src/svg/SkSVGParser.cpp
+++ b/src/svg/SkSVGParser.cpp
@@ -76,8 +76,8 @@ void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) {
int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue,
size_t len, bool isPaint) {
const SkSVGAttribute* attributes;
- int count = element->getAttributes(&attributes);
- int result = 0;
+ size_t count = element->getAttributes(&attributes);
+ size_t result = 0;
while (result < count) {
if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) {
SkASSERT(result == (attributes->fOffset -
@@ -200,7 +200,7 @@ bool SkSVGParser::onStartElementLen(const char name[], size_t len) {
} else if (fInSVG == false)
return false;
const char* nextColon = strchr(name, ':');
- if (nextColon && nextColon - name < len)
+ if (nextColon && (size_t)(nextColon - name) < len)
return false;
SkSVGTypes type = GetType(name, len);
// SkASSERT(type >= 0);
diff --git a/src/utils/SkEGLContext_none.cpp b/src/utils/SkEGLContext_none.cpp
index 1c55c95e36..cb08f400ee 100644
--- a/src/utils/SkEGLContext_none.cpp
+++ b/src/utils/SkEGLContext_none.cpp
@@ -1,6 +1,6 @@
#include "SkEGLContext.h"
-SkEGLContext::SkEGLContext() : fContext(NULL) {
+SkEGLContext::SkEGLContext() {
}
SkEGLContext::~SkEGLContext() {
diff --git a/src/utils/mac/SkBitmap_Mac.cpp b/src/utils/mac/SkBitmap_Mac.cpp
new file mode 100644
index 0000000000..06c2b2701a
--- /dev/null
+++ b/src/utils/mac/SkBitmap_Mac.cpp
@@ -0,0 +1,142 @@
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkMath.h"
+
+#if defined(SK_BUILD_FOR_MAC) && !defined(SK_USE_WXWIDGETS)
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#ifndef __ppc__
+ #define SWAP_16BIT
+#endif
+
+static void convertGL32_to_Mac32(uint32_t dst[], const SkBitmap& bm) {
+ memcpy(dst, bm.getPixels(), bm.getSize());
+ return;
+
+ uint32_t* stop = dst + (bm.getSize() >> 2);
+ const uint8_t* src = (const uint8_t*)bm.getPixels();
+ while (dst < stop) {
+ *dst++ = src[2] << 24 | src[1] << 16 | src[0] << 8 | src[3] << 0;
+ src += sizeof(uint32_t);
+ }
+}
+
+static void convert565_to_32(uint32_t dst[], const SkBitmap& bm) {
+ for (int y = 0; y < bm.height(); y++) {
+ const uint16_t* src = bm.getAddr16(0, y);
+ const uint16_t* stop = src + bm.width();
+ while (src < stop) {
+ unsigned c = *src++;
+ unsigned r = SkPacked16ToR32(c);
+ unsigned g = SkPacked16ToG32(c);
+ unsigned b = SkPacked16ToB32(c);
+
+ *dst++ = (b << 24) | (g << 16) | (r << 8) | 0xFF;
+ }
+ }
+}
+
+static void convert4444_to_555(uint16_t dst[], const uint16_t src[], int count)
+{
+ const uint16_t* stop = src + count;
+
+ while (src < stop)
+ {
+ unsigned c = *src++;
+
+ unsigned r = SkGetPackedR4444(c);
+ unsigned g = SkGetPackedG4444(c);
+ unsigned b = SkGetPackedB4444(c);
+ // convert to 5 bits
+ r = (r << 1) | (r >> 3);
+ g = (g << 1) | (g >> 3);
+ b = (b << 1) | (b >> 3);
+ // build the 555
+ c = (r << 10) | (g << 5) | b;
+
+#ifdef SWAP_16BIT
+ c = (c >> 8) | (c << 8);
+#endif
+ *dst++ = c;
+ }
+}
+
+#include "SkTemplates.h"
+
+static CGImageRef bitmap2imageref(const SkBitmap& bm) {
+ size_t bitsPerComp;
+ size_t bitsPerPixel;
+ CGBitmapInfo info;
+ CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ CGDataProviderRef data = CGDataProviderCreateWithData(NULL,
+ bm.getPixels(),
+ bm.getSize(),
+ NULL);
+ SkAutoTCallVProc<CGDataProvider, CGDataProviderRelease> acp(data);
+ SkAutoTCallVProc<CGColorSpace, CGColorSpaceRelease> acp2(cs);
+
+ switch (bm.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ bitsPerComp = 8;
+ bitsPerPixel = 32;
+ info = kCGImageAlphaPremultipliedLast;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ bitsPerComp = 4;
+ bitsPerPixel = 16;
+ info = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder16Little;
+ break;
+#if 0 // not supported by quartz !!!
+ case SkBitmap::kRGB_565_Config:
+ bitsPerComp = 5;
+ bitsPerPixel = 16;
+ info = kCGImageAlphaNone | kCGBitmapByteOrder16Little;
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ return CGImageCreate(bm.width(), bm.height(), bitsPerComp, bitsPerPixel,
+ bm.rowBytes(), cs, info, data,
+ NULL, false, kCGRenderingIntentDefault);
+}
+
+void SkBitmap::drawToPort(WindowRef wind, CGContextRef cg) const {
+ if (fPixels == NULL || fWidth == 0 || fHeight == 0) {
+ return;
+ }
+
+ bool useQD = false;
+ if (NULL == cg) {
+ SetPortWindowPort(wind);
+ QDBeginCGContext(GetWindowPort(wind), &cg);
+ useQD = true;
+ }
+
+ SkBitmap bm;
+ if (this->config() == kRGB_565_Config) {
+ this->copyTo(&bm, kARGB_8888_Config);
+ } else {
+ bm = *this;
+ }
+ bm.lockPixels();
+
+ CGImageRef image = bitmap2imageref(bm);
+ if (image) {
+ CGRect rect;
+ rect.origin.x = rect.origin.y = 0;
+ rect.size.width = bm.width();
+ rect.size.height = bm.height();
+
+ CGContextDrawImage(cg, rect, image);
+ CGImageRelease(image);
+ }
+
+ if (useQD) {
+ QDEndCGContext(GetWindowPort(wind), &cg);
+ }
+}
+
+#endif
diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp
new file mode 100644
index 0000000000..f4bda45c9a
--- /dev/null
+++ b/src/utils/mac/SkCreateCGImageRef.cpp
@@ -0,0 +1,129 @@
+#include "SkCGUtils.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+
+static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
+ delete bitmap;
+}
+
+#define HAS_ARGB_SHIFTS(a, r, g, b) \
+ (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
+ && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
+
+static SkBitmap* prepareForImageRef(const SkBitmap& bm,
+ size_t* bitsPerComponent,
+ CGBitmapInfo* info) {
+ bool upscaleTo32 = false;
+
+ switch (bm.config()) {
+ case SkBitmap::kRGB_565_Config:
+ upscaleTo32 = true;
+ // fall through
+ case SkBitmap::kARGB_8888_Config:
+ *bitsPerComponent = 8;
+#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \
+ || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8)
+ *info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
+#elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \
+ || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
+ // Matches the CGBitmapInfo that Apple recommends for best
+ // performance, used by google chrome.
+ *info = kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst;
+#else
+// ...add more formats as required...
+#warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \
+ This will probably not work.
+ // Legacy behavior. Perhaps turn this into an error at some
+ // point.
+ *info = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
+#endif
+ break;
+#if 0
+ case SkBitmap::kRGB_565_Config:
+ // doesn't see quite right. Are they thinking 1555?
+ *bitsPerComponent = 5;
+ *info = kCGBitmapByteOrder16Little;
+ break;
+#endif
+ case SkBitmap::kARGB_4444_Config:
+ *bitsPerComponent = 4;
+ *info = kCGBitmapByteOrder16Little | kCGImageAlphaPremultipliedLast;
+ break;
+ default:
+ return NULL;
+ }
+
+ SkBitmap* copy;
+ if (upscaleTo32) {
+ copy = new SkBitmap;
+ // here we make a ceep copy of the pixels, since CG won't take our
+ // 565 directly
+ bm.copyTo(copy, SkBitmap::kARGB_8888_Config);
+ } else {
+ copy = new SkBitmap(bm);
+ }
+ return copy;
+}
+
+#undef HAS_ARGB_SHIFTS
+
+CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
+ CGColorSpaceRef colorSpace) {
+ size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
+ CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
+
+ SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
+ if (NULL == bitmap) {
+ return NULL;
+ }
+
+ const int w = bitmap->width();
+ const int h = bitmap->height();
+ const size_t s = bitmap->getSize();
+
+ // our provider "owns" the bitmap*, and will take care of deleting it
+ // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
+ // proc, which will in turn unlock the pixels
+ bitmap->lockPixels();
+ CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
+ SkBitmap_ReleaseInfo);
+
+ bool releaseColorSpace = false;
+ if (NULL == colorSpace) {
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+ releaseColorSpace = true;
+ }
+
+ CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
+ bitmap->bytesPerPixel() * 8,
+ bitmap->rowBytes(), colorSpace, info, dataRef,
+ NULL, false, kCGRenderingIntentDefault);
+
+ if (releaseColorSpace) {
+ CGColorSpaceRelease(colorSpace);
+ }
+ CGDataProviderRelease(dataRef);
+ return ref;
+}
+
+void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
+ CGImageRef img = SkCreateCGImageRef(bm);
+
+ if (img) {
+ CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
+
+ CGContextSaveGState(cg);
+ CGContextTranslateCTM(cg, x, r.size.height + y);
+ CGContextScaleCTM(cg, 1, -1);
+
+ CGContextDrawImage(cg, r, img);
+
+ CGContextRestoreGState(cg);
+
+ CGImageRelease(img);
+ }
+}
+
+
+
diff --git a/src/utils/mac/SkEGLContext_mac.cpp b/src/utils/mac/SkEGLContext_mac.cpp
new file mode 100644
index 0000000000..e601f35617
--- /dev/null
+++ b/src/utils/mac/SkEGLContext_mac.cpp
@@ -0,0 +1,68 @@
+#include "SkEGLContext.h"
+//#include "SkTypes.h"
+#include <AGL/agl.h>
+
+SkEGLContext::SkEGLContext() : context(NULL) {
+}
+
+SkEGLContext::~SkEGLContext() {
+ if (this->context) {
+ aglDestroyContext(this->context);
+ }
+}
+
+bool SkEGLContext::init(int width, int height) {
+ GLint major, minor;
+ AGLContext ctx;
+
+ aglGetVersion(&major, &minor);
+ //SkDebugf("---- agl version %d %d\n", major, minor);
+
+ const GLint pixelAttrs[] = {
+ AGL_RGBA,
+ AGL_STENCIL_SIZE, 8,
+/*
+ AGL_SAMPLE_BUFFERS_ARB, 1,
+ AGL_MULTISAMPLE,
+ AGL_SAMPLES_ARB, 2,
+*/
+ AGL_ACCELERATED,
+ AGL_NONE
+ };
+ AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs);
+ //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs);
+ //SkDebugf("----- agl format %p\n", format);
+ ctx = aglCreateContext(format, NULL);
+ //SkDebugf("----- agl context %p\n", ctx);
+ aglDestroyPixelFormat(format);
+
+/*
+ static const GLint interval = 1;
+ aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval);
+*/
+
+ aglSetCurrentContext(ctx);
+ this->context = ctx;
+
+ // Now create our FBO render target
+
+ GLuint fboID;
+ GLuint cbID;
+ GLuint dsID;
+ glGenFramebuffersEXT(1, &fboID);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
+ glGenRenderbuffers(1, &cbID);
+ glBindRenderbuffer(GL_RENDERBUFFER, cbID);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+ glGenRenderbuffers(1, &dsID);
+ glBindRenderbuffer(GL_RENDERBUFFER, dsID);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+ glViewport(0, 0, width, height);
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ return GL_FRAMEBUFFER_COMPLETE == status;
+}
diff --git a/src/utils/mac/SkOSWindow_Mac.cpp b/src/utils/mac/SkOSWindow_Mac.cpp
new file mode 100644
index 0000000000..f1a2926560
--- /dev/null
+++ b/src/utils/mac/SkOSWindow_Mac.cpp
@@ -0,0 +1,534 @@
+#include "SkTypes.h"
+
+#if defined(SK_BUILD_FOR_MAC) && !defined(SK_USE_WXWIDGETS)
+
+#include <AGL/agl.h>
+
+#include <Carbon/Carbon.h>
+#include "SkCGUtils.h"
+
+#include "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkOSMenu.h"
+#include "SkTime.h"
+
+#include "SkGraphics.h"
+#include <new.h>
+
+static void (*gPrevNewHandler)();
+
+extern "C" {
+ static void sk_new_handler()
+ {
+ if (SkGraphics::SetFontCacheUsed(0))
+ return;
+ if (gPrevNewHandler)
+ gPrevNewHandler();
+ else
+ sk_throw();
+ }
+}
+
+static SkOSWindow* gCurrOSWin;
+static EventTargetRef gEventTarget;
+static EventQueueRef gCurrEventQ;
+
+static OSStatus MyDrawEventHandler(EventHandlerCallRef myHandler,
+ EventRef event, void *userData) {
+ // NOTE: GState is save/restored by the HIView system doing the callback,
+ // so the draw handler doesn't need to do it
+
+ OSStatus status = noErr;
+ CGContextRef context;
+ HIRect bounds;
+
+ // Get the CGContextRef
+ status = GetEventParameter (event, kEventParamCGContextRef,
+ typeCGContextRef, NULL,
+ sizeof (CGContextRef),
+ NULL,
+ &context);
+
+ if (status != noErr) {
+ SkDebugf("Got error %d getting the context!\n", status);
+ return status;
+ }
+
+ // Get the bounding rectangle
+ HIViewGetBounds ((HIViewRef) userData, &bounds);
+
+ gCurrOSWin->doPaint(context);
+ return status;
+}
+
+#define SK_MacEventClass FOUR_CHAR_CODE('SKec')
+#define SK_MacEventKind FOUR_CHAR_CODE('SKek')
+#define SK_MacEventParamName FOUR_CHAR_CODE('SKev')
+#define SK_MacEventSinkIDParamName FOUR_CHAR_CODE('SKes')
+
+static void set_bindingside(HISideBinding* side, HIViewRef parent, HIBindingKind kind) {
+ side->toView = parent;
+ side->kind = kind;
+ side->offset = 0;
+}
+
+static void set_axisscale(HIAxisScale* axis, HIViewRef parent) {
+ axis->toView = parent;
+ axis->kind = kHILayoutScaleAbsolute;
+ axis->ratio = 1;
+}
+
+static void set_axisposition(HIAxisPosition* pos, HIViewRef parent, HIPositionKind kind) {
+ pos->toView = parent;
+ pos->kind = kind;
+ pos->offset = 0;
+}
+
+SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd), fAGLCtx(NULL)
+{
+ OSStatus result;
+ WindowRef wr = (WindowRef)hWnd;
+
+ HIViewRef imageView, parent;
+ HIViewRef rootView = HIViewGetRoot(wr);
+ HIViewFindByID(rootView, kHIViewWindowContentID, &parent);
+ result = HIImageViewCreate(NULL, &imageView);
+ SkASSERT(result == noErr);
+
+ result = HIViewAddSubview(parent, imageView);
+ SkASSERT(result == noErr);
+
+ fHVIEW = imageView;
+
+ HIViewSetVisible(imageView, true);
+ HIViewPlaceInSuperviewAt(imageView, 0, 0);
+
+ if (true) {
+ HILayoutInfo layout;
+ layout.version = kHILayoutInfoVersionZero;
+ set_bindingside(&layout.binding.left, parent, kHILayoutBindLeft);
+ set_bindingside(&layout.binding.top, parent, kHILayoutBindTop);
+ set_bindingside(&layout.binding.right, parent, kHILayoutBindRight);
+ set_bindingside(&layout.binding.bottom, parent, kHILayoutBindBottom);
+ set_axisscale(&layout.scale.x, parent);
+ set_axisscale(&layout.scale.y, parent);
+ set_axisposition(&layout.position.x, parent, kHILayoutPositionLeft);
+ set_axisposition(&layout.position.y, rootView, kHILayoutPositionTop);
+ HIViewSetLayoutInfo(imageView, &layout);
+ }
+
+ HIImageViewSetOpaque(imageView, true);
+ HIImageViewSetScaleToFit(imageView, false);
+
+ static const EventTypeSpec gTypes[] = {
+ { kEventClassKeyboard, kEventRawKeyDown },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseMoved },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
+ { kEventClassWindow, kEventWindowBoundsChanged },
+// { kEventClassWindow, kEventWindowDrawContent },
+ { SK_MacEventClass, SK_MacEventKind }
+ };
+
+ EventHandlerUPP handlerUPP = NewEventHandlerUPP(SkOSWindow::EventHandler);
+ int count = SK_ARRAY_COUNT(gTypes);
+
+ result = InstallEventHandler(GetWindowEventTarget(wr), handlerUPP,
+ count, gTypes, this, nil);
+ SkASSERT(result == noErr);
+
+ gCurrOSWin = this;
+ gCurrEventQ = GetCurrentEventQueue();
+ gEventTarget = GetWindowEventTarget(wr);
+
+ static bool gOnce = true;
+ if (gOnce) {
+ gOnce = false;
+ gPrevNewHandler = set_new_handler(sk_new_handler);
+ }
+}
+
+void SkOSWindow::doPaint(void* ctx)
+{
+#if 0
+ this->update(NULL);
+
+ const SkBitmap& bm = this->getBitmap();
+ CGImageRef img = SkCreateCGImageRef(bm);
+
+ if (img) {
+ CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
+
+ CGContextRef cg = reinterpret_cast<CGContextRef>(ctx);
+
+ CGContextSaveGState(cg);
+ CGContextTranslateCTM(cg, 0, r.size.height);
+ CGContextScaleCTM(cg, 1, -1);
+
+ CGContextDrawImage(cg, r, img);
+
+ CGContextRestoreGState(cg);
+
+ CGImageRelease(img);
+ }
+#endif
+}
+
+void SkOSWindow::updateSize()
+{
+ Rect r;
+
+ GetWindowBounds((WindowRef)fHWND, kWindowContentRgn, &r);
+ this->resize(r.right - r.left, r.bottom - r.top);
+
+#if 0
+ HIRect frame;
+ HIViewRef imageView = (HIViewRef)getHVIEW();
+ HIViewRef parent = HIViewGetSuperview(imageView);
+
+ HIViewGetBounds(imageView, &frame);
+ SkDebugf("------ %d bounds %g %g %g %g\n", r.right - r.left,
+ frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
+#endif
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& r)
+{
+ SkEvent* evt = new SkEvent("inval-imageview");
+ evt->post(this->getSinkID());
+}
+
+bool SkOSWindow::onEvent(const SkEvent& evt) {
+ if (evt.isType("inval-imageview")) {
+ this->update(NULL);
+
+ const SkBitmap& bm = this->getBitmap();
+
+ CGImageRef img = SkCreateCGImageRef(bm);
+ HIImageViewSetImage((HIViewRef)getHVIEW(), img);
+ CGImageRelease(img);
+ return true;
+ }
+ return INHERITED::onEvent(evt);
+}
+
+void SkOSWindow::onSetTitle(const char title[])
+{
+ CFStringRef str = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
+ SetWindowTitleWithCFString((WindowRef)fHWND, str);
+ CFRelease(str);
+}
+
+void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
+{
+}
+
+static void getparam(EventRef inEvent, OSType name, OSType type, UInt32 size, void* data)
+{
+ EventParamType actualType;
+ UInt32 actualSize;
+ OSStatus status;
+
+ status = GetEventParameter(inEvent, name, type, &actualType, size, &actualSize, data);
+ SkASSERT(status == noErr);
+ SkASSERT(actualType == type);
+ SkASSERT(actualSize == size);
+}
+
+enum {
+ SK_MacReturnKey = 36,
+ SK_MacDeleteKey = 51,
+ SK_MacEndKey = 119,
+ SK_MacLeftKey = 123,
+ SK_MacRightKey = 124,
+ SK_MacDownKey = 125,
+ SK_MacUpKey = 126,
+
+ SK_Mac0Key = 0x52,
+ SK_Mac1Key = 0x53,
+ SK_Mac2Key = 0x54,
+ SK_Mac3Key = 0x55,
+ SK_Mac4Key = 0x56,
+ SK_Mac5Key = 0x57,
+ SK_Mac6Key = 0x58,
+ SK_Mac7Key = 0x59,
+ SK_Mac8Key = 0x5b,
+ SK_Mac9Key = 0x5c
+};
+
+static SkKey raw2key(UInt32 raw)
+{
+ static const struct {
+ UInt32 fRaw;
+ SkKey fKey;
+ } gKeys[] = {
+ { SK_MacUpKey, kUp_SkKey },
+ { SK_MacDownKey, kDown_SkKey },
+ { SK_MacLeftKey, kLeft_SkKey },
+ { SK_MacRightKey, kRight_SkKey },
+ { SK_MacReturnKey, kOK_SkKey },
+ { SK_MacDeleteKey, kBack_SkKey },
+ { SK_MacEndKey, kEnd_SkKey },
+ { SK_Mac0Key, k0_SkKey },
+ { SK_Mac1Key, k1_SkKey },
+ { SK_Mac2Key, k2_SkKey },
+ { SK_Mac3Key, k3_SkKey },
+ { SK_Mac4Key, k4_SkKey },
+ { SK_Mac5Key, k5_SkKey },
+ { SK_Mac6Key, k6_SkKey },
+ { SK_Mac7Key, k7_SkKey },
+ { SK_Mac8Key, k8_SkKey },
+ { SK_Mac9Key, k9_SkKey }
+ };
+
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
+ if (gKeys[i].fRaw == raw)
+ return gKeys[i].fKey;
+ return kNONE_SkKey;
+}
+
+static void post_skmacevent()
+{
+ EventRef ref;
+ OSStatus status = CreateEvent(nil, SK_MacEventClass, SK_MacEventKind, 0, 0, &ref);
+ SkASSERT(status == noErr);
+
+#if 0
+ status = SetEventParameter(ref, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt);
+ SkASSERT(status == noErr);
+ status = SetEventParameter(ref, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID);
+ SkASSERT(status == noErr);
+#endif
+
+ EventTargetRef target = gEventTarget;
+ SetEventParameter(ref, kEventParamPostTarget, typeEventTargetRef, sizeof(target), &target);
+ SkASSERT(status == noErr);
+
+ status = PostEventToQueue(gCurrEventQ, ref, kEventPriorityStandard);
+ SkASSERT(status == noErr);
+
+ ReleaseEvent(ref);
+}
+
+pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData )
+{
+ SkOSWindow* win = (SkOSWindow*)userData;
+ OSStatus result = eventNotHandledErr;
+ UInt32 wClass = GetEventClass(inEvent);
+ UInt32 wKind = GetEventKind(inEvent);
+
+ gCurrOSWin = win; // will need to be in TLS. Set this so PostEvent will work
+
+ switch (wClass) {
+ case kEventClassMouse: {
+ Point pt;
+ getparam(inEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pt), &pt);
+ SetPortWindowPort((WindowRef)win->getHWND());
+ GlobalToLocal(&pt);
+
+ switch (wKind) {
+ case kEventMouseDown:
+ if (win->handleClick(pt.h, pt.v, Click::kDown_State)) {
+ result = noErr;
+ }
+ break;
+ case kEventMouseMoved:
+ // fall through
+ case kEventMouseDragged:
+ (void)win->handleClick(pt.h, pt.v, Click::kMoved_State);
+ // result = noErr;
+ break;
+ case kEventMouseUp:
+ (void)win->handleClick(pt.h, pt.v, Click::kUp_State);
+ // result = noErr;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case kEventClassKeyboard:
+ if (wKind == kEventRawKeyDown) {
+ UInt32 raw;
+ getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw);
+ SkKey key = raw2key(raw);
+ if (key != kNONE_SkKey)
+ (void)win->handleKey(key);
+ } else if (wKind == kEventRawKeyUp) {
+ UInt32 raw;
+ getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw);
+ SkKey key = raw2key(raw);
+ if (key != kNONE_SkKey)
+ (void)win->handleKeyUp(key);
+ }
+ break;
+ case kEventClassTextInput:
+ if (wKind == kEventTextInputUnicodeForKeyEvent) {
+ UInt16 uni;
+ getparam(inEvent, kEventParamTextInputSendText, typeUnicodeText, sizeof(uni), &uni);
+ win->handleChar(uni);
+ }
+ break;
+ case kEventClassWindow:
+ switch (wKind) {
+ case kEventWindowBoundsChanged:
+ win->updateSize();
+ break;
+ case kEventWindowDrawContent: {
+ CGContextRef cg;
+ result = GetEventParameter(inEvent,
+ kEventParamCGContextRef,
+ typeCGContextRef,
+ NULL,
+ sizeof (CGContextRef),
+ NULL,
+ &cg);
+ if (result != 0) {
+ cg = NULL;
+ }
+ win->doPaint(cg);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case SK_MacEventClass: {
+ SkASSERT(wKind == SK_MacEventKind);
+ if (SkEvent::ProcessEvent()) {
+ post_skmacevent();
+ }
+ #if 0
+ SkEvent* evt;
+ SkEventSinkID sinkID;
+ getparam(inEvent, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt);
+ getparam(inEvent, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID);
+ #endif
+ result = noErr;
+ break;
+ }
+ default:
+ break;
+ }
+ if (result == eventNotHandledErr) {
+ result = CallNextEventHandler(inHandler, inEvent);
+ }
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkEvent::SignalNonEmptyQueue()
+{
+ post_skmacevent();
+// SkDebugf("signal nonempty\n");
+}
+
+static TMTask gTMTaskRec;
+static TMTask* gTMTaskPtr;
+
+static void sk_timer_proc(TMTask* rec)
+{
+ SkEvent::ServiceQueueTimer();
+// SkDebugf("timer task fired\n");
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+ if (gTMTaskPtr)
+ {
+ RemoveTimeTask((QElem*)gTMTaskPtr);
+ DisposeTimerUPP(gTMTaskPtr->tmAddr);
+ gTMTaskPtr = nil;
+ }
+ if (delay)
+ {
+ gTMTaskPtr = &gTMTaskRec;
+ memset(gTMTaskPtr, 0, sizeof(gTMTaskRec));
+ gTMTaskPtr->tmAddr = NewTimerUPP(sk_timer_proc);
+ OSErr err = InstallTimeTask((QElem*)gTMTaskPtr);
+// SkDebugf("installtimetask of %d returned %d\n", delay, err);
+ PrimeTimeTask((QElem*)gTMTaskPtr, delay);
+ }
+}
+
+#define USE_MSAA 0
+
+AGLContext create_gl(WindowRef wref)
+{
+ GLint major, minor;
+ AGLContext ctx;
+
+ aglGetVersion(&major, &minor);
+ SkDebugf("---- agl version %d %d\n", major, minor);
+
+ const GLint pixelAttrs[] = {
+ AGL_RGBA,
+ AGL_STENCIL_SIZE, 8,
+#if USE_MSAA
+ AGL_SAMPLE_BUFFERS_ARB, 1,
+ AGL_MULTISAMPLE,
+ AGL_SAMPLES_ARB, 8,
+#endif
+ AGL_ACCELERATED,
+ AGL_DOUBLEBUFFER,
+ AGL_NONE
+ };
+ AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs);
+ //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs);
+ SkDebugf("----- agl format %p\n", format);
+ ctx = aglCreateContext(format, NULL);
+ SkDebugf("----- agl context %p\n", ctx);
+ aglDestroyPixelFormat(format);
+
+ static const GLint interval = 1;
+ aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval);
+ aglSetCurrentContext(ctx);
+ return ctx;
+}
+
+bool SkOSWindow::attachGL()
+{
+ if (NULL == fAGLCtx) {
+ fAGLCtx = create_gl((WindowRef)fHWND);
+ if (NULL == fAGLCtx) {
+ return false;
+ }
+ }
+
+ GLboolean success = true;
+
+ int width, height;
+
+ success = aglSetWindowRef((AGLContext)fAGLCtx, (WindowRef)fHWND);
+ width = this->width();
+ height = this->height();
+
+ GLenum err = aglGetError();
+ if (err) {
+ SkDebugf("---- aglSetWindowRef %d %d %s [%d %d]\n", success, err,
+ aglErrorString(err), width, height);
+ }
+
+ if (success) {
+ glViewport(0, 0, width, height);
+ glClearColor(0, 0, 0, 0);
+ glClearStencil(0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+ return success;
+}
+
+void SkOSWindow::detachGL() {
+ aglSetWindowRef((AGLContext)fAGLCtx, NULL);
+}
+
+void SkOSWindow::presentGL() {
+ aglSwapBuffers((AGLContext)fAGLCtx);
+}
+
+#endif
+
diff --git a/src/utils/mac/skia_mac.cpp b/src/utils/mac/skia_mac.cpp
new file mode 100644
index 0000000000..a1345cf62c
--- /dev/null
+++ b/src/utils/mac/skia_mac.cpp
@@ -0,0 +1,43 @@
+#include <Carbon/Carbon.h>
+#include "SkApplication.h"
+#include "SkWindow.h"
+
+int main(int argc, char* argv[])
+{
+ WindowRef window;
+ OSStatus err = noErr;
+
+ Rect bounds = {100, 100, 500, 500};
+ WindowAttributes attrs = kWindowStandardHandlerAttribute |
+ kWindowLiveResizeAttribute |
+ kWindowInWindowMenuAttribute |
+ kWindowCompositingAttribute |
+ kWindowAsyncDragAttribute |
+ kWindowFullZoomAttribute |
+ kWindowFrameworkScaledAttribute;
+ //kWindowDoesNotCycleAttribute;
+ CreateNewWindow(kDocumentWindowClass, attrs, &bounds, &window);
+
+ MenuRef menu;
+ CreateNewMenu(0, 0, &menu);
+
+ // if we get here, we can start our normal Skia sequence
+ {
+ application_init();
+ (void)create_sk_window(window);
+ SizeWindow(window, 640, 480, false);
+ }
+
+ // The window was created hidden so show it.
+ ShowWindow( window );
+
+ // Call the event loop
+ RunApplicationEventLoop();
+
+ application_term();
+
+CantCreateWindow:
+CantGetNibRef:
+ return err;
+}
+
diff --git a/src/utils/mesa/SkEGLContext_Mesa.cpp b/src/utils/mesa/SkEGLContext_Mesa.cpp
new file mode 100644
index 0000000000..ed1b7cd451
--- /dev/null
+++ b/src/utils/mesa/SkEGLContext_Mesa.cpp
@@ -0,0 +1,128 @@
+#include "SkEGLContext.h"
+#include "SkTypes.h"
+
+#include "GL/osmesa.h"
+#include "GL/glu.h"
+
+#define SK_GL_DECL_PROC(T, F) T F ## _func = NULL;
+#define SK_GL_GET_PROC(T, F) F ## _func = (T)OSMesaGetProcAddress(#F);
+#define SK_GL_GET_EXT_PROC(T, F) F ## _func = (T)OSMesaGetProcAddress(#F "EXT");
+
+SkEGLContext::SkEGLContext() : context(NULL), image(NULL) {
+}
+
+SkEGLContext::~SkEGLContext() {
+ if (this->image)
+ free(this->image);
+
+ if (this->context)
+ OSMesaDestroyContext(this->context);
+}
+
+#if SK_B32_SHIFT < SK_G32_SHIFT &&\
+ SK_G32_SHIFT < SK_R32_SHIFT &&\
+ SK_R32_SHIFT < SK_A32_SHIFT
+ #define SK_OSMESA_COLOR_ORDER OSMESA_BGRA
+#elif SK_R32_SHIFT < SK_G32_SHIFT &&\
+ SK_G32_SHIFT < SK_B32_SHIFT &&\
+ SK_B32_SHIFT < SK_A32_SHIFT
+ #define SK_OSMESA_COLOR_ORDER OSMESA_RGBA
+#elif SK_A32_SHIFT < SK_R32_SHIFT && \
+ SK_R32_SHIFT < SK_G32_SHIFT && \
+ SK_G32_SHIFT < SK_B32_SHIFT
+ #define SK_OSMESA_COLOR_ORDER OSMESA_ARGB
+#else
+ //Color order (rgba) SK_R32_SHIFT SK_G32_SHIFT SK_B32_SHIFT SK_A32_SHIFT
+ #define SK_OSMESA_COLOR_ORDER OSMESA_RGBA
+#endif
+
+bool SkEGLContext::init(const int width, const int height) {
+ /* Create an RGBA-mode context */
+#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
+ /* specify Z, stencil, accum sizes */
+ OSMesaContext ctx = OSMesaCreateContextExt(SK_OSMESA_COLOR_ORDER, 16, 0, 0, NULL);
+#else
+ OSMesaContext ctx = OSMesaCreateContext(SK_OSMESA_COLOR_ORDER, NULL);
+#endif
+ if (!ctx) {
+ SkDebugf("OSMesaCreateContext failed!\n");
+ return false;
+ }
+ this->context = ctx;
+
+ // Allocate the image buffer
+ GLfloat *buffer = (GLfloat *) malloc(width * height * 4 * sizeof(GLfloat));
+ if (!buffer) {
+ SkDebugf("Alloc image buffer failed!\n");
+ return false;
+ }
+ this->image = buffer;
+
+ // Bind the buffer to the context and make it current
+ if (!OSMesaMakeCurrent(ctx, buffer, GL_FLOAT, width, height)) {
+ SkDebugf("OSMesaMakeCurrent failed!\n");
+ return false;
+ }
+
+ //Setup the framebuffers
+ SK_GL_DECL_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers)
+ SK_GL_DECL_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer)
+ SK_GL_DECL_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
+ SK_GL_DECL_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
+ SK_GL_DECL_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
+ SK_GL_DECL_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
+ SK_GL_DECL_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus)
+
+ const GLubyte* glExts = glGetString(GL_EXTENSIONS);
+ if (gluCheckExtension(
+ reinterpret_cast<const GLubyte*>("GL_ARB_framebuffer_object")
+ , glExts))
+ {
+ SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers)
+ SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer)
+ SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
+ SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
+ SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
+ SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
+ SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus)
+
+ //osmesa on mac currently only supports EXT
+ } else if (gluCheckExtension(
+ reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object")
+ , glExts))
+ {
+ SK_GL_GET_EXT_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffers)
+ SK_GL_GET_EXT_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebuffer)
+ SK_GL_GET_EXT_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffers)
+ SK_GL_GET_EXT_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbuffer)
+ SK_GL_GET_EXT_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorage)
+ SK_GL_GET_EXT_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbuffer)
+ SK_GL_GET_EXT_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatus)
+ } else {
+ SkDebugf("GL_ARB_framebuffer_object not found.\n");
+ return false;
+ }
+
+ GLuint fboID;
+ GLuint cbID;
+ GLuint dsID;
+ glGenFramebuffers_func(1, &fboID);
+ glBindFramebuffer_func(GL_FRAMEBUFFER, fboID);
+
+ glGenRenderbuffers_func(1, &cbID);
+ glBindRenderbuffer_func(GL_RENDERBUFFER, cbID);
+ glRenderbufferStorage_func(GL_RENDERBUFFER, OSMESA_RGBA, width, height);
+ glFramebufferRenderbuffer_func(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+
+ glGenRenderbuffers_func(1, &dsID);
+ glBindRenderbuffer_func(GL_RENDERBUFFER_EXT, dsID);
+ glRenderbufferStorage_func(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
+ glFramebufferRenderbuffer_func(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+
+ glViewport(0, 0, width, height);
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ GLenum status = glCheckFramebufferStatus_func(GL_FRAMEBUFFER);
+ return GL_FRAMEBUFFER_COMPLETE == status;
+}
diff --git a/src/utils/unix/SkEGLContext_Unix.cpp b/src/utils/unix/SkEGLContext_Unix.cpp
new file mode 100644
index 0000000000..7921b8a542
--- /dev/null
+++ b/src/utils/unix/SkEGLContext_Unix.cpp
@@ -0,0 +1,264 @@
+#include "SkEGLContext.h"
+#include "SkTypes.h"
+
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <GL/glu.h>
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#define SK_GL_GET_PROC(T, F) T F = NULL; \
+ F = (T) glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(#F));
+
+static bool ctxErrorOccurred = false;
+static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
+ ctxErrorOccurred = true;
+ return 0;
+}
+
+SkEGLContext::SkEGLContext() : context(NULL), display(NULL), pixmap(0), glxPixmap(0) {
+}
+
+SkEGLContext::~SkEGLContext() {
+ if (this->display) {
+ glXMakeCurrent(this->display, 0, 0);
+
+ if (this->context)
+ glXDestroyContext(this->display, this->context);
+
+ if (this->glxPixmap)
+ glXDestroyGLXPixmap(this->display, this->glxPixmap);
+
+ if (this->pixmap)
+ XFreePixmap(this->display, this->pixmap);
+
+ XCloseDisplay(this->display);
+ }
+}
+
+bool SkEGLContext::init(const int width, const int height) {
+ Display *display = XOpenDisplay(0);
+ this->display = display;
+
+ if (!display) {
+ SkDebugf("Failed to open X display.\n");
+ return false;
+ }
+
+ // Get a matching FB config
+ static int visual_attribs[] = {
+ GLX_X_RENDERABLE , True,
+ GLX_DRAWABLE_TYPE , GLX_PIXMAP_BIT,
+ GLX_RENDER_TYPE , GLX_RGBA_BIT,
+ GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
+ GLX_RED_SIZE , 8,
+ GLX_GREEN_SIZE , 8,
+ GLX_BLUE_SIZE , 8,
+ GLX_ALPHA_SIZE , 8,
+ GLX_DEPTH_SIZE , 24,
+ GLX_STENCIL_SIZE , 8,
+ GLX_DOUBLEBUFFER , True,
+ //GLX_SAMPLE_BUFFERS , 1,
+ //GLX_SAMPLES , 4,
+ None
+ };
+
+ int glx_major, glx_minor;
+
+ // FBConfigs were added in GLX version 1.3.
+ if (!glXQueryVersion( display, &glx_major, &glx_minor) ||
+ ( (glx_major == 1) && (glx_minor < 3) ) || (glx_major < 1))
+ {
+ SkDebugf("Invalid GLX version.");
+ return false;
+ }
+
+ //SkDebugf("Getting matching framebuffer configs.\n");
+ int fbcount;
+ GLXFBConfig *fbc = glXChooseFBConfig(display, DefaultScreen(display),
+ visual_attribs, &fbcount);
+ if (!fbc) {
+ SkDebugf("Failed to retrieve a framebuffer config.\n");
+ return false;
+ }
+ //SkDebugf("Found %d matching FB configs.\n", fbcount);
+
+ // Pick the FB config/visual with the most samples per pixel
+ //SkDebugf("Getting XVisualInfos.\n");
+ int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
+
+ int i;
+ for (i = 0; i < fbcount; ++i) {
+ XVisualInfo *vi = glXGetVisualFromFBConfig(display, fbc[i]);
+ if (vi) {
+ int samp_buf, samples;
+ glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+ glXGetFBConfigAttrib(display, fbc[i], GLX_SAMPLES, &samples);
+
+ //SkDebugf(" Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
+ // " SAMPLES = %d\n",
+ // i, (unsigned int)vi->visualid, samp_buf, samples);
+
+ if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
+ best_fbc = i, best_num_samp = samples;
+ if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp)
+ worst_fbc = i, worst_num_samp = samples;
+ }
+ XFree(vi);
+ }
+
+ GLXFBConfig bestFbc = fbc[best_fbc];
+
+ // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
+ XFree(fbc);
+
+ // Get a visual
+ XVisualInfo *vi = glXGetVisualFromFBConfig(display, bestFbc);
+ //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
+
+ Pixmap pixmap = XCreatePixmap(
+ display, RootWindow(display, vi->screen), width, height, vi->depth
+ );
+
+ this->pixmap = pixmap;
+ if (!pixmap) {
+ SkDebugf("Failed to create pixmap.\n");
+ return false;
+ }
+
+ GLXPixmap glxPixmap = glXCreateGLXPixmap(display, vi, pixmap);
+ this->glxPixmap = glxPixmap;
+
+ // Done with the visual info data
+ XFree(vi);
+
+ // Create the context
+ GLXContext ctx = 0;
+
+ // Install an X error handler so the application won't exit if GL 3.0
+ // context allocation fails.
+ //
+ // Note this error handler is global.
+ // All display connections in all threads of a process use the same
+ // error handler, so be sure to guard against other threads issuing
+ // X commands while this code is running.
+ ctxErrorOccurred = false;
+ int (*oldHandler)(Display*, XErrorEvent*) =
+ XSetErrorHandler(&ctxErrorHandler);
+
+ // Get the default screen's GLX extension list
+ const char *glxExts = glXQueryExtensionsString(
+ display, DefaultScreen(display)
+ );
+ // Check for the GLX_ARB_create_context extension string and the function.
+ // If either is not present, use GLX 1.3 context creation method.
+ if (!gluCheckExtension(
+ reinterpret_cast<const GLubyte*>("GLX_ARB_create_context")
+ , reinterpret_cast<const GLubyte*>(glxExts)))
+ {
+ //SkDebugf("GLX_ARB_create_context not found."
+ // " Using old-style GLX context.\n");
+ ctx = glXCreateNewContext(display, bestFbc, GLX_RGBA_TYPE, 0, True);
+
+ } else {
+ //SkDebugf("Creating context.\n");
+
+ SK_GL_GET_PROC(PFNGLXCREATECONTEXTATTRIBSARBPROC, glXCreateContextAttribsARB)
+ int context_attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 0,
+ //GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+ None
+ };
+ ctx = glXCreateContextAttribsARB(
+ display, bestFbc, 0, True, context_attribs
+ );
+
+ // Sync to ensure any errors generated are processed.
+ XSync(display, False);
+ if (!ctxErrorOccurred && ctx) {
+ //SkDebugf( "Created GL 3.0 context.\n" );
+ } else {
+ // Couldn't create GL 3.0 context.
+ // Fall back to old-style 2.x context.
+ // When a context version below 3.0 is requested,
+ // implementations will return the newest context version compatible
+ // with OpenGL versions less than version 3.0.
+
+ // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
+ context_attribs[1] = 1;
+ // GLX_CONTEXT_MINOR_VERSION_ARB = 0
+ context_attribs[3] = 0;
+
+ ctxErrorOccurred = false;
+
+ //SkDebugf("Failed to create GL 3.0 context."
+ // " Using old-style GLX context.\n");
+ ctx = glXCreateContextAttribsARB(
+ display, bestFbc, 0, True, context_attribs
+ );
+ }
+ }
+
+ // Sync to ensure any errors generated are processed.
+ XSync(display, False);
+
+ // Restore the original error handler
+ XSetErrorHandler(oldHandler);
+
+ if (ctxErrorOccurred || !ctx) {
+ SkDebugf("Failed to create an OpenGL context.\n");
+ return false;
+ }
+ this->context = ctx;
+
+ // Verify that context is a direct context
+ if (!glXIsDirect(display, ctx)) {
+ //SkDebugf("Indirect GLX rendering context obtained.\n");
+ } else {
+ //SkDebugf("Direct GLX rendering context obtained.\n");
+ }
+
+ //SkDebugf("Making context current.\n");
+ if (!glXMakeCurrent(display, glxPixmap, ctx)) {
+ SkDebugf("Could not set the context.\n");
+ return false;
+ }
+
+ //Setup the framebuffers
+ const GLubyte* glExts = glGetString(GL_EXTENSIONS);
+ if (!gluCheckExtension(
+ reinterpret_cast<const GLubyte*>("GL_EXT_framebuffer_object")
+ , glExts))
+ {
+ SkDebugf("GL_EXT_framebuffer_object not found.\n");
+ return false;
+ }
+ SK_GL_GET_PROC(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT)
+ SK_GL_GET_PROC(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT)
+ SK_GL_GET_PROC(PFNGLGENRENDERBUFFERSPROC, glGenRenderbuffersEXT)
+ SK_GL_GET_PROC(PFNGLBINDRENDERBUFFERPROC, glBindRenderbufferEXT)
+ SK_GL_GET_PROC(PFNGLRENDERBUFFERSTORAGEPROC, glRenderbufferStorageEXT)
+ SK_GL_GET_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, glFramebufferRenderbufferEXT)
+ SK_GL_GET_PROC(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT)
+
+ GLuint fboID;
+ GLuint cbID;
+ GLuint dsID;
+ glGenFramebuffersEXT(1, &fboID);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
+ glGenRenderbuffersEXT(1, &cbID);
+ glBindRenderbufferEXT(GL_RENDERBUFFER, cbID);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, width, height);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cbID);
+ glGenRenderbuffersEXT(1, &dsID);
+ glBindRenderbufferEXT(GL_RENDERBUFFER, dsID);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dsID);
+ glViewport(0, 0, width, height);
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
+ return GL_FRAMEBUFFER_COMPLETE == status;
+}
diff --git a/src/utils/unix/SkOSWindow_Unix.cpp b/src/utils/unix/SkOSWindow_Unix.cpp
index 4ec0c74306..ae881d5148 100644
--- a/src/utils/unix/SkOSWindow_Unix.cpp
+++ b/src/utils/unix/SkOSWindow_Unix.cpp
@@ -76,7 +76,7 @@ void SkOSWindow::post_linuxevent()
long event_mask = NoEventMask;
XClientMessageEvent event;
event.type = ClientMessage;
- Atom myAtom;
+ Atom myAtom(0);
event.message_type = myAtom;
event.format = 32;
event.data.l[0] = 0;
diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp
new file mode 100644
index 0000000000..07da123284
--- /dev/null
+++ b/src/views/SkBGViewArtist.cpp
@@ -0,0 +1,24 @@
+#include "SkBGViewArtist.h"
+#include "SkCanvas.h"
+#include "SkParsePaint.h"
+
+SkBGViewArtist::SkBGViewArtist(SkColor c)
+{
+ fPaint.setColor(c);
+}
+
+SkBGViewArtist::~SkBGViewArtist()
+{
+}
+
+void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas)
+{
+ // only works for views that are clipped their bounds.
+ canvas->drawPaint(fPaint);
+}
+
+void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp
new file mode 100644
index 0000000000..74a24775f4
--- /dev/null
+++ b/src/views/SkBorderView.cpp
@@ -0,0 +1,89 @@
+#include "SkBorderView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+#include "SkStackViewLayout.h"
+
+SkBorderView::SkBorderView() : fLeft(SkIntToScalar(0)),
+ fRight(SkIntToScalar(0)),
+ fTop(SkIntToScalar(0)),
+ fBottom(SkIntToScalar(0))
+{
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kBorder_SkinEnum, &fAnim);
+}
+
+SkBorderView::~SkBorderView()
+{
+
+}
+
+void SkBorderView::setSkin(const char skin[])
+{
+ init_skin_anim(skin, &fAnim);
+}
+
+/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+}
+
+/*virtual*/ void SkBorderView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(NULL);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ evt.findScalar("leftMargin", &fLeft);
+ evt.findScalar("rightMargin", &fRight);
+ evt.findScalar("topMargin", &fTop);
+ evt.findScalar("bottomMargin", &fBottom);
+
+ //setup_views.cpp uses SkView::Layout instead of SkStackViewLayout
+ //but that gives me an error
+ SkStackViewLayout* layout;
+ fMargin.set(fLeft, fTop, fRight, fBottom);
+ if (this->getLayout())
+ {
+ layout = (SkStackViewLayout*)this->getLayout();
+ layout->setMargin(fMargin);
+ }
+ else
+ {
+ layout = new SkStackViewLayout;
+ layout->setMargin(fMargin);
+ this->setLayout(layout)->unref();
+ }
+ this->invokeLayout();
+ }
+ return this->INHERITED::onEvent(evt);
+}
diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp
new file mode 100644
index 0000000000..ec4a7b47b7
--- /dev/null
+++ b/src/views/SkEvent.cpp
@@ -0,0 +1,580 @@
+/* libs/graphics/views/SkEvent.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkEvent.h"
+
+void SkEvent::initialize(const char* type, size_t typeLen) {
+ fType = NULL;
+ setType(type, typeLen);
+ f32 = 0;
+#ifdef SK_DEBUG
+ fTargetID = 0;
+ fTime = 0;
+ fNextEvent = NULL;
+#endif
+ SkDEBUGCODE(fDebugTrace = false;)
+}
+
+SkEvent::SkEvent()
+{
+ initialize("", 0);
+}
+
+SkEvent::SkEvent(const SkEvent& src)
+{
+ *this = src;
+ if (((size_t) fType & 1) == 0)
+ setType(src.fType);
+}
+
+SkEvent::SkEvent(const SkString& type)
+{
+ initialize(type.c_str(), type.size());
+}
+
+SkEvent::SkEvent(const char type[])
+{
+ SkASSERT(type);
+ initialize(type, strlen(type));
+}
+
+SkEvent::~SkEvent()
+{
+ if (((size_t) fType & 1) == 0)
+ sk_free((void*) fType);
+}
+
+static size_t makeCharArray(char* buffer, size_t compact)
+{
+ size_t bits = (size_t) compact >> 1;
+ memcpy(buffer, &bits, sizeof(compact));
+ buffer[sizeof(compact)] = 0;
+ return strlen(buffer);
+}
+
+#if 0
+const char* SkEvent::getType() const
+{
+ if ((size_t) fType & 1) { // not a pointer
+ char chars[sizeof(size_t) + 1];
+ size_t len = makeCharArray(chars, (size_t) fType);
+ fType = (char*) sk_malloc_throw(len);
+ SkASSERT(((size_t) fType & 1) == 0);
+ memcpy(fType, chars, len);
+ }
+ return fType;
+}
+#endif
+
+void SkEvent::getType(SkString* str) const
+{
+ if (str)
+ {
+ if ((size_t) fType & 1) // not a pointer
+ {
+ char chars[sizeof(size_t) + 1];
+ size_t len = makeCharArray(chars, (size_t) fType);
+ str->set(chars, len);
+ }
+ else
+ str->set(fType);
+ }
+}
+
+bool SkEvent::isType(const SkString& str) const
+{
+ return this->isType(str.c_str(), str.size());
+}
+
+bool SkEvent::isType(const char type[], size_t typeLen) const
+{
+ if (typeLen == 0)
+ typeLen = strlen(type);
+ if ((size_t) fType & 1) { // not a pointer
+ char chars[sizeof(size_t) + 1];
+ size_t len = makeCharArray(chars, (size_t) fType);
+ return len == typeLen && strncmp(chars, type, typeLen) == 0;
+ }
+ return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0;
+}
+
+void SkEvent::setType(const char type[], size_t typeLen)
+{
+ if (typeLen == 0)
+ typeLen = strlen(type);
+ if (typeLen <= sizeof(fType)) {
+ size_t slot = 0;
+ memcpy(&slot, type, typeLen);
+ if (slot << 1 >> 1 != slot)
+ goto useCharStar;
+ slot <<= 1;
+ slot |= 1;
+ fType = (char*) slot;
+ } else {
+useCharStar:
+ fType = (char*) sk_malloc_throw(typeLen + 1);
+ SkASSERT(((size_t) fType & 1) == 0);
+ memcpy(fType, type, typeLen);
+ fType[typeLen] = 0;
+ }
+}
+
+void SkEvent::setType(const SkString& type)
+{
+ setType(type.c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#include "SkParse.h"
+
+void SkEvent::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ const char* name = dom.findAttr(node, "type");
+ if (name)
+ this->setType(name);
+
+ const char* value;
+ if ((value = dom.findAttr(node, "fast32")) != NULL)
+ {
+ int32_t n;
+ if (SkParse::FindS32(value, &n))
+ this->setFast32(n);
+ }
+
+ for (node = dom.getFirstChild(node); node; node = dom.getNextSibling(node))
+ {
+ if (strcmp(dom.getName(node), "data"))
+ {
+ SkDEBUGCODE(SkDebugf("SkEvent::inflate unrecognized subelement <%s>\n", dom.getName(node));)
+ continue;
+ }
+
+ name = dom.findAttr(node, "name");
+ if (name == NULL)
+ {
+ SkDEBUGCODE(SkDebugf("SkEvent::inflate missing required \"name\" attribute in <data> subelement\n");)
+ continue;
+ }
+
+ if ((value = dom.findAttr(node, "s32")) != NULL)
+ {
+ int32_t n;
+ if (SkParse::FindS32(value, &n))
+ this->setS32(name, n);
+ }
+ else if ((value = dom.findAttr(node, "scalar")) != NULL)
+ {
+ SkScalar x;
+ if (SkParse::FindScalar(value, &x))
+ this->setScalar(name, x);
+ }
+ else if ((value = dom.findAttr(node, "string")) != NULL)
+ this->setString(name, value);
+#ifdef SK_DEBUG
+ else
+ {
+ SkDebugf("SkEvent::inflate <data name=\"%s\"> subelement missing required type attribute [S32 | scalar | string]\n", name);
+ }
+#endif
+ }
+}
+
+#ifdef SK_DEBUG
+
+ #ifndef SkScalarToFloat
+ #define SkScalarToFloat(x) ((x) / 65536.f)
+ #endif
+
+ void SkEvent::dump(const char title[])
+ {
+ if (title)
+ SkDebugf("%s ", title);
+
+ SkString etype;
+ this->getType(&etype);
+ SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32());
+
+ const SkMetaData& md = this->getMetaData();
+ SkMetaData::Iter iter(md);
+ SkMetaData::Type mtype;
+ int count;
+ const char* name;
+
+ while ((name = iter.next(&mtype, &count)) != NULL)
+ {
+ SkASSERT(count > 0);
+
+ SkDebugf(" <%s>=", name);
+ switch (mtype) {
+ case SkMetaData::kS32_Type: // vector version???
+ {
+ int32_t value;
+ md.findS32(name, &value);
+ SkDebugf("%d ", value);
+ }
+ break;
+ case SkMetaData::kScalar_Type:
+ {
+ const SkScalar* values = md.findScalars(name, &count, NULL);
+ SkDebugf("%f", SkScalarToFloat(values[0]));
+ for (int i = 1; i < count; i++)
+ SkDebugf(", %f", SkScalarToFloat(values[i]));
+ SkDebugf(" ");
+ }
+ break;
+ case SkMetaData::kString_Type:
+ {
+ const char* value = md.findString(name);
+ SkASSERT(value);
+ SkDebugf("<%s> ", value);
+ }
+ break;
+ case SkMetaData::kPtr_Type: // vector version???
+ {
+ void* value;
+ md.findPtr(name, &value);
+ SkDebugf("%p ", value);
+ }
+ break;
+ case SkMetaData::kBool_Type: // vector version???
+ {
+ bool value;
+ md.findBool(name, &value);
+ SkDebugf("%s ", value ? "true" : "false");
+ }
+ break;
+ default:
+ SkASSERT(!"unknown metadata type returned from iterator");
+ break;
+ }
+ }
+ SkDebugf("\n");
+ }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+// #define SK_TRACE_EVENTSx
+#endif
+
+#ifdef SK_TRACE_EVENTS
+ static void event_log(const char s[])
+ {
+ SkDEBUGF(("%s\n", s));
+ }
+
+ #define EVENT_LOG(s) event_log(s)
+ #define EVENT_LOGN(s, n) do { SkString str(s); str.append(" "); str.appendS32(n); event_log(str.c_str()); } while (0)
+#else
+ #define EVENT_LOG(s)
+ #define EVENT_LOGN(s, n)
+#endif
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+#include "SkTime.h"
+
+#define SK_Event_GlobalsTag SkSetFourByteTag('e', 'v', 'n', 't')
+
+class SkEvent_Globals : public SkGlobals::Rec {
+public:
+ SkMutex fEventMutex;
+ SkEvent* fEventQHead, *fEventQTail;
+ SkEvent* fDelayQHead;
+ SkDEBUGCODE(int fEventCounter;)
+};
+
+static SkGlobals::Rec* create_globals()
+{
+ SkEvent_Globals* rec = new SkEvent_Globals;
+ rec->fEventQHead = NULL;
+ rec->fEventQTail = NULL;
+ rec->fDelayQHead = NULL;
+ SkDEBUGCODE(rec->fEventCounter = 0;)
+ return rec;
+}
+
+bool SkEvent::Post(SkEvent* evt, SkEventSinkID sinkID, SkMSec delay)
+{
+ if (delay)
+ return SkEvent::PostTime(evt, sinkID, SkTime::GetMSecs() + delay);
+
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ evt->fTargetID = sinkID;
+
+#ifdef SK_TRACE_EVENTS
+ {
+ SkString str("SkEvent::Post(");
+ str.append(evt->getType());
+ str.append(", 0x");
+ str.appendHex(sinkID);
+ str.append(", ");
+ str.appendS32(delay);
+ str.append(")");
+ event_log(str.c_str());
+ }
+#endif
+
+ globals.fEventMutex.acquire();
+ bool wasEmpty = SkEvent::Enqueue(evt);
+ globals.fEventMutex.release();
+
+ // call outside of us holding the mutex
+ if (wasEmpty)
+ SkEvent::SignalNonEmptyQueue();
+ return true;
+}
+
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS)
+SkMSec gMaxDrawTime;
+#endif
+
+bool SkEvent::PostTime(SkEvent* evt, SkEventSinkID sinkID, SkMSec time)
+{
+#if defined(SK_SIMULATE_FAILED_MALLOC) && defined(SK_FIND_MEMORY_LEAKS)
+ gMaxDrawTime = time;
+#endif
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ evt->fTargetID = sinkID;
+
+#ifdef SK_TRACE_EVENTS
+ {
+ SkString str("SkEvent::Post(");
+ str.append(evt->getType());
+ str.append(", 0x");
+ str.appendHex(sinkID);
+ str.append(", ");
+ str.appendS32(time);
+ str.append(")");
+ event_log(str.c_str());
+ }
+#endif
+
+ globals.fEventMutex.acquire();
+ SkMSec queueDelay = SkEvent::EnqueueTime(evt, time);
+ globals.fEventMutex.release();
+
+ // call outside of us holding the mutex
+ if ((int32_t)queueDelay != ~0)
+ SkEvent::SignalQueueTimer(queueDelay);
+ return true;
+}
+
+bool SkEvent::Enqueue(SkEvent* evt)
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ // gEventMutex acquired by caller
+
+ SkASSERT(evt);
+
+ bool wasEmpty = globals.fEventQHead == NULL;
+
+ if (globals.fEventQTail)
+ globals.fEventQTail->fNextEvent = evt;
+ globals.fEventQTail = evt;
+ if (globals.fEventQHead == NULL)
+ globals.fEventQHead = evt;
+ evt->fNextEvent = NULL;
+
+ SkDEBUGCODE(++globals.fEventCounter);
+// SkDebugf("Enqueue: count=%d\n", gEventCounter);
+
+ return wasEmpty;
+}
+
+SkEvent* SkEvent::Dequeue(SkEventSinkID* sinkID)
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ globals.fEventMutex.acquire();
+
+ SkEvent* evt = globals.fEventQHead;
+ if (evt)
+ {
+ SkDEBUGCODE(--globals.fEventCounter);
+
+ if (sinkID)
+ *sinkID = evt->fTargetID;
+
+ globals.fEventQHead = evt->fNextEvent;
+ if (globals.fEventQHead == NULL)
+ globals.fEventQTail = NULL;
+ }
+ globals.fEventMutex.release();
+
+// SkDebugf("Dequeue: count=%d\n", gEventCounter);
+
+ return evt;
+}
+
+bool SkEvent::QHasEvents()
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ // this is not thread accurate, need a semaphore for that
+ return globals.fEventQHead != NULL;
+}
+
+#ifdef SK_TRACE_EVENTS
+ static int gDelayDepth;
+#endif
+
+SkMSec SkEvent::EnqueueTime(SkEvent* evt, SkMSec time)
+{
+#ifdef SK_TRACE_EVENTS
+ SkDebugf("enqueue-delay %s %d (%d)", evt->getType(), time, gDelayDepth);
+ const char* idStr = evt->findString("id");
+ if (idStr)
+ SkDebugf(" (%s)", idStr);
+ SkDebugf("\n");
+ ++gDelayDepth;
+#endif
+
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ // gEventMutex acquired by caller
+
+ SkEvent* curr = globals.fDelayQHead;
+ SkEvent* prev = NULL;
+
+ while (curr)
+ {
+ if (SkMSec_LT(time, curr->fTime))
+ break;
+ prev = curr;
+ curr = curr->fNextEvent;
+ }
+
+ evt->fTime = time;
+ evt->fNextEvent = curr;
+ if (prev == NULL)
+ globals.fDelayQHead = evt;
+ else
+ prev->fNextEvent = evt;
+
+ SkMSec delay = globals.fDelayQHead->fTime - SkTime::GetMSecs();
+ if ((int32_t)delay <= 0)
+ delay = 1;
+ return delay;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include "SkEventSink.h"
+
+bool SkEvent::ProcessEvent()
+{
+ SkEventSinkID sinkID;
+ SkEvent* evt = SkEvent::Dequeue(&sinkID);
+ SkAutoTDelete<SkEvent> autoDelete(evt);
+ bool again = false;
+
+ EVENT_LOGN("ProcessEvent", (int32_t)evt);
+
+ if (evt)
+ {
+ (void)SkEventSink::DoEvent(*evt, sinkID);
+ again = SkEvent::QHasEvents();
+ }
+ return again;
+}
+
+void SkEvent::ServiceQueueTimer()
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ globals.fEventMutex.acquire();
+
+ bool wasEmpty = false;
+ SkMSec now = SkTime::GetMSecs();
+ SkEvent* evt = globals.fDelayQHead;
+
+ while (evt)
+ {
+ if (SkMSec_LT(now, evt->fTime))
+ break;
+
+#ifdef SK_TRACE_EVENTS
+ --gDelayDepth;
+ SkDebugf("dequeue-delay %s (%d)", evt->getType(), gDelayDepth);
+ const char* idStr = evt->findString("id");
+ if (idStr)
+ SkDebugf(" (%s)", idStr);
+ SkDebugf("\n");
+#endif
+
+ SkEvent* next = evt->fNextEvent;
+ if (SkEvent::Enqueue(evt))
+ wasEmpty = true;
+ evt = next;
+ }
+ globals.fDelayQHead = evt;
+
+ SkMSec time = evt ? evt->fTime - now : 0;
+
+ globals.fEventMutex.release();
+
+ if (wasEmpty)
+ SkEvent::SignalNonEmptyQueue();
+
+ SkEvent::SignalQueueTimer(time);
+}
+
+int SkEvent::CountEventsOnQueue() {
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+ globals.fEventMutex.acquire();
+
+ int count = 0;
+ const SkEvent* evt = globals.fEventQHead;
+ while (evt) {
+ count += 1;
+ evt = evt->fNextEvent;
+ }
+ globals.fEventMutex.release();
+
+ return count;
+}
+
+////////////////////////////////////////////////////////////////
+
+void SkEvent::Init()
+{
+}
+
+void SkEvent::Term()
+{
+ SkEvent_Globals& globals = *(SkEvent_Globals*)SkGlobals::Find(SK_Event_GlobalsTag, create_globals);
+
+ SkEvent* evt = globals.fEventQHead;
+ while (evt)
+ {
+ SkEvent* next = evt->fNextEvent;
+ delete evt;
+ evt = next;
+ }
+
+ evt = globals.fDelayQHead;
+ while (evt)
+ {
+ SkEvent* next = evt->fNextEvent;
+ delete evt;
+ evt = next;
+ }
+}
+
diff --git a/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp
new file mode 100644
index 0000000000..c8fe35ca18
--- /dev/null
+++ b/src/views/SkEventSink.cpp
@@ -0,0 +1,345 @@
+/* libs/graphics/views/SkEventSink.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkEventSink.h"
+#include "SkTagList.h"
+#include "SkThread.h"
+
+#include "SkGlobals.h"
+#include "SkThread.h"
+#include "SkTime.h"
+
+#define SK_EventSink_GlobalsTag SkSetFourByteTag('e', 'v', 's', 'k')
+
+class SkEventSink_Globals : public SkGlobals::Rec {
+public:
+ SkMutex fSinkMutex;
+ SkEventSinkID fNextSinkID;
+ SkEventSink* fSinkHead;
+};
+
+static SkGlobals::Rec* create_globals()
+{
+ SkEventSink_Globals* rec = new SkEventSink_Globals;
+ rec->fNextSinkID = 0;
+ rec->fSinkHead = NULL;
+ return rec;
+}
+
+SkEventSink::SkEventSink() : fTagHead(NULL)
+{
+ SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+
+ globals.fSinkMutex.acquire();
+
+ fID = ++globals.fNextSinkID;
+ fNextSink = globals.fSinkHead;
+ globals.fSinkHead = this;
+
+ globals.fSinkMutex.release();
+}
+
+SkEventSink::~SkEventSink()
+{
+ SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+
+ if (fTagHead)
+ SkTagList::DeleteAll(fTagHead);
+
+ globals.fSinkMutex.acquire();
+
+ SkEventSink* sink = globals.fSinkHead;
+ SkEventSink* prev = NULL;
+
+ for (;;)
+ {
+ SkEventSink* next = sink->fNextSink;
+ if (sink == this)
+ {
+ if (prev)
+ prev->fNextSink = next;
+ else
+ globals.fSinkHead = next;
+ break;
+ }
+ prev = sink;
+ sink = next;
+ }
+ globals.fSinkMutex.release();
+}
+
+bool SkEventSink::doEvent(const SkEvent& evt)
+{
+ return this->onEvent(evt);
+}
+
+bool SkEventSink::doQuery(SkEvent* evt)
+{
+ SkASSERT(evt);
+ return this->onQuery(evt);
+}
+
+bool SkEventSink::onEvent(const SkEvent&)
+{
+ return false;
+}
+
+bool SkEventSink::onQuery(SkEvent*)
+{
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTagList* SkEventSink::findTagList(U8CPU tag) const
+{
+ return fTagHead ? SkTagList::Find(fTagHead, tag) : NULL;
+}
+
+void SkEventSink::addTagList(SkTagList* rec)
+{
+ SkASSERT(rec);
+ SkASSERT(fTagHead == NULL || SkTagList::Find(fTagHead, rec->fTag) == NULL);
+
+ rec->fNext = fTagHead;
+ fTagHead = rec;
+}
+
+void SkEventSink::removeTagList(U8CPU tag)
+{
+ if (fTagHead)
+ SkTagList::DeleteTag(&fTagHead, tag);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkListenersTagList : SkTagList {
+ SkListenersTagList(U16CPU count) : SkTagList(kListeners_SkTagList)
+ {
+ fExtra16 = SkToU16(count);
+ fIDs = (SkEventSinkID*)sk_malloc_throw(count * sizeof(SkEventSinkID));
+ }
+ virtual ~SkListenersTagList()
+ {
+ sk_free(fIDs);
+ }
+
+ int countListners() const { return fExtra16; }
+
+ int find(SkEventSinkID id) const
+ {
+ const SkEventSinkID* idptr = fIDs;
+ for (int i = fExtra16 - 1; i >= 0; --i)
+ if (idptr[i] == id)
+ return i;
+ return -1;
+ }
+
+ SkEventSinkID* fIDs;
+};
+
+void SkEventSink::addListenerID(SkEventSinkID id)
+{
+ if (id == 0)
+ return;
+
+ SkListenersTagList* prev = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+ int count = 0;
+
+ if (prev)
+ {
+ if (prev->find(id) >= 0)
+ return;
+ count = prev->countListners();
+ }
+
+ SkListenersTagList* next = SkNEW_ARGS(SkListenersTagList, (count + 1));
+
+ if (prev)
+ {
+ memcpy(next->fIDs, prev->fIDs, count * sizeof(SkEventSinkID));
+ this->removeTagList(kListeners_SkTagList);
+ }
+ next->fIDs[count] = id;
+ this->addTagList(next);
+}
+
+void SkEventSink::copyListeners(const SkEventSink& sink)
+{
+ SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList);
+ if (sinkList == NULL)
+ return;
+ SkASSERT(sinkList->countListners() > 0);
+ const SkEventSinkID* iter = sinkList->fIDs;
+ const SkEventSinkID* stop = iter + sinkList->countListners();
+ while (iter < stop)
+ addListenerID(*iter++);
+}
+
+void SkEventSink::removeListenerID(SkEventSinkID id)
+{
+ if (id == 0)
+ return;
+
+ SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+
+ if (list == NULL)
+ return;
+
+ int index = list->find(id);
+ if (index >= 0)
+ {
+ int count = list->countListners();
+ SkASSERT(count > 0);
+ if (count == 1)
+ this->removeTagList(kListeners_SkTagList);
+ else
+ {
+ // overwrite without resize/reallocating our struct (for speed)
+ list->fIDs[index] = list->fIDs[count - 1];
+ list->fExtra16 = SkToU16(count - 1);
+ }
+ }
+}
+
+bool SkEventSink::hasListeners() const
+{
+ return this->findTagList(kListeners_SkTagList) != NULL;
+}
+
+void SkEventSink::postToListeners(const SkEvent& evt, SkMSec delay)
+{
+ SkListenersTagList* list = (SkListenersTagList*)this->findTagList(kListeners_SkTagList);
+ if (list)
+ {
+ SkASSERT(list->countListners() > 0);
+ const SkEventSinkID* iter = list->fIDs;
+ const SkEventSinkID* stop = iter + list->countListners();
+ while (iter < stop)
+ (SkNEW_ARGS(SkEvent, (evt)))->post(*iter++, delay);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkEventSink::EventResult SkEventSink::DoEvent(const SkEvent& evt, SkEventSinkID sinkID)
+{
+ SkEventSink* sink = SkEventSink::FindSink(sinkID);
+
+ if (sink)
+ {
+#ifdef SK_DEBUG
+ if (evt.isDebugTrace())
+ {
+ SkString etype;
+ evt.getType(&etype);
+ SkDebugf("SkEventTrace: dispatching event <%s> to 0x%x", etype.c_str(), sinkID);
+ const char* idStr = evt.findString("id");
+ if (idStr)
+ SkDebugf(" (%s)", idStr);
+ SkDebugf("\n");
+ }
+#endif
+ return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult;
+ }
+ else
+ {
+#ifdef SK_DEBUG
+ if (sinkID)
+ SkDebugf("DoEvent: Can't find sink for ID(%x)\n", sinkID);
+ else
+ SkDebugf("Event sent to 0 sinkID\n");
+
+ if (evt.isDebugTrace())
+ {
+ SkString etype;
+ evt.getType(&etype);
+ SkDebugf("SkEventTrace: eventsink not found <%s> for 0x%x\n", etype.c_str(), sinkID);
+ }
+#endif
+ return kSinkNotFound_EventResult;
+ }
+}
+
+SkEventSink* SkEventSink::FindSink(SkEventSinkID sinkID)
+{
+ if (sinkID == 0)
+ return 0;
+
+ SkEventSink_Globals& globals = *(SkEventSink_Globals*)SkGlobals::Find(SK_EventSink_GlobalsTag, create_globals);
+ SkAutoMutexAcquire ac(globals.fSinkMutex);
+ SkEventSink* sink = globals.fSinkHead;
+
+ while (sink)
+ {
+ if (sink->getSinkID() == sinkID)
+ return sink;
+ sink = sink->fNextSink;
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+
+#if 0 // experimental, not tested
+
+#include "SkThread.h"
+#include "SkTDict.h"
+
+#define kMinStringBufferSize 128
+static SkMutex gNamedSinkMutex;
+static SkTDict<SkEventSinkID> gNamedSinkIDs(kMinStringBufferSize);
+
+/** Register a name/id pair with the system. If the name already exists,
+ replace its ID with the new id. This pair will persist until UnregisterNamedSink()
+ is called.
+*/
+void SkEventSink::RegisterNamedSinkID(const char name[], SkEventSinkID id)
+{
+ if (id && name && *name)
+ {
+ SkAutoMutexAcquire ac(gNamedSinkMutex);
+ gNamedSinkIDs.set(name, id);
+ }
+}
+
+/** Return the id that matches the specified name (from a previous call to
+ RegisterNamedSinkID(). If no match is found, return 0
+*/
+SkEventSinkID SkEventSink::FindNamedSinkID(const char name[])
+{
+ SkEventSinkID id = 0;
+
+ if (name && *name)
+ {
+ SkAutoMutexAcquire ac(gNamedSinkMutex);
+ (void)gNamedSinkIDs.find(name, &id);
+ }
+ return id;
+}
+
+/** Remove all name/id pairs from the system. This is call internally
+ on shutdown, to ensure no memory leaks. It should not be called
+ before shutdown.
+*/
+void SkEventSink::RemoveAllNamedSinkIDs()
+{
+ SkAutoMutexAcquire ac(gNamedSinkMutex);
+ (void)gNamedSinkIDs.reset();
+}
+#endif
diff --git a/src/views/SkImageView.cpp b/src/views/SkImageView.cpp
new file mode 100644
index 0000000000..9c358c7e26
--- /dev/null
+++ b/src/views/SkImageView.cpp
@@ -0,0 +1,296 @@
+#include "SkImageView.h"
+#include "SkAnimator.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkMatrix.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+SkImageView::SkImageView()
+{
+ fMatrix = NULL;
+ fScaleType = kMatrix_ScaleType;
+
+ fData.fAnim = NULL; // handles initializing the other union values
+ fDataIsAnim = true;
+
+ fUriIsValid = false; // an empty string is not valid
+}
+
+SkImageView::~SkImageView()
+{
+ if (fMatrix)
+ sk_free(fMatrix);
+
+ this->freeData();
+}
+
+void SkImageView::getUri(SkString* uri) const
+{
+ if (uri)
+ *uri = fUri;
+}
+
+void SkImageView::setUri(const char uri[])
+{
+ if (!fUri.equals(uri))
+ {
+ fUri.set(uri);
+ this->onUriChange();
+ }
+}
+
+void SkImageView::setUri(const SkString& uri)
+{
+ if (fUri != uri)
+ {
+ fUri = uri;
+ this->onUriChange();
+ }
+}
+
+void SkImageView::setScaleType(ScaleType st)
+{
+ SkASSERT((unsigned)st <= kFitEnd_ScaleType);
+
+ if ((ScaleType)fScaleType != st)
+ {
+ fScaleType = SkToU8(st);
+ if (fUriIsValid)
+ this->inval(NULL);
+ }
+}
+
+bool SkImageView::getImageMatrix(SkMatrix* matrix) const
+{
+ if (fMatrix)
+ {
+ SkASSERT(!fMatrix->isIdentity());
+ if (matrix)
+ *matrix = *fMatrix;
+ return true;
+ }
+ else
+ {
+ if (matrix)
+ matrix->reset();
+ return false;
+ }
+}
+
+void SkImageView::setImageMatrix(const SkMatrix* matrix)
+{
+ bool changed = false;
+
+ if (matrix && !matrix->isIdentity())
+ {
+ if (fMatrix == NULL)
+ fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+ *fMatrix = *matrix;
+ changed = true;
+ }
+ else // set us to identity
+ {
+ if (fMatrix)
+ {
+ SkASSERT(!fMatrix->isIdentity());
+ sk_free(fMatrix);
+ fMatrix = NULL;
+ changed = true;
+ }
+ }
+
+ // only redraw if we changed our matrix and we're not in scaleToFit mode
+ if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
+ this->inval(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImageView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ if (fUriIsValid)
+ this->inval(NULL);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
+{
+ SkASSERT(st != SkImageView::kMatrix_ScaleType);
+ SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
+
+ SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
+ SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
+ SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
+ SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
+
+ return (SkMatrix::ScaleToFit)(st - 1);
+}
+
+void SkImageView::onDraw(SkCanvas* canvas)
+{
+ SkRect src;
+ if (!this->getDataBounds(&src))
+ {
+ SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
+ return; // nothing to draw
+ }
+
+ SkAutoCanvasRestore restore(canvas, true);
+ SkMatrix matrix;
+
+ if (this->getScaleType() == kMatrix_ScaleType)
+ (void)this->getImageMatrix(&matrix);
+ else
+ {
+ SkRect dst;
+ dst.set(0, 0, this->width(), this->height());
+ matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
+ }
+ canvas->concat(matrix);
+
+ SkPaint paint;
+
+ paint.setAntiAlias(true);
+
+ if (fDataIsAnim)
+ {
+ SkMSec now = SkTime::GetMSecs();
+
+ SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
+
+SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fData.fAnim->getInvalBounds(&bounds);
+ matrix.mapRect(&bounds); // get the bounds into view coordinates
+ this->inval(&bounds);
+ }
+ }
+ else
+ canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
+}
+
+void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* src = dom.findAttr(node, "src");
+ if (src)
+ this->setUri(src);
+
+ int index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
+ if (index >= 0)
+ this->setScaleType((ScaleType)index);
+
+ // need inflate syntax/reader for matrix
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkImageView::onUriChange()
+{
+ if (this->freeData())
+ this->inval(NULL);
+ fUriIsValid = true; // give ensureUriIsLoaded() a shot at the new uri
+}
+
+bool SkImageView::freeData()
+{
+ if (fData.fAnim) // test is valid for all union values
+ {
+ if (fDataIsAnim)
+ delete fData.fAnim;
+ else
+ delete fData.fBitmap;
+
+ fData.fAnim = NULL; // valid for all union values
+ return true;
+ }
+ return false;
+}
+
+bool SkImageView::getDataBounds(SkRect* bounds)
+{
+ SkASSERT(bounds);
+
+ if (this->ensureUriIsLoaded())
+ {
+ SkScalar width, height;
+
+ if (fDataIsAnim)
+ {
+ if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
+ SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
+ {
+ // cons up fake bounds
+ width = this->width();
+ height = this->height();
+ }
+ }
+ else
+ {
+ width = SkIntToScalar(fData.fBitmap->width());
+ height = SkIntToScalar(fData.fBitmap->height());
+ }
+ bounds->set(0, 0, width, height);
+ return true;
+ }
+ return false;
+}
+
+bool SkImageView::ensureUriIsLoaded()
+{
+ if (fData.fAnim) // test is valid for all union values
+ {
+ SkASSERT(fUriIsValid);
+ return true;
+ }
+ if (!fUriIsValid)
+ return false;
+
+ // try to load the url
+ if (fUri.endsWith(".xml")) // assume it is screenplay
+ {
+ SkAnimator* anim = new SkAnimator;
+
+ if (!anim->decodeURI(fUri.c_str()))
+ {
+ delete anim;
+ fUriIsValid = false;
+ return false;
+ }
+ anim->setHostEventSink(this);
+
+ fData.fAnim = anim;
+ fDataIsAnim = true;
+ }
+ else // assume it is an image format
+ {
+ #if 0
+ SkBitmap* bitmap = new SkBitmap;
+
+ if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
+ {
+ delete bitmap;
+ fUriIsValid = false;
+ return false;
+ }
+ fData.fBitmap = bitmap;
+ fDataIsAnim = false;
+ #else
+ return false;
+ #endif
+ }
+ return true;
+}
+
diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp
new file mode 100644
index 0000000000..ba4f02afc9
--- /dev/null
+++ b/src/views/SkListView.cpp
@@ -0,0 +1,895 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+
+#if 0
+
+SkEvent* SkListSource::getEvent(int index)
+{
+ return NULL;
+}
+
+#include "SkOSFile.h"
+
+class SkDirListSource : public SkListSource {
+public:
+ SkDirListSource(const char path[], const char suffix[], const char target[])
+ : fPath(path), fSuffix(suffix), fTarget(target)
+ {
+ fCount = -1;
+ }
+ virtual int countRows()
+ {
+ if (fCount < 0)
+ {
+ fCount = 0;
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ while (fIter.next(NULL))
+ fCount += 1;
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ fIndex = 0;
+ }
+ return fCount;
+ }
+ virtual void getRow(int index, SkString* left, SkString* right)
+ {
+ (void)this->countRows();
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (fIndex > index)
+ {
+ fIter.reset(fPath.c_str(), fSuffix.c_str());
+ fIndex = 0;
+ }
+
+ while (fIndex < index)
+ {
+ fIter.next(NULL);
+ fIndex += 1;
+ }
+
+ if (fIter.next(left))
+ {
+ if (left)
+ left->remove(left->size() - fSuffix.size(), fSuffix.size());
+ }
+ else
+ {
+ if (left)
+ left->reset();
+ }
+ if (right) // only set to ">" if we know we're on a sub-directory
+ right->reset();
+
+ fIndex += 1;
+ }
+ virtual SkEvent* getEvent(int index)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ SkEvent* evt = new SkEvent();
+ SkString label;
+
+ this->getRow(index, &label, NULL);
+ evt->setString("name", label.c_str());
+
+ int c = fPath.c_str()[fPath.size() - 1];
+ if (c != '/' && c != '\\')
+ label.prepend("/");
+ label.prepend(fPath);
+ label.append(fSuffix);
+ evt->setString("path", label.c_str());
+ evt->setS32("index", index);
+ evt->setS32("duration", 22);
+ evt->setType(fTarget);
+ return evt;
+ }
+
+private:
+ SkString fPath, fSuffix;
+ SkString fTarget;
+ SkOSFile::Iter fIter;
+ int fCount;
+ int fIndex;
+};
+
+SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[])
+{
+ return new SkDirListSource(path, suffix, target);
+}
+
+//////////////////////////////////////////////////////////////////
+
+class SkDOMListSource : public SkListSource {
+public:
+ enum Type {
+ kUnknown_Type,
+ kDir_Type,
+ kToggle_Type
+ };
+ struct ItemRec {
+ SkString fLabel;
+ SkString fTail, fAltTail;
+ SkString fTarget;
+ Type fType;
+ };
+
+ SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">")
+ {
+ const SkDOM::Node* child = dom.getFirstChild(node, "item");
+ int count = 0;
+
+ while (child)
+ {
+ count += 1;
+ child = dom.getNextSibling(child, "item");
+ }
+
+ fCount = count;
+ fList = NULL;
+ if (count)
+ {
+ ItemRec* rec = fList = new ItemRec[count];
+
+ child = dom.getFirstChild(node, "item");
+ while (child)
+ {
+ rec->fLabel.set(dom.findAttr(child, "label"));
+ rec->fTail.set(dom.findAttr(child, "tail"));
+ rec->fAltTail.set(dom.findAttr(child, "alt-tail"));
+ rec->fTarget.set(dom.findAttr(child, "target"));
+ rec->fType = kUnknown_Type;
+
+ int index = dom.findList(child, "type", "dir,toggle");
+ if (index >= 0)
+ rec->fType = (Type)(index + 1);
+
+ child = dom.getNextSibling(child, "item");
+ rec += 1;
+ }
+ }
+ }
+ virtual ~SkDOMListSource()
+ {
+ delete[] fList;
+ }
+ virtual int countRows()
+ {
+ return fCount;
+ }
+ virtual void getRow(int index, SkString* left, SkString* right)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (left)
+ *left = fList[index].fLabel;
+ if (right)
+ *right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail;
+ }
+ virtual SkEvent* getEvent(int index)
+ {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+
+ if (fList[index].fType == kDir_Type)
+ {
+ SkEvent* evt = new SkEvent();
+ evt->setType(fList[index].fTarget);
+ evt->setFast32(index);
+ return evt;
+ }
+ if (fList[index].fType == kToggle_Type)
+ fList[index].fTail.swap(fList[index].fAltTail);
+
+ return NULL;
+ }
+
+private:
+ int fCount;
+ ItemRec* fList;
+ SkString fDirTail;
+};
+
+SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node)
+{
+ return new SkDOMListSource(dom, node);
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkListView::SkListView(U32 flags) : SkWidgetView(flags)
+{
+ fSource = NULL;
+ fScrollIndex = 0;
+ fCurrIndex = -1;
+ fRowHeight = SkIntToScalar(16);
+ fVisibleRowCount = 0;
+ fStrCache = NULL;
+
+ fPaint[kBG_Attr].setColor(0);
+ fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14));
+ fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14));
+ fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE);
+ fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE);
+}
+
+SkListView::~SkListView()
+{
+ delete[] fStrCache;
+ fSource->safeUnref();
+}
+
+void SkListView::setRowHeight(SkScalar height)
+{
+ SkASSERT(height >= 0);
+
+ if (fRowHeight != height)
+ {
+ fRowHeight = height;
+ this->inval(NULL);
+ this->onSizeChange();
+ }
+}
+
+void SkListView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+
+ {
+ SkEvent evt;
+ evt.setType("listview-selection");
+ evt.setFast32(index);
+ this->sendEventToParents(evt);
+ }
+ }
+}
+
+void SkListView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRows() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+ this->setSelection(index);
+ }
+}
+
+void SkListView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRows() - 1);
+ this->setSelection(index);
+ }
+}
+
+void SkListView::invalSelection()
+{
+ SkRect r;
+ if (this->getRowRect(fCurrIndex, &r))
+ this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+ if (fSource == NULL)
+ return;
+
+ if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ if (index < 0) // too high
+ fScrollIndex = fCurrIndex;
+ else
+ fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+ this->dirtyStrCache();
+ this->inval(NULL);
+ }
+ }
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+ SkASSERT(r);
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ SkScalar top = index * fRowHeight;
+
+ if (top < this->height())
+ {
+ if (r)
+ r->set(0, top, this->width(), top + fRowHeight);
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPaint& SkListView::paint(Attr attr)
+{
+ SkASSERT((unsigned)attr < kAttrCount);
+ return fPaint[attr];
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ this->dirtyStrCache();
+ this->ensureSelectionIsVisible();
+ this->inval(NULL);
+ }
+ return src;
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ canvas->drawPaint(fPaint[kBG_Attr]);
+
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+ if (visibleCount == 0)
+ return;
+
+ this->ensureStrCache(visibleCount);
+ int currIndex = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)currIndex < (unsigned)visibleCount)
+ {
+ SkAutoCanvasRestore restore(canvas, true);
+ SkRect r;
+
+ canvas->translate(0, currIndex * fRowHeight);
+ (void)this->getRowRect(fScrollIndex, &r);
+ canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+ }
+
+ SkPaint* p;
+ SkScalar y, x = SkIntToScalar(6);
+ SkScalar rite = this->width() - x;
+
+ {
+ SkScalar ascent, descent;
+ fPaint[kNormalText_Attr].measureText(0, NULL, &ascent, &descent);
+ y = SkScalarHalf(fRowHeight - descent + ascent) - ascent;
+ }
+
+ for (int i = 0; i < visibleCount; i++)
+ {
+ if (i == currIndex)
+ p = &fPaint[kHiliteText_Attr];
+ else
+ p = &fPaint[kNormalText_Attr];
+
+ p->setTextAlign(SkPaint::kLeft_Align);
+ canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p);
+ p->setTextAlign(SkPaint::kRight_Align);
+ canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p);
+ canvas->translate(0, fRowHeight);
+ }
+}
+
+void SkListView::onSizeChange()
+{
+ SkScalar count = SkScalarDiv(this->height(), fRowHeight);
+ int n = SkScalarFloor(count);
+
+ // only want to show rows that are mostly visible
+ if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100)
+ n += 1;
+
+ if (fVisibleRowCount != n)
+ {
+ fVisibleRowCount = n;
+ this->ensureSelectionIsVisible();
+ this->dirtyStrCache();
+ }
+}
+
+void SkListView::dirtyStrCache()
+{
+ if (fStrCache)
+ {
+ delete[] fStrCache;
+ fStrCache = NULL;
+ }
+}
+
+void SkListView::ensureStrCache(int count)
+{
+ if (fStrCache == NULL)
+ {
+ fStrCache = new SkString[count << 1];
+
+ if (fSource)
+ for (int i = 0; i < count; i++)
+ fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]);
+ }
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ this->moveSelectionUp();
+ return true;
+ case kDown_SkKey:
+ this->moveSelectionDown();
+ return true;
+ case kRight_SkKey:
+ case kOK_SkKey:
+ if (fSource && fCurrIndex >= 0)
+ {
+ SkEvent* evt = fSource->getEvent(fCurrIndex);
+ if (evt)
+ {
+ SkView* view = this->sendEventToParents(*evt);
+ delete evt;
+ return view != NULL;
+ }
+ else // hack to make toggle work
+ {
+ this->dirtyStrCache();
+ this->inval(NULL);
+ }
+ }
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ SkScalar x;
+ const SkDOM::Node* child;
+
+ if (dom.findScalar(node, "row-height", &x))
+ this->setRowHeight(x);
+
+ if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL)
+ SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+ // look for a listsource
+ {
+ SkListSource* src = NULL;
+
+ if ((child = dom.getFirstChild(node, "file-listsource")) != NULL)
+ {
+ const char* path = dom.findAttr(child, "path");
+ if (path)
+ src = SkListSource::CreateFromDir( path,
+ dom.findAttr(child, "filter"),
+ dom.findAttr(child, "target"));
+ }
+ else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL)
+ {
+ src = SkListSource::CreateFromDOM(dom, child);
+ }
+
+ if (src)
+ {
+ this->setListSource(src)->unref();
+ this->setSelection(0);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+#include "SkShader.h"
+
+class SkScrollBarView : public SkView {
+public:
+ SkScrollBarView(const char bg[], const char fg[])
+ {
+ fBGRef = SkBitmapRef::Decode(bg, true);
+ fFGRef = SkBitmapRef::Decode(fg, true);
+
+ if (fBGRef)
+ this->setWidth(SkIntToScalar(fBGRef->bitmap().width()));
+ }
+ ~SkScrollBarView()
+ {
+ delete fBGRef;
+ delete fFGRef;
+ }
+protected:
+ virtual void onDraw(SkCanvas* canvas)
+ {
+ if (fBGRef == NULL) return;
+
+ SkPaint paint;
+
+ SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+ paint.setShader(shader)->unref();
+
+ canvas->drawPaint(paint);
+ }
+private:
+ SkBitmapRef* fBGRef, *fFGRef;
+};
+
+SkGridView::SkGridView(U32 flags) : SkWidgetView(flags)
+{
+ fSource = NULL;
+ fCurrIndex = -1;
+ fVisibleCount.set(0, 0);
+
+ fPaint[kBG_Attr].setColor(SK_ColorWHITE);
+ fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW);
+ fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style);
+ fPaint[kHiliteCell_Attr].setAntiAliasOn(true);
+ fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3);
+
+ fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg");
+ this->attachChildToFront(fScrollBar)->unref();
+ fScrollBar->setVisibleP(true);
+}
+
+SkGridView::~SkGridView()
+{
+ fSource->safeUnref();
+}
+
+void SkGridView::getCellSize(SkPoint* size) const
+{
+ if (size)
+ *size = fCellSize;
+}
+
+void SkGridView::setCellSize(SkScalar x, SkScalar y)
+{
+ SkASSERT(x >= 0 && y >= 0);
+
+ if (!fCellSize.equals(x, y))
+ {
+ fCellSize.set(x, y);
+ this->inval(NULL);
+ }
+}
+
+void SkGridView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+
+ // this generates the click
+ {
+ SkEvent evt;
+ evt.setType("listview-selection");
+ evt.setFast32(index);
+ this->sendEventToParents(evt);
+ }
+ }
+}
+
+void SkGridView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRows() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+ this->setSelection(index);
+ }
+}
+
+void SkGridView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRows() - 1);
+ this->setSelection(index);
+ }
+}
+
+void SkGridView::invalSelection()
+{
+ SkRect r;
+ if (this->getCellRect(fCurrIndex, &r))
+ {
+ SkScalar inset = 0;
+ if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style)
+ inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2;
+ if (fPaint[kHiliteCell_Attr].isAntiAliasOn())
+ inset += SK_Scalar1;
+ r.inset(-inset, -inset);
+ this->inval(&r);
+ }
+}
+
+void SkGridView::ensureSelectionIsVisible()
+{
+ if (fSource == NULL)
+ return;
+#if 0
+ if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ if (index < 0) // too high
+ fScrollIndex = fCurrIndex;
+ else
+ fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
+
+ this->dirtyStrCache();
+ this->inval(NULL);
+ }
+ }
+#endif
+}
+
+bool SkGridView::getCellRect(int index, SkRect* r) const
+{
+ if (fVisibleCount.fY == 0)
+ return false;
+
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ SkRect bounds;
+ int row = index / fVisibleCount.fY;
+ int col = index % fVisibleCount.fY;
+
+ bounds.set(0, 0, fCellSize.fX, fCellSize.fY);
+ bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)),
+ row * (fCellSize.fY + SkIntToScalar(row > 0)));
+
+ if (bounds.fTop < this->height())
+ {
+ if (r)
+ *r = bounds;
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPaint& SkGridView::paint(Attr attr)
+{
+ SkASSERT((unsigned)attr < kAttrCount);
+ return fPaint[attr];
+}
+
+SkListSource* SkGridView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ // this->dirtyStrCache();
+ this->ensureSelectionIsVisible();
+ this->inval(NULL);
+ }
+ return src;
+}
+
+#include "SkShader.h"
+
+static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint)
+{
+ SkRect src;
+ SkMatrix matrix;
+
+ src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()));
+ if (matrix.setRectToRect(src, dst))
+ {
+ SkPaint p(paint);
+ SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
+ p.setShader(shader)->unref();
+
+ shader->setLocalMatrix(matrix);
+ canvas->drawRect(dst, p);
+ }
+}
+
+#include "SkImageDecoder.h"
+
+void SkGridView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ canvas->drawPaint(fPaint[kBG_Attr]);
+
+ if (fSource == NULL)
+ return;
+
+#if 0
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
+ if (visibleCount == 0)
+ return;
+
+ this->ensureStrCache(visibleCount);
+ int currIndex = this->logicalToVisualIndex(fCurrIndex);
+#endif
+
+ SkPaint p;
+ for (int i = 0; i < fSource->countRows(); i++)
+ {
+ bool forced = false;
+ SkEvent* evt = fSource->getEvent(i);
+ SkASSERT(evt);
+ SkString path(evt->findString("path"));
+ delete evt;
+
+ SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false);
+ if (bmr == NULL)
+ {
+ bmr = SkBitmapRef::Decode(path.c_str(), true);
+ if (bmr)
+ forced = true;
+ }
+
+ if (bmr)
+ {
+ SkAutoTDelete<SkBitmapRef> autoRef(bmr);
+ SkRect r;
+ if (!this->getCellRect(i, &r))
+ break;
+ copybits(canvas, bmr->bitmap(), r, p);
+ }
+ // only draw one forced bitmap at a time
+ if (forced)
+ {
+ this->inval(NULL); // could inval only the remaining visible cells...
+ break;
+ }
+ }
+
+ // draw the hilite
+ {
+ SkRect r;
+ if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r))
+ canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
+ }
+}
+
+static int check_count(int n, SkScalar s)
+{
+ // only want to show cells that are mostly visible
+ if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100)
+ n += 1;
+ return n;
+}
+
+void SkGridView::onSizeChange()
+{
+ fScrollBar->setHeight(this->height());
+ fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0);
+
+ if (fCellSize.equals(0, 0))
+ {
+ fVisibleCount.set(0, 0);
+ return;
+ }
+
+ SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY);
+ SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX);
+ int y = SkScalarFloor(rows);
+ int x = SkScalarFloor(cols);
+
+ y = check_count(y, rows);
+ x = check_count(x, cols);
+
+ if (!fVisibleCount.equals(x, y))
+ {
+ fVisibleCount.set(x, y);
+ this->ensureSelectionIsVisible();
+ // this->dirtyStrCache();
+ }
+}
+
+bool SkGridView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ this->moveSelectionUp();
+ return true;
+ case kDown_SkKey:
+ this->moveSelectionDown();
+ return true;
+ case kRight_SkKey:
+ case kOK_SkKey:
+ if (fSource && fCurrIndex >= 0)
+ {
+ SkEvent* evt = fSource->getEvent(fCurrIndex);
+ if (evt)
+ {
+ // augment the event with our local rect
+ (void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, NULL));
+
+ SkView* view = this->sendEventToParents(*evt);
+ delete evt;
+ return view != NULL;
+ }
+ }
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ SkScalar x[2];
+ const SkDOM::Node* child;
+
+ if (dom.findScalars(node, "cell-size", x, 2))
+ this->setCellSize(x[0], x[1]);
+
+ if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL)
+ SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
+
+ // look for a listsource
+ {
+ SkListSource* src = NULL;
+
+ if ((child = dom.getFirstChild(node, "file-listsource")) != NULL)
+ {
+ const char* path = dom.findAttr(child, "path");
+ if (path)
+ src = SkListSource::CreateFromDir( path,
+ dom.findAttr(child, "filter"),
+ dom.findAttr(child, "target"));
+ }
+ else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL)
+ {
+ src = SkListSource::CreateFromDOM(dom, child);
+ }
+
+ if (src)
+ {
+ this->setListSource(src)->unref();
+ this->setSelection(0);
+ }
+ }
+ this->onSizeChange();
+}
+
+#endif
diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp
new file mode 100644
index 0000000000..89008e7ebe
--- /dev/null
+++ b/src/views/SkListWidget.cpp
@@ -0,0 +1,623 @@
+#include "SkWidgetViews.h"
+
+#include "SkAnimator.h"
+#include "SkScrollBarView.h"
+
+extern void init_skin_anim(const char name[], SkAnimator*);
+
+struct SkListView::BindingRec {
+ SkString fSlotName;
+ int fFieldIndex;
+};
+
+SkListView::SkListView()
+{
+ fSource = NULL; // our list-source
+ fScrollBar = NULL;
+ fAnims = NULL; // array of animators[fVisibleRowCount]
+ fBindings = NULL; // our fields->slot array
+ fBindingCount = 0; // number of entries in fSlots array
+ fScrollIndex = 0; // number of cells to skip before first visible cell
+ fCurrIndex = -1; // index of "selected" cell
+ fVisibleRowCount = 0; // number of cells that can fit in our bounds
+ fAnimContentDirty = true; // true if fAnims[] have their correct content
+ fAnimFocusDirty = true;
+
+ fHeights[kNormal_Height] = SkIntToScalar(16);
+ fHeights[kSelected_Height] = SkIntToScalar(16);
+
+ this->setFlags(this->getFlags() | kFocusable_Mask);
+}
+
+SkListView::~SkListView()
+{
+ SkSafeUnref(fScrollBar);
+ SkSafeUnref(fSource);
+ delete[] fAnims;
+ delete[] fBindings;
+}
+
+void SkListView::setHasScrollBar(bool hasSB)
+{
+ if (hasSB != this->hasScrollBar())
+ {
+ if (hasSB)
+ {
+ SkASSERT(fScrollBar == NULL);
+ fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
+ fScrollBar->setVisibleP(true);
+ this->attachChildToFront(fScrollBar);
+ fScrollBar->setHeight(this->height()); // assume it auto-sets its width
+ // fScrollBar->setLoc(this->getContentWidth(), 0);
+ fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+ }
+ else
+ {
+ SkASSERT(fScrollBar);
+ fScrollBar->detachFromParent();
+ fScrollBar->unref();
+ fScrollBar = NULL;
+ }
+ this->dirtyCache(kAnimContent_DirtyFlag);
+ }
+}
+
+void SkListView::setSelection(int index)
+{
+ if (fCurrIndex != index)
+ {
+ fAnimFocusDirty = true;
+ this->inval(NULL);
+
+ this->invalSelection();
+ fCurrIndex = index;
+ this->invalSelection();
+ this->ensureSelectionIsVisible();
+ }
+}
+
+bool SkListView::moveSelectionUp()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = fSource->countRecords() - 1;
+ else
+ index = SkMax32(index - 1, 0);
+
+ if (fCurrIndex != index)
+ {
+ this->setSelection(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SkListView::moveSelectionDown()
+{
+ if (fSource)
+ {
+ int index = fCurrIndex;
+ if (index < 0) // no selection
+ index = 0;
+ else
+ index = SkMin32(index + 1, fSource->countRecords() - 1);
+
+ if (fCurrIndex != index)
+ {
+ this->setSelection(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SkListView::invalSelection()
+{
+ SkRect r;
+ if (this->getRowRect(fCurrIndex, &r))
+ this->inval(&r);
+}
+
+void SkListView::ensureSelectionIsVisible()
+{
+ if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
+ {
+ int index = this->logicalToVisualIndex(fCurrIndex);
+
+ if ((unsigned)index >= (unsigned)fVisibleRowCount) // need to scroll
+ {
+ int newIndex;
+
+ if (index < 0) // too high
+ newIndex = fCurrIndex;
+ else
+ newIndex = fCurrIndex - fVisibleRowCount + 1;
+ SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
+ this->inval(NULL);
+
+ if (fScrollIndex != newIndex)
+ {
+ fScrollIndex = newIndex;
+ if (fScrollBar)
+ fScrollBar->setStart(newIndex);
+ this->dirtyCache(kAnimContent_DirtyFlag);
+ }
+ }
+ }
+}
+
+SkScalar SkListView::getContentWidth() const
+{
+ SkScalar width = this->width();
+
+ if (fScrollBar)
+ {
+ width -= fScrollBar->width();
+ if (width < 0)
+ width = 0;
+ }
+ return width;
+}
+
+bool SkListView::getRowRect(int index, SkRect* r) const
+{
+ SkASSERT(r);
+
+ index = this->logicalToVisualIndex(index);
+ if (index >= 0)
+ {
+ int selection = this->logicalToVisualIndex(fCurrIndex);
+
+ SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
+ SkScalar top = index * fHeights[kNormal_Height];
+
+ if (index > selection && selection >= 0)
+ top += fHeights[kSelected_Height] - fHeights[kNormal_Height];
+
+ if (top < this->height())
+ {
+ if (r)
+ r->set(0, top, this->getContentWidth(), top + height);
+ return true;
+ }
+ }
+ return false;
+}
+
+SkListSource* SkListView::setListSource(SkListSource* src)
+{
+ if (fSource != src)
+ {
+ SkRefCnt_SafeAssign(fSource, src);
+ this->ensureSelectionIsVisible();
+ this->inval(NULL);
+
+ if (fScrollBar)
+ fScrollBar->setTotal(fSource->countRecords());
+ }
+ return src;
+}
+
+void SkListView::dirtyCache(unsigned dirtyFlags)
+{
+ if (dirtyFlags & kAnimCount_DirtyFlag)
+ {
+ delete fAnims;
+ fAnims = NULL;
+ fAnimContentDirty = true;
+ fAnimFocusDirty = true;
+ }
+ if (dirtyFlags & kAnimContent_DirtyFlag)
+ {
+ if (!fAnimContentDirty)
+ {
+ this->inval(NULL);
+ fAnimContentDirty = true;
+ }
+ fAnimFocusDirty = true;
+ }
+}
+
+bool SkListView::ensureCache()
+{
+ if (fSkinName.size() == 0)
+ return false;
+
+ if (fAnims == NULL)
+ {
+ int n = SkMax32(1, fVisibleRowCount);
+
+ SkASSERT(fAnimContentDirty);
+ fAnims = new SkAnimator[n];
+ for (int i = 0; i < n; i++)
+ {
+ fAnims[i].setHostEventSink(this);
+ init_skin_anim(fSkinName.c_str(), &fAnims[i]);
+ }
+
+ fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
+ fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
+
+ fAnimFocusDirty = true;
+ }
+
+ if (fAnimContentDirty && fSource)
+ {
+ fAnimContentDirty = false;
+
+ SkString str;
+ SkEvent evt("user");
+ evt.setString("id", "setFields");
+ evt.setS32("rowCount", fVisibleRowCount);
+
+ SkEvent dimEvt("user");
+ dimEvt.setString("id", "setDim");
+ dimEvt.setScalar("dimX", this->getContentWidth());
+ dimEvt.setScalar("dimY", this->height());
+
+ for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+ {
+ evt.setS32("relativeIndex", i - fScrollIndex);
+ for (int j = 0; j < fBindingCount; j++)
+ {
+ fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
+//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
+ evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
+ }
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
+ }
+ fAnimFocusDirty = true;
+ }
+
+ if (fAnimFocusDirty)
+ {
+//SkDEBUGF(("service fAnimFocusDirty\n"));
+ fAnimFocusDirty = false;
+
+ SkEvent focusEvt("user");
+ focusEvt.setString("id", "setFocus");
+
+ for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
+ {
+ focusEvt.setS32("FOCUS", i == fCurrIndex);
+ (void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
+ }
+ }
+
+ return true;
+}
+
+void SkListView::ensureVisibleRowCount()
+{
+ SkScalar height = this->height();
+ int n = 0;
+
+ if (height > 0)
+ {
+ n = 1;
+ height -= fHeights[kSelected_Height];
+ if (height > 0)
+ {
+ SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
+ n += SkScalarFloor(count);
+ if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
+ n += 1;
+
+ // SkDebugf("count %g, n %d\n", count/65536., n);
+ }
+ }
+
+ if (fVisibleRowCount != n)
+ {
+ if (fScrollBar)
+ fScrollBar->setShown(n);
+
+ fVisibleRowCount = n;
+ this->ensureSelectionIsVisible();
+ this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+void SkListView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+
+ if (fScrollBar)
+ fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
+
+ this->ensureVisibleRowCount();
+}
+
+void SkListView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ this->ensureVisibleRowCount();
+
+ int visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
+ if (visibleCount == 0 || !this->ensureCache())
+ return;
+
+//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
+
+ SkAutoCanvasRestore ar(canvas, true);
+ SkMSec now = SkTime::GetMSecs();
+ SkRect bounds;
+
+ bounds.fLeft = 0;
+ bounds.fRight = this->getContentWidth();
+ bounds.fBottom = 0;
+ // assign bounds.fTop inside the loop
+
+ // hack to reveal our bounds for debugging
+ if (this->hasFocus())
+ canvas->drawARGB(0x11, 0, 0, 0xFF);
+ else
+ canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
+
+ for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
+ {
+ SkPaint paint;
+ SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
+
+ bounds.fTop = bounds.fBottom;
+ bounds.fBottom += height;
+
+ canvas->save();
+ if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
+ this->inval(&bounds);
+ canvas->restore();
+
+ canvas->translate(0, height);
+ }
+}
+
+bool SkListView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key))
+ {
+ switch (evt.getFast32()) {
+ case kUp_SkKey:
+ return this->moveSelectionUp();
+ case kDown_SkKey:
+ return this->moveSelectionDown();
+ case kRight_SkKey:
+ case kOK_SkKey:
+ this->postWidgetEvent();
+ return true;
+ default:
+ break;
+ }
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const char gListViewEventSlot[] = "sk-listview-slot-name";
+
+/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
+ fSource->prepareWidgetEvent(evt, fCurrIndex))
+ {
+ evt->setS32(gListViewEventSlot, fCurrIndex);
+ return true;
+ }
+ return false;
+}
+
+int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
+{
+ int32_t index;
+
+ return evt.findS32(gListViewEventSlot, &index) ? index : -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ {
+ bool hasScrollBar;
+ if (dom.findBool(node, "scrollBar", &hasScrollBar))
+ this->setHasScrollBar(hasScrollBar);
+ }
+
+ const SkDOM::Node* child;
+
+ if ((child = dom.getFirstChild(node, "bindings")) != NULL)
+ {
+ delete[] fBindings;
+ fBindings = NULL;
+ fBindingCount = 0;
+
+ SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
+ SkASSERT(listSrc);
+ fSkinName.set(dom.findAttr(child, "skin-slots"));
+ SkASSERT(fSkinName.size());
+
+ this->setListSource(listSrc)->unref();
+
+ int count = dom.countChildren(child, "bind");
+ if (count > 0)
+ {
+ fBindings = new BindingRec[count];
+ count = 0; // reuse this to count up to the number of valid bindings
+
+ child = dom.getFirstChild(child, "bind");
+ SkASSERT(child);
+ do {
+ const char* fieldName = dom.findAttr(child, "field");
+ const char* slotName = dom.findAttr(child, "slot");
+ if (fieldName && slotName)
+ {
+ fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
+ if (fBindings[count].fFieldIndex >= 0)
+ fBindings[count++].fSlotName.set(slotName);
+ }
+ } while ((child = dom.getNextSibling(child, "bind")) != NULL);
+
+ fBindingCount = SkToU16(count);
+ if (count == 0)
+ {
+ SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
+ delete[] fBindings;
+ }
+ }
+ this->dirtyCache(kAnimCount_DirtyFlag);
+ this->setSelection(0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkXMLListSource : public SkListSource {
+public:
+ SkXMLListSource(const char doc[], size_t len);
+ virtual ~SkXMLListSource()
+ {
+ delete[] fFields;
+ delete[] fRecords;
+ }
+
+ virtual int countFields() { return fFieldCount; }
+ virtual void getFieldName(int index, SkString* field)
+ {
+ SkASSERT((unsigned)index < (unsigned)fFieldCount);
+ if (field)
+ *field = fFields[index];
+ }
+ virtual int findFieldIndex(const char field[])
+ {
+ for (int i = 0; i < fFieldCount; i++)
+ if (fFields[i].equals(field))
+ return i;
+ return -1;
+ }
+
+ virtual int countRecords() { return fRecordCount; }
+ virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
+ {
+ SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
+ SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
+ if (data)
+ *data = fRecords[rowIndex * fFieldCount + fieldIndex];
+ }
+
+ virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
+ {
+ // hack, for testing right now. Need the xml to tell us what to jam in and where
+ SkString data;
+
+ this->getRecord(rowIndex, 0, &data);
+ evt->setString("xml-listsource", data.c_str());
+ return true;
+ }
+
+private:
+ SkString* fFields; // [fFieldCount]
+ SkString* fRecords; // [fRecordCount][fFieldCount]
+ int fFieldCount, fRecordCount;
+};
+
+#include "SkDOM.h"
+
+SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
+{
+ fFieldCount = fRecordCount = 0;
+ fFields = fRecords = NULL;
+
+ SkDOM dom;
+
+ const SkDOM::Node* node = dom.build(doc, len);
+ SkASSERT(node);
+ const SkDOM::Node* child;
+
+ child = dom.getFirstChild(node, "fields");
+ if (child)
+ {
+ fFieldCount = dom.countChildren(child, "field");
+ fFields = new SkString[fFieldCount];
+
+ int n = 0;
+ child = dom.getFirstChild(child, "field");
+ while (child)
+ {
+ fFields[n].set(dom.findAttr(child, "name"));
+ child = dom.getNextSibling(child, "field");
+ n += 1;
+ }
+ SkASSERT(n == fFieldCount);
+ }
+
+ child = dom.getFirstChild(node, "records");
+ if (child)
+ {
+ fRecordCount = dom.countChildren(child, "record");
+ fRecords = new SkString[fRecordCount * fFieldCount];
+
+ int n = 0;
+ child = dom.getFirstChild(child, "record");
+ while (child)
+ {
+ for (int i = 0; i < fFieldCount; i++)
+ fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
+ child = dom.getNextSibling(child, "record");
+ n += 1;
+ }
+ SkASSERT(n == fRecordCount);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+SkListSource* SkListSource::Factory(const char name[])
+{
+ static const char gDoc[] =
+ "<db name='contacts.db'>"
+ "<fields>"
+ "<field name='name'/>"
+ "<field name='work-num'/>"
+ "<field name='home-num'/>"
+ "<field name='type'/>"
+ "</fields>"
+ "<records>"
+ "<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
+ "<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
+ "<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
+ "<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
+ "<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
+ "<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
+ "<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
+ "<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
+ "<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
+ "</records>"
+ "</db>";
+
+//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
+ return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
+}
+
+
+
diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
new file mode 100644
index 0000000000..3760ddd5cb
--- /dev/null
+++ b/src/views/SkOSMenu.cpp
@@ -0,0 +1,53 @@
+#include "SkOSMenu.h"
+
+static int gOSMenuCmd = 7000;
+
+SkOSMenu::SkOSMenu(const char title[])
+{
+ fTitle = title;
+}
+
+SkOSMenu::~SkOSMenu()
+{
+}
+
+int SkOSMenu::countItems() const
+{
+ return fItems.count();
+}
+
+void SkOSMenu::appendItem(const char title[], const char eventType[], int32_t eventData)
+{
+ Item* item = fItems.append();
+
+ item->fTitle = title;
+ item->fEventType = eventType;
+ item->fEventData = eventData;
+ item->fOSCmd = ++gOSMenuCmd;
+}
+
+SkEvent* SkOSMenu::createEvent(uint32_t os_cmd)
+{
+ const Item* iter = fItems.begin();
+ const Item* stop = fItems.end();
+
+ while (iter < stop)
+ {
+ if (iter->fOSCmd == os_cmd)
+ {
+ SkEvent* evt = new SkEvent(iter->fEventType);
+ evt->setFast32(iter->fEventData);
+ return evt;
+ }
+ iter++;
+ }
+ return NULL;
+}
+
+const char* SkOSMenu::getItem(int index, uint32_t* cmdID) const
+{
+ if (cmdID)
+ *cmdID = fItems[index].fOSCmd;
+ return fItems[index].fTitle;
+}
+
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000000..a79f30c150
--- /dev/null
+++ b/src/views/SkParsePaint.cpp
@@ -0,0 +1,103 @@
+#include "SkParsePaint.h"
+#include "SkTSearch.h"
+#include "SkParse.h"
+#include "SkImageDecoder.h"
+#include "SkGradientShader.h"
+
+static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node)
+{
+ if ((node = dom.getFirstChild(node, "shader")) == NULL)
+ return NULL;
+
+ const char* str;
+
+ if (dom.hasAttr(node, "type", "linear-gradient"))
+ {
+ SkColor colors[2];
+ SkPoint pts[2];
+
+ colors[0] = colors[1] = SK_ColorBLACK; // need to initialized the alpha to opaque, since FindColor doesn't set it
+ if ((str = dom.findAttr(node, "c0")) != NULL &&
+ SkParse::FindColor(str, &colors[0]) &&
+ (str = dom.findAttr(node, "c1")) != NULL &&
+ SkParse::FindColor(str, &colors[1]) &&
+ dom.findScalars(node, "p0", &pts[0].fX, 2) &&
+ dom.findScalars(node, "p1", &pts[1].fX, 2))
+ {
+ SkShader::TileMode mode = SkShader::kClamp_TileMode;
+ int index;
+
+ if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+ mode = (SkShader::TileMode)index;
+ return SkGradientShader::CreateLinear(pts, colors, NULL, 2, mode);
+ }
+ }
+ else if (dom.hasAttr(node, "type", "bitmap"))
+ {
+ if ((str = dom.findAttr(node, "src")) == NULL)
+ return NULL;
+
+ SkBitmap bm;
+
+ if (SkImageDecoder::DecodeFile(str, &bm))
+ {
+ SkShader::TileMode mode = SkShader::kRepeat_TileMode;
+ int index;
+
+ if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+ mode = (SkShader::TileMode)index;
+
+ return SkShader::CreateBitmapShader(bm, mode, mode);
+ }
+ }
+ return NULL;
+}
+
+void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(paint);
+ SkASSERT(&dom);
+ SkASSERT(node);
+
+ SkScalar x;
+
+ if (dom.findScalar(node, "stroke-width", &x))
+ paint->setStrokeWidth(x);
+ if (dom.findScalar(node, "text-size", &x))
+ paint->setTextSize(x);
+
+ bool b;
+
+ SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b));
+
+ if (dom.findBool(node, "is-stroke", &b))
+ paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+ if (dom.findBool(node, "is-antialias", &b))
+ paint->setAntiAlias(b);
+ if (dom.findBool(node, "is-lineartext", &b))
+ paint->setLinearText(b);
+
+ const char* str = dom.findAttr(node, "color");
+ if (str)
+ {
+ SkColor c = paint->getColor();
+ if (SkParse::FindColor(str, &c))
+ paint->setColor(c);
+ }
+
+ // do this AFTER parsing for the color
+ if (dom.findScalar(node, "opacity", &x))
+ {
+ x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1));
+ paint->setAlpha(SkScalarRound(x * 255));
+ }
+
+ int index = dom.findList(node, "text-anchor", "left,center,right");
+ if (index >= 0)
+ paint->setTextAlign((SkPaint::Align)index);
+
+ SkShader* shader = inflate_shader(dom, node);
+ if (shader)
+ paint->setShader(shader)->unref();
+}
+
diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp
new file mode 100644
index 0000000000..a9fd516458
--- /dev/null
+++ b/src/views/SkProgressBarView.cpp
@@ -0,0 +1,102 @@
+#include "SkProgressBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkTime.h"
+#include "SkSystemEventTypes.h"
+
+SkProgressBarView::SkProgressBarView()
+{
+ init_skin_anim(kProgress_SkinEnum, &fAnim);
+ fAnim.setHostEventSink(this);
+ fProgress = 0;
+ fMax = 100;
+
+}
+
+void SkProgressBarView::changeProgress(int diff)
+{
+ int newProg = fProgress + diff;
+ if (newProg > 0 && newProg < fMax)
+ this->setProgress(newProg);
+ //otherwise i'll just leave it as it is
+ //this implies that if a new max and progress are set, max must be set first
+}
+
+/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(NULL);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar height;
+
+ if (evt.findScalar("y", &height))
+ this->setHeight(height);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+ int32_t temp;
+ if (dom.findS32(node, "max", &temp))
+ this->setMax(temp);
+ if (dom.findS32(node, "progress", &temp))
+ this->setProgress(temp);
+}
+
+/*virtual*/ void SkProgressBarView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+void SkProgressBarView::reset()
+{
+ fProgress = 0;
+ SkEvent e("user");
+ e.setString("id", "reset");
+ fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setMax(int max)
+{
+ fMax = max;
+ SkEvent e("user");
+ e.setString("id", "setMax");
+ e.setS32("newMax", max);
+ fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setProgress(int progress)
+{
+ fProgress = progress;
+ SkEvent e("user");
+ e.setString("id", "setProgress");
+ e.setS32("newProgress", progress);
+ fAnim.doUserEvent(e);
+}
diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp
new file mode 100644
index 0000000000..0c81bccacd
--- /dev/null
+++ b/src/views/SkProgressView.cpp
@@ -0,0 +1,125 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+
+SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL)
+{
+ fValue = 0;
+ fMax = 0;
+ fInterp = NULL;
+ fDoInterp = false;
+}
+
+SkProgressView::~SkProgressView()
+{
+ delete fInterp;
+ SkSafeUnref(fOnShader);
+ SkSafeUnref(fOffShader);
+}
+
+void SkProgressView::setMax(U16CPU max)
+{
+ if (fMax != max)
+ {
+ fMax = SkToU16(max);
+ if (fValue > 0)
+ this->inval(NULL);
+ }
+}
+
+void SkProgressView::setValue(U16CPU value)
+{
+ if (fValue != value)
+ {
+ if (fDoInterp)
+ {
+ if (fInterp)
+ delete fInterp;
+ fInterp = new SkInterpolator(1, 2);
+ SkScalar x = (SkScalar)(fValue << 8);
+ fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0);
+ x = (SkScalar)(value << 8);
+ fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x);
+ }
+ fValue = SkToU16(value);
+ this->inval(NULL);
+ }
+}
+
+void SkProgressView::onDraw(SkCanvas* canvas)
+{
+ if (fMax == 0)
+ return;
+
+ SkFixed percent;
+
+ if (fInterp)
+ {
+ SkScalar x;
+ if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result)
+ {
+ delete fInterp;
+ fInterp = NULL;
+ }
+ percent = (SkFixed)x; // now its 16.8
+ percent = SkMax32(0, SkMin32(percent, fMax << 8)); // now its pinned
+ percent = SkFixedDiv(percent, fMax << 8); // now its 0.16
+ this->inval(NULL);
+ }
+ else
+ {
+ U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+ percent = SkFixedDiv(value, fMax);
+ }
+
+
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+ p.setAntiAlias(true);
+
+ r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+ p.setStyle(SkPaint::kFill_Style);
+
+ p.setColor(SK_ColorDKGRAY);
+ p.setShader(fOnShader);
+ canvas->drawRect(r, p);
+
+ p.setColor(SK_ColorWHITE);
+ p.setShader(fOffShader);
+ r.fLeft = r.fRight;
+ r.fRight = this->width() - SK_Scalar1;
+ if (r.width() > 0)
+ canvas->drawRect(r, p);
+}
+
+#include "SkImageDecoder.h"
+
+static SkShader* inflate_shader(const char file[])
+{
+ SkBitmap bm;
+
+ return SkImageDecoder::DecodeFile(file, &bm) ?
+ SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) :
+ NULL;
+}
+
+void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* s;
+
+ SkASSERT(fOnShader == NULL);
+ SkASSERT(fOffShader == NULL);
+
+ if ((s = dom.findAttr(node, "src-on")) != NULL)
+ fOnShader = inflate_shader(s);
+ if ((s = dom.findAttr(node, "src-off")) != NULL)
+ fOffShader = inflate_shader(s);
+ (void)dom.findBool(node, "do-interp", &fDoInterp);
+}
+
diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp
new file mode 100644
index 0000000000..2e087bd1ae
--- /dev/null
+++ b/src/views/SkScrollBarView.cpp
@@ -0,0 +1,139 @@
+#include "SkScrollBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+//see SkProgressBarView.cpp
+//#include "SkWidgetViews.cpp"
+
+SkScrollBarView::SkScrollBarView()
+{
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kScroll_SkinEnum, &fAnim);
+
+ fTotalLength = 0;
+ fStartPoint = 0;
+ fShownLength = 0;
+
+ this->adjust();
+}
+
+void SkScrollBarView::setStart(unsigned start)
+{
+ if ((int)start < 0)
+ start = 0;
+
+ if (fStartPoint != start)
+ {
+ fStartPoint = start;
+ this->adjust();
+ }
+}
+
+void SkScrollBarView::setShown(unsigned shown)
+{
+ if ((int)shown < 0)
+ shown = 0;
+
+ if (fShownLength != shown)
+ {
+ fShownLength = shown;
+ this->adjust();
+ }
+}
+
+void SkScrollBarView::setTotal(unsigned total)
+{
+ if ((int)total < 0)
+ total = 0;
+
+ if (fTotalLength != total)
+ {
+ fTotalLength = total;
+ this->adjust();
+ }
+}
+
+/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int32_t value;
+ if (dom.findS32(node, "total", &value))
+ this->setTotal(value);
+ if (dom.findS32(node, "shown", &value))
+ this->setShown(value);
+}
+
+/*virtual*/ void SkScrollBarView::onSizeChange()
+{
+ this->INHERITED::onSizeChange();
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas)
+{
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+}
+
+/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(NULL);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar width;
+
+ if (evt.findScalar("x", &width))
+ this->setWidth(width);
+ return true;
+ }
+
+ return this->INHERITED::onEvent(evt);
+}
+
+void SkScrollBarView::adjust()
+{
+ int total = fTotalLength;
+ int start = fStartPoint;
+ int shown = fShownLength;
+ int hideBar = 0;
+
+ if (total <= 0 || shown <= 0 || shown >= total) // no bar to show
+ {
+ total = 1; // avoid divide-by-zero. should be done by skin/script
+ hideBar = 1; // signal we don't want a thumb
+ }
+ else
+ {
+ if (start + shown > total)
+ start = total - shown;
+ }
+
+ SkEvent e("user");
+ e.setString("id", "adjustScrollBar");
+ e.setScalar("_totalLength", SkIntToScalar(total));
+ e.setScalar("_startPoint", SkIntToScalar(start));
+ e.setScalar("_shownLength", SkIntToScalar(shown));
+// e.setS32("hideBar", hideBar);
+ fAnim.doUserEvent(e);
+}
+
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
new file mode 100644
index 0000000000..ae2e9e81a5
--- /dev/null
+++ b/src/views/SkStackViewLayout.cpp
@@ -0,0 +1,262 @@
+#include "SkStackViewLayout.h"
+
+SkStackViewLayout::SkStackViewLayout()
+{
+ fMargin.set(0, 0, 0, 0);
+ fSpacer = 0;
+ fOrient = kHorizontal_Orient;
+ fPack = kStart_Pack;
+ fAlign = kStart_Align;
+ fRound = false;
+}
+
+void SkStackViewLayout::setOrient(Orient ori)
+{
+ SkASSERT((unsigned)ori < kOrientCount);
+ fOrient = SkToU8(ori);
+}
+
+void SkStackViewLayout::getMargin(SkRect* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStackViewLayout::setMargin(const SkRect& margin)
+{
+ fMargin = margin;
+}
+
+void SkStackViewLayout::setSpacer(SkScalar spacer)
+{
+ fSpacer = spacer;
+}
+
+void SkStackViewLayout::setPack(Pack pack)
+{
+ SkASSERT((unsigned)pack < kPackCount);
+ fPack = SkToU8(pack);
+}
+
+void SkStackViewLayout::setAlign(Align align)
+{
+ SkASSERT((unsigned)align < kAlignCount);
+ fAlign = SkToU8(align);
+}
+
+void SkStackViewLayout::setRound(bool r)
+{
+ fRound = SkToU8(r);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef SkScalar (*AlignProc)(SkScalar childLimit, SkScalar parentLimit);
+typedef SkScalar (SkView::*GetSizeProc)() const;
+typedef void (SkView::*SetLocProc)(SkScalar coord);
+typedef void (SkView::*SetSizeProc)(SkScalar coord);
+
+static SkScalar left_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+static SkScalar center_align_proc(SkScalar childLimit, SkScalar parentLimit) { return SkScalarHalf(parentLimit - childLimit); }
+static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
+static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
+
+/* Measure the main-dimension for all the children. If a child is marked flex in that direction
+ ignore its current value but increment the counter for flexChildren
+*/
+static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
+ uint32_t flexMask, int* flexCount)
+{
+ SkView::B2FIter iter(parent);
+ SkView* child;
+ SkScalar limit = 0;
+ int n = 0, flex = 0;
+
+ while ((child = iter.next()) != NULL)
+ {
+ n += 1;
+ if (child->getFlags() & flexMask)
+ flex += 1;
+ else
+ limit += (child->*sizeProc)();
+ }
+ if (count)
+ *count = n;
+ if (flexCount)
+ *flexCount = flex;
+ return limit;
+}
+
+void SkStackViewLayout::onLayoutChildren(SkView* parent)
+{
+ static AlignProc gAlignProcs[] = {
+ left_align_proc,
+ center_align_proc,
+ right_align_proc,
+ fill_align_proc
+ };
+
+ SkScalar startM, endM, crossStartM, crossLimit;
+ GetSizeProc mainGetSizeP, crossGetSizeP;
+ SetLocProc mainLocP, crossLocP;
+ SetSizeProc mainSetSizeP, crossSetSizeP;
+ SkView::Flag_Mask flexMask;
+
+ if (fOrient == kHorizontal_Orient)
+ {
+ startM = fMargin.fLeft;
+ endM = fMargin.fRight;
+ crossStartM = fMargin.fTop;
+ crossLimit = -fMargin.fTop - fMargin.fBottom;
+
+ mainGetSizeP = &SkView::width;
+ crossGetSizeP = &SkView::height;
+ mainLocP = &SkView::setLocX;
+ crossLocP = &SkView::setLocY;
+
+ mainSetSizeP = &SkView::setWidth;
+ crossSetSizeP = &SkView::setHeight;
+
+ flexMask = SkView::kFlexH_Mask;
+ }
+ else
+ {
+ startM = fMargin.fTop;
+ endM = fMargin.fBottom;
+ crossStartM = fMargin.fLeft;
+ crossLimit = -fMargin.fLeft - fMargin.fRight;
+
+ mainGetSizeP = &SkView::height;
+ crossGetSizeP = &SkView::width;
+ mainLocP = &SkView::setLocY;
+ crossLocP = &SkView::setLocX;
+
+ mainSetSizeP = &SkView::setHeight;
+ crossSetSizeP = &SkView::setWidth;
+
+ flexMask = SkView::kFlexV_Mask;
+ }
+ crossLimit += (parent->*crossGetSizeP)();
+ if (fAlign != kStretch_Align)
+ crossSetSizeP = NULL;
+
+ int childCount, flexCount;
+ SkScalar childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
+
+ if (childCount == 0)
+ return;
+
+ childLimit += (childCount - 1) * fSpacer;
+
+ SkScalar parentLimit = (parent->*mainGetSizeP)() - startM - endM;
+ SkScalar pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
+ SkScalar flexAmount = 0;
+ SkView::B2FIter iter(parent);
+ SkView* child;
+
+ if (flexCount > 0 && parentLimit > childLimit)
+ flexAmount = (parentLimit - childLimit) / flexCount;
+
+ while ((child = iter.next()) != NULL)
+ {
+ if (fRound)
+ pos = SkIntToScalar(SkScalarRound(pos));
+ (child->*mainLocP)(pos);
+ SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
+ if (fRound)
+ crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
+ (child->*crossLocP)(crossLoc);
+
+ if (crossSetSizeP)
+ (child->*crossSetSizeP)(crossLimit);
+ if (child->getFlags() & flexMask)
+ (child->*mainSetSizeP)(flexAmount);
+ pos += (child->*mainGetSizeP)() + fSpacer;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ int index;
+ SkScalar value[4];
+
+ if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
+ this->setOrient((Orient)index);
+ else
+ assert_no_attr(dom, node, "orient");
+
+ if (dom.findScalars(node, "margin", value, 4))
+ {
+ SkRect margin;
+ margin.set(value[0], value[1], value[2], value[3]);
+ this->setMargin(margin);
+ }
+ else
+ assert_no_attr(dom, node, "margin");
+
+ if (dom.findScalar(node, "spacer", value))
+ this->setSpacer(value[0]);
+ else
+ assert_no_attr(dom, node, "spacer");
+
+ if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
+ this->setPack((Pack)index);
+ else
+ assert_no_attr(dom, node, "pack");
+
+ if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
+ this->setAlign((Align)index);
+ else
+ assert_no_attr(dom, node, "align");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+SkFillViewLayout::SkFillViewLayout()
+{
+ fMargin.setEmpty();
+}
+
+void SkFillViewLayout::getMargin(SkRect* r) const
+{
+ if (r)
+ *r = fMargin;
+}
+
+void SkFillViewLayout::setMargin(const SkRect& margin)
+{
+ fMargin = margin;
+}
+
+void SkFillViewLayout::onLayoutChildren(SkView* parent)
+{
+ SkView::B2FIter iter(parent);
+ SkView* child;
+
+ while ((child = iter.next()) != NULL)
+ {
+ child->setLoc(fMargin.fLeft, fMargin.fTop);
+ child->setSize( parent->width() - fMargin.fRight - fMargin.fLeft,
+ parent->height() - fMargin.fBottom - fMargin.fTop);
+ }
+}
+
+void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+ (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
+}
+
diff --git a/src/views/SkStaticTextView.cpp b/src/views/SkStaticTextView.cpp
new file mode 100644
index 0000000000..8fb8bc12a5
--- /dev/null
+++ b/src/views/SkStaticTextView.cpp
@@ -0,0 +1,177 @@
+#include "SkWidgetViews.h"
+#include "SkTextBox.h"
+
+#ifdef SK_DEBUG
+static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+{
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+}
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+SkStaticTextView::SkStaticTextView()
+{
+ fMargin.set(0, 0);
+ fMode = kFixedSize_Mode;
+ fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+
+// init_skin_paint(kStaticText_SkinEnum, &fPaint);
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+ if (fMode == kAutoWidth_Mode)
+ {
+ SkScalar width = fPaint.measureText(fText.c_str(), fText.size());
+ this->setWidth(width + fMargin.fX * 2);
+ }
+ else if (fMode == kAutoHeight_Mode)
+ {
+ SkScalar width = this->width() - fMargin.fX * 2;
+ int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+ this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2);
+ }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+
+ if (fMode != mode)
+ {
+ fMode = SkToU8(mode);
+ this->computeSize();
+ }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+ fSpacingAlign = SkToU8(align);
+ this->inval(NULL);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+ if (fMargin.fX != dx || fMargin.fY != dy)
+ {
+ fMargin.set(dx, dy);
+ this->computeSize();
+ this->inval(NULL);
+ }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+ if (text)
+ *text = fText;
+ return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+ if (text)
+ memcpy(text, fText.c_str(), fText.size());
+ return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+ this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+ if (text == NULL)
+ text = "";
+ this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+ if (!fText.equals(text, len))
+ {
+ fText.set(text, len);
+ this->computeSize();
+ this->inval(NULL);
+ }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+ if (paint)
+ *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+ if (fPaint != paint)
+ {
+ fPaint = paint;
+ this->computeSize();
+ this->inval(NULL);
+ }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.isEmpty())
+ return;
+
+ SkTextBox box;
+
+ box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+ box.setSpacingAlign(this->getSpacingAlign());
+ box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+ box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+#if 0
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+ this->setMode((Mode)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+ this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+ else
+ assert_no_attr(dom, node, "spacing-align");
+
+ SkScalar s[2];
+ if (dom.findScalars(node, "margin", s, 2))
+ this->setMargin(s[0], s[1]);
+ else
+ assert_no_attr(dom, node, "margin");
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ if ((node = dom.getFirstChild(node, "paint")) != NULL &&
+ (node = dom.getFirstChild(node, "screenplay")) != NULL)
+ {
+ inflate_paint(dom, node, &fPaint);
+ }
+#endif
+}
+
diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp
new file mode 100644
index 0000000000..4576ce67c0
--- /dev/null
+++ b/src/views/SkTagList.cpp
@@ -0,0 +1,71 @@
+/* libs/graphics/views/SkTagList.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTagList.h"
+
+SkTagList::~SkTagList()
+{
+}
+
+SkTagList* SkTagList::Find(SkTagList* rec, U8CPU tag)
+{
+ SkASSERT(tag < kSkTagListCount);
+
+ while (rec != NULL)
+ {
+ if (rec->fTag == tag)
+ break;
+ rec = rec->fNext;
+ }
+ return rec;
+}
+
+void SkTagList::DeleteTag(SkTagList** head, U8CPU tag)
+{
+ SkASSERT(tag < kSkTagListCount);
+
+ SkTagList* rec = *head;
+ SkTagList* prev = NULL;
+
+ while (rec != NULL)
+ {
+ SkTagList* next = rec->fNext;
+
+ if (rec->fTag == tag)
+ {
+ if (prev)
+ prev->fNext = next;
+ else
+ *head = next;
+ delete rec;
+ break;
+ }
+ prev = rec;
+ rec = next;
+ }
+}
+
+void SkTagList::DeleteAll(SkTagList* rec)
+{
+ while (rec)
+ {
+ SkTagList* next = rec->fNext;
+ delete rec;
+ rec = next;
+ }
+}
+
diff --git a/src/views/SkTagList.h b/src/views/SkTagList.h
new file mode 100644
index 0000000000..5f428e4022
--- /dev/null
+++ b/src/views/SkTagList.h
@@ -0,0 +1,51 @@
+/* libs/graphics/views/SkTagList.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef SkTagList_DEFINED
+#define SkTagList_DEFINED
+
+#include "SkTypes.h"
+
+enum SkTagListEnum {
+ kListeners_SkTagList,
+ kViewLayout_SkTagList,
+ kViewArtist_SkTagList,
+
+ kSkTagListCount
+};
+
+struct SkTagList {
+ SkTagList* fNext;
+ uint16_t fExtra16;
+ uint8_t fExtra8;
+ uint8_t fTag;
+
+ SkTagList(U8CPU tag) : fTag(SkToU8(tag))
+ {
+ SkASSERT(tag < kSkTagListCount);
+ fNext = NULL;
+ fExtra16 = 0;
+ fExtra8 = 0;
+ }
+ virtual ~SkTagList();
+
+ static SkTagList* Find(SkTagList* head, U8CPU tag);
+ static void DeleteTag(SkTagList** headptr, U8CPU tag);
+ static void DeleteAll(SkTagList* head);
+};
+
+#endif
diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp
new file mode 100644
index 0000000000..0e31ac68e4
--- /dev/null
+++ b/src/views/SkTextBox.cpp
@@ -0,0 +1,237 @@
+/* libs/graphics/views/SkTextBox.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkTextBox.h"
+#include "../core/SkGlyphCache.h"
+#include "SkUtils.h"
+#include "SkAutoKern.h"
+
+static inline int is_ws(int c)
+{
+ return !((c - 1) >> 5);
+}
+
+static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin)
+{
+ const char* start = text;
+
+ SkAutoGlyphCache ac(paint, NULL);
+ SkGlyphCache* cache = ac.getCache();
+ SkFixed w = 0;
+ SkFixed limit = SkScalarToFixed(margin);
+ SkAutoKern autokern;
+
+ const char* word_start = text;
+ int prevWS = true;
+
+ while (text < stop)
+ {
+ const char* prevText = text;
+ SkUnichar uni = SkUTF8_NextUnichar(&text);
+ int currWS = is_ws(uni);
+ const SkGlyph& glyph = cache->getUnicharMetrics(uni);
+
+ if (!currWS && prevWS)
+ word_start = prevText;
+ prevWS = currWS;
+
+ w += autokern.adjust(glyph) + glyph.fAdvanceX;
+ if (w > limit)
+ {
+ if (currWS) // eat the rest of the whitespace
+ {
+ while (text < stop && is_ws(SkUTF8_ToUnichar(text)))
+ text += SkUTF8_CountUTF8Bytes(text);
+ }
+ else // backup until a whitespace (or 1 char)
+ {
+ if (word_start == start)
+ {
+ if (prevText > start)
+ text = prevText;
+ }
+ else
+ text = word_start;
+ }
+ break;
+ }
+ }
+ return text - start;
+}
+
+int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
+{
+ const char* stop = text + len;
+ int count = 0;
+
+ if (width > 0)
+ {
+ do {
+ count += 1;
+ text += linebreak(text, stop, paint, width);
+ } while (text < stop);
+ }
+ return count;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SkTextBox::SkTextBox()
+{
+ fBox.setEmpty();
+ fSpacingMul = SK_Scalar1;
+ fSpacingAdd = 0;
+ fMode = kLineBreak_Mode;
+ fSpacingAlign = kStart_SpacingAlign;
+}
+
+void SkTextBox::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+ fMode = SkToU8(mode);
+}
+
+void SkTextBox::setSpacingAlign(SpacingAlign align)
+{
+ SkASSERT((unsigned)align < kSpacingAlignCount);
+ fSpacingAlign = SkToU8(align);
+}
+
+void SkTextBox::getBox(SkRect* box) const
+{
+ if (box)
+ *box = fBox;
+}
+
+void SkTextBox::setBox(const SkRect& box)
+{
+ fBox = box;
+}
+
+void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
+{
+ fBox.set(left, top, right, bottom);
+}
+
+void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
+{
+ if (mul)
+ *mul = fSpacingMul;
+ if (add)
+ *add = fSpacingAdd;
+}
+
+void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
+{
+ fSpacingMul = mul;
+ fSpacingAdd = add;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
+{
+ SkASSERT(canvas && &paint && (text || len == 0));
+
+ SkScalar marginWidth = fBox.width();
+
+ if (marginWidth <= 0 || len == 0)
+ return;
+
+ const char* textStop = text + len;
+
+ SkScalar x, y, scaledSpacing, height, fontHeight;
+ SkPaint::FontMetrics metrics;
+
+ switch (paint.getTextAlign()) {
+ case SkPaint::kLeft_Align:
+ x = 0;
+ break;
+ case SkPaint::kCenter_Align:
+ x = SkScalarHalf(marginWidth);
+ break;
+ default:
+ x = marginWidth;
+ break;
+ }
+ x += fBox.fLeft;
+
+ fontHeight = paint.getFontMetrics(&metrics);
+ scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
+ height = fBox.height();
+
+ // compute Y position for first line
+ {
+ SkScalar textHeight = fontHeight;
+
+ if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
+ {
+ int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
+ SkASSERT(count > 0);
+ textHeight += scaledSpacing * (count - 1);
+ }
+
+ switch (fSpacingAlign) {
+ case kStart_SpacingAlign:
+ y = 0;
+ break;
+ case kCenter_SpacingAlign:
+ y = SkScalarHalf(height - textHeight);
+ break;
+ default:
+ SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
+ y = height - textHeight;
+ break;
+ }
+ y += fBox.fTop - metrics.fAscent;
+ }
+
+ for (;;)
+ {
+ len = linebreak(text, textStop, paint, marginWidth);
+ if (y + metrics.fDescent + metrics.fLeading > 0)
+ canvas->drawText(text, len, x, y, paint);
+ text += len;
+ if (text >= textStop)
+ break;
+ y += scaledSpacing;
+ if (y + metrics.fAscent >= height)
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
+ fText = text;
+ fLen = len;
+ fPaint = &paint;
+}
+
+void SkTextBox::draw(SkCanvas* canvas) {
+ this->draw(canvas, fText, fLen, *fPaint);
+}
+
+int SkTextBox::countLines() const {
+ return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
+}
+
+SkScalar SkTextBox::getTextHeight() const {
+ SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
+ return this->countLines() * spacing;
+}
+
diff --git a/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp
new file mode 100644
index 0000000000..1732176983
--- /dev/null
+++ b/src/views/SkTouchGesture.cpp
@@ -0,0 +1,336 @@
+/*
+ Copyright 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "SkTouchGesture.h"
+#include "SkMatrix.h"
+#include "SkTime.h"
+
+#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER true
+
+static const float MAX_FLING_SPEED = 1500;
+
+static float pin_max_fling(float speed) {
+ if (speed > MAX_FLING_SPEED) {
+ speed = MAX_FLING_SPEED;
+ }
+ return speed;
+}
+
+static double getseconds() {
+ return SkTime::GetMSecs() * 0.001;
+}
+
+// returns +1 or -1, depending on the sign of x
+// returns +1 if x is zero
+static SkScalar SkScalarSign(SkScalar x) {
+ SkScalar sign = SK_Scalar1;
+ if (x < 0) {
+ sign = -sign;
+ }
+ return sign;
+}
+
+static void unit_axis_align(SkVector* unit) {
+ const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
+ if (SkScalarAbs(unit->fX) < TOLERANCE) {
+ unit->fX = 0;
+ unit->fY = SkScalarSign(unit->fY);
+ } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
+ unit->fX = SkScalarSign(unit->fX);
+ unit->fY = 0;
+ }
+}
+
+void SkFlingState::reset(float sx, float sy) {
+ fActive = true;
+ fDirection.set(sx, sy);
+ fSpeed0 = SkPoint::Normalize(&fDirection);
+ fSpeed0 = pin_max_fling(fSpeed0);
+ fTime0 = getseconds();
+
+ unit_axis_align(&fDirection);
+// printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
+}
+
+bool SkFlingState::evaluateMatrix(SkMatrix* matrix) {
+ if (!fActive) {
+ return false;
+ }
+
+ const float t = (float)(getseconds() - fTime0);
+ const float MIN_SPEED = 2;
+ const float K0 = 5;
+ const float K1 = 0.02f;
+ const float speed = fSpeed0 * (sk_float_exp(- K0 * t) - K1);
+ if (speed <= MIN_SPEED) {
+ fActive = false;
+ return false;
+ }
+ float dist = (fSpeed0 - speed) / K0;
+
+// printf("---- time %g speed %g dist %g\n", t, speed, dist);
+ float tx = fDirection.fX * dist;
+ float ty = fDirection.fY * dist;
+ if (DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER) {
+ tx = (float)sk_float_round2int(tx);
+ ty = (float)sk_float_round2int(ty);
+ }
+ matrix->setTranslate(tx, ty);
+// printf("---- evaluate (%g %g)\n", tx, ty);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
+static const float MAX_DBL_TAP_DISTANCE = 100;
+static const float MAX_JITTER_RADIUS = 2;
+
+// if true, then ignore the touch-move, 'cause its probably just jitter
+static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
+ return sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
+ sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTouchGesture::SkTouchGesture() {
+ this->reset();
+}
+
+SkTouchGesture::~SkTouchGesture() {
+}
+
+void SkTouchGesture::reset() {
+ fTouches.reset();
+ fState = kEmpty_State;
+ fLocalM.reset();
+ fGlobalM.reset();
+
+ fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
+ fLastUpP.set(0, 0);
+}
+
+void SkTouchGesture::flushLocalM() {
+ fGlobalM.postConcat(fLocalM);
+ fLocalM.reset();
+}
+
+const SkMatrix& SkTouchGesture::localM() {
+ if (fFlinger.isActive()) {
+ if (!fFlinger.evaluateMatrix(&fLocalM)) {
+ this->flushLocalM();
+ }
+ }
+ return fLocalM;
+}
+
+void SkTouchGesture::appendNewRec(void* owner, float x, float y) {
+ Rec* rec = fTouches.append();
+ rec->fOwner = owner;
+ rec->fStartX = rec->fPrevX = rec->fLastX = x;
+ rec->fStartY = rec->fPrevY = rec->fLastY = y;
+ rec->fLastT = rec->fPrevT = SkTime::GetMSecs();
+}
+
+void SkTouchGesture::touchBegin(void* owner, float x, float y) {
+// GrPrintf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
+
+ int index = this->findRec(owner);
+ if (index >= 0) {
+ this->flushLocalM();
+ fTouches.removeShuffle(index);
+ SkDebugf("---- already exists, removing\n");
+ }
+
+ if (fTouches.count() == 2) {
+ return;
+ }
+
+ this->flushLocalM();
+ fFlinger.stop();
+
+ this->appendNewRec(owner, x, y);
+
+ switch (fTouches.count()) {
+ case 1:
+ fState = kTranslate_State;
+ break;
+ case 2:
+ fState = kZoom_State;
+ break;
+ default:
+ break;
+ }
+}
+
+int SkTouchGesture::findRec(void* owner) const {
+ for (int i = 0; i < fTouches.count(); i++) {
+ if (owner == fTouches[i].fOwner) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static float center(float pos0, float pos1) {
+ return (pos0 + pos1) * 0.5f;
+}
+
+static const float MAX_ZOOM_SCALE = 4;
+static const float MIN_ZOOM_SCALE = 0.25f;
+
+float SkTouchGesture::limitTotalZoom(float scale) const {
+ // this query works 'cause we know that we're square-scale w/ no skew/rotation
+ const float curr = fGlobalM[0];
+
+ if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
+ scale = MAX_ZOOM_SCALE / curr;
+ } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
+ scale = MIN_ZOOM_SCALE / curr;
+ }
+ return scale;
+}
+
+void SkTouchGesture::touchMoved(void* owner, float x, float y) {
+// GrPrintf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
+
+ SkASSERT(kEmpty_State != fState);
+
+ int index = this->findRec(owner);
+ if (index < 0) {
+ // not found, so I guess we should add it...
+ SkDebugf("---- add missing begin\n");
+ this->appendNewRec(owner, x, y);
+ index = fTouches.count() - 1;
+ }
+
+ Rec& rec = fTouches[index];
+
+ // not sure how valuable this is
+ if (fTouches.count() == 2) {
+ if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
+// GrPrintf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
+ return;
+ }
+ }
+
+ rec.fPrevX = rec.fLastX; rec.fLastX = x;
+ rec.fPrevY = rec.fLastY; rec.fLastY = y;
+ rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs();
+
+ switch (fTouches.count()) {
+ case 1: {
+ float dx = rec.fLastX - rec.fStartX;
+ float dy = rec.fLastY - rec.fStartY;
+ dx = (float)sk_float_round2int(dx);
+ dy = (float)sk_float_round2int(dy);
+ fLocalM.setTranslate(dx, dy);
+ } break;
+ case 2: {
+ SkASSERT(kZoom_State == fState);
+ const Rec& rec0 = fTouches[0];
+ const Rec& rec1 = fTouches[1];
+
+ float scale = this->computePinch(rec0, rec1);
+ scale = this->limitTotalZoom(scale);
+
+ fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
+ -center(rec0.fStartY, rec1.fStartY));
+ fLocalM.postScale(scale, scale);
+ fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
+ center(rec0.fLastY, rec1.fLastY));
+ } break;
+ default:
+ break;
+ }
+}
+
+void SkTouchGesture::touchEnd(void* owner) {
+// GrPrintf("--- %d touchEnd %p\n", fTouches.count(), owner);
+
+ int index = this->findRec(owner);
+ if (index < 0) {
+ SkDebugf("--- not found\n");
+ return;
+ }
+
+ const Rec& rec = fTouches[index];
+ if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
+ return;
+ }
+
+ // count() reflects the number before we removed the owner
+ switch (fTouches.count()) {
+ case 1: {
+ this->flushLocalM();
+ float dx = rec.fLastX - rec.fPrevX;
+ float dy = rec.fLastY - rec.fPrevY;
+ float dur = (rec.fLastT - rec.fPrevT) * 0.001f;
+ if (dur > 0) {
+ fFlinger.reset(dx / dur, dy / dur);
+ }
+ fState = kEmpty_State;
+ } break;
+ case 2:
+ this->flushLocalM();
+ SkASSERT(kZoom_State == fState);
+ fState = kEmpty_State;
+ break;
+ default:
+ SkASSERT(kZoom_State == fState);
+ break;
+ }
+
+ fTouches.removeShuffle(index);
+}
+
+float SkTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
+ double dx = rec0.fStartX - rec1.fStartX;
+ double dy = rec0.fStartY - rec1.fStartY;
+ double dist0 = sqrt(dx*dx + dy*dy);
+
+ dx = rec0.fLastX - rec1.fLastX;
+ dy = rec0.fLastY - rec1.fLastY;
+ double dist1 = sqrt(dx*dx + dy*dy);
+
+ double scale = dist1 / dist0;
+ return (float)scale;
+}
+
+bool SkTouchGesture::handleDblTap(float x, float y) {
+ bool found = false;
+ SkMSec now = SkTime::GetMSecs();
+ if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) {
+ if (SkPoint::Length(fLastUpP.fX - x,
+ fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
+ fFlinger.stop();
+ fLocalM.reset();
+ fGlobalM.reset();
+ fTouches.reset();
+ fState = kEmpty_State;
+ found = true;
+ }
+ }
+
+ fLastUpT = now;
+ fLastUpP.set(x, y);
+ return found;
+}
+
+
diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp
new file mode 100644
index 0000000000..1cd63394db
--- /dev/null
+++ b/src/views/SkView.cpp
@@ -0,0 +1,793 @@
+#include "SkView.h"
+#include "SkCanvas.h"
+
+////////////////////////////////////////////////////////////////////////
+
+SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
+{
+ fWidth = fHeight = 0;
+ fLoc.set(0, 0);
+ fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
+
+ fContainsFocus = 0;
+}
+
+SkView::~SkView()
+{
+ this->detachAllChildren();
+}
+
+void SkView::setFlags(uint32_t flags)
+{
+ SkASSERT((flags & ~kAllFlagMasks) == 0);
+
+ uint32_t diff = fFlags ^ flags;
+
+ if (diff & kVisible_Mask)
+ this->inval(NULL);
+
+ fFlags = SkToU8(flags);
+
+ if (diff & kVisible_Mask)
+ {
+ this->inval(NULL);
+ }
+}
+
+void SkView::setVisibleP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
+}
+
+void SkView::setEnabledP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
+}
+
+void SkView::setFocusableP(bool pred)
+{
+ this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
+}
+
+void SkView::setClipToBounds(bool pred) {
+ this->setFlags(SkSetClearShift(fFlags, !pred, kNoClip_Shift));
+}
+
+void SkView::setSize(SkScalar width, SkScalar height)
+{
+ width = SkMaxScalar(0, width);
+ height = SkMaxScalar(0, height);
+
+ if (fWidth != width || fHeight != height)
+ {
+ this->inval(NULL);
+ fWidth = width;
+ fHeight = height;
+ this->inval(NULL);
+ this->onSizeChange();
+ this->invokeLayout();
+ }
+}
+
+void SkView::setLoc(SkScalar x, SkScalar y)
+{
+ if (fLoc.fX != x || fLoc.fY != y)
+ {
+ this->inval(NULL);
+ fLoc.set(x, y);
+ this->inval(NULL);
+ }
+}
+
+void SkView::offset(SkScalar dx, SkScalar dy)
+{
+ if (dx || dy)
+ this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
+}
+
+void SkView::draw(SkCanvas* canvas)
+{
+ if (fWidth && fHeight && this->isVisible())
+ {
+ SkRect r;
+ r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
+ if (this->isClipToBounds() &&
+ canvas->quickReject(r, SkCanvas::kBW_EdgeType)) {
+ return;
+ }
+
+ SkAutoCanvasRestore as(canvas, true);
+
+ if (this->isClipToBounds()) {
+ canvas->clipRect(r);
+ }
+ canvas->translate(fLoc.fX, fLoc.fY);
+
+ if (fParent) {
+ fParent->beforeChild(this, canvas);
+ }
+
+ int sc = canvas->save();
+ this->onDraw(canvas);
+ canvas->restoreToCount(sc);
+
+ if (fParent) {
+ fParent->afterChild(this, canvas);
+ }
+
+ B2FIter iter(this);
+ SkView* child;
+
+ SkCanvas* childCanvas = this->beforeChildren(canvas);
+
+ while ((child = iter.next()) != NULL)
+ child->draw(childCanvas);
+
+ this->afterChildren(canvas);
+ }
+}
+
+void SkView::inval(SkRect* rect) {
+ SkView* view = this;
+ SkRect storage;
+
+ for (;;) {
+ if (!view->isVisible()) {
+ return;
+ }
+ if (view->isClipToBounds()) {
+ SkRect bounds;
+ view->getLocalBounds(&bounds);
+ if (rect && !bounds.intersect(*rect)) {
+ return;
+ }
+ storage = bounds;
+ rect = &storage;
+ }
+ if (view->handleInval(rect)) {
+ return;
+ }
+
+ SkView* parent = view->fParent;
+ if (parent == NULL) {
+ return;
+ }
+
+ if (rect) {
+ rect->offset(view->fLoc.fX, view->fLoc.fY);
+ }
+ view = parent;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+bool SkView::setFocusView(SkView* fv)
+{
+ SkView* view = this;
+
+ do {
+ if (view->onSetFocusView(fv))
+ return true;
+ } while ((view = view->fParent) != NULL);
+ return false;
+}
+
+SkView* SkView::getFocusView() const
+{
+ SkView* focus = NULL;
+ const SkView* view = this;
+ do {
+ if (view->onGetFocusView(&focus))
+ break;
+ } while ((view = view->fParent) != NULL);
+ return focus;
+}
+
+bool SkView::hasFocus() const
+{
+ return this == this->getFocusView();
+}
+
+bool SkView::acceptFocus()
+{
+ return this->isFocusable() && this->setFocusView(this);
+}
+
+/*
+ Try to give focus to this view, or its children
+*/
+SkView* SkView::acceptFocus(FocusDirection dir)
+{
+ if (dir == kNext_FocusDirection)
+ {
+ if (this->acceptFocus())
+ return this;
+
+ B2FIter iter(this);
+ SkView* child, *focus;
+ while ((child = iter.next()) != NULL)
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ }
+ else // prev
+ {
+ F2BIter iter(this);
+ SkView* child, *focus;
+ while ((child = iter.next()) != NULL)
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+
+ if (this->acceptFocus())
+ return this;
+ }
+
+ return NULL;
+}
+
+SkView* SkView::moveFocus(FocusDirection dir)
+{
+ SkView* focus = this->getFocusView();
+
+ if (focus == NULL)
+ { // start with the root
+ focus = this;
+ while (focus->fParent)
+ focus = focus->fParent;
+ }
+
+ SkView* child, *parent;
+
+ if (dir == kNext_FocusDirection)
+ {
+ parent = focus;
+ child = focus->fFirstChild;
+ if (child)
+ goto FIRST_CHILD;
+ else
+ goto NEXT_SIB;
+
+ do {
+ while (child != parent->fFirstChild)
+ {
+ FIRST_CHILD:
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ child = child->fNextSibling;
+ }
+ NEXT_SIB:
+ child = parent->fNextSibling;
+ parent = parent->fParent;
+ } while (parent != NULL);
+ }
+ else // prevfocus
+ {
+ parent = focus->fParent;
+ if (parent == NULL) // we're the root
+ return focus->acceptFocus(dir);
+ else
+ {
+ child = focus;
+ while (parent)
+ {
+ while (child != parent->fFirstChild)
+ {
+ child = child->fPrevSibling;
+ if ((focus = child->acceptFocus(dir)) != NULL)
+ return focus;
+ }
+ if (parent->acceptFocus())
+ return parent;
+
+ child = parent;
+ parent = parent->fParent;
+ }
+ }
+ }
+ return NULL;
+}
+
+void SkView::onFocusChange(bool gainFocusP)
+{
+ this->inval(NULL);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SkView::Click::Click(SkView* target)
+{
+ SkASSERT(target);
+ fTargetID = target->getSinkID();
+ fType = NULL;
+ fWeOwnTheType = false;
+}
+
+SkView::Click::~Click()
+{
+ this->resetType();
+}
+
+void SkView::Click::resetType()
+{
+ if (fWeOwnTheType)
+ {
+ sk_free(fType);
+ fWeOwnTheType = false;
+ }
+ fType = NULL;
+}
+
+bool SkView::Click::isType(const char type[]) const
+{
+ const char* t = fType;
+
+ if (type == t)
+ return true;
+
+ if (type == NULL)
+ type = "";
+ if (t == NULL)
+ t = "";
+ return !strcmp(t, type);
+}
+
+void SkView::Click::setType(const char type[])
+{
+ this->resetType();
+ fType = (char*)type;
+}
+
+void SkView::Click::copyType(const char type[])
+{
+ if (fType != type)
+ {
+ this->resetType();
+ if (type)
+ {
+ size_t len = strlen(type) + 1;
+ fType = (char*)sk_malloc_throw(len);
+ memcpy(fType, type, len);
+ fWeOwnTheType = true;
+ }
+ }
+}
+
+SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
+{
+ if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
+ return false;
+ }
+
+ if (this->onSendClickToChildren(x, y)) {
+ F2BIter iter(this);
+ SkView* child;
+
+ while ((child = iter.next()) != NULL)
+ {
+ Click* click = child->findClickHandler(x - child->fLoc.fX,
+ y - child->fLoc.fY);
+ if (click) {
+ return click;
+ }
+ }
+ }
+ return this->onFindClickHandler(x, y);
+}
+
+void SkView::DoClickDown(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIOrig.set(x, y);
+ click->fICurr = click->fIPrev = click->fIOrig;
+
+ click->fOrig.iset(x, y);
+ target->globalToLocal(&click->fOrig);
+ click->fPrev = click->fCurr = click->fOrig;
+
+ click->fState = Click::kDown_State;
+ target->onClick(click);
+}
+
+void SkView::DoClickMoved(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIPrev = click->fICurr;
+ click->fICurr.set(x, y);
+
+ click->fPrev = click->fCurr;
+ click->fCurr.iset(x, y);
+ target->globalToLocal(&click->fCurr);
+
+ click->fState = Click::kMoved_State;
+ target->onClick(click);
+}
+
+void SkView::DoClickUp(Click* click, int x, int y)
+{
+ SkASSERT(click);
+
+ SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+ if (target == NULL)
+ return;
+
+ click->fIPrev = click->fICurr;
+ click->fICurr.set(x, y);
+
+ click->fPrev = click->fCurr;
+ click->fCurr.iset(x, y);
+ target->globalToLocal(&click->fCurr);
+
+ click->fState = Click::kUp_State;
+ target->onClick(click);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::invokeLayout() {
+ SkView::Layout* layout = this->getLayout();
+
+ if (layout) {
+ layout->layoutChildren(this);
+ }
+}
+
+void SkView::onDraw(SkCanvas* canvas) {
+ Artist* artist = this->getArtist();
+
+ if (artist) {
+ artist->draw(this, canvas);
+ }
+}
+
+void SkView::onSizeChange() {}
+
+bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
+ return true;
+}
+
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
+ return NULL;
+}
+
+bool SkView::onClick(Click*) {
+ return false;
+}
+
+bool SkView::handleInval(const SkRect*) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::getLocalBounds(SkRect* bounds) const
+{
+ if (bounds)
+ bounds->set(0, 0, fWidth, fHeight);
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+void SkView::detachFromParent_NoLayout()
+{
+ if (fParent == NULL)
+ return;
+
+ if (fContainsFocus)
+ (void)this->setFocusView(NULL);
+
+ this->inval(NULL);
+
+ SkView* next = NULL;
+
+ if (fNextSibling != this) // do we have any siblings
+ {
+ fNextSibling->fPrevSibling = fPrevSibling;
+ fPrevSibling->fNextSibling = fNextSibling;
+ next = fNextSibling;
+ }
+
+ if (fParent->fFirstChild == this)
+ fParent->fFirstChild = next;
+
+ fParent = fNextSibling = fPrevSibling = NULL;
+
+ this->unref();
+}
+
+void SkView::detachFromParent()
+{
+ SkView* parent = fParent;
+
+ if (parent)
+ {
+ this->detachFromParent_NoLayout();
+ parent->invokeLayout();
+ }
+}
+
+SkView* SkView::attachChildToBack(SkView* child)
+{
+ SkASSERT(child != this);
+
+ if (child == NULL || fFirstChild == child)
+ goto DONE;
+
+ child->ref();
+ child->detachFromParent_NoLayout();
+
+ if (fFirstChild == NULL)
+ {
+ child->fNextSibling = child;
+ child->fPrevSibling = child;
+ }
+ else
+ {
+ child->fNextSibling = fFirstChild;
+ child->fPrevSibling = fFirstChild->fPrevSibling;
+ fFirstChild->fPrevSibling->fNextSibling = child;
+ fFirstChild->fPrevSibling = child;
+ }
+
+ fFirstChild = child;
+ child->fParent = this;
+ child->inval(NULL);
+
+ this->invokeLayout();
+DONE:
+ return child;
+}
+
+SkView* SkView::attachChildToFront(SkView* child)
+{
+ SkASSERT(child != this);
+
+ if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
+ goto DONE;
+
+ child->ref();
+ child->detachFromParent_NoLayout();
+
+ if (fFirstChild == NULL)
+ {
+ fFirstChild = child;
+ child->fNextSibling = child;
+ child->fPrevSibling = child;
+ }
+ else
+ {
+ child->fNextSibling = fFirstChild;
+ child->fPrevSibling = fFirstChild->fPrevSibling;
+ fFirstChild->fPrevSibling->fNextSibling = child;
+ fFirstChild->fPrevSibling = child;
+ }
+
+ child->fParent = this;
+ child->inval(NULL);
+
+ this->invokeLayout();
+DONE:
+ return child;
+}
+
+void SkView::detachAllChildren()
+{
+ while (fFirstChild)
+ fFirstChild->detachFromParent_NoLayout();
+}
+
+void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+{
+ SkASSERT(this);
+
+ if (local)
+ {
+ const SkView* view = this;
+ while (view)
+ {
+ x -= view->fLoc.fX;
+ y -= view->fLoc.fY;
+ view = view->fParent;
+ }
+ local->set(x, y);
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+
+/* Even if the subclass overrides onInflate, they should always be
+ sure to call the inherited method, so that we get called.
+*/
+void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkScalar x, y;
+
+ x = this->locX();
+ y = this->locY();
+ (void)dom.findScalar(node, "x", &x);
+ (void)dom.findScalar(node, "y", &y);
+ this->setLoc(x, y);
+
+ x = this->width();
+ y = this->height();
+ (void)dom.findScalar(node, "width", &x);
+ (void)dom.findScalar(node, "height", &y);
+ this->setSize(x, y);
+
+ // inflate the flags
+
+ static const char* gFlagNames[] = {
+ "visible", "enabled", "focusable", "flexH", "flexV"
+ };
+ SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
+
+ bool b;
+ uint32_t flags = this->getFlags();
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
+ if (dom.findBool(node, gFlagNames[i], &b))
+ flags = SkSetClearShift(flags, b, i);
+ this->setFlags(flags);
+}
+
+void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->onInflate(dom, node);
+}
+
+void SkView::onPostInflate(const SkTDict<SkView*>&)
+{
+ // override in subclass as needed
+}
+
+void SkView::postInflate(const SkTDict<SkView*>& dict)
+{
+ this->onPostInflate(dict);
+
+ B2FIter iter(this);
+ SkView* child;
+ while ((child = iter.next()) != NULL)
+ child->postInflate(dict);
+}
+
+//////////////////////////////////////////////////////////////////
+
+SkView* SkView::sendEventToParents(const SkEvent& evt)
+{
+ SkView* parent = fParent;
+
+ while (parent)
+ {
+ if (parent->doEvent(evt))
+ return parent;
+ parent = parent->fParent;
+ }
+ return NULL;
+}
+
+SkView* SkView::sendQueryToParents(SkEvent* evt) {
+ SkView* parent = fParent;
+
+ while (parent) {
+ if (parent->doQuery(evt)) {
+ return parent;
+ }
+ parent = parent->fParent;
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+SkView::F2BIter::F2BIter(const SkView* parent)
+{
+ fFirstChild = parent ? parent->fFirstChild : NULL;
+ fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
+}
+
+SkView* SkView::F2BIter::next()
+{
+ SkView* curr = fChild;
+
+ if (fChild)
+ {
+ if (fChild == fFirstChild)
+ fChild = NULL;
+ else
+ fChild = fChild->fPrevSibling;
+ }
+ return curr;
+}
+
+SkView::B2FIter::B2FIter(const SkView* parent)
+{
+ fFirstChild = parent ? parent->fFirstChild : NULL;
+ fChild = fFirstChild;
+}
+
+SkView* SkView::B2FIter::next()
+{
+ SkView* curr = fChild;
+
+ if (fChild)
+ {
+ SkView* next = fChild->fNextSibling;
+ if (next == fFirstChild)
+ next = NULL;
+ fChild = next;
+ }
+ return curr;
+}
+
+//////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static inline void show_if_nonzero(const char name[], SkScalar value)
+{
+ if (value)
+ SkDebugf("%s=\"%g\"", name, value/65536.);
+}
+
+static void tab(int level)
+{
+ for (int i = 0; i < level; i++)
+ SkDebugf(" ");
+}
+
+static void dumpview(const SkView* view, int level, bool recurse)
+{
+ tab(level);
+
+ SkDebugf("<view");
+ show_if_nonzero(" x", view->locX());
+ show_if_nonzero(" y", view->locY());
+ show_if_nonzero(" width", view->width());
+ show_if_nonzero(" height", view->height());
+
+ if (recurse)
+ {
+ SkView::B2FIter iter(view);
+ SkView* child;
+ bool noChildren = true;
+
+ while ((child = iter.next()) != NULL)
+ {
+ if (noChildren)
+ SkDebugf(">\n");
+ noChildren = false;
+ dumpview(child, level + 1, true);
+ }
+
+ if (!noChildren)
+ {
+ tab(level);
+ SkDebugf("</view>\n");
+ }
+ else
+ goto ONELINER;
+ }
+ else
+ {
+ ONELINER:
+ SkDebugf(" />\n");
+ }
+}
+
+void SkView::dump(bool recurse) const
+{
+ dumpview(this, 0, recurse);
+}
+
+#endif
diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp
new file mode 100644
index 0000000000..8f5489de54
--- /dev/null
+++ b/src/views/SkViewInflate.cpp
@@ -0,0 +1,139 @@
+#include "SkViewInflate.h"
+#include "SkView.h"
+#include <stdio.h>
+
+SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc)
+{
+}
+
+SkViewInflate::~SkViewInflate()
+{
+}
+
+void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent)
+{
+ const char* str = dom.findAttr(node, "id");
+ if (str)
+ fIDs.set(str, parent);
+
+ const SkDOM::Node* child = dom.getFirstChild(node);
+ while (child)
+ {
+ SkView* view = this->createView(dom, child);
+ if (view)
+ {
+ this->rInflate(dom, child, view);
+ parent->attachChildToFront(view)->unref();
+ }
+ else
+ {
+ const char* name = dom.getName(child);
+ const char* target;
+
+ if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL)
+ this->addIDStr(&fListenTo, parent, target);
+
+ if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL)
+ this->addIDStr(&fBroadcastTo, parent, target);
+ }
+ child = dom.getNextSibling(child);
+ }
+
+ parent->setVisibleP(true);
+ this->inflateView(parent, dom, node);
+}
+
+void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node)
+{
+ // called after all of view's children have been instantiated.
+ // this may be overridden by a subclass, to load in layout or other helpers
+ // they should call through to us (INHERITED) before or after their patch
+ view->inflate(dom, node);
+}
+
+SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root)
+{
+ fIDs.reset();
+
+ if (root == NULL)
+ {
+ root = this->createView(dom, node);
+ if (root == NULL)
+ {
+ printf("createView returned NULL on <%s>\n", dom.getName(node));
+ return NULL;
+ }
+ }
+ this->rInflate(dom, node, root);
+
+ // resolve listeners and broadcasters
+ {
+ SkView* target;
+ const IDStr* iter = fListenTo.begin();
+ const IDStr* stop = fListenTo.end();
+ for (; iter < stop; iter++)
+ {
+ if (fIDs.find(iter->fStr, &target))
+ target->addListenerID(iter->fView->getSinkID());
+ }
+
+ iter = fBroadcastTo.begin();
+ stop = fBroadcastTo.end();
+ for (; iter < stop; iter++)
+ {
+ if (fIDs.find(iter->fStr, &target))
+ iter->fView->addListenerID(target->getSinkID());
+ }
+ }
+
+ // now that the tree is built, give everyone a shot at the ID dict
+ root->postInflate(fIDs);
+ return root;
+}
+
+SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root)
+{
+ SkDOM dom;
+ const SkDOM::Node* node = dom.build(xml, len);
+
+ return node ? this->inflate(dom, node, root) : NULL;
+}
+
+SkView* SkViewInflate::findViewByID(const char id[]) const
+{
+ SkASSERT(id);
+ SkView* view;
+ return fIDs.find(id, &view) ? view : NULL;
+}
+
+SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node)
+{
+ if (!strcmp(dom.getName(node), "view"))
+ return new SkView;
+ return NULL;
+}
+
+void SkViewInflate::addIDStr(SkTDArray<IDStr>* list, SkView* view, const char* str)
+{
+ size_t len = strlen(str) + 1;
+ IDStr* pair = list->append();
+ pair->fView = view;
+ pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType);
+ memcpy(pair->fStr, str, len);
+}
+
+#ifdef SK_DEBUG
+void SkViewInflate::dump() const
+{
+ const IDStr* iter = fListenTo.begin();
+ const IDStr* stop = fListenTo.end();
+ for (; iter < stop; iter++)
+ SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr);
+
+ iter = fBroadcastTo.begin();
+ stop = fBroadcastTo.end();
+ for (; iter < stop; iter++)
+ SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
+}
+#endif
+
diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp
new file mode 100644
index 0000000000..b03ca8c101
--- /dev/null
+++ b/src/views/SkViewPriv.cpp
@@ -0,0 +1,97 @@
+#include "SkViewPriv.h"
+
+//////////////////////////////////////////////////////////////////////
+
+void SkView::Artist::draw(SkView* view, SkCanvas* canvas)
+{
+ SkASSERT(view && canvas);
+ this->onDraw(view, canvas);
+}
+
+void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(&dom && node);
+ this->onInflate(dom, node);
+}
+
+void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ // subclass should override this as needed
+}
+
+SkView::Artist* SkView::getArtist() const
+{
+ Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+ SkASSERT(rec == NULL || rec->fArtist != NULL);
+
+ return rec ? rec->fArtist : NULL;
+}
+
+SkView::Artist* SkView::setArtist(Artist* obj)
+{
+ if (obj == NULL)
+ {
+ this->removeTagList(kViewArtist_SkTagList);
+ }
+ else // add/replace
+ {
+ Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+
+ if (rec)
+ SkRefCnt_SafeAssign(rec->fArtist, obj);
+ else
+ this->addTagList(new Artist_SkTagList(obj));
+ }
+ return obj;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void SkView::Layout::layoutChildren(SkView* parent)
+{
+ SkASSERT(parent);
+ if (parent->width() > 0 && parent->height() > 0)
+ this->onLayoutChildren(parent);
+}
+
+void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ SkASSERT(&dom && node);
+ this->onInflate(dom, node);
+}
+
+void SkView::Layout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ // subclass should override this as needed
+}
+
+SkView::Layout* SkView::getLayout() const
+{
+ Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+ SkASSERT(rec == NULL || rec->fLayout != NULL);
+
+ return rec ? rec->fLayout : NULL;
+}
+
+SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow)
+{
+ if (obj == NULL)
+ {
+ this->removeTagList(kViewLayout_SkTagList);
+ }
+ else // add/replace
+ {
+ Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+
+ if (rec)
+ SkRefCnt_SafeAssign(rec->fLayout, obj);
+ else
+ this->addTagList(new Layout_SkTagList(obj));
+ }
+
+ if (invokeLayoutNow)
+ this->invokeLayout();
+
+ return obj;
+}
+
diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h
new file mode 100644
index 0000000000..06ce59bec6
--- /dev/null
+++ b/src/views/SkViewPriv.h
@@ -0,0 +1,38 @@
+#ifndef SkViewPriv_DEFINED
+#define SkViewPriv_DEFINED
+
+#include "SkView.h"
+#include "SkTagList.h"
+
+struct Layout_SkTagList : SkTagList {
+ SkView::Layout* fLayout;
+
+ Layout_SkTagList(SkView::Layout* layout)
+ : SkTagList(kViewLayout_SkTagList), fLayout(layout)
+ {
+ SkASSERT(layout);
+ layout->ref();
+ }
+ virtual ~Layout_SkTagList()
+ {
+ fLayout->unref();
+ }
+};
+
+struct Artist_SkTagList : SkTagList {
+ SkView::Artist* fArtist;
+
+ Artist_SkTagList(SkView::Artist* artist)
+ : SkTagList(kViewArtist_SkTagList), fArtist(artist)
+ {
+ SkASSERT(artist);
+ artist->ref();
+ }
+ virtual ~Artist_SkTagList()
+ {
+ fArtist->unref();
+ }
+};
+
+#endif
+
diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp
new file mode 100644
index 0000000000..4d055c43ec
--- /dev/null
+++ b/src/views/SkWidget.cpp
@@ -0,0 +1,323 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkInterpolator.h"
+#include "SkTime.h"
+#include "SkParsePaint.h"
+
+#if 0
+SkWidgetView::SkWidgetView(U32 flags) : SkView(flags)
+{
+}
+
+SkWidgetView::~SkWidgetView()
+{
+}
+
+const char* SkWidgetView::GetEventType()
+{
+ return "SkWidgetView";
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+class SkTextView::Interp {
+public:
+ Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2)
+ {
+ SkScalar x = 0;
+ fInterp.setKeyFrame(0, now, &x, 0);
+ x = SK_Scalar1;
+ if (dir == kBackward_AnimDir)
+ x = -x;
+ fInterp.setKeyFrame(1, now + dur, &x);
+ }
+ bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint)
+ {
+ SkScalar scale;
+
+ if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result)
+ {
+ canvas->drawText(newText.c_str(), newText.size(), x, y, paint);
+ return false;
+ }
+ else
+ {
+ U8 alpha = paint.getAlpha();
+ SkScalar above, below;
+ (void)paint.measureText(NULL, 0, &above, &below);
+ SkScalar height = below - above;
+ SkScalar dy = SkScalarMul(height, scale);
+ if (scale < 0)
+ height = -height;
+
+ // draw the old
+ paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale)));
+ canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint);
+ // draw the new
+ paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale)));
+ canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint);
+ // restore the paint
+ paint.setAlpha(alpha);
+ return true;
+ }
+ }
+
+private:
+ SkString fOldText;
+ SkInterpolator fInterp;
+};
+
+SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(NULL), fDoInterp(false)
+{
+ fMargin.set(0, 0);
+}
+
+SkTextView::~SkTextView()
+{
+ delete fInterp;
+}
+
+void SkTextView::getText(SkString* str) const
+{
+ if (str)
+ str->set(fText);
+}
+
+void SkTextView::setText(const char text[], AnimaDir dir)
+{
+ if (!fText.equals(text))
+ {
+ SkString tmp(text);
+ this->privSetText(tmp, dir);
+ }
+}
+
+void SkTextView::setText(const char text[], size_t len, AnimaDir dir)
+{
+ if (!fText.equals(text))
+ {
+ SkString tmp(text, len);
+ this->privSetText(tmp, dir);
+ }
+}
+
+void SkTextView::setText(const SkString& src, AnimaDir dir)
+{
+ if (fText != src)
+ this->privSetText(src, dir);
+}
+
+void SkTextView::privSetText(const SkString& src, AnimaDir dir)
+{
+ SkASSERT(fText != src);
+
+ if (fDoInterp)
+ {
+ if (fInterp)
+ delete fInterp;
+ fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir);
+ }
+ fText = src;
+ this->inval(NULL);
+}
+
+/////////////////////////////////////////////////////////////////
+
+void SkTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkTextView::setMargin(const SkPoint& margin)
+{
+ if (fMargin != margin)
+ {
+ fMargin = margin;
+ this->inval(NULL);
+ }
+}
+
+void SkTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.size() == 0)
+ return;
+
+ SkPaint::Align align = fPaint.getTextAlign();
+ SkScalar x, y;
+
+ switch (align) {
+ case SkPaint::kLeft_Align:
+ x = fMargin.fX;
+ break;
+ case SkPaint::kCenter_Align:
+ x = SkScalarHalf(this->width());
+ break;
+ default:
+ SkASSERT(align == SkPaint::kRight_Align);
+ x = this->width() - fMargin.fX;
+ break;
+ }
+
+ fPaint.measureText(NULL, 0, &y, NULL);
+ y = fMargin.fY - y;
+
+ if (fInterp)
+ {
+ if (fInterp->draw(canvas, fText, x, y, fPaint))
+ this->inval(NULL);
+ else
+ {
+ delete fInterp;
+ fInterp = NULL;
+ }
+ }
+ else
+ canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ SkPoint margin;
+ if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2))
+ this->setMargin(margin);
+ (void)dom.findBool(node, "do-interp", &fDoInterp);
+
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags)
+{
+ fValue = 0;
+ fMax = 0;
+}
+
+static U16 actual_value(U16CPU value, U16CPU max)
+{
+ return SkToU16(SkMax32(0, SkMin32(value, max)));
+}
+
+void SkSliderView::setMax(U16CPU max)
+{
+ if (fMax != max)
+ {
+ fMax = SkToU16(max);
+ if (fValue > 0)
+ this->inval(NULL);
+ }
+}
+
+void SkSliderView::setValue(U16CPU value)
+{
+ if (fValue != value)
+ {
+ U16 prev = actual_value(fValue, fMax);
+ U16 next = actual_value(value, fMax);
+
+ fValue = SkToU16(value);
+ if (prev != next)
+ {
+ this->inval(NULL);
+
+ if (this->hasListeners())
+ {
+ SkEvent evt;
+
+ evt.setType(SkWidgetView::GetEventType());
+ evt.setFast32(this->getSinkID());
+ evt.setS32("sliderValue", next);
+ this->postToListeners(evt);
+ }
+ }
+ }
+}
+
+#include "SkGradientShader.h"
+
+static void setgrad(SkPaint* paint, const SkRect& r)
+{
+ SkPoint pts[2];
+ SkColor colors[2];
+
+#if 0
+ pts[0].set(r.fLeft, r.fTop);
+ pts[1].set(r.fLeft + r.height(), r.fBottom);
+#else
+ pts[0].set(r.fRight, r.fBottom);
+ pts[1].set(r.fRight - r.height(), r.fTop);
+#endif
+ colors[0] = SK_ColorBLUE;
+ colors[1] = SK_ColorWHITE;
+
+ paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kMirror_TileMode))->unref();
+}
+
+void SkSliderView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+
+ p.setAntiAliasOn(true);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SK_Scalar1);
+ r.inset(SK_Scalar1/2, SK_Scalar1/2);
+ canvas->drawRect(r, p);
+
+ if (fMax)
+ {
+ SkFixed percent = SkFixedDiv(value, fMax);
+
+ r.inset(SK_Scalar1/2, SK_Scalar1/2);
+ r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+ p.setStyle(SkPaint::kFill_Style);
+ setgrad(&p, r);
+ canvas->drawRect(r, p);
+ }
+
+#if 0
+ r.set(0, 0, this->width(), this->height());
+ r.inset(SK_Scalar1, SK_Scalar1);
+ r.inset(r.width()/2, 0);
+ p.setColor(SK_ColorBLACK);
+ canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p);
+#endif
+}
+
+SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ return new Click(this);
+}
+
+bool SkSliderView::onClick(Click* click)
+{
+ if (fMax)
+ {
+ SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2);
+ percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1));
+ this->setValue(SkScalarRound(percent * fMax));
+ return true;
+ }
+ return false;
+}
+
+#endif
+
diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp
new file mode 100644
index 0000000000..2705307472
--- /dev/null
+++ b/src/views/SkWidgetViews.cpp
@@ -0,0 +1,405 @@
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkSystemEventTypes.h"
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+/*
+I have moved this to SkWidgetViews.h
+enum SkinEnum {
+ kButton_SkinEnum,
+ kProgress_SkinEnum,
+ kScroll_SkinEnum,
+ kStaticText_SkinEnum,
+
+ kSkinEnumCount
+};
+*/
+
+const char* get_skin_enum_path(SkinEnum se)
+{
+ SkASSERT((unsigned)se < kSkinEnumCount);
+
+ static const char* gSkinPaths[] = {
+ "common/default/default/skins/border3.xml",
+ "common/default/default/skins/button.xml",
+ "common/default/default/skins/progressBar.xml",
+ "common/default/default/skins/scrollBar.xml",
+ "common/default/default/skins/statictextpaint.xml"
+ };
+
+ return gSkinPaths[se];
+}
+
+void init_skin_anim(const char path[], SkAnimator* anim)
+{
+ SkASSERT(path && anim);
+
+ SkFILEStream stream(path);
+
+ if (!stream.isValid())
+ {
+ SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
+ sk_throw();
+ }
+
+ if (!anim->decodeStream(&stream))
+ {
+ SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
+ sk_throw();
+ }
+}
+
+void init_skin_anim(SkinEnum se, SkAnimator* anim)
+{
+ init_skin_anim(get_skin_enum_path(se), anim);
+}
+
+void init_skin_paint(SkinEnum se, SkPaint* paint)
+{
+ SkASSERT(paint);
+
+ SkAnimator anim;
+ SkCanvas canvas;
+
+ init_skin_anim(se, &anim);
+ anim.draw(&canvas, paint, 0);
+}
+
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
+{
+ SkASSERT(paint);
+
+ SkAnimator anim;
+ SkCanvas canvas;
+
+ if (!anim.decodeDOM(dom, node))
+ {
+ SkDEBUGF(("inflate_paint: decoding dom failed\n"));
+ SkDEBUGCODE(dom.dump(node);)
+ sk_throw();
+ }
+ anim.draw(&canvas, paint, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
+{
+}
+
+const char* SkWidgetView::getLabel() const
+{
+ return fLabel.c_str();
+}
+
+void SkWidgetView::getLabel(SkString* label) const
+{
+ if (label)
+ *label = fLabel;
+}
+
+void SkWidgetView::setLabel(const char label[])
+{
+ this->setLabel(label, label ? strlen(label) : 0);
+}
+
+void SkWidgetView::setLabel(const char label[], size_t len)
+{
+ if ((label == NULL && fLabel.size() != 0) || !fLabel.equals(label, len))
+ {
+ SkString tmp(label, len);
+
+ this->onLabelChange(fLabel.c_str(), tmp.c_str());
+ fLabel.swap(tmp);
+ }
+}
+
+void SkWidgetView::setLabel(const SkString& label)
+{
+ if (fLabel != label)
+ {
+ this->onLabelChange(fLabel.c_str(), label.c_str());
+ fLabel = label;
+ }
+}
+
+bool SkWidgetView::postWidgetEvent()
+{
+ if (!fEvent.isType(""))
+ {
+ SkEvent evt(fEvent); // make a copy since onPrepareWidgetEvent may edit the event
+
+ if (this->onPrepareWidgetEvent(&evt))
+ {
+ SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)
+
+ this->postToListeners(evt); // wonder if this should return true if there are > 0 listeners...
+ return true;
+ }
+ }
+ return false;
+}
+
+/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* label = dom.findAttr(node, "label");
+ if (label)
+ this->setLabel(label);
+
+ if ((node = dom.getFirstChild(node, "event")) != NULL)
+ fEvent.inflate(dom, node);
+}
+
+/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
+{
+ this->inval(NULL);
+}
+
+static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";
+
+/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
+ return true;
+}
+
+SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
+{
+ int32_t sinkID;
+
+ return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+ {
+ this->postWidgetEvent();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
+{
+}
+
+void SkCheckButtonView::setCheckState(CheckState state)
+{
+ SkASSERT((unsigned)state <= kUnknown_CheckState);
+
+ if (fCheckState != state)
+ {
+ this->onCheckStateChange(this->getCheckState(), state);
+ fCheckState = SkToU8(state);
+ }
+}
+
+/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
+{
+ this->inval(NULL);
+}
+
+/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index = dom.findList(node, "check-state", "off,on,unknown");
+ if (index >= 0)
+ this->setCheckState((CheckState)index);
+}
+
+static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";
+
+/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
+{
+ // could check if we're "disabled", and return false...
+
+ evt->setS32(gCheckStateSlotName, this->getCheckState());
+ return true;
+}
+
+bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
+{
+ int32_t state32;
+
+ if (evt.findS32(gCheckStateSlotName, &state32))
+ {
+ if (state)
+ *state = (CheckState)state32;
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+#include <stdio.h>
+
+class SkAnimButtonView : public SkButtonView {
+public:
+ SkAnimButtonView()
+ {
+ fAnim.setHostEventSink(this);
+ init_skin_anim(kButton_SkinEnum, &fAnim);
+ }
+
+protected:
+ virtual void onLabelChange(const char oldLabel[], const char newLabel[])
+ {
+ this->INHERITED::onLabelChange(oldLabel, newLabel);
+
+ SkEvent evt("user");
+ evt.setString("id", "setLabel");
+ evt.setString("LABEL", newLabel);
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onFocusChange(bool gainFocus)
+ {
+ this->INHERITED::onFocusChange(gainFocus);
+
+ SkEvent evt("user");
+ evt.setString("id", "setFocus");
+ evt.setS32("FOCUS", gainFocus);
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onSizeChange()
+ {
+ this->INHERITED::onSizeChange();
+
+ SkEvent evt("user");
+ evt.setString("id", "setDim");
+ evt.setScalar("dimX", this->width());
+ evt.setScalar("dimY", this->height());
+ fAnim.doUserEvent(evt);
+ }
+
+ virtual void onDraw(SkCanvas* canvas)
+ {
+ SkPaint paint;
+ SkAnimator::DifferenceType diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+ if (diff == SkAnimator::kDifferent)
+ this->inval(NULL);
+ else if (diff == SkAnimator::kPartiallyDifferent)
+ {
+ SkRect bounds;
+ fAnim.getInvalBounds(&bounds);
+ this->inval(&bounds);
+ }
+ }
+
+ virtual bool onEvent(const SkEvent& evt)
+ {
+ if (evt.isType(SK_EventType_Inval))
+ {
+ this->inval(NULL);
+ return true;
+ }
+ if (evt.isType("recommendDim"))
+ {
+ SkScalar height;
+
+ if (evt.findScalar("y", &height))
+ this->setHeight(height);
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+ }
+
+ virtual bool onPrepareWidgetEvent(SkEvent* evt)
+ {
+ if (this->INHERITED::onPrepareWidgetEvent(evt))
+ {
+ SkEvent e("user");
+ e.setString("id", "handlePress");
+ (void)fAnim.doUserEvent(e);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SkAnimator fAnim;
+
+ typedef SkButtonView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkView* SkWidgetFactory(const char name[])
+{
+ if (name == NULL)
+ return NULL;
+
+ // must be in the same order as the SkSkinWidgetEnum is declared
+ static const char* gNames[] = {
+ "sk-border",
+ "sk-button",
+ "sk-image",
+ "sk-list",
+ "sk-progress",
+ "sk-scroll",
+ "sk-text"
+
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++)
+ if (!strcmp(gNames[i], name))
+ return SkWidgetFactory((SkWidgetEnum)i);
+
+ return NULL;
+}
+
+#include "SkImageView.h"
+#include "SkProgressBarView.h"
+#include "SkScrollBarView.h"
+#include "SkBorderView.h"
+
+SkView* SkWidgetFactory(SkWidgetEnum sw)
+{
+ switch (sw) {
+ case kBorder_WidgetEnum:
+ return new SkBorderView;
+ case kButton_WidgetEnum:
+ return new SkAnimButtonView;
+ case kImage_WidgetEnum:
+ return new SkImageView;
+ case kList_WidgetEnum:
+ return new SkListView;
+ case kProgress_WidgetEnum:
+ return new SkProgressBarView;
+ case kScroll_WidgetEnum:
+ return new SkScrollBarView;
+ case kText_WidgetEnum:
+ return new SkStaticTextView;
+ default:
+ SkASSERT(!"unknown enum passed to SkWidgetFactory");
+ break;
+ }
+ return NULL;
+}
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
new file mode 100644
index 0000000000..dba9ab354e
--- /dev/null
+++ b/src/views/SkWidgets.cpp
@@ -0,0 +1,554 @@
+#include "SkWidget.h"
+#include "SkCanvas.h"
+#include "SkKey.h"
+#include "SkParsePaint.h"
+#include "SkSystemEventTypes.h"
+#include "SkTextBox.h"
+
+#if 0
+
+#ifdef SK_DEBUG
+ static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+ {
+ const char* value = dom.findAttr(node, attr);
+ if (value)
+ SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+ }
+#else
+ #define assert_no_attr(dom, node, attr)
+#endif
+
+#include "SkAnimator.h"
+#include "SkTime.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SkinType {
+ kPushButton_SkinType,
+ kStaticText_SkinType,
+
+ kSkinTypeCount
+};
+
+struct SkinSuite {
+ SkinSuite();
+ ~SkinSuite()
+ {
+ for (int i = 0; i < kSkinTypeCount; i++)
+ delete fAnimators[i];
+ }
+
+ SkAnimator* get(SkinType);
+
+private:
+ SkAnimator* fAnimators[kSkinTypeCount];
+};
+
+SkinSuite::SkinSuite()
+{
+ static const char kSkinPath[] = "skins/";
+
+ static const char* gSkinNames[] = {
+ "pushbutton_skin.xml",
+ "statictext_skin.xml"
+ };
+
+ for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++)
+ {
+ size_t len = strlen(gSkinNames[i]);
+ SkString path(sizeof(kSkinPath) - 1 + len);
+
+ memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1);
+ memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len);
+
+ fAnimators[i] = new SkAnimator;
+ if (!fAnimators[i]->decodeURI(path.c_str()))
+ {
+ delete fAnimators[i];
+ fAnimators[i] = NULL;
+ }
+ }
+}
+
+SkAnimator* SkinSuite::get(SkinType st)
+{
+ SkASSERT((unsigned)st < kSkinTypeCount);
+ return fAnimators[st];
+}
+
+static SkinSuite* gSkinSuite;
+
+static SkAnimator* get_skin_animator(SkinType st)
+{
+#if 0
+ if (gSkinSuite == NULL)
+ gSkinSuite = new SkinSuite;
+ return gSkinSuite->get(st);
+#else
+ return NULL;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkWidget::Init()
+{
+}
+
+void SkWidget::Term()
+{
+ delete gSkinSuite;
+}
+
+void SkWidget::onEnabledChange()
+{
+ this->inval(NULL);
+}
+
+void SkWidget::postWidgetEvent()
+{
+ if (!fEvent.isType("") && this->hasListeners())
+ {
+ this->prepareWidgetEvent(&fEvent);
+ this->postToListeners(fEvent);
+ }
+}
+
+void SkWidget::prepareWidgetEvent(SkEvent*)
+{
+ // override in subclass to add any additional fields before posting
+}
+
+void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ if ((node = dom.getFirstChild(node, "event")) != NULL)
+ fEvent.inflate(dom, node);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+size_t SkHasLabelWidget::getLabel(SkString* str) const
+{
+ if (str)
+ *str = fLabel;
+ return fLabel.size();
+}
+
+size_t SkHasLabelWidget::getLabel(char buffer[]) const
+{
+ if (buffer)
+ memcpy(buffer, fLabel.c_str(), fLabel.size());
+ return fLabel.size();
+}
+
+void SkHasLabelWidget::setLabel(const SkString& str)
+{
+ this->setLabel(str.c_str(), str.size());
+}
+
+void SkHasLabelWidget::setLabel(const char label[])
+{
+ this->setLabel(label, strlen(label));
+}
+
+void SkHasLabelWidget::setLabel(const char label[], size_t len)
+{
+ if (!fLabel.equals(label, len))
+ {
+ fLabel.set(label, len);
+ this->onLabelChange();
+ }
+}
+
+void SkHasLabelWidget::onLabelChange()
+{
+ // override in subclass
+}
+
+void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* text = dom.findAttr(node, "label");
+ if (text)
+ this->setLabel(text);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkButtonWidget::setButtonState(State state)
+{
+ if (fState != state)
+ {
+ fState = state;
+ this->onButtonStateChange();
+ }
+}
+
+void SkButtonWidget::onButtonStateChange()
+{
+ this->inval(NULL);
+}
+
+void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0)
+ this->setButtonState((State)index);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+bool SkPushButtonWidget::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+ {
+ this->postWidgetEvent();
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state)
+{
+ if (!enabled)
+ return "disabled";
+ if (state == SkButtonWidget::kOn_State)
+ {
+ SkASSERT(focused);
+ return "enabled-pressed";
+ }
+ if (focused)
+ return "enabled-focused";
+ return "enabled";
+}
+
+#include "SkBlurMaskFilter.h"
+#include "SkEmbossMaskFilter.h"
+
+static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed)
+{
+ SkEmbossMaskFilter::Light light;
+
+ light.fDirection[0] = SK_Scalar1/2;
+ light.fDirection[1] = SK_Scalar1/2;
+ light.fDirection[2] = SK_Scalar1/3;
+ light.fAmbient = 0x48;
+ light.fSpecular = 0x80;
+
+ if (pressed)
+ {
+ light.fDirection[0] = -light.fDirection[0];
+ light.fDirection[1] = -light.fDirection[1];
+ }
+ if (focus)
+ light.fDirection[2] += SK_Scalar1/4;
+
+ paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref();
+}
+
+void SkPushButtonWidget::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ SkString label;
+ this->getLabel(&label);
+
+ SkAnimator* anim = get_skin_animator(kPushButton_SkinType);
+
+ if (anim)
+ {
+ SkEvent evt("user");
+
+ evt.setString("id", "prime");
+ evt.setScalar("prime-width", this->width());
+ evt.setScalar("prime-height", this->height());
+ evt.setString("prime-text", label);
+ evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState()));
+
+ (void)anim->doUserEvent(evt);
+ SkPaint paint;
+ anim->draw(canvas, &paint, SkTime::GetMSecs());
+ }
+ else
+ {
+ SkRect r;
+ SkPaint p;
+
+ r.set(0, 0, this->width(), this->height());
+ p.setAntiAliasOn(true);
+ p.setColor(SK_ColorBLUE);
+ create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State);
+ canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p);
+ p.setMaskFilter(NULL);
+
+ p.setTextAlign(SkPaint::kCenter_Align);
+
+ SkTextBox box;
+ box.setMode(SkTextBox::kOneLine_Mode);
+ box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign);
+ box.setBox(0, 0, this->width(), this->height());
+
+// if (this->getButtonState() == kOn_State)
+// p.setColor(SK_ColorRED);
+// else
+ p.setColor(SK_ColorWHITE);
+
+ box.draw(canvas, label.c_str(), label.size(), p);
+ }
+}
+
+SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y)
+{
+ this->acceptFocus();
+ return new Click(this);
+}
+
+bool SkPushButtonWidget::onClick(Click* click)
+{
+ SkRect r;
+ State state = kOff_State;
+
+ this->getLocalBounds(&r);
+ if (r.contains(click->fCurr))
+ {
+ if (click->fState == Click::kUp_State)
+ this->postWidgetEvent();
+ else
+ state = kOn_State;
+ }
+ this->setButtonState(state);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags)
+{
+ fMargin.set(0, 0);
+ fMode = kFixedSize_Mode;
+ fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+ if (fMode == kAutoWidth_Mode)
+ {
+ SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), NULL, NULL);
+ this->setWidth(width + fMargin.fX * 2);
+ }
+ else if (fMode == kAutoHeight_Mode)
+ {
+ SkScalar width = this->width() - fMargin.fX * 2;
+ int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+ SkScalar before, after;
+ (void)fPaint.measureText(0, NULL, &before, &after);
+
+ this->setHeight(lines * (after - before) + fMargin.fY * 2);
+ }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+ SkASSERT((unsigned)mode < kModeCount);
+
+ if (fMode != mode)
+ {
+ fMode = SkToU8(mode);
+ this->computeSize();
+ }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+ fSpacingAlign = SkToU8(align);
+ this->inval(NULL);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+ if (margin)
+ *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+ if (fMargin.fX != dx || fMargin.fY != dy)
+ {
+ fMargin.set(dx, dy);
+ this->computeSize();
+ this->inval(NULL);
+ }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+ if (text)
+ *text = fText;
+ return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+ if (text)
+ memcpy(text, fText.c_str(), fText.size());
+ return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+ this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+ this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+ if (!fText.equals(text, len))
+ {
+ fText.set(text, len);
+ this->computeSize();
+ this->inval(NULL);
+ }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+ if (paint)
+ *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+ if (fPaint != paint)
+ {
+ fPaint = paint;
+ this->computeSize();
+ this->inval(NULL);
+ }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+ this->INHERITED::onDraw(canvas);
+
+ if (fText.isEmpty())
+ return;
+
+ SkTextBox box;
+
+ box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+ box.setSpacingAlign(this->getSpacingAlign());
+ box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+ box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ int index;
+ if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+ this->setMode((Mode)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+ this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+ else
+ assert_no_attr(dom, node, "mode");
+
+ SkScalar s[2];
+ if (dom.findScalars(node, "margin", s, 2))
+ this->setMargin(s[0], s[1]);
+ else
+ assert_no_attr(dom, node, "margin");
+
+ const char* text = dom.findAttr(node, "text");
+ if (text)
+ this->setText(text);
+
+ if ((node = dom.getFirstChild(node, "paint")) != NULL)
+ SkPaint_Inflate(&fPaint, dom, node);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkImageDecoder.h"
+
+SkBitmapView::SkBitmapView(U32 flags) : SkView(flags)
+{
+}
+
+SkBitmapView::~SkBitmapView()
+{
+}
+
+bool SkBitmapView::getBitmap(SkBitmap* bitmap) const
+{
+ if (bitmap)
+ *bitmap = fBitmap;
+ return fBitmap.getConfig() != SkBitmap::kNo_Config;
+}
+
+void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels)
+{
+ if (bitmap)
+ {
+ fBitmap = *bitmap;
+ fBitmap.setOwnsPixels(viewOwnsPixels);
+ }
+}
+
+bool SkBitmapView::loadBitmapFromFile(const char path[])
+{
+ SkBitmap bitmap;
+
+ if (SkImageDecoder::DecodeFile(path, &bitmap))
+ {
+ this->setBitmap(&bitmap, true);
+ bitmap.setOwnsPixels(false);
+ return true;
+ }
+ return false;
+}
+
+void SkBitmapView::onDraw(SkCanvas* canvas)
+{
+ if (fBitmap.getConfig() != SkBitmap::kNo_Config &&
+ fBitmap.width() && fBitmap.height())
+ {
+ SkAutoCanvasRestore restore(canvas, true);
+ SkPaint p;
+
+ p.setFilterType(SkPaint::kBilinear_FilterType);
+ canvas->scale( this->width() / fBitmap.width(),
+ this->height() / fBitmap.height(),
+ 0, 0);
+ canvas->drawBitmap(fBitmap, 0, 0, p);
+ }
+}
+
+void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+ this->INHERITED::onInflate(dom, node);
+
+ const char* src = dom.findAttr(node, "src");
+ if (src)
+ (void)this->loadBitmapFromFile(src);
+}
+
+#endif
+
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
new file mode 100644
index 0000000000..db4faee11f
--- /dev/null
+++ b/src/views/SkWindow.cpp
@@ -0,0 +1,412 @@
+#include "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkOSMenu.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+#define SK_EventDelayInval "\xd" "n" "\xa" "l"
+
+#define TEST_BOUNDERx
+
+#include "SkBounder.h"
+class test_bounder : public SkBounder {
+public:
+ test_bounder(const SkBitmap& bm) : fCanvas(bm) {}
+protected:
+ virtual bool onIRect(const SkIRect& r)
+ {
+ SkRect rr;
+
+ rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop),
+ SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom));
+
+ SkPaint p;
+
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setColor(SK_ColorYELLOW);
+
+#if 0
+ rr.inset(SK_ScalarHalf, SK_ScalarHalf);
+#else
+ rr.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+#endif
+
+ fCanvas.drawRect(rr, p);
+ return true;
+ }
+private:
+ SkCanvas fCanvas;
+};
+
+SkWindow::SkWindow() : fFocusView(NULL)
+{
+ fClick = NULL;
+ fWaitingOnInval = false;
+
+#ifdef SK_BUILD_FOR_WINCE
+ fConfig = SkBitmap::kRGB_565_Config;
+#else
+ fConfig = SkBitmap::kARGB_8888_Config;
+#endif
+
+ fMatrix.reset();
+}
+
+SkWindow::~SkWindow()
+{
+ delete fClick;
+
+ fMenus.deleteAll();
+}
+
+void SkWindow::setMatrix(const SkMatrix& matrix) {
+ if (fMatrix != matrix) {
+ fMatrix = matrix;
+ this->inval(NULL);
+ }
+}
+
+void SkWindow::preConcat(const SkMatrix& matrix) {
+ SkMatrix m;
+ m.setConcat(fMatrix, matrix);
+ this->setMatrix(m);
+}
+
+void SkWindow::postConcat(const SkMatrix& matrix) {
+ SkMatrix m;
+ m.setConcat(matrix, fMatrix);
+ this->setMatrix(m);
+}
+
+void SkWindow::setConfig(SkBitmap::Config config)
+{
+ this->resize(fBitmap.width(), fBitmap.height(), config);
+}
+
+void SkWindow::resize(int width, int height, SkBitmap::Config config)
+{
+ if (config == SkBitmap::kNo_Config)
+ config = fConfig;
+
+ if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig)
+ {
+ fConfig = config;
+ fBitmap.setConfig(config, width, height);
+ fBitmap.allocPixels();
+ fBitmap.setIsOpaque(true);
+
+ this->setSize(SkIntToScalar(width), SkIntToScalar(height));
+ this->inval(NULL);
+ }
+}
+
+void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
+{
+ fBitmap.eraseARGB(a, r, g, b);
+}
+
+void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b)
+{
+ fBitmap.eraseRGB(r, g, b);
+}
+
+bool SkWindow::handleInval(const SkRect* localR)
+{
+ SkIRect ir;
+
+ if (localR) {
+ SkRect devR;
+ SkMatrix inverse;
+ if (!fMatrix.invert(&inverse)) {
+ return false;
+ }
+ fMatrix.mapRect(&devR, *localR);
+ devR.round(&ir);
+ } else {
+ ir.set(0, 0,
+ SkScalarRound(this->width()),
+ SkScalarRound(this->height()));
+ }
+ fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+
+ this->onHandleInval(ir);
+ return true;
+}
+
+void SkWindow::forceInvalAll() {
+ fDirtyRgn.setRect(0, 0,
+ SkScalarCeil(this->width()),
+ SkScalarCeil(this->height()));
+}
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ #include <windows.h>
+ #include <gx.h>
+ extern GXDisplayProperties gDisplayProps;
+#endif
+
+#ifdef SK_SIMULATE_FAILED_MALLOC
+extern bool gEnableControlledThrow;
+#endif
+
+bool SkWindow::update(SkIRect* updateArea, SkCanvas* canvas)
+{
+ if (!fDirtyRgn.isEmpty())
+ {
+ SkBitmap bm = this->getBitmap();
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ char* buffer = (char*)GXBeginDraw();
+ SkASSERT(buffer);
+
+ RECT rect;
+ GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+ buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+
+ bm.setPixels(buffer);
+#endif
+
+ SkCanvas rasterCanvas;
+ SkDevice* device;
+
+ if (NULL == canvas) {
+ canvas = &rasterCanvas;
+ device = new SkDevice(canvas, bm, false);
+ canvas->setDevice(device)->unref();
+ } else {
+ canvas->setBitmapDevice(bm);
+ }
+
+ canvas->clipRegion(fDirtyRgn);
+ if (updateArea)
+ *updateArea = fDirtyRgn.getBounds();
+
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->concat(fMatrix);
+
+ // empty this now, so we can correctly record any inval calls that
+ // might be made during the draw call.
+ fDirtyRgn.setEmpty();
+
+#ifdef TEST_BOUNDER
+ test_bounder b(bm);
+ canvas->setBounder(&b);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+ gEnableControlledThrow = true;
+#endif
+#ifdef SK_BUILD_FOR_WIN32
+ //try {
+ this->draw(canvas);
+ //}
+ //catch (...) {
+ //}
+#else
+ this->draw(canvas);
+#endif
+#ifdef SK_SIMULATE_FAILED_MALLOC
+ gEnableControlledThrow = false;
+#endif
+#ifdef TEST_BOUNDER
+ canvas->setBounder(NULL);
+#endif
+
+#if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
+ GXEndDraw();
+#endif
+
+ return true;
+ }
+ return false;
+}
+
+bool SkWindow::handleChar(SkUnichar uni)
+{
+ if (this->onHandleChar(uni))
+ return true;
+
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ SkEvent evt(SK_EventType_Unichar);
+ evt.setFast32(uni);
+ return focus->doEvent(evt);
+}
+
+bool SkWindow::handleKey(SkKey key)
+{
+ if (key == kNONE_SkKey)
+ return false;
+
+ if (this->onHandleKey(key))
+ return true;
+
+ // send an event to the focus-view
+ {
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ SkEvent evt(SK_EventType_Key);
+ evt.setFast32(key);
+ if (focus->doEvent(evt))
+ return true;
+ }
+
+ if (key == kUp_SkKey || key == kDown_SkKey)
+ {
+ if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL)
+ this->onSetFocusView(NULL);
+ return true;
+ }
+ return false;
+}
+
+bool SkWindow::handleKeyUp(SkKey key)
+{
+ if (key == kNONE_SkKey)
+ return false;
+
+ if (this->onHandleKeyUp(key))
+ return true;
+
+ //send an event to the focus-view
+ {
+ SkView* focus = this->getFocusView();
+ if (focus == NULL)
+ focus = this;
+
+ //should this one be the same?
+ SkEvent evt(SK_EventType_KeyUp);
+ evt.setFast32(key);
+ if (focus->doEvent(evt))
+ return true;
+ }
+ return false;
+}
+
+void SkWindow::addMenu(SkOSMenu* menu)
+{
+ *fMenus.append() = menu;
+ this->onAddMenu(menu);
+}
+
+void SkWindow::setTitle(const char title[]) {
+ if (NULL == title) {
+ title = "";
+ }
+ fTitle.set(title);
+ this->onSetTitle(title);
+}
+
+bool SkWindow::handleMenu(uint32_t cmd)
+{
+ for (int i = 0; i < fMenus.count(); i++)
+ {
+ SkEvent* evt = fMenus[i]->createEvent(cmd);
+ if (evt)
+ {
+ evt->post(this->getSinkID());
+ return true;
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+bool SkWindow::onEvent(const SkEvent& evt)
+{
+ if (evt.isType(SK_EventDelayInval))
+ {
+ SkRegion::Iterator iter(fDirtyRgn);
+
+ for (; !iter.done(); iter.next())
+ this->onHandleInval(iter.rect());
+ fWaitingOnInval = false;
+ return true;
+ }
+ return this->INHERITED::onEvent(evt);
+}
+
+bool SkWindow::onGetFocusView(SkView** focus) const
+{
+ if (focus)
+ *focus = fFocusView;
+ return true;
+}
+
+bool SkWindow::onSetFocusView(SkView* focus)
+{
+ if (fFocusView != focus)
+ {
+ if (fFocusView)
+ fFocusView->onFocusChange(false);
+ fFocusView = focus;
+ if (focus)
+ focus->onFocusChange(true);
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+void SkWindow::onHandleInval(const SkIRect&)
+{
+}
+
+bool SkWindow::onHandleChar(SkUnichar)
+{
+ return false;
+}
+
+bool SkWindow::onHandleKey(SkKey key)
+{
+ return false;
+}
+
+bool SkWindow::onHandleKeyUp(SkKey key)
+{
+ return false;
+}
+
+bool SkWindow::handleClick(int x, int y, Click::State state) {
+ return this->onDispatchClick(x, y, state);
+}
+
+bool SkWindow::onDispatchClick(int x, int y, Click::State state) {
+ bool handled = false;
+
+ switch (state) {
+ case Click::kDown_State:
+ if (fClick)
+ delete fClick;
+ fClick = this->findClickHandler(SkIntToScalar(x), SkIntToScalar(y));
+ if (fClick)
+ {
+ SkView::DoClickDown(fClick, x, y);
+ handled = true;
+ }
+ break;
+ case Click::kMoved_State:
+ if (fClick)
+ {
+ SkView::DoClickMoved(fClick, x, y);
+ handled = true;
+ }
+ break;
+ case Click::kUp_State:
+ if (fClick)
+ {
+ SkView::DoClickUp(fClick, x, y);
+ delete fClick;
+ fClick = NULL;
+ handled = true;
+ }
+ break;
+ }
+ return handled;
+}
+
diff --git a/src/views/views_files.mk b/src/views/views_files.mk
new file mode 100644
index 0000000000..9c5e9e0fc6
--- /dev/null
+++ b/src/views/views_files.mk
@@ -0,0 +1,26 @@
+SOURCE := \
+ SkEvent.cpp \
+ SkEventSink.cpp \
+ SkOSMenu.cpp \
+ SkTagList.cpp \
+ SkView.cpp \
+ SkViewPriv.cpp \
+ SkWindow.cpp \
+ SkTouchGesture.cpp
+# SkBGViewArtist.cpp \
+ SkMetaData.cpp \
+ SkListView.cpp \
+ SkListWidget.cpp \
+ SkParsePaint.cpp \
+ SkProgressBarView.cpp \
+ SkProgressView.cpp \
+ SkScrollBarView.cpp \
+ SkStackViewLayout.cpp \
+ SkStaticTextView.cpp \
+ SkTextBox.cpp \
+ SkViewInflate.cpp \
+ SkWidget.cpp \
+ SkWidgetViews.cpp \
+ SkWidgets.cpp \
+# SkBorderView.cpp \
+# SkImageView.cpp \
diff --git a/src/xml/xml_files.mk b/src/xml/xml_files.mk
new file mode 100644
index 0000000000..d4342dd2ef
--- /dev/null
+++ b/src/xml/xml_files.mk
@@ -0,0 +1,3 @@
+SOURCE := \
+ SkDOM.cpp \
+ SkXMLParser.cpp
diff --git a/tests/Android.mk b/tests/Android.mk
index 09c7739cef..5823b0e4a6 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -26,6 +26,7 @@ LOCAL_SRC_FILES:= \
ParsePathTest.cpp \
PathMeasureTest.cpp \
PathTest.cpp \
+ Reader32Test.cpp \
RefDictTest.cpp \
RegionTest.cpp \
Sk64Test.cpp \
@@ -36,6 +37,7 @@ LOCAL_SRC_FILES:= \
Test.cpp \
TestSize.cpp \
UtilsTest.cpp \
+ Writer32Test.cpp \
XfermodeTest.cpp
# The name of the file with a main function must
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 4ef33ff35b..eafdd69052 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -107,7 +107,8 @@ static void TestClipStack(skiatest::Reporter* reporter) {
// all of the above rects should have been intersected, leaving only 1 rect
SkClipStack::B2FIter iter(stack);
const SkClipStack::B2FIter::Clip* clip = iter.next();
- const SkRect answer = { 25, 25, 75, 75 };
+ SkRect answer;
+ answer.iset(25, 25, 75, 75);
REPORTER_ASSERT(reporter, clip);
REPORTER_ASSERT(reporter, clip->fRect);
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 49a98c2c2c..4125f9f243 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -2,10 +2,12 @@
#include "SkMatrix.h"
static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
+ // Note that we get more compounded error for multiple operations when
+ // SK_SCALAR_IS_FIXED.
#ifdef SK_SCALAR_IS_FLOAT
- const float tolerance = 0.000005f;
+ const SkScalar tolerance = SK_Scalar1 / 200000;
#else
- const int32_t tolerance = 8;
+ const SkScalar tolerance = SK_Scalar1 / 1024;
#endif
return SkScalarAbs(a - b) <= tolerance;
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 6feab51d6a..9a58fa6c1d 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -122,15 +122,14 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
realHalf->unref(); // SkRefPtr and new both took a reference.
CheckObjectOutput(reporter, realHalf.get(), "0.5", true);
+#if defined(SK_SCALAR_IS_FLOAT)
SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
bigScalar->unref(); // SkRefPtr and new both took a reference.
-#if defined(SK_SCALAR_IS_FIXED) || !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
CheckObjectOutput(reporter, bigScalar.get(), "111000", true);
#else
CheckObjectOutput(reporter, bigScalar.get(), "110999.75", true);
-#endif
-#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
biggerScalar->unref(); // SkRefPtr and new both took a reference.
CheckObjectOutput(reporter, biggerScalar.get(), "50000000", true);
@@ -139,6 +138,7 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
smallestScalar->unref(); // SkRefPtr and new both took a reference.
CheckObjectOutput(reporter, smallestScalar.get(), "0.00001526", true);
#endif
+#endif
SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
stringSimple->unref(); // SkRefPtr and new both took a reference.
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 3884308c64..7e4e6bc6f0 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -13,15 +13,13 @@ static void test_convexity2(skiatest::Reporter* reporter) {
SkPath pt;
pt.moveTo(0, 0);
pt.close();
-// check_convexity(reporter, pt, SkPath::kConvex_Convexity);
- check_convexity(reporter, pt, SkPath::kUnknown_Convexity);
+ check_convexity(reporter, pt, SkPath::kConvex_Convexity);
SkPath line;
line.moveTo(12, 20);
line.lineTo(-12, -20);
line.close();
- // check_convexity(reporter, pt, SkPath::kConvex_Convexity);
- check_convexity(reporter, pt, SkPath::kUnknown_Convexity);
+ check_convexity(reporter, pt, SkPath::kConvex_Convexity);
SkPath triLeft;
triLeft.moveTo(0, 0);
@@ -79,21 +77,21 @@ static void test_convexity2(skiatest::Reporter* reporter) {
SkPath spiral;
spiral.moveTo(0, 0);
- spiral.lineTo(1, 0);
- spiral.lineTo(1, 1);
- spiral.lineTo(0, 1);
- spiral.lineTo(0,.5);
- spiral.lineTo(.5,.5);
- spiral.lineTo(.5,.75);
+ spiral.lineTo(100, 0);
+ spiral.lineTo(100, 100);
+ spiral.lineTo(0, 100);
+ spiral.lineTo(0, 50);
+ spiral.lineTo(50, 50);
+ spiral.lineTo(50, 75);
spiral.close();
check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
SkPath dent;
- dent.moveTo(0, 0);
- dent.lineTo(1, 1);
- dent.lineTo(0, 1);
- dent.lineTo(-.5,2);
- dent.lineTo(-2, 1);
+ dent.moveTo(SkIntToScalar(0), SkIntToScalar(0));
+ dent.lineTo(SkIntToScalar(100), SkIntToScalar(100));
+ dent.lineTo(SkIntToScalar(0), SkIntToScalar(100));
+ dent.lineTo(SkIntToScalar(-50), SkIntToScalar(200));
+ dent.lineTo(SkIntToScalar(-200), SkIntToScalar(100));
dent.close();
check_convexity(reporter, dent, SkPath::kConcave_Convexity);
}
@@ -133,13 +131,12 @@ static void setFromString(SkPath* path, const char str[]) {
}
static void test_convexity(skiatest::Reporter* reporter) {
- static const SkPath::Convexity U = SkPath::kUnknown_Convexity;
static const SkPath::Convexity C = SkPath::kConcave_Convexity;
static const SkPath::Convexity V = SkPath::kConvex_Convexity;
SkPath path;
- REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path));
+ REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
path.addCircle(0, 0, 10);
REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
path.addCircle(0, 0, 10); // 2nd circle
@@ -155,8 +152,9 @@ static void test_convexity(skiatest::Reporter* reporter) {
const char* fPathStr;
SkPath::Convexity fExpectedConvexity;
} gRec[] = {
- { "0 0", SkPath::kUnknown_Convexity },
- { "0 0 10 10", SkPath::kUnknown_Convexity },
+ { "", SkPath::kConvex_Convexity },
+ { "0 0", SkPath::kConvex_Convexity },
+ { "0 0 10 10", SkPath::kConvex_Convexity },
{ "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
{ "0 0 10 10 10 20", SkPath::kConvex_Convexity },
{ "0 0 10 10 10 0", SkPath::kConvex_Convexity },
@@ -188,7 +186,7 @@ void TestPath(skiatest::Reporter* reporter) {
SkRect bounds, bounds2;
REPORTER_ASSERT(reporter, p.isEmpty());
- REPORTER_ASSERT(reporter, !p.isConvex());
+ REPORTER_ASSERT(reporter, p.isConvex());
REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
REPORTER_ASSERT(reporter, !p.isInverseFillType());
REPORTER_ASSERT(reporter, p == p2);
@@ -198,17 +196,14 @@ void TestPath(skiatest::Reporter* reporter) {
bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
- p.setIsConvex(false);
p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
check_convex_bounds(reporter, p, bounds);
p.reset();
- p.setIsConvex(false);
p.addOval(bounds);
check_convex_bounds(reporter, p, bounds);
p.reset();
- p.setIsConvex(false);
p.addRect(bounds);
check_convex_bounds(reporter, p, bounds);
@@ -245,18 +240,6 @@ void TestPath(skiatest::Reporter* reporter) {
p.getLastPt(&pt);
REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
- // check that reset and rewind clear the convex hint back to false
- p.setIsConvex(false);
- REPORTER_ASSERT(reporter, !p.isConvex());
- p.setIsConvex(true);
- REPORTER_ASSERT(reporter, p.isConvex());
- p.reset();
- REPORTER_ASSERT(reporter, !p.isConvex());
- p.setIsConvex(true);
- REPORTER_ASSERT(reporter, p.isConvex());
- p.rewind();
- REPORTER_ASSERT(reporter, !p.isConvex());
-
test_convexity(reporter);
test_convexity2(reporter);
}
diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp
new file mode 100644
index 0000000000..c752b0f123
--- /dev/null
+++ b/tests/Reader32Test.cpp
@@ -0,0 +1,93 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "SkReader32.h"
+#include "Test.h"
+
+static void assert_eof(skiatest::Reporter* reporter, const SkReader32& reader) {
+ REPORTER_ASSERT(reporter, reader.eof());
+ REPORTER_ASSERT(reporter, reader.size() == reader.offset());
+ REPORTER_ASSERT(reporter, (const char*)reader.peek() ==
+ (const char*)reader.base() + reader.size());
+}
+
+static void assert_start(skiatest::Reporter* reporter, const SkReader32& reader) {
+ REPORTER_ASSERT(reporter, 0 == reader.offset());
+ REPORTER_ASSERT(reporter, reader.size() == reader.available());
+ REPORTER_ASSERT(reporter, reader.isAvailable(reader.size()));
+ REPORTER_ASSERT(reporter, !reader.isAvailable(reader.size() + 1));
+ REPORTER_ASSERT(reporter, reader.peek() == reader.base());
+}
+
+static void assert_empty(skiatest::Reporter* reporter, const SkReader32& reader) {
+ REPORTER_ASSERT(reporter, 0 == reader.size());
+ REPORTER_ASSERT(reporter, 0 == reader.offset());
+ REPORTER_ASSERT(reporter, 0 == reader.available());
+ REPORTER_ASSERT(reporter, !reader.isAvailable(1));
+ assert_eof(reporter, reader);
+ assert_start(reporter, reader);
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+ SkReader32 reader;
+ assert_empty(reporter, reader);
+ REPORTER_ASSERT(reporter, NULL == reader.base());
+ REPORTER_ASSERT(reporter, NULL == reader.peek());
+
+ size_t i;
+
+ const int32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ const SkScalar data2[] = { 0, SK_Scalar1, -SK_Scalar1, SK_Scalar1/2 };
+ char buffer[SkMax32(sizeof(data), sizeof(data2))];
+
+ reader.setMemory(data, sizeof(data));
+ for (i = 0; i < SK_ARRAY_COUNT(data); ++i) {
+ REPORTER_ASSERT(reporter, sizeof(data) == reader.size());
+ REPORTER_ASSERT(reporter, i*4 == reader.offset());
+ REPORTER_ASSERT(reporter, (const void*)data == reader.base());
+ REPORTER_ASSERT(reporter, (const void*)&data[i] == reader.peek());
+ REPORTER_ASSERT(reporter, data[i] == reader.readInt());
+ }
+ assert_eof(reporter, reader);
+ reader.rewind();
+ assert_start(reporter, reader);
+ reader.read(buffer, sizeof(data));
+ REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(data)));
+
+ reader.setMemory(data2, sizeof(data2));
+ for (i = 0; i < SK_ARRAY_COUNT(data2); ++i) {
+ REPORTER_ASSERT(reporter, sizeof(data2) == reader.size());
+ REPORTER_ASSERT(reporter, i*4 == reader.offset());
+ REPORTER_ASSERT(reporter, (const void*)data2 == reader.base());
+ REPORTER_ASSERT(reporter, (const void*)&data2[i] == reader.peek());
+ REPORTER_ASSERT(reporter, data2[i] == reader.readScalar());
+ }
+ assert_eof(reporter, reader);
+ reader.rewind();
+ assert_start(reporter, reader);
+ reader.read(buffer, sizeof(data2));
+ REPORTER_ASSERT(reporter, !memcmp(data2, buffer, sizeof(data2)));
+
+ reader.setMemory(NULL, 0);
+ assert_empty(reporter, reader);
+ REPORTER_ASSERT(reporter, NULL == reader.base());
+ REPORTER_ASSERT(reporter, NULL == reader.peek());
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Reader32", Reader32Class, Tests)
+
diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp
index a051af23c7..4f36afe4a5 100644
--- a/tests/UtilsTest.cpp
+++ b/tests/UtilsTest.cpp
@@ -64,7 +64,7 @@ static void test_autounref(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
}
-//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
#define kSEARCH_COUNT 91
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
new file mode 100644
index 0000000000..63b1209e98
--- /dev/null
+++ b/tests/Writer32Test.cpp
@@ -0,0 +1,90 @@
+/*
+ Copyright 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "SkReader32.h"
+#include "SkWriter32.h"
+#include "Test.h"
+
+static void test1(skiatest::Reporter* reporter, SkWriter32* writer) {
+ const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) {
+ REPORTER_ASSERT(reporter, i*4 == writer->size());
+ writer->write32(data[i]);
+ uint32_t* addr = writer->peek32(i * 4);
+ REPORTER_ASSERT(reporter, data[i] == *addr);
+ }
+
+ char buffer[sizeof(data)];
+ REPORTER_ASSERT(reporter, sizeof(buffer) == writer->size());
+ writer->flatten(buffer);
+ REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(buffer)));
+}
+
+static void test2(skiatest::Reporter* reporter, SkWriter32* writer) {
+ static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
+ size_t i;
+
+ size_t len = 0;
+ for (i = 0; i <= 26; ++i) {
+ len += SkWriter32::WriteStringSize(gStr, i);
+ writer->writeString(gStr, i);
+ }
+ REPORTER_ASSERT(reporter, writer->size() == len);
+
+ SkAutoMalloc storage(len);
+ writer->flatten(storage.get());
+
+ SkReader32 reader;
+ reader.setMemory(storage.get(), len);
+ for (i = 0; i <= 26; ++i) {
+ REPORTER_ASSERT(reporter, !reader.eof());
+ const char* str = reader.readString(&len);
+ REPORTER_ASSERT(reporter, i == len);
+ REPORTER_ASSERT(reporter, strlen(str) == len);
+ REPORTER_ASSERT(reporter, !memcmp(str, gStr, len));
+ }
+ REPORTER_ASSERT(reporter, reader.eof());
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+ // dynamic allocator
+ {
+ SkWriter32 writer(256 * 4);
+ REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
+ test1(reporter, &writer);
+
+ writer.reset();
+ test2(reporter, &writer);
+ }
+
+ // single-block
+ {
+ SkWriter32 writer(0);
+ uint32_t storage[256];
+ REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
+ writer.reset(storage, sizeof(storage));
+ REPORTER_ASSERT(reporter, (void*)storage == writer.getSingleBlock());
+ test1(reporter, &writer);
+
+ writer.reset(storage, sizeof(storage));
+ test2(reporter, &writer);
+ }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Writer32", Writer32Class, Tests)
+
diff --git a/tests/tests_files.mk b/tests/tests_files.mk
index 28d5fe75f9..9b90179c7e 100644
--- a/tests/tests_files.mk
+++ b/tests/tests_files.mk
@@ -22,6 +22,7 @@ SOURCE := \
ParsePathTest.cpp \
PathMeasureTest.cpp \
PathTest.cpp \
+ Reader32Test.cpp \
RefDictTest.cpp \
RegionTest.cpp \
Sk64Test.cpp \
@@ -33,4 +34,5 @@ SOURCE := \
Test.cpp \
TestSize.cpp \
UtilsTest.cpp \
+ Writer32Test.cpp \
XfermodeTest.cpp